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
This commit is contained in:
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)
|
||||||
|
|
||||||
// cannot walk while already walking
|
|
||||||
if(m_walking && !prewalkTimeouted)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// last walk is not done yet
|
||||||
|
if(m_walkTimer.ticksElapsed() < getStepDuration())
|
||||||
|
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
|
||||||
|
// 1 - ground borders
|
||||||
|
// 2 - bottom (walls)
|
||||||
|
// 3 - on top (doors)
|
||||||
|
// 4 - creatures, from top to bottom
|
||||||
|
// 5 - items, from top to bottom
|
||||||
|
if(stackPos < 0 || stackPos == 255) {
|
||||||
|
int priority = thing->getStackPriority();
|
||||||
|
bool prepend = (stackPos == -2 || priority <= 3);
|
||||||
|
for(stackPos = 0; stackPos < (int)m_things.size(); ++stackPos) {
|
||||||
|
int otherPriority = m_things[stackPos]->getStackPriority();
|
||||||
|
if((prepend && otherPriority > priority) || (!prepend && otherPriority >= priority))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(stackPos > (int)m_things.size())
|
||||||
|
stackPos = m_things.size();
|
||||||
|
|
||||||
// the items stackpos follows this order:
|
m_things.insert(m_things.begin() + stackPos, thing);
|
||||||
// 0 - ground
|
|
||||||
// 1 - ground borders
|
if(m_things.size() > MAX_THINGS)
|
||||||
// 2 - bottom (walls)
|
removeThing(m_things[MAX_THINGS]);
|
||||||
// 3 - on top (doors)
|
|
||||||
// 4 - creatures, from top to bottom
|
/*
|
||||||
// 5 - items, from top to bottom
|
// check stack priorities
|
||||||
if(stackPos < 0 || stackPos == 255) {
|
// this code exists to find stackpos bugs faster
|
||||||
int priority = thing->getStackPriority();
|
int lastPriority = 0;
|
||||||
bool prepend = (stackPos == -2 || priority <= 3);
|
for(const ThingPtr& thing : m_things) {
|
||||||
for(stackPos = 0; stackPos < (int)m_things.size(); ++stackPos) {
|
int priority = thing->getStackPriority();
|
||||||
int otherPriority = m_things[stackPos]->getStackPriority();
|
assert(lastPriority <= priority);
|
||||||
if((prepend && otherPriority > priority) || (!prepend && otherPriority >= priority))
|
lastPriority = priority;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else if(stackPos > (int)m_things.size())
|
*/
|
||||||
stackPos = m_things.size();
|
|
||||||
|
|
||||||
m_things.insert(m_things.begin() + stackPos, thing);
|
|
||||||
|
|
||||||
if(m_things.size() > MAX_THINGS)
|
|
||||||
removeThing(m_things[MAX_THINGS]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
// check stack priorities
|
|
||||||
// this code exists to find stackpos bugs faster
|
|
||||||
int lastPriority = 0;
|
|
||||||
for(const ThingPtr& thing : m_things) {
|
|
||||||
int priority = thing->getStackPriority();
|
|
||||||
assert(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…
Reference in New Issue