From 2a313e217b029d38b14aba1d2c5e77e5666553b4 Mon Sep 17 00:00:00 2001 From: Joris Dauphin <joris.dauphin@gmail.com> Date: Mon, 18 Jul 2011 12:54:15 +0200 Subject: [PATCH] Fix some action with big units (TileSize > (1;1)) Fix regression whan AI should attack with transporter. --- src/action/action_build.cpp | 4 +- src/action/action_die.cpp | 64 ++++---- src/action/action_follow.cpp | 2 +- src/action/action_resource.cpp | 47 +++--- src/action/action_train.cpp | 2 +- src/action/action_unload.cpp | 290 ++++++++++++++------------------- src/ai/ai_force.cpp | 4 +- src/ai/ai_local.h | 1 + src/include/actions.h | 3 +- src/include/unit.h | 6 +- src/pathfinder/astar.cpp | 29 ++-- src/stratagus/spells.cpp | 5 +- src/unit/script_unit.cpp | 20 +-- src/unit/unit.cpp | 103 +++++++----- 14 files changed, 275 insertions(+), 305 deletions(-) diff --git a/src/action/action_build.cpp b/src/action/action_build.cpp index cd89f20ac..55abf07f4 100644 --- a/src/action/action_build.cpp +++ b/src/action/action_build.cpp @@ -500,7 +500,7 @@ void HandleActionBuilt(CUnit &unit) unit.Data.Built.Worker = NoUnitP; // HACK: make sure the sight is updated correctly unit.CurrentSightRange = 1; - DropOutOnSide(*worker, LookingW, type->TileWidth, type->TileHeight); + DropOutOnSide(*worker, LookingW, &unit); unit.CurrentSightRange = 0; } @@ -544,7 +544,7 @@ void HandleActionBuilt(CUnit &unit) worker->SubAction = 0;//may be 40 // HACK: make sure the sight is updated correctly unit.CurrentSightRange = 1; - DropOutOnSide(*worker, LookingW, type->TileWidth, type->TileHeight); + DropOutOnSide(*worker, LookingW, &unit); worker->CurrentOrder()->ClearGoal(); diff --git a/src/action/action_die.cpp b/src/action/action_die.cpp index 4239ff1ff..88d662067 100644 --- a/src/action/action_die.cpp +++ b/src/action/action_die.cpp @@ -55,9 +55,7 @@ */ void HandleActionDie(CUnit &unit) { - // // Show death animation - // if (unit.Type->Animations && unit.Type->Animations->Death[unit.DamagedType]) { UnitShowAnimation(unit, unit.Type->Animations->Death[unit.DamagedType]); } @@ -68,43 +66,41 @@ void HandleActionDie(CUnit &unit) unit.Anim.Unbreakable = 0; } - // + if (unit.Anim.Unbreakable) { + return; + } // Die sequence terminated, generate corpse. - // - if (!unit.Anim.Unbreakable) { - if (!unit.Type->CorpseType) { - // We may be in the cache if we just finished out death animation - // even though there is no corpse. - // (unit.Type->Animations && unit.Type->Animations->Death) - // Remove us from the map to be safe - unit.Remove(NULL); - unit.Release(); - return; - } + if (!unit.Type->CorpseType) { + // We may be in the cache if we just finished out death animation + // even though there is no corpse. + // (unit.Type->Animations && unit.Type->Animations->Death) + // Remove us from the map to be safe + unit.Remove(NULL); + unit.Release(); + return; + } - Assert(unit.Type->TileWidth == unit.Type->CorpseType->TileWidth && - unit.Type->TileHeight == unit.Type->CorpseType->TileHeight); + Assert(unit.Type->TileWidth >= unit.Type->CorpseType->TileWidth && + unit.Type->TileHeight >= unit.Type->CorpseType->TileHeight); - // Update sight for new corpse - // We have to unmark BEFORE changing the type. - // Always do that, since types can have different vision properties. - MapUnmarkUnitGuard(unit); - MapUnmarkUnitSight(unit); - unit.Type = unit.Type->CorpseType; - unit.CurrentSightRange = - unit.Type->Stats[unit.Player->Index].Variables[SIGHTRANGE_INDEX].Max; - MapMarkUnitSight(unit); + // Update sight for new corpse + // We have to unmark BEFORE changing the type. + // Always do that, since types can have different vision properties. - // We must be dead to get here, it we aren't we need to know why - // This assert replaces and old DEBUG message "Reset to die is really needed" - Assert(unit.CurrentAction() == UnitActionDie); + unit.Remove(NULL); + unit.Type = unit.Type->CorpseType; + unit.Stats = &unit.Type->Stats[unit.Player->Index]; + unit.Place(unit.tilePos); - unit.SubAction = 0; - unit.Frame = 0; - UnitUpdateHeading(unit); - if (unit.Type->Animations && unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]) { - UnitShowAnimation(unit, unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]); - } + // We must be dead to get here, it we aren't we need to know why + // This assert replaces and old DEBUG message "Reset to die is really needed" + Assert(unit.CurrentAction() == UnitActionDie); + + unit.SubAction = 0; + unit.Frame = 0; + UnitUpdateHeading(unit); + if (unit.Type->Animations && unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]) { + UnitShowAnimation(unit, unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]); } } diff --git a/src/action/action_follow.cpp b/src/action/action_follow.cpp index bce20592e..0254c8db3 100644 --- a/src/action/action_follow.cpp +++ b/src/action/action_follow.cpp @@ -132,7 +132,7 @@ void HandleActionFollow(CUnit &unit) // Teleport the unit unit.Remove(NULL); unit.tilePos = goal->Goal->tilePos; - DropOutOnSide(unit, unit.Direction, 1, 1); + DropOutOnSide(unit, unit.Direction, NULL); #if 0 // FIXME: SoundForName() should be called once PlayGameSound(SoundForName("invisibility"), MaxSampleVolume); diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp index b22d7485d..a33f52a78 100644 --- a/src/action/action_resource.cpp +++ b/src/action/action_resource.cpp @@ -275,7 +275,7 @@ static void LoseResource(CUnit &unit, const CUnit &source) CUnit *depot; ResourceInfo *resinfo = unit.Type->ResInfo[unit.CurrentResource]; - Assert((unit.Container && !resinfo->HarvestFromOutside) || + Assert((unit.Container == &source && !resinfo->HarvestFromOutside) || (!unit.Container && resinfo->HarvestFromOutside)); if (resinfo->HarvestFromOutside) { @@ -287,8 +287,7 @@ static void LoseResource(CUnit &unit, const CUnit &source) // if (unit.ResourcesHeld && (depot = FindDeposit(unit, 1000, unit.CurrentResource))) { if (unit.Container) { - DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), - source.Type->TileWidth, source.Type->TileHeight); + DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), &source); } // // Remember were it mined, so it can look around for another resource. @@ -306,8 +305,7 @@ static void LoseResource(CUnit &unit, const CUnit &source) // if (unit.Container) { Assert(!resinfo->HarvestFromOutside); - DropOutOnSide(unit, LookingW, source.Type->TileWidth, - source.Type->TileHeight); + DropOutOnSide(unit, LookingW, &source); } unit.CurrentOrder()->goalPos.x = unit.CurrentOrder()->goalPos.y = -1; //use depot as goal @@ -593,8 +591,7 @@ static int StopGathering(CUnit &unit) !unit.ResourcesHeld) { if (!(resinfo->HarvestFromOutside || resinfo->TerrainHarvester)) { Assert(unit.Container); - DropOutOnSide(unit, LookingW, source->Type->TileWidth, - source->Type->TileHeight); + DropOutOnSide(unit, LookingW, source); } DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n" _C_ unit.Player->Index _C_ unit.Slot _C_ unit.CurrentResource); @@ -605,8 +602,7 @@ static int StopGathering(CUnit &unit) } else { if (!(resinfo->HarvestFromOutside || resinfo->TerrainHarvester)) { Assert(unit.Container); - DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), - source->Type->TileWidth, source->Type->TileHeight); + DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source); } UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT); } @@ -733,14 +729,14 @@ static int WaitInDepot(CUnit &unit) pos = unit.CurrentOrder()->Arg1.Resource.Pos; if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 10, unit.Player, pos, &pos)) { - if(depot) - DropOutNearest(unit, pos, depot->Type->TileWidth, - depot->Type->TileHeight); + if (depot) { + DropOutNearest(unit, pos, depot); + } unit.CurrentOrder()->goalPos = pos; } else { - if(depot) - DropOutOnSide(unit, LookingW, depot->Type->TileWidth, - depot->Type->TileHeight); + if (depot) { + DropOutOnSide(unit, LookingW, depot); + } unit.ClearAction(); } } else { @@ -752,12 +748,12 @@ static int WaitInDepot(CUnit &unit) goal = UnitFindResource(unit, pos.x, pos.y, range, unit.CurrentResource, unit.Player->AiEnabled, depot); if (goal) { - if(depot) - DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), - depot->Type->TileWidth, depot->Type->TileHeight); + if (depot) { + DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), depot); + } - if(goal != mine) { - if(mine) { + if (goal != mine) { + if (mine) { unit.DeAssignWorkerFromMine(*mine); mine->RefsDecrease(); } @@ -774,20 +770,17 @@ static int WaitInDepot(CUnit &unit) _C_ unit.Player->Index _C_ unit.Slot _C_ unit.tilePos.x _C_ unit.tilePos.y _C_ pos.x _C_ pos.y _C_ range); - if(depot) - DropOutOnSide(unit, - LookingW, depot->Type->TileWidth, depot->Type->TileHeight); - - if(mine) { + if (depot) { + DropOutOnSide(unit, LookingW, depot); + } + if (mine) { unit.DeAssignWorkerFromMine(*mine); mine->RefsDecrease(); unit.CurrentOrder()->Arg1.Resource.Mine = NULL; } - unit.ClearAction(); } } - return unit.CurrentAction() != UnitActionStill; } diff --git a/src/action/action_train.cpp b/src/action/action_train.cpp index 00a8f2bd1..4c93c2e48 100644 --- a/src/action/action_train.cpp +++ b/src/action/action_train.cpp @@ -161,7 +161,7 @@ void HandleActionTrain(CUnit &unit) AddToGroup(&nunit, 1, num); } - DropOutOnSide(*nunit, LookingW, type->TileWidth, type->TileHeight); + DropOutOnSide(*nunit, LookingW, &unit); // Set life span if (type->DecayRate) { diff --git a/src/action/action_unload.cpp b/src/action/action_unload.cpp index e0e75ff1e..3cfbd5cfc 100644 --- a/src/action/action_unload.cpp +++ b/src/action/action_unload.cpp @@ -49,84 +49,64 @@ -- Functions ----------------------------------------------------------------------------*/ - -// Flag for searching a valid tileset for unloading -#define LandUnitMask ( \ - MapFieldLandUnit | \ - MapFieldBuilding | \ - MapFieldWall | \ - MapFieldRocks | \ - MapFieldForest | \ - MapFieldCoastAllowed | \ - MapFieldWaterAllowed | \ - MapFieldUnpassable) - -#define NavalUnitMask ( \ - MapFieldLandUnit | \ - MapFieldBuilding | \ - MapFieldWall | \ - MapFieldRocks | \ - MapFieldForest | \ - MapFieldCoastAllowed | \ - MapFieldLandAllowed | \ - MapFieldUnpassable) - - /** ** Find a free position close to startPos ** +** @param transporter +** @param unit Unit to unload. ** @param startPos Original search position -** @param res Unload position. -** @param mask Movement mask for the unit to be droped. +** @param maxrange maximal range to unload. +** @param res Unload position. ** ** @return True if a position was found, False otherwise. -** @note resx and resy are undefined if a position is not found. +** @note res is undefined if a position is not found. ** ** @bug FIXME: Place unit only on fields reachable from the transporter -** @bug FIXME: This function fails for units larger than 1x1. */ -static int FindUnloadPosition(const Vec2i startPos, Vec2i *res, int mask) +static bool FindUnloadPosition(const CUnit &transporter, const CUnit &unit, const Vec2i startPos, int maxRange, Vec2i *res) { - int i; - int n; - int addx; - int addy; Vec2i pos = startPos; - addx = addy = 1; + + pos.x -= unit.Type->TileWidth - 1; + pos.y -= unit.Type->TileHeight - 1; + int addx = transporter.Type->TileWidth + unit.Type->TileWidth - 1; + int addy = transporter.Type->TileHeight + unit.Type->TileHeight - 1; + --pos.x; - for (n = 0; n < 2; ++n) { - // Nobody: There was some code here to check for unloading units that can - // only go on even tiles. It's useless, since we can only unload land units. - for (i = addy; i--; ++pos.y) { - if (CheckedCanMoveToMask(pos, mask)) { + for (int range = 0; range < maxRange; ++range) { + for (int i = addy; i--; ++pos.y) { + if (UnitCanBeAt(unit, pos)) { *res = pos; - return 1; + return true; } } ++addx; - for (i = addx; i--; ++pos.x) { - if (CheckedCanMoveToMask(pos, mask)) { + + for (int i = addx; i--; ++pos.x) { + if (UnitCanBeAt(unit, pos)) { *res = pos; - return 1; + return true; } } ++addy; - for (i = addy; i--; --pos.y) { - if (CheckedCanMoveToMask(pos, mask)) { + + for (int i = addy; i--; --pos.y) { + if (UnitCanBeAt(unit, pos)) { *res = pos; - return 1; + return true; } } ++addx; - for (i = addx; i--; --pos.x) { - if (CheckedCanMoveToMask(pos, mask)) { + + for (int i = addx; i--; --pos.x) { + if (UnitCanBeAt(unit, pos)) { *res = pos; - return 1; + return true; } } ++addy; } - return 0; + return false; } /** @@ -138,12 +118,13 @@ static int FindUnloadPosition(const Vec2i startPos, Vec2i *res, int mask) ** ** @bug FIXME: Place unit only on fields reachable from the transporter */ -int UnloadUnit(CUnit &unit) +static int UnloadUnit(CUnit &transporter, CUnit &unit) { + const int maxRange = 1; Vec2i pos; Assert(unit.Removed); - if (!FindUnloadPosition(unit.tilePos, &pos, unit.Type->MovementMask)) { + if (!FindUnloadPosition(transporter, unit, transporter.tilePos, maxRange, &pos)) { return 0; } unit.Boarded = 0; @@ -152,72 +133,80 @@ int UnloadUnit(CUnit &unit) } /** -** Find the closest piece of coast you can unload units on +** Return true is possition is a correct place to drop out units. ** -** @param x start location for the search -** @param y start location for the search -** @param resPos coast position -** -** @return 1 if a location was found, 0 otherwise +** @param transporter Transporter unit. +** @param pos position to drop out units. */ -static int ClosestFreeCoast(const Vec2i &startPos, Vec2i *resPos) +static bool IsDropZonePossible(const CUnit &transporter, const Vec2i &pos) { - int i; - int addx; - int addy; - Vec2i nullpos; - Vec2i pos = startPos; - int n; + const int maxUnloadRange = 1; - addx = addy = 1; - if (Map.CoastOnMap(pos) && - FindUnloadPosition(pos, &nullpos, LandUnitMask)) { - *resPos = pos; - return 1; + if (!UnitCanBeAt(transporter, pos)) { + return false; } - --pos.x; - // The maximum distance to the coast. We have to stop somewhere... - n = 20; - while (n--) { - for (i = addy; i--; ++pos.y) { - if (Map.Info.IsPointOnMap(pos) && - Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) && - FindUnloadPosition(pos, &nullpos, LandUnitMask)) { + Vec2i dummyPos; + CUnit* unit = transporter.UnitInside; + for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) { + if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos)) { + return true; + } + } + // Check unit can be droped from here. + return false; +} + + +/** +** Find the closest available drop zone for a transporter. +** Fail if transporter don't transport any unit.. +** +** @param transporter the transporter +** @param startPos start location for the search +** @param maxRange The maximum distance from initial position to search... +** @param resPos drop zone position +** +** @return true if a location was found, false otherwise +** @note to be called only from ClosestFreeDropZone. +*/ +static bool ClosestFreeDropZone_internal(const CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos) +{ + int addx = 0; + int addy = 1; + Vec2i pos = startPos; + + for (int range = 0; range < maxRange; ++range) { + for (int i = addy; i--; ++pos.y) { + if (IsDropZonePossible(transporter, pos)) { *resPos = pos; - return 1; + return true; } } ++addx; - for (i = addx; i--; ++pos.x) { - if (Map.Info.IsPointOnMap(pos) && - Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) && - FindUnloadPosition(pos, &nullpos, LandUnitMask)) { + for (int i = addx; i--; ++pos.x) { + if (IsDropZonePossible(transporter, pos)) { *resPos = pos; - return 1; + return true; } } ++addy; - for (i = addy; i--; --pos.y) { - if (Map.Info.IsPointOnMap(pos) && - Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) && - FindUnloadPosition(pos, &nullpos, LandUnitMask)) { + for (int i = addy; i--; --pos.y) { + if (IsDropZonePossible(transporter, pos)) { *resPos = pos; - return 1; + return true; } } ++addx; - for (i = addx; i--; --pos.x) { - if (Map.Info.IsPointOnMap(pos) && - Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) && - FindUnloadPosition(pos, &nullpos, LandUnitMask)) { + for (int i = addx; i--; --pos.x) { + if (IsDropZonePossible(transporter, pos)) { *resPos = pos; - return 1; + return true; } } ++addy; } DebugPrint("Try clicking closer to an actual coast.\n"); - return 0; + return false; } /** @@ -226,52 +215,31 @@ static int ClosestFreeCoast(const Vec2i &startPos, Vec2i *resPos) ** ** @param transporter the transporter ** @param startPos start location for the search -** @param resPos coast position +** @param maxRange The maximum distance from initial position to search... +** @param resPos drop zone position ** ** @return 1 if a location was found, 0 otherwise -** */ -static int ClosestFreeDropZone(CUnit &transporter, const Vec2i& startPos, Vec2i *resPos) +static int ClosestFreeDropZone(CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos) { - // Type (land/fly/naval) of the transporter - int transporterType; - // Type (land/fly/naval) of the units to unload - int loadedType; - // Check there are units onboard if (!transporter.UnitInside) { return 0; } + const bool isTransporterRemoved = transporter.Removed; - transporterType = transporter.Type->UnitType; - // Take the type of the onboard unit - loadedType = transporter.UnitInside->Type->UnitType; - - // Don't move in thoses cases - if ((transporterType == loadedType) || (loadedType == UnitTypeFly)) { - *resPos = startPos; - return 1; + if (!isTransporterRemoved) { + // Remove transporter to avoid "collision" with itself. + transporter.Remove(NULL); } - - switch (transporterType) { - case UnitTypeLand: - // in this case, loadedType == UnitTypeSea - return ClosestFreeCoast(startPos, resPos); - case UnitTypeNaval: - // Same ( but reversed... ) - return ClosestFreeCoast(startPos, resPos); - case UnitTypeFly: - // Here we have loadedType in [ UnitTypeLand,UnitTypeNaval ] - if (loadedType == UnitTypeLand) { - return FindUnloadPosition(startPos, resPos, LandUnitMask); - } else { - return FindUnloadPosition(startPos, resPos, NavalUnitMask); - } + const bool res = ClosestFreeDropZone_internal(transporter, startPos, maxRange, resPos); + if (!isTransporterRemoved) { + transporter.Place(transporter.tilePos); } - // Just to avoid a warning - return 0; + return res; } + /** ** Move to dropzone. ** @@ -299,14 +267,10 @@ static int MoveToDropZone(CUnit &unit) ** ** @param unit Pointer to unit. */ -static void LeaveTransporter(CUnit &unit) +static void LeaveTransporter(CUnit &transporter) { - int i; - int stillonboard; - CUnit *goal; - - stillonboard = 0; - goal = unit.CurrentOrder()->GetGoal(); + int stillonboard = 0; + CUnit *goal = transporter.CurrentOrder()->GetGoal(); // // Goal is the specific unit unit that you want to unload. // This can be NULL, in case you want to unload everything. @@ -314,30 +278,30 @@ static void LeaveTransporter(CUnit &unit) if (goal) { if (goal->Destroyed) { DebugPrint("destroyed unit unloading?\n"); - unit.CurrentOrder()->ClearGoal(); + transporter.CurrentOrder()->ClearGoal(); return; } - unit.CurrentOrder()->ClearGoal(); - goal->tilePos = unit.tilePos; + transporter.CurrentOrder()->ClearGoal(); + goal->tilePos = transporter.tilePos; // Try to unload the unit. If it doesn't work there is no problem. - if (UnloadUnit(*goal)) { - unit.BoardCount--; + if (UnloadUnit(transporter, *goal)) { + transporter.BoardCount--; } } else { // Unload all units. - goal = unit.UnitInside; - for (i = unit.InsideCount; i; --i, goal = goal->NextContained) { + goal = transporter.UnitInside; + for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) { if (goal->Boarded) { - goal->tilePos = unit.tilePos; - if (!UnloadUnit(*goal)) { + goal->tilePos = transporter.tilePos; + if (!UnloadUnit(transporter, *goal)) { ++stillonboard; } else { - unit.BoardCount--; + transporter.BoardCount--; } } } } - if (IsOnlySelected(unit)) { + if (IsOnlySelected(transporter)) { SelectedUnitChanged(); } @@ -345,12 +309,12 @@ static void LeaveTransporter(CUnit &unit) if (stillonboard) { // We tell it to unload at it's current position. This can't be done, // so it will search for a piece of free coast nearby. - unit.CurrentOrder()->Action = UnitActionUnload; - unit.CurrentOrder()->ClearGoal(); - unit.CurrentOrder()->goalPos = unit.tilePos; - unit.SubAction = 0; + transporter.CurrentOrder()->Action = UnitActionUnload; + transporter.CurrentOrder()->ClearGoal(); + transporter.CurrentOrder()->goalPos = transporter.tilePos; + transporter.SubAction = 0; } else { - unit.ClearAction(); + transporter.ClearAction(); } } @@ -361,19 +325,17 @@ static void LeaveTransporter(CUnit &unit) */ void HandleActionUnload(CUnit &unit) { - int i; - Vec2i pos; + const int maxSearchRange = 20; if (!unit.CanMove()) { unit.SubAction = 2; } switch (unit.SubAction) { - // - // Move the transporter - // - case 0: + case 0: // Choose destination if (!unit.CurrentOrder()->HasGoal()) { - if (!ClosestFreeDropZone(unit, unit.CurrentOrder()->goalPos, &pos)) { + Vec2i pos; + + if (!ClosestFreeDropZone(unit, unit.CurrentOrder()->goalPos, maxSearchRange, &pos)) { // Sorry... I give up. unit.ClearAction(); return; @@ -383,12 +345,15 @@ void HandleActionUnload(CUnit &unit) NewResetPath(unit); unit.SubAction = 1; - case 1: + // follow on next case + case 1: // Move unit to destination // The Goal is the unit that we have to unload. if (!unit.CurrentOrder()->HasGoal()) { + const int moveResult = MoveToDropZone(unit); + // We have to unload everything - if ((i = MoveToDropZone(unit))) { - if (i == PF_REACHED) { + if (moveResult) { + if (moveResult == PF_REACHED) { if (++unit.SubAction == 1) { unit.ClearAction(); } @@ -398,10 +363,7 @@ void HandleActionUnload(CUnit &unit) } break; } - // - // Leave the transporter - // - case 2: + case 2: // Leave the transporter // FIXME: show still animations ? LeaveTransporter(unit); if (unit.CanMove() && unit.CurrentAction() != UnitActionStill) { diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp index 4b7f61c9f..7725f1e30 100644 --- a/src/ai/ai_force.cpp +++ b/src/ai/ai_force.cpp @@ -616,7 +616,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce) } } if (transporterIndex == aiForce.Size()) { - aiForce.State = AiForceAttackingState_Attacking; + aiForce.State = AiForceAttackingState_AttackingWithTransporter; return ; } for (unsigned int i = 0; i < aiForce.Size(); ++i) { @@ -628,7 +628,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce) } } if (goNext == true) { - aiForce.State = AiForceAttackingState_Attacking; + aiForce.State = AiForceAttackingState_AttackingWithTransporter; return ; } for (unsigned int i = 0; i < aiForce.Size(); ++i) { diff --git a/src/ai/ai_local.h b/src/ai/ai_local.h index f3fdd4172..9340a0c6a 100644 --- a/src/ai/ai_local.h +++ b/src/ai/ai_local.h @@ -98,6 +98,7 @@ enum AiForceAttackingState AiForceAttackingState_Free = -1, AiForceAttackingState_Waiting = 0, AiForceAttackingState_Boarding, + AiForceAttackingState_AttackingWithTransporter, AiForceAttackingState_Attacking, }; diff --git a/src/include/actions.h b/src/include/actions.h index 4e04c7a9d..ceec18c0c 100644 --- a/src/include/actions.h +++ b/src/include/actions.h @@ -208,8 +208,7 @@ extern int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scal extern int UnitShowAnimation(CUnit &unit, const CAnimation *anim); /// Handle the actions of all units each game cycle extern void UnitActions(); - /// Unload a unit. -extern int UnloadUnit(CUnit &unit); + //@} #endif // !__ACTIONS_H__ diff --git a/src/include/unit.h b/src/include/unit.h index ef55a2855..ba0148c84 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -1221,7 +1221,7 @@ struct CResourceDepositFinder { class CPreference { public: CPreference() : ShowSightRange(false), ShowReactionRange(false), - ShowAttackRange(false), ShowMessages(false), + ShowAttackRange(false), ShowMessages(false), BigScreen(false),ShowOrders(0) {}; bool ShowSightRange; /// Show sight range. @@ -1311,9 +1311,9 @@ extern void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta); /// @todo more docu -extern void DropOutOnSide(CUnit &unit, int heading, int addx, int addy); +extern void DropOutOnSide(CUnit &unit, int heading, const CUnit *container); /// @todo more docu -extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy); +extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container); /// Drop out all units in the unit extern void DropOutAll(const CUnit &unit); diff --git a/src/pathfinder/astar.cpp b/src/pathfinder/astar.cpp index cd42a2d22..be90aad72 100644 --- a/src/pathfinder/astar.cpp +++ b/src/pathfinder/astar.cpp @@ -615,12 +615,13 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh, bool goal_reachable = false; const Vec2i goal = {gx, gy}; + const Vec2i extratilesize = {tilesizex - 1, tilesizey - 1}; // top hemi cycle - const int miny = std::max(-maxrange, 0 - goal.y); - for (int offsety = miny; offsety < -minrange; ++offsety) { + const int miny = std::max(-maxrange - extratilesize.y, 0 - goal.y); + for (int offsety = miny; offsety < -minrange - extratilesize.y; ++offsety) { const int offsetx = isqrt(square(maxrange + 1) - square(-offsety) - 1); - const int minx = std::max(0, goal.x - offsetx); + const int minx = std::max(0, goal.x - offsetx - extratilesize.x); const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + offsetx); Vec2i mpos = {minx, goal.y + offsety}; const unsigned int offset = mpos.y * Map.Info.MapWidth; @@ -635,8 +636,8 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh, } if (minrange == 0) { // center - for (int offsety = 0; offsety < gh; ++offsety) { - const int minx = std::max(0, goal.x - maxrange); + for (int offsety = -extratilesize.y; offsety < gh; ++offsety) { + const int minx = std::max(0, goal.x - maxrange - extratilesize.x); const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + maxrange); Vec2i mpos = {minx, goal.y + offsety}; const unsigned int offset = mpos.y * Map.Info.MapWidth; @@ -651,12 +652,12 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh, } } else { // top hemi cycle - const int miny = std::max(-minrange, 0 - goal.y); + const int miny = std::max(-minrange - extratilesize.y, 0 - goal.y); for (int offsety = miny; offsety < 0; ++offsety) { const int offsetx1 = isqrt(square(maxrange + 1) - square(-offsety) - 1); const int offsetx2 = isqrt(square(minrange + 1) - square(-offsety) - 1); - const int minxs[2] = {std::max(0, goal.x - offsetx1), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)}; - const int maxxs[2] = {std::max(0, goal.x - offsetx2), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)}; + const int minxs[2] = {std::max(0, goal.x - offsetx1 - extratilesize.x), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)}; + const int maxxs[2] = {std::max(0, goal.x - offsetx2 - extratilesize.x), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)}; for (int i = 0; i < 2; ++i) { @@ -676,11 +677,11 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh, } // center - const int mincenters[] = {std::max(0, goal.x - maxrange), 0, std::min(Map.Info.MapWidth, goal.x + gw + minrange)}; - const int maxcenters[] = {std::max(0, goal.x - minrange), gw, std::min(Map.Info.MapWidth, goal.x + gw + maxrange)}; + const int mincenters[] = {std::max(0, goal.x - maxrange - extratilesize.x), -extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + minrange)}; + const int maxcenters[] = {std::max(0, goal.x - minrange - extratilesize.x), gw, std::min(Map.Info.MapWidth, goal.x + gw + maxrange)}; for (int i = 0; i < 3; ++i) { - for (int offsety = 0; offsety < gh; ++offsety) { + for (int offsety = -extratilesize.y; offsety < gh; ++offsety) { const int minx = mincenters[i]; const int maxx = maxcenters[i]; Vec2i mpos = {minx, goal.y + offsety}; @@ -701,8 +702,8 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh, for (int offsety = 0; offsety < maxy; ++offsety) { const int offsetx1 = isqrt(square(maxrange + 1) - square(offsety) - 1); const int offsetx2 = isqrt(square(minrange + 1) - square(offsety) - 1); - const int minxs[2] = {std::max(0, goal.x - offsetx1), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)}; - const int maxxs[2] = {std::max(0, goal.x - offsetx2), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)}; + const int minxs[2] = {std::max(0, goal.x - offsetx1) - extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)}; + const int maxxs[2] = {std::max(0, goal.x - offsetx2) - extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)}; for (int i = 0; i < 2; ++i) { @@ -726,7 +727,7 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh, const int maxy = std::min(maxrange, Map.Info.MapHeight - goal.y - gh); for (int offsety = minrange; offsety < maxy; ++offsety) { const int offsetx = isqrt(square(maxrange + 1) - square(offsety) - 1); - const int minx = std::max(0, goal.x - offsetx); + const int minx = std::max(0, goal.x - offsetx - extratilesize.x); const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + offsetx); Vec2i mpos = {minx, goal.y + gh + offsety}; const unsigned int offset = mpos.y * Map.Info.MapWidth; diff --git a/src/stratagus/spells.cpp b/src/stratagus/spells.cpp index 4790a60c2..295dd7b0c 100644 --- a/src/stratagus/spells.cpp +++ b/src/stratagus/spells.cpp @@ -676,10 +676,9 @@ int Summon::Cast(CUnit &caster, const SpellType *spell, // target = MakeUnit(unittype, caster.Player); if (target != NoUnitP) { - // This is a hack to walk around behaviour of DropOutOnSide - target->tilePos.x = x + 1; + target->tilePos.x = x; target->tilePos.y = y; - DropOutOnSide(*target, LookingW, 0, 0); // FIXME : 0,0) : good parameter ? + DropOutOnSide(*target, LookingW, NULL); // // set life span. ttl=0 results in a permanent unit. // diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp index 9c691fe5d..82a8dfb7b 100644 --- a/src/unit/script_unit.cpp +++ b/src/unit/script_unit.cpp @@ -940,14 +940,12 @@ static int CclUnit(lua_State *l) */ static int CclMoveUnit(lua_State *l) { - CUnit *unit; - int heading; Vec2i ipos; LuaCheckArgs(l, 2); lua_pushvalue(l, 1); - unit = CclGetUnit(l); + CUnit *unit = CclGetUnit(l); lua_pop(l, 1); lua_rawgeti(l, 2, 1); @@ -957,14 +955,14 @@ static int CclMoveUnit(lua_State *l) ipos.y = LuaToNumber(l, -1); lua_pop(l, 1); - heading = SyncRand() % 256; if (UnitCanBeAt(*unit, ipos)) { unit->Place(ipos); } else { + const int heading = SyncRand() % 256; + unit->tilePos = ipos; - DropOutOnSide(*unit, heading, 1, 1); + DropOutOnSide(*unit, heading, NULL); } -// PlaceUnit(unit, ipos.x, ipos.y); lua_pushvalue(l, 1); return 1; } @@ -980,7 +978,6 @@ static int CclCreateUnit(lua_State *l) { CUnitType *unittype; CUnit *unit; - int heading; int playerno; Vec2i ipos; @@ -999,7 +996,6 @@ static int CclCreateUnit(lua_State *l) ipos.y = LuaToNumber(l, -1); lua_pop(l, 1); - heading = SyncRand() % 256; lua_pushvalue(l, 2); playerno = TriggerGetPlayer(l); lua_pop(l, 1); @@ -1023,8 +1019,10 @@ static int CclCreateUnit(lua_State *l) (unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) { unit->Place(ipos); } else { + const int heading = SyncRand() % 256; + unit->tilePos = ipos; - DropOutOnSide(*unit, heading, 1, 1); + DropOutOnSide(*unit, heading, NULL); } UpdateForNewUnit(*unit, 0); @@ -1344,9 +1342,9 @@ static int CclSetUnitVariable(lua_State *l) if (!strcmp(name, "RegenerationRate")) { value = LuaToNumber(l, 3); - if (value > unit->Variable[HP_INDEX].Max) + if (value > unit->Variable[HP_INDEX].Max) unit->Stats->Variables[HP_INDEX].Increase = unit->Variable[HP_INDEX].Max; - else + else unit->Stats->Variables[HP_INDEX].Increase = value; } else diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 6aedda7e4..fe3290349 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -634,6 +634,10 @@ void MarkUnitFieldFlags(const CUnit &unit) int w, h = unit.Type->TileHeight; // Tile height of the unit. const int width = unit.Type->TileWidth; // Tile width of the unit. unsigned int index = unit.Offset; + + if (unit.Type->Vanishes) { + return ; + } do { mf = Map.Field(index); w = width; @@ -671,6 +675,10 @@ void UnmarkUnitFieldFlags(const CUnit &unit) const int width = unit.Type->TileWidth; // Tile width of the unit. unsigned int index = unit.Offset; + if (unit.Type->Vanishes) { + return ; + } + _UnmarkUnitFieldFlags funct(unit); do { @@ -1814,62 +1822,74 @@ void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta) ** ** @param unit Unit to drop out. ** @param heading Direction in which the unit should appear. -** @param addx Tile width of unit it's dropping out of. -** @param addy Tile height of unit it's dropping out of. +** @param container Unit "containing" unit to drop (may be different of unit.Container). */ -void DropOutOnSide(CUnit &unit, int heading, int addx, int addy) +void DropOutOnSide(CUnit &unit, int heading, const CUnit *container) { Vec2i pos; - int i; + int addx = 0; + int addy = 0; - if (unit.Container) { - pos = unit.Container->tilePos; + if (container) { + pos = container->tilePos; + pos.x -= unit.Type->TileWidth - 1; + pos.y -= unit.Type->TileHeight - 1; + addx = container->Type->TileWidth + unit.Type->TileWidth - 1; + addy = container->Type->TileHeight + unit.Type->TileHeight - 1; + + if (heading < LookingNE || heading > LookingNW) { + pos.x += addx - 1; + --pos.y; + goto startn; + } else if (heading < LookingSE) { + pos.x += addx; + pos.y += addy - 1; + goto starte; + } else if (heading < LookingSW) { + pos.y += addy; + goto starts; + } else { + --pos.x; + goto startw; + } } else { pos = unit.tilePos; - } - if (heading < LookingNE || heading > LookingNW) { - pos.x += addx - 1; - --pos.y; - goto startn; + if (heading < LookingNE || heading > LookingNW) { + goto starts; + } else if (heading < LookingSE) { + goto startw; + } else if (heading < LookingSW) { + goto startn; + } else { + goto starte; + } } - if (heading < LookingSE) { - pos.x += addx; - pos.y += addy - 1; - goto starte; - } - if (heading < LookingSW) { - pos.y += addy; - goto starts; - } - --pos.x; - goto startw; - // FIXME: don't search outside of the map for (;;) { startw: - for (i = addy; i--; ++pos.y) { + for (int i = addy; i--; ++pos.y) { if (UnitCanBeAt(unit, pos)) { goto found; } } ++addx; starts: - for (i = addx; i--; ++pos.x) { + for (int i = addx; i--; ++pos.x) { if (UnitCanBeAt(unit, pos)) { goto found; } } ++addy; starte: - for (i = addy; i--; --pos.y) { + for (int i = addy; i--; --pos.y) { if (UnitCanBeAt(unit, pos)) { goto found; } } ++addx; startn: - for (i = addx; i--; --pos.x) { + for (int i = addx; i--; --pos.x) { if (UnitCanBeAt(unit, pos)) { goto found; } @@ -1882,29 +1902,34 @@ found: } /** -** Place a unit on the map nearest to x, y. +** Place a unit on the map nearest to goalPos. ** ** @param unit Unit to drop out. ** @param goalPos Goal map tile position. ** @param addx Tile width of unit it's dropping out of. ** @param addy Tile height of unit it's dropping out of. */ -void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy) +void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container) { Vec2i pos; Vec2i bestPos = {0, 0}; int bestd = 99999; - + int addx = 0; + int addy = 0; Assert(unit.Removed); - if (unit.Container) { - pos = unit.Container->tilePos; + if (container) { + pos = container->tilePos; + pos.x -= unit.Type->TileWidth - 1; + pos.y -= unit.Type->TileHeight - 1; + addx = container->Type->TileWidth + unit.Type->TileWidth - 1; + addy = container->Type->TileHeight + unit.Type->TileHeight - 1; + --pos.x; } else { pos = unit.tilePos; } - // FIXME: if we reach the map borders we can go fast up, left, ... - --pos.x; + for (;;) { for (int i = addy; i--; ++pos.y) { // go down if (UnitCanBeAt(unit, pos)) { @@ -1964,18 +1989,14 @@ void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy) */ void DropOutAll(const CUnit &source) { - CUnit *unit; - int i; + CUnit *unit = source.UnitInside; - unit = source.UnitInside; - for (i = source.InsideCount; i; --i, unit = unit->NextContained) { - DropOutOnSide(*unit, LookingW, - source.Type->TileWidth, source.Type->TileHeight); + for (int i = source.InsideCount; i; --i, unit = unit->NextContained) { + DropOutOnSide(*unit, LookingW, &source); Assert(!unit->CurrentOrder()->HasGoal()); unit->CurrentOrder()->Action = UnitActionStill; unit->SubAction = 0; } - DebugPrint("Drop out %d of %d\n" _C_ i _C_ source.Data.Resource.Active); }