Merge pull request #385 from Wargus/tim/desync-debugging
desync debugging
This commit is contained in:
commit
e75958d2ef
15 changed files with 249 additions and 79 deletions
.vscode
src
action
ai
include
map
network
pathfinder
stratagus
unit
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
|
@ -14,7 +14,7 @@
|
|||
"windows": {
|
||||
"type": "cppvsdbg",
|
||||
"program": "${workspaceFolder}/build/Debug/stratagus-dbg.exe",
|
||||
"args": ["-W", "-p", "-a", "-d", "${workspaceFolder}\\..\\data.${input:game}"],
|
||||
"args": ["-W", "-i", "-p", "-P", "${input:port}", "-a", "-d", "${workspaceFolder}\\..\\data.${input:game}"],
|
||||
"environment": [
|
||||
{"name": "PATH", "value": "${workspaceFolder}\\..\\dependencies\\bin;${env:PATH}"},
|
||||
{"name": "OMP_WAIT_POLICY", "value": "passive"}
|
||||
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/../${input:game}",
|
||||
}
|
||||
},
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
|
@ -37,5 +37,11 @@
|
|||
],
|
||||
"default": "wargus"
|
||||
},
|
||||
{
|
||||
"type": "promptString",
|
||||
"id": "port",
|
||||
"description": "Network port?",
|
||||
"default": "6600"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -221,6 +221,8 @@ COrder_Resource::~COrder_Resource()
|
|||
worker->DeAssignWorkerFromMine(*mine);
|
||||
}
|
||||
|
||||
Depot = NULL;
|
||||
|
||||
CUnit *goal = this->GetGoal();
|
||||
if (goal) {
|
||||
// If mining decrease the active count on the resource.
|
||||
|
@ -908,10 +910,19 @@ int COrder_Resource::StopGathering(CUnit &unit)
|
|||
|
||||
// Find and send to resource deposit.
|
||||
CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
|
||||
// There's a bug in the traversal that leads to workers "sometimes" not finding their way to the old depot.
|
||||
// timfel: of course, maybe it's actually nice that workers drop out towards their last depot...
|
||||
if (!depot && (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) && Depot && Depot->IsAlive()) {
|
||||
CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
|
||||
Assert(unit.Container);
|
||||
DropOutNearest(unit, Depot->tilePos + Depot->Type->GetHalfTileSize(), source);
|
||||
}
|
||||
Depot = depot;
|
||||
if (!depot || !unit.ResourcesHeld || this->Finished) {
|
||||
if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
|
||||
Assert(unit.Container);
|
||||
DropOutOnSide(unit, LookingW, source);
|
||||
if (unit.Container) {
|
||||
DropOutOnSide(unit, LookingW, source);
|
||||
}
|
||||
}
|
||||
CUnit *mine = this->Resource.Mine;
|
||||
|
||||
|
@ -921,13 +932,15 @@ int COrder_Resource::StopGathering(CUnit &unit)
|
|||
}
|
||||
|
||||
DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n"
|
||||
_C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.CurrentResource);
|
||||
_C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.CurrentResource);
|
||||
this->Finished = true;
|
||||
return 0;
|
||||
} else {
|
||||
if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
|
||||
Assert(unit.Container);
|
||||
DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source);
|
||||
if (unit.Container) {
|
||||
// may have dropped out above
|
||||
DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source);
|
||||
}
|
||||
}
|
||||
UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,14 @@
|
|||
#include "unit_manager.h"
|
||||
#include "unittype.h"
|
||||
|
||||
#ifdef USE_STACKTRACE
|
||||
#include <stdexcept>
|
||||
#include <stacktrace/call_stack.hpp>
|
||||
#include <stacktrace/stack_exception.hpp>
|
||||
#else
|
||||
#include "st_backtrace.h"
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Variables
|
||||
----------------------------------------------------------------------------*/
|
||||
|
@ -483,11 +491,38 @@ static void DumpUnitInfo(CUnit &unit)
|
|||
}
|
||||
|
||||
fprintf(logf, "%lu: ", GameCycle);
|
||||
fprintf(logf, "%d %s %d P%d Refs %d: %X %d,%d %d,%d\n",
|
||||
|
||||
const char *currentAction;
|
||||
switch (!unit.Orders.empty() ? unit.CurrentAction() : -1) {
|
||||
case -1: currentAction = "No Orders"; break;
|
||||
case UnitActionNone: currentAction = "None"; break;
|
||||
case UnitActionStill: currentAction = "Still"; break;
|
||||
case UnitActionStandGround: currentAction = "StandGround"; break;
|
||||
case UnitActionFollow: currentAction = "Follow"; break;
|
||||
case UnitActionDefend: currentAction = "Defend"; break;
|
||||
case UnitActionMove: currentAction = "Move"; break;
|
||||
case UnitActionAttack: currentAction = "Attack"; break;
|
||||
case UnitActionAttackGround: currentAction = "AttackGround"; break;
|
||||
case UnitActionDie: currentAction = "Die"; break;
|
||||
case UnitActionSpellCast: currentAction = "SpellCast"; break;
|
||||
case UnitActionTrain: currentAction = "Train"; break;
|
||||
case UnitActionUpgradeTo: currentAction = "UpgradeTo"; break;
|
||||
case UnitActionResearch: currentAction = "Research"; break;
|
||||
case UnitActionBuilt: currentAction = "Built"; break;
|
||||
case UnitActionBoard: currentAction = "Board"; break;
|
||||
case UnitActionUnload: currentAction = "Unload"; break;
|
||||
case UnitActionPatrol: currentAction = "Patrol"; break;
|
||||
case UnitActionBuild: currentAction = "Build"; break;
|
||||
case UnitActionExplore: currentAction = "Explore"; break;
|
||||
case UnitActionRepair: currentAction = "Repair"; break;
|
||||
case UnitActionResource: currentAction = "Resource"; break;
|
||||
case UnitActionTransformInto: currentAction = "TransformInto"; break;
|
||||
}
|
||||
|
||||
fprintf(logf, "%d %s %s P%d Refs %d: Seed %X Hash %X %d@%d %d@%d\n",
|
||||
UnitNumber(unit), unit.Type ? unit.Type->Ident.c_str() : "unit-killed",
|
||||
!unit.Orders.empty() ? unit.CurrentAction() : -1,
|
||||
unit.Player ? unit.Player->Index : -1, unit.Refs, SyncRandSeed,
|
||||
unit.tilePos.x, unit.tilePos.y, unit.IX, unit.IY);
|
||||
currentAction, unit.Player ? unit.Player->Index : -1, unit.Refs,
|
||||
SyncRandSeed, SyncHash, unit.tilePos.x, unit.tilePos.y, unit.IX, unit.IY);
|
||||
#if 0
|
||||
SaveUnit(unit, logf);
|
||||
#endif
|
||||
|
@ -532,10 +567,18 @@ static void UnitActionsEachCycle(UNITP_ITERATOR begin, UNITP_ITERATOR end)
|
|||
if (EnableUnitDebug) {
|
||||
DumpUnitInfo(unit);
|
||||
}
|
||||
|
||||
// Calculate some hash.
|
||||
SyncHash = (SyncHash << 5) | (SyncHash >> 27);
|
||||
SyncHash ^= unit.Orders.empty() == false ? unit.CurrentAction() << 18 : 0;
|
||||
SyncHash ^= unit.Refs << 3;
|
||||
|
||||
if (EnableUnitDebug) {
|
||||
fprintf(stderr, "GameCycle: %lud, new SyncHash: %x (unit: %d:%s, order: %d, refs: %d)\n", GameCycle, SyncHash,
|
||||
UnitNumber(unit), unit.Type->Ident.c_str(), unit.Orders.empty() ? -1 : unit.CurrentAction(), unit.Refs);
|
||||
print_backtrace(8);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -926,7 +926,7 @@ void AiCanNotMove(CUnit &unit)
|
|||
const int gh = unit.pathFinderData->input.GetGoalSize().y;
|
||||
|
||||
AiPlayer = unit.Player->Ai;
|
||||
if (PlaceReachable(unit, goalPos, gw, gh, 0, 255)) {
|
||||
if (PlaceReachable(unit, goalPos, gw, gh, 0, 255, false)) {
|
||||
// Path probably closed by unit here
|
||||
AiMoveUnitInTheWay(unit);
|
||||
}
|
||||
|
|
|
@ -219,13 +219,13 @@ extern void FreePathfinder();
|
|||
/// Returns the next element of the path
|
||||
extern int NextPathElement(CUnit &unit, short int *xdp, short int *ydp);
|
||||
/// Return path length to unit 'dst'.
|
||||
extern int UnitReachable(const CUnit &src, const CUnit &dst, int range);
|
||||
extern int UnitReachable(const CUnit &src, const CUnit &dst, int range, bool from_outside_container);
|
||||
/// Return path length to unit 'dst' or error code.
|
||||
extern int CalcPathLengthToUnit(const CUnit &src, const CUnit &dst,
|
||||
const int minrange, const int range);
|
||||
/// Can the unit 'src' reach the place x,y
|
||||
extern int PlaceReachable(const CUnit &src, const Vec2i &pos, int w, int h,
|
||||
int minrange, int maxrange);
|
||||
int minrange, int maxrange, bool from_outside_container);
|
||||
|
||||
//
|
||||
// in astar.cpp
|
||||
|
|
|
@ -16,13 +16,12 @@ inline void print_backtrace(int sz = 100) {
|
|||
|
||||
#elif defined(USE_WIN32)
|
||||
|
||||
#if 1 // the below would mean we give up XP support
|
||||
#include "windows.h"
|
||||
#include "winbase.h"
|
||||
#include "dbghelp.h"
|
||||
#include "process.h"
|
||||
|
||||
inline void print_backtrace(void) {
|
||||
inline void print_backtrace(int sz = 100) {
|
||||
unsigned int i;
|
||||
void *stack[100];
|
||||
unsigned short frames;
|
||||
|
@ -34,7 +33,7 @@ inline void print_backtrace(void) {
|
|||
|
||||
process = GetCurrentProcess();
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
frames = CaptureStackBackTrace(0, 100, stack, NULL);
|
||||
frames = CaptureStackBackTrace(0, sz, stack, NULL);
|
||||
fprintf(stderr, "backtrace returned %d addresses\n", frames);
|
||||
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
|
||||
symbol->MaxNameLen = 1024;
|
||||
|
@ -58,17 +57,12 @@ inline void print_backtrace(void) {
|
|||
}
|
||||
free(symbol);
|
||||
}
|
||||
#else
|
||||
inline void print_backtrace(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
inline void print_backtrace(void) {
|
||||
inline void print_backtrace(int sz = 100) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -227,6 +227,9 @@ public:
|
|||
unsigned char getCost() const { return cost; }
|
||||
unsigned int getFlag() const { return Flags; }
|
||||
void setGraphicTile(unsigned int tile) { this->tile = tile; }
|
||||
#ifdef DEBUG
|
||||
int64_t lastAStarCost; /// debugging pathfinder
|
||||
#endif
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
unsigned int tilesetTile; /// tileset tile number
|
||||
|
|
|
@ -286,6 +286,38 @@ void CViewport::DrawMapBackgroundInViewport() const
|
|||
tile = mf.playerInfo.SeenTile;
|
||||
}
|
||||
Map.TileGraphic->DrawFrameClip(tile, dx, dy);
|
||||
#if 0
|
||||
int64_t cost = mf.lastAStarCost;
|
||||
int32_t alpha;
|
||||
// we use the msb as marker, but only consider the lower 32-bits as numeric value
|
||||
if (cost != 0) {
|
||||
if (cost == -1) {
|
||||
// non traversible tiles always start full red
|
||||
alpha = -60;
|
||||
} else if (cost > 0) {
|
||||
// msb not set means this has not been scaled
|
||||
// scale cost to be between 1 and 60
|
||||
cost <<= 3;
|
||||
if (cost > 60) {
|
||||
cost = 60;
|
||||
}
|
||||
alpha = static_cast<int32_t>(cost);
|
||||
} else {
|
||||
// consider only low 32-bits of already scaled value
|
||||
alpha = static_cast<int32_t>(cost);
|
||||
}
|
||||
}
|
||||
if (alpha > 0) {
|
||||
Video.FillTransRectangleClip(ColorGreen, dx, dy,
|
||||
dx + Map.TileGraphic->getWidth(), dy + dx + Map.TileGraphic->getWidth(), alpha * 200 / 60);
|
||||
alpha--;
|
||||
} else if (alpha < 0) {
|
||||
Video.FillTransRectangleClip(ColorRed, dx, dy,
|
||||
dx + Map.TileGraphic->getWidth(), dy + dx + Map.TileGraphic->getWidth(), -alpha * 200 / 60);
|
||||
alpha++;
|
||||
}
|
||||
const_cast<CMapField &>(mf).lastAStarCost = alpha | ((uint64_t)1 << 63);
|
||||
#endif
|
||||
++sx;
|
||||
dx += PixelTileSize.x;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,13 @@
|
|||
#include "video.h"
|
||||
#include "../video/intern_video.h"
|
||||
|
||||
#ifdef USE_STACKTRACE
|
||||
#include <stdexcept>
|
||||
#include <stacktrace/call_stack.hpp>
|
||||
#include <stacktrace/stack_exception.hpp>
|
||||
#else
|
||||
#include "st_backtrace.h"
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Variables
|
||||
|
@ -205,6 +212,7 @@ void MapMarkTileSight(const CPlayer &player, const unsigned int index)
|
|||
{
|
||||
CMapField &mf = *Map.Field(index);
|
||||
unsigned short *v = &(mf.playerInfo.Visible[player.Index]);
|
||||
|
||||
if (*v == 0 || *v == 1) { // Unexplored or unseen
|
||||
// When there is no fog only unexplored tiles are marked.
|
||||
if (!Map.NoFogOfWar || *v == 0) {
|
||||
|
@ -214,10 +222,25 @@ void MapMarkTileSight(const CPlayer &player, const unsigned int index)
|
|||
if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
|
||||
Map.MarkSeenTile(mf);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
Assert(*v != 65535);
|
||||
++*v;
|
||||
}
|
||||
Assert(*v != 65535);
|
||||
++*v;
|
||||
#if 0
|
||||
if (EnableDebugPrint) {
|
||||
fprintf(stderr, "Mapsight: GameCycle: %lud, SyncHash before: %x", GameCycle, SyncHash);
|
||||
}
|
||||
// Calculate some hash.
|
||||
SyncHash = (SyncHash << 5) | (SyncHash >> 27);
|
||||
SyncHash ^= (*v << 16) | *v;
|
||||
|
||||
if (EnableDebugPrint) {
|
||||
fprintf(stderr, ", after: %x (mapfield: %d, player: %d, sight: %d)\n", SyncHash,
|
||||
index, player.Index, *v);
|
||||
print_backtrace(8);
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapMarkTileSight(const CPlayer &player, const Vec2i &pos)
|
||||
|
|
|
@ -250,6 +250,8 @@
|
|||
// Declaration
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
extern int SaveGame(const std::string &filename); /// Save game
|
||||
|
||||
/**
|
||||
** Network command input/output queue.
|
||||
*/
|
||||
|
@ -298,8 +300,8 @@ CUDPSocket NetworkFildes; /// Network file descriptor
|
|||
static unsigned long NetworkLastFrame[PlayerMax]; /// Last frame received packet
|
||||
static unsigned long NetworkLastCycle[PlayerMax]; /// Last cycle received packet
|
||||
|
||||
static int NetworkSyncSeeds[256]; /// Network sync seeds.
|
||||
static int NetworkSyncHashs[256]; /// Network sync hashs.
|
||||
static unsigned int NetworkSyncSeeds[256]; /// Network sync seeds.
|
||||
static unsigned int NetworkSyncHashs[256]; /// Network sync hashs.
|
||||
static CNetworkCommandQueue NetworkIn[256][PlayerMax][MaxNetworkCommands]; /// Per-player network packet input queue
|
||||
static std::deque<CNetworkCommandQueue> CommandsIn; /// Network command input queue
|
||||
static std::deque<CNetworkCommandQueue> MsgCommandsIn; /// Network message input queue
|
||||
|
@ -917,8 +919,8 @@ static void NetworkExecCommand_Sync(const CNetworkCommandQueue &ncq)
|
|||
CNetworkCommandSync nc;
|
||||
nc.Deserialize(&ncq.Data[0]);
|
||||
const unsigned long gameNetCycle = GameCycle;
|
||||
const int syncSeed = nc.syncSeed;
|
||||
const int syncHash = nc.syncHash;
|
||||
const unsigned int syncSeed = nc.syncSeed;
|
||||
const unsigned int syncHash = nc.syncHash;
|
||||
|
||||
if (syncSeed != NetworkSyncSeeds[gameNetCycle & 0xFF]
|
||||
|| syncHash != NetworkSyncHashs[gameNetCycle & 0xFF]) {
|
||||
|
@ -929,10 +931,20 @@ static void NetworkExecCommand_Sync(const CNetworkCommandQueue &ncq)
|
|||
// only print this message circa every 5 seconds...
|
||||
SetMessage("%s", _("Network out of sync"));
|
||||
gameInSync = false;
|
||||
SetGamePaused(true);
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
std::string savefile = "desync_savegame_";
|
||||
savefile += std::to_string(ThisPlayer->Index);
|
||||
savefile += "_";
|
||||
savefile += std::to_string((intmax_t)now);
|
||||
savefile += ".sav";
|
||||
SaveGame(savefile);
|
||||
}
|
||||
DebugPrint("\nNetwork out of sync %x!=%x! %d!=%d! Cycle %lu\n\n" _C_
|
||||
DebugPrint("\nNetwork out of sync seed: %X!=%X , hash: %X!=%X Cycle %lu\n\n" _C_
|
||||
syncSeed _C_ NetworkSyncSeeds[gameNetCycle & 0xFF] _C_
|
||||
syncHash _C_ NetworkSyncHashs[gameNetCycle & 0xFF] _C_ GameCycle);
|
||||
syncHash _C_ NetworkSyncHashs[gameNetCycle & 0xFF] _C_ GameCycle);
|
||||
} else {
|
||||
gameInSync = true;
|
||||
}
|
||||
|
|
|
@ -534,12 +534,18 @@ static int CostMoveToCallBack_Default(unsigned int index, const CUnit &unit)
|
|||
if (flag && (AStarKnowUnseenTerrain || mf->playerInfo.IsExplored(*unit.Player))) {
|
||||
if (flag & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)) {
|
||||
// we can't cross fixed units and other unpassable things
|
||||
#ifdef DEBUG
|
||||
const_cast<CMapField *>(mf)->lastAStarCost = -1;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
CUnit *goal = mf->UnitCache.find(unit_finder);
|
||||
if (!goal) {
|
||||
// Shouldn't happen, mask says there is something on this tile
|
||||
Assert(0);
|
||||
#ifdef DEBUG
|
||||
const_cast<CMapField *>(mf)->lastAStarCost = -1;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if (goal->Moving) {
|
||||
|
@ -549,6 +555,9 @@ static int CostMoveToCallBack_Default(unsigned int index, const CUnit &unit)
|
|||
// for non moving unit Always Fail unless goal is unit, or unit can attack the target
|
||||
if (&unit != goal) {
|
||||
if (GetAStarFixedEnemyUnitsUnpassable() == true) {
|
||||
#ifdef DEBUG
|
||||
const_cast<CMapField *>(mf)->lastAStarCost = -1;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if (goal->Player->IsEnemy(unit) && unit.IsAgressive() && CanTarget(*unit.Type, *goal->Type)
|
||||
|
@ -556,6 +565,9 @@ static int CostMoveToCallBack_Default(unsigned int index, const CUnit &unit)
|
|||
cost += 2 * AStarMovingUnitCrossingCost;
|
||||
} else {
|
||||
// FIXME: Need support for moving a fixed unit to add cost
|
||||
#ifdef DEBUG
|
||||
const_cast<CMapField *>(mf)->lastAStarCost = -1;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
//cost += AStarFixedUnitCrossingCost;
|
||||
|
@ -569,6 +581,9 @@ static int CostMoveToCallBack_Default(unsigned int index, const CUnit &unit)
|
|||
}
|
||||
// Add tile movement cost
|
||||
cost += mf->getCost();
|
||||
#ifdef DEBUG
|
||||
const_cast<CMapField *>(mf)->lastAStarCost = cost;
|
||||
#endif
|
||||
++mf;
|
||||
} while (--i);
|
||||
index += AStarMapWidth;
|
||||
|
|
|
@ -183,33 +183,68 @@ void FreePathfinder()
|
|||
**
|
||||
** @return Distance to place.
|
||||
*/
|
||||
int PlaceReachable(const CUnit &src, const Vec2i &goalPos, int w, int h, int minrange, int range)
|
||||
int PlaceReachable(const CUnit &src, const Vec2i &goalPos, int w, int h, int minrange, int range, bool from_outside_container)
|
||||
{
|
||||
SetAStarFixedEnemyUnitsUnpassable(true); /// change Path Finder setting to don't count tiles with enemy units as passable
|
||||
int i = AStarFindPath(src.tilePos, goalPos, w, h,
|
||||
src.Type->TileWidth, src.Type->TileHeight,
|
||||
minrange, range, NULL, 0, src);
|
||||
int i;
|
||||
Vec2i srcTilePos = src.tilePos;
|
||||
int srcTW = src.Type->TileWidth;
|
||||
int srcTH = src.Type->TileHeight;
|
||||
if (!from_outside_container || !src.Container) {
|
||||
i = AStarFindPath(srcTilePos, goalPos, w, h,
|
||||
srcTW, srcTH,
|
||||
minrange, range, nullptr, 0, src);
|
||||
} else {
|
||||
const CUnit *first_container = GetFirstContainer(src);
|
||||
|
||||
const Vec2i offset(1, 1);
|
||||
int containerW = first_container->Type->TileWidth;
|
||||
int containerH = first_container->Type->TileHeight;
|
||||
Vec2i containerTilePos = first_container->tilePos;
|
||||
// check top and bottom rows and left and right columns around the container
|
||||
for (int x = -1; x <= containerW; x++) {
|
||||
for (int y = -1; y <= containerH; y++) {
|
||||
if (x >= 0 && x < containerW && y >= 0 && y < containerH) {
|
||||
// inside the container, no need to check
|
||||
continue;
|
||||
}
|
||||
Vec2i tile_pos = containerTilePos + Vec2i(x, y);
|
||||
if (!Map.Info.IsPointOnMap(tile_pos)) {
|
||||
continue;
|
||||
}
|
||||
if (!CanMoveToMask(tile_pos, src.Type->MovementMask)) {
|
||||
//ignore tiles to which the unit cannot be dropped from its container
|
||||
continue;
|
||||
}
|
||||
|
||||
i = AStarFindPath(tile_pos, goalPos, w, h,
|
||||
srcTW, srcTH,
|
||||
minrange, range, nullptr, 0, src);
|
||||
|
||||
switch (i) {
|
||||
case PF_FAILED:
|
||||
case PF_UNREACHABLE:
|
||||
case PF_WAIT:
|
||||
continue;
|
||||
}
|
||||
goto finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
finished:
|
||||
SetAStarFixedEnemyUnitsUnpassable(false); /// restore Path Finder setting
|
||||
switch (i) {
|
||||
case PF_FAILED:
|
||||
case PF_UNREACHABLE:
|
||||
i = 0;
|
||||
break;
|
||||
case PF_WAIT:
|
||||
return 0;
|
||||
case PF_REACHED:
|
||||
/* since most of this function usage check return value as bool
|
||||
* then reached state should be track as true value */
|
||||
i = 1;
|
||||
break;
|
||||
case PF_WAIT:
|
||||
Assert(0);
|
||||
i = 0;
|
||||
break;
|
||||
case PF_MOVE:
|
||||
break;
|
||||
* then reached state should be track as true value */
|
||||
return std::max(i, 1);
|
||||
default:
|
||||
break;
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,14 +256,14 @@ int PlaceReachable(const CUnit &src, const Vec2i &goalPos, int w, int h, int min
|
|||
**
|
||||
** @return Distance to place.
|
||||
*/
|
||||
int UnitReachable(const CUnit &src, const CUnit &dst, int range)
|
||||
int UnitReachable(const CUnit &src, const CUnit &dst, int range, bool from_outside_container)
|
||||
{
|
||||
// Find a path to the goal.
|
||||
if (src.Type->Building) {
|
||||
return 0;
|
||||
}
|
||||
const int depth = PlaceReachable(src, dst.tilePos,
|
||||
dst.Type->TileWidth, dst.Type->TileHeight, 0, range);
|
||||
dst.Type->TileWidth, dst.Type->TileHeight, 0, range, from_outside_container);
|
||||
if (depth <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ int SyncRand()
|
|||
|
||||
if (EnableDebugPrint) {
|
||||
fprintf(stderr, "GameCycle: %lud, seed: %x, Sync rand: %d\n", GameCycle, SyncRandSeed, val);
|
||||
print_backtrace();
|
||||
print_backtrace(8);
|
||||
fflush(stderr);
|
||||
}
|
||||
return val;
|
||||
|
|
|
@ -430,43 +430,45 @@ void CUnit::Init()
|
|||
pathFinderData = new PathFinderData;
|
||||
pathFinderData->input.SetUnit(*this);
|
||||
|
||||
Frame = 0;
|
||||
Colors = -1;
|
||||
memset(IndividualUpgrades, 0, sizeof(IndividualUpgrades));
|
||||
IX = 0;
|
||||
IY = 0;
|
||||
Frame = 0;
|
||||
Direction = 0;
|
||||
CurrentResource = 0;
|
||||
ResourcesHeld = 0;
|
||||
DamagedType = ANIMATIONS_DEATHTYPES;
|
||||
Attacked = 0;
|
||||
Summoned = 0;
|
||||
Blink = 0;
|
||||
Moving = 0;
|
||||
ReCast = 0;
|
||||
AutoRepair = 0;
|
||||
Burning = 0;
|
||||
Destroyed = 0;
|
||||
Removed = 0;
|
||||
Selected = 0;
|
||||
TeamSelected = 0;
|
||||
Constructed = 0;
|
||||
Active = 0;
|
||||
Boarded = 0;
|
||||
CacheLock = 0;
|
||||
Waiting = 0;
|
||||
MineLow = 0;
|
||||
TeamSelected = 0;
|
||||
RescuedFrom = NULL;
|
||||
memset(VisCount, 0, sizeof(VisCount));
|
||||
memset(&Seen, 0, sizeof(Seen));
|
||||
delete Variable;
|
||||
Variable = NULL;
|
||||
TTL = 0;
|
||||
Threshold = 0;
|
||||
UnderAttack = 0;
|
||||
GroupId = 0;
|
||||
LastGroup = 0;
|
||||
ResourcesHeld = 0;
|
||||
Wait = 0;
|
||||
Blink = 0;
|
||||
Moving = 0;
|
||||
ReCast = 0;
|
||||
CacheLock = 0;
|
||||
Summoned = 0;
|
||||
Waiting = 0;
|
||||
MineLow = 0;
|
||||
Threshold = 0;
|
||||
UnderAttack = 0;
|
||||
memset(&Anim, 0, sizeof(Anim));
|
||||
memset(&WaitBackup, 0, sizeof(WaitBackup));
|
||||
CurrentResource = 0;
|
||||
Orders.clear();
|
||||
delete SavedOrder;
|
||||
SavedOrder = NULL;
|
||||
|
@ -478,9 +480,7 @@ void CUnit::Init()
|
|||
AutoCastSpell = NULL;
|
||||
delete SpellCoolDownTimers;
|
||||
SpellCoolDownTimers = NULL;
|
||||
AutoRepair = 0;
|
||||
Goal = NULL;
|
||||
memset(IndividualUpgrades, 0, sizeof(IndividualUpgrades));
|
||||
}
|
||||
|
||||
CUnit::~CUnit() {
|
||||
|
@ -2984,7 +2984,7 @@ static void HitUnit_AttackBack(CUnit &attacker, CUnit &target)
|
|||
COrder_Attack &order = dynamic_cast<COrder_Attack &>(*target.CurrentOrder());
|
||||
if (order.IsAutoTargeting() || target.Player->AiEnabled) {
|
||||
if (attacker.IsVisibleAsGoal(*target.Player)) {
|
||||
if (UnitReachable(target, attacker, target.Stats->Variables[ATTACKRANGE_INDEX].Max)) {
|
||||
if (UnitReachable(target, attacker, target.Stats->Variables[ATTACKRANGE_INDEX].Max, false)) {
|
||||
target.UnderAttack = underAttack; /// allow target to ignore non aggressive targets while searching attacker
|
||||
order.OfferNewTarget(target, &attacker);
|
||||
}
|
||||
|
@ -3018,7 +3018,7 @@ static void HitUnit_AttackBack(CUnit &attacker, CUnit &target)
|
|||
const Vec2i posToAttack = (attacker.IsVisibleAsGoal(*target.Player))
|
||||
? attacker.tilePos
|
||||
: GetRndPosInDirection(target.tilePos, attacker.tilePos, false, target.Type->ReactRangeComputer, 2);
|
||||
if (!PlaceReachable(target, posToAttack, 1, 1, 0, target.Stats->Variables[ATTACKRANGE_INDEX].Max)) {
|
||||
if (!PlaceReachable(target, posToAttack, 1, 1, 0, target.Stats->Variables[ATTACKRANGE_INDEX].Max, false)) {
|
||||
return;
|
||||
}
|
||||
COrder *savedOrder = NULL;
|
||||
|
|
|
@ -213,14 +213,8 @@ class BestDepotFinder
|
|||
return;
|
||||
}
|
||||
|
||||
// calck real travel distance
|
||||
if (worker->Container != nullptr) {
|
||||
UnmarkUnitFieldFlags(*first_container);
|
||||
}
|
||||
const int travel_distance = UnitReachable(*worker, *dest, 1);
|
||||
if (worker->Container != nullptr) {
|
||||
MarkUnitFieldFlags(*first_container);
|
||||
}
|
||||
// calc real travel distance
|
||||
const int travel_distance = UnitReachable(*worker, *dest, 1, worker->Container != nullptr);
|
||||
//
|
||||
// Take this depot?
|
||||
//
|
||||
|
@ -730,7 +724,7 @@ private:
|
|||
// Unit in range ?
|
||||
const int d = attacker->MapDistanceTo(*dest);
|
||||
|
||||
if (d > attackrange && !UnitReachable(*attacker, *dest, attackrange)) {
|
||||
if (d > attackrange && !UnitReachable(*attacker, *dest, attackrange, false)) {
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
|
@ -934,7 +928,7 @@ public:
|
|||
|
||||
int attackrange = attacker->Stats->Variables[ATTACKRANGE_INDEX].Max;
|
||||
if (d <= attackrange ||
|
||||
(d <= range && UnitReachable(*attacker, *dest, attackrange))) {
|
||||
(d <= range && UnitReachable(*attacker, *dest, attackrange, false))) {
|
||||
++enemy_count;
|
||||
} else {
|
||||
dest->CacheLock = 1;
|
||||
|
|
Loading…
Add table
Reference in a new issue