diff --git a/src/action/action_repair.cpp b/src/action/action_repair.cpp index c1a0cef13..38f90a35d 100644 --- a/src/action/action_repair.cpp +++ b/src/action/action_repair.cpp @@ -99,8 +99,8 @@ local void RepairUnit(Unit* unit,Unit* goal) for( i=1; i<MaxCosts; ++i ) { if( player->Resources[i]<costs[i] ) { SetMessage("We need resources!"); - // FIXME: perhaps we should not animate if no resources are available. - return; + // FIXME: perhaps we should not animate if no resources are available. + return; } } // diff --git a/src/action/action_research.cpp b/src/action/action_research.cpp index fa47c06c8..f2e184e31 100644 --- a/src/action/action_research.cpp +++ b/src/action/action_research.cpp @@ -45,8 +45,9 @@ global void HandleActionResearch(Unit* unit) if( unit->Command.Data.Research.Ticks>=upgrade->Costs[TimeCost] ) { // FIXME: should als speak and tell ai - SetMessage2( unit->X, unit->Y, "%s: Upgrade complete", unit->Type->Name ); - + if( unit->Player==ThisPlayer ) { + SetMessage2( unit->X, unit->Y, "%s: Upgrade complete", unit->Type->Name ); + } UpgradeAcquire(unit->Player,upgrade); unit->Reset=1; diff --git a/src/action/action_spellcast.cpp b/src/action/action_spellcast.cpp index 5a990d90c..d93ddecf7 100644 --- a/src/action/action_spellcast.cpp +++ b/src/action/action_spellcast.cpp @@ -136,8 +136,11 @@ global void HandleActionSpellCast(Unit* unit) const SpellType* spell = SpellTypeById( unit->Command.Data.Move.SpellId ); if ( unit->Mana < spell->ManaCost ) { - SetMessage( "%s: not enough mana to cast spell: %s", - unit->Type->Name, spell->Ident ); + if( unit->Player==ThisPlayer ) + { + SetMessage( "%s: not enough mana to cast spell: %s", + unit->Type->Name, spell->Ident ); + } repeat = 0; } else diff --git a/src/stratagus/oldmissile.cpp b/src/stratagus/oldmissile.cpp index 8fb08aecb..f43ea1957 100644 --- a/src/stratagus/oldmissile.cpp +++ b/src/stratagus/oldmissile.cpp @@ -210,7 +210,7 @@ global MissileType MissileTypes[MissileTypeMax] = { "death and decay.png", 32,32, { NULL }, - MissileClassStayWithDelay, + MissileClassDeathDecay, 1, }, { MissileTypeType, @@ -603,13 +603,15 @@ local int CalculateDamageStats(const UnitStats* attacker_stats ,const UnitStats* goal_stats) { int damage; - + int basic_damage = attacker_stats->BasicDamage; + int piercing_damage = attacker_stats->PiercingDamage; + damage=-goal_stats->Armor; - damage+=attacker_stats->BasicDamage; + damage+= basic_damage; if( damage<0 ) { damage=0; } - damage+=attacker_stats->PiercingDamage+1; // round up + damage+=piercing_damage+1; // round up damage/=2; damage*=((SyncRand()>>15)&1)+1; DebugLevel3Fn("Damage done %d\n",damage); @@ -984,6 +986,7 @@ local int PointToPointMissile(Missile* missile) return 0; } +local int BlizzardMissileHit = 0; /** ** Work for missile hit. */ @@ -1038,6 +1041,9 @@ global void MissileHit(const Missile* missile) DebugLevel3Fn("Oops nothing to hit (%d,%d)?\n",x,y); return; } + if ( BlizzardMissileHit && goal == missile->SourceUnit ) + return; // blizzard cannot hit owner unit + BlizzardMissileHit = 0; if ( missile->Damage ) HitUnit(goal,missile->Damage); // direct damage, spells mostly else @@ -1184,11 +1190,26 @@ global void MissileActions(void) missile->Frame+=4; // FIXME: frames pro row if( (missile->Frame&127) >=VideoGraphicFrames(missile->Type->Sprite) ) { + //NOTE: vladi: blizzard cannot hit owner... + BlizzardMissileHit = 1; MissileHit(missile); missile->Type=MissileFree; } } break; + + case MissileClassDeathDecay: + //NOTE: vladi: this is exact copy of MissileClassStayWithDelay + // but with check for blizzard-type hit (friendly fire:)) + missile->Wait=missile->Type->Speed; + if( ++missile->Frame + ==VideoGraphicFrames(missile->Type->Sprite) ) { + BlizzardMissileHit = 1; + MissileHit(missile); + missile->Type=MissileFree; + } + break; + case MissileClassWhirlwind: missile->Wait=missile->Type->Speed; missile->Frame++; @@ -1199,7 +1220,7 @@ global void MissileActions(void) if ( missile->TTL < 1 || missile->TTL % 10 == 0 ) PointToPointMissile(missile); break; - + case MissileClassStayWithDelay: missile->Wait=missile->Type->Speed; if( ++missile->Frame diff --git a/src/stratagus/spells.cpp b/src/stratagus/spells.cpp index ecfd5141b..b7746aaad 100644 --- a/src/stratagus/spells.cpp +++ b/src/stratagus/spells.cpp @@ -75,7 +75,7 @@ global SpellType SpellTypeTable[] = { { 0, "spell-healing", 4, 6, -1, SpellActionHealing , "healing", NULL }, { 0, "spell-exorcism", 10, 4, -1, SpellActionExorcism , "exorcism", NULL }, // ---human mages--- ---human mages--- -{ 0, "spell-fireball", 8, 100, 999, SpellActionFireball , "fireball", NULL }, +{ 0, "spell-fireball", 8, 100,1000, SpellActionFireball , "fireball", NULL }, { 0, "spell-slow", 10, 50,1000, SpellActionSlow , "slow", NULL }, { 0, "spell-flame-shield", 6, 80, 600, SpellActionFlameShield , "flame shield", NULL }, { 0, "spell-invisibility", 6, 200,2000, SpellActionInvisibility , "invisibility", NULL }, @@ -109,8 +109,11 @@ MissileType* missile_rune = NULL; ----------------------------------------------------------------------------*/ /* -** Missile controllers should return 0 to continue -** or 1 to cancel the spell/missile +** Missile controllers +** +** To cancel a missile set it's TTL to 0, it will be handled right after +** the controller call and missile will be down. +** */ /* @@ -125,9 +128,10 @@ global int SpellFireballController( void* missile ) Missile* mis = (Missile*)missile; //NOTE: vladi: TTL is used as counter for explosions - // first 6 counts there are no explosions, then on each second - // and at the target destination - if ( (mis->TTL < 993 && mis->TTL % 2 == 0) || (mis->X == mis->DX && mis->Y == mis->DY) ) // approx. + // explosions start at target and continue (10 tiles) beyond + // explosions are on each tile on the way + + if ( mis->TTL <= mis->State && mis->TTL % 2 == 0 ) // approx. { //+TileSize/2 to align gfx to baseline int x = mis->X + TileSizeX/2; @@ -144,7 +148,8 @@ global int SpellFireballController( void* missile ) for( i=0; i<n; ++i ) HitUnit(table[i],FIREBALL_DAMAGE); } - return 0; + + return 0; }; /* @@ -486,6 +491,7 @@ global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y ) int sy = unit->Y; int dx = x; int dy = y; + int dist; if ( target ) { @@ -493,15 +499,22 @@ global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y ) dy = target->Y; } + dist = MapDistance( sx, sy, dx, dy ); + dx += ((dx - sx)*10)/dist; + dy += ((dy - sy)*10)/dist; + + sx = sx*TileSizeX+TileSizeX/2; + sy = sy*TileSizeX+TileSizeX/2; + dx = dx*TileSizeX+TileSizeX/2; + dy = dy*TileSizeX+TileSizeX/2; + unit->Mana -= spell->ManaCost; PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \ mis = MakeMissile( MissileTypeByIdent("missile-fireball"), - sx*TileSizeX+TileSizeX/2, - sy*TileSizeX+TileSizeX/2, - dx*TileSizeX+TileSizeX/2, - dy*TileSizeX+TileSizeX/2 ); + sx, sy, dx, dy ); + mis->State = spell->TTL - (dist - 1) * 2; mis->TTL = spell->TTL; mis->Controller = SpellFireballController; } @@ -646,7 +659,7 @@ global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y ) // ---orc death knights--- case SpellActionDeathCoil: if( (target && target->Type->Organic) || (!target) ) - { //NOTE: fireball can be casted on spot + { Missile* mis; int sx = unit->X; int sy = unit->Y; diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp index a05974d00..0cc158b79 100644 --- a/src/ui/mainscr.cpp +++ b/src/ui/mainscr.cpp @@ -480,7 +480,7 @@ global void DrawResources(void) // FIXME: need messages for chat! -#define MESSAGES_TIMEOUT FRAMES_PER_SECOND*2 // two seconds +#define MESSAGES_TIMEOUT FRAMES_PER_SECOND*5 // 5 seconds local char MessageBuffer[40]; // message buffer local char* Message; // message in map window @@ -490,6 +490,7 @@ local int MessageFrameTimeout; // frame to expire message local char Messages[ MESSAGES_MAX ][64]; local int MessagesCount; +local int SameMessageCount; local char MessagesEvent[ MESSAGES_MAX ][64]; local int MessagesEventX[ MESSAGES_MAX ]; @@ -550,11 +551,51 @@ global void DrawMessage(void) } for ( z = 0; z < MessagesCount; z++ ) { - if ( Messages[z][0] == '*' ) - DrawText(TheUI.MapX+8,TheUI.MapY+8 + z*16,GameFont,Messages[z]+1); - else - DrawReverseText(TheUI.MapX+8,TheUI.MapY+8 + z*16,GameFont,Messages[z]); + DrawText(TheUI.MapX+8,TheUI.MapY+8 + z*16,GameFont,Messages[z] ); } + if ( MessagesCount < 1 ) + SameMessageCount = 0; +} + +/* +** Adds message to the stack +** +** @param msg Message to add. +*/ +global void AddMessage( const char* msg ) +{ + if ( MessagesCount == MESSAGES_MAX ) + ShiftMessages(); + strcpy( Messages[ MessagesCount ], msg ); + MessagesCount++; +} + +/* +** Check if this message repeats +** +** @param msg Message to check. +** @return non-zero to skip this message +*/ +global int CheckRepeatMessage( const char* msg ) +{ + if ( MessagesCount < 1 ) + return 0; + if ( strcmp( msg, Messages[ MessagesCount-1 ] ) == 0 ) + { + SameMessageCount++; + return 1; + } + else + if ( SameMessageCount > 0 ) + { + char temp[128]; + int n = SameMessageCount; + SameMessageCount = 0; + // NOTE: vladi: yep it's a tricky one, but should work fine prbably :) + sprintf( temp, "Last message repeated ~<%d~> times", n+1 ); + AddMessage( temp ); + } + return 0; } /** @@ -569,10 +610,9 @@ global void SetMessage( char* fmt, ... ) va_start( va, fmt ); vsprintf( temp, fmt, va ); va_end( va ); - if ( MessagesCount == MESSAGES_MAX ) - ShiftMessages(); - strcpy( Messages[ MessagesCount ], temp ); - MessagesCount++; + if ( CheckRepeatMessage( temp ) ) + return; + AddMessage( temp ); MustRedraw|=RedrawMessage|RedrawMap; MessageFrameTimeout = FrameCounter + MESSAGES_TIMEOUT; } @@ -594,10 +634,10 @@ global void SetMessage2( int x, int y, char* fmt, ... ) va_start( va, fmt ); vsprintf( temp, fmt, va ); va_end( va ); - if ( MessagesCount == MESSAGES_MAX ) - ShiftMessages(); - strcpy( Messages[ MessagesCount ], temp ); - MessagesCount++; + if ( CheckRepeatMessage( temp ) == 0 ) + { + AddMessage( temp ); + } if ( MessagesEventCount == MESSAGES_MAX ) ShiftMessagesEvent(); @@ -667,7 +707,7 @@ global void CenterOnMessage(void) return; MapCenter( MessagesEventX[ MessagesEventIndex ], MessagesEventY[ MessagesEventIndex ] ); - SetMessage( "*Event: %s", MessagesEvent[ MessagesEventIndex ] ); + SetMessage( "~<Event: %s~>", MessagesEvent[ MessagesEventIndex ] ); MessagesEventIndex++; } diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp index 4f0ab8580..a92850bc2 100644 --- a/src/ui/mouse.cpp +++ b/src/ui/mouse.cpp @@ -535,6 +535,12 @@ global void UIHandleMouseMove(int x,int y) } } + //NOTE: vladi: if unit is invisible, no cursor hint should be allowed + if ( UnitUnderCursor && !UnitVisible( UnitUnderCursor ) ) + { + UnitUnderCursor = NULL; + } + // // Selecting target. // diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 12f5fb0fa..819a0e4f8 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -663,6 +663,12 @@ global int UnitVisible(const Unit* unit) unsigned m; MapField* mf; + if ( unit->Invisible && unit->Player != ThisPlayer ) + { + //FIXME: vladi: should handle teams and shared vision + return 0; + } + // // Check if visible on screen // @@ -705,6 +711,12 @@ global int UnitVisible(const Unit* unit) } ); + if ( unit->Invisible && unit->Player != ThisPlayer ) + { + //FIXME: vladi: should handle teams and shared vision + return 0; + } + // // Check if visible on screen // @@ -2127,6 +2139,11 @@ global void HitUnit(Unit* unit,int damage) DebugCheck( damage==0 || unit->HP==0 || unit->Type->Vanishes ); + if ( unit->UnholyArmor > 0 ) + { //NOTE: vladi: units with active UnholyArmour are invulnerable + return; + } + type=unit->Type; if( !unit->Attacked ) { if( unit->Player==ThisPlayer ) {