More walk changes and creature events

Improve walking, no more random cancelWalks
Add 3 new creatures events onAppear/onDisappear/onWalk
Add algorithm that calculates walk ping
Fix paralyze animation while walking
master
Eduardo Bart 12 years ago
parent 76d32b5493
commit 57785d2001

@ -28,6 +28,7 @@
#include "item.h" #include "item.h"
#include "game.h" #include "game.h"
#include "effect.h" #include "effect.h"
#include "luavaluecasts.h"
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
@ -46,12 +47,12 @@ Creature::Creature() : Thing()
m_speed = 200; m_speed = 200;
m_direction = Otc::South; m_direction = Otc::South;
m_walkAnimationPhase = 0; m_walkAnimationPhase = 0;
m_walkInterval = 0; m_walkedPixels = 0;
m_walkAnimationInterval = 0;
m_walkTurnDirection = Otc::InvalidDirection; m_walkTurnDirection = Otc::InvalidDirection;
m_skull = Otc::SkullNone; m_skull = Otc::SkullNone;
m_shield = Otc::ShieldNone; m_shield = Otc::ShieldNone;
m_emblem = Otc::EmblemNone; m_emblem = Otc::EmblemNone;
m_lastStepDirection = Otc::InvalidDirection;
m_nameCache.setFont(g_fonts.getFont("verdana-11px-rounded")); m_nameCache.setFont(g_fonts.getFont("verdana-11px-rounded"));
m_nameCache.setAlign(Fw::AlignTopCenter); m_nameCache.setAlign(Fw::AlignTopCenter);
m_footStep = 0; m_footStep = 0;
@ -255,34 +256,17 @@ void Creature::walk(const Position& oldPos, const Position& newPos)
return; return;
// get walk direction // get walk direction
Otc::Direction direction = oldPos.getDirectionFromPosition(newPos); m_lastStepDirection = oldPos.getDirectionFromPosition(newPos);
// set current walking direction // set current walking direction
setDirection(direction); setDirection(m_lastStepDirection);
// starts counting walk // starts counting walk
m_walking = true; m_walking = true;
m_walkTimer.restart(); m_walkTimer.restart();
m_walkedPixels = 0;
// calculates walk interval // no direction need to be changed when the walk ends
int groundSpeed = 0;
TilePtr tile = g_map.getTile(newPos);
if(tile)
groundSpeed = tile->getGroundSpeed();
float interval = 1000;
if(groundSpeed > 0 && m_speed > 0)
interval = (1000.0f * groundSpeed) / m_speed;
interval = std::floor(interval / g_game.getServerBeat()) * g_game.getServerBeat();
m_walkAnimationInterval = interval;
m_walkInterval = interval;
// diagonal walking lasts 3 times more.
if(direction == Otc::NorthWest || direction == Otc::NorthEast || direction == Otc::SouthWest || direction == Otc::SouthEast)
m_walkInterval *= 3;
// no direction needs to be changed when the walk ends
m_walkTurnDirection = Otc::InvalidDirection; m_walkTurnDirection = Otc::InvalidDirection;
// starts updating walk // starts updating walk
@ -302,9 +286,50 @@ void Creature::stopWalk()
terminateWalk(); terminateWalk();
} }
void Creature::onMove(const Position& newPos, const Position& oldPos) void Creature::onAppear()
{ {
walk(oldPos, newPos); // cancel any disappear event
if(m_disappearEvent) {
m_disappearEvent->cancel();
m_disappearEvent = nullptr;
}
// creature appeared the first time or wasn't seen for a long time
if(m_removed) {
m_removed = false;
callLuaField("onAppear");
// walk
} else if(m_oldPosition != m_position && m_oldPosition.isInRange(m_position,1,1)) {
walk(m_oldPosition, m_position);
callLuaField("onWalk", m_oldPosition, m_position);
// teleport
} else if(m_oldPosition != m_position) {
callLuaField("onDisappear");
callLuaField("onAppear");
} // else turn
}
void Creature::onDisappear()
{
if(m_disappearEvent)
m_disappearEvent->cancel();
m_oldPosition = m_position;
// a pair onDisappear and onAppear events are fired even when creatures walks or turns,
// so we must filter
auto self = static_self_cast<Creature>();
m_disappearEvent = g_dispatcher.addEvent([self] {
self->m_removed = true;
self->stopWalk();
self->callLuaField("onDisappear");
// invalidate this creature position
self->m_position = Position();
self->m_oldPosition = Position();
self->m_disappearEvent = nullptr;
});
} }
void Creature::updateWalkAnimation(int totalPixelsWalked) void Creature::updateWalkAnimation(int totalPixelsWalked)
@ -316,7 +341,7 @@ void Creature::updateWalkAnimation(int totalPixelsWalked)
int footAnimPhases = getAnimationPhases() - 1; int footAnimPhases = getAnimationPhases() - 1;
if(totalPixelsWalked == 32 || footAnimPhases == 0) if(totalPixelsWalked == 32 || footAnimPhases == 0)
m_walkAnimationPhase = 0; m_walkAnimationPhase = 0;
else if(m_footStepDrawn && m_footTimer.ticksElapsed() >= m_walkAnimationInterval / 4 ) { else if(m_footStepDrawn && m_footTimer.ticksElapsed() >= getStepDuration() / 4 ) {
m_footStep++; m_footStep++;
m_walkAnimationPhase = 1 + (m_footStep % footAnimPhases); m_walkAnimationPhase = 1 + (m_footStep % footAnimPhases);
m_footStepDrawn = false; m_footStepDrawn = false;
@ -380,22 +405,26 @@ void Creature::nextWalkUpdate()
m_walkUpdateEvent = g_dispatcher.scheduleEvent([self] { m_walkUpdateEvent = g_dispatcher.scheduleEvent([self] {
self->m_walkUpdateEvent = nullptr; self->m_walkUpdateEvent = nullptr;
self->nextWalkUpdate(); self->nextWalkUpdate();
}, m_walkAnimationInterval / 32); }, getStepDuration() / 32);
} }
} }
void Creature::updateWalk() void Creature::updateWalk()
{ {
float walkTicksPerPixel = m_walkAnimationInterval / 32; int stepDuration = getStepDuration();
float walkTicksPerPixel = stepDuration / 32;
int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f); int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
// needed for paralyze effect
m_walkedPixels = std::max(m_walkedPixels, totalPixelsWalked);
// update walk animation and offsets // update walk animation and offsets
updateWalkAnimation(totalPixelsWalked); updateWalkAnimation(totalPixelsWalked);
updateWalkOffset(totalPixelsWalked); updateWalkOffset(m_walkedPixels);
updateWalkingTile(); updateWalkingTile();
// terminate walk // terminate walk
if(m_walking && m_walkTimer.ticksElapsed() >= m_walkInterval) if(m_walking && m_walkTimer.ticksElapsed() >= stepDuration)
terminateWalk(); terminateWalk();
} }
@ -419,6 +448,7 @@ void Creature::terminateWalk()
} }
m_walking = false; m_walking = false;
m_walkedPixels = 0;
} }
void Creature::setName(const std::string& name) void Creature::setName(const std::string& name)
@ -472,6 +502,15 @@ void Creature::setOutfit(const Outfit& outfit)
m_outfit = outfit; m_outfit = outfit;
} }
void Creature::setSpeed(uint16 speed)
{
m_speed = speed;
// speed can change while walking (utani hur, paralyze, etc..)
if(m_walking)
nextWalkUpdate();
}
void Creature::setSkull(uint8 skull) void Creature::setSkull(uint8 skull)
{ {
m_skull = skull; m_skull = skull;
@ -556,6 +595,27 @@ Point Creature::getDrawOffset()
return drawOffset; return drawOffset;
} }
int Creature::getStepDuration()
{
int groundSpeed = 0;
const TilePtr& tile = getTile();
if(tile)
groundSpeed = tile->getGroundSpeed();
int interval = 1000;
if(groundSpeed > 0 && m_speed > 0)
interval = (1000 * groundSpeed) / m_speed;
if(g_game.getClientVersion() >= 900)
interval = (interval / g_game.getServerBeat()) * g_game.getServerBeat();
if(m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast)
interval *= 3;
return interval;
}
const ThingTypePtr& Creature::getThingType() const ThingTypePtr& Creature::getThingType()
{ {
return g_things.getThingType(m_outfit.getId(), m_outfit.getCategory()); return g_things.getThingType(m_outfit.getId(), m_outfit.getCategory());

@ -55,7 +55,7 @@ public:
void setDirection(Otc::Direction direction); void setDirection(Otc::Direction direction);
void setOutfit(const Outfit& outfit); void setOutfit(const Outfit& outfit);
void setLight(const Light& light) { m_light = light; } void setLight(const Light& light) { m_light = light; }
void setSpeed(uint16 speed) { m_speed = speed; } void setSpeed(uint16 speed);
void setSkull(uint8 skull); void setSkull(uint8 skull);
void setShield(uint8 shield); void setShield(uint8 shield);
void setEmblem(uint8 emblem); void setEmblem(uint8 emblem);
@ -63,7 +63,6 @@ public:
void setShieldTexture(const std::string& filename, bool blink); void setShieldTexture(const std::string& filename, bool blink);
void setEmblemTexture(const std::string& filename); void setEmblemTexture(const std::string& filename);
void setPassable(bool passable) { m_passable = passable; } void setPassable(bool passable) { m_passable = passable; }
void setRemoved(bool removed) { m_removed = removed; }
void addTimedSquare(uint8 color); void addTimedSquare(uint8 color);
void removeTimedSquare() { m_showTimedSquare = false; } void removeTimedSquare() { m_showTimedSquare = false; }
@ -83,6 +82,7 @@ public:
uint8 getEmblem() { return m_emblem; } uint8 getEmblem() { return m_emblem; }
bool isPassable() { return m_passable; } bool isPassable() { return m_passable; }
Point getDrawOffset(); Point getDrawOffset();
int getStepDuration();
Point getWalkOffset() { return m_walkOffset; } Point getWalkOffset() { return m_walkOffset; }
void updateShield(); void updateShield();
@ -100,8 +100,8 @@ public:
const ThingTypePtr& getThingType(); const ThingTypePtr& getThingType();
ThingType *rawGetThingType(); ThingType *rawGetThingType();
protected: virtual void onAppear();
virtual void onMove(const Position& newPos, const Position& oldPos); virtual void onDisappear();
protected: protected:
virtual void updateWalkAnimation(int totalPixelsWalked); virtual void updateWalkAnimation(int totalPixelsWalked);
@ -131,23 +131,25 @@ protected:
Color m_staticSquareColor; Color m_staticSquareColor;
stdext::boolean<false> m_showTimedSquare; stdext::boolean<false> m_showTimedSquare;
stdext::boolean<false> m_showStaticSquare; stdext::boolean<false> m_showStaticSquare;
stdext::boolean<false> m_removed; stdext::boolean<true> m_removed;
CachedText m_nameCache; CachedText m_nameCache;
Color m_informationColor; Color m_informationColor;
// walk related // walk related
int m_walkAnimationPhase; int m_walkAnimationPhase;
int m_walkedPixels;
uint m_footStep; uint m_footStep;
Timer m_walkTimer; Timer m_walkTimer;
Timer m_footTimer; Timer m_footTimer;
TilePtr m_walkingTile; TilePtr m_walkingTile;
int m_walkInterval;
int m_walkAnimationInterval;
stdext::boolean<false> m_walking; stdext::boolean<false> m_walking;
stdext::boolean<false> m_footStepDrawn; stdext::boolean<false> m_footStepDrawn;
ScheduledEventPtr m_walkUpdateEvent; ScheduledEventPtr m_walkUpdateEvent;
EventPtr m_disappearEvent;
Point m_walkOffset; Point m_walkOffset;
Otc::Direction m_walkTurnDirection; Otc::Direction m_walkTurnDirection;
Otc::Direction m_lastStepDirection;
Position m_oldPosition;
}; };
// @bindclass // @bindclass

@ -247,26 +247,6 @@ void Game::processInventoryChange(int slot, const ItemPtr& item)
m_localPlayer->setInventoryItem((Otc::InventorySlot)slot, item); m_localPlayer->setInventoryItem((Otc::InventorySlot)slot, item);
} }
void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos)
{
// animate walk
creature->walk(oldPos, newPos);
g_lua.callGlobalField("g_game", "onCreatureMove", creature, oldPos, newPos);
}
void Game::processCreatureTeleport(const CreaturePtr& creature)
{
// stop walking on creature teleports
creature->stopWalk();
// locks the walk for a while when teleporting
if(creature == m_localPlayer)
m_localPlayer->lockWalk();
g_lua.callGlobalField("g_game", "onCreatureTeleport", creature);
}
void Game::processChannelList(const std::vector<std::tuple<int, std::string> >& channelList) void Game::processChannelList(const std::vector<std::tuple<int, std::string> >& channelList)
{ {
g_lua.callGlobalField("g_game", "onChannelList", channelList); g_lua.callGlobalField("g_game", "onChannelList", channelList);
@ -552,9 +532,11 @@ void Game::autoWalk(const std::vector<Otc::Direction>& dirs)
cancelFollow(); cancelFollow();
Otc::Direction direction = dirs.front(); Otc::Direction direction = dirs.front();
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); if(m_localPlayer->canWalk(direction)) {
if(toTile && toTile->isWalkable() && !m_localPlayer->isAutoWalking()) TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
m_localPlayer->preWalk(direction); if(toTile && toTile->isWalkable() && !m_localPlayer->isAutoWalking())
m_localPlayer->preWalk(direction);
}
m_protocolGame->sendAutoWalk(dirs); m_protocolGame->sendAutoWalk(dirs);

@ -65,8 +65,6 @@ protected:
void processGMActions(const std::vector<uint8>& actions); void processGMActions(const std::vector<uint8>& actions);
void processInventoryChange(int slot, const ItemPtr& item); void processInventoryChange(int slot, const ItemPtr& item);
void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos);
void processCreatureTeleport(const CreaturePtr& creature);
void processAttackCancel(uint seq); void processAttackCancel(uint seq);
void processWalkCancel(Otc::Direction direction); void processWalkCancel(Otc::Direction direction);

@ -29,7 +29,6 @@
LocalPlayer::LocalPlayer() LocalPlayer::LocalPlayer()
{ {
m_preWalking = false; m_preWalking = false;
m_walkLocked = false;
m_lastPrewalkDone = true; m_lastPrewalkDone = true;
m_autoWalking = false; m_autoWalking = false;
m_known = false; m_known = false;
@ -37,7 +36,7 @@ LocalPlayer::LocalPlayer()
m_states = 0; m_states = 0;
m_vocation = 0; m_vocation = 0;
m_walkLockInterval = 0; m_walkLockExpiration = 0;
m_skillsLevel.fill(-1); m_skillsLevel.fill(-1);
m_skillsLevelPercent.fill(-1); m_skillsLevelPercent.fill(-1);
@ -58,33 +57,29 @@ LocalPlayer::LocalPlayer()
void LocalPlayer::lockWalk(int millis) void LocalPlayer::lockWalk(int millis)
{ {
// prevents double locks m_walkLockExpiration = std::max(m_walkLockExpiration, (ticks_t) g_clock.millis() + millis);
if(m_walkLocked)
return;
m_walkLocked = true;
m_walkLockTimer.restart();
m_walkLockInterval = millis;
} }
bool LocalPlayer::canWalk(Otc::Direction direction) bool LocalPlayer::canWalk(Otc::Direction direction)
{ {
// prewalk has a timeout, because for some reason that I don't know yet the server sometimes doesn't answer the prewalk // cannot walk while locked
bool prewalkTimeouted = m_walking && m_preWalking && m_walkTimer.ticksElapsed() >= m_walkInterval + PREWALK_TIMEOUT; if(m_walkLockExpiration != 0 && g_clock.millis() < m_walkLockExpiration)
return false;
// cannot walk while already walking // last walk is not done yet
if(m_walking && !prewalkTimeouted) if(m_walkTimer.ticksElapsed() < getStepDuration())
return false; return false;
// prewalk has a timeout, because for some reason that I don't know yet the server sometimes doesn't answer the prewalk
bool prewalkTimeouted = m_walking && m_preWalking && m_walkTimer.ticksElapsed() >= getStepDuration() + PREWALK_TIMEOUT;
// avoid doing more walks than wanted when receiving a lot of walks from server // avoid doing more walks than wanted when receiving a lot of walks from server
if(!m_lastPrewalkDone && m_preWalking && !prewalkTimeouted) if(!m_lastPrewalkDone && m_preWalking && !prewalkTimeouted)
return false; return false;
// cannot walk while locked // cannot walk while already walking
if(m_walkLocked && m_walkLockTimer.ticksElapsed() <= m_walkLockInterval) if(m_walking && !prewalkTimeouted)
return false; return false;
else
m_walkLocked = false;
return true; return true;
} }
@ -93,6 +88,16 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos)
{ {
// a prewalk was going on // a prewalk was going on
if(m_preWalking) { if(m_preWalking) {
if(m_waitingWalkPong) {
if(newPos == m_lastPrewalkDestionation) {
m_lastWalkPings.push_back(m_walkPingTimer.ticksElapsed());
if(m_lastWalkPings.size() > 10)
m_lastWalkPings.pop_front();
}
m_waitingWalkPong = false;
}
// switch to normal walking // switch to normal walking
m_preWalking = false; m_preWalking = false;
m_lastPrewalkDone = true; m_lastPrewalkDone = true;
@ -115,13 +120,18 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos)
void LocalPlayer::preWalk(Otc::Direction direction) void LocalPlayer::preWalk(Otc::Direction direction)
{ {
// start walking to direction
Position newPos = m_position.translatedToDirection(direction); Position newPos = m_position.translatedToDirection(direction);
// avoid reanimating prewalks
if(m_preWalking && m_lastPrewalkDestionation == newPos)
return;
m_preWalking = true; m_preWalking = true;
if(m_autoWalkEndEvent) if(m_autoWalkEndEvent)
m_autoWalkEndEvent->cancel(); m_autoWalkEndEvent->cancel();
// start walking to direction
m_lastPrewalkDone = false; m_lastPrewalkDone = false;
m_lastPrewalkDestionation = newPos; m_lastPrewalkDestionation = newPos;
Creature::walk(m_position, newPos); Creature::walk(m_position, newPos);
@ -134,6 +144,7 @@ void LocalPlayer::cancelWalk(Otc::Direction direction)
stopWalk(); stopWalk();
m_lastPrewalkDone = true; m_lastPrewalkDone = true;
m_waitingWalkPong = false;
// turn to the cancel direction // turn to the cancel direction
if(direction != Otc::InvalidDirection) if(direction != Otc::InvalidDirection)
@ -167,7 +178,8 @@ void LocalPlayer::updateWalkOffset(int totalPixelsWalked)
void LocalPlayer::updateWalk() void LocalPlayer::updateWalk()
{ {
float walkTicksPerPixel = m_walkAnimationInterval / 32.0f; int stepDuration = getStepDuration();
float walkTicksPerPixel = stepDuration / 32.0f;
int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f); int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
// update walk animation and offsets // update walk animation and offsets
@ -176,7 +188,7 @@ void LocalPlayer::updateWalk()
updateWalkingTile(); updateWalkingTile();
// terminate walk only when client and server side walk are complated // terminate walk only when client and server side walk are complated
if(m_walking && !m_preWalking && m_walkTimer.ticksElapsed() >= m_walkInterval) if(m_walking && !m_preWalking && m_walkTimer.ticksElapsed() >= stepDuration)
terminateWalk(); terminateWalk();
} }
@ -196,6 +208,15 @@ void LocalPlayer::terminateWalk()
} }
} }
void LocalPlayer::onAppear()
{
Creature::onAppear();
// on teleports lock the walk
if(!m_oldPosition.isInRange(m_position,1,1))
lockWalk();
}
void LocalPlayer::setStates(int states) void LocalPlayer::setStates(int states)
{ {
if(m_states != states) { if(m_states != states) {
@ -351,3 +372,14 @@ void LocalPlayer::setPremium(bool premium)
callLuaField("onPremiumChange", premium); callLuaField("onPremiumChange", premium);
} }
} }
double LocalPlayer::getWalkPing()
{
if(m_lastWalkPings.empty())
return 9999;
double sum = 0;
for(int p : m_lastWalkPings)
sum += p;
return sum / (double)m_lastWalkPings.size();
}

@ -35,7 +35,7 @@ class LocalPlayer : public Player
public: public:
LocalPlayer(); LocalPlayer();
void unlockWalk() { m_walkLocked = false; } void unlockWalk() { m_walkLockExpiration = 0; }
void lockWalk(int millis = 250); void lockWalk(int millis = 250);
bool canWalk(Otc::Direction direction); bool canWalk(Otc::Direction direction);
@ -58,6 +58,7 @@ public:
int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; } int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; }
int getSkillLevelPercent(Otc::Skill skill) { return m_skillsLevelPercent[skill]; } int getSkillLevelPercent(Otc::Skill skill) { return m_skillsLevelPercent[skill]; }
int getVocation() { return m_vocation; } int getVocation() { return m_vocation; }
double getWalkPing();
double getHealth() { return m_health; } double getHealth() { return m_health; }
double getMaxHealth() { return m_maxHealth; } double getMaxHealth() { return m_maxHealth; }
double getFreeCapacity() { return m_freeCapacity; } double getFreeCapacity() { return m_freeCapacity; }
@ -80,6 +81,8 @@ public:
LocalPlayerPtr asLocalPlayer() { return static_self_cast<LocalPlayer>(); } LocalPlayerPtr asLocalPlayer() { return static_self_cast<LocalPlayer>(); }
bool isLocalPlayer() { return true; } bool isLocalPlayer() { return true; }
virtual void onAppear();
protected: protected:
void walk(const Position& oldPos, const Position& newPos); void walk(const Position& oldPos, const Position& newPos);
void preWalk(Otc::Direction direction); void preWalk(Otc::Direction direction);
@ -97,13 +100,14 @@ private:
// walk related // walk related
bool m_preWalking; bool m_preWalking;
bool m_lastPrewalkDone; bool m_lastPrewalkDone;
bool m_walkLocked;
bool m_autoWalking; bool m_autoWalking;
bool m_premium; bool m_premium;
Position m_lastPrewalkDestionation; Position m_lastPrewalkDestionation;
Timer m_walkLockTimer;
ItemPtr m_inventoryItems[Otc::LastInventorySlot]; ItemPtr m_inventoryItems[Otc::LastInventorySlot];
ScheduledEventPtr m_autoWalkEndEvent; ScheduledEventPtr m_autoWalkEndEvent;
stdext::boolean<false> m_waitingWalkPong;
Timer m_walkPingTimer;
std::deque<int> m_lastWalkPings;
std::array<int, Otc::LastSkill> m_skillsLevel; std::array<int, Otc::LastSkill> m_skillsLevel;
std::array<int, Otc::LastSkill> m_skillsLevelPercent; std::array<int, Otc::LastSkill> m_skillsLevelPercent;
@ -111,7 +115,7 @@ private:
bool m_known; bool m_known;
int m_states; int m_states;
int m_vocation; int m_vocation;
int m_walkLockInterval; ticks_t m_walkLockExpiration;
double m_health; double m_health;
double m_maxHealth; double m_maxHealth;

@ -78,7 +78,6 @@ void Map::cleanDynamicThings()
for(const auto& pair : m_knownCreatures) { for(const auto& pair : m_knownCreatures) {
const CreaturePtr& creature = pair.second; const CreaturePtr& creature = pair.second;
removeThing(creature); removeThing(creature);
creature->setRemoved(true);
} }
m_knownCreatures.clear(); m_knownCreatures.clear();
@ -99,50 +98,35 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
if(!thing) if(!thing)
return; return;
TilePtr tile = getOrCreateTile(pos);
Position oldPos = thing->getPosition();
if(thing->isItem() || thing->isCreature() || thing->isEffect()) { if(thing->isItem() || thing->isCreature() || thing->isEffect()) {
const TilePtr& tile = getOrCreateTile(pos);
tile->addThing(thing, stackPos); tile->addThing(thing, stackPos);
} else if(thing->isMissile()) { } else {
m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>()); if(thing->isMissile()) {
} else if(thing->isAnimatedText()) { m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>());
m_animatedTexts.push_back(thing->static_self_cast<AnimatedText>()); thing->onAppear();
} else if(thing->isStaticText()) { } else if(thing->isAnimatedText()) {
StaticTextPtr staticText = thing->static_self_cast<StaticText>(); AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
bool mustAdd = true; m_animatedTexts.push_back(animatedText);
for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) { } else if(thing->isStaticText()) {
StaticTextPtr cStaticText = *it; StaticTextPtr staticText = thing->static_self_cast<StaticText>();
if(cStaticText->getPosition() == pos) { bool mustAdd = true;
for(auto other : m_staticTexts) {
// try to combine messages // try to combine messages
if(cStaticText->addMessage(staticText->getName(), staticText->getMessageMode(), staticText->getFirstMessage())) { if(other->getPosition() == pos && other->addMessage(staticText->getName(), staticText->getMessageMode(), staticText->getFirstMessage())) {
mustAdd = false; mustAdd = false;
break; break;
} else {
// must add another message and rearrenge current
} }
} }
if(mustAdd) {
m_staticTexts.push_back(staticText);
staticText->onAppear();
}
} }
if(mustAdd) thing->setPosition(pos);
m_staticTexts.push_back(staticText);
}
if(!thing->isCreature())
thing->onAppear(); thing->onAppear();
thing->setPosition(pos);
if(thing->isCreature()) {
CreaturePtr creature = thing->static_self_cast<Creature>();
if(oldPos != pos) {
if(oldPos.isInRange(pos,1,1))
g_game.processCreatureMove(creature, oldPos, pos);
else
g_game.processCreatureTeleport(creature);
}
} }
notificateTileUpdateToMapViews(pos); notificateTileUpdateToMapViews(pos);
@ -289,9 +273,7 @@ void Map::removeCreatureById(uint32 id)
auto it = m_knownCreatures.find(id); auto it = m_knownCreatures.find(id);
if(it != m_knownCreatures.end()) if(it != m_knownCreatures.end())
it->second->setRemoved(true); m_knownCreatures.erase(it);
m_knownCreatures.erase(it);
} }
void Map::setCentralPosition(const Position& centralPosition) void Map::setCentralPosition(const Position& centralPosition)
@ -318,12 +300,10 @@ void Map::setCentralPosition(const Position& centralPosition)
Position oldPos = localPlayer->getPosition(); Position oldPos = localPlayer->getPosition();
Position pos = m_centralPosition; Position pos = m_centralPosition;
localPlayer->setPosition(pos);
if(oldPos != pos) { if(oldPos != pos) {
if(oldPos.isInRange(pos,1,1)) localPlayer->onDisappear();
g_game.processCreatureMove(localPlayer, oldPos, pos); localPlayer->setPosition(pos);
else localPlayer->onAppear();
g_game.processCreatureTeleport(localPlayer);
} }
}); });

@ -506,10 +506,6 @@ void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg)
g_map.removeThing(thing); g_map.removeThing(thing);
g_map.addThing(thing, newPos, stackPos); g_map.addThing(thing, newPos, stackPos);
//CreaturePtr creature = thing->static_self_cast<Creature>();
//Position oldPos = thing->getPosition();
//creature->onMove(newPos, oldPos);
} }
void ProtocolGame::parseOpenContainer(const InputMessagePtr& msg) void ProtocolGame::parseOpenContainer(const InputMessagePtr& msg)

@ -35,6 +35,9 @@ Thing::Thing() :
void Thing::setPosition(const Position& position) void Thing::setPosition(const Position& position)
{ {
if(m_position == position)
return;
Position oldPos = m_position; Position oldPos = m_position;
m_position = position; m_position = position;
onPositionChange(position, oldPos); onPositionChange(position, oldPos);

@ -118,13 +118,11 @@ public:
bool isMarketable() { return rawGetThingType()->isMarketable(); } bool isMarketable() { return rawGetThingType()->isMarketable(); }
MarketData getMarketData() { return rawGetThingType()->getMarketData(); } MarketData getMarketData() { return rawGetThingType()->getMarketData(); }
protected:
virtual void onPositionChange(const Position& newPos, const Position& oldPos) { } virtual void onPositionChange(const Position& newPos, const Position& oldPos) { }
virtual void onAppear() { } virtual void onAppear() { }
virtual void onDisappear() { } virtual void onDisappear() { }
friend class Map; protected:
Position m_position; Position m_position;
uint16 m_datId; uint16 m_datId;
}; };

@ -101,8 +101,6 @@ void Tile::draw(const Point& dest, float scaleFactor, int drawFlags)
if(drawFlags & Otc::DrawCreatures) { if(drawFlags & Otc::DrawCreatures) {
if(animate) { if(animate) {
for(const CreaturePtr& creature : m_walkingCreatures) { for(const CreaturePtr& creature : m_walkingCreatures) {
if(creature->isRemoved())
continue;
creature->draw(Point(dest.x + ((creature->getPosition().x - m_position.x)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor, creature->draw(Point(dest.x + ((creature->getPosition().x - m_position.x)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor,
dest.y + ((creature->getPosition().y - m_position.y)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor), scaleFactor, animate); dest.y + ((creature->getPosition().y - m_position.y)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor), scaleFactor, animate);
@ -159,42 +157,44 @@ void Tile::addThing(const ThingPtr& thing, int stackPos)
if(thing->isEffect()) { if(thing->isEffect()) {
m_effects.push_back(thing->static_self_cast<Effect>()); m_effects.push_back(thing->static_self_cast<Effect>());
return; } else {
} // the items stackpos follows this order:
// 0 - ground
// the items stackpos follows this order: // 1 - ground borders
// 0 - ground // 2 - bottom (walls)
// 1 - ground borders // 3 - on top (doors)
// 2 - bottom (walls) // 4 - creatures, from top to bottom
// 3 - on top (doors) // 5 - items, from top to bottom
// 4 - creatures, from top to bottom if(stackPos < 0 || stackPos == 255) {
// 5 - items, from top to bottom int priority = thing->getStackPriority();
if(stackPos < 0 || stackPos == 255) { bool prepend = (stackPos == -2 || priority <= 3);
int priority = thing->getStackPriority(); for(stackPos = 0; stackPos < (int)m_things.size(); ++stackPos) {
bool prepend = (stackPos == -2 || priority <= 3); int otherPriority = m_things[stackPos]->getStackPriority();
for(stackPos = 0; stackPos < (int)m_things.size(); ++stackPos) { if((prepend && otherPriority > priority) || (!prepend && otherPriority >= priority))
int otherPriority = m_things[stackPos]->getStackPriority(); break;
if((prepend && otherPriority > priority) || (!prepend && otherPriority >= priority)) }
break; } else if(stackPos > (int)m_things.size())
} stackPos = m_things.size();
} else if(stackPos > (int)m_things.size())
stackPos = m_things.size();
m_things.insert(m_things.begin() + stackPos, thing); m_things.insert(m_things.begin() + stackPos, thing);
if(m_things.size() > MAX_THINGS) if(m_things.size() > MAX_THINGS)
removeThing(m_things[MAX_THINGS]); removeThing(m_things[MAX_THINGS]);
/* /*
// check stack priorities // check stack priorities
// this code exists to find stackpos bugs faster // this code exists to find stackpos bugs faster
int lastPriority = 0; int lastPriority = 0;
for(const ThingPtr& thing : m_things) { for(const ThingPtr& thing : m_things) {
int priority = thing->getStackPriority(); int priority = thing->getStackPriority();
assert(lastPriority <= priority); assert(lastPriority <= priority);
lastPriority = priority; lastPriority = priority;
}
*/
} }
*/
thing->setPosition(m_position);
thing->onAppear();
} }
bool Tile::removeThing(ThingPtr thing) bool Tile::removeThing(ThingPtr thing)
@ -219,6 +219,8 @@ bool Tile::removeThing(ThingPtr thing)
} }
} }
thing->onDisappear();
return removed; return removed;
} }

Loading…
Cancel
Save