mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-26 22:57:00 +02:00
TurnInfo: store all bonuses and use TileInfo for everything
Currently this going to break ONE_WEEK bonuses because those don't work with CWillLastDays selector.
This commit is contained in:
parent
9ed9d94009
commit
abc4ea272f
@ -50,11 +50,11 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan
|
|||||||
}
|
}
|
||||||
|
|
||||||
hlp = make_unique<CPathfinderHelper>(hero);
|
hlp = make_unique<CPathfinderHelper>(hero);
|
||||||
if(hlp->ti->bonusFlying)
|
if(hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT))
|
||||||
options.useFlying = true;
|
options.useFlying = true;
|
||||||
if(hlp->ti->bonusWaterWalking)
|
if(hlp->hasBonusOfType(Bonus::WATER_WALKING))
|
||||||
options.useWaterWalking = true;
|
options.useWaterWalking = true;
|
||||||
if(CGWhirlpool::isProtected(hero))
|
if(hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
|
||||||
options.useTeleportWhirlpool = true;
|
options.useTeleportWhirlpool = true;
|
||||||
|
|
||||||
initializeGraph();
|
initializeGraph();
|
||||||
@ -109,7 +109,7 @@ void CPathfinder::calculatePaths()
|
|||||||
if(!movement)
|
if(!movement)
|
||||||
{
|
{
|
||||||
hlp->updateTurnInfo(++turn);
|
hlp->updateTurnInfo(++turn);
|
||||||
movement = hlp->getMaxMovePoints(cp->layer, turn);
|
movement = hlp->getMaxMovePoints(cp->layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
//add accessible neighbouring nodes to the queue
|
//add accessible neighbouring nodes to the queue
|
||||||
@ -140,11 +140,11 @@ void CPathfinder::calculatePaths()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
destAction = getDestAction();
|
destAction = getDestAction();
|
||||||
int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo(turn));
|
int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo());
|
||||||
int remains = movement - cost;
|
int remains = movement - cost;
|
||||||
if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
|
if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
|
||||||
{
|
{
|
||||||
remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1);
|
remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1, hlp->getTurnInfo());
|
||||||
cost = movement - remains;
|
cost = movement - remains;
|
||||||
}
|
}
|
||||||
int turnAtNextTile = turn;
|
int turnAtNextTile = turn;
|
||||||
@ -152,8 +152,8 @@ void CPathfinder::calculatePaths()
|
|||||||
{
|
{
|
||||||
//occurs rarely, when hero with low movepoints tries to leave the road
|
//occurs rarely, when hero with low movepoints tries to leave the road
|
||||||
hlp->updateTurnInfo(++turnAtNextTile);
|
hlp->updateTurnInfo(++turnAtNextTile);
|
||||||
int moveAtNextTile = hlp->getMaxMovePoints(i, turnAtNextTile);
|
int moveAtNextTile = hlp->getMaxMovePoints(i);
|
||||||
cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo(turnAtNextTile)); //cost must be updated, movement points changed :(
|
cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo()); //cost must be updated, movement points changed :(
|
||||||
remains = moveAtNextTile - cost;
|
remains = moveAtNextTile - cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,13 +285,13 @@ bool CPathfinder::isLayerAvailable(const ELayer layer, const int turn) const
|
|||||||
switch(layer)
|
switch(layer)
|
||||||
{
|
{
|
||||||
case ELayer::AIR:
|
case ELayer::AIR:
|
||||||
if(!hlp->ti->bonusFlying)
|
if(!hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::WATER:
|
case ELayer::WATER:
|
||||||
if(!hlp->ti->bonusWaterWalking)
|
if(!hlp->hasBonusOfType(Bonus::WATER_WALKING))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -701,47 +701,63 @@ bool CPathfinder::canVisitObject() const
|
|||||||
return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL;
|
return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TurnInfo::TurnInfo(const CGHeroInstance * h, const int Turn)
|
TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn)
|
||||||
: turn(Turn)
|
: hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1)
|
||||||
{
|
{
|
||||||
maxMovePointsLand = h->maxMovePoints(true);
|
bonuses = hero->getAllBonuses(Selector::days(turn), nullptr);
|
||||||
maxMovePointsWater = h->maxMovePoints(false);
|
}
|
||||||
bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn);
|
|
||||||
bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn);
|
bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const
|
||||||
|
{
|
||||||
|
return bonuses->getFirst(Selector::type(type).And(Selector::subtype(subtype)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const
|
||||||
|
{
|
||||||
|
return bonuses->valOfBonuses(Selector::type(type).And(Selector::subtype(subtype)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
|
int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
|
||||||
{
|
{
|
||||||
|
if(maxMovePointsLand == -1)
|
||||||
|
maxMovePointsLand = hero->maxMovePoints(true, this);
|
||||||
|
if(maxMovePointsWater == -1)
|
||||||
|
maxMovePointsWater = hero->maxMovePoints(false, this);
|
||||||
|
|
||||||
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero)
|
CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero)
|
||||||
: ti(nullptr), hero(Hero)
|
: turn(0), hero(Hero)
|
||||||
{
|
{
|
||||||
turnsInfo.reserve(16);
|
turnsInfo.reserve(16);
|
||||||
updateTurnInfo();
|
updateTurnInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPathfinderHelper::updateTurnInfo(const int turn)
|
void CPathfinderHelper::updateTurnInfo(const int Turn)
|
||||||
{
|
{
|
||||||
if(!ti || ti->turn != turn)
|
if(turn != Turn)
|
||||||
{
|
{
|
||||||
if(turn < turnsInfo.size())
|
turn = Turn;
|
||||||
ti = turnsInfo[turn];
|
if(turn >= turnsInfo.size())
|
||||||
else
|
|
||||||
{
|
{
|
||||||
ti = new TurnInfo(hero, turn);
|
auto ti = new TurnInfo(hero, turn);
|
||||||
turnsInfo.push_back(ti);
|
turnsInfo.push_back(ti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TurnInfo * CPathfinderHelper::getTurnInfo(const int turn) const
|
const TurnInfo * CPathfinderHelper::getTurnInfo() const
|
||||||
{
|
{
|
||||||
return turnsInfo[turn];
|
return turnsInfo[turn];
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer, const int turn) const
|
bool CPathfinderHelper::hasBonusOfType(const Bonus::BonusType type, const int subtype) const
|
||||||
|
{
|
||||||
|
return turnsInfo[turn]->hasBonusOfType(type, subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const
|
||||||
{
|
{
|
||||||
return turnsInfo[turn]->getMaxMovePoints(layer);
|
return turnsInfo[turn]->getMaxMovePoints(layer);
|
||||||
}
|
}
|
||||||
@ -796,17 +812,17 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr
|
|||||||
auto s = h->cb->getTile(src), d = h->cb->getTile(dst);
|
auto s = h->cb->getTile(src), d = h->cb->getTile(dst);
|
||||||
int ret = h->getTileCost(*d, *s, ti);
|
int ret = h->getTileCost(*d, *s, ti);
|
||||||
|
|
||||||
if(d->blocked && ti->bonusFlying)
|
if(d->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT))
|
||||||
{
|
{
|
||||||
ret *= (100.0 + ti->bonusFlying->val) / 100.0;
|
ret *= (100.0 + ti->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0;
|
||||||
}
|
}
|
||||||
else if(d->terType == ETerrainType::WATER)
|
else if(d->terType == ETerrainType::WATER)
|
||||||
{
|
{
|
||||||
if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds
|
if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds
|
||||||
ret *= 0.666;
|
ret *= 0.666;
|
||||||
else if(!h->boat && ti->bonusWaterWalking)
|
else if(!h->boat && ti->hasBonusOfType(Bonus::WATER_WALKING))
|
||||||
{
|
{
|
||||||
ret *= (100.0 + ti->bonusWaterWalking->val) / 100.0;
|
ret *= (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,28 +192,30 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TurnInfo
|
struct DLL_LINKAGE TurnInfo
|
||||||
{
|
{
|
||||||
int turn;
|
const CGHeroInstance * hero;
|
||||||
int maxMovePointsLand;
|
TBonusListPtr bonuses;
|
||||||
int maxMovePointsWater;
|
mutable int maxMovePointsLand;
|
||||||
const Bonus * bonusFlying;
|
mutable int maxMovePointsWater;
|
||||||
const Bonus * bonusWaterWalking;
|
|
||||||
|
|
||||||
TurnInfo(const CGHeroInstance * h, const int Turn = 0);
|
TurnInfo(const CGHeroInstance * Hero, const int Turn = 0);
|
||||||
|
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||||
|
int valOfBonuses(const Bonus::BonusType type, const int subtype = -1) const;
|
||||||
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CPathfinderHelper
|
class DLL_LINKAGE CPathfinderHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TurnInfo * ti;
|
int turn;
|
||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
|
|
||||||
CPathfinderHelper(const CGHeroInstance * Hero);
|
CPathfinderHelper(const CGHeroInstance * Hero);
|
||||||
void updateTurnInfo(const int turn = 0);
|
void updateTurnInfo(const int turn = 0);
|
||||||
const TurnInfo * getTurnInfo(const int turn) const;
|
const TurnInfo * getTurnInfo() const;
|
||||||
int getMaxMovePoints(const EPathfindingLayer layer, const int turn) const;
|
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||||
|
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
||||||
|
|
||||||
static void getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
|
static void getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, ti->turn, from.terType))
|
else if(!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
|
||||||
{
|
{
|
||||||
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
|
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
|
||||||
// This is clearly bug in H3 however intended behaviour is not clear.
|
// This is clearly bug in H3 however intended behaviour is not clear.
|
||||||
@ -129,11 +129,6 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Bonus * CGHeroInstance::getBonusAtTurn(const Bonus::BonusType &type, const int &turn, const TBonusSubtype &subType) const
|
|
||||||
{
|
|
||||||
return getBonus(Selector::type(type).And(Selector::days(turn)).And(Selector::subtype(subType)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
|
ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
|
||||||
{
|
{
|
||||||
for(auto & elem : secSkills)
|
for(auto & elem : secSkills)
|
||||||
@ -176,8 +171,11 @@ bool CGHeroInstance::canLearnSkill() const
|
|||||||
return secSkills.size() < GameConstants::SKILL_PER_HERO;
|
return secSkills.size() < GameConstants::SKILL_PER_HERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGHeroInstance::maxMovePoints(bool onLand) const
|
int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
|
||||||
{
|
{
|
||||||
|
if(!ti)
|
||||||
|
ti = new TurnInfo(this);
|
||||||
|
|
||||||
int base;
|
int base;
|
||||||
|
|
||||||
if(onLand)
|
if(onLand)
|
||||||
@ -196,10 +194,10 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
|
const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
|
||||||
const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt);
|
const int bonus = ti->valOfBonuses(Bonus::MOVEMENT) + ti->valOfBonuses(bt);
|
||||||
|
|
||||||
const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
|
const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
|
||||||
const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
|
const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
|
||||||
|
|
||||||
return int(base* (1+modifier)) + bonus;
|
return int(base* (1+modifier)) + bonus;
|
||||||
}
|
}
|
||||||
@ -1166,9 +1164,12 @@ CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
|||||||
return CArmedInstance::whereShouldBeAttached(gs);
|
return CArmedInstance::whereShouldBeAttached(gs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const
|
int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/, const TurnInfo * ti) const
|
||||||
{
|
{
|
||||||
if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
|
if(!ti)
|
||||||
|
ti = new TurnInfo(this);
|
||||||
|
|
||||||
|
if(ti->hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
|
||||||
return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
|
return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
|
||||||
|
|
||||||
return 0; //take all MPs otherwise
|
return 0; //take all MPs otherwise
|
||||||
|
@ -134,7 +134,6 @@ public:
|
|||||||
ui32 getLowestCreatureSpeed() const;
|
ui32 getLowestCreatureSpeed() const;
|
||||||
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
|
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
|
||||||
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
|
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
|
||||||
const Bonus * getBonusAtTurn(const Bonus::BonusType &type, const int &turn = 0, const TBonusSubtype &subType = -1) const;
|
|
||||||
int getCurrentLuck(int stack=-1, bool town=false) const;
|
int getCurrentLuck(int stack=-1, bool town=false) const;
|
||||||
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
|
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
|
||||||
|
|
||||||
@ -161,8 +160,8 @@ public:
|
|||||||
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
||||||
void levelUp(std::vector<SecondarySkill> skills);
|
void levelUp(std::vector<SecondarySkill> skills);
|
||||||
|
|
||||||
int maxMovePoints(bool onLand) const;
|
int maxMovePoints(bool onLand, const TurnInfo * ti = nullptr) const;
|
||||||
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
|
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
|
||||||
|
|
||||||
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
||||||
double getFightingStrength() const; // takes attack / defense skill into account
|
double getFightingStrength() const; // takes attack / defense skill into account
|
||||||
|
@ -1778,9 +1778,10 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
|||||||
tmh.movePoints = h->movement;
|
tmh.movePoints = h->movement;
|
||||||
|
|
||||||
//check if destination tile is available
|
//check if destination tile is available
|
||||||
const bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT);
|
auto ti = new TurnInfo(h);
|
||||||
const bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING);
|
const bool canFly = ti->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
||||||
const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, h->movement);
|
const bool canWalkOnSea = ti->hasBonusOfType(Bonus::WATER_WALKING);
|
||||||
|
const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, h->movement, ti);
|
||||||
|
|
||||||
//it's a rock or blocked and not visitable tile
|
//it's a rock or blocked and not visitable tile
|
||||||
//OR hero is on land and dest is water and (there is not present only one object - boat)
|
//OR hero is on land and dest is water and (there is not present only one object - boat)
|
||||||
@ -1872,14 +1873,14 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
|
|||||||
|
|
||||||
if(!transit && embarking)
|
if(!transit && embarking)
|
||||||
{
|
{
|
||||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false);
|
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false, ti);
|
||||||
return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
|
return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
|
||||||
//attack guards on embarking? In H3 creatures on water had no zone of control at all
|
//attack guards on embarking? In H3 creatures on water had no zone of control at all
|
||||||
}
|
}
|
||||||
|
|
||||||
if(disembarking)
|
if(disembarking)
|
||||||
{
|
{
|
||||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true);
|
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true, ti);
|
||||||
return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
|
return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user