Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions source/entities/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,14 @@ void Creature::doUpkeep()
return;
}

// The ground the creature is standing on, or the ground it is walking towards, may have
// stopped being walkable since it last decided anything. Selling or destroying a bridge
// is what does that: the tiles it covered go back to being lava or water, and nothing
// told the creatures using them. Both checks have to happen before the creature acts, so
// that it acts on where it can actually go.
checkStandsOnWalkableTile();
checkWalkPathIsStillValid();

// Check to see if we have earned enough experience to level up.
checkLevelUp();

Expand Down Expand Up @@ -2480,6 +2488,86 @@ bool Creature::setDestination(Tile* tile)
return true;
}

void Creature::checkWalkPathIsStillValid()
{
if(mWalkQueue.empty())
return;

bool isPathBlocked = false;
for(const Ogre::Vector2& step : mWalkQueue)
{
Tile* tile = getGameMap()->getTile(Helper::round(step.x), Helper::round(step.y));
if(canGoThroughTile(tile))
continue;

// A building that will not let the creature through, a closed door or a prison, is
// another matter: those are meant to stop it where it is, and it was already
// walking towards one knowing what it would find. Only the ground going away from
// under the path is unexpected.
if((tile != nullptr) && (tile->getCoveringBuilding() != nullptr))
continue;

isPathBlocked = true;
break;
}

if(!isPathBlocked)
return;

const Ogre::Vector2& lastStep = mWalkQueue.back();
Tile* destination = getGameMap()->getTile(Helper::round(lastStep.x), Helper::round(lastStep.y));

OD_LOG_INF("creature=" + getName() + " cannot walk its path anymore, destination="
+ Tile::displayAsString(destination));

// Stop where we are rather than walk into what is now lava or water. If there is no
// other way to the destination, the action that started the walk gets to decide what
// to do instead, exactly as it would have done had the creature arrived.
clearDestinations(EntityAnimation::idle_anim, true, true);

if(!canGoThroughTile(destination))
return;

std::list<Tile*> result = getGameMap()->path(this, destination);
if(result.size() <= 1)
return;

std::vector<Ogre::Vector2> path;
tileToVector2(result, path, true, 0.0);
setWalkPath(EntityAnimation::walk_anim, EntityAnimation::idle_anim, true, true, path, true);
}

void Creature::checkStandsOnWalkableTile()
{
Tile* myTile = getPositionTile();
if(canGoThroughTile(myTile))
return;

// Prisons, fenced rooms and locked doors give a null speed on purpose, to hold the
// creature where it is. What we are after here is the ground going away from under
// it, which leaves the tile covered by nothing at all.
if(myTile->getCoveringBuilding() != nullptr)
return;

// And a bridge is the only thing that can be taken away from under a creature, so
// water and lava are the only ground it can be left standing on. Anything else that
// gives a null speed is left alone rather than guessed at.
if((myTile->getTileVisual() != TileVisual::waterGround) &&
(myTile->getTileVisual() != TileVisual::lavaGround))
{
return;
}

// The creature cannot go through this tile, or canGoThroughTile above would have
// accepted it, so it cannot live in what it is now standing in. It does not get
// fished out or teleported ashore: it drowns, or burns.
OD_LOG_INF("creature=" + getName() + " lost the ground under its feet on tile="
+ Tile::displayAsString(myTile) + " and dies");

clearDestinations(EntityAnimation::idle_anim, true, true);
takeDamage(nullptr, getHP(), 0.0, 0.0, 0.0, myTile, false);
}

bool Creature::wanderRandomly(const std::string& animationState)
{
// We pick randomly a visible tile far away (at the end of visible tiles)
Expand Down
11 changes: 11 additions & 0 deletions source/entities/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ class Creature: public MovableGameEntity, public Subject

bool setDestination(Tile* tile);

//! \brief Server side. Checks that the creature can still walk the path it is following and,
//! if it cannot, looks for another way to the same destination. Tiles the creature could walk
//! on when the path was computed can stop being walkable while it is on its way, which is what
//! happens to a bridge over lava or water that is sold or destroyed.
void checkWalkPathIsStillValid();

//! \brief Server side. Checks that the creature is standing somewhere it can stand and, if
//! the ground has turned into water or lava it cannot live in, kills it. A creature on a
//! tile it cannot walk on has a null move speed, so it could never leave by itself anyway.
void checkStandsOnWalkableTile();

//! \brief Picks a destination far away in the visible tiles and goes there
//! Returns true if a valid Tile was found. The creature will go there
//! Returns false if no reachable Tile was found
Expand Down
7 changes: 6 additions & 1 deletion source/network/ODClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,13 @@ bool ODClient::processMessage(ServerNotificationType cmd, ODPacket& packetReceiv

OD_ASSERT_TRUE(packetReceived >> objName >> dest);
MovableGameEntity *obj = gameMap->getAnimatedObject(objName);
if (obj == nullptr)
{
OD_LOG_ERR("Server told us to teleport unknown entity name=" + objName);
break;
}
obj->setPosition(dest);

break;
}
case ServerNotificationType::entitySlapped:
Expand Down