/** ** Fire missile. */ global void FireMissile(Unit* unit) { int x; int y; int dx; int dy; Unit* goal; Missile* missile; DebugLevel3("%s:\n",__FUNCTION__); if( unit->Type->MissileWeapon==MissileNone ) { // FIXME: must hit now!!! if( !(goal=unit->Command.Data.Move.Goal) ) { DebugLevel1("Missile-none hits no unit, shouldn't happen!\n"); return; } // FIXME: make sure thats the correct unit. if( !goal->HP || goal->Command.Action==UnitActionDie ) { return; } HitUnit(goal,CalculateDamage(unit->Stats,goal)); return; } x=unit->X*TileSizeX+TileSizeX/2; y=unit->Y*TileSizeY+TileSizeY/2; if( (goal=unit->Command.Data.Move.Goal) ) { // Fire to nearest point of unit! if( goal->Type ) { NearestOfUnit(goal,unit->X,unit->Y,&dx,&dy); } else { // FIXME: unit killed? dx=goal->X; dy=goal->Y; } DebugLevel3("Fire to unit at %d,%d\n",dx,dy); dx=dx*TileSizeX+TileSizeX/2; dy=dy*TileSizeY+TileSizeY/2; } else { dx=unit->Command.Data.Move.DX*TileSizeX+TileSizeX/2; dy=unit->Command.Data.Move.DY*TileSizeY+TileSizeY/2; } missile=MakeMissile(unit->Type->MissileWeapon,x,y,dx,dy); // // Damage of missile // // missile->Damage=CalculateDamage(unit,goal); missile->SourceUnit=unit; missile->SourceType=unit->Type; missile->SourceStats=unit->Stats; missile->SourcePlayer=unit->Player; }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos) { Vec2i newgoalPos = goalPos; // Goal dead? if (goal) { Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range); if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed) { return; } if (goal->CurrentAction() == UnitActionDie) { if (unit.Type->Missile.Missile->AlwaysFire) { newgoalPos = goal->tilePos; goal = NULL; } else { return; } } } // No missile hits immediately! if (unit.Type->Missile.Missile->Class == MissileClassNone) { // No goal, take target coordinates if (!goal) { if (Map.WallOnMap(goalPos)) { if (Map.HumanWallOnMap(goalPos)) { Map.HitWall(goalPos, CalculateDamageStats(*unit.Stats, *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); } else { Map.HitWall(goalPos, CalculateDamageStats(*unit.Stats, *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); } return; } DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } HitUnit(&unit, *goal, CalculateDamage(unit, *goal)); return; } // If Firing from inside a Bunker CUnit *from = GetFirstContainer(unit); const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos) + unit.Type->MissileOffsets[dir][0]; Vec2i dpos; if (goal) { Assert(goal->Type); // Target invalid? // Moved out of attack range? if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit.Container) { NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos); } else { dpos = goal->tilePos + goal->Type->GetHalfTileSize(); } } else { dpos = newgoalPos; // FIXME: Can this be too near?? } PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos); Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos); // // Damage of missile // if (goal) { missile->TargetUnit = goal; } missile->SourceUnit = &unit; }
/* ** Chop the wood. ** Return TRUE if ready, otherwise FALSE. */ local int ChopWood(Unit* unit) { Unit* destu; int flags; extern Animation PeonAttack[]; flags=UnitShowAnimation(unit,PeonAttack); if( (flags&AnimationSound) ) { PlayUnitSound(unit,VoiceTreeChopping); } if( unit->Reset ) { DebugCheck( unit->Wait!=1 ); // // This a work around the bug: "lumber bug" // We give a worker a new command and in the next cycle // the worker is ready chopping. // #if 0 // FIXME: johns+cade: this didn't work with the current code if( unit->NextCommand[0].Action==UnitActionHarvest || unit->NextCommand[0].Action==UnitActionMineGold ) { unit->SubAction=0; return 0; } #endif // // Wood gone while chopping? // if( !ForestOnMap(unit->Command.Data.Move.DX ,unit->Command.Data.Move.DY) ) { if( FindWoodInSight(unit ,&unit->Command.Data.Move.DX ,&unit->Command.Data.Move.DY) ) { unit->Command.Data.Move.Fast=1; unit->Command.Data.Move.Goal=NoUnitP; unit->Command.Data.Move.Range=0; // FIXME: shouldn't it be range=1 ?? DebugCheck( unit->Command.Action!=UnitActionHarvest ); unit->SubAction=0; } else { unit->Command.Action=UnitActionStill; unit->SubAction=0; DebugLevel3("NO-WOOD in sight range\n"); } return 0; } // // Ready chopping wood? // if( !(unit->WoodToHarvest = --unit->Value) ) { // Have wood if( unit->Type->Type==UnitPeon ) { unit->Type=&UnitTypes[UnitPeonWithWood]; } else if( unit->Type->Type==UnitPeasant ) { unit->Type=&UnitTypes[UnitPeasantWithWood]; } else { DebugLevel0("Wrong unit for chopping wood %d\n" ,unit->Type->Type); } // // Update the display. // if( UnitVisible(unit) ) { MustRedraw|=RedrawMap; } if( IsSelected(unit) ) { UpdateBottomPanel(); MustRedraw|=RedrawBottomPanel; } // // Update the map. // MapRemoveWood(unit->Command.Data.Move.DX ,unit->Command.Data.Move.DY); // // Find place to return wood. // unit->Command.Data.Move.SX=unit->X; unit->Command.Data.Move.SY=unit->Y; if( !(destu=FindWoodDeposit(unit->Player,unit->X,unit->Y)) ) { unit->Command.Action=UnitActionStill; unit->SubAction=0; } else { unit->Command.Data.Move.Fast=1; unit->Command.Data.Move.Range=1; unit->Command.Data.Move.Goal=destu; #if 1 // Fast movement need this?? NearestOfUnit(destu,unit->X,unit->Y ,&unit->Command.Data.Move.DX ,&unit->Command.Data.Move.DY); #else unit->Command.Data.Move.DX=destu->X; unit->Command.Data.Move.DY=destu->Y; #endif DebugLevel3("Return to %Zd=%d,%d\n" ,destu-UnitsPool ,unit->Command.Data.Move.DX ,unit->Command.Data.Move.DY); DebugCheck( unit->Command.Action!=UnitActionHarvest ); return 1; } } } return 0; }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos) { Vec2i newgoalPos = goalPos; // Goal dead? if (goal) { Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range); if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed) { return; } if (goal->CurrentAction() == UnitActionDie) { if (unit.Type->Missile.Missile->AlwaysFire) { newgoalPos = goal->tilePos; goal = NULL; } else { return; } } } // No missile hits immediately! //Wyrmgus start // if (unit.Type->Missile.Missile->Class == MissileClassNone) { if (unit.Type->Missile.Missile->Class == MissileClassNone || (unit.Type->Animations && unit.Type->Animations->Attack && unit.Type->Animations->RangedAttack && ((goal && unit.MapDistanceTo(*goal) <= 1) || (!goal && unit.MapDistanceTo(goalPos) <= 1)) && !unit.Container)) { // treat melee attacks from units that have both attack and ranged attack animations as having missile class none //Wyrmgus end //Wyrmgus start int damage = 0; //Wyrmgus end // No goal, take target coordinates if (!goal) { if (Map.WallOnMap(goalPos)) { //Wyrmgus start // if (Map.HumanWallOnMap(goalPos)) { if (Map.HumanWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeHumanWall->Stats, NULL) == true) { //Wyrmgus end //Wyrmgus start PlayUnitSound(unit, VoiceHit); damage = CalculateDamageStats(unit, *UnitTypeHumanWall->Stats, NULL); //Wyrmgus end Map.HitWall(goalPos, //Wyrmgus start // CalculateDamageStats(*unit.Stats, // *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); damage); //Wyrmgus end //Wyrmgus start // } else { } else if (Map.OrcWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeOrcWall->Stats, NULL) == true) { //Wyrmgus end //Wyrmgus start PlayUnitSound(unit, VoiceHit); damage = CalculateDamageStats(unit, *UnitTypeOrcWall->Stats, NULL); //Wyrmgus end Map.HitWall(goalPos, //Wyrmgus start // CalculateDamageStats(*unit.Stats, // *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); damage); //Wyrmgus end } return; } DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } //Wyrmgus start // HitUnit(&unit, *goal, CalculateDamage(unit, *goal, Damage)); if (CalculateHit(unit, *goal->Stats, goal) == true) { damage = CalculateDamage(unit, *goal, Damage); HitUnit(&unit, *goal, damage); PlayUnitSound(unit, VoiceHit); //apply Thorns damage if attacker is at melee range if (goal && goal->Variable[THORNSDAMAGE_INDEX].Value && unit.MapDistanceTo(*goal) <= 1) { int thorns_damage = std::max<int>(goal->Variable[THORNSDAMAGE_INDEX].Value - unit.Variable[ARMOR_INDEX].Value, 1); if (GameSettings.NoRandomness) { thorns_damage -= ((thorns_damage + 2) / 2) / 2; //if no randomness setting is used, then the damage will always return what would have been the average damage with randomness } else { thorns_damage -= SyncRand() % ((thorns_damage + 2) / 2); } HitUnit(goal, unit, thorns_damage); } } else { PlayUnitSound(unit, VoiceMiss); } //Wyrmgus end return; } // If Firing from inside a Bunker CUnit *from = GetFirstContainer(unit); const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos) + unit.Type->MissileOffsets[dir][0]; Vec2i dpos; if (goal) { Assert(goal->Type); // Target invalid? // Moved out of attack range? if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit.Container) { NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos); } else { dpos = goal->tilePos + goal->Type->GetHalfTileSize(); } } else { dpos = newgoalPos; // FIXME: Can this be too near?? } PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos); Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos); // // Damage of missile // if (goal) { missile->TargetUnit = goal; } missile->SourceUnit = &unit; }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit *unit) { int x; int y; int dx; int dy; CUnit *goal; Missile *missile; // // Goal dead? // goal = unit->Orders[0]->Goal; if (goal) { // Better let the caller/action handle this. if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed || goal->Orders[0]->Action == UnitActionDie) { return; } // FIXME: Some missile hit the field of the target and all units on it. // FIXME: goal is already dead, but missile could hit others? } // // No missile hits immediately! // if (unit->Type->Missile.Missile->Class == MissileClassNone) { // No goal, take target coordinates if (!goal) { DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } HitUnit(unit, goal, CalculateDamage(unit, goal)); return; } // If Firing from inside a Bunker if (unit->Container) { x = unit->Container->X * TileSizeX + TileSizeX / 2; // missile starts in tile middle y = unit->Container->Y * TileSizeY + TileSizeY / 2; } else { x = unit->X * TileSizeX + TileSizeX / 2; // missile starts in tile middle y = unit->Y * TileSizeY + TileSizeY / 2; } if (goal) { Assert(goal->Type); // Target invalid? // // Moved out of attack range? // if (MapDistanceBetweenUnits(unit, goal) < unit->Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ MapDistanceBetweenUnits(unit, goal) _C_ unit->Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit->Container) { NearestOfUnit(goal, unit->Container->X, unit->Container->Y, &dx, &dy); } else { NearestOfUnit(goal, unit->X, unit->Y, &dx, &dy); } } else { dx = unit->Orders[0]->X; dy = unit->Orders[0]->Y; // FIXME: Can this be too near?? } // Fire to the tile center of the destination. dx = dx * TileSizeX + TileSizeX / 2; dy = dy * TileSizeY + TileSizeY / 2; missile = MakeMissile(unit->Type->Missile.Missile, x, y, dx, dy); // // Damage of missile // if (goal) { missile->TargetUnit = goal; goal->RefsIncrease(); } missile->SourceUnit = unit; unit->RefsIncrease(); }