From 00cda69b883d6354f093be6ee39a7936cb798979 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Sat, 31 Aug 2013 22:42:10 +0300 Subject: move being related files into being dir. --- src/CMakeLists.txt | 32 +- src/Makefile.am | 32 +- src/actionmanager.cpp | 7 +- src/actor.cpp | 71 - src/actor.h | 142 - src/actorsprite.cpp | 397 --- src/actorsprite.h | 246 -- src/actorspritelistener.h | 44 - src/actorspritemanager.cpp | 7 +- src/actorspritemanager.h | 3 +- src/auctionmanager.cpp | 3 +- src/avatar.cpp | 2 +- src/being.cpp | 3040 ------------------- src/being.h | 1063 ------- src/being/actor.cpp | 71 + src/being/actor.h | 142 + src/being/actorsprite.cpp | 398 +++ src/being/actorsprite.h | 247 ++ src/being/actorspritelistener.h | 44 + src/being/being.cpp | 3041 +++++++++++++++++++ src/being/being.h | 1063 +++++++ src/being/beingcacheentry.h | 128 + src/being/compoundsprite.cpp | 567 ++++ src/being/compoundsprite.h | 161 + src/being/localplayer.cpp | 4355 ++++++++++++++++++++++++++++ src/being/localplayer.h | 652 +++++ src/being/playerinfo.cpp | 398 +++ src/being/playerinfo.h | 267 ++ src/being/playerrelations.cpp | 630 ++++ src/being/playerrelations.h | 285 ++ src/beingcacheentry.h | 128 - src/beingequipbackend.cpp | 2 +- src/client.cpp | 3 +- src/commands.cpp | 3 +- src/commands.h | 2 +- src/compoundsprite.cpp | 567 ---- src/compoundsprite.h | 161 - src/defaults.cpp | 3 +- src/dropshortcut.cpp | 5 +- src/effectmanager.cpp | 3 +- src/emoteshortcut.cpp | 3 +- src/flooritem.h | 2 +- src/game.cpp | 5 +- src/gui/beingpopup.cpp | 5 +- src/gui/botcheckerwindow.cpp | 3 +- src/gui/charcreatedialog.h | 2 +- src/gui/charselectdialog.h | 3 +- src/gui/chatwindow.cpp | 5 +- src/gui/debugwindow.cpp | 3 +- src/gui/equipmentwindow.cpp | 5 +- src/gui/inventorywindow.cpp | 3 +- src/gui/killstats.cpp | 5 +- src/gui/minimap.cpp | 3 +- src/gui/ministatuswindow.cpp | 5 +- src/gui/npcdialog.cpp | 3 +- src/gui/outfitwindow.cpp | 5 +- src/gui/popupmenu.cpp | 7 +- src/gui/popupmenu.h | 2 +- src/gui/questswindow.cpp | 3 +- src/gui/selldialog.cpp | 3 +- src/gui/setup_chat.cpp | 3 +- src/gui/setup_perfomance.cpp | 3 +- src/gui/setup_players.cpp | 3 +- src/gui/setup_relations.cpp | 3 +- src/gui/setup_relations.h | 2 +- src/gui/setup_theme.cpp | 3 +- src/gui/setup_video.cpp | 2 +- src/gui/setup_video.h | 2 +- src/gui/shopwindow.cpp | 7 +- src/gui/socialwindow.cpp | 5 +- src/gui/statuspopup.cpp | 3 +- src/gui/statuswindow.cpp | 5 +- src/gui/textcommandeditor.cpp | 3 +- src/gui/tradewindow.cpp | 7 +- src/gui/viewport.cpp | 5 +- src/gui/whoisonline.cpp | 5 +- src/gui/widgets/avatarlistbox.cpp | 3 +- src/gui/widgets/battletab.cpp | 3 +- src/gui/widgets/chattab.cpp | 3 +- src/gui/widgets/dropshortcutcontainer.cpp | 5 +- src/gui/widgets/emoteshortcutcontainer.cpp | 3 +- src/gui/widgets/gmtab.cpp | 3 +- src/gui/widgets/guildchattab.cpp | 3 +- src/gui/widgets/itemcontainer.cpp | 3 +- src/gui/widgets/itemshortcutcontainer.cpp | 5 +- src/gui/widgets/playerbox.cpp | 3 +- src/gui/widgets/shoplistbox.cpp | 3 +- src/gui/widgets/skillinfo.cpp | 2 +- src/gui/widgets/skillmodel.cpp | 5 +- src/gui/widgets/skillmodel.h | 5 +- src/gui/widgets/spellshortcutcontainer.cpp | 3 +- src/gui/widgets/tradetab.cpp | 3 +- src/gui/widgets/whispertab.cpp | 3 +- src/guildmanager.cpp | 3 +- src/input/inputmanager.cpp | 3 +- src/inventory.h | 2 +- src/itemshortcut.cpp | 3 +- src/localplayer.cpp | 4354 --------------------------- src/localplayer.h | 650 ----- src/map.cpp | 3 +- src/map.h | 3 +- src/maplayer.cpp | 2 +- src/maplayer.h | 3 +- src/net/beinghandler.h | 3 +- src/net/charserverhandler.h | 4 +- src/net/ea/beinghandler.cpp | 7 +- src/net/ea/buysellhandler.cpp | 5 +- src/net/ea/buysellhandler.h | 2 +- src/net/ea/chathandler.cpp | 5 +- src/net/ea/gamehandler.cpp | 3 +- src/net/ea/gui/guildtab.cpp | 3 +- src/net/ea/gui/partytab.cpp | 3 +- src/net/ea/guildhandler.cpp | 3 +- src/net/ea/inventoryhandler.cpp | 3 +- src/net/ea/inventoryhandler.h | 3 +- src/net/ea/partyhandler.cpp | 3 +- src/net/ea/playerhandler.cpp | 3 +- src/net/ea/skillhandler.cpp | 5 +- src/net/ea/token.h | 2 +- src/net/ea/tradehandler.cpp | 5 +- src/net/eathena/adminhandler.cpp | 3 +- src/net/eathena/beinghandler.cpp | 7 +- src/net/eathena/buysellhandler.cpp | 5 +- src/net/eathena/chathandler.cpp | 5 +- src/net/eathena/gamehandler.cpp | 3 +- src/net/eathena/gui/guildtab.cpp | 3 +- src/net/eathena/gui/partytab.cpp | 3 +- src/net/eathena/guildhandler.cpp | 5 +- src/net/eathena/npchandler.cpp | 2 +- src/net/eathena/partyhandler.cpp | 3 +- src/net/eathena/tradehandler.cpp | 3 +- src/net/logindata.h | 2 +- src/net/playerhandler.h | 5 +- src/net/tmwa/adminhandler.cpp | 3 +- src/net/tmwa/beinghandler.cpp | 7 +- src/net/tmwa/buysellhandler.cpp | 5 +- src/net/tmwa/chathandler.cpp | 5 +- src/net/tmwa/gamehandler.cpp | 3 +- src/net/tmwa/gui/guildtab.cpp | 3 +- src/net/tmwa/gui/partytab.cpp | 3 +- src/net/tmwa/guildhandler.cpp | 5 +- src/net/tmwa/npchandler.cpp | 2 +- src/net/tmwa/partyhandler.cpp | 3 +- src/net/tmwa/questhandler.cpp | 2 +- src/net/tmwa/tradehandler.cpp | 3 +- src/net/tradehandler.h | 2 +- src/notifymanager.cpp | 3 +- src/particle/particle.h | 3 +- src/playerinfo.cpp | 398 --- src/playerinfo.h | 267 -- src/playerrelations.cpp | 629 ---- src/playerrelations.h | 285 -- src/resources/beinginfo.h | 2 +- src/resources/iteminfo.h | 2 +- src/soundmanager.cpp | 3 +- src/spellmanager.cpp | 5 +- src/spellmanager.h | 3 +- src/spellshortcut.cpp | 3 +- 158 files changed, 12752 insertions(+), 12643 deletions(-) delete mode 100644 src/actor.cpp delete mode 100644 src/actor.h delete mode 100644 src/actorsprite.cpp delete mode 100644 src/actorsprite.h delete mode 100644 src/actorspritelistener.h delete mode 100644 src/being.cpp delete mode 100644 src/being.h create mode 100644 src/being/actor.cpp create mode 100644 src/being/actor.h create mode 100644 src/being/actorsprite.cpp create mode 100644 src/being/actorsprite.h create mode 100644 src/being/actorspritelistener.h create mode 100644 src/being/being.cpp create mode 100644 src/being/being.h create mode 100644 src/being/beingcacheentry.h create mode 100644 src/being/compoundsprite.cpp create mode 100644 src/being/compoundsprite.h create mode 100644 src/being/localplayer.cpp create mode 100644 src/being/localplayer.h create mode 100644 src/being/playerinfo.cpp create mode 100644 src/being/playerinfo.h create mode 100644 src/being/playerrelations.cpp create mode 100644 src/being/playerrelations.h delete mode 100644 src/beingcacheentry.h delete mode 100644 src/compoundsprite.cpp delete mode 100644 src/compoundsprite.h delete mode 100644 src/localplayer.cpp delete mode 100644 src/localplayer.h delete mode 100644 src/playerinfo.cpp delete mode 100644 src/playerinfo.h delete mode 100644 src/playerrelations.cpp delete mode 100644 src/playerrelations.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 813e23672..7c90947af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -558,11 +558,11 @@ SET(SRCS test/testmain.h actionmanager.cpp actionmanager.h - actor.cpp - actor.h - actorsprite.cpp - actorsprite.h - actorspritelistener.h + being/actor.cpp + being/actor.h + being/actorsprite.cpp + being/actorsprite.h + being/actorspritelistener.h actorspritemanager.cpp actorspritemanager.h animatedsprite.cpp @@ -575,9 +575,9 @@ SET(SRCS auctionmanager.h avatar.cpp avatar.h - being.cpp - being.h - beingcacheentry.h + being/being.cpp + being/being.h + being/beingcacheentry.h beingequipbackend.cpp beingequipbackend.h spellmanager.cpp @@ -590,8 +590,8 @@ SET(SRCS commandhandler.h commands.cpp commands.h - compoundsprite.cpp - compoundsprite.h + being/compoundsprite.cpp + being/compoundsprite.h configlistener.h configuration.cpp configuration.h @@ -653,8 +653,8 @@ SET(SRCS input/keyinput.cpp input/keyinput.h localconsts.h - localplayer.cpp - localplayer.h + being/localplayer.cpp + being/localplayer.h logger.cpp logger.h main.cpp @@ -689,10 +689,10 @@ SET(SRCS particle/particleemitterprop.h party.cpp party.h - playerinfo.cpp - playerinfo.h - playerrelations.cpp - playerrelations.h + being/playerinfo.cpp + being/playerinfo.h + being/playerrelations.cpp + being/playerrelations.h position.cpp position.h properties.h diff --git a/src/Makefile.am b/src/Makefile.am index 252aa05f7..302194821 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -569,11 +569,11 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ test/testmain.h \ actionmanager.cpp \ actionmanager.h \ - actor.cpp \ - actor.h \ - actorsprite.cpp \ - actorsprite.h \ - actorspritelistener.h \ + being/actor.cpp \ + being/actor.h \ + being/actorsprite.cpp \ + being/actorsprite.h \ + being/actorspritelistener.h \ actorspritemanager.cpp \ actorspritemanager.h \ animatedsprite.cpp \ @@ -586,9 +586,9 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ auctionmanager.h \ avatar.cpp \ avatar.h \ - being.cpp \ - being.h \ - beingcacheentry.h \ + being/being.cpp \ + being/being.h \ + being/beingcacheentry.h \ beingequipbackend.cpp \ beingequipbackend.h \ spellmanager.cpp \ @@ -601,8 +601,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ commandhandler.h \ commands.cpp \ commands.h \ - compoundsprite.cpp \ - compoundsprite.h \ + being/compoundsprite.cpp \ + being/compoundsprite.h \ configlistener.h \ configuration.cpp \ configuration.h \ @@ -664,8 +664,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ input/keyinput.cpp \ input/keyinput.h \ localconsts.h \ - localplayer.cpp \ - localplayer.h \ + being/localplayer.cpp \ + being/localplayer.h \ logger.cpp \ logger.h \ main.cpp \ @@ -698,10 +698,10 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ particle/particleemitterprop.h \ party.cpp \ party.h \ - playerinfo.cpp \ - playerinfo.h \ - playerrelations.cpp \ - playerrelations.h \ + being/playerinfo.cpp \ + being/playerinfo.h \ + being/playerrelations.cpp \ + being/playerrelations.h \ position.cpp \ position.h \ properties.h \ diff --git a/src/actionmanager.cpp b/src/actionmanager.cpp index 091a68c87..ecc3ddc58 100644 --- a/src/actionmanager.cpp +++ b/src/actionmanager.cpp @@ -25,11 +25,12 @@ #include "emoteshortcut.h" #include "game.h" #include "itemshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" -#include "playerrelations.h" #include "soundmanager.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" +#include "being/playerrelations.h" + #include "input/inputevent.h" #include "input/inputmanager.h" diff --git a/src/actor.cpp b/src/actor.cpp deleted file mode 100644 index 0ca168e32..000000000 --- a/src/actor.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "actor.h" - -#include "map.h" - -#include "resources/image.h" -#include "resources/resourcemanager.h" - -#include "debug.h" - -Actor::Actor(): - mMap(nullptr), - mPos(), - mYDiff(0), - mMapActor() -{ -} - -Actor::~Actor() -{ - setMap(nullptr); -} - -void Actor::setMap(Map *const map) -{ - // Remove Actor from potential previous map - if (mMap) - mMap->removeActor(mMapActor); - - mMap = map; - - // Add Actor to potential new map - if (mMap) - mMapActor = mMap->addActor(this); -} - -int Actor::getTileX() const -{ - if (!mMap || !mMap->getTileWidth()) - return 0; - - return getPixelX() / mMap->getTileWidth(); -} - -int Actor::getTileY() const -{ - if (!mMap || !mMap->getTileHeight()) - return 0; - - return getPixelY() / mMap->getTileHeight(); -} diff --git a/src/actor.h b/src/actor.h deleted file mode 100644 index 87e35b002..000000000 --- a/src/actor.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef ACTOR_H -#define ACTOR_H - -#include "vector.h" - -#include - -#include "localconsts.h" - -class Actor; -class Graphics; -class Image; -class Map; - -typedef std::list Actors; -typedef Actors::const_iterator ActorsCIter; - -class Actor -{ -public: - Actor(); - - A_DELETE_COPY(Actor) - - virtual ~Actor(); - - /** - * Draws the Actor to the given graphics context. - * - * Note: this function could be simplified if the graphics context - * would support setting a translation offset. It already does this - * partly with the clipping rectangle support. - */ - virtual bool draw(Graphics *const graphics, - const int offsetX, const int offsetY) const = 0; - - /** - * Returns the horizontal size of the actors graphical representation - * in pixels or 0 when it is undefined. - */ - virtual int getWidth() const A_WARN_UNUSED - { return 0; } - - /** - * Returns the vertical size of the actors graphical representation - * in pixels or 0 when it is undefined. - */ - virtual int getHeight() const A_WARN_UNUSED - { return 0; } - - /** - * Returns the pixel position of this actor. - */ - const Vector &getPosition() const A_WARN_UNUSED - { return mPos; } - - /** - * Sets the pixel position of this actor. - */ - virtual void setPosition(const Vector &pos) - { mPos = pos; } - - /** - * Returns the pixels X coordinate of the actor. - */ - int getPixelX() const A_WARN_UNUSED - { return static_cast(mPos.x); } - - /** - * Returns the pixel Y coordinate of the actor. - */ - virtual int getPixelY() const A_WARN_UNUSED - { return static_cast(mPos.y); } - - /** - * Returns the pixel Y coordinate of the actor for sorting only. - */ - virtual int getSortPixelY() const A_WARN_UNUSED - { return static_cast(mPos.y) - mYDiff; } - - /** - * Returns the x coordinate in tiles of the actor. - */ - virtual int getTileX() const A_WARN_UNUSED; - - /** - * Returns the y coordinate in tiles of the actor. - */ - virtual int getTileY() const A_WARN_UNUSED; - - /** - * Returns the number of Image layers used to draw the actor. - */ - virtual int getNumberOfLayers() const A_WARN_UNUSED - { return 0; } - - /** - * Returns the current alpha value used to draw the actor. - */ - virtual float getAlpha() const A_WARN_UNUSED = 0; - - /** - * Sets the alpha value used to draw the actor. - */ - virtual void setAlpha(float alpha) = 0; - - virtual void setMap(Map *const map); - - const Map* getMap() const A_WARN_UNUSED - { return mMap; } - -protected: - Map *mMap; - Vector mPos; /**< Position in pixels relative to map. */ - int mYDiff; - -private: - Actors::iterator mMapActor; -}; - -#endif // ACTOR_H diff --git a/src/actorsprite.cpp b/src/actorsprite.cpp deleted file mode 100644 index 0b624741a..000000000 --- a/src/actorsprite.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "actorsprite.h" - -#include "actorspritelistener.h" -#include "client.h" -#include "configuration.h" -#include "effectmanager.h" -#include "imagesprite.h" -#include "localplayer.h" -#include "simpleanimation.h" -#include "soundmanager.h" -#include "statuseffect.h" - -#include "gui/theme.h" - -#include "net/net.h" - -#include "resources/imageset.h" -#include "resources/resourcemanager.h" - -#include "utils/checkutils.h" - -#include "debug.h" - -AnimatedSprite *ActorSprite::targetCursor[2][NUM_TC]; -bool ActorSprite::loaded = false; - -ActorSprite::ActorSprite(const int id) : - CompoundSprite(), - Actor(), - mStatusEffects(), - mStunParticleEffects(), - mStatusParticleEffects(&mStunParticleEffects, false), - mChildParticleEffects(&mStatusParticleEffects, false), - mId(id), - mStunMode(0), - mUsedTargetCursor(nullptr), - mActorSpriteListeners(), - mCursorPaddingX(0), - mCursorPaddingY(0), - mMustResetParticles(false) -{ -} - -ActorSprite::~ActorSprite() -{ - setMap(nullptr); - - mUsedTargetCursor = nullptr; - - if (player_node && player_node->getTarget() == this) - player_node->setTarget(nullptr); - - // Notify listeners of the destruction. - FOR_EACH (ActorSpriteListenerIterator, iter, mActorSpriteListeners) - { - if (reportFalse(*iter)) - (*iter)->actorSpriteDestroyed(*this); - } -} - -bool ActorSprite::draw(Graphics *const graphics, - const int offsetX, const int offsetY) const -{ - FUNC_BLOCK("ActorSprite::draw", 1) - const int px = getPixelX() + offsetX - 16; - // Temporary fix to the Y offset. -#ifdef MANASERV_SUPPORT - const int py = getPixelY() + offsetY - - ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); -#else - const int py = getPixelY() + offsetY - 32; -#endif - - if (mUsedTargetCursor) - { - mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); - mUsedTargetCursor->draw(graphics, - px + getTargetOffsetX() - mCursorPaddingX, - py + getTargetOffsetY() - mCursorPaddingY); - } - - return drawSpriteAt(graphics, px, py); -} - -bool ActorSprite::drawSpriteAt(Graphics *const graphics, - const int x, const int y) const -{ - return CompoundSprite::draw(graphics, x, y); -} - -void ActorSprite::logic() -{ - BLOCK_START("ActorSprite::logic") - // Update sprite animations - update(tick_time * MILLISECONDS_IN_A_TICK); - - // Restart status/particle effects, if needed - if (mMustResetParticles) - { - mMustResetParticles = false; - FOR_EACH (std::set::const_iterator, it, mStatusEffects) - { - const StatusEffect *const effect - = StatusEffect::getStatusEffect(*it, true); - if (effect && effect->particleEffectIsPersistent()) - updateStatusEffect(*it, true); - } - } - - // Update particle effects - mChildParticleEffects.moveTo(mPos.x, mPos.y); - BLOCK_END("ActorSprite::logic") -} - -void ActorSprite::actorLogic() -{ -} - -void ActorSprite::setMap(Map *const map) -{ - Actor::setMap(map); - - // Clear particle effect list because child particles became invalid - mChildParticleEffects.clear(); - mMustResetParticles = true; // Reset status particles on next redraw -} - -void ActorSprite::controlParticle(Particle *const particle) -{ - mChildParticleEffects.addLocally(particle); -} - -void ActorSprite::setTargetType(const TargetCursorType type) -{ - static const int targetWidths[ActorSprite::NUM_TC] = {0, 0, 0}; - static const int targetHeights[ActorSprite::NUM_TC] = {-16, -16, -32}; - - if (type == TCT_NONE) - { - untarget(); - } - else - { - const TargetCursorSize sz = getTargetCursorSize(); - mUsedTargetCursor = targetCursor[type][sz]; - if (mUsedTargetCursor) - { - mCursorPaddingX = targetWidths[sz]; - mCursorPaddingY = targetHeights[sz]; - } - } -} - -struct EffectDescription final -{ - std::string mGFXEffect; - std::string mSFXEffect; -}; - -void ActorSprite::setStatusEffect(const int index, const bool active) -{ - const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end(); - - if (active != wasActive) - { - updateStatusEffect(index, active); - if (active) - mStatusEffects.insert(index); - else - mStatusEffects.erase(index); - } -} - -void ActorSprite::setStatusEffectBlock(const int offset, - const uint16_t newEffects) -{ - for (unsigned i = 0; i < STATUS_EFFECTS; i++) - { - const int index = StatusEffect::blockEffectIndexToEffectIndex( - offset + i); - - if (index != -1) - setStatusEffect(index, (newEffects & (1 << i)) > 0); - } -} - -void ActorSprite::updateStunMode(const int oldMode, const int newMode) -{ - handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); - handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); -} - -void ActorSprite::updateStatusEffect(const int index, const bool newStatus) -{ - handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); -} - -void ActorSprite::handleStatusEffect(StatusEffect *const effect, - const int effectId) -{ - if (!effect) - return; - - Particle *const particle = effect->getParticle(); - - if (effectId >= 0) - { - mStatusParticleEffects.setLocally(effectId, particle); - } - else - { - mStunParticleEffects.clearLocally(); - if (particle) - mStunParticleEffects.addLocally(particle); - } -} - -void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, - const bool forceDisplay, - const int imageType, - const std::string &color) -{ - clear(); - - FOR_EACH (SpriteRefs, it, display.sprites) - { - if (!*it) - continue; - const std::string file = paths.getStringValue("sprites").append( - combineDye3((*it)->sprite, color)); - - const int variant = (*it)->variant; - addSprite(AnimatedSprite::delayedLoad(file, variant)); - } - - // Ensure that something is shown, if desired - if (empty() && forceDisplay) - { - if (display.image.empty()) - { - addSprite(AnimatedSprite::delayedLoad( - paths.getStringValue("sprites").append( - paths.getStringValue("spriteErrorFile")))); - } - else - { - ResourceManager *const resman = ResourceManager::getInstance(); - std::string imagePath; - switch (imageType) - { - case 0: - default: - imagePath = paths.getStringValue("itemIcons").append( - display.image); - break; - case 1: - imagePath = paths.getStringValue("itemIcons").append( - display.floor); - break; - } - imagePath = combineDye2(imagePath, color); - - Image *img = resman->getImage(imagePath); - - if (!img) - img = Theme::getImageFromTheme("unknown-item.png"); - - addSprite(new ImageSprite(img)); - if (img) - img->decRef(); - } - } - - mChildParticleEffects.clear(); - - // setup particle effects - if (Particle::enabled && particleEngine) - { - FOR_EACH (StringVectCIter, itr, display.particles) - { - Particle *const p = particleEngine->addEffect(*itr, 0, 0); - controlParticle(p); - } - } - - mMustResetParticles = true; -} - -void ActorSprite::load() -{ - if (loaded) - unload(); - - initTargetCursor(); - - loaded = true; -} - -void ActorSprite::unload() -{ - if (reportTrue(!loaded)) - return; - - cleanupTargetCursors(); - loaded = false; -} - -void ActorSprite::addActorSpriteListener(ActorSpriteListener *const listener) -{ - mActorSpriteListeners.push_front(listener); -} - -void ActorSprite::removeActorSpriteListener(ActorSpriteListener *const - listener) -{ - mActorSpriteListeners.remove(listener); -} - -static const char *cursorType(const int type) -{ - switch (type) - { - case ActorSprite::TCT_IN_RANGE: - return "in-range"; - default: - case ActorSprite::TCT_NORMAL: - return "normal"; - } -} - -static const char *cursorSize(const int size) -{ - switch (size) - { - case ActorSprite::TC_LARGE: - return "l"; - case ActorSprite::TC_MEDIUM: - return "m"; - default: - case ActorSprite::TC_SMALL: - return "s"; - } -} - -void ActorSprite::initTargetCursor() -{ - static const std::string targetCursorFile = "%s/target-cursor-%s-%s.xml"; - - const std::string path = branding.getStringValue("guiPath"); - - // Load target cursors - for (int size = TC_SMALL; size < NUM_TC; size++) - { - for (int type = TCT_NORMAL; type < NUM_TCT; type++) - { - targetCursor[type][size] = AnimatedSprite::load(strprintf( - targetCursorFile.c_str(), path.c_str(), cursorType(type), - cursorSize(size))); - } - } -} - -void ActorSprite::cleanupTargetCursors() -{ - for (int size = TC_SMALL; size < NUM_TC; size++) - { - for (int type = TCT_NORMAL; type < NUM_TCT; type++) - { - if (targetCursor[type][size]) - { - delete targetCursor[type][size]; - targetCursor[type][size] = nullptr; - } - } - } -} diff --git a/src/actorsprite.h b/src/actorsprite.h deleted file mode 100644 index c8b1e03df..000000000 --- a/src/actorsprite.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef ACTORSPRITE_H -#define ACTORSPRITE_H - -#include "actor.h" -#include "compoundsprite.h" -#include "localconsts.h" -#include "map.h" - -#include "particle/particlecontainer.h" - -#include - -#include -#include - -#include "localconsts.h" - -class AnimatedSprite; -class StatusEffect; -class ActorSpriteListener; - -class ActorSprite : public CompoundSprite, public Actor -{ -public: - enum Type - { - UNKNOWN = 0, - PLAYER, - NPC, - MONSTER, - FLOOR_ITEM, - PORTAL, - PET, - AVATAR - }; - - enum TargetCursorSize - { - TC_SMALL = 0, - TC_MEDIUM, - TC_LARGE, - NUM_TC - }; - - enum TargetCursorType - { - TCT_NONE = -1, - TCT_NORMAL = 0, - TCT_IN_RANGE, - NUM_TCT - }; - - explicit ActorSprite(const int id); - - A_DELETE_COPY(ActorSprite) - - ~ActorSprite(); - - int getId() const A_WARN_UNUSED - { return mId; } - - void setId(const int id) - { mId = id; } - - /** - * Returns the type of the ActorSprite. - */ - virtual Type getType() const A_WARN_UNUSED - { return UNKNOWN; } - - virtual bool draw(Graphics *const graphics, - const int offsetX, const int offsetY) const override; - - virtual bool drawSpriteAt(Graphics *const graphics, - const int x, const int y) const; - - virtual void logic(); - - static void actorLogic(); - - void setMap(Map *const map) override; - - /** - * Gets the way the object blocks pathfinding for other objects - */ - virtual Map::BlockType getBlockType() const A_WARN_UNUSED - { return Map::BLOCKTYPE_NONE; } - - /** - * Take control of a particle. - */ - void controlParticle(Particle *const particle); - - /** - * Returns the required size of a target cursor for this being. - */ - virtual TargetCursorSize getTargetCursorSize() const A_WARN_UNUSED - { return TC_MEDIUM; } - - virtual int getTargetOffsetX() const A_WARN_UNUSED - { return 0; } - - virtual int getTargetOffsetY() const A_WARN_UNUSED - { return 0; } - - /** - * Sets the target animation for this actor. - */ - void setTargetType(const TargetCursorType type); - - /** - * Untargets the actor. - */ - void untarget() - { mUsedTargetCursor = nullptr; } - - /** - * Sets the actor's stun mode. If zero, the being is `normal', otherwise it - * is `stunned' in some fashion. - */ - void setStunMode(const uint16_t stunMode) - { - if (mStunMode != stunMode) - updateStunMode(mStunMode, stunMode); - mStunMode = stunMode; - } - - void setStatusEffect(const int index, const bool active); - - /** - * A status effect block is a 16 bit mask of status effects. We assign each - * such flag a block ID of offset + bitnr. - * - * These are NOT the same as the status effect indices. - */ - void setStatusEffectBlock(const int offset, const uint16_t flags); - - virtual void setAlpha(const float alpha) override - { CompoundSprite::setAlpha(alpha); } - - virtual float getAlpha() const override A_WARN_UNUSED - { return CompoundSprite::getAlpha(); } - - virtual int getWidth() const override A_WARN_UNUSED - { return CompoundSprite::getWidth(); } - - virtual int getHeight() const override A_WARN_UNUSED - { return CompoundSprite::getHeight(); } - - static void load(); - - static void unload(); - - /** - * Add an ActorSprite listener. - */ - void addActorSpriteListener(ActorSpriteListener *const listener); - - /** - * Remove an ActorSprite listener. - */ - void removeActorSpriteListener(ActorSpriteListener *const listener); - -protected: - /** - * Notify self that the stun mode has been updated. Invoked by - * setStunMode if something changed. - */ - virtual void updateStunMode(const int oldMode, const int newMode); - - /** - * Notify self that a status effect has flipped. - * The new flag is passed. - */ - virtual void updateStatusEffect(const int index, const bool newStatus); - - /** - * Handle an update to a status or stun effect - * - * \param The StatusEffect to effect - * \param effectId -1 for stun, otherwise the effect index - */ - virtual void handleStatusEffect(StatusEffect *const effect, - const int effectId); - - void setupSpriteDisplay(const SpriteDisplay &display, - const bool forceDisplay = true, - const int imageType = 0, - const std::string &color = ""); - - std::set mStatusEffects; /**< set of active status effects */ - - ParticleList mStunParticleEffects; - ParticleVector mStatusParticleEffects; - ParticleList mChildParticleEffects; - int mId; - uint16_t mStunMode; /**< Stun mode; zero if not stunned */ - -private: - /** Load the target cursors into memory */ - static void initTargetCursor(); - - /** Remove the target cursors from memory */ - static void cleanupTargetCursors(); - - /** Animated target cursors. */ - static AnimatedSprite *targetCursor[NUM_TCT][NUM_TC]; - - static bool loaded; - - /** Target cursor being used */ - AnimatedSprite *mUsedTargetCursor; - - typedef std::list ActorSpriteListeners; - typedef ActorSpriteListeners::iterator ActorSpriteListenerIterator; - ActorSpriteListeners mActorSpriteListeners; - - int mCursorPaddingX; - int mCursorPaddingY; - - /** Reset particle status effects on next redraw? */ - bool mMustResetParticles; -}; - -#endif // ACTORSPRITE_H diff --git a/src/actorspritelistener.h b/src/actorspritelistener.h deleted file mode 100644 index 6b75f957b..000000000 --- a/src/actorspritelistener.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef ACTORSPRITELISTENER_H -#define ACTORSPRITELISTENER_H - -class ActorSprite; - -class ActorSpriteListener -{ - public: - /** - * Destructor. - */ - virtual ~ActorSpriteListener() - { } - - /** - * Called when the ActorSprite has been destroyed. The listener will - * have to be registered first. - * @param actorSprite the ActorSprite being destroyed. - */ - virtual void actorSpriteDestroyed(const ActorSprite &actorSprite) = 0; -}; - -#endif // ACTORSPRITELISTENER_H diff --git a/src/actorspritemanager.cpp b/src/actorspritemanager.cpp index cd2a62e04..62557d74c 100644 --- a/src/actorspritemanager.cpp +++ b/src/actorspritemanager.cpp @@ -24,10 +24,11 @@ #include "client.h" #include "configuration.h" -#include "localplayer.h" #include "main.h" -#include "playerinfo.h" -#include "playerrelations.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" +#include "being/playerrelations.h" #include "gui/chatwindow.h" #include "gui/equipmentwindow.h" diff --git a/src/actorspritemanager.h b/src/actorspritemanager.h index 1a599a865..74ddd7a32 100644 --- a/src/actorspritemanager.h +++ b/src/actorspritemanager.h @@ -23,9 +23,10 @@ #ifndef ACTORSPRITEMANAGER_H #define ACTORSPRITEMANAGER_H -#include "being.h" #include "flooritem.h" +#include "being/being.h" + #include "localconsts.h" class LocalPlayer; diff --git a/src/auctionmanager.cpp b/src/auctionmanager.cpp index e5b253933..c083c9252 100644 --- a/src/auctionmanager.cpp +++ b/src/auctionmanager.cpp @@ -23,7 +23,8 @@ #include "actorspritemanager.h" #include "client.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/chathandler.h" #include "net/net.h" diff --git a/src/avatar.cpp b/src/avatar.cpp index 4fbf4f01f..41de4b3b7 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -22,7 +22,7 @@ #include "avatar.h" -#include "localplayer.h" +#include "being/localplayer.h" #include diff --git a/src/being.cpp b/src/being.cpp deleted file mode 100644 index 535c0c0e3..000000000 --- a/src/being.cpp +++ /dev/null @@ -1,3040 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "being.h" - -#include "actorspritemanager.h" -#include "animatedsprite.h" -#include "beingcacheentry.h" -#include "beingequipbackend.h" -#include "client.h" -#include "effectmanager.h" -#include "guild.h" -#include "party.h" -#include "playerrelations.h" -#include "soundconsts.h" -#include "soundmanager.h" -#include "text.h" - -#include "particle/particle.h" - -#include "gui/equipmentwindow.h" -#include "gui/socialwindow.h" -#include "gui/speechbubble.h" -#include "gui/sdlfont.h" -#include "gui/skilldialog.h" - -#include "net/charserverhandler.h" -#include "net/gamehandler.h" -#include "net/inventoryhandler.h" -#include "net/net.h" -#include "net/npchandler.h" -#include "net/playerhandler.h" - -#include "resources/avatardb.h" -#include "resources/emotedb.h" -#include "resources/iteminfo.h" -#include "resources/monsterdb.h" -#include "resources/npcdb.h" -#include "resources/petdb.h" -#include "resources/resourcemanager.h" - -#include "gui/widgets/langtab.h" -#include "gui/widgets/skillinfo.h" - -#include "utils/gettext.h" - -#include - -#include "debug.h" - -const unsigned int CACHE_SIZE = 50; - -int Being::mNumberOfHairstyles = 1; -int Being::mNumberOfRaces = 1; - -int Being::mUpdateConfigTime = 0; -unsigned int Being::mConfLineLim = 0; -int Being::mSpeechType = 0; -bool Being::mHighlightMapPortals = false; -bool Being::mHighlightMonsterAttackRange = false; -bool Being::mLowTraffic = true; -bool Being::mDrawHotKeys = true; -bool Being::mShowBattleEvents = false; -bool Being::mShowMobHP = false; -bool Being::mShowOwnHP = false; -bool Being::mShowGender = false; -bool Being::mShowLevel = false; -bool Being::mShowPlayersStatus = false; -bool Being::mEnableReorderSprites = true; -bool Being::mHideErased = false; -bool Being::mMoveNames = false; -int Being::mAwayEffect = -1; - -std::list beingInfoCache; -typedef std::map::const_iterator GuildsMapCIter; -typedef std::map::const_iterator IntMapCIter; - -Being::Being(const int id, const Type type, const uint16_t subtype, - Map *const map) : - ActorSprite(id), - mNextSound(), - mInfo(BeingInfo::unknown), - mEmotionSprite(nullptr), - mAnimationEffect(nullptr), - mSpriteAction(SpriteAction::STAND), - mName(), - mRaceName(), - mPartyName(), - mGuildName(), - mSpeech(), - mDispName(nullptr), - mNameColor(nullptr), - mEquippedWeapon(nullptr), - mPath(), - mText(nullptr), - mTextColor(nullptr), - mDest(), - mSpriteColors(), - mSpriteIDs(), - mSpriteColorsIds(), - mGuilds(), - mParty(nullptr), - mActionTime(0), - mEmotionTime(0), - mSpeechTime(0), - mAttackSpeed(350), - mLevel(0), - mAttackRange(1), - mGender(GENDER_UNSPECIFIED), - mAction(STAND), - mSubType(0xFFFF), - mDirection(DOWN), - mDirectionDelayed(0), - mSpriteDirection(DIRECTION_DOWN), - mShowName(false), - mIsGM(false), - mType(type), - mSpeechBubble(new SpeechBubble), - mWalkSpeed(Net::getPlayerHandler()->getDefaultWalkSpeed()), - mIp(), - mSpriteRemap(new int[20]), - mSpriteHide(new int[20]), - mComment(), - mPet(nullptr), - mOwner(nullptr), - mSpecialParticle(nullptr), - mX(0), - mY(0), - mDamageTaken(0), - mHP(0), - mMaxHP(0), - mDistance(0), - mIsReachable(REACH_UNKNOWN), - mGoodStatus(-1), - mMoveTime(0), - mAttackTime(0), - mTalkTime(0), - mOtherTime(0), - mTestTime(cur_time), - mAttackDelay(0), - mMinHit(0), - mMaxHit(0), - mCriticalHit(0), - mPvpRank(0), - mNumber(100), - mPetId(0), - mLook(0), - mHairColor(0), - mErased(false), - mEnemy(false), - mGotComment(false), - mAdvanced(false), - mShop(false), - mAway(false), - mInactive(false) -{ - for (int f = 0; f < 20; f ++) - { - mSpriteRemap[f] = f; - mSpriteHide[f] = 0; - } - - setMap(map); - setSubtype(subtype, 0); - - if (mType == PLAYER) - mShowName = config.getBoolValue("visiblenames"); - else if (mType != NPC) - mGotComment = true; - - config.addListener("visiblenames", this); - - reReadConfig(); - - if (mType == NPC) - setShowName(true); - else - setShowName(mShowName); - - updateColors(); - updatePercentHP(); -} - -Being::~Being() -{ - config.removeListener("visiblenames", this); - - delete [] mSpriteRemap; - mSpriteRemap = nullptr; - delete [] mSpriteHide; - mSpriteHide = nullptr; - - delete mSpeechBubble; - mSpeechBubble = nullptr; - delete mDispName; - mDispName = nullptr; - delete mText; - mText = nullptr; - - delete mEmotionSprite; - mEmotionSprite = nullptr; - delete mAnimationEffect; - mAnimationEffect = nullptr; - - if (mOwner) - mOwner->setPet(nullptr); - if (mPet) - mPet->setOwner(nullptr); -} - -void Being::setSubtype(const uint16_t subtype, const uint8_t look) -{ - if (!mInfo) - return; - - if (subtype == mSubType && mLook == look) - return; - - mSubType = subtype; - mLook = look; - - if (mType == MONSTER) - { - mInfo = MonsterDB::get(mSubType); - if (mInfo) - { - setName(mInfo->getName()); - setupSpriteDisplay(mInfo->getDisplay(), true, 0, - mInfo->getColor(mLook)); - mYDiff = mInfo->getSortOffsetY(); - } - } - else if (mType == NPC) - { - mInfo = NPCDB::get(mSubType); - if (mInfo) - { - setupSpriteDisplay(mInfo->getDisplay(), false); - mYDiff = mInfo->getSortOffsetY(); - } - } - else if (mType == AVATAR) - { - mInfo = AvatarDB::get(mSubType); - if (mInfo) - setupSpriteDisplay(mInfo->getDisplay(), false); - } - else if (mType == PET) - { - mInfo = PETDB::get(mId); - if (mInfo) - { - setupSpriteDisplay(mInfo->getDisplay(), false); - mYDiff = mInfo->getSortOffsetY(); - } - } - else if (mType == PLAYER) - { - int id = -100 - subtype; - - // Prevent showing errors when sprite doesn't exist - if (!ItemDB::exists(id)) - { - id = -100; - // TRANSLATORS: default race name - setRaceName(_("Human")); - if (Net::getCharServerHandler()) - setSprite(Net::getCharServerHandler()->baseSprite(), id); - } - else - { - const ItemInfo &info = ItemDB::get(id); - setRaceName(info.getName()); - if (Net::getCharServerHandler()) - { - setSprite(Net::getCharServerHandler()->baseSprite(), - id, info.getColor(mLook)); - } - } - } -} - -ActorSprite::TargetCursorSize Being::getTargetCursorSize() const -{ - if (!mInfo) - return ActorSprite::TC_SMALL; - - return mInfo->getTargetCursorSize(); -} - -void Being::setPosition(const Vector &pos) -{ - Actor::setPosition(pos); - - updateCoords(); - - if (mText) - { - mText->adviseXY(static_cast(pos.x), static_cast(pos.y) - - getHeight() - mText->getHeight() - 6, mMoveNames); - } -} - -void Being::setDestination(const int dstX, const int dstY) -{ - // We can't calculate anything without a map anyway. - if (!mMap) - return; - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - setPath(mMap->findPath(mX, mY, dstX, dstY, getWalkMask())); - return; - } - -#ifdef MANASERV_SUPPORT - // Don't handle flawed destinations from server... - if (dstX == 0 || dstY == 0) - return; - - // If the destination is unwalkable, don't bother trying to get there - if (!mMap->getWalk(dstX / 32, dstY / 32)) - return; - - Position dest = mMap->checkNodeOffsets(getCollisionRadius(), getWalkMask(), - dstX, dstY); - Path thisPath = mMap->findPixelPath(static_cast(mPos.x), - static_cast(mPos.y), dest.x, dest.y, - static_cast(getCollisionRadius()), - static_cast(getWalkMask())); - - if (thisPath.empty()) - { - // If there is no path but the destination is on the same walkable tile, - // we accept it. - if (static_cast(mPos.x) / 32 == dest.x / 32 - && static_cast(mPos.y) / 32 == dest.y / 32) - { - mDest.x = static_cast(dest.x); - mDest.y = static_cast(dest.y); - } - setPath(Path()); - return; - } - - // The destination is valid, so we set it. - mDest.x = static_cast(dest.x); - mDest.y = static_cast(dest.y); - - setPath(thisPath); -#endif -} - -void Being::clearPath() -{ - mPath.clear(); -} - -void Being::setPath(const Path &path) -{ - mPath = path; - if (mPath.empty()) - return; - -#ifdef MANASERV_SUPPORT - if ((Net::getNetworkType() != ServerInfo::MANASERV) && - mAction != MOVE && mAction != DEAD) -#else - if (mAction != MOVE && mAction != DEAD) -#endif - { - nextTile(); - mActionTime = tick_time; - } -} - -void Being::setSpeech(const std::string &text, const std::string &channel, - int time) -{ - if (!userPalette) - return; - - if (!channel.empty() && (langChatTab && langChatTab->getChannelName() - != channel)) - { - return; - } - - // Remove colors - mSpeech = removeColors(text); - - // Trim whitespace - trim(mSpeech); - - const unsigned int lineLim = mConfLineLim; - if (lineLim > 0 && mSpeech.length() > lineLim) - mSpeech = mSpeech.substr(0, lineLim); - - trim(mSpeech); - if (mSpeech.empty()) - return; - - if (!time) - { - const size_t sz = mSpeech.size(); - if (sz < 200) - time = static_cast(SPEECH_TIME - 300 + (3 * sz)); - } - - if (time < static_cast(SPEECH_MIN_TIME)) - time = static_cast(SPEECH_MIN_TIME); - - // Check for links - size_t start = mSpeech.find('['); - size_t e = mSpeech.find(']', start); - - while (start != std::string::npos && e != std::string::npos) - { - // Catch multiple embeds and ignore them so it doesn't crash the client. - while ((mSpeech.find('[', start + 1) != std::string::npos) && - (mSpeech.find('[', start + 1) < e)) - { - start = mSpeech.find('[', start + 1); - } - - size_t position = mSpeech.find('|'); - if (mSpeech[start + 1] == '@' && mSpeech[start + 2] == '@') - { - mSpeech.erase(e, 1); - mSpeech.erase(start, (position - start) + 1); - } - position = mSpeech.find('@'); - - while (position != std::string::npos) - { - mSpeech.erase(position, 2); - position = mSpeech.find('@'); - } - - start = mSpeech.find('[', start + 1); - e = mSpeech.find(']', start); - } - - if (!mSpeech.empty()) - { - mSpeechTime = time <= static_cast(SPEECH_MAX_TIME) - ? time : static_cast(SPEECH_MAX_TIME); - } - - const int speech = mSpeechType; - if (speech == TEXT_OVERHEAD && userPalette) - { - delete mText; - - mText = new Text(mSpeech, - getPixelX(), getPixelY() - getHeight(), - gcn::Graphics::CENTER, - &userPalette->getColor(UserPalette::PARTICLE), - true); - } -} - -void Being::takeDamage(Being *const attacker, const int amount, - const AttackType type, const int attackId) -{ - if (!userPalette || !attacker) - return; - - gcn::Font *font = nullptr; - // TRANSLATORS: hit or miss message in attacks - const std::string damage = amount ? toString(amount) : type == FLEE ? - _("dodge") : _("miss"); - const gcn::Color *color; - - if (gui) - font = gui->getInfoParticleFont(); - - // Selecting the right color - if (type == CRITICAL || type == FLEE) - { - if (type == CRITICAL) - attacker->setCriticalHit(amount); - - if (attacker == player_node) - { - color = &userPalette->getColor( - UserPalette::HIT_LOCAL_PLAYER_CRITICAL); - } - else - { - color = &userPalette->getColor(UserPalette::HIT_CRITICAL); - } - } - else if (!amount) - { - if (attacker == player_node) - { - // This is intended to be the wrong direction to visually - // differentiate between hits and misses - color = &userPalette->getColor(UserPalette::HIT_LOCAL_PLAYER_MISS); - } - else - { - color = &userPalette->getColor(UserPalette::MISS); - } - } - else if (mType == MONSTER) - { - if (attacker == player_node) - { - color = &userPalette->getColor( - UserPalette::HIT_LOCAL_PLAYER_MONSTER); - } - else - { - color = &userPalette->getColor( - UserPalette::HIT_PLAYER_MONSTER); - } - } - else if (mType == PLAYER && attacker != player_node - && this == player_node) - { - // here player was attacked by other player. mark him as enemy. - color = &userPalette->getColor(UserPalette::HIT_PLAYER_PLAYER); - attacker->setEnemy(true); - attacker->updateColors(); - } - else - { - color = &userPalette->getColor(UserPalette::HIT_MONSTER_PLAYER); - } - - if (chatWindow && mShowBattleEvents) - { - if (this == player_node) - { - if (attacker->mType == PLAYER || amount) - { - chatWindow->battleChatLog(strprintf("%s : Hit you -%d", - attacker->getName().c_str(), amount), BY_OTHER); - } - } - else if (attacker == player_node && amount) - { - chatWindow->battleChatLog(strprintf("%s : You hit %s -%d", - attacker->getName().c_str(), getName().c_str(), amount), - BY_PLAYER); - } - } - if (font && particleEngine) - { - // Show damage number - particleEngine->addTextSplashEffect(damage, - getPixelX(), getPixelY() - 16, color, font, true); - } - - if (type != SKILL) - attacker->updateHit(amount); - - if (amount > 0) - { - if (player_node && player_node == this) - player_node->setLastHitFrom(attacker->getName()); - - mDamageTaken += amount; - if (mInfo) - { - playSfx(mInfo->getSound(SOUND_EVENT_HURT), this, false, mX, mY); - - if (!mInfo->isStaticMaxHP()) - { - if (!mHP && mInfo->getMaxHP() < mDamageTaken) - mInfo->setMaxHP(mDamageTaken); - } - } - if (mHP && isAlive()) - { - mHP -= amount; - if (mHP < 0) - mHP = 0; - } - - if (mType == MONSTER) - { - updatePercentHP(); - updateName(); - } - else if (mType == PLAYER && socialWindow && getName() != "") - { - socialWindow->updateAvatar(getName()); - } - - if (effectManager) - { - const int hitEffectId = getHitEffect(attacker, type, attackId); - if (hitEffectId >= 0) - effectManager->trigger(hitEffectId, this); - } - } - else - { - if (effectManager) - { - const int hitEffectId = getHitEffect(attacker, - MISS, attackId); - if (hitEffectId >= 0) - effectManager->trigger(hitEffectId, this); - } - } -} - -int Being::getHitEffect(const Being *const attacker, - const AttackType type, const int attackId) const -{ - if (!effectManager) - return 0; - - // Init the particle effect path based on current - // weapon or default. - int hitEffectId = 0; - if (type != SKILL) - { - if (attacker) - { - const ItemInfo *attackerWeapon = attacker->getEquippedWeapon(); - if (attackerWeapon && attacker->getType() == PLAYER) - { - if (type == MISS) - hitEffectId = attackerWeapon->getMissEffectId(); - else if (type != CRITICAL) - hitEffectId = attackerWeapon->getHitEffectId(); - else - hitEffectId = attackerWeapon->getCriticalHitEffectId(); - } - else if (attacker->getType() == MONSTER) - { - const BeingInfo *const info = attacker->getInfo(); - if (info) - { - const Attack *atk = info->getAttack(attackId); - if (atk) - { - if (type == MISS) - hitEffectId = atk->mMissEffectId; - else if (type != CRITICAL) - hitEffectId = atk->mHitEffectId; - else - hitEffectId = atk->mCriticalHitEffectId; - } - else - { - hitEffectId = getDefaultEffectId(type); - } - } - } - else - { - hitEffectId = getDefaultEffectId(type); - } - } - else - { - hitEffectId = getDefaultEffectId(type); - } - } - else - { - // move skills effects to +100000 in effects list - hitEffectId = attackId + 100000; - } - return hitEffectId; -} - -int Being::getDefaultEffectId(const int type) -{ - if (type == MISS) - return paths.getIntValue("missEffectId"); - else if (type != CRITICAL) - return paths.getIntValue("hitEffectId"); - else - return paths.getIntValue("criticalHitEffectId"); -} - -void Being::handleAttack(Being *const victim, const int damage, - const int attackId) -{ - if (!victim || !mInfo) - return; - - if (this != player_node) - setAction(Being::ATTACK, attackId); - - if (mType == PLAYER && mEquippedWeapon) - fireMissile(victim, mEquippedWeapon->getMissileParticleFile()); - else if (mInfo->getAttack(attackId)) - fireMissile(victim, mInfo->getAttack(attackId)->mMissileParticle); - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - reset(); - mActionTime = tick_time; - } - - if (this != player_node) - { - const uint8_t dir = calcDirection(victim->getTileX(), - victim->getTileY()); - if (dir) - setDirection(dir); - } - if (damage && victim->mType == PLAYER && victim->mAction == SIT) - victim->setAction(STAND); - - if (mType == PLAYER) - { - if (mSpriteIDs.size() >= 10) - { - // here 10 is weapon slot - int weaponId = mSpriteIDs[10]; - if (!weaponId) - weaponId = -100 - mSubType; - const ItemInfo &info = ItemDB::get(weaponId); - playSfx(info.getSound((damage > 0) ? - SOUND_EVENT_HIT : SOUND_EVENT_MISS), victim, true, mX, mY); - } - } - else - { - playSfx(mInfo->getSound((damage > 0) ? - SOUND_EVENT_HIT : SOUND_EVENT_MISS), victim, true, mX, mY); - } -} - -void Being::handleSkill(Being *const victim, const int damage, - const int skillId, const int skillLevel) -{ - if (!victim || !mInfo || !skillDialog) - return; - - if (this != player_node) - setAction(Being::ATTACK, 1); - - SkillInfo *const skill = skillDialog->getSkill(skillId); - const SkillData *const data = skill - ? skill->getData1(skillLevel) : nullptr; - if (data) - fireMissile(victim, data->particle); - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - reset(); - mActionTime = tick_time; - } - - if (this != player_node) - { - const uint8_t dir = calcDirection(victim->getTileX(), - victim->getTileY()); - if (dir) - setDirection(dir); - } - if (damage && victim->mType == PLAYER && victim->mAction == SIT) - victim->setAction(STAND); - if (data) - { - if (damage > 0) - playSfx(data->soundHit, victim, true, mX, mY); - else - playSfx(data->soundMiss, victim, true, mX, mY); - } - else - { - playSfx(mInfo->getSound((damage > 0) ? - SOUND_EVENT_HIT : SOUND_EVENT_MISS), victim, true, mX, mY); - } -} - -void Being::setName(const std::string &name) -{ - if (mType == NPC) - { - mName = name.substr(0, name.find('#', 0)); - showName(); - } - else - { - mName = name; - - if (mType == PLAYER && getShowName()) - showName(); - } -} - -void Being::setShowName(const bool doShowName) -{ - if (mShowName == doShowName) - return; - - mShowName = doShowName; - - if (doShowName) - { - showName(); - } - else - { - delete mDispName; - mDispName = nullptr; - } -} - -void Being::setGuildName(const std::string &name) -{ - mGuildName = name; -} - -void Being::setGuildPos(const std::string &pos A_UNUSED) -{ -} - -void Being::addGuild(Guild *const guild) -{ - if (!guild) - return; - - mGuilds[guild->getId()] = guild; - - if (this == player_node && socialWindow) - socialWindow->addTab(guild); -} - -void Being::removeGuild(const int id) -{ - if (this == player_node && socialWindow) - socialWindow->removeTab(mGuilds[id]); - - if (mGuilds[id]) - mGuilds[id]->removeMember(getName()); - mGuilds.erase(id); -} - -Guild *Being::getGuild(const std::string &guildName) const -{ - FOR_EACH (GuildsMapCIter, itr, mGuilds) - { - Guild *const guild = itr->second; - if (guild && guild->getName() == guildName) - return guild; - } - - return nullptr; -} - -Guild *Being::getGuild(const int id) const -{ - const std::map::const_iterator itr = mGuilds.find(id); - if (itr != mGuilds.end()) - return itr->second; - - return nullptr; -} - -Guild *Being::getGuild() const -{ - const std::map::const_iterator itr = mGuilds.begin(); - if (itr != mGuilds.end()) - return itr->second; - - return nullptr; -} - -void Being::clearGuilds() -{ - FOR_EACH (GuildsMapCIter, itr, mGuilds) - { - Guild *const guild = itr->second; - - if (guild) - { - if (this == player_node && socialWindow) - socialWindow->removeTab(guild); - - guild->removeMember(mId); - } - } - - mGuilds.clear(); -} - -void Being::setParty(Party *const party) -{ - if (party == mParty) - return; - - Party *const old = mParty; - mParty = party; - - if (old) - old->removeMember(mId); - - if (party) - party->addMember(mId, mName); - - updateColors(); - - if (this == player_node && socialWindow) - { - if (old) - socialWindow->removeTab(old); - - if (party) - socialWindow->addTab(party); - } -} - -void Being::updateGuild() -{ - if (!player_node) - return; - - Guild *const guild = player_node->getGuild(); - if (!guild) - { - clearGuilds(); - updateColors(); - return; - } - if (guild->getMember(getName())) - { - setGuild(guild); - if (!guild->getName().empty()) - mGuildName = guild->getName(); - } - updateColors(); -} - -void Being::setGuild(Guild *const guild) -{ - Guild *const old = getGuild(); - if (guild == old) - return; - - clearGuilds(); - addGuild(guild); - - if (old) - old->removeMember(mName); - - updateColors(); - - if (this == player_node && socialWindow) - { - if (old) - socialWindow->removeTab(old); - - if (guild) - socialWindow->addTab(guild); - } -} - -void Being::fireMissile(Being *const victim, const std::string &particle) const -{ - if (!victim || particle.empty() || !particleEngine) - return; - - Particle *const target = particleEngine->createChild(); - - if (!target) - return; - - Particle *const missile = target->addEffect( - particle, getPixelX(), getPixelY()); - - if (missile) - { - target->moveBy(Vector(0.0f, 0.0f, 32.0f)); - target->setLifetime(1000); - victim->controlParticle(target); - - missile->setDestination(target, 7, 0); - missile->setDieDistance(8); - missile->setLifetime(900); - } -} - -std::string Being::getSitAction() const -{ - if (serverVersion < 0) - { - return SpriteAction::SIT; - } - else - { - if (mMap) - { - const unsigned char mask = mMap->getBlockMask(mX, mY); - if (mask & Map::BLOCKMASK_GROUNDTOP) - return SpriteAction::SITTOP; - else if (mask & Map::BLOCKMASK_AIR) - return SpriteAction::SITSKY; - else if (mask & Map::BLOCKMASK_WATER) - return SpriteAction::SITWATER; - } - return SpriteAction::SIT; - } -} - - -std::string Being::getMoveAction() const -{ - if (serverVersion < 0) - { - return SpriteAction::MOVE; - } - else - { - if (mMap) - { - const unsigned char mask = mMap->getBlockMask(mX, mY); - if (mask & Map::BLOCKMASK_AIR) - return SpriteAction::FLY; - else if (mask & Map::BLOCKMASK_WATER) - return SpriteAction::SWIM; - } - return SpriteAction::MOVE; - } -} - -std::string Being::getWeaponAttackAction(const ItemInfo *const weapon) const -{ - if (!weapon) - return SpriteAction::ATTACK; - - if (serverVersion < 0 || !weapon) - { - return weapon->getAttackAction(); - } - else - { - if (mMap) - { - const unsigned char mask = mMap->getBlockMask(mX, mY); - if (mask & Map::BLOCKMASK_AIR) - return weapon->getSkyAttackAction(); - else if (mask & Map::BLOCKMASK_WATER) - return weapon->getWaterAttackAction(); - } - return weapon->getAttackAction(); - } -} - -std::string Being::getAttackAction(const Attack *const attack1) const -{ - if (!attack1) - return SpriteAction::ATTACK; - - if (serverVersion < 0 || !attack1) - { - return attack1->mAction; - } - else - { - if (mMap) - { - const unsigned char mask = mMap->getBlockMask(mX, mY); - if (mask & Map::BLOCKMASK_AIR) - return attack1->mSkyAction; - else if (mask & Map::BLOCKMASK_WATER) - return attack1->mWaterAction; - } - return attack1->mAction; - } -} - -#define getSpriteAction(func, action) \ - std::string Being::get##func##Action() const \ -{ \ - if (serverVersion < 0) \ - { \ - return SpriteAction::action; \ - } \ - else \ - { \ - if (mMap) \ - { \ - const unsigned char mask = mMap->getBlockMask(mX, mY); \ - if (mask & Map::BLOCKMASK_AIR) \ - return SpriteAction::action##SKY; \ - else if (mask & Map::BLOCKMASK_WATER) \ - return SpriteAction::action##WATER; \ - } \ - return SpriteAction::action; \ - } \ -} - -getSpriteAction(Dead, DEAD) -getSpriteAction(Stand, STAND) -getSpriteAction(Spawn, SPAWN) - -void Being::setAction(const Action &action, const int attackId) -{ - std::string currentAction = SpriteAction::INVALID; - - switch (action) - { - case MOVE: - if (mInfo) - { - playSfx(mInfo->getSound( - SOUND_EVENT_MOVE), nullptr, true, mX, mY); - } - currentAction = getMoveAction(); - // Note: When adding a run action, - // Differentiate walk and run with action name, - // while using only the ACTION_MOVE. - break; - case SIT: - currentAction = getSitAction(); - if (mInfo) - { - SoundEvent event; - if (currentAction == SpriteAction::SITTOP) - event = SOUND_EVENT_SITTOP; - else - event = SOUND_EVENT_SIT; - playSfx(mInfo->getSound(event), nullptr, true, mX, mY); - } - break; - case ATTACK: - if (mEquippedWeapon) - { - currentAction = getWeaponAttackAction(mEquippedWeapon); - reset(); - } - else - { - if (!mInfo || !mInfo->getAttack(attackId)) - break; - - currentAction = getAttackAction(mInfo->getAttack(attackId)); - reset(); - - // attack particle effect - if (Particle::enabled) - { - const int effectId = mInfo->getAttack(attackId)->mEffectId; - - int rotation; - switch (mSpriteDirection) - { - case DIRECTION_DOWN: - default: - rotation = 0; - break; - case DIRECTION_LEFT: - rotation = 90; - break; - case DIRECTION_UP: - rotation = 180; - break; - case DIRECTION_RIGHT: - rotation = 270; - break; - } - if (Particle::enabled && effectManager && effectId >= 0) - effectManager->trigger(effectId, this, rotation); - } - } - break; - case HURT: - if (mInfo) - { - playSfx(mInfo->getSound(SOUND_EVENT_HURT), - this, false, mX, mY); - } - break; - case DEAD: - currentAction = getDeadAction(); - if (mInfo) - { - playSfx(mInfo->getSound(SOUND_EVENT_DIE), this, true, mX, mY); - if (mType == MONSTER || mType == NPC) - mYDiff = mInfo->getDeadSortOffsetY(); - } - break; - case STAND: - currentAction = getStandAction(); - break; - case SPAWN: - if (mInfo) - { - playSfx(mInfo->getSound(SOUND_EVENT_SPAWN), - nullptr, true, mX, mY); - } - currentAction = getSpawnAction(); - break; - default: - logger->log("Being::setAction unknown action: " - + toString(static_cast(action))); - break; - } - - if (currentAction != SpriteAction::INVALID) - { - mSpriteAction = currentAction; - play(currentAction); - if (mEmotionSprite) - mEmotionSprite->play(currentAction); - if (mAnimationEffect) - mAnimationEffect->play(currentAction); - mAction = action; - } - - if (currentAction != SpriteAction::MOVE - && currentAction != SpriteAction::FLY - && currentAction != SpriteAction::SWIM) - { - mActionTime = tick_time; - } -} - -void Being::setDirection(const uint8_t direction) -{ - if (mDirection == direction) - return; - - mDirection = direction; - - mDirectionDelayed = 0; - - // if the direction does not change much, keep the common component - int mFaceDirection = mDirection & direction; - if (!mFaceDirection) - mFaceDirection = direction; - - SpriteDirection dir; - if (mFaceDirection & UP) - { - if (mFaceDirection & LEFT) - dir = DIRECTION_UPLEFT; - else if (mFaceDirection & RIGHT) - dir = DIRECTION_UPRIGHT; - else - dir = DIRECTION_UP; - } - else if (mFaceDirection & DOWN) - { - if (mFaceDirection & LEFT) - dir = DIRECTION_DOWNLEFT; - else if (mFaceDirection & RIGHT) - dir = DIRECTION_DOWNRIGHT; - else - dir = DIRECTION_DOWN; - } - else if (mFaceDirection & RIGHT) - { - dir = DIRECTION_RIGHT; - } - else - { - dir = DIRECTION_LEFT; - } - mSpriteDirection = static_cast(dir); - - CompoundSprite::setSpriteDirection(dir); - if (mEmotionSprite) - mEmotionSprite->setSpriteDirection(dir); - if (mAnimationEffect) - mAnimationEffect->setSpriteDirection(dir); - recalcSpritesOrder(); -} - -uint8_t Being::calcDirection() const -{ - uint8_t dir = 0; - if (mDest.x > mX) - dir |= RIGHT; - else if (mDest.x < mX) - dir |= LEFT; - if (mDest.y > mY) - dir |= DOWN; - else if (mDest.y < mY) - dir |= UP; - return dir; -} - -uint8_t Being::calcDirection(const int dstX, const int dstY) const -{ - uint8_t dir = 0; - if (dstX > mX) - dir |= RIGHT; - else if (dstX < mX) - dir |= LEFT; - if (dstY > mY) - dir |= DOWN; - else if (dstY < mY) - dir |= UP; - return dir; -} - -void Being::nextTile() -{ - if (mPath.empty()) - { - setAction(STAND); - return; - } - - const Position pos = mPath.front(); - mPath.pop_front(); - - const uint8_t dir = calcDirection(pos.x, pos.y); - if (dir) - setDirection(dir); - - if (!mMap || !mMap->getWalk(pos.x, pos.y, getWalkMask())) - { - setAction(STAND); - return; - } - - mX = pos.x; - mY = pos.y; - setAction(MOVE); - mActionTime += static_cast(mWalkSpeed.x / 10); -} - -void Being::logic() -{ - BLOCK_START("Being::logic") - // Reduce the time that speech is still displayed - if (mSpeechTime > 0) - mSpeechTime--; - - // Remove text and speechbubbles if speech boxes aren't being used - if (mSpeechTime == 0 && mText) - { - delete mText; - mText = nullptr; - } - - const int time = tick_time * MILLISECONDS_IN_A_TICK; - if (mEmotionSprite) - mEmotionSprite->update(time); - - if (mAnimationEffect) - { - mAnimationEffect->update(time); - if (mAnimationEffect->isTerminated()) - { - delete mAnimationEffect; - mAnimationEffect = nullptr; - } - } - - int frameCount = static_cast(getFrameCount()); -#ifdef MANASERV_SUPPORT - if ((Net::getNetworkType() == ServerInfo::MANASERV) && (mAction != DEAD)) - { - const Vector dest = (mPath.empty()) ? - mDest : Vector(static_cast(mPath.front().x), - static_cast(mPath.front().y)); - - // This is a hack that stops NPCs from running off the map... - if (mDest.x <= 0 && mDest.y <= 0) - { - BLOCK_END("Being::logic") - return; - } - - // The Vector representing the difference between current position - // and the next destination path node. - Vector dir = dest - mPos; - - const float nominalLength = dir.length(); - - // When we've not reached our destination, move to it. - if (nominalLength > 0.0f && !mWalkSpeed.isNull()) - { - // The deplacement of a point along a vector is calculated - // using the Unit Vector (â) multiplied by the point speed. - // â = a / ||a|| (||a|| is the a length.) - // Then, diff = (dir/||dir||) * speed. - const Vector normalizedDir = dir.normalized(); - Vector diff(normalizedDir.x * mWalkSpeed.x, - normalizedDir.y * mWalkSpeed.y); - - // Test if we don't miss the destination by a move too far: - if (diff.length() > nominalLength) - { - setPosition(mPos + dir); - - // Also, if the destination is reached, try to get the next - // path point, if existing. - if (!mPath.empty()) - mPath.pop_front(); - } - // Otherwise, go to it using the nominal speed. - else - { - setPosition(mPos + diff); - } - - if (mAction != MOVE) - setAction(MOVE); - - // Update the player sprite direction. - // N.B.: We only change this if the distance is more than one pixel. - if (nominalLength > 1.0f) - { - int direction = 0; - const float dx = std::abs(dir.x); - float dy = std::abs(dir.y); - - // When not using mouse for the player, we slightly prefer - // UP and DOWN position, especially when walking diagonally. - if (player_node && this == player_node && - !player_node->isPathSetByMouse()) - { - dy = dy + 2; - } - - if (dx > dy) - direction |= (dir.x > 0) ? RIGHT : LEFT; - else - direction |= (dir.y > 0) ? DOWN : UP; - - setDirection(static_cast(direction)); - } - } - else if (!mPath.empty()) - { - // If the current path node has been reached, - // remove it and go to the next one. - mPath.pop_front(); - } - else if (mAction == MOVE) - { - setAction(STAND); - } - } - else - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - switch (mAction) - { - case STAND: - case SIT: - case DEAD: - case HURT: - case SPAWN: - default: - break; - - case MOVE: - { - if (getWalkSpeed().x && static_cast ((static_cast( - get_elapsed_time(mActionTime)) * static_cast( - frameCount)) / getWalkSpeed().x) - >= frameCount) - { - nextTile(); - } - break; - } - - case ATTACK: - { -// std::string particleEffect(""); - - if (!mActionTime) - break; - - int curFrame = 0; - if (mAttackSpeed) - { - curFrame = (get_elapsed_time(mActionTime) * frameCount) - / mAttackSpeed; - } - - if (this == player_node && curFrame >= frameCount) - nextTile(); - - break; - } - } - - // Update pixel coordinates - setPosition(static_cast(mX * 32 + 16 + getXOffset()), - static_cast(mY * 32 + 32 + getYOffset())); - } - - if (mEmotionSprite) - { - mEmotionTime--; - if (mEmotionTime == 0) - { - delete mEmotionSprite; - mEmotionSprite = nullptr; - } - } - - ActorSprite::logic(); - - if (frameCount < 10) - frameCount = 10; - - if (!isAlive() && getWalkSpeed().x - && Net::getGameHandler()->removeDeadBeings() - && static_cast ((static_cast(get_elapsed_time(mActionTime)) - / static_cast(getWalkSpeed().x))) >= frameCount) - { - if (mType != PLAYER && actorSpriteManager) - actorSpriteManager->destroy(this); - } - - const SoundInfo *const sound = mNextSound.sound; - if (sound) - { - const int time2 = tick_time; - if (time2 > mNextSound.time) - { - soundManager.playSfx(sound->sound, mNextSound.x, mNextSound.y); - - mNextSound.sound = nullptr; - mNextSound.time = time2 + sound->delay; - } - } - - BLOCK_END("Being::logic") -} - -void Being::drawEmotion(Graphics *const graphics, const int offsetX, - const int offsetY) -{ - const int px = getPixelX() - offsetX - 16; - const int py = getPixelY() - offsetY - 64 - 32; - if (mEmotionSprite) - mEmotionSprite->draw(graphics, px, py); - if (mAnimationEffect) - mAnimationEffect->draw(graphics, px, py); -} - -void Being::drawSpeech(const int offsetX, const int offsetY) -{ - if (!mSpeechBubble || mSpeech.empty()) - return; - - const int px = getPixelX() - offsetX; - const int py = getPixelY() - offsetY; - const int speech = mSpeechType; - - // Draw speech above this being - if (mSpeechTime == 0) - { - if (mSpeechBubble->isVisible()) - mSpeechBubble->setVisible(false); - } - else if (mSpeechTime > 0 && (speech == NAME_IN_BUBBLE || - speech == NO_NAME_IN_BUBBLE)) - { - const bool isShowName = (speech == NAME_IN_BUBBLE); - - delete mText; - mText = nullptr; - - mSpeechBubble->setCaption(isShowName ? mName : ""); - - mSpeechBubble->setText(mSpeech, isShowName); - mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() / 2), - py - getHeight() - (mSpeechBubble->getHeight())); - mSpeechBubble->setVisible(true); - } - else if (mSpeechTime > 0 && speech == TEXT_OVERHEAD) - { - mSpeechBubble->setVisible(false); - - if (!mText && userPalette) - { - mText = new Text(mSpeech, getPixelX(), getPixelY() - getHeight(), - gcn::Graphics::CENTER, &Theme::getThemeColor( - Theme::BUBBLE_TEXT), true); - } - } - else if (speech == NO_SPEECH) - { - mSpeechBubble->setVisible(false); - - delete mText; - mText = nullptr; - } -} - -int Being::getOffset(const signed char pos, const signed char neg) const -{ - // Check whether we're walking in the requested direction - if (mAction != MOVE || !(mDirection & (pos | neg))) - return 0; - - int offset = 0; - - if (mMap) - { - const int time = get_elapsed_time(mActionTime); - offset = (pos == LEFT && neg == RIGHT) ? - static_cast((static_cast(time) - * static_cast(mMap->getTileWidth())) - / static_cast(mWalkSpeed.x)) : - static_cast((static_cast(time) - * static_cast(mMap->getTileHeight())) - / static_cast(mWalkSpeed.y)); - } - - // We calculate the offset _from_ the _target_ location - offset -= 32; - if (offset > 0) - offset = 0; - - // Going into negative direction? Invert the offset. - if (mDirection & pos) - offset = -offset; - - if (offset > 32) - offset = 32; - if (offset < -32) - offset = -32; - - return offset; -} - -void Being::updateCoords() -{ - if (!mDispName) - return; - - int offsetX = getPixelX(); - int offsetY = getPixelY(); - if (mInfo) - { - offsetX += mInfo->getNameOffsetX(); - offsetY += mInfo->getNameOffsetY(); - } - // Monster names show above the sprite instead of below it - if (mType == MONSTER) - offsetY += - getHeight() - mDispName->getHeight(); - - mDispName->adviseXY(offsetX, offsetY, mMoveNames); -} - -void Being::optionChanged(const std::string &value) -{ - if (mType == PLAYER && value == "visiblenames") - setShowName(config.getBoolValue("visiblenames")); -} - -void Being::flashName(const int time) -{ - if (mDispName) - mDispName->flash(time); -} - -std::string Being::getGenderSignWithSpace() const -{ - const std::string &str = getGenderSign(); - if (str.empty()) - return str; - else - return std::string(" ").append(str); -} - -std::string Being::getGenderSign() const -{ - std::string str; - if (mShowGender) - { - if (getGender() == GENDER_FEMALE) - str = "\u2640"; - else if (getGender() == GENDER_MALE) - str = "\u2642"; - } - if (mShowPlayersStatus && mAdvanced) - { - if (mShop) - str.append("$"); - if (mAway) - { - // TRANSLATORS: this away status writed in player nick - str.append(_("A")); - } - else if (mInactive) - { - // TRANSLATORS: this inactive status writed in player nick - str.append(_("I")); - } - } - return str; -} - -void Being::showName() -{ - if (mName.empty()) - return; - - delete mDispName; - mDispName = nullptr; - - if (mHideErased && player_relations.getRelation(mName) == - PlayerRelation::ERASED) - { - return; - } - - std::string displayName(mName); - - if (mType != MONSTER && (mShowGender || mShowLevel)) - { - displayName.append(" "); - if (mShowLevel && getLevel() != 0) - displayName.append(toString(getLevel())); - - displayName.append(getGenderSign()); - } - - if (mType == MONSTER) - { - if (config.getBoolValue("showMonstersTakedDamage")) - displayName.append(", ").append(toString(getDamageTaken())); - } - - gcn::Font *font = nullptr; - if (player_node && player_node->getTarget() == this - && mType != MONSTER) - { - font = boldFont; - } - else if (mType == PLAYER && !player_relations.isGoodName(this) && gui) - { - font = gui->getSecureFont(); - } - - if (mInfo) - { - mDispName = new FlashText(displayName, - getPixelX() + mInfo->getNameOffsetX(), - getPixelY() + mInfo->getNameOffsetY(), - gcn::Graphics::CENTER, mNameColor, font); - } - else - { - mDispName = new FlashText(displayName, getPixelX(), getPixelY(), - gcn::Graphics::CENTER, mNameColor, font); - } - - updateCoords(); -} - -void Being::updateColors() -{ - if (userPalette) - { - if (mType == MONSTER) - { - mNameColor = &userPalette->getColor(UserPalette::MONSTER); - mTextColor = &userPalette->getColor(UserPalette::MONSTER); - } - else if (mType == NPC) - { - mNameColor = &userPalette->getColor(UserPalette::NPC); - mTextColor = &userPalette->getColor(UserPalette::NPC); - } - else if (this == player_node) - { - mNameColor = &userPalette->getColor(UserPalette::SELF); - mTextColor = &Theme::getThemeColor(Theme::PLAYER); - } - else - { - mTextColor = &Theme::getThemeColor(Theme::PLAYER); - - if (player_relations.getRelation(mName) != PlayerRelation::ERASED) - mErased = false; - else - mErased = true; - - if (mIsGM) - { - mTextColor = &userPalette->getColor(UserPalette::GM); - mNameColor = &userPalette->getColor(UserPalette::GM); - } - else if (mEnemy) - { - mNameColor = &userPalette->getColor(UserPalette::MONSTER); - } - else if (mParty && mParty == player_node->getParty()) - { - mNameColor = &userPalette->getColor(UserPalette::PARTY); - } - else if (player_node && getGuild() - && getGuild() == player_node->getGuild()) - { - mNameColor = &userPalette->getColor(UserPalette::GUILD); - } - else if (player_relations.getRelation(mName) == - PlayerRelation::FRIEND) - { - mNameColor = &userPalette->getColor(UserPalette::FRIEND); - } - else if (player_relations.getRelation(mName) == - PlayerRelation::DISREGARDED - || player_relations.getRelation(mName) == - PlayerRelation::BLACKLISTED) - { - mNameColor = &userPalette->getColor(UserPalette::DISREGARDED); - } - else if (player_relations.getRelation(mName) == - PlayerRelation::IGNORED - || player_relations.getRelation(mName) == - PlayerRelation::ENEMY2) - { - mNameColor = &userPalette->getColor(UserPalette::IGNORED); - } - else if (player_relations.getRelation(mName) == - PlayerRelation::ERASED) - { - mNameColor = &userPalette->getColor(UserPalette::ERASED); - } - else - { - mNameColor = &userPalette->getColor(UserPalette::PC); - } - } - - if (mDispName) - mDispName->setColor(mNameColor); - } -} - -void Being::setSprite(const unsigned int slot, const int id, - std::string color, const unsigned char colorId, - const bool isWeapon, const bool isTempSprite) -{ - if (slot >= Net::getCharServerHandler()->maxSprite()) - return; - - if (slot >= size()) - ensureSize(slot + 1); - - if (slot >= mSpriteIDs.size()) - mSpriteIDs.resize(slot + 1, 0); - - if (slot >= mSpriteColors.size()) - mSpriteColors.resize(slot + 1, ""); - - if (slot >= mSpriteColorsIds.size()) - mSpriteColorsIds.resize(slot + 1, 1); - - // id = 0 means unequip - if (id == 0) - { - removeSprite(slot); - - if (isWeapon) - mEquippedWeapon = nullptr; - const int id1 = mSpriteIDs[slot]; - if (id1) - { - const ItemInfo &info = ItemDB::get(id1); - if (mMap) - { - const int pet = info.getPet(); - if (pet) - removePet(); - } - } - } - else - { - const ItemInfo &info = ItemDB::get(id); - const std::string filename = info.getSprite(mGender, mSubType); - AnimatedSprite *equipmentSprite = nullptr; - - if (mType == PLAYER) - { - const int pet = info.getPet(); - if (pet) - addPet(pet); - } - - if (!filename.empty()) - { - if (color.empty()) - color = info.getDyeColorsString(colorId); - - equipmentSprite = AnimatedSprite::delayedLoad( - paths.getStringValue("sprites").append( - combineDye(filename, color))); - } - - if (equipmentSprite) - equipmentSprite->setSpriteDirection(getSpriteDirection()); - - CompoundSprite::setSprite(slot, equipmentSprite); - - if (isWeapon) - mEquippedWeapon = &ItemDB::get(id); - - setAction(mAction); - } - - if (!isTempSprite) - { - mSpriteIDs[slot] = id; - mSpriteColors[slot] = color; - mSpriteColorsIds[slot] = colorId; - recalcSpritesOrder(); - if (beingEquipmentWindow) - beingEquipmentWindow->updateBeing(this); - } -} - -void Being::setSpriteID(const unsigned int slot, const int id) -{ - setSprite(slot, id, mSpriteColors[slot]); -} - -void Being::setSpriteColor(const unsigned int slot, const std::string &color) -{ - setSprite(slot, mSpriteIDs[slot], color); -} - -void Being::setHairStyle(const unsigned int slot, const int id) -{ -// dumpSprites(); - setSprite(slot, id, ItemDB::get(id).getDyeColorsString(mHairColor)); -// dumpSprites(); -} - -void Being::setHairColor(const unsigned int slot, const unsigned char color) -{ - mHairColor = color; - setSprite(slot, mSpriteIDs[slot], ItemDB::get( - getSpriteID(slot)).getDyeColorsString(color)); -} - -void Being::dumpSprites() const -{ - std::vector::const_iterator it1 = mSpriteIDs.begin(); - const std::vector::const_iterator it1_end = mSpriteIDs.end(); - StringVectCIter it2 = mSpriteColors.begin(); - const StringVectCIter it2_end = mSpriteColors.end(); - std::vector::const_iterator it3 = mSpriteColorsIds.begin(); - const std::vector::const_iterator it3_end = mSpriteColorsIds.end(); - - logger->log("sprites"); - for (; it1 != it1_end && it2 != it2_end && it3 != it3_end; - ++ it1, ++ it2, ++ it3) - { - logger->log("%d,%s,%d", *it1, (*it2).c_str(), *it3); - } -} - -void Being::load() -{ - // Hairstyles are encoded as negative numbers. Count how far negative - // we can go. - int hairstyles = 1; - while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE, 0) != - paths.getStringValue("spriteErrorFile")) - { - hairstyles ++; - } - mNumberOfHairstyles = hairstyles; - - int races = 100; - while (ItemDB::get(-races).getSprite(GENDER_MALE, 0) != - paths.getStringValue("spriteErrorFile")) - { - races ++; - } - mNumberOfRaces = races - 100; -} - -void Being::updateName() -{ - if (mShowName) - showName(); -} - -void Being::reReadConfig() -{ - BLOCK_START("Being::reReadConfig") - if (mUpdateConfigTime + 1 < cur_time) - { - mAwayEffect = paths.getIntValue("afkEffectId"); - mHighlightMapPortals = config.getBoolValue("highlightMapPortals"); - mConfLineLim = config.getIntValue("chatMaxCharLimit"); - mSpeechType = config.getIntValue("speech"); - mHighlightMonsterAttackRange = - config.getBoolValue("highlightMonsterAttackRange"); - mLowTraffic = config.getBoolValue("lowTraffic"); - mDrawHotKeys = config.getBoolValue("drawHotKeys"); - mShowBattleEvents = config.getBoolValue("showBattleEvents"); - mShowMobHP = config.getBoolValue("showMobHP"); - mShowOwnHP = config.getBoolValue("showOwnHP"); - mShowGender = config.getBoolValue("showgender"); - mShowLevel = config.getBoolValue("showlevel"); - mShowPlayersStatus = config.getBoolValue("showPlayersStatus"); - mEnableReorderSprites = config.getBoolValue("enableReorderSprites"); - mHideErased = config.getBoolValue("hideErased"); - mMoveNames = config.getBoolValue("moveNames"); - - mUpdateConfigTime = cur_time; - } - BLOCK_END("Being::reReadConfig") -} - -bool Being::updateFromCache() -{ - const BeingCacheEntry *const entry = Being::getCacheEntry(getId()); - - if (entry && entry->getTime() + 120 >= cur_time) - { - if (!entry->getName().empty()) - setName(entry->getName()); - setPartyName(entry->getPartyName()); - setGuildName(entry->getGuildName()); - setLevel(entry->getLevel()); - setPvpRank(entry->getPvpRank()); - setIp(entry->getIp()); - - mAdvanced = entry->isAdvanced(); - if (mAdvanced) - { - const int flags = entry->getFlags(); - mShop = ((flags & FLAG_SHOP) != 0); - mAway = ((flags & FLAG_AWAY) != 0); - mInactive = ((flags & FLAG_INACTIVE) != 0); - if (mShop || mAway || mInactive) - updateName(); - } - else - { - mShop = false; - mAway = false; - mInactive = false; - } - - updateAwayEffect(); - if (mType == PLAYER) - updateColors(); - return true; - } - return false; -} - -void Being::addToCache() const -{ - if (player_node == this) - return; - - BeingCacheEntry *entry = Being::getCacheEntry(getId()); - if (!entry) - { - entry = new BeingCacheEntry(getId()); - beingInfoCache.push_front(entry); - - if (beingInfoCache.size() >= CACHE_SIZE) - { - delete beingInfoCache.back(); - beingInfoCache.pop_back(); - } - } - if (!mLowTraffic) - return; - - entry->setName(getName()); - entry->setLevel(getLevel()); - entry->setPartyName(getPartyName()); - entry->setGuildName(getGuildName()); - entry->setTime(cur_time); - entry->setPvpRank(getPvpRank()); - entry->setIp(getIp()); - entry->setAdvanced(isAdvanced()); - if (isAdvanced()) - { - int flags = 0; - if (mShop) - flags += FLAG_SHOP; - if (mAway) - flags += FLAG_AWAY; - if (mInactive) - flags += FLAG_INACTIVE; - entry->setFlags(flags); - } - else - { - entry->setFlags(0); - } -} - -BeingCacheEntry* Being::getCacheEntry(const int id) -{ - FOR_EACH (std::list::iterator, i, beingInfoCache) - { - if (!*i) - continue; - - if (id == (*i)->getId()) - { - // Raise priority: move it to front - if ((*i)->getTime() + 120 < cur_time) - { - beingInfoCache.splice(beingInfoCache.begin(), - beingInfoCache, i); - } - return *i; - } - } - return nullptr; -} - - -void Being::setGender(const Gender gender) -{ - if (gender != mGender) - { - mGender = gender; - - // Reload all subsprites - for (unsigned int i = 0; i < mSpriteIDs.size(); i++) - { - if (mSpriteIDs.at(i) != 0) - setSprite(i, mSpriteIDs.at(i), mSpriteColors.at(i)); - } - - updateName(); - } -} - -void Being::setGM(const bool gm) -{ - mIsGM = gm; - - updateColors(); -} - -void Being::talkTo() const -{ - if (!client->limitPackets(PACKET_NPC_TALK)) - return; - - Net::getNpcHandler()->talk(mId); -} - -bool Being::draw(Graphics *const graphics, - const int offsetX, const int offsetY) const -{ - bool res = true; - if (!mErased) - res = ActorSprite::draw(graphics, offsetX, offsetY); - - return res; -} - -void Being::drawSprites(Graphics *const graphics, - const int posX, const int posY) const -{ - const int sz = getNumberOfLayers(); - for (int f = 0; f < sz; f ++) - { - const int rSprite = mSpriteHide[mSpriteRemap[f]]; - if (rSprite == 1) - continue; - - Sprite *const sprite = getSprite(mSpriteRemap[f]); - if (sprite) - { - sprite->setAlpha(mAlpha); - sprite->draw(graphics, posX, posY); - } - } -} - -void Being::drawSpritesSDL(Graphics *const graphics, - const int posX, const int posY) const -{ - const size_t sz = size(); - for (unsigned f = 0; f < sz; f ++) - { - const int rSprite = mSpriteHide[mSpriteRemap[f]]; - if (rSprite == 1) - continue; - - const Sprite *const sprite = getSprite(mSpriteRemap[f]); - if (sprite) - sprite->draw(graphics, posX, posY); - } -} - -bool Being::drawSpriteAt(Graphics *const graphics, - const int x, const int y) const -{ - bool res = true; - - if (!mErased) - res = ActorSprite::drawSpriteAt(graphics, x, y); - - if (!userPalette) - return res; - - if (mHighlightMapPortals && mMap && mSubType == 45 && !mMap->getHasWarps()) - { - graphics->setColor(userPalette-> - getColorWithAlpha(UserPalette::PORTAL_HIGHLIGHT)); - - graphics->fillRectangle(gcn::Rectangle(x, y, 32, 32)); - - if (mDrawHotKeys && !mName.empty()) - { - gcn::Font *const font = gui->getFont(); - if (font) - { - graphics->setColor(userPalette->getColor(UserPalette::BEING)); - font->drawString(graphics, mName, x, y); - } - } - } - - if (mHighlightMonsterAttackRange && mType == ActorSprite::MONSTER - && isAlive()) - { - int attackRange; - if (mAttackRange) - attackRange = 32 * mAttackRange; - else - attackRange = 32; - - graphics->setColor(userPalette->getColorWithAlpha( - UserPalette::MONSTER_ATTACK_RANGE)); - - graphics->fillRectangle(gcn::Rectangle( - x - attackRange, y - attackRange, - 2 * attackRange + 32, 2 * attackRange + 32)); - } - - if (mShowMobHP && mInfo && player_node && player_node->getTarget() == this - && mType == MONSTER) - { - // show hp bar here - int maxHP = mMaxHP; - if (!maxHP) - maxHP = mInfo->getMaxHP(); - - drawHpBar(graphics, maxHP, mHP, mDamageTaken, - UserPalette::MONSTER_HP, UserPalette::MONSTER_HP2, - x - 50 + 16 + mInfo->getHpBarOffsetX(), - y + 32 - 6 + mInfo->getHpBarOffsetY(), - 2 * 50, 4); - } - if (mShowOwnHP && mInfo && player_node == this && mAction != DEAD) - { - drawHpBar(graphics, PlayerInfo::getAttribute(PlayerInfo::MAX_HP), - PlayerInfo::getAttribute(PlayerInfo::HP), 0, - UserPalette::PLAYER_HP, UserPalette::PLAYER_HP2, - x - 50 + 16 + mInfo->getHpBarOffsetX(), - y + 32 - 6 + mInfo->getHpBarOffsetY(), - 2 * 50, 4); - } - return res; -} - -void Being::drawHpBar(Graphics *const graphics, const int maxHP, const int hp, - const int damage, const int color1, const int color2, - const int x, const int y, const int width, - const int height) const -{ - if (maxHP <= 0 || !userPalette) - return; - - float p; - - if (hp) - { - p = static_cast(maxHP) / static_cast(hp); - } - else if (maxHP != damage) - { - p = static_cast(maxHP) - / static_cast(maxHP - damage); - } - else - { - p = 1; - } - - if (p <= 0 || p > width) - return; - - const int dx = static_cast(static_cast(width) / p); - - if (serverVersion < 1) - { // old servers - if ((!damage && (this != player_node || hp == maxHP)) - || (!hp && maxHP == damage)) - { - graphics->setColor(userPalette->getColorWithAlpha(color1)); - graphics->fillRectangle(gcn::Rectangle( - x, y, dx, height)); - return; - } - else if (width - dx <= 0) - { - graphics->setColor(userPalette->getColorWithAlpha(color2)); - graphics->fillRectangle(gcn::Rectangle( - x, y, width, height)); - return; - } - } - else - { // evol servers - if (hp == maxHP) - { - graphics->setColor(userPalette->getColorWithAlpha(color1)); - graphics->fillRectangle(gcn::Rectangle( - x, y, dx, height)); - return; - } - else if (width - dx <= 0) - { - graphics->setColor(userPalette->getColorWithAlpha(color2)); - graphics->fillRectangle(gcn::Rectangle( - x, y, width, height)); - return; - } - } - - graphics->setColor(userPalette->getColorWithAlpha(color1)); - graphics->fillRectangle(gcn::Rectangle( - x, y, dx, height)); - - graphics->setColor(userPalette->getColorWithAlpha(color2)); - graphics->fillRectangle(gcn::Rectangle( - x + dx, y, width - dx, height)); -} - -void Being::setHP(const int hp) -{ - mHP = hp; - if (mMaxHP < mHP) - mMaxHP = mHP; - if (mType == MONSTER) - updatePercentHP(); -} - -void Being::setMaxHP(const int hp) -{ - mMaxHP = hp; - if (mMaxHP < mHP) - mMaxHP = mHP; -} - -void Being::resetCounters() -{ - mMoveTime = 0; - mAttackTime = 0; - mTalkTime = 0; - mOtherTime = 0; - mTestTime = cur_time; -} - -void Being::recalcSpritesOrder() -{ - if (!mEnableReorderSprites) - return; - -// logger->log("recalcSpritesOrder"); - const unsigned sz = static_cast(size()); - if (sz < 1) - return; - - std::vector slotRemap; - std::map itemSlotRemap; - - std::vector::iterator it; - int oldHide[20]; - int dir = mSpriteDirection; - if (dir < 0 || dir >= 9) - dir = 0; - // hack for allow different logic in dead player - if (mAction == DEAD) - dir = 9; - - const unsigned int hairSlot = Net::getCharServerHandler()->hairSprite(); - - for (unsigned slot = 0; slot < sz; slot ++) - { - oldHide[slot] = mSpriteHide[slot]; - mSpriteHide[slot] = 0; - } - - const size_t spriteIdSize = mSpriteIDs.size(); - for (unsigned slot = 0; slot < sz; slot ++) - { - slotRemap.push_back(slot); - - if (spriteIdSize <= slot) - continue; - - const int id = mSpriteIDs[slot]; - if (!id) - continue; - - const ItemInfo &info = ItemDB::get(id); - - if (info.isRemoveSprites()) - { - SpriteToItemMap *const spriteToItems - = info.getSpriteToItemReplaceMap(dir); - - if (spriteToItems) - { - FOR_EACHP (SpriteToItemMapCIter, itr, spriteToItems) - { - const int remSprite = itr->first; - const std::map &itemReplacer = itr->second; - if (remSprite >= 0) - { // slot known - if (itemReplacer.empty()) - { - mSpriteHide[remSprite] = 1; - } - else - { - std::map::const_iterator repIt - = itemReplacer.find(mSpriteIDs[remSprite]); - if (repIt == itemReplacer.end()) - { - repIt = itemReplacer.find(0); - if (repIt->second == 0) - repIt = itemReplacer.end(); - } - if (repIt != itemReplacer.end()) - { - mSpriteHide[remSprite] = repIt->second; - if (repIt->second != 1) - { - if (static_cast(remSprite) - != hairSlot) - { - setSprite(remSprite, repIt->second, - mSpriteColors[remSprite], - 1, false, true); - } - else - { - setSprite(remSprite, repIt->second, - ItemDB::get(repIt->second) - .getDyeColorsString(mHairColor), - 1, false, true); - } - } - } - } - } - else - { // slot unknown. Search for real slot, this can be slow - FOR_EACH (IntMapCIter, repIt, itemReplacer) - { - for (unsigned slot2 = 0; slot2 < sz; slot2 ++) - { - if (mSpriteIDs[slot2] == repIt->first) - { - mSpriteHide[slot2] = repIt->second; - if (repIt->second != 1) - { - if (slot2 != hairSlot) - { - setSprite(slot2, repIt->second, - mSpriteColors[slot2], - 1, false, true); - } - else - { - setSprite(slot2, repIt->second, - ItemDB::get(repIt->second) - .getDyeColorsString( - mHairColor), - 1, false, true); - } - } - } - } - } - } - } - } - } - - if (info.mDrawBefore[dir] > 0) - { - const int id2 = mSpriteIDs[info.mDrawBefore[dir]]; - if (itemSlotRemap.find(id2) != itemSlotRemap.end()) - { -// logger->log("found duplicate (before)"); - const ItemInfo &info2 = ItemDB::get(id2); - if (info.mDrawPriority[dir] < info2.mDrawPriority[dir]) - { -// logger->log("old more priority"); - continue; - } - else - { -// logger->log("new more priority"); - itemSlotRemap.erase(id2); - } - } - - itemSlotRemap[id] = -info.mDrawBefore[dir]; - } - else if (info.mDrawAfter[dir] > 0) - { - const int id2 = mSpriteIDs[info.mDrawAfter[dir]]; - if (itemSlotRemap.find(id2) != itemSlotRemap.end()) - { - const ItemInfo &info2 = ItemDB::get(id2); - if (info.mDrawPriority[dir] < info2.mDrawPriority[dir]) - { -// logger->log("old more priority"); - continue; - } - else - { -// logger->log("new more priority"); - itemSlotRemap.erase(id2); - } - } - - itemSlotRemap[id] = info.mDrawAfter[dir]; -// logger->log("item slot->slot %d %d->%d", id, slot, itemSlotRemap[id]); - } - } -// logger->log("preparation end"); - - int lastRemap = 0; - unsigned cnt = 0; - - while (cnt < 15 && lastRemap >= 0) - { - lastRemap = -1; - cnt ++; -// logger->log("iteration"); - - for (unsigned slot0 = 0; slot0 < sz; slot0 ++) - { - const int slot = searchSlotValue(slotRemap, slot0); - const int val = slotRemap.at(slot); - int id = 0; - - if (static_cast(spriteIdSize) > val) - id = mSpriteIDs[val]; - - int idx = -1; - int idx1 = -1; -// logger->log("item %d, id=%d", slot, id); - int reorder = 0; - const std::map::const_iterator - orderIt = itemSlotRemap.find(id); - if (orderIt != itemSlotRemap.end()) - reorder = orderIt->second; - - if (reorder < 0) - { -// logger->log("move item %d before %d", slot, -reorder); - searchSlotValueItr(it, idx, slotRemap, -reorder); - if (it == slotRemap.end()) - return; - searchSlotValueItr(it, idx1, slotRemap, val); - if (it == slotRemap.end()) - return; - lastRemap = idx1; - if (idx1 + 1 != idx) - { - slotRemap.erase(it); - searchSlotValueItr(it, idx, slotRemap, -reorder); - slotRemap.insert(it, val); - } - } - else if (reorder > 0) - { -// logger->log("move item %d after %d", slot, reorder); - searchSlotValueItr(it, idx, slotRemap, reorder); - searchSlotValueItr(it, idx1, slotRemap, val); - if (it == slotRemap.end()) - return; - lastRemap = idx1; - if (idx1 != idx + 1) - { - slotRemap.erase(it); - searchSlotValueItr(it, idx, slotRemap, reorder); - if (it != slotRemap.end()) - { - ++ it; - if (it != slotRemap.end()) - slotRemap.insert(it, val); - else - slotRemap.push_back(val); - } - else - { - slotRemap.push_back(val); - } - } - } - } - } - -// logger->log("after remap"); - for (unsigned slot = 0; slot < sz; slot ++) - { - mSpriteRemap[slot] = slotRemap[slot]; - if (oldHide[slot] != 0 && oldHide[slot] != 1 && mSpriteHide[slot] == 0) - { - const int id = mSpriteIDs[slot]; - if (!id) - continue; - - setSprite(slot, id, mSpriteColors[slot], 1, false, true); - } -// logger->log("slot %d = %d", slot, mSpriteRemap[slot]); - } -} - -int Being::searchSlotValue(const std::vector &slotRemap, - const int val) const -{ - const size_t sz = size(); - for (size_t slot = 0; slot < sz; slot ++) - { - if (slotRemap[slot] == val) - return slot; - } - return getNumberOfLayers() - 1; -} - -void Being::searchSlotValueItr(std::vector::iterator &it, int &idx, - std::vector &slotRemap, - const int val) const -{ -// logger->log("searching %d", val); - it = slotRemap.begin(); - const std::vector::iterator it_end = slotRemap.end(); - idx = 0; - while (it != it_end) - { -// logger->log("testing %d", *it); - if (*it == val) - { -// logger->log("found at %d", idx); - return; - } - ++ it; - idx ++; - } -// logger->log("not found"); - idx = -1; - return; -} - -void Being::updateHit(const int amount) -{ - if (amount > 0) - { - if (!mMinHit || amount < mMinHit) - mMinHit = amount; - if (amount != mCriticalHit && (!mMaxHit || amount > mMaxHit)) - mMaxHit = amount; - } -} - -Equipment *Being::getEquipment() -{ - Equipment *const eq = new Equipment(); - Equipment::Backend *const bk = new BeingEquipBackend(this); - eq->setBackend(bk); - return eq; -} - -void Being::undressItemById(const int id) -{ - const size_t sz = mSpriteIDs.size(); - - for (size_t f = 0; f < sz; f ++) - { - if (id == mSpriteIDs[f]) - { - setSprite(static_cast(f), 0); - break; - } - } -} - -void Being::clearCache() -{ - delete_all(beingInfoCache); - beingInfoCache.clear(); -} - -void Being::updateComment() -{ - if (mGotComment || mName.empty()) - return; - - mGotComment = true; - mComment = loadComment(mName, mType); -} - -std::string Being::loadComment(const std::string &name, const int type) -{ - std::string str; - switch (type) - { - case PLAYER: - str = client->getUsersDirectory(); - break; - case NPC: - str = client->getNpcsDirectory(); - break; - default: - return ""; - } - - str.append(stringToHexPath(name)).append("/comment.txt"); - logger->log("load from: %s", str.c_str()); - StringVect lines; - - const ResourceManager *const resman = ResourceManager::getInstance(); - if (resman->existsLocal(str)) - { - lines = resman->loadTextFileLocal(str); - if (lines.size() >= 2) - return lines[1]; - } - return ""; -} - -void Being::saveComment(const std::string &name, - const std::string &comment, const int type) -{ - std::string dir; - switch (type) - { - case PLAYER: - dir = client->getUsersDirectory(); - break; - case NPC: - dir = client->getNpcsDirectory(); - break; - default: - return; - } - dir.append(stringToHexPath(name)); - const ResourceManager *const resman = ResourceManager::getInstance(); - resman->saveTextFile(dir, "comment.txt", - (name + "\n").append(comment)); -} - -void Being::setState(const uint8_t state) -{ - const bool shop = ((state & FLAG_SHOP) != 0); - const bool away = ((state & FLAG_AWAY) != 0); - const bool inactive = ((state & FLAG_INACTIVE) != 0); - const bool needUpdate = (shop != mShop || away != mAway - || inactive != mInactive); - - mShop = shop; - mAway = away; - mInactive = inactive; - updateAwayEffect(); - - if (needUpdate) - { - if (shop || away || inactive) - mAdvanced = true; - updateName(); - addToCache(); - } -} - -void Being::setEmote(const uint8_t emotion, const int emote_time) -{ - if ((emotion & FLAG_SPECIAL) == FLAG_SPECIAL) - { - setState(emotion); - mAdvanced = true; - } - else - { - const int emotionIndex = emotion - 1; - if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast()) - { - delete mEmotionSprite; - mEmotionSprite = nullptr; - const EmoteInfo *const info = EmoteDB::get2(emotionIndex, true); - if (info) - { - const EmoteSprite *const sprite = info->sprites.front(); - if (sprite) - { - mEmotionSprite = AnimatedSprite::clone(sprite->sprite); - if (mEmotionSprite) - mEmotionTime = info->time; - else - mEmotionTime = emote_time; - } - } - } - - if (mEmotionSprite) - { - mEmotionSprite->play(mSpriteAction); - mEmotionSprite->setSpriteDirection(static_cast( - mSpriteDirection)); - } - else - { - mEmotionTime = 0; - } - } -} - -void Being::updatePercentHP() -{ - if (!mMaxHP || !serverVersion) - return; - if (mHP) - { - const unsigned num = mHP * 100 / mMaxHP; - if (num != mNumber) - { - mNumber = num; - if (updateNumber(mNumber)) - setAction(mAction); - } - } -} - -uint8_t Being::genderToInt(const Gender sex) -{ - switch (sex) - { - case GENDER_FEMALE: - case GENDER_UNSPECIFIED: - default: - return 0; - case GENDER_MALE: - return 1; - case GENDER_OTHER: - return 3; - } -} - -Gender Being::intToGender(const uint8_t sex) -{ - switch (sex) - { - case 0: - default: - return GENDER_FEMALE; - case 1: - return GENDER_MALE; - case 3: - return GENDER_OTHER; - } -} - -int Being::getSpriteID(const int slot) const -{ - if (slot < 0 || static_cast(slot) >= mSpriteIDs.size()) - return -1; - - return mSpriteIDs[slot]; -} - -void Being::addAfkEffect() -{ - addSpecialEffect(mAwayEffect); -} - -void Being::removeAfkEffect() -{ - removeSpecialEffect(); -} - -void Being::addSpecialEffect(const int effect) -{ - if (effectManager && Particle::enabled - && !mSpecialParticle && effect != -1) - { - mSpecialParticle = effectManager->triggerReturn(effect, this); - } -} - -void Being::removeSpecialEffect() -{ - if (effectManager && mSpecialParticle) - { - mChildParticleEffects.removeLocally(mSpecialParticle); - mSpecialParticle = nullptr; - } - delete mAnimationEffect; - mAnimationEffect = nullptr; -} - -void Being::updateAwayEffect() -{ - if (mAway) - addAfkEffect(); - else - removeAfkEffect(); -} - -void Being::addEffect(const std::string &name) -{ - delete mAnimationEffect; - mAnimationEffect = AnimatedSprite::load( - paths.getStringValue("sprites") + name); -} - -void Being::addPet(const int id) -{ - if (!actorSpriteManager) - return; - - removePet(); - Being *const being = actorSpriteManager->createBeing( - id, ActorSprite::PET, 0); - if (being) - { - being->setTileCoords(getTileX(), getTileY()); - being->setOwner(this); - mPetId = id; - mPet = being; - } -} - -void Being::removePet() -{ - if (!actorSpriteManager) - return; - - mPetId = 0; - if (mPet) - { - mPet->setOwner(nullptr); - actorSpriteManager->destroy(mPet); - mPet = nullptr; - } -} - -void Being::updatePets() -{ - removePet(); - FOR_EACH (std::vector::const_iterator, it, mSpriteIDs) - { - const int id = *it; - if (!id) - continue; - const ItemInfo &info = ItemDB::get(id); - const int pet = info.getPet(); - if (pet) - { - addPet(pet); - return; - } - } -} - -void Being::playSfx(const SoundInfo &sound, Being *const being, - const bool main, const int x, const int y) -{ - if (being) - { - // here need add timer and delay sound - const int time = tick_time; - if (main) - { - being->mNextSound.sound = nullptr; - being->mNextSound.time = time + sound.delay; - soundManager.playSfx(sound.sound, x, y); - } - else if (mNextSound.time <= time) - { // old event sound time is gone. we can play new sound - being->mNextSound.sound = nullptr; - being->mNextSound.time = time + sound.delay; - soundManager.playSfx(sound.sound, x, y); - } - else - { // old event sound in progress. need save sound and wait - being->mNextSound.sound = &sound; - being->mNextSound.x = x; - being->mNextSound.y = y; - } - } - else - { - soundManager.playSfx(sound.sound, x, y); - } -} - -void Being::setLook(const int look) -{ - if (mType == PLAYER) - setSubtype(mSubType, look); -} diff --git a/src/being.h b/src/being.h deleted file mode 100644 index 46d82c79d..000000000 --- a/src/being.h +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef BEING_H -#define BEING_H - -#include "equipment.h" - -#include "resources/beinginfo.h" - -#include - -#include - -#include -#include - -#include "localconsts.h" - -static const unsigned int FIRST_IGNORE_EMOTE = 14; -static const unsigned int STATUS_EFFECTS = 32; - -static const unsigned int SPEECH_TIME = 500; -static const unsigned int SPEECH_MIN_TIME = 200; -static const unsigned int SPEECH_MAX_TIME = 800; - -static const int DEFAULT_BEING_WIDTH = 32; -static const int DEFAULT_BEING_HEIGHT = 32; - -class AnimatedSprite; -class BeingCacheEntry; -class Being; -class FlashText; -class Guild; -class Inventory; -class ItemInfo; -class Item; -class Particle; -class Party; -class SpeechBubble; -class Text; - -struct Position; - -extern volatile int cur_time; - -enum Gender -{ - GENDER_MALE = 0, - GENDER_FEMALE = 1, - GENDER_UNSPECIFIED = 2, - GENDER_OTHER = 3 -}; - - -struct NextSoundInfo -{ - NextSoundInfo() : - sound(nullptr), - x(0), - y(0), - time(0) - { - } - - const SoundInfo *sound; - int x; - int y; - int time; -}; - -class Being : public ActorSprite, public ConfigListener -{ - public: - friend class BeingEquipBackend; - friend class LocalPlayer; - - enum FLAGS - { - FLAG_SHOP = 1, - FLAG_AWAY = 2, - FLAG_INACTIVE = 4, - FLAG_GENDER_OTHER = 32, - FLAG_GM = 64, - FLAG_GENDER_MALE = 128, - FLAG_SPECIAL = 128 + 64 - }; - - /** - * Action the being is currently performing - * WARNING: Has to be in sync with the same enum in the Being class - * of the server! - */ - enum Action - { - STAND = 0, - MOVE, - ATTACK, - SIT, - DEAD, - HURT, - SPAWN - }; - - enum Speech - { - NO_SPEECH = 0, - TEXT_OVERHEAD, - NO_NAME_IN_BUBBLE, - NAME_IN_BUBBLE - }; - - enum AttackType - { - HIT = 0x00, - CRITICAL = 0x0a, - MULTI = 0x08, - REFLECT = 0x04, - FLEE = 0x0b, - SKILL = 0xff, - MISS = 0xffff // pseudo value for miss attacks - }; - - enum Reachable - { - REACH_UNKNOWN = 0, - REACH_YES = 1, - REACH_NO = 2 - }; - - /** - * Directions, to be used as bitmask values - */ - enum BeingDirection - { - DOWN = 1, - LEFT = 2, - UP = 4, - RIGHT = 8 - }; - - /** - * Constructor. - * - * @param id a unique being id - * @param subtype partly determines the type of the being - * @param map the map the being is on - */ - Being(const int id, const Type type, const uint16_t subtype, - Map *const map); - - A_DELETE_COPY(Being) - - virtual ~Being(); - - Type getType() const A_WARN_UNUSED - { return mType; } - - /** - * Removes all path nodes from this being. - */ - void clearPath(); - - /** - * Returns the time spent in the current action. - */ - int getActionTime() const A_WARN_UNUSED - { return mActionTime; } - - /** - * Set the current action time. - * @see Ea::BeingHandler that set it to tick time. - */ - void setActionTime(const int actionTime) - { mActionTime = actionTime; } - - /** - * Makes this being take the next tile of its path. - * TODO: Used by eAthena only? - */ - virtual void nextTile(); - - /** - * Get the current X pixel offset. - * TODO: Used by eAthena only? - */ - int getXOffset() const A_WARN_UNUSED - { return getOffset(LEFT, RIGHT); } - - /** - * Get the current Y pixel offset. - * TODO: Used by eAthena only? - */ - int getYOffset() const A_WARN_UNUSED - { return getOffset(UP, DOWN); } - - /** - * Creates a path for the being from current position to ex and ey - */ - void setDestination(const int dstX, const int dstY); - - /** - * Returns the destination for this being. - */ - const Vector &getDestination() const A_WARN_UNUSED - { return mDest; } - - /** - * Returns the tile x coord - */ - int getTileX() const A_WARN_UNUSED - { return mX; } - - /** - * Returns the tile y coord - */ - int getTileY() const A_WARN_UNUSED - { return mY; } - - /** - * Sets the tile x and y coord - */ - void setTileCoords(const int x, const int y) - { mX = x; mY = y; } - - /** - * Puts a "speech balloon" above this being for the specified amount - * of time. - * - * @param text The text that should appear. - * @param time The amount of time the text should stay in milliseconds. - */ - void setSpeech(const std::string &text, - const std::string &channel = "", - int time = 0); - - /** - * Puts a damage bubble above this being. - * - * @param attacker the attacking being - * @param damage the amount of damage recieved (0 means miss) - * @param type the attack type - * @param id skill id - */ - void takeDamage(Being *const attacker, const int damage, - const AttackType type, const int attackId = 1); - - /** - * Handles an attack of another being by this being. - * - * @param victim the victim being - * @param damage the amount of damage dealt (0 means miss) - * @param type the attack type - */ - void handleAttack(Being *const victim, const int damage, - const int attackId = 1); - - virtual void handleSkill(Being *const victim, const int damage, - const int skillId, const int skillLevel); - - const ItemInfo *getEquippedWeapon() const A_WARN_UNUSED - { return mEquippedWeapon; } - - /** - * Returns the name of the being. - */ - const std::string &getName() const A_WARN_UNUSED - { return mName; } - - /** - * Sets the name for the being. - * - * @param name The name that should appear. - */ - void setName(const std::string &name); - - bool getShowName() const A_WARN_UNUSED - { return mShowName; } - - void setShowName(const bool doShowName); - - /** - * Sets the name of the party the being is in. Shown in BeingPopup. - */ - void setPartyName(const std::string &name) - { mPartyName = name; } - - const std::string &getPartyName() const A_WARN_UNUSED - { return mPartyName; } - - const std::string &getGuildName() const A_WARN_UNUSED - { return mGuildName; } - - /** - * Sets the name of the primary guild the being is in. Shown in - * BeingPopup (eventually). - */ - void setGuildName(const std::string &name); - - void setGuildPos(const std::string &pos); - - /** - * Adds a guild to the being. - */ - void addGuild(Guild *const guild); - - /** - * Removers a guild from the being. - */ - void removeGuild(const int id); - - /** - * Returns a pointer to the specified guild that the being is in. - */ - Guild *getGuild(const std::string &guildName) const A_WARN_UNUSED; - - /** - * Returns a pointer to the specified guild that the being is in. - */ - Guild *getGuild(const int id) const A_WARN_UNUSED; - - /** - * Returns a pointer to the specified guild that the being is in. - */ - Guild *getGuild() const A_WARN_UNUSED; - - /** - * Returns all guilds the being is in. - */ - const std::map &getGuilds() const A_WARN_UNUSED - { return mGuilds; } - - /** - * Removes all guilds the being is in. - */ - void clearGuilds(); - - /** - * Get number of guilds the being belongs to. - */ - int16_t getNumberOfGuilds() const A_WARN_UNUSED - { return static_cast(mGuilds.size()); } - - bool isInParty() const A_WARN_UNUSED - { return mParty; } - - void setParty(Party *const party); - - void setGuild(Guild *const guild); - - void updateGuild(); - - Party *getParty() const A_WARN_UNUSED - { return mParty; } - - int getSpritesCount() const A_WARN_UNUSED - { return static_cast(size()); } - - /** - * Sets visible equipments for this being. - */ - void setSprite(const unsigned int slot, const int id, - std::string color = "", - const unsigned char colorId = 1, - const bool isWeapon = false, - const bool isTempSprite = false); - - void setSpriteID(const unsigned int slot, const int id); - - void setSpriteColor(const unsigned int slot, - const std::string &color = ""); - - /** - * Get the number of hairstyles implemented - */ - static int getNumOfHairstyles() A_WARN_UNUSED - { return mNumberOfHairstyles; } - - /** - * Get the number of races implemented - */ - static int getNumOfRaces() A_WARN_UNUSED - { return mNumberOfRaces; } - - /** - * Get the number of layers used to draw the being - */ - int getNumberOfLayers() const A_WARN_UNUSED - { return CompoundSprite::getNumberOfLayers(); } - - /** - * Performs being logic. - */ - virtual void logic() override; - - /** - * Draws the speech text above the being. - */ - void drawSpeech(const int offsetX, const int offsetY); - - /** - * Draws the emotion picture above the being. - */ - void drawEmotion(Graphics *const graphics, const int offsetX, - const int offsetY); - - uint16_t getSubType() const - { return mSubType; } - - /** - * Set Being's subtype (mostly for view for monsters and NPCs) - */ - void setSubtype(const uint16_t subtype, const uint8_t look); - - const BeingInfo *getInfo() const A_WARN_UNUSED - { return mInfo; } - - TargetCursorSize getTargetCursorSize() const A_WARN_UNUSED; - - int getTargetOffsetX() const A_WARN_UNUSED - { - if (!mInfo) - return 0; - return mInfo->getTargetOffsetX(); - } - - int getTargetOffsetY() const A_WARN_UNUSED - { - if (!mInfo) - return 0; - return mInfo->getTargetOffsetY(); - } - - /** - * Gets the way the object is blocked by other objects. - */ - virtual unsigned char getWalkMask() const A_WARN_UNUSED - { - if (!mInfo) - return 0; - return mInfo->getWalkMask(); - } - - /** - * Gets the way the monster blocks pathfinding for other objects - */ - Map::BlockType getBlockType() const A_WARN_UNUSED - { - if (!mInfo) - return Map::BLOCKTYPE_NONE; - return mInfo->getBlockType(); - } - - /** - * Sets the walk speed. - * in pixels per second for eAthena, - * in tiles per second for Manaserv. - */ - void setWalkSpeed(const Vector &speed) - { mWalkSpeed = speed; } - - /** - * Gets the walk speed. - * in pixels per second for eAthena, - * in tiles per second for Manaserv (0.1 precision). - */ - Vector getWalkSpeed() const A_WARN_UNUSED - { return mWalkSpeed; } - - /** - * Sets the attack speed. - * @todo In what unit? - */ - void setAttackSpeed(const int speed) - { mAttackSpeed = speed; } - - /** - * Gets the attack speed. - * @todo In what unit? - */ - int getAttackSpeed() const A_WARN_UNUSED - { return mAttackSpeed; } - - /** - * Sets the current action. - */ - virtual void setAction(const Action &action, const int attackType = 0); - - /** - * Get the being's action currently performed. - */ - Action getCurrentAction() const A_WARN_UNUSED - { return mAction; } - - /** - * Returns whether this being is still alive. - */ - bool isAlive() const A_WARN_UNUSED - { return mAction != DEAD; } - - /** - * Returns the current direction. - */ - uint8_t getDirection() const A_WARN_UNUSED - { return mDirection; } - - /** - * Sets the current direction. - */ - virtual void setDirection(const uint8_t direction); - - virtual void setDirectionDelayed(const uint8_t direction) - { mDirectionDelayed = direction; } - - uint8_t getDirectionDelayed() const A_WARN_UNUSED - { return mDirectionDelayed; } - - /** - * Returns the direction the being is facing. - */ - SpriteDirection getSpriteDirection() const A_WARN_UNUSED - { return static_cast(mSpriteDirection); } - - void setPosition(const Vector &pos); - - /** - * Overloaded method provided for convenience. - * - * @see setPosition(const Vector &pos) - */ - inline void setPosition(const float x, const float y, - const float z = 0.0f) - { setPosition(Vector(x, y, z)); } - - /** - * Returns the horizontal size of the current base sprite of the being. - */ - virtual int getWidth() const override A_WARN_UNUSED - { return std::max(CompoundSprite::getWidth(), DEFAULT_BEING_WIDTH); } - - /** - * Returns the vertical size of the current base sprite of the being. - */ - virtual int getHeight() const override A_WARN_UNUSED - { return std::max(CompoundSprite::getHeight(), DEFAULT_BEING_HEIGHT); } - - /** - * Returns the being's pixel radius used to detect collisions. - */ - virtual int getCollisionRadius() const A_WARN_UNUSED - { return 16; } - - /** - * Shoots a missile particle from this being, to target being - */ - void fireMissile(Being *const target, - const std::string &particle) const; - - /** - * Returns the path this being is following. An empty path is returned - * when this being isn't following any path currently. - */ - const Path &getPath() const A_WARN_UNUSED - { return mPath; } - - int getDistance() const A_WARN_UNUSED - { return mDistance; } - - void setDistance(const int n) - { mDistance = n; } - - /** - * Set the Emoticon type and time displayed above - * the being. - */ - void setEmote(const uint8_t emotion, const int emote_time); - - void setState(const uint8_t state); - - virtual void drawSprites(Graphics *const graphics, - int posX, int posY) const override; - - virtual void drawSpritesSDL(Graphics *const graphics, - int posX, int posY) const override; - - void drawHpBar(Graphics *const graphics, const int x, const int y, - const int maxHP, const int hp, const int damage, - const int color1, const int color2, const int width, - const int height) const; - - static void load(); - - virtual void optionChanged(const std::string &value) override; - - void flashName(const int time); - - int getDamageTaken() const A_WARN_UNUSED - { return mDamageTaken; } - - void setDamageTaken(const int damage) - { mDamageTaken = damage; } - - void updateName(); - - void setLevel(const int n) - { mLevel = n; } - - virtual int getLevel() const A_WARN_UNUSED - { return mLevel; } - - void setIsReachable(const int n) - { mIsReachable = n; } - - int isReachable() const A_WARN_UNUSED - { return mIsReachable; } - - static void reReadConfig(); - - static BeingCacheEntry* getCacheEntry(const int id) A_WARN_UNUSED; - - void addToCache() const; - - bool updateFromCache(); - - /** - * Sets the gender of this being. - */ - virtual void setGender(const Gender gender); - - Gender getGender() const A_WARN_UNUSED - { return mGender; } - - /** - * Return sprite sit action for current environment. - */ - std::string getSitAction() const A_WARN_UNUSED; - - std::string getMoveAction() const A_WARN_UNUSED; - - std::string getDeadAction() const A_WARN_UNUSED; - - std::string getStandAction() const A_WARN_UNUSED; - - std::string getSpawnAction() const A_WARN_UNUSED; - - std::string getWeaponAttackAction(const ItemInfo *const weapon) const; - - std::string getAttackAction(const Attack *const attack) const; - - /** - * Whether or not this player is a GM. - */ - bool isGM() const A_WARN_UNUSED - { return mIsGM; } - - /** - * Triggers whether or not to show the name as a GM name. - */ - void setGM(const bool gm); - - bool canTalk() const A_WARN_UNUSED - { return mType == NPC; } - - void talkTo() const; - - bool draw(Graphics *const graphics, - const int offsetX, const int offsetY) const override; - - bool drawSpriteAt(Graphics *const graphics, - const int x, const int y) const; - - void setMoveTime() - { mMoveTime = cur_time; } - - void setAttackTime() - { mAttackTime = cur_time; } - - void setTalkTime() - { mTalkTime = cur_time; } - - void setTestTime() - { mTestTime = cur_time; } - - void setOtherTime() - { mOtherTime = cur_time; } - - unsigned int getMoveTime() const - { return mMoveTime; } - - unsigned int getAttackTime() const - { return mAttackTime; } - - unsigned int getTalkTime() const - { return mTalkTime; } - - unsigned int getTestTime() const - { return mTestTime; } - - unsigned int getOtherTime() const - { return mOtherTime; } - - void resetCounters(); - - virtual void updateColors(); - - void setEnemy(const bool n) - { mEnemy = n; } - - const std::string &getIp() const A_WARN_UNUSED - { return mIp; } - - void setIp(std::string ip) - { mIp = ip; } - - unsigned int getPvpRank() const A_WARN_UNUSED - { return mPvpRank; } - - void setPvpRank(const unsigned int rank) - { mPvpRank = rank; } - - void setHP(const int n); - - void setMaxHP(const int hp); - - int getHP() const A_WARN_UNUSED - { return mHP; } - - uint8_t calcDirection(const int dstX, - const int dstY) const A_WARN_UNUSED; - - uint8_t calcDirection() const A_WARN_UNUSED; - - void setAttackDelay(const int n) - { mAttackDelay = n; } - - int getAttackDelay() const A_WARN_UNUSED - { return mAttackDelay; } - - int getMinHit() const A_WARN_UNUSED - { return mMinHit; } - - void setMinHit(const int n) - { mMinHit = n; } - - int getMaxHit() const A_WARN_UNUSED - { return mMaxHit; } - - void setMaxHit(const int n) - { mMaxHit = n; } - - int getCriticalHit() const A_WARN_UNUSED - { return mCriticalHit; } - - void setCriticalHit(const int n) - { mCriticalHit = n; } - - void updateHit(const int amount); - - Equipment *getEquipment() A_WARN_UNUSED; - - void undressItemById(const int id); - - int getGoodStatus() const A_WARN_UNUSED - { return mGoodStatus; } - - void setGoodStatus(const int n) - { mGoodStatus = n; } - - std::string getGenderSign() const A_WARN_UNUSED; - - std::string getGenderSignWithSpace() const A_WARN_UNUSED; - - void updateComment(); - - const std::string getComment() const A_WARN_UNUSED - { return mComment; } - - void setComment(std::string n) - { mComment = n; } - - static void clearCache(); - - static std::string loadComment(const std::string &name, - const int type) A_WARN_UNUSED; - - static void saveComment(const std::string &name, - const std::string &comment, const int type); - - bool isAdvanced() const A_WARN_UNUSED - { return mAdvanced; } - - void setAdvanced(const bool n) - { mAdvanced = n; addToCache(); } - - bool isShopEnabled() const A_WARN_UNUSED - { return mShop; } - - void enableShop(const bool b) - { mShop = b; } - - /** - * Sets the attack range. - */ - void setAttackRange(const int range) - { mAttackRange = range; } - - void attack(Being *target = nullptr, bool keep = false, - bool dontChangeEquipment = false); - - void attack2(Being *target = nullptr, bool keep = false, - bool dontChangeEquipment = false); - - void updatePercentHP(); - - void setRaceName(std::string name) - { mRaceName = name; } - - std::string getRaceName() const A_WARN_UNUSED - { return mRaceName; } - - int getSpriteID(const int slot) const A_WARN_UNUSED; - - void setHairStyle(const unsigned int slot, const int id); - - void setHairColor(const unsigned int slot, - const unsigned char color); - - void setHairColor(const unsigned char color) - { mHairColor = color; } - - unsigned char getHairColor() const A_WARN_UNUSED - { return mHairColor; } - - void recalcSpritesOrder(); - - int getHitEffect(const Being *const attacker, - const AttackType type, - const int attackId) const A_WARN_UNUSED; - - Cursor::Cursor getHoverCursor() const A_WARN_UNUSED - { return mInfo ? mInfo->getHoverCursor() : Cursor::CURSOR_POINTER; } - - void addAfkEffect(); - - void removeAfkEffect(); - - void updateAwayEffect(); - - void addSpecialEffect(const int effect); - - void removeSpecialEffect(); - - void addEffect(const std::string &name); - - void addPet(const int id); - - void removePet(); - - void updatePets(); - - Being *getPet() - { return mPet; } - - void setPet(Being *const pet) - { mPet = pet; } - - void setOwner(Being *const owner) - { mOwner = owner; } - - void playSfx(const SoundInfo &sound, Being *const being, - const bool main, const int x, const int y); - - int getLook() - { return mLook; } - - void setLook(const int look); - - static uint8_t genderToInt(const Gender sex) A_WARN_UNUSED; - - static Gender intToGender(uint8_t sex) A_WARN_UNUSED; - - NextSoundInfo mNextSound; - - protected: - /** - * Sets the new path for this being. - */ - void setPath(const Path &path); - - /** - * Updates name's location. - */ - virtual void updateCoords(); - - void showName(); - - static int getDefaultEffectId(const int type); - - BeingInfo *mInfo; - AnimatedSprite *mEmotionSprite; - AnimatedSprite* mAnimationEffect; - - std::string mSpriteAction; - std::string mName; /**< Name of character */ - std::string mRaceName; - std::string mPartyName; - std::string mGuildName; - std::string mSpeech; - - /** - * Holds a text object when the being displays it's name, 0 otherwise - */ - FlashText *mDispName; - const gcn::Color *mNameColor; - - /** Engine-related infos about weapon. */ - const ItemInfo *mEquippedWeapon; - - static int mNumberOfHairstyles; /** Number of hair styles in use */ - static int mNumberOfRaces; /** Number of races in use */ - - Path mPath; - Text *mText; - const gcn::Color *mTextColor; - - Vector mDest; /**< destination coordinates. */ - - StringVect mSpriteColors; - std::vector mSpriteIDs; - std::vector mSpriteColorsIds; - - // Character guild information - std::map mGuilds; - Party *mParty; - - int mActionTime; /**< Time spent in current action */ - int mEmotionTime; /**< Time until emotion disappears */ - - /** Time until the last speech sentence disappears */ - int mSpeechTime; - int mAttackSpeed; /**< Attack speed */ - - int mLevel; - int mAttackRange; - Gender mGender; - Action mAction; /**< Action the being is performing */ - uint16_t mSubType; /**< Subtype (graphical view, basically) */ - uint8_t mDirection; /**< Facing direction */ - uint8_t mDirectionDelayed; /**< Facing direction */ - uint8_t mSpriteDirection; /**< Facing direction */ - bool mShowName; - bool mIsGM; - - private: - /** - * Calculates the offset in the given directions. - * If walking in direction 'neg' the value is negated. - * TODO: Used by eAthena only? - */ - int getOffset(const signed char pos, - const signed char neg) const A_WARN_UNUSED; - - int searchSlotValue(const std::vector &slotRemap, - const int val) const A_WARN_UNUSED; - - void searchSlotValueItr(std::vector::iterator &it, int &idx, - std::vector &slotRemap, - const int val) const; - - void dumpSprites() const; - - const Type mType; - - /** Speech Bubble components */ - SpeechBubble *mSpeechBubble; - - /** - * Walk speed for x and y movement values. - * In pixels per second for eAthena, - * In pixels per ticks for Manaserv. - * @see MILLISECONDS_IN_A_TICK - */ - Vector mWalkSpeed; - std::string mIp; - int *mSpriteRemap; - int *mSpriteHide; - std::string mComment; - Being *mPet; - Being *mOwner; - Particle *mSpecialParticle; - - int mX, mY; /**< Position in tile */ - - int mDamageTaken; - int mHP; - int mMaxHP; - int mDistance; - int mIsReachable; /**< 0 - unknown, 1 - reachable, 2 - not reachable*/ - int mGoodStatus; - - static int mUpdateConfigTime; - static unsigned int mConfLineLim; - static int mSpeechType; - static bool mHighlightMapPortals; - static bool mHighlightMonsterAttackRange; - static bool mLowTraffic; - static bool mDrawHotKeys; - static bool mShowBattleEvents; - static bool mShowMobHP; - static bool mShowOwnHP; - static bool mShowGender; - static bool mShowLevel; - static bool mShowPlayersStatus; - static bool mEnableReorderSprites; - static bool mHideErased; - static bool mMoveNames; - static int mAwayEffect; - - unsigned int mMoveTime; - unsigned int mAttackTime; - unsigned int mTalkTime; - unsigned int mOtherTime; - unsigned int mTestTime; - int mAttackDelay; - int mMinHit; - int mMaxHit; - int mCriticalHit; - unsigned int mPvpRank; - unsigned int mNumber; - int mPetId; - int mLook; - unsigned char mHairColor; - bool mErased; - bool mEnemy; - bool mGotComment; - bool mAdvanced; - bool mShop; - bool mAway; - bool mInactive; -}; - -extern std::list beingInfoCache; - -#endif // BEING_H diff --git a/src/being/actor.cpp b/src/being/actor.cpp new file mode 100644 index 000000000..0efed5d18 --- /dev/null +++ b/src/being/actor.cpp @@ -0,0 +1,71 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/actor.h" + +#include "map.h" + +#include "resources/image.h" +#include "resources/resourcemanager.h" + +#include "debug.h" + +Actor::Actor(): + mMap(nullptr), + mPos(), + mYDiff(0), + mMapActor() +{ +} + +Actor::~Actor() +{ + setMap(nullptr); +} + +void Actor::setMap(Map *const map) +{ + // Remove Actor from potential previous map + if (mMap) + mMap->removeActor(mMapActor); + + mMap = map; + + // Add Actor to potential new map + if (mMap) + mMapActor = mMap->addActor(this); +} + +int Actor::getTileX() const +{ + if (!mMap || !mMap->getTileWidth()) + return 0; + + return getPixelX() / mMap->getTileWidth(); +} + +int Actor::getTileY() const +{ + if (!mMap || !mMap->getTileHeight()) + return 0; + + return getPixelY() / mMap->getTileHeight(); +} diff --git a/src/being/actor.h b/src/being/actor.h new file mode 100644 index 000000000..326b9f40c --- /dev/null +++ b/src/being/actor.h @@ -0,0 +1,142 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_ACTOR_H +#define BEING_ACTOR_H + +#include "vector.h" + +#include + +#include "localconsts.h" + +class Actor; +class Graphics; +class Image; +class Map; + +typedef std::list Actors; +typedef Actors::const_iterator ActorsCIter; + +class Actor +{ +public: + Actor(); + + A_DELETE_COPY(Actor) + + virtual ~Actor(); + + /** + * Draws the Actor to the given graphics context. + * + * Note: this function could be simplified if the graphics context + * would support setting a translation offset. It already does this + * partly with the clipping rectangle support. + */ + virtual bool draw(Graphics *const graphics, + const int offsetX, const int offsetY) const = 0; + + /** + * Returns the horizontal size of the actors graphical representation + * in pixels or 0 when it is undefined. + */ + virtual int getWidth() const A_WARN_UNUSED + { return 0; } + + /** + * Returns the vertical size of the actors graphical representation + * in pixels or 0 when it is undefined. + */ + virtual int getHeight() const A_WARN_UNUSED + { return 0; } + + /** + * Returns the pixel position of this actor. + */ + const Vector &getPosition() const A_WARN_UNUSED + { return mPos; } + + /** + * Sets the pixel position of this actor. + */ + virtual void setPosition(const Vector &pos) + { mPos = pos; } + + /** + * Returns the pixels X coordinate of the actor. + */ + int getPixelX() const A_WARN_UNUSED + { return static_cast(mPos.x); } + + /** + * Returns the pixel Y coordinate of the actor. + */ + virtual int getPixelY() const A_WARN_UNUSED + { return static_cast(mPos.y); } + + /** + * Returns the pixel Y coordinate of the actor for sorting only. + */ + virtual int getSortPixelY() const A_WARN_UNUSED + { return static_cast(mPos.y) - mYDiff; } + + /** + * Returns the x coordinate in tiles of the actor. + */ + virtual int getTileX() const A_WARN_UNUSED; + + /** + * Returns the y coordinate in tiles of the actor. + */ + virtual int getTileY() const A_WARN_UNUSED; + + /** + * Returns the number of Image layers used to draw the actor. + */ + virtual int getNumberOfLayers() const A_WARN_UNUSED + { return 0; } + + /** + * Returns the current alpha value used to draw the actor. + */ + virtual float getAlpha() const A_WARN_UNUSED = 0; + + /** + * Sets the alpha value used to draw the actor. + */ + virtual void setAlpha(float alpha) = 0; + + virtual void setMap(Map *const map); + + const Map* getMap() const A_WARN_UNUSED + { return mMap; } + +protected: + Map *mMap; + Vector mPos; /**< Position in pixels relative to map. */ + int mYDiff; + +private: + Actors::iterator mMapActor; +}; + +#endif // BEING_ACTOR_H diff --git a/src/being/actorsprite.cpp b/src/being/actorsprite.cpp new file mode 100644 index 000000000..9c5ff6def --- /dev/null +++ b/src/being/actorsprite.cpp @@ -0,0 +1,398 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/actorsprite.h" + +#include "client.h" +#include "configuration.h" +#include "effectmanager.h" +#include "imagesprite.h" +#include "simpleanimation.h" +#include "soundmanager.h" +#include "statuseffect.h" + +#include "being/actorspritelistener.h" +#include "being/localplayer.h" + +#include "gui/theme.h" + +#include "net/net.h" + +#include "resources/imageset.h" +#include "resources/resourcemanager.h" + +#include "utils/checkutils.h" + +#include "debug.h" + +AnimatedSprite *ActorSprite::targetCursor[2][NUM_TC]; +bool ActorSprite::loaded = false; + +ActorSprite::ActorSprite(const int id) : + CompoundSprite(), + Actor(), + mStatusEffects(), + mStunParticleEffects(), + mStatusParticleEffects(&mStunParticleEffects, false), + mChildParticleEffects(&mStatusParticleEffects, false), + mId(id), + mStunMode(0), + mUsedTargetCursor(nullptr), + mActorSpriteListeners(), + mCursorPaddingX(0), + mCursorPaddingY(0), + mMustResetParticles(false) +{ +} + +ActorSprite::~ActorSprite() +{ + setMap(nullptr); + + mUsedTargetCursor = nullptr; + + if (player_node && player_node->getTarget() == this) + player_node->setTarget(nullptr); + + // Notify listeners of the destruction. + FOR_EACH (ActorSpriteListenerIterator, iter, mActorSpriteListeners) + { + if (reportFalse(*iter)) + (*iter)->actorSpriteDestroyed(*this); + } +} + +bool ActorSprite::draw(Graphics *const graphics, + const int offsetX, const int offsetY) const +{ + FUNC_BLOCK("ActorSprite::draw", 1) + const int px = getPixelX() + offsetX - 16; + // Temporary fix to the Y offset. +#ifdef MANASERV_SUPPORT + const int py = getPixelY() + offsetY - + ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); +#else + const int py = getPixelY() + offsetY - 32; +#endif + + if (mUsedTargetCursor) + { + mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); + mUsedTargetCursor->draw(graphics, + px + getTargetOffsetX() - mCursorPaddingX, + py + getTargetOffsetY() - mCursorPaddingY); + } + + return drawSpriteAt(graphics, px, py); +} + +bool ActorSprite::drawSpriteAt(Graphics *const graphics, + const int x, const int y) const +{ + return CompoundSprite::draw(graphics, x, y); +} + +void ActorSprite::logic() +{ + BLOCK_START("ActorSprite::logic") + // Update sprite animations + update(tick_time * MILLISECONDS_IN_A_TICK); + + // Restart status/particle effects, if needed + if (mMustResetParticles) + { + mMustResetParticles = false; + FOR_EACH (std::set::const_iterator, it, mStatusEffects) + { + const StatusEffect *const effect + = StatusEffect::getStatusEffect(*it, true); + if (effect && effect->particleEffectIsPersistent()) + updateStatusEffect(*it, true); + } + } + + // Update particle effects + mChildParticleEffects.moveTo(mPos.x, mPos.y); + BLOCK_END("ActorSprite::logic") +} + +void ActorSprite::actorLogic() +{ +} + +void ActorSprite::setMap(Map *const map) +{ + Actor::setMap(map); + + // Clear particle effect list because child particles became invalid + mChildParticleEffects.clear(); + mMustResetParticles = true; // Reset status particles on next redraw +} + +void ActorSprite::controlParticle(Particle *const particle) +{ + mChildParticleEffects.addLocally(particle); +} + +void ActorSprite::setTargetType(const TargetCursorType type) +{ + static const int targetWidths[ActorSprite::NUM_TC] = {0, 0, 0}; + static const int targetHeights[ActorSprite::NUM_TC] = {-16, -16, -32}; + + if (type == TCT_NONE) + { + untarget(); + } + else + { + const TargetCursorSize sz = getTargetCursorSize(); + mUsedTargetCursor = targetCursor[type][sz]; + if (mUsedTargetCursor) + { + mCursorPaddingX = targetWidths[sz]; + mCursorPaddingY = targetHeights[sz]; + } + } +} + +struct EffectDescription final +{ + std::string mGFXEffect; + std::string mSFXEffect; +}; + +void ActorSprite::setStatusEffect(const int index, const bool active) +{ + const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end(); + + if (active != wasActive) + { + updateStatusEffect(index, active); + if (active) + mStatusEffects.insert(index); + else + mStatusEffects.erase(index); + } +} + +void ActorSprite::setStatusEffectBlock(const int offset, + const uint16_t newEffects) +{ + for (unsigned i = 0; i < STATUS_EFFECTS; i++) + { + const int index = StatusEffect::blockEffectIndexToEffectIndex( + offset + i); + + if (index != -1) + setStatusEffect(index, (newEffects & (1 << i)) > 0); + } +} + +void ActorSprite::updateStunMode(const int oldMode, const int newMode) +{ + handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); + handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); +} + +void ActorSprite::updateStatusEffect(const int index, const bool newStatus) +{ + handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); +} + +void ActorSprite::handleStatusEffect(StatusEffect *const effect, + const int effectId) +{ + if (!effect) + return; + + Particle *const particle = effect->getParticle(); + + if (effectId >= 0) + { + mStatusParticleEffects.setLocally(effectId, particle); + } + else + { + mStunParticleEffects.clearLocally(); + if (particle) + mStunParticleEffects.addLocally(particle); + } +} + +void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, + const bool forceDisplay, + const int imageType, + const std::string &color) +{ + clear(); + + FOR_EACH (SpriteRefs, it, display.sprites) + { + if (!*it) + continue; + const std::string file = paths.getStringValue("sprites").append( + combineDye3((*it)->sprite, color)); + + const int variant = (*it)->variant; + addSprite(AnimatedSprite::delayedLoad(file, variant)); + } + + // Ensure that something is shown, if desired + if (empty() && forceDisplay) + { + if (display.image.empty()) + { + addSprite(AnimatedSprite::delayedLoad( + paths.getStringValue("sprites").append( + paths.getStringValue("spriteErrorFile")))); + } + else + { + ResourceManager *const resman = ResourceManager::getInstance(); + std::string imagePath; + switch (imageType) + { + case 0: + default: + imagePath = paths.getStringValue("itemIcons").append( + display.image); + break; + case 1: + imagePath = paths.getStringValue("itemIcons").append( + display.floor); + break; + } + imagePath = combineDye2(imagePath, color); + + Image *img = resman->getImage(imagePath); + + if (!img) + img = Theme::getImageFromTheme("unknown-item.png"); + + addSprite(new ImageSprite(img)); + if (img) + img->decRef(); + } + } + + mChildParticleEffects.clear(); + + // setup particle effects + if (Particle::enabled && particleEngine) + { + FOR_EACH (StringVectCIter, itr, display.particles) + { + Particle *const p = particleEngine->addEffect(*itr, 0, 0); + controlParticle(p); + } + } + + mMustResetParticles = true; +} + +void ActorSprite::load() +{ + if (loaded) + unload(); + + initTargetCursor(); + + loaded = true; +} + +void ActorSprite::unload() +{ + if (reportTrue(!loaded)) + return; + + cleanupTargetCursors(); + loaded = false; +} + +void ActorSprite::addActorSpriteListener(ActorSpriteListener *const listener) +{ + mActorSpriteListeners.push_front(listener); +} + +void ActorSprite::removeActorSpriteListener(ActorSpriteListener *const + listener) +{ + mActorSpriteListeners.remove(listener); +} + +static const char *cursorType(const int type) +{ + switch (type) + { + case ActorSprite::TCT_IN_RANGE: + return "in-range"; + default: + case ActorSprite::TCT_NORMAL: + return "normal"; + } +} + +static const char *cursorSize(const int size) +{ + switch (size) + { + case ActorSprite::TC_LARGE: + return "l"; + case ActorSprite::TC_MEDIUM: + return "m"; + default: + case ActorSprite::TC_SMALL: + return "s"; + } +} + +void ActorSprite::initTargetCursor() +{ + static const std::string targetCursorFile = "%s/target-cursor-%s-%s.xml"; + + const std::string path = branding.getStringValue("guiPath"); + + // Load target cursors + for (int size = TC_SMALL; size < NUM_TC; size++) + { + for (int type = TCT_NORMAL; type < NUM_TCT; type++) + { + targetCursor[type][size] = AnimatedSprite::load(strprintf( + targetCursorFile.c_str(), path.c_str(), cursorType(type), + cursorSize(size))); + } + } +} + +void ActorSprite::cleanupTargetCursors() +{ + for (int size = TC_SMALL; size < NUM_TC; size++) + { + for (int type = TCT_NORMAL; type < NUM_TCT; type++) + { + if (targetCursor[type][size]) + { + delete targetCursor[type][size]; + targetCursor[type][size] = nullptr; + } + } + } +} diff --git a/src/being/actorsprite.h b/src/being/actorsprite.h new file mode 100644 index 000000000..debdc69d1 --- /dev/null +++ b/src/being/actorsprite.h @@ -0,0 +1,247 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_ACTORSPRITE_H +#define BEING_ACTORSPRITE_H + +#include "localconsts.h" +#include "map.h" + +#include "being/actor.h" +#include "being/compoundsprite.h" + +#include "particle/particlecontainer.h" + +#include + +#include +#include + +#include "localconsts.h" + +class AnimatedSprite; +class StatusEffect; +class ActorSpriteListener; + +class ActorSprite : public CompoundSprite, public Actor +{ +public: + enum Type + { + UNKNOWN = 0, + PLAYER, + NPC, + MONSTER, + FLOOR_ITEM, + PORTAL, + PET, + AVATAR + }; + + enum TargetCursorSize + { + TC_SMALL = 0, + TC_MEDIUM, + TC_LARGE, + NUM_TC + }; + + enum TargetCursorType + { + TCT_NONE = -1, + TCT_NORMAL = 0, + TCT_IN_RANGE, + NUM_TCT + }; + + explicit ActorSprite(const int id); + + A_DELETE_COPY(ActorSprite) + + ~ActorSprite(); + + int getId() const A_WARN_UNUSED + { return mId; } + + void setId(const int id) + { mId = id; } + + /** + * Returns the type of the ActorSprite. + */ + virtual Type getType() const A_WARN_UNUSED + { return UNKNOWN; } + + virtual bool draw(Graphics *const graphics, + const int offsetX, const int offsetY) const override; + + virtual bool drawSpriteAt(Graphics *const graphics, + const int x, const int y) const; + + virtual void logic(); + + static void actorLogic(); + + void setMap(Map *const map) override; + + /** + * Gets the way the object blocks pathfinding for other objects + */ + virtual Map::BlockType getBlockType() const A_WARN_UNUSED + { return Map::BLOCKTYPE_NONE; } + + /** + * Take control of a particle. + */ + void controlParticle(Particle *const particle); + + /** + * Returns the required size of a target cursor for this being. + */ + virtual TargetCursorSize getTargetCursorSize() const A_WARN_UNUSED + { return TC_MEDIUM; } + + virtual int getTargetOffsetX() const A_WARN_UNUSED + { return 0; } + + virtual int getTargetOffsetY() const A_WARN_UNUSED + { return 0; } + + /** + * Sets the target animation for this actor. + */ + void setTargetType(const TargetCursorType type); + + /** + * Untargets the actor. + */ + void untarget() + { mUsedTargetCursor = nullptr; } + + /** + * Sets the actor's stun mode. If zero, the being is `normal', otherwise it + * is `stunned' in some fashion. + */ + void setStunMode(const uint16_t stunMode) + { + if (mStunMode != stunMode) + updateStunMode(mStunMode, stunMode); + mStunMode = stunMode; + } + + void setStatusEffect(const int index, const bool active); + + /** + * A status effect block is a 16 bit mask of status effects. We assign each + * such flag a block ID of offset + bitnr. + * + * These are NOT the same as the status effect indices. + */ + void setStatusEffectBlock(const int offset, const uint16_t flags); + + virtual void setAlpha(const float alpha) override + { CompoundSprite::setAlpha(alpha); } + + virtual float getAlpha() const override A_WARN_UNUSED + { return CompoundSprite::getAlpha(); } + + virtual int getWidth() const override A_WARN_UNUSED + { return CompoundSprite::getWidth(); } + + virtual int getHeight() const override A_WARN_UNUSED + { return CompoundSprite::getHeight(); } + + static void load(); + + static void unload(); + + /** + * Add an ActorSprite listener. + */ + void addActorSpriteListener(ActorSpriteListener *const listener); + + /** + * Remove an ActorSprite listener. + */ + void removeActorSpriteListener(ActorSpriteListener *const listener); + +protected: + /** + * Notify self that the stun mode has been updated. Invoked by + * setStunMode if something changed. + */ + virtual void updateStunMode(const int oldMode, const int newMode); + + /** + * Notify self that a status effect has flipped. + * The new flag is passed. + */ + virtual void updateStatusEffect(const int index, const bool newStatus); + + /** + * Handle an update to a status or stun effect + * + * \param The StatusEffect to effect + * \param effectId -1 for stun, otherwise the effect index + */ + virtual void handleStatusEffect(StatusEffect *const effect, + const int effectId); + + void setupSpriteDisplay(const SpriteDisplay &display, + const bool forceDisplay = true, + const int imageType = 0, + const std::string &color = ""); + + std::set mStatusEffects; /**< set of active status effects */ + + ParticleList mStunParticleEffects; + ParticleVector mStatusParticleEffects; + ParticleList mChildParticleEffects; + int mId; + uint16_t mStunMode; /**< Stun mode; zero if not stunned */ + +private: + /** Load the target cursors into memory */ + static void initTargetCursor(); + + /** Remove the target cursors from memory */ + static void cleanupTargetCursors(); + + /** Animated target cursors. */ + static AnimatedSprite *targetCursor[NUM_TCT][NUM_TC]; + + static bool loaded; + + /** Target cursor being used */ + AnimatedSprite *mUsedTargetCursor; + + typedef std::list ActorSpriteListeners; + typedef ActorSpriteListeners::iterator ActorSpriteListenerIterator; + ActorSpriteListeners mActorSpriteListeners; + + int mCursorPaddingX; + int mCursorPaddingY; + + /** Reset particle status effects on next redraw? */ + bool mMustResetParticles; +}; + +#endif // BEING_ACTORSPRITE_H diff --git a/src/being/actorspritelistener.h b/src/being/actorspritelistener.h new file mode 100644 index 000000000..3d5a48443 --- /dev/null +++ b/src/being/actorspritelistener.h @@ -0,0 +1,44 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_ACTORSPRITELISTENER_H +#define BEING_ACTORSPRITELISTENER_H + +class ActorSprite; + +class ActorSpriteListener +{ + public: + /** + * Destructor. + */ + virtual ~ActorSpriteListener() + { } + + /** + * Called when the ActorSprite has been destroyed. The listener will + * have to be registered first. + * @param actorSprite the ActorSprite being destroyed. + */ + virtual void actorSpriteDestroyed(const ActorSprite &actorSprite) = 0; +}; + +#endif // BEING_ACTORSPRITELISTENER_H diff --git a/src/being/being.cpp b/src/being/being.cpp new file mode 100644 index 000000000..7dfe9d9b6 --- /dev/null +++ b/src/being/being.cpp @@ -0,0 +1,3041 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/being.h" + +#include "actorspritemanager.h" +#include "animatedsprite.h" +#include "beingequipbackend.h" +#include "client.h" +#include "effectmanager.h" +#include "guild.h" +#include "party.h" +#include "soundconsts.h" +#include "soundmanager.h" +#include "text.h" + +#include "being/beingcacheentry.h" +#include "being/playerrelations.h" + +#include "particle/particle.h" + +#include "gui/equipmentwindow.h" +#include "gui/socialwindow.h" +#include "gui/speechbubble.h" +#include "gui/sdlfont.h" +#include "gui/skilldialog.h" + +#include "net/charserverhandler.h" +#include "net/gamehandler.h" +#include "net/inventoryhandler.h" +#include "net/net.h" +#include "net/npchandler.h" +#include "net/playerhandler.h" + +#include "resources/avatardb.h" +#include "resources/emotedb.h" +#include "resources/iteminfo.h" +#include "resources/monsterdb.h" +#include "resources/npcdb.h" +#include "resources/petdb.h" +#include "resources/resourcemanager.h" + +#include "gui/widgets/langtab.h" +#include "gui/widgets/skillinfo.h" + +#include "utils/gettext.h" + +#include + +#include "debug.h" + +const unsigned int CACHE_SIZE = 50; + +int Being::mNumberOfHairstyles = 1; +int Being::mNumberOfRaces = 1; + +int Being::mUpdateConfigTime = 0; +unsigned int Being::mConfLineLim = 0; +int Being::mSpeechType = 0; +bool Being::mHighlightMapPortals = false; +bool Being::mHighlightMonsterAttackRange = false; +bool Being::mLowTraffic = true; +bool Being::mDrawHotKeys = true; +bool Being::mShowBattleEvents = false; +bool Being::mShowMobHP = false; +bool Being::mShowOwnHP = false; +bool Being::mShowGender = false; +bool Being::mShowLevel = false; +bool Being::mShowPlayersStatus = false; +bool Being::mEnableReorderSprites = true; +bool Being::mHideErased = false; +bool Being::mMoveNames = false; +int Being::mAwayEffect = -1; + +std::list beingInfoCache; +typedef std::map::const_iterator GuildsMapCIter; +typedef std::map::const_iterator IntMapCIter; + +Being::Being(const int id, const Type type, const uint16_t subtype, + Map *const map) : + ActorSprite(id), + mNextSound(), + mInfo(BeingInfo::unknown), + mEmotionSprite(nullptr), + mAnimationEffect(nullptr), + mSpriteAction(SpriteAction::STAND), + mName(), + mRaceName(), + mPartyName(), + mGuildName(), + mSpeech(), + mDispName(nullptr), + mNameColor(nullptr), + mEquippedWeapon(nullptr), + mPath(), + mText(nullptr), + mTextColor(nullptr), + mDest(), + mSpriteColors(), + mSpriteIDs(), + mSpriteColorsIds(), + mGuilds(), + mParty(nullptr), + mActionTime(0), + mEmotionTime(0), + mSpeechTime(0), + mAttackSpeed(350), + mLevel(0), + mAttackRange(1), + mGender(GENDER_UNSPECIFIED), + mAction(STAND), + mSubType(0xFFFF), + mDirection(DOWN), + mDirectionDelayed(0), + mSpriteDirection(DIRECTION_DOWN), + mShowName(false), + mIsGM(false), + mType(type), + mSpeechBubble(new SpeechBubble), + mWalkSpeed(Net::getPlayerHandler()->getDefaultWalkSpeed()), + mIp(), + mSpriteRemap(new int[20]), + mSpriteHide(new int[20]), + mComment(), + mPet(nullptr), + mOwner(nullptr), + mSpecialParticle(nullptr), + mX(0), + mY(0), + mDamageTaken(0), + mHP(0), + mMaxHP(0), + mDistance(0), + mIsReachable(REACH_UNKNOWN), + mGoodStatus(-1), + mMoveTime(0), + mAttackTime(0), + mTalkTime(0), + mOtherTime(0), + mTestTime(cur_time), + mAttackDelay(0), + mMinHit(0), + mMaxHit(0), + mCriticalHit(0), + mPvpRank(0), + mNumber(100), + mPetId(0), + mLook(0), + mHairColor(0), + mErased(false), + mEnemy(false), + mGotComment(false), + mAdvanced(false), + mShop(false), + mAway(false), + mInactive(false) +{ + for (int f = 0; f < 20; f ++) + { + mSpriteRemap[f] = f; + mSpriteHide[f] = 0; + } + + setMap(map); + setSubtype(subtype, 0); + + if (mType == PLAYER) + mShowName = config.getBoolValue("visiblenames"); + else if (mType != NPC) + mGotComment = true; + + config.addListener("visiblenames", this); + + reReadConfig(); + + if (mType == NPC) + setShowName(true); + else + setShowName(mShowName); + + updateColors(); + updatePercentHP(); +} + +Being::~Being() +{ + config.removeListener("visiblenames", this); + + delete [] mSpriteRemap; + mSpriteRemap = nullptr; + delete [] mSpriteHide; + mSpriteHide = nullptr; + + delete mSpeechBubble; + mSpeechBubble = nullptr; + delete mDispName; + mDispName = nullptr; + delete mText; + mText = nullptr; + + delete mEmotionSprite; + mEmotionSprite = nullptr; + delete mAnimationEffect; + mAnimationEffect = nullptr; + + if (mOwner) + mOwner->setPet(nullptr); + if (mPet) + mPet->setOwner(nullptr); +} + +void Being::setSubtype(const uint16_t subtype, const uint8_t look) +{ + if (!mInfo) + return; + + if (subtype == mSubType && mLook == look) + return; + + mSubType = subtype; + mLook = look; + + if (mType == MONSTER) + { + mInfo = MonsterDB::get(mSubType); + if (mInfo) + { + setName(mInfo->getName()); + setupSpriteDisplay(mInfo->getDisplay(), true, 0, + mInfo->getColor(mLook)); + mYDiff = mInfo->getSortOffsetY(); + } + } + else if (mType == NPC) + { + mInfo = NPCDB::get(mSubType); + if (mInfo) + { + setupSpriteDisplay(mInfo->getDisplay(), false); + mYDiff = mInfo->getSortOffsetY(); + } + } + else if (mType == AVATAR) + { + mInfo = AvatarDB::get(mSubType); + if (mInfo) + setupSpriteDisplay(mInfo->getDisplay(), false); + } + else if (mType == PET) + { + mInfo = PETDB::get(mId); + if (mInfo) + { + setupSpriteDisplay(mInfo->getDisplay(), false); + mYDiff = mInfo->getSortOffsetY(); + } + } + else if (mType == PLAYER) + { + int id = -100 - subtype; + + // Prevent showing errors when sprite doesn't exist + if (!ItemDB::exists(id)) + { + id = -100; + // TRANSLATORS: default race name + setRaceName(_("Human")); + if (Net::getCharServerHandler()) + setSprite(Net::getCharServerHandler()->baseSprite(), id); + } + else + { + const ItemInfo &info = ItemDB::get(id); + setRaceName(info.getName()); + if (Net::getCharServerHandler()) + { + setSprite(Net::getCharServerHandler()->baseSprite(), + id, info.getColor(mLook)); + } + } + } +} + +ActorSprite::TargetCursorSize Being::getTargetCursorSize() const +{ + if (!mInfo) + return ActorSprite::TC_SMALL; + + return mInfo->getTargetCursorSize(); +} + +void Being::setPosition(const Vector &pos) +{ + Actor::setPosition(pos); + + updateCoords(); + + if (mText) + { + mText->adviseXY(static_cast(pos.x), static_cast(pos.y) + - getHeight() - mText->getHeight() - 6, mMoveNames); + } +} + +void Being::setDestination(const int dstX, const int dstY) +{ + // We can't calculate anything without a map anyway. + if (!mMap) + return; + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + setPath(mMap->findPath(mX, mY, dstX, dstY, getWalkMask())); + return; + } + +#ifdef MANASERV_SUPPORT + // Don't handle flawed destinations from server... + if (dstX == 0 || dstY == 0) + return; + + // If the destination is unwalkable, don't bother trying to get there + if (!mMap->getWalk(dstX / 32, dstY / 32)) + return; + + Position dest = mMap->checkNodeOffsets(getCollisionRadius(), getWalkMask(), + dstX, dstY); + Path thisPath = mMap->findPixelPath(static_cast(mPos.x), + static_cast(mPos.y), dest.x, dest.y, + static_cast(getCollisionRadius()), + static_cast(getWalkMask())); + + if (thisPath.empty()) + { + // If there is no path but the destination is on the same walkable tile, + // we accept it. + if (static_cast(mPos.x) / 32 == dest.x / 32 + && static_cast(mPos.y) / 32 == dest.y / 32) + { + mDest.x = static_cast(dest.x); + mDest.y = static_cast(dest.y); + } + setPath(Path()); + return; + } + + // The destination is valid, so we set it. + mDest.x = static_cast(dest.x); + mDest.y = static_cast(dest.y); + + setPath(thisPath); +#endif +} + +void Being::clearPath() +{ + mPath.clear(); +} + +void Being::setPath(const Path &path) +{ + mPath = path; + if (mPath.empty()) + return; + +#ifdef MANASERV_SUPPORT + if ((Net::getNetworkType() != ServerInfo::MANASERV) && + mAction != MOVE && mAction != DEAD) +#else + if (mAction != MOVE && mAction != DEAD) +#endif + { + nextTile(); + mActionTime = tick_time; + } +} + +void Being::setSpeech(const std::string &text, const std::string &channel, + int time) +{ + if (!userPalette) + return; + + if (!channel.empty() && (langChatTab && langChatTab->getChannelName() + != channel)) + { + return; + } + + // Remove colors + mSpeech = removeColors(text); + + // Trim whitespace + trim(mSpeech); + + const unsigned int lineLim = mConfLineLim; + if (lineLim > 0 && mSpeech.length() > lineLim) + mSpeech = mSpeech.substr(0, lineLim); + + trim(mSpeech); + if (mSpeech.empty()) + return; + + if (!time) + { + const size_t sz = mSpeech.size(); + if (sz < 200) + time = static_cast(SPEECH_TIME - 300 + (3 * sz)); + } + + if (time < static_cast(SPEECH_MIN_TIME)) + time = static_cast(SPEECH_MIN_TIME); + + // Check for links + size_t start = mSpeech.find('['); + size_t e = mSpeech.find(']', start); + + while (start != std::string::npos && e != std::string::npos) + { + // Catch multiple embeds and ignore them so it doesn't crash the client. + while ((mSpeech.find('[', start + 1) != std::string::npos) && + (mSpeech.find('[', start + 1) < e)) + { + start = mSpeech.find('[', start + 1); + } + + size_t position = mSpeech.find('|'); + if (mSpeech[start + 1] == '@' && mSpeech[start + 2] == '@') + { + mSpeech.erase(e, 1); + mSpeech.erase(start, (position - start) + 1); + } + position = mSpeech.find('@'); + + while (position != std::string::npos) + { + mSpeech.erase(position, 2); + position = mSpeech.find('@'); + } + + start = mSpeech.find('[', start + 1); + e = mSpeech.find(']', start); + } + + if (!mSpeech.empty()) + { + mSpeechTime = time <= static_cast(SPEECH_MAX_TIME) + ? time : static_cast(SPEECH_MAX_TIME); + } + + const int speech = mSpeechType; + if (speech == TEXT_OVERHEAD && userPalette) + { + delete mText; + + mText = new Text(mSpeech, + getPixelX(), getPixelY() - getHeight(), + gcn::Graphics::CENTER, + &userPalette->getColor(UserPalette::PARTICLE), + true); + } +} + +void Being::takeDamage(Being *const attacker, const int amount, + const AttackType type, const int attackId) +{ + if (!userPalette || !attacker) + return; + + gcn::Font *font = nullptr; + // TRANSLATORS: hit or miss message in attacks + const std::string damage = amount ? toString(amount) : type == FLEE ? + _("dodge") : _("miss"); + const gcn::Color *color; + + if (gui) + font = gui->getInfoParticleFont(); + + // Selecting the right color + if (type == CRITICAL || type == FLEE) + { + if (type == CRITICAL) + attacker->setCriticalHit(amount); + + if (attacker == player_node) + { + color = &userPalette->getColor( + UserPalette::HIT_LOCAL_PLAYER_CRITICAL); + } + else + { + color = &userPalette->getColor(UserPalette::HIT_CRITICAL); + } + } + else if (!amount) + { + if (attacker == player_node) + { + // This is intended to be the wrong direction to visually + // differentiate between hits and misses + color = &userPalette->getColor(UserPalette::HIT_LOCAL_PLAYER_MISS); + } + else + { + color = &userPalette->getColor(UserPalette::MISS); + } + } + else if (mType == MONSTER) + { + if (attacker == player_node) + { + color = &userPalette->getColor( + UserPalette::HIT_LOCAL_PLAYER_MONSTER); + } + else + { + color = &userPalette->getColor( + UserPalette::HIT_PLAYER_MONSTER); + } + } + else if (mType == PLAYER && attacker != player_node + && this == player_node) + { + // here player was attacked by other player. mark him as enemy. + color = &userPalette->getColor(UserPalette::HIT_PLAYER_PLAYER); + attacker->setEnemy(true); + attacker->updateColors(); + } + else + { + color = &userPalette->getColor(UserPalette::HIT_MONSTER_PLAYER); + } + + if (chatWindow && mShowBattleEvents) + { + if (this == player_node) + { + if (attacker->mType == PLAYER || amount) + { + chatWindow->battleChatLog(strprintf("%s : Hit you -%d", + attacker->getName().c_str(), amount), BY_OTHER); + } + } + else if (attacker == player_node && amount) + { + chatWindow->battleChatLog(strprintf("%s : You hit %s -%d", + attacker->getName().c_str(), getName().c_str(), amount), + BY_PLAYER); + } + } + if (font && particleEngine) + { + // Show damage number + particleEngine->addTextSplashEffect(damage, + getPixelX(), getPixelY() - 16, color, font, true); + } + + if (type != SKILL) + attacker->updateHit(amount); + + if (amount > 0) + { + if (player_node && player_node == this) + player_node->setLastHitFrom(attacker->getName()); + + mDamageTaken += amount; + if (mInfo) + { + playSfx(mInfo->getSound(SOUND_EVENT_HURT), this, false, mX, mY); + + if (!mInfo->isStaticMaxHP()) + { + if (!mHP && mInfo->getMaxHP() < mDamageTaken) + mInfo->setMaxHP(mDamageTaken); + } + } + if (mHP && isAlive()) + { + mHP -= amount; + if (mHP < 0) + mHP = 0; + } + + if (mType == MONSTER) + { + updatePercentHP(); + updateName(); + } + else if (mType == PLAYER && socialWindow && getName() != "") + { + socialWindow->updateAvatar(getName()); + } + + if (effectManager) + { + const int hitEffectId = getHitEffect(attacker, type, attackId); + if (hitEffectId >= 0) + effectManager->trigger(hitEffectId, this); + } + } + else + { + if (effectManager) + { + const int hitEffectId = getHitEffect(attacker, + MISS, attackId); + if (hitEffectId >= 0) + effectManager->trigger(hitEffectId, this); + } + } +} + +int Being::getHitEffect(const Being *const attacker, + const AttackType type, const int attackId) const +{ + if (!effectManager) + return 0; + + // Init the particle effect path based on current + // weapon or default. + int hitEffectId = 0; + if (type != SKILL) + { + if (attacker) + { + const ItemInfo *attackerWeapon = attacker->getEquippedWeapon(); + if (attackerWeapon && attacker->getType() == PLAYER) + { + if (type == MISS) + hitEffectId = attackerWeapon->getMissEffectId(); + else if (type != CRITICAL) + hitEffectId = attackerWeapon->getHitEffectId(); + else + hitEffectId = attackerWeapon->getCriticalHitEffectId(); + } + else if (attacker->getType() == MONSTER) + { + const BeingInfo *const info = attacker->getInfo(); + if (info) + { + const Attack *atk = info->getAttack(attackId); + if (atk) + { + if (type == MISS) + hitEffectId = atk->mMissEffectId; + else if (type != CRITICAL) + hitEffectId = atk->mHitEffectId; + else + hitEffectId = atk->mCriticalHitEffectId; + } + else + { + hitEffectId = getDefaultEffectId(type); + } + } + } + else + { + hitEffectId = getDefaultEffectId(type); + } + } + else + { + hitEffectId = getDefaultEffectId(type); + } + } + else + { + // move skills effects to +100000 in effects list + hitEffectId = attackId + 100000; + } + return hitEffectId; +} + +int Being::getDefaultEffectId(const int type) +{ + if (type == MISS) + return paths.getIntValue("missEffectId"); + else if (type != CRITICAL) + return paths.getIntValue("hitEffectId"); + else + return paths.getIntValue("criticalHitEffectId"); +} + +void Being::handleAttack(Being *const victim, const int damage, + const int attackId) +{ + if (!victim || !mInfo) + return; + + if (this != player_node) + setAction(Being::ATTACK, attackId); + + if (mType == PLAYER && mEquippedWeapon) + fireMissile(victim, mEquippedWeapon->getMissileParticleFile()); + else if (mInfo->getAttack(attackId)) + fireMissile(victim, mInfo->getAttack(attackId)->mMissileParticle); + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + reset(); + mActionTime = tick_time; + } + + if (this != player_node) + { + const uint8_t dir = calcDirection(victim->getTileX(), + victim->getTileY()); + if (dir) + setDirection(dir); + } + if (damage && victim->mType == PLAYER && victim->mAction == SIT) + victim->setAction(STAND); + + if (mType == PLAYER) + { + if (mSpriteIDs.size() >= 10) + { + // here 10 is weapon slot + int weaponId = mSpriteIDs[10]; + if (!weaponId) + weaponId = -100 - mSubType; + const ItemInfo &info = ItemDB::get(weaponId); + playSfx(info.getSound((damage > 0) ? + SOUND_EVENT_HIT : SOUND_EVENT_MISS), victim, true, mX, mY); + } + } + else + { + playSfx(mInfo->getSound((damage > 0) ? + SOUND_EVENT_HIT : SOUND_EVENT_MISS), victim, true, mX, mY); + } +} + +void Being::handleSkill(Being *const victim, const int damage, + const int skillId, const int skillLevel) +{ + if (!victim || !mInfo || !skillDialog) + return; + + if (this != player_node) + setAction(Being::ATTACK, 1); + + SkillInfo *const skill = skillDialog->getSkill(skillId); + const SkillData *const data = skill + ? skill->getData1(skillLevel) : nullptr; + if (data) + fireMissile(victim, data->particle); + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + reset(); + mActionTime = tick_time; + } + + if (this != player_node) + { + const uint8_t dir = calcDirection(victim->getTileX(), + victim->getTileY()); + if (dir) + setDirection(dir); + } + if (damage && victim->mType == PLAYER && victim->mAction == SIT) + victim->setAction(STAND); + if (data) + { + if (damage > 0) + playSfx(data->soundHit, victim, true, mX, mY); + else + playSfx(data->soundMiss, victim, true, mX, mY); + } + else + { + playSfx(mInfo->getSound((damage > 0) ? + SOUND_EVENT_HIT : SOUND_EVENT_MISS), victim, true, mX, mY); + } +} + +void Being::setName(const std::string &name) +{ + if (mType == NPC) + { + mName = name.substr(0, name.find('#', 0)); + showName(); + } + else + { + mName = name; + + if (mType == PLAYER && getShowName()) + showName(); + } +} + +void Being::setShowName(const bool doShowName) +{ + if (mShowName == doShowName) + return; + + mShowName = doShowName; + + if (doShowName) + { + showName(); + } + else + { + delete mDispName; + mDispName = nullptr; + } +} + +void Being::setGuildName(const std::string &name) +{ + mGuildName = name; +} + +void Being::setGuildPos(const std::string &pos A_UNUSED) +{ +} + +void Being::addGuild(Guild *const guild) +{ + if (!guild) + return; + + mGuilds[guild->getId()] = guild; + + if (this == player_node && socialWindow) + socialWindow->addTab(guild); +} + +void Being::removeGuild(const int id) +{ + if (this == player_node && socialWindow) + socialWindow->removeTab(mGuilds[id]); + + if (mGuilds[id]) + mGuilds[id]->removeMember(getName()); + mGuilds.erase(id); +} + +Guild *Being::getGuild(const std::string &guildName) const +{ + FOR_EACH (GuildsMapCIter, itr, mGuilds) + { + Guild *const guild = itr->second; + if (guild && guild->getName() == guildName) + return guild; + } + + return nullptr; +} + +Guild *Being::getGuild(const int id) const +{ + const std::map::const_iterator itr = mGuilds.find(id); + if (itr != mGuilds.end()) + return itr->second; + + return nullptr; +} + +Guild *Being::getGuild() const +{ + const std::map::const_iterator itr = mGuilds.begin(); + if (itr != mGuilds.end()) + return itr->second; + + return nullptr; +} + +void Being::clearGuilds() +{ + FOR_EACH (GuildsMapCIter, itr, mGuilds) + { + Guild *const guild = itr->second; + + if (guild) + { + if (this == player_node && socialWindow) + socialWindow->removeTab(guild); + + guild->removeMember(mId); + } + } + + mGuilds.clear(); +} + +void Being::setParty(Party *const party) +{ + if (party == mParty) + return; + + Party *const old = mParty; + mParty = party; + + if (old) + old->removeMember(mId); + + if (party) + party->addMember(mId, mName); + + updateColors(); + + if (this == player_node && socialWindow) + { + if (old) + socialWindow->removeTab(old); + + if (party) + socialWindow->addTab(party); + } +} + +void Being::updateGuild() +{ + if (!player_node) + return; + + Guild *const guild = player_node->getGuild(); + if (!guild) + { + clearGuilds(); + updateColors(); + return; + } + if (guild->getMember(getName())) + { + setGuild(guild); + if (!guild->getName().empty()) + mGuildName = guild->getName(); + } + updateColors(); +} + +void Being::setGuild(Guild *const guild) +{ + Guild *const old = getGuild(); + if (guild == old) + return; + + clearGuilds(); + addGuild(guild); + + if (old) + old->removeMember(mName); + + updateColors(); + + if (this == player_node && socialWindow) + { + if (old) + socialWindow->removeTab(old); + + if (guild) + socialWindow->addTab(guild); + } +} + +void Being::fireMissile(Being *const victim, const std::string &particle) const +{ + if (!victim || particle.empty() || !particleEngine) + return; + + Particle *const target = particleEngine->createChild(); + + if (!target) + return; + + Particle *const missile = target->addEffect( + particle, getPixelX(), getPixelY()); + + if (missile) + { + target->moveBy(Vector(0.0f, 0.0f, 32.0f)); + target->setLifetime(1000); + victim->controlParticle(target); + + missile->setDestination(target, 7, 0); + missile->setDieDistance(8); + missile->setLifetime(900); + } +} + +std::string Being::getSitAction() const +{ + if (serverVersion < 0) + { + return SpriteAction::SIT; + } + else + { + if (mMap) + { + const unsigned char mask = mMap->getBlockMask(mX, mY); + if (mask & Map::BLOCKMASK_GROUNDTOP) + return SpriteAction::SITTOP; + else if (mask & Map::BLOCKMASK_AIR) + return SpriteAction::SITSKY; + else if (mask & Map::BLOCKMASK_WATER) + return SpriteAction::SITWATER; + } + return SpriteAction::SIT; + } +} + + +std::string Being::getMoveAction() const +{ + if (serverVersion < 0) + { + return SpriteAction::MOVE; + } + else + { + if (mMap) + { + const unsigned char mask = mMap->getBlockMask(mX, mY); + if (mask & Map::BLOCKMASK_AIR) + return SpriteAction::FLY; + else if (mask & Map::BLOCKMASK_WATER) + return SpriteAction::SWIM; + } + return SpriteAction::MOVE; + } +} + +std::string Being::getWeaponAttackAction(const ItemInfo *const weapon) const +{ + if (!weapon) + return SpriteAction::ATTACK; + + if (serverVersion < 0 || !weapon) + { + return weapon->getAttackAction(); + } + else + { + if (mMap) + { + const unsigned char mask = mMap->getBlockMask(mX, mY); + if (mask & Map::BLOCKMASK_AIR) + return weapon->getSkyAttackAction(); + else if (mask & Map::BLOCKMASK_WATER) + return weapon->getWaterAttackAction(); + } + return weapon->getAttackAction(); + } +} + +std::string Being::getAttackAction(const Attack *const attack1) const +{ + if (!attack1) + return SpriteAction::ATTACK; + + if (serverVersion < 0 || !attack1) + { + return attack1->mAction; + } + else + { + if (mMap) + { + const unsigned char mask = mMap->getBlockMask(mX, mY); + if (mask & Map::BLOCKMASK_AIR) + return attack1->mSkyAction; + else if (mask & Map::BLOCKMASK_WATER) + return attack1->mWaterAction; + } + return attack1->mAction; + } +} + +#define getSpriteAction(func, action) \ + std::string Being::get##func##Action() const \ +{ \ + if (serverVersion < 0) \ + { \ + return SpriteAction::action; \ + } \ + else \ + { \ + if (mMap) \ + { \ + const unsigned char mask = mMap->getBlockMask(mX, mY); \ + if (mask & Map::BLOCKMASK_AIR) \ + return SpriteAction::action##SKY; \ + else if (mask & Map::BLOCKMASK_WATER) \ + return SpriteAction::action##WATER; \ + } \ + return SpriteAction::action; \ + } \ +} + +getSpriteAction(Dead, DEAD) +getSpriteAction(Stand, STAND) +getSpriteAction(Spawn, SPAWN) + +void Being::setAction(const Action &action, const int attackId) +{ + std::string currentAction = SpriteAction::INVALID; + + switch (action) + { + case MOVE: + if (mInfo) + { + playSfx(mInfo->getSound( + SOUND_EVENT_MOVE), nullptr, true, mX, mY); + } + currentAction = getMoveAction(); + // Note: When adding a run action, + // Differentiate walk and run with action name, + // while using only the ACTION_MOVE. + break; + case SIT: + currentAction = getSitAction(); + if (mInfo) + { + SoundEvent event; + if (currentAction == SpriteAction::SITTOP) + event = SOUND_EVENT_SITTOP; + else + event = SOUND_EVENT_SIT; + playSfx(mInfo->getSound(event), nullptr, true, mX, mY); + } + break; + case ATTACK: + if (mEquippedWeapon) + { + currentAction = getWeaponAttackAction(mEquippedWeapon); + reset(); + } + else + { + if (!mInfo || !mInfo->getAttack(attackId)) + break; + + currentAction = getAttackAction(mInfo->getAttack(attackId)); + reset(); + + // attack particle effect + if (Particle::enabled) + { + const int effectId = mInfo->getAttack(attackId)->mEffectId; + + int rotation; + switch (mSpriteDirection) + { + case DIRECTION_DOWN: + default: + rotation = 0; + break; + case DIRECTION_LEFT: + rotation = 90; + break; + case DIRECTION_UP: + rotation = 180; + break; + case DIRECTION_RIGHT: + rotation = 270; + break; + } + if (Particle::enabled && effectManager && effectId >= 0) + effectManager->trigger(effectId, this, rotation); + } + } + break; + case HURT: + if (mInfo) + { + playSfx(mInfo->getSound(SOUND_EVENT_HURT), + this, false, mX, mY); + } + break; + case DEAD: + currentAction = getDeadAction(); + if (mInfo) + { + playSfx(mInfo->getSound(SOUND_EVENT_DIE), this, true, mX, mY); + if (mType == MONSTER || mType == NPC) + mYDiff = mInfo->getDeadSortOffsetY(); + } + break; + case STAND: + currentAction = getStandAction(); + break; + case SPAWN: + if (mInfo) + { + playSfx(mInfo->getSound(SOUND_EVENT_SPAWN), + nullptr, true, mX, mY); + } + currentAction = getSpawnAction(); + break; + default: + logger->log("Being::setAction unknown action: " + + toString(static_cast(action))); + break; + } + + if (currentAction != SpriteAction::INVALID) + { + mSpriteAction = currentAction; + play(currentAction); + if (mEmotionSprite) + mEmotionSprite->play(currentAction); + if (mAnimationEffect) + mAnimationEffect->play(currentAction); + mAction = action; + } + + if (currentAction != SpriteAction::MOVE + && currentAction != SpriteAction::FLY + && currentAction != SpriteAction::SWIM) + { + mActionTime = tick_time; + } +} + +void Being::setDirection(const uint8_t direction) +{ + if (mDirection == direction) + return; + + mDirection = direction; + + mDirectionDelayed = 0; + + // if the direction does not change much, keep the common component + int mFaceDirection = mDirection & direction; + if (!mFaceDirection) + mFaceDirection = direction; + + SpriteDirection dir; + if (mFaceDirection & UP) + { + if (mFaceDirection & LEFT) + dir = DIRECTION_UPLEFT; + else if (mFaceDirection & RIGHT) + dir = DIRECTION_UPRIGHT; + else + dir = DIRECTION_UP; + } + else if (mFaceDirection & DOWN) + { + if (mFaceDirection & LEFT) + dir = DIRECTION_DOWNLEFT; + else if (mFaceDirection & RIGHT) + dir = DIRECTION_DOWNRIGHT; + else + dir = DIRECTION_DOWN; + } + else if (mFaceDirection & RIGHT) + { + dir = DIRECTION_RIGHT; + } + else + { + dir = DIRECTION_LEFT; + } + mSpriteDirection = static_cast(dir); + + CompoundSprite::setSpriteDirection(dir); + if (mEmotionSprite) + mEmotionSprite->setSpriteDirection(dir); + if (mAnimationEffect) + mAnimationEffect->setSpriteDirection(dir); + recalcSpritesOrder(); +} + +uint8_t Being::calcDirection() const +{ + uint8_t dir = 0; + if (mDest.x > mX) + dir |= RIGHT; + else if (mDest.x < mX) + dir |= LEFT; + if (mDest.y > mY) + dir |= DOWN; + else if (mDest.y < mY) + dir |= UP; + return dir; +} + +uint8_t Being::calcDirection(const int dstX, const int dstY) const +{ + uint8_t dir = 0; + if (dstX > mX) + dir |= RIGHT; + else if (dstX < mX) + dir |= LEFT; + if (dstY > mY) + dir |= DOWN; + else if (dstY < mY) + dir |= UP; + return dir; +} + +void Being::nextTile() +{ + if (mPath.empty()) + { + setAction(STAND); + return; + } + + const Position pos = mPath.front(); + mPath.pop_front(); + + const uint8_t dir = calcDirection(pos.x, pos.y); + if (dir) + setDirection(dir); + + if (!mMap || !mMap->getWalk(pos.x, pos.y, getWalkMask())) + { + setAction(STAND); + return; + } + + mX = pos.x; + mY = pos.y; + setAction(MOVE); + mActionTime += static_cast(mWalkSpeed.x / 10); +} + +void Being::logic() +{ + BLOCK_START("Being::logic") + // Reduce the time that speech is still displayed + if (mSpeechTime > 0) + mSpeechTime--; + + // Remove text and speechbubbles if speech boxes aren't being used + if (mSpeechTime == 0 && mText) + { + delete mText; + mText = nullptr; + } + + const int time = tick_time * MILLISECONDS_IN_A_TICK; + if (mEmotionSprite) + mEmotionSprite->update(time); + + if (mAnimationEffect) + { + mAnimationEffect->update(time); + if (mAnimationEffect->isTerminated()) + { + delete mAnimationEffect; + mAnimationEffect = nullptr; + } + } + + int frameCount = static_cast(getFrameCount()); +#ifdef MANASERV_SUPPORT + if ((Net::getNetworkType() == ServerInfo::MANASERV) && (mAction != DEAD)) + { + const Vector dest = (mPath.empty()) ? + mDest : Vector(static_cast(mPath.front().x), + static_cast(mPath.front().y)); + + // This is a hack that stops NPCs from running off the map... + if (mDest.x <= 0 && mDest.y <= 0) + { + BLOCK_END("Being::logic") + return; + } + + // The Vector representing the difference between current position + // and the next destination path node. + Vector dir = dest - mPos; + + const float nominalLength = dir.length(); + + // When we've not reached our destination, move to it. + if (nominalLength > 0.0f && !mWalkSpeed.isNull()) + { + // The deplacement of a point along a vector is calculated + // using the Unit Vector (â) multiplied by the point speed. + // â = a / ||a|| (||a|| is the a length.) + // Then, diff = (dir/||dir||) * speed. + const Vector normalizedDir = dir.normalized(); + Vector diff(normalizedDir.x * mWalkSpeed.x, + normalizedDir.y * mWalkSpeed.y); + + // Test if we don't miss the destination by a move too far: + if (diff.length() > nominalLength) + { + setPosition(mPos + dir); + + // Also, if the destination is reached, try to get the next + // path point, if existing. + if (!mPath.empty()) + mPath.pop_front(); + } + // Otherwise, go to it using the nominal speed. + else + { + setPosition(mPos + diff); + } + + if (mAction != MOVE) + setAction(MOVE); + + // Update the player sprite direction. + // N.B.: We only change this if the distance is more than one pixel. + if (nominalLength > 1.0f) + { + int direction = 0; + const float dx = std::abs(dir.x); + float dy = std::abs(dir.y); + + // When not using mouse for the player, we slightly prefer + // UP and DOWN position, especially when walking diagonally. + if (player_node && this == player_node && + !player_node->isPathSetByMouse()) + { + dy = dy + 2; + } + + if (dx > dy) + direction |= (dir.x > 0) ? RIGHT : LEFT; + else + direction |= (dir.y > 0) ? DOWN : UP; + + setDirection(static_cast(direction)); + } + } + else if (!mPath.empty()) + { + // If the current path node has been reached, + // remove it and go to the next one. + mPath.pop_front(); + } + else if (mAction == MOVE) + { + setAction(STAND); + } + } + else + if (Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + switch (mAction) + { + case STAND: + case SIT: + case DEAD: + case HURT: + case SPAWN: + default: + break; + + case MOVE: + { + if (getWalkSpeed().x && static_cast ((static_cast( + get_elapsed_time(mActionTime)) * static_cast( + frameCount)) / getWalkSpeed().x) + >= frameCount) + { + nextTile(); + } + break; + } + + case ATTACK: + { +// std::string particleEffect(""); + + if (!mActionTime) + break; + + int curFrame = 0; + if (mAttackSpeed) + { + curFrame = (get_elapsed_time(mActionTime) * frameCount) + / mAttackSpeed; + } + + if (this == player_node && curFrame >= frameCount) + nextTile(); + + break; + } + } + + // Update pixel coordinates + setPosition(static_cast(mX * 32 + 16 + getXOffset()), + static_cast(mY * 32 + 32 + getYOffset())); + } + + if (mEmotionSprite) + { + mEmotionTime--; + if (mEmotionTime == 0) + { + delete mEmotionSprite; + mEmotionSprite = nullptr; + } + } + + ActorSprite::logic(); + + if (frameCount < 10) + frameCount = 10; + + if (!isAlive() && getWalkSpeed().x + && Net::getGameHandler()->removeDeadBeings() + && static_cast ((static_cast(get_elapsed_time(mActionTime)) + / static_cast(getWalkSpeed().x))) >= frameCount) + { + if (mType != PLAYER && actorSpriteManager) + actorSpriteManager->destroy(this); + } + + const SoundInfo *const sound = mNextSound.sound; + if (sound) + { + const int time2 = tick_time; + if (time2 > mNextSound.time) + { + soundManager.playSfx(sound->sound, mNextSound.x, mNextSound.y); + + mNextSound.sound = nullptr; + mNextSound.time = time2 + sound->delay; + } + } + + BLOCK_END("Being::logic") +} + +void Being::drawEmotion(Graphics *const graphics, const int offsetX, + const int offsetY) +{ + const int px = getPixelX() - offsetX - 16; + const int py = getPixelY() - offsetY - 64 - 32; + if (mEmotionSprite) + mEmotionSprite->draw(graphics, px, py); + if (mAnimationEffect) + mAnimationEffect->draw(graphics, px, py); +} + +void Being::drawSpeech(const int offsetX, const int offsetY) +{ + if (!mSpeechBubble || mSpeech.empty()) + return; + + const int px = getPixelX() - offsetX; + const int py = getPixelY() - offsetY; + const int speech = mSpeechType; + + // Draw speech above this being + if (mSpeechTime == 0) + { + if (mSpeechBubble->isVisible()) + mSpeechBubble->setVisible(false); + } + else if (mSpeechTime > 0 && (speech == NAME_IN_BUBBLE || + speech == NO_NAME_IN_BUBBLE)) + { + const bool isShowName = (speech == NAME_IN_BUBBLE); + + delete mText; + mText = nullptr; + + mSpeechBubble->setCaption(isShowName ? mName : ""); + + mSpeechBubble->setText(mSpeech, isShowName); + mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() / 2), + py - getHeight() - (mSpeechBubble->getHeight())); + mSpeechBubble->setVisible(true); + } + else if (mSpeechTime > 0 && speech == TEXT_OVERHEAD) + { + mSpeechBubble->setVisible(false); + + if (!mText && userPalette) + { + mText = new Text(mSpeech, getPixelX(), getPixelY() - getHeight(), + gcn::Graphics::CENTER, &Theme::getThemeColor( + Theme::BUBBLE_TEXT), true); + } + } + else if (speech == NO_SPEECH) + { + mSpeechBubble->setVisible(false); + + delete mText; + mText = nullptr; + } +} + +int Being::getOffset(const signed char pos, const signed char neg) const +{ + // Check whether we're walking in the requested direction + if (mAction != MOVE || !(mDirection & (pos | neg))) + return 0; + + int offset = 0; + + if (mMap) + { + const int time = get_elapsed_time(mActionTime); + offset = (pos == LEFT && neg == RIGHT) ? + static_cast((static_cast(time) + * static_cast(mMap->getTileWidth())) + / static_cast(mWalkSpeed.x)) : + static_cast((static_cast(time) + * static_cast(mMap->getTileHeight())) + / static_cast(mWalkSpeed.y)); + } + + // We calculate the offset _from_ the _target_ location + offset -= 32; + if (offset > 0) + offset = 0; + + // Going into negative direction? Invert the offset. + if (mDirection & pos) + offset = -offset; + + if (offset > 32) + offset = 32; + if (offset < -32) + offset = -32; + + return offset; +} + +void Being::updateCoords() +{ + if (!mDispName) + return; + + int offsetX = getPixelX(); + int offsetY = getPixelY(); + if (mInfo) + { + offsetX += mInfo->getNameOffsetX(); + offsetY += mInfo->getNameOffsetY(); + } + // Monster names show above the sprite instead of below it + if (mType == MONSTER) + offsetY += - getHeight() - mDispName->getHeight(); + + mDispName->adviseXY(offsetX, offsetY, mMoveNames); +} + +void Being::optionChanged(const std::string &value) +{ + if (mType == PLAYER && value == "visiblenames") + setShowName(config.getBoolValue("visiblenames")); +} + +void Being::flashName(const int time) +{ + if (mDispName) + mDispName->flash(time); +} + +std::string Being::getGenderSignWithSpace() const +{ + const std::string &str = getGenderSign(); + if (str.empty()) + return str; + else + return std::string(" ").append(str); +} + +std::string Being::getGenderSign() const +{ + std::string str; + if (mShowGender) + { + if (getGender() == GENDER_FEMALE) + str = "\u2640"; + else if (getGender() == GENDER_MALE) + str = "\u2642"; + } + if (mShowPlayersStatus && mAdvanced) + { + if (mShop) + str.append("$"); + if (mAway) + { + // TRANSLATORS: this away status writed in player nick + str.append(_("A")); + } + else if (mInactive) + { + // TRANSLATORS: this inactive status writed in player nick + str.append(_("I")); + } + } + return str; +} + +void Being::showName() +{ + if (mName.empty()) + return; + + delete mDispName; + mDispName = nullptr; + + if (mHideErased && player_relations.getRelation(mName) == + PlayerRelation::ERASED) + { + return; + } + + std::string displayName(mName); + + if (mType != MONSTER && (mShowGender || mShowLevel)) + { + displayName.append(" "); + if (mShowLevel && getLevel() != 0) + displayName.append(toString(getLevel())); + + displayName.append(getGenderSign()); + } + + if (mType == MONSTER) + { + if (config.getBoolValue("showMonstersTakedDamage")) + displayName.append(", ").append(toString(getDamageTaken())); + } + + gcn::Font *font = nullptr; + if (player_node && player_node->getTarget() == this + && mType != MONSTER) + { + font = boldFont; + } + else if (mType == PLAYER && !player_relations.isGoodName(this) && gui) + { + font = gui->getSecureFont(); + } + + if (mInfo) + { + mDispName = new FlashText(displayName, + getPixelX() + mInfo->getNameOffsetX(), + getPixelY() + mInfo->getNameOffsetY(), + gcn::Graphics::CENTER, mNameColor, font); + } + else + { + mDispName = new FlashText(displayName, getPixelX(), getPixelY(), + gcn::Graphics::CENTER, mNameColor, font); + } + + updateCoords(); +} + +void Being::updateColors() +{ + if (userPalette) + { + if (mType == MONSTER) + { + mNameColor = &userPalette->getColor(UserPalette::MONSTER); + mTextColor = &userPalette->getColor(UserPalette::MONSTER); + } + else if (mType == NPC) + { + mNameColor = &userPalette->getColor(UserPalette::NPC); + mTextColor = &userPalette->getColor(UserPalette::NPC); + } + else if (this == player_node) + { + mNameColor = &userPalette->getColor(UserPalette::SELF); + mTextColor = &Theme::getThemeColor(Theme::PLAYER); + } + else + { + mTextColor = &Theme::getThemeColor(Theme::PLAYER); + + if (player_relations.getRelation(mName) != PlayerRelation::ERASED) + mErased = false; + else + mErased = true; + + if (mIsGM) + { + mTextColor = &userPalette->getColor(UserPalette::GM); + mNameColor = &userPalette->getColor(UserPalette::GM); + } + else if (mEnemy) + { + mNameColor = &userPalette->getColor(UserPalette::MONSTER); + } + else if (mParty && mParty == player_node->getParty()) + { + mNameColor = &userPalette->getColor(UserPalette::PARTY); + } + else if (player_node && getGuild() + && getGuild() == player_node->getGuild()) + { + mNameColor = &userPalette->getColor(UserPalette::GUILD); + } + else if (player_relations.getRelation(mName) == + PlayerRelation::FRIEND) + { + mNameColor = &userPalette->getColor(UserPalette::FRIEND); + } + else if (player_relations.getRelation(mName) == + PlayerRelation::DISREGARDED + || player_relations.getRelation(mName) == + PlayerRelation::BLACKLISTED) + { + mNameColor = &userPalette->getColor(UserPalette::DISREGARDED); + } + else if (player_relations.getRelation(mName) == + PlayerRelation::IGNORED + || player_relations.getRelation(mName) == + PlayerRelation::ENEMY2) + { + mNameColor = &userPalette->getColor(UserPalette::IGNORED); + } + else if (player_relations.getRelation(mName) == + PlayerRelation::ERASED) + { + mNameColor = &userPalette->getColor(UserPalette::ERASED); + } + else + { + mNameColor = &userPalette->getColor(UserPalette::PC); + } + } + + if (mDispName) + mDispName->setColor(mNameColor); + } +} + +void Being::setSprite(const unsigned int slot, const int id, + std::string color, const unsigned char colorId, + const bool isWeapon, const bool isTempSprite) +{ + if (slot >= Net::getCharServerHandler()->maxSprite()) + return; + + if (slot >= size()) + ensureSize(slot + 1); + + if (slot >= mSpriteIDs.size()) + mSpriteIDs.resize(slot + 1, 0); + + if (slot >= mSpriteColors.size()) + mSpriteColors.resize(slot + 1, ""); + + if (slot >= mSpriteColorsIds.size()) + mSpriteColorsIds.resize(slot + 1, 1); + + // id = 0 means unequip + if (id == 0) + { + removeSprite(slot); + + if (isWeapon) + mEquippedWeapon = nullptr; + const int id1 = mSpriteIDs[slot]; + if (id1) + { + const ItemInfo &info = ItemDB::get(id1); + if (mMap) + { + const int pet = info.getPet(); + if (pet) + removePet(); + } + } + } + else + { + const ItemInfo &info = ItemDB::get(id); + const std::string filename = info.getSprite(mGender, mSubType); + AnimatedSprite *equipmentSprite = nullptr; + + if (mType == PLAYER) + { + const int pet = info.getPet(); + if (pet) + addPet(pet); + } + + if (!filename.empty()) + { + if (color.empty()) + color = info.getDyeColorsString(colorId); + + equipmentSprite = AnimatedSprite::delayedLoad( + paths.getStringValue("sprites").append( + combineDye(filename, color))); + } + + if (equipmentSprite) + equipmentSprite->setSpriteDirection(getSpriteDirection()); + + CompoundSprite::setSprite(slot, equipmentSprite); + + if (isWeapon) + mEquippedWeapon = &ItemDB::get(id); + + setAction(mAction); + } + + if (!isTempSprite) + { + mSpriteIDs[slot] = id; + mSpriteColors[slot] = color; + mSpriteColorsIds[slot] = colorId; + recalcSpritesOrder(); + if (beingEquipmentWindow) + beingEquipmentWindow->updateBeing(this); + } +} + +void Being::setSpriteID(const unsigned int slot, const int id) +{ + setSprite(slot, id, mSpriteColors[slot]); +} + +void Being::setSpriteColor(const unsigned int slot, const std::string &color) +{ + setSprite(slot, mSpriteIDs[slot], color); +} + +void Being::setHairStyle(const unsigned int slot, const int id) +{ +// dumpSprites(); + setSprite(slot, id, ItemDB::get(id).getDyeColorsString(mHairColor)); +// dumpSprites(); +} + +void Being::setHairColor(const unsigned int slot, const unsigned char color) +{ + mHairColor = color; + setSprite(slot, mSpriteIDs[slot], ItemDB::get( + getSpriteID(slot)).getDyeColorsString(color)); +} + +void Being::dumpSprites() const +{ + std::vector::const_iterator it1 = mSpriteIDs.begin(); + const std::vector::const_iterator it1_end = mSpriteIDs.end(); + StringVectCIter it2 = mSpriteColors.begin(); + const StringVectCIter it2_end = mSpriteColors.end(); + std::vector::const_iterator it3 = mSpriteColorsIds.begin(); + const std::vector::const_iterator it3_end = mSpriteColorsIds.end(); + + logger->log("sprites"); + for (; it1 != it1_end && it2 != it2_end && it3 != it3_end; + ++ it1, ++ it2, ++ it3) + { + logger->log("%d,%s,%d", *it1, (*it2).c_str(), *it3); + } +} + +void Being::load() +{ + // Hairstyles are encoded as negative numbers. Count how far negative + // we can go. + int hairstyles = 1; + while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE, 0) != + paths.getStringValue("spriteErrorFile")) + { + hairstyles ++; + } + mNumberOfHairstyles = hairstyles; + + int races = 100; + while (ItemDB::get(-races).getSprite(GENDER_MALE, 0) != + paths.getStringValue("spriteErrorFile")) + { + races ++; + } + mNumberOfRaces = races - 100; +} + +void Being::updateName() +{ + if (mShowName) + showName(); +} + +void Being::reReadConfig() +{ + BLOCK_START("Being::reReadConfig") + if (mUpdateConfigTime + 1 < cur_time) + { + mAwayEffect = paths.getIntValue("afkEffectId"); + mHighlightMapPortals = config.getBoolValue("highlightMapPortals"); + mConfLineLim = config.getIntValue("chatMaxCharLimit"); + mSpeechType = config.getIntValue("speech"); + mHighlightMonsterAttackRange = + config.getBoolValue("highlightMonsterAttackRange"); + mLowTraffic = config.getBoolValue("lowTraffic"); + mDrawHotKeys = config.getBoolValue("drawHotKeys"); + mShowBattleEvents = config.getBoolValue("showBattleEvents"); + mShowMobHP = config.getBoolValue("showMobHP"); + mShowOwnHP = config.getBoolValue("showOwnHP"); + mShowGender = config.getBoolValue("showgender"); + mShowLevel = config.getBoolValue("showlevel"); + mShowPlayersStatus = config.getBoolValue("showPlayersStatus"); + mEnableReorderSprites = config.getBoolValue("enableReorderSprites"); + mHideErased = config.getBoolValue("hideErased"); + mMoveNames = config.getBoolValue("moveNames"); + + mUpdateConfigTime = cur_time; + } + BLOCK_END("Being::reReadConfig") +} + +bool Being::updateFromCache() +{ + const BeingCacheEntry *const entry = Being::getCacheEntry(getId()); + + if (entry && entry->getTime() + 120 >= cur_time) + { + if (!entry->getName().empty()) + setName(entry->getName()); + setPartyName(entry->getPartyName()); + setGuildName(entry->getGuildName()); + setLevel(entry->getLevel()); + setPvpRank(entry->getPvpRank()); + setIp(entry->getIp()); + + mAdvanced = entry->isAdvanced(); + if (mAdvanced) + { + const int flags = entry->getFlags(); + mShop = ((flags & FLAG_SHOP) != 0); + mAway = ((flags & FLAG_AWAY) != 0); + mInactive = ((flags & FLAG_INACTIVE) != 0); + if (mShop || mAway || mInactive) + updateName(); + } + else + { + mShop = false; + mAway = false; + mInactive = false; + } + + updateAwayEffect(); + if (mType == PLAYER) + updateColors(); + return true; + } + return false; +} + +void Being::addToCache() const +{ + if (player_node == this) + return; + + BeingCacheEntry *entry = Being::getCacheEntry(getId()); + if (!entry) + { + entry = new BeingCacheEntry(getId()); + beingInfoCache.push_front(entry); + + if (beingInfoCache.size() >= CACHE_SIZE) + { + delete beingInfoCache.back(); + beingInfoCache.pop_back(); + } + } + if (!mLowTraffic) + return; + + entry->setName(getName()); + entry->setLevel(getLevel()); + entry->setPartyName(getPartyName()); + entry->setGuildName(getGuildName()); + entry->setTime(cur_time); + entry->setPvpRank(getPvpRank()); + entry->setIp(getIp()); + entry->setAdvanced(isAdvanced()); + if (isAdvanced()) + { + int flags = 0; + if (mShop) + flags += FLAG_SHOP; + if (mAway) + flags += FLAG_AWAY; + if (mInactive) + flags += FLAG_INACTIVE; + entry->setFlags(flags); + } + else + { + entry->setFlags(0); + } +} + +BeingCacheEntry* Being::getCacheEntry(const int id) +{ + FOR_EACH (std::list::iterator, i, beingInfoCache) + { + if (!*i) + continue; + + if (id == (*i)->getId()) + { + // Raise priority: move it to front + if ((*i)->getTime() + 120 < cur_time) + { + beingInfoCache.splice(beingInfoCache.begin(), + beingInfoCache, i); + } + return *i; + } + } + return nullptr; +} + + +void Being::setGender(const Gender gender) +{ + if (gender != mGender) + { + mGender = gender; + + // Reload all subsprites + for (unsigned int i = 0; i < mSpriteIDs.size(); i++) + { + if (mSpriteIDs.at(i) != 0) + setSprite(i, mSpriteIDs.at(i), mSpriteColors.at(i)); + } + + updateName(); + } +} + +void Being::setGM(const bool gm) +{ + mIsGM = gm; + + updateColors(); +} + +void Being::talkTo() const +{ + if (!client->limitPackets(PACKET_NPC_TALK)) + return; + + Net::getNpcHandler()->talk(mId); +} + +bool Being::draw(Graphics *const graphics, + const int offsetX, const int offsetY) const +{ + bool res = true; + if (!mErased) + res = ActorSprite::draw(graphics, offsetX, offsetY); + + return res; +} + +void Being::drawSprites(Graphics *const graphics, + const int posX, const int posY) const +{ + const int sz = getNumberOfLayers(); + for (int f = 0; f < sz; f ++) + { + const int rSprite = mSpriteHide[mSpriteRemap[f]]; + if (rSprite == 1) + continue; + + Sprite *const sprite = getSprite(mSpriteRemap[f]); + if (sprite) + { + sprite->setAlpha(mAlpha); + sprite->draw(graphics, posX, posY); + } + } +} + +void Being::drawSpritesSDL(Graphics *const graphics, + const int posX, const int posY) const +{ + const size_t sz = size(); + for (unsigned f = 0; f < sz; f ++) + { + const int rSprite = mSpriteHide[mSpriteRemap[f]]; + if (rSprite == 1) + continue; + + const Sprite *const sprite = getSprite(mSpriteRemap[f]); + if (sprite) + sprite->draw(graphics, posX, posY); + } +} + +bool Being::drawSpriteAt(Graphics *const graphics, + const int x, const int y) const +{ + bool res = true; + + if (!mErased) + res = ActorSprite::drawSpriteAt(graphics, x, y); + + if (!userPalette) + return res; + + if (mHighlightMapPortals && mMap && mSubType == 45 && !mMap->getHasWarps()) + { + graphics->setColor(userPalette-> + getColorWithAlpha(UserPalette::PORTAL_HIGHLIGHT)); + + graphics->fillRectangle(gcn::Rectangle(x, y, 32, 32)); + + if (mDrawHotKeys && !mName.empty()) + { + gcn::Font *const font = gui->getFont(); + if (font) + { + graphics->setColor(userPalette->getColor(UserPalette::BEING)); + font->drawString(graphics, mName, x, y); + } + } + } + + if (mHighlightMonsterAttackRange && mType == ActorSprite::MONSTER + && isAlive()) + { + int attackRange; + if (mAttackRange) + attackRange = 32 * mAttackRange; + else + attackRange = 32; + + graphics->setColor(userPalette->getColorWithAlpha( + UserPalette::MONSTER_ATTACK_RANGE)); + + graphics->fillRectangle(gcn::Rectangle( + x - attackRange, y - attackRange, + 2 * attackRange + 32, 2 * attackRange + 32)); + } + + if (mShowMobHP && mInfo && player_node && player_node->getTarget() == this + && mType == MONSTER) + { + // show hp bar here + int maxHP = mMaxHP; + if (!maxHP) + maxHP = mInfo->getMaxHP(); + + drawHpBar(graphics, maxHP, mHP, mDamageTaken, + UserPalette::MONSTER_HP, UserPalette::MONSTER_HP2, + x - 50 + 16 + mInfo->getHpBarOffsetX(), + y + 32 - 6 + mInfo->getHpBarOffsetY(), + 2 * 50, 4); + } + if (mShowOwnHP && mInfo && player_node == this && mAction != DEAD) + { + drawHpBar(graphics, PlayerInfo::getAttribute(PlayerInfo::MAX_HP), + PlayerInfo::getAttribute(PlayerInfo::HP), 0, + UserPalette::PLAYER_HP, UserPalette::PLAYER_HP2, + x - 50 + 16 + mInfo->getHpBarOffsetX(), + y + 32 - 6 + mInfo->getHpBarOffsetY(), + 2 * 50, 4); + } + return res; +} + +void Being::drawHpBar(Graphics *const graphics, const int maxHP, const int hp, + const int damage, const int color1, const int color2, + const int x, const int y, const int width, + const int height) const +{ + if (maxHP <= 0 || !userPalette) + return; + + float p; + + if (hp) + { + p = static_cast(maxHP) / static_cast(hp); + } + else if (maxHP != damage) + { + p = static_cast(maxHP) + / static_cast(maxHP - damage); + } + else + { + p = 1; + } + + if (p <= 0 || p > width) + return; + + const int dx = static_cast(static_cast(width) / p); + + if (serverVersion < 1) + { // old servers + if ((!damage && (this != player_node || hp == maxHP)) + || (!hp && maxHP == damage)) + { + graphics->setColor(userPalette->getColorWithAlpha(color1)); + graphics->fillRectangle(gcn::Rectangle( + x, y, dx, height)); + return; + } + else if (width - dx <= 0) + { + graphics->setColor(userPalette->getColorWithAlpha(color2)); + graphics->fillRectangle(gcn::Rectangle( + x, y, width, height)); + return; + } + } + else + { // evol servers + if (hp == maxHP) + { + graphics->setColor(userPalette->getColorWithAlpha(color1)); + graphics->fillRectangle(gcn::Rectangle( + x, y, dx, height)); + return; + } + else if (width - dx <= 0) + { + graphics->setColor(userPalette->getColorWithAlpha(color2)); + graphics->fillRectangle(gcn::Rectangle( + x, y, width, height)); + return; + } + } + + graphics->setColor(userPalette->getColorWithAlpha(color1)); + graphics->fillRectangle(gcn::Rectangle( + x, y, dx, height)); + + graphics->setColor(userPalette->getColorWithAlpha(color2)); + graphics->fillRectangle(gcn::Rectangle( + x + dx, y, width - dx, height)); +} + +void Being::setHP(const int hp) +{ + mHP = hp; + if (mMaxHP < mHP) + mMaxHP = mHP; + if (mType == MONSTER) + updatePercentHP(); +} + +void Being::setMaxHP(const int hp) +{ + mMaxHP = hp; + if (mMaxHP < mHP) + mMaxHP = mHP; +} + +void Being::resetCounters() +{ + mMoveTime = 0; + mAttackTime = 0; + mTalkTime = 0; + mOtherTime = 0; + mTestTime = cur_time; +} + +void Being::recalcSpritesOrder() +{ + if (!mEnableReorderSprites) + return; + +// logger->log("recalcSpritesOrder"); + const unsigned sz = static_cast(size()); + if (sz < 1) + return; + + std::vector slotRemap; + std::map itemSlotRemap; + + std::vector::iterator it; + int oldHide[20]; + int dir = mSpriteDirection; + if (dir < 0 || dir >= 9) + dir = 0; + // hack for allow different logic in dead player + if (mAction == DEAD) + dir = 9; + + const unsigned int hairSlot = Net::getCharServerHandler()->hairSprite(); + + for (unsigned slot = 0; slot < sz; slot ++) + { + oldHide[slot] = mSpriteHide[slot]; + mSpriteHide[slot] = 0; + } + + const size_t spriteIdSize = mSpriteIDs.size(); + for (unsigned slot = 0; slot < sz; slot ++) + { + slotRemap.push_back(slot); + + if (spriteIdSize <= slot) + continue; + + const int id = mSpriteIDs[slot]; + if (!id) + continue; + + const ItemInfo &info = ItemDB::get(id); + + if (info.isRemoveSprites()) + { + SpriteToItemMap *const spriteToItems + = info.getSpriteToItemReplaceMap(dir); + + if (spriteToItems) + { + FOR_EACHP (SpriteToItemMapCIter, itr, spriteToItems) + { + const int remSprite = itr->first; + const std::map &itemReplacer = itr->second; + if (remSprite >= 0) + { // slot known + if (itemReplacer.empty()) + { + mSpriteHide[remSprite] = 1; + } + else + { + std::map::const_iterator repIt + = itemReplacer.find(mSpriteIDs[remSprite]); + if (repIt == itemReplacer.end()) + { + repIt = itemReplacer.find(0); + if (repIt->second == 0) + repIt = itemReplacer.end(); + } + if (repIt != itemReplacer.end()) + { + mSpriteHide[remSprite] = repIt->second; + if (repIt->second != 1) + { + if (static_cast(remSprite) + != hairSlot) + { + setSprite(remSprite, repIt->second, + mSpriteColors[remSprite], + 1, false, true); + } + else + { + setSprite(remSprite, repIt->second, + ItemDB::get(repIt->second) + .getDyeColorsString(mHairColor), + 1, false, true); + } + } + } + } + } + else + { // slot unknown. Search for real slot, this can be slow + FOR_EACH (IntMapCIter, repIt, itemReplacer) + { + for (unsigned slot2 = 0; slot2 < sz; slot2 ++) + { + if (mSpriteIDs[slot2] == repIt->first) + { + mSpriteHide[slot2] = repIt->second; + if (repIt->second != 1) + { + if (slot2 != hairSlot) + { + setSprite(slot2, repIt->second, + mSpriteColors[slot2], + 1, false, true); + } + else + { + setSprite(slot2, repIt->second, + ItemDB::get(repIt->second) + .getDyeColorsString( + mHairColor), + 1, false, true); + } + } + } + } + } + } + } + } + } + + if (info.mDrawBefore[dir] > 0) + { + const int id2 = mSpriteIDs[info.mDrawBefore[dir]]; + if (itemSlotRemap.find(id2) != itemSlotRemap.end()) + { +// logger->log("found duplicate (before)"); + const ItemInfo &info2 = ItemDB::get(id2); + if (info.mDrawPriority[dir] < info2.mDrawPriority[dir]) + { +// logger->log("old more priority"); + continue; + } + else + { +// logger->log("new more priority"); + itemSlotRemap.erase(id2); + } + } + + itemSlotRemap[id] = -info.mDrawBefore[dir]; + } + else if (info.mDrawAfter[dir] > 0) + { + const int id2 = mSpriteIDs[info.mDrawAfter[dir]]; + if (itemSlotRemap.find(id2) != itemSlotRemap.end()) + { + const ItemInfo &info2 = ItemDB::get(id2); + if (info.mDrawPriority[dir] < info2.mDrawPriority[dir]) + { +// logger->log("old more priority"); + continue; + } + else + { +// logger->log("new more priority"); + itemSlotRemap.erase(id2); + } + } + + itemSlotRemap[id] = info.mDrawAfter[dir]; +// logger->log("item slot->slot %d %d->%d", id, slot, itemSlotRemap[id]); + } + } +// logger->log("preparation end"); + + int lastRemap = 0; + unsigned cnt = 0; + + while (cnt < 15 && lastRemap >= 0) + { + lastRemap = -1; + cnt ++; +// logger->log("iteration"); + + for (unsigned slot0 = 0; slot0 < sz; slot0 ++) + { + const int slot = searchSlotValue(slotRemap, slot0); + const int val = slotRemap.at(slot); + int id = 0; + + if (static_cast(spriteIdSize) > val) + id = mSpriteIDs[val]; + + int idx = -1; + int idx1 = -1; +// logger->log("item %d, id=%d", slot, id); + int reorder = 0; + const std::map::const_iterator + orderIt = itemSlotRemap.find(id); + if (orderIt != itemSlotRemap.end()) + reorder = orderIt->second; + + if (reorder < 0) + { +// logger->log("move item %d before %d", slot, -reorder); + searchSlotValueItr(it, idx, slotRemap, -reorder); + if (it == slotRemap.end()) + return; + searchSlotValueItr(it, idx1, slotRemap, val); + if (it == slotRemap.end()) + return; + lastRemap = idx1; + if (idx1 + 1 != idx) + { + slotRemap.erase(it); + searchSlotValueItr(it, idx, slotRemap, -reorder); + slotRemap.insert(it, val); + } + } + else if (reorder > 0) + { +// logger->log("move item %d after %d", slot, reorder); + searchSlotValueItr(it, idx, slotRemap, reorder); + searchSlotValueItr(it, idx1, slotRemap, val); + if (it == slotRemap.end()) + return; + lastRemap = idx1; + if (idx1 != idx + 1) + { + slotRemap.erase(it); + searchSlotValueItr(it, idx, slotRemap, reorder); + if (it != slotRemap.end()) + { + ++ it; + if (it != slotRemap.end()) + slotRemap.insert(it, val); + else + slotRemap.push_back(val); + } + else + { + slotRemap.push_back(val); + } + } + } + } + } + +// logger->log("after remap"); + for (unsigned slot = 0; slot < sz; slot ++) + { + mSpriteRemap[slot] = slotRemap[slot]; + if (oldHide[slot] != 0 && oldHide[slot] != 1 && mSpriteHide[slot] == 0) + { + const int id = mSpriteIDs[slot]; + if (!id) + continue; + + setSprite(slot, id, mSpriteColors[slot], 1, false, true); + } +// logger->log("slot %d = %d", slot, mSpriteRemap[slot]); + } +} + +int Being::searchSlotValue(const std::vector &slotRemap, + const int val) const +{ + const size_t sz = size(); + for (size_t slot = 0; slot < sz; slot ++) + { + if (slotRemap[slot] == val) + return slot; + } + return getNumberOfLayers() - 1; +} + +void Being::searchSlotValueItr(std::vector::iterator &it, int &idx, + std::vector &slotRemap, + const int val) const +{ +// logger->log("searching %d", val); + it = slotRemap.begin(); + const std::vector::iterator it_end = slotRemap.end(); + idx = 0; + while (it != it_end) + { +// logger->log("testing %d", *it); + if (*it == val) + { +// logger->log("found at %d", idx); + return; + } + ++ it; + idx ++; + } +// logger->log("not found"); + idx = -1; + return; +} + +void Being::updateHit(const int amount) +{ + if (amount > 0) + { + if (!mMinHit || amount < mMinHit) + mMinHit = amount; + if (amount != mCriticalHit && (!mMaxHit || amount > mMaxHit)) + mMaxHit = amount; + } +} + +Equipment *Being::getEquipment() +{ + Equipment *const eq = new Equipment(); + Equipment::Backend *const bk = new BeingEquipBackend(this); + eq->setBackend(bk); + return eq; +} + +void Being::undressItemById(const int id) +{ + const size_t sz = mSpriteIDs.size(); + + for (size_t f = 0; f < sz; f ++) + { + if (id == mSpriteIDs[f]) + { + setSprite(static_cast(f), 0); + break; + } + } +} + +void Being::clearCache() +{ + delete_all(beingInfoCache); + beingInfoCache.clear(); +} + +void Being::updateComment() +{ + if (mGotComment || mName.empty()) + return; + + mGotComment = true; + mComment = loadComment(mName, mType); +} + +std::string Being::loadComment(const std::string &name, const int type) +{ + std::string str; + switch (type) + { + case PLAYER: + str = client->getUsersDirectory(); + break; + case NPC: + str = client->getNpcsDirectory(); + break; + default: + return ""; + } + + str.append(stringToHexPath(name)).append("/comment.txt"); + logger->log("load from: %s", str.c_str()); + StringVect lines; + + const ResourceManager *const resman = ResourceManager::getInstance(); + if (resman->existsLocal(str)) + { + lines = resman->loadTextFileLocal(str); + if (lines.size() >= 2) + return lines[1]; + } + return ""; +} + +void Being::saveComment(const std::string &name, + const std::string &comment, const int type) +{ + std::string dir; + switch (type) + { + case PLAYER: + dir = client->getUsersDirectory(); + break; + case NPC: + dir = client->getNpcsDirectory(); + break; + default: + return; + } + dir.append(stringToHexPath(name)); + const ResourceManager *const resman = ResourceManager::getInstance(); + resman->saveTextFile(dir, "comment.txt", + (name + "\n").append(comment)); +} + +void Being::setState(const uint8_t state) +{ + const bool shop = ((state & FLAG_SHOP) != 0); + const bool away = ((state & FLAG_AWAY) != 0); + const bool inactive = ((state & FLAG_INACTIVE) != 0); + const bool needUpdate = (shop != mShop || away != mAway + || inactive != mInactive); + + mShop = shop; + mAway = away; + mInactive = inactive; + updateAwayEffect(); + + if (needUpdate) + { + if (shop || away || inactive) + mAdvanced = true; + updateName(); + addToCache(); + } +} + +void Being::setEmote(const uint8_t emotion, const int emote_time) +{ + if ((emotion & FLAG_SPECIAL) == FLAG_SPECIAL) + { + setState(emotion); + mAdvanced = true; + } + else + { + const int emotionIndex = emotion - 1; + if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast()) + { + delete mEmotionSprite; + mEmotionSprite = nullptr; + const EmoteInfo *const info = EmoteDB::get2(emotionIndex, true); + if (info) + { + const EmoteSprite *const sprite = info->sprites.front(); + if (sprite) + { + mEmotionSprite = AnimatedSprite::clone(sprite->sprite); + if (mEmotionSprite) + mEmotionTime = info->time; + else + mEmotionTime = emote_time; + } + } + } + + if (mEmotionSprite) + { + mEmotionSprite->play(mSpriteAction); + mEmotionSprite->setSpriteDirection(static_cast( + mSpriteDirection)); + } + else + { + mEmotionTime = 0; + } + } +} + +void Being::updatePercentHP() +{ + if (!mMaxHP || !serverVersion) + return; + if (mHP) + { + const unsigned num = mHP * 100 / mMaxHP; + if (num != mNumber) + { + mNumber = num; + if (updateNumber(mNumber)) + setAction(mAction); + } + } +} + +uint8_t Being::genderToInt(const Gender sex) +{ + switch (sex) + { + case GENDER_FEMALE: + case GENDER_UNSPECIFIED: + default: + return 0; + case GENDER_MALE: + return 1; + case GENDER_OTHER: + return 3; + } +} + +Gender Being::intToGender(const uint8_t sex) +{ + switch (sex) + { + case 0: + default: + return GENDER_FEMALE; + case 1: + return GENDER_MALE; + case 3: + return GENDER_OTHER; + } +} + +int Being::getSpriteID(const int slot) const +{ + if (slot < 0 || static_cast(slot) >= mSpriteIDs.size()) + return -1; + + return mSpriteIDs[slot]; +} + +void Being::addAfkEffect() +{ + addSpecialEffect(mAwayEffect); +} + +void Being::removeAfkEffect() +{ + removeSpecialEffect(); +} + +void Being::addSpecialEffect(const int effect) +{ + if (effectManager && Particle::enabled + && !mSpecialParticle && effect != -1) + { + mSpecialParticle = effectManager->triggerReturn(effect, this); + } +} + +void Being::removeSpecialEffect() +{ + if (effectManager && mSpecialParticle) + { + mChildParticleEffects.removeLocally(mSpecialParticle); + mSpecialParticle = nullptr; + } + delete mAnimationEffect; + mAnimationEffect = nullptr; +} + +void Being::updateAwayEffect() +{ + if (mAway) + addAfkEffect(); + else + removeAfkEffect(); +} + +void Being::addEffect(const std::string &name) +{ + delete mAnimationEffect; + mAnimationEffect = AnimatedSprite::load( + paths.getStringValue("sprites") + name); +} + +void Being::addPet(const int id) +{ + if (!actorSpriteManager) + return; + + removePet(); + Being *const being = actorSpriteManager->createBeing( + id, ActorSprite::PET, 0); + if (being) + { + being->setTileCoords(getTileX(), getTileY()); + being->setOwner(this); + mPetId = id; + mPet = being; + } +} + +void Being::removePet() +{ + if (!actorSpriteManager) + return; + + mPetId = 0; + if (mPet) + { + mPet->setOwner(nullptr); + actorSpriteManager->destroy(mPet); + mPet = nullptr; + } +} + +void Being::updatePets() +{ + removePet(); + FOR_EACH (std::vector::const_iterator, it, mSpriteIDs) + { + const int id = *it; + if (!id) + continue; + const ItemInfo &info = ItemDB::get(id); + const int pet = info.getPet(); + if (pet) + { + addPet(pet); + return; + } + } +} + +void Being::playSfx(const SoundInfo &sound, Being *const being, + const bool main, const int x, const int y) +{ + if (being) + { + // here need add timer and delay sound + const int time = tick_time; + if (main) + { + being->mNextSound.sound = nullptr; + being->mNextSound.time = time + sound.delay; + soundManager.playSfx(sound.sound, x, y); + } + else if (mNextSound.time <= time) + { // old event sound time is gone. we can play new sound + being->mNextSound.sound = nullptr; + being->mNextSound.time = time + sound.delay; + soundManager.playSfx(sound.sound, x, y); + } + else + { // old event sound in progress. need save sound and wait + being->mNextSound.sound = &sound; + being->mNextSound.x = x; + being->mNextSound.y = y; + } + } + else + { + soundManager.playSfx(sound.sound, x, y); + } +} + +void Being::setLook(const int look) +{ + if (mType == PLAYER) + setSubtype(mSubType, look); +} diff --git a/src/being/being.h b/src/being/being.h new file mode 100644 index 000000000..29b735956 --- /dev/null +++ b/src/being/being.h @@ -0,0 +1,1063 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_BEING_H +#define BEING_BEING_H + +#include "equipment.h" + +#include "resources/beinginfo.h" + +#include + +#include + +#include +#include + +#include "localconsts.h" + +static const unsigned int FIRST_IGNORE_EMOTE = 14; +static const unsigned int STATUS_EFFECTS = 32; + +static const unsigned int SPEECH_TIME = 500; +static const unsigned int SPEECH_MIN_TIME = 200; +static const unsigned int SPEECH_MAX_TIME = 800; + +static const int DEFAULT_BEING_WIDTH = 32; +static const int DEFAULT_BEING_HEIGHT = 32; + +class AnimatedSprite; +class BeingCacheEntry; +class Being; +class FlashText; +class Guild; +class Inventory; +class ItemInfo; +class Item; +class Particle; +class Party; +class SpeechBubble; +class Text; + +struct Position; + +extern volatile int cur_time; + +enum Gender +{ + GENDER_MALE = 0, + GENDER_FEMALE = 1, + GENDER_UNSPECIFIED = 2, + GENDER_OTHER = 3 +}; + + +struct NextSoundInfo +{ + NextSoundInfo() : + sound(nullptr), + x(0), + y(0), + time(0) + { + } + + const SoundInfo *sound; + int x; + int y; + int time; +}; + +class Being : public ActorSprite, public ConfigListener +{ + public: + friend class BeingEquipBackend; + friend class LocalPlayer; + + enum FLAGS + { + FLAG_SHOP = 1, + FLAG_AWAY = 2, + FLAG_INACTIVE = 4, + FLAG_GENDER_OTHER = 32, + FLAG_GM = 64, + FLAG_GENDER_MALE = 128, + FLAG_SPECIAL = 128 + 64 + }; + + /** + * Action the being is currently performing + * WARNING: Has to be in sync with the same enum in the Being class + * of the server! + */ + enum Action + { + STAND = 0, + MOVE, + ATTACK, + SIT, + DEAD, + HURT, + SPAWN + }; + + enum Speech + { + NO_SPEECH = 0, + TEXT_OVERHEAD, + NO_NAME_IN_BUBBLE, + NAME_IN_BUBBLE + }; + + enum AttackType + { + HIT = 0x00, + CRITICAL = 0x0a, + MULTI = 0x08, + REFLECT = 0x04, + FLEE = 0x0b, + SKILL = 0xff, + MISS = 0xffff // pseudo value for miss attacks + }; + + enum Reachable + { + REACH_UNKNOWN = 0, + REACH_YES = 1, + REACH_NO = 2 + }; + + /** + * Directions, to be used as bitmask values + */ + enum BeingDirection + { + DOWN = 1, + LEFT = 2, + UP = 4, + RIGHT = 8 + }; + + /** + * Constructor. + * + * @param id a unique being id + * @param subtype partly determines the type of the being + * @param map the map the being is on + */ + Being(const int id, const Type type, const uint16_t subtype, + Map *const map); + + A_DELETE_COPY(Being) + + virtual ~Being(); + + Type getType() const A_WARN_UNUSED + { return mType; } + + /** + * Removes all path nodes from this being. + */ + void clearPath(); + + /** + * Returns the time spent in the current action. + */ + int getActionTime() const A_WARN_UNUSED + { return mActionTime; } + + /** + * Set the current action time. + * @see Ea::BeingHandler that set it to tick time. + */ + void setActionTime(const int actionTime) + { mActionTime = actionTime; } + + /** + * Makes this being take the next tile of its path. + * TODO: Used by eAthena only? + */ + virtual void nextTile(); + + /** + * Get the current X pixel offset. + * TODO: Used by eAthena only? + */ + int getXOffset() const A_WARN_UNUSED + { return getOffset(LEFT, RIGHT); } + + /** + * Get the current Y pixel offset. + * TODO: Used by eAthena only? + */ + int getYOffset() const A_WARN_UNUSED + { return getOffset(UP, DOWN); } + + /** + * Creates a path for the being from current position to ex and ey + */ + void setDestination(const int dstX, const int dstY); + + /** + * Returns the destination for this being. + */ + const Vector &getDestination() const A_WARN_UNUSED + { return mDest; } + + /** + * Returns the tile x coord + */ + int getTileX() const A_WARN_UNUSED + { return mX; } + + /** + * Returns the tile y coord + */ + int getTileY() const A_WARN_UNUSED + { return mY; } + + /** + * Sets the tile x and y coord + */ + void setTileCoords(const int x, const int y) + { mX = x; mY = y; } + + /** + * Puts a "speech balloon" above this being for the specified amount + * of time. + * + * @param text The text that should appear. + * @param time The amount of time the text should stay in milliseconds. + */ + void setSpeech(const std::string &text, + const std::string &channel = "", + int time = 0); + + /** + * Puts a damage bubble above this being. + * + * @param attacker the attacking being + * @param damage the amount of damage recieved (0 means miss) + * @param type the attack type + * @param id skill id + */ + void takeDamage(Being *const attacker, const int damage, + const AttackType type, const int attackId = 1); + + /** + * Handles an attack of another being by this being. + * + * @param victim the victim being + * @param damage the amount of damage dealt (0 means miss) + * @param type the attack type + */ + void handleAttack(Being *const victim, const int damage, + const int attackId = 1); + + virtual void handleSkill(Being *const victim, const int damage, + const int skillId, const int skillLevel); + + const ItemInfo *getEquippedWeapon() const A_WARN_UNUSED + { return mEquippedWeapon; } + + /** + * Returns the name of the being. + */ + const std::string &getName() const A_WARN_UNUSED + { return mName; } + + /** + * Sets the name for the being. + * + * @param name The name that should appear. + */ + void setName(const std::string &name); + + bool getShowName() const A_WARN_UNUSED + { return mShowName; } + + void setShowName(const bool doShowName); + + /** + * Sets the name of the party the being is in. Shown in BeingPopup. + */ + void setPartyName(const std::string &name) + { mPartyName = name; } + + const std::string &getPartyName() const A_WARN_UNUSED + { return mPartyName; } + + const std::string &getGuildName() const A_WARN_UNUSED + { return mGuildName; } + + /** + * Sets the name of the primary guild the being is in. Shown in + * BeingPopup (eventually). + */ + void setGuildName(const std::string &name); + + void setGuildPos(const std::string &pos); + + /** + * Adds a guild to the being. + */ + void addGuild(Guild *const guild); + + /** + * Removers a guild from the being. + */ + void removeGuild(const int id); + + /** + * Returns a pointer to the specified guild that the being is in. + */ + Guild *getGuild(const std::string &guildName) const A_WARN_UNUSED; + + /** + * Returns a pointer to the specified guild that the being is in. + */ + Guild *getGuild(const int id) const A_WARN_UNUSED; + + /** + * Returns a pointer to the specified guild that the being is in. + */ + Guild *getGuild() const A_WARN_UNUSED; + + /** + * Returns all guilds the being is in. + */ + const std::map &getGuilds() const A_WARN_UNUSED + { return mGuilds; } + + /** + * Removes all guilds the being is in. + */ + void clearGuilds(); + + /** + * Get number of guilds the being belongs to. + */ + int16_t getNumberOfGuilds() const A_WARN_UNUSED + { return static_cast(mGuilds.size()); } + + bool isInParty() const A_WARN_UNUSED + { return mParty; } + + void setParty(Party *const party); + + void setGuild(Guild *const guild); + + void updateGuild(); + + Party *getParty() const A_WARN_UNUSED + { return mParty; } + + int getSpritesCount() const A_WARN_UNUSED + { return static_cast(size()); } + + /** + * Sets visible equipments for this being. + */ + void setSprite(const unsigned int slot, const int id, + std::string color = "", + const unsigned char colorId = 1, + const bool isWeapon = false, + const bool isTempSprite = false); + + void setSpriteID(const unsigned int slot, const int id); + + void setSpriteColor(const unsigned int slot, + const std::string &color = ""); + + /** + * Get the number of hairstyles implemented + */ + static int getNumOfHairstyles() A_WARN_UNUSED + { return mNumberOfHairstyles; } + + /** + * Get the number of races implemented + */ + static int getNumOfRaces() A_WARN_UNUSED + { return mNumberOfRaces; } + + /** + * Get the number of layers used to draw the being + */ + int getNumberOfLayers() const A_WARN_UNUSED + { return CompoundSprite::getNumberOfLayers(); } + + /** + * Performs being logic. + */ + virtual void logic() override; + + /** + * Draws the speech text above the being. + */ + void drawSpeech(const int offsetX, const int offsetY); + + /** + * Draws the emotion picture above the being. + */ + void drawEmotion(Graphics *const graphics, const int offsetX, + const int offsetY); + + uint16_t getSubType() const + { return mSubType; } + + /** + * Set Being's subtype (mostly for view for monsters and NPCs) + */ + void setSubtype(const uint16_t subtype, const uint8_t look); + + const BeingInfo *getInfo() const A_WARN_UNUSED + { return mInfo; } + + TargetCursorSize getTargetCursorSize() const A_WARN_UNUSED; + + int getTargetOffsetX() const A_WARN_UNUSED + { + if (!mInfo) + return 0; + return mInfo->getTargetOffsetX(); + } + + int getTargetOffsetY() const A_WARN_UNUSED + { + if (!mInfo) + return 0; + return mInfo->getTargetOffsetY(); + } + + /** + * Gets the way the object is blocked by other objects. + */ + virtual unsigned char getWalkMask() const A_WARN_UNUSED + { + if (!mInfo) + return 0; + return mInfo->getWalkMask(); + } + + /** + * Gets the way the monster blocks pathfinding for other objects + */ + Map::BlockType getBlockType() const A_WARN_UNUSED + { + if (!mInfo) + return Map::BLOCKTYPE_NONE; + return mInfo->getBlockType(); + } + + /** + * Sets the walk speed. + * in pixels per second for eAthena, + * in tiles per second for Manaserv. + */ + void setWalkSpeed(const Vector &speed) + { mWalkSpeed = speed; } + + /** + * Gets the walk speed. + * in pixels per second for eAthena, + * in tiles per second for Manaserv (0.1 precision). + */ + Vector getWalkSpeed() const A_WARN_UNUSED + { return mWalkSpeed; } + + /** + * Sets the attack speed. + * @todo In what unit? + */ + void setAttackSpeed(const int speed) + { mAttackSpeed = speed; } + + /** + * Gets the attack speed. + * @todo In what unit? + */ + int getAttackSpeed() const A_WARN_UNUSED + { return mAttackSpeed; } + + /** + * Sets the current action. + */ + virtual void setAction(const Action &action, const int attackType = 0); + + /** + * Get the being's action currently performed. + */ + Action getCurrentAction() const A_WARN_UNUSED + { return mAction; } + + /** + * Returns whether this being is still alive. + */ + bool isAlive() const A_WARN_UNUSED + { return mAction != DEAD; } + + /** + * Returns the current direction. + */ + uint8_t getDirection() const A_WARN_UNUSED + { return mDirection; } + + /** + * Sets the current direction. + */ + virtual void setDirection(const uint8_t direction); + + virtual void setDirectionDelayed(const uint8_t direction) + { mDirectionDelayed = direction; } + + uint8_t getDirectionDelayed() const A_WARN_UNUSED + { return mDirectionDelayed; } + + /** + * Returns the direction the being is facing. + */ + SpriteDirection getSpriteDirection() const A_WARN_UNUSED + { return static_cast(mSpriteDirection); } + + void setPosition(const Vector &pos); + + /** + * Overloaded method provided for convenience. + * + * @see setPosition(const Vector &pos) + */ + inline void setPosition(const float x, const float y, + const float z = 0.0f) + { setPosition(Vector(x, y, z)); } + + /** + * Returns the horizontal size of the current base sprite of the being. + */ + virtual int getWidth() const override A_WARN_UNUSED + { return std::max(CompoundSprite::getWidth(), DEFAULT_BEING_WIDTH); } + + /** + * Returns the vertical size of the current base sprite of the being. + */ + virtual int getHeight() const override A_WARN_UNUSED + { return std::max(CompoundSprite::getHeight(), DEFAULT_BEING_HEIGHT); } + + /** + * Returns the being's pixel radius used to detect collisions. + */ + virtual int getCollisionRadius() const A_WARN_UNUSED + { return 16; } + + /** + * Shoots a missile particle from this being, to target being + */ + void fireMissile(Being *const target, + const std::string &particle) const; + + /** + * Returns the path this being is following. An empty path is returned + * when this being isn't following any path currently. + */ + const Path &getPath() const A_WARN_UNUSED + { return mPath; } + + int getDistance() const A_WARN_UNUSED + { return mDistance; } + + void setDistance(const int n) + { mDistance = n; } + + /** + * Set the Emoticon type and time displayed above + * the being. + */ + void setEmote(const uint8_t emotion, const int emote_time); + + void setState(const uint8_t state); + + virtual void drawSprites(Graphics *const graphics, + int posX, int posY) const override; + + virtual void drawSpritesSDL(Graphics *const graphics, + int posX, int posY) const override; + + void drawHpBar(Graphics *const graphics, const int x, const int y, + const int maxHP, const int hp, const int damage, + const int color1, const int color2, const int width, + const int height) const; + + static void load(); + + virtual void optionChanged(const std::string &value) override; + + void flashName(const int time); + + int getDamageTaken() const A_WARN_UNUSED + { return mDamageTaken; } + + void setDamageTaken(const int damage) + { mDamageTaken = damage; } + + void updateName(); + + void setLevel(const int n) + { mLevel = n; } + + virtual int getLevel() const A_WARN_UNUSED + { return mLevel; } + + void setIsReachable(const int n) + { mIsReachable = n; } + + int isReachable() const A_WARN_UNUSED + { return mIsReachable; } + + static void reReadConfig(); + + static BeingCacheEntry* getCacheEntry(const int id) A_WARN_UNUSED; + + void addToCache() const; + + bool updateFromCache(); + + /** + * Sets the gender of this being. + */ + virtual void setGender(const Gender gender); + + Gender getGender() const A_WARN_UNUSED + { return mGender; } + + /** + * Return sprite sit action for current environment. + */ + std::string getSitAction() const A_WARN_UNUSED; + + std::string getMoveAction() const A_WARN_UNUSED; + + std::string getDeadAction() const A_WARN_UNUSED; + + std::string getStandAction() const A_WARN_UNUSED; + + std::string getSpawnAction() const A_WARN_UNUSED; + + std::string getWeaponAttackAction(const ItemInfo *const weapon) const; + + std::string getAttackAction(const Attack *const attack) const; + + /** + * Whether or not this player is a GM. + */ + bool isGM() const A_WARN_UNUSED + { return mIsGM; } + + /** + * Triggers whether or not to show the name as a GM name. + */ + void setGM(const bool gm); + + bool canTalk() const A_WARN_UNUSED + { return mType == NPC; } + + void talkTo() const; + + bool draw(Graphics *const graphics, + const int offsetX, const int offsetY) const override; + + bool drawSpriteAt(Graphics *const graphics, + const int x, const int y) const; + + void setMoveTime() + { mMoveTime = cur_time; } + + void setAttackTime() + { mAttackTime = cur_time; } + + void setTalkTime() + { mTalkTime = cur_time; } + + void setTestTime() + { mTestTime = cur_time; } + + void setOtherTime() + { mOtherTime = cur_time; } + + unsigned int getMoveTime() const + { return mMoveTime; } + + unsigned int getAttackTime() const + { return mAttackTime; } + + unsigned int getTalkTime() const + { return mTalkTime; } + + unsigned int getTestTime() const + { return mTestTime; } + + unsigned int getOtherTime() const + { return mOtherTime; } + + void resetCounters(); + + virtual void updateColors(); + + void setEnemy(const bool n) + { mEnemy = n; } + + const std::string &getIp() const A_WARN_UNUSED + { return mIp; } + + void setIp(std::string ip) + { mIp = ip; } + + unsigned int getPvpRank() const A_WARN_UNUSED + { return mPvpRank; } + + void setPvpRank(const unsigned int rank) + { mPvpRank = rank; } + + void setHP(const int n); + + void setMaxHP(const int hp); + + int getHP() const A_WARN_UNUSED + { return mHP; } + + uint8_t calcDirection(const int dstX, + const int dstY) const A_WARN_UNUSED; + + uint8_t calcDirection() const A_WARN_UNUSED; + + void setAttackDelay(const int n) + { mAttackDelay = n; } + + int getAttackDelay() const A_WARN_UNUSED + { return mAttackDelay; } + + int getMinHit() const A_WARN_UNUSED + { return mMinHit; } + + void setMinHit(const int n) + { mMinHit = n; } + + int getMaxHit() const A_WARN_UNUSED + { return mMaxHit; } + + void setMaxHit(const int n) + { mMaxHit = n; } + + int getCriticalHit() const A_WARN_UNUSED + { return mCriticalHit; } + + void setCriticalHit(const int n) + { mCriticalHit = n; } + + void updateHit(const int amount); + + Equipment *getEquipment() A_WARN_UNUSED; + + void undressItemById(const int id); + + int getGoodStatus() const A_WARN_UNUSED + { return mGoodStatus; } + + void setGoodStatus(const int n) + { mGoodStatus = n; } + + std::string getGenderSign() const A_WARN_UNUSED; + + std::string getGenderSignWithSpace() const A_WARN_UNUSED; + + void updateComment(); + + const std::string getComment() const A_WARN_UNUSED + { return mComment; } + + void setComment(std::string n) + { mComment = n; } + + static void clearCache(); + + static std::string loadComment(const std::string &name, + const int type) A_WARN_UNUSED; + + static void saveComment(const std::string &name, + const std::string &comment, const int type); + + bool isAdvanced() const A_WARN_UNUSED + { return mAdvanced; } + + void setAdvanced(const bool n) + { mAdvanced = n; addToCache(); } + + bool isShopEnabled() const A_WARN_UNUSED + { return mShop; } + + void enableShop(const bool b) + { mShop = b; } + + /** + * Sets the attack range. + */ + void setAttackRange(const int range) + { mAttackRange = range; } + + void attack(Being *target = nullptr, bool keep = false, + bool dontChangeEquipment = false); + + void attack2(Being *target = nullptr, bool keep = false, + bool dontChangeEquipment = false); + + void updatePercentHP(); + + void setRaceName(std::string name) + { mRaceName = name; } + + std::string getRaceName() const A_WARN_UNUSED + { return mRaceName; } + + int getSpriteID(const int slot) const A_WARN_UNUSED; + + void setHairStyle(const unsigned int slot, const int id); + + void setHairColor(const unsigned int slot, + const unsigned char color); + + void setHairColor(const unsigned char color) + { mHairColor = color; } + + unsigned char getHairColor() const A_WARN_UNUSED + { return mHairColor; } + + void recalcSpritesOrder(); + + int getHitEffect(const Being *const attacker, + const AttackType type, + const int attackId) const A_WARN_UNUSED; + + Cursor::Cursor getHoverCursor() const A_WARN_UNUSED + { return mInfo ? mInfo->getHoverCursor() : Cursor::CURSOR_POINTER; } + + void addAfkEffect(); + + void removeAfkEffect(); + + void updateAwayEffect(); + + void addSpecialEffect(const int effect); + + void removeSpecialEffect(); + + void addEffect(const std::string &name); + + void addPet(const int id); + + void removePet(); + + void updatePets(); + + Being *getPet() + { return mPet; } + + void setPet(Being *const pet) + { mPet = pet; } + + void setOwner(Being *const owner) + { mOwner = owner; } + + void playSfx(const SoundInfo &sound, Being *const being, + const bool main, const int x, const int y); + + int getLook() + { return mLook; } + + void setLook(const int look); + + static uint8_t genderToInt(const Gender sex) A_WARN_UNUSED; + + static Gender intToGender(uint8_t sex) A_WARN_UNUSED; + + NextSoundInfo mNextSound; + + protected: + /** + * Sets the new path for this being. + */ + void setPath(const Path &path); + + /** + * Updates name's location. + */ + virtual void updateCoords(); + + void showName(); + + static int getDefaultEffectId(const int type); + + BeingInfo *mInfo; + AnimatedSprite *mEmotionSprite; + AnimatedSprite* mAnimationEffect; + + std::string mSpriteAction; + std::string mName; /**< Name of character */ + std::string mRaceName; + std::string mPartyName; + std::string mGuildName; + std::string mSpeech; + + /** + * Holds a text object when the being displays it's name, 0 otherwise + */ + FlashText *mDispName; + const gcn::Color *mNameColor; + + /** Engine-related infos about weapon. */ + const ItemInfo *mEquippedWeapon; + + static int mNumberOfHairstyles; /** Number of hair styles in use */ + static int mNumberOfRaces; /** Number of races in use */ + + Path mPath; + Text *mText; + const gcn::Color *mTextColor; + + Vector mDest; /**< destination coordinates. */ + + StringVect mSpriteColors; + std::vector mSpriteIDs; + std::vector mSpriteColorsIds; + + // Character guild information + std::map mGuilds; + Party *mParty; + + int mActionTime; /**< Time spent in current action */ + int mEmotionTime; /**< Time until emotion disappears */ + + /** Time until the last speech sentence disappears */ + int mSpeechTime; + int mAttackSpeed; /**< Attack speed */ + + int mLevel; + int mAttackRange; + Gender mGender; + Action mAction; /**< Action the being is performing */ + uint16_t mSubType; /**< Subtype (graphical view, basically) */ + uint8_t mDirection; /**< Facing direction */ + uint8_t mDirectionDelayed; /**< Facing direction */ + uint8_t mSpriteDirection; /**< Facing direction */ + bool mShowName; + bool mIsGM; + + private: + /** + * Calculates the offset in the given directions. + * If walking in direction 'neg' the value is negated. + * TODO: Used by eAthena only? + */ + int getOffset(const signed char pos, + const signed char neg) const A_WARN_UNUSED; + + int searchSlotValue(const std::vector &slotRemap, + const int val) const A_WARN_UNUSED; + + void searchSlotValueItr(std::vector::iterator &it, int &idx, + std::vector &slotRemap, + const int val) const; + + void dumpSprites() const; + + const Type mType; + + /** Speech Bubble components */ + SpeechBubble *mSpeechBubble; + + /** + * Walk speed for x and y movement values. + * In pixels per second for eAthena, + * In pixels per ticks for Manaserv. + * @see MILLISECONDS_IN_A_TICK + */ + Vector mWalkSpeed; + std::string mIp; + int *mSpriteRemap; + int *mSpriteHide; + std::string mComment; + Being *mPet; + Being *mOwner; + Particle *mSpecialParticle; + + int mX, mY; /**< Position in tile */ + + int mDamageTaken; + int mHP; + int mMaxHP; + int mDistance; + int mIsReachable; /**< 0 - unknown, 1 - reachable, 2 - not reachable*/ + int mGoodStatus; + + static int mUpdateConfigTime; + static unsigned int mConfLineLim; + static int mSpeechType; + static bool mHighlightMapPortals; + static bool mHighlightMonsterAttackRange; + static bool mLowTraffic; + static bool mDrawHotKeys; + static bool mShowBattleEvents; + static bool mShowMobHP; + static bool mShowOwnHP; + static bool mShowGender; + static bool mShowLevel; + static bool mShowPlayersStatus; + static bool mEnableReorderSprites; + static bool mHideErased; + static bool mMoveNames; + static int mAwayEffect; + + unsigned int mMoveTime; + unsigned int mAttackTime; + unsigned int mTalkTime; + unsigned int mOtherTime; + unsigned int mTestTime; + int mAttackDelay; + int mMinHit; + int mMaxHit; + int mCriticalHit; + unsigned int mPvpRank; + unsigned int mNumber; + int mPetId; + int mLook; + unsigned char mHairColor; + bool mErased; + bool mEnemy; + bool mGotComment; + bool mAdvanced; + bool mShop; + bool mAway; + bool mInactive; +}; + +extern std::list beingInfoCache; + +#endif // BEING_BEING_H diff --git a/src/being/beingcacheentry.h b/src/being/beingcacheentry.h new file mode 100644 index 000000000..2890d44a6 --- /dev/null +++ b/src/being/beingcacheentry.h @@ -0,0 +1,128 @@ +/* + * The ManaPlus Client + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_BEINGCACHEENTRY_H +#define BEING_BEINGCACHEENTRY_H + +#include "localconsts.h" + +#include + +class BeingCacheEntry final +{ + public: + explicit BeingCacheEntry(const int id): + mName(), + mPartyName(), + mGuildName(), + mIp(), + mId(id), + mLevel(0), + mPvpRank(0), + mTime(0), + mFlags(0), + mIsAdvanced(false) + { + } + + A_DELETE_COPY(BeingCacheEntry) + + int getId() const + { return mId; } + + /** + * Returns the name of the being. + */ + const std::string &getName() const + { return mName; } + + /** + * Sets the name for the being. + * + * @param name The name that should appear. + */ + void setName(const std::string &name) + { mName = name; } + + /** + * Following are set from the server (mainly for players) + */ + void setPartyName(const std::string &name) + { mPartyName = name; } + + void setGuildName(const std::string &name) + { mGuildName = name; } + + const std::string &getPartyName() const + { return mPartyName; } + + const std::string &getGuildName() const + { return mGuildName; } + + void setLevel(const int n) + { mLevel = n; } + + int getLevel() const + { return mLevel; } + + void setTime(const int n) + { mTime = n; } + + int getTime() const + { return mTime; } + + unsigned getPvpRank() const + { return mPvpRank; } + + void setPvpRank(const int r) + { mPvpRank = r; } + + std::string getIp() const + { return mIp; } + + void setIp(std::string ip) + { mIp = ip; } + + bool isAdvanced() const + { return mIsAdvanced; } + + void setAdvanced(const bool a) + { mIsAdvanced = a; } + + int getFlags() const + { return mFlags; } + + void setFlags(const int flags) + { mFlags = flags; } + + protected: + std::string mName; /**< Name of character */ + std::string mPartyName; + std::string mGuildName; + std::string mIp; + int mId; /**< Unique sprite id */ + int mLevel; + unsigned int mPvpRank; + int mTime; + int mFlags; + bool mIsAdvanced; +}; + +#endif // BEING_BEINGCACHEENTRY_H diff --git a/src/being/compoundsprite.cpp b/src/being/compoundsprite.cpp new file mode 100644 index 000000000..68a0d42e3 --- /dev/null +++ b/src/being/compoundsprite.cpp @@ -0,0 +1,567 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/compoundsprite.h" + +#include "client.h" +#include "configuration.h" +#include "game.h" + +#ifdef USE_OPENGL +#include "main.h" +#endif + +#include "map.h" +#include "sdlshared.h" + +#include "render/surfacegraphics.h" + +#include "resources/image.h" +#include "resources/imagehelper.h" + +#include "utils/dtor.h" +#include "utils/sdlcheckutils.h" + +#include + +#include "debug.h" + +static const int BUFFER_WIDTH = 100; +static const int BUFFER_HEIGHT = 100; + +static const unsigned cache_max_size = 10; +static const unsigned cache_clean_part = 3; +bool CompoundSprite::mEnableDelay = true; + +CompoundSprite::CompoundSprite() : + imagesCache(), + mCacheItem(nullptr), + mImage(nullptr), + mAlphaImage(nullptr), + mOffsetX(0), + mOffsetY(0), + mSprites(), + mNextRedrawTime(0), + mNeedsRedraw(false), + mEnableAlphaFix(config.getBoolValue("enableAlphaFix")), + mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")), + mDisableBeingCaching(config.getBoolValue("disableBeingCaching")) +{ + mAlpha = 1.0f; +} + +CompoundSprite::~CompoundSprite() +{ + clear(); + mImage = nullptr; + mAlphaImage = nullptr; +} + +bool CompoundSprite::reset() +{ + bool ret = false; + FOR_EACH (SpriteIterator, it, mSprites) + { + if (*it) + ret |= (*it)->reset(); + } + mNeedsRedraw |= ret; + return ret; +} + +bool CompoundSprite::play(const std::string &action) +{ + bool ret = false; + FOR_EACH (SpriteIterator, it, mSprites) + { + if (*it) + ret |= (*it)->play(action); + } + mNeedsRedraw |= ret; + return ret; +} + +bool CompoundSprite::update(const int time) +{ + bool ret = false; + FOR_EACH (SpriteIterator, it, mSprites) + { + if (*it) + ret |= (*it)->update(time); + } + mNeedsRedraw |= ret; + return ret; +} + +bool CompoundSprite::draw(Graphics *const graphics, + const int posX, const int posY) const +{ + FUNC_BLOCK("CompoundSprite::draw", 1) + if (mNeedsRedraw) + updateImages(); + + if (mSprites.empty()) // Nothing to draw + return false; + + if (mAlpha == 1.0f && mImage) + { + return graphics->drawImage(mImage, posX + mOffsetX, posY + mOffsetY); + } + else if (mAlpha && mAlphaImage) + { + mAlphaImage->setAlpha(mAlpha); + return graphics->drawImage(mAlphaImage, + posX + mOffsetX, posY + mOffsetY); + } + else + { + drawSprites(graphics, posX, posY); + } + return false; +} + +void CompoundSprite::drawSprites(Graphics *const graphics, + const int posX, const int posY) const +{ + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + { + (*it)->setAlpha(mAlpha); + (*it)->draw(graphics, posX, posY); + } + } +} + +void CompoundSprite::drawSpritesSDL(Graphics *const graphics, + const int posX, const int posY) const +{ + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + (*it)->draw(graphics, posX, posY); + } +} + +int CompoundSprite::getWidth() const +{ + FOR_EACH (SpriteConstIterator, it, mSprites) + { + const Sprite *const base = *it; + if (base) + return base->getWidth(); + } + + return 0; +} + +int CompoundSprite::getHeight() const +{ + FOR_EACH (SpriteConstIterator, it, mSprites) + { + const Sprite *const base = *it; + if (base) + return base->getHeight(); + } + + return 0; +} + +const Image *CompoundSprite::getImage() const +{ + return mImage; +} + +bool CompoundSprite::setSpriteDirection(const SpriteDirection direction) +{ + bool ret = false; + FOR_EACH (SpriteIterator, it, mSprites) + { + if (*it) + ret |= (*it)->setSpriteDirection(direction); + } + mNeedsRedraw |= ret; + return ret; +} + +int CompoundSprite::getNumberOfLayers() const +{ + if (mImage || mAlphaImage) + return 1; + else + return static_cast(size()); +} + +unsigned int CompoundSprite::getCurrentFrame() const +{ + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + return (*it)->getCurrentFrame(); + } + return 0; +} + +unsigned int CompoundSprite::getFrameCount() const +{ + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + return (*it)->getFrameCount(); + } + return 0; +} + +void CompoundSprite::addSprite(Sprite *const sprite) +{ + mSprites.push_back(sprite); + mNeedsRedraw = true; +} + +void CompoundSprite::setSprite(const int layer, Sprite *const sprite) +{ + // Skip if it won't change anything + if (mSprites.at(layer) == sprite) + return; + + delete mSprites.at(layer); + mSprites[layer] = sprite; + mNeedsRedraw = true; +} + +void CompoundSprite::removeSprite(const int layer) +{ + // Skip if it won't change anything + if (!mSprites.at(layer)) + return; + + delete mSprites.at(layer); + mSprites.at(layer) = nullptr; + mNeedsRedraw = true; +} + +void CompoundSprite::clear() +{ + // Skip if it won't change anything + if (!mSprites.empty()) + { + delete_all(mSprites); + mSprites.clear(); + } + mNeedsRedraw = true; + delete_all(imagesCache); + imagesCache.clear(); + delete mCacheItem; + mCacheItem = nullptr; +} + +void CompoundSprite::ensureSize(size_t layerCount) +{ + // Skip if it won't change anything + if (mSprites.size() >= layerCount) + return; + +// resize(layerCount, nullptr); + mSprites.resize(layerCount); +} + +/** + * Returns the curent frame in the current animation of the given layer. + */ +unsigned int CompoundSprite::getCurrentFrame(unsigned int layer) const +{ + if (layer >= mSprites.size()) + return 0; + + const Sprite *const s = getSprite(layer); + if (s) + return s->getCurrentFrame(); + + return 0; +} + +/** + * Returns the frame count in the current animation of the given layer. + */ +unsigned int CompoundSprite::getFrameCount(unsigned int layer) +{ + if (layer >= mSprites.size()) + return 0; + + const Sprite *const s = getSprite(layer); + if (s) + return s->getFrameCount(); + + return 0; +} + +void CompoundSprite::redraw() const +{ +#ifndef USE_SDL2 + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int rmask = 0xff000000; + const int gmask = 0x00ff0000; + const int bmask = 0x0000ff00; + const int amask = 0x000000ff; +#else + const int rmask = 0x000000ff; + const int gmask = 0x0000ff00; + const int bmask = 0x00ff0000; + const int amask = 0xff000000; +#endif + + SDL_Surface *const surface = MSDL_CreateRGBSurface(SDL_HWSURFACE, + BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask); + + if (!surface) + return; + + SurfaceGraphics *graphics = new SurfaceGraphics(); + graphics->setBlitMode(SurfaceGraphics::BLIT_GFX); + graphics->setTarget(surface); + graphics->_beginDraw(); + + int tileX = 32 / 2; + int tileY = 32; + + const Game *const game = Game::instance(); + if (game) + { + const Map *const map = game->getCurrentMap(); + if (map) + { + tileX = map->getTileWidth() / 2; + tileY = map->getTileWidth(); + } + } + + const int posX = BUFFER_WIDTH / 2 - tileX; + const int posY = BUFFER_HEIGHT - tileY; + + mOffsetX = tileX - BUFFER_WIDTH / 2; + mOffsetY = tileY - BUFFER_HEIGHT; + + drawSpritesSDL(graphics, posX, posY); + + delete graphics; + graphics = nullptr; + + SDL_Surface *const surfaceA = MSDL_CreateRGBSurface(SDL_HWSURFACE, + BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask); + +#ifdef USE_SDL2 + SDL_SetSurfaceAlphaMod(surface, 255); +#else + SDL_SetAlpha(surface, 0, SDL_ALPHA_OPAQUE); +#endif + SDL_BlitSurface(surface, nullptr, surfaceA, nullptr); + + delete mImage; + delete mAlphaImage; + + mImage = imageHelper->load(surface); + MSDL_FreeSurface(surface); + + if (ImageHelper::mEnableAlpha) + { + mAlphaImage = imageHelper->load(surfaceA); + MSDL_FreeSurface(surfaceA); + } + else + { + mAlphaImage = nullptr; + } +#endif +} + +void CompoundSprite::setAlpha(float alpha) +{ + if (alpha != mAlpha) + { +#ifdef USE_OPENGL + if (mEnableAlphaFix && imageHelper->useOpenGL() == 0 + && size() > 3) +#else + if (mEnableAlphaFix && size() > 3) +#endif + { + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + (*it)->setAlpha(alpha); + } + } + mAlpha = alpha; + } +} + +void CompoundSprite::updateImages() const +{ +#ifndef USE_SDL2 +#ifdef USE_OPENGL + if (imageHelper->useOpenGL()) + return; +#endif + + if (mEnableDelay) + { + if (get_elapsed_time1(mNextRedrawTime) < 10) + return; + mNextRedrawTime = tick_time; + } + mNeedsRedraw = false; + + if (!mDisableBeingCaching) + { + if (size() <= 3) + return; + + if (!mDisableAdvBeingCaching) + { + if (updateFromCache()) + return; + + redraw(); + + if (mImage) + initCurrentCacheItem(); + } + else + { + redraw(); + } + } +#endif +} + +bool CompoundSprite::updateFromCache() const +{ +#ifndef USE_SDL2 +// static int hits = 0; +// static int miss = 0; + + if (mCacheItem && mCacheItem->image) + { + imagesCache.push_front(mCacheItem); + mCacheItem = nullptr; + if (imagesCache.size() > cache_max_size) + { + for (unsigned f = 0; f < cache_clean_part; f ++) + { + CompoundItem *item = imagesCache.back(); + imagesCache.pop_back(); + delete item; + } + } + } + +// logger->log("cache size: %d, hit %d, miss %d", +// (int)imagesCache.size(), hits, miss); + + const size_t sz = size(); + FOR_EACH (ImagesCache::iterator, it, imagesCache) + { + CompoundItem *const ic = *it; + if (ic && ic->data.size() == sz) + { + bool fail(false); + VectorPointers::const_iterator it2 = ic->data.begin(); + const VectorPointers::const_iterator it2_end = ic->data.end(); + + for (SpriteConstIterator it1 = mSprites.begin(), + it1_end = mSprites.end(); + it1 != it1_end && it2 != it2_end; + ++ it1, ++ it2) + { + const void *ptr1 = nullptr; + const void *ptr2 = nullptr; + if (*it1) + ptr1 = (*it1)->getHash(); + if (*it2) + ptr2 = *it2; + if (ptr1 != ptr2) + { + fail = true; + break; + } + } + if (!fail) + { +// hits ++; + mImage = (*it)->image; + mAlphaImage = (*it)->alphaImage; + imagesCache.erase(it); + mCacheItem = ic; + return true; + } + } + } + mImage = nullptr; + mAlphaImage = nullptr; +// miss++; +#endif + return false; +} + +void CompoundSprite::initCurrentCacheItem() const +{ + delete mCacheItem; + mCacheItem = new CompoundItem(); + mCacheItem->image = mImage; + mCacheItem->alphaImage = mAlphaImage; +// mCacheItem->alpha = mAlpha; + + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + mCacheItem->data.push_back((*it)->getHash()); + else + mCacheItem->data.push_back(nullptr); + } +} + +bool CompoundSprite::updateNumber(unsigned num) +{ + bool res(false); + FOR_EACH (SpriteConstIterator, it, mSprites) + { + if (*it) + { + if ((*it)->updateNumber(num)) + res = true; + } + } + return res; +} + +CompoundItem::CompoundItem() : + data(), + image(nullptr), + alphaImage(nullptr) +{ +} + +CompoundItem::~CompoundItem() +{ + delete image; + delete alphaImage; +} diff --git a/src/being/compoundsprite.h b/src/being/compoundsprite.h new file mode 100644 index 000000000..5024f0122 --- /dev/null +++ b/src/being/compoundsprite.h @@ -0,0 +1,161 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_COMPOUNDSPRITE_H +#define BEING_COMPOUNDSPRITE_H + +#include "sprite.h" + +#include +#include + +#include "localconsts.h" + +class Image; + +typedef std::list VectorPointers; + +class CompoundItem final +{ + public: + CompoundItem(); + + A_DELETE_COPY(CompoundItem) + + ~CompoundItem(); + + VectorPointers data; + Image *image; + Image *alphaImage; +}; + +class CompoundSprite : public Sprite +{ +public: + typedef std::vector::iterator SpriteIterator; + typedef std::vector::const_iterator SpriteConstIterator; + + CompoundSprite(); + + A_DELETE_COPY(CompoundSprite) + + ~CompoundSprite(); + + virtual bool reset() override; + + virtual bool play(const std::string &action) override; + + virtual bool update(const int time) override; + + virtual bool draw(Graphics *const graphics, + const int posX, const int posY) const override; + + /** + * Gets the width in pixels of the first sprite in the list. + */ + virtual int getWidth() const override A_WARN_UNUSED; + + /** + * Gets the height in pixels of the first sprite in the list. + */ + virtual int getHeight() const override A_WARN_UNUSED; + + virtual const Image *getImage() const override A_WARN_UNUSED; + + virtual bool setSpriteDirection(const SpriteDirection direction) override; + + int getNumberOfLayers() const A_WARN_UNUSED; + + unsigned int getCurrentFrame() const override A_WARN_UNUSED; + + unsigned int getFrameCount() const override A_WARN_UNUSED; + + size_t size() const A_WARN_UNUSED + { return mSprites.size(); } + + bool empty() const A_WARN_UNUSED + { return mSprites.empty(); } + + void addSprite(Sprite *const sprite); + + void setSprite(const int layer, Sprite *const sprite); + + Sprite *getSprite(int layer) const A_WARN_UNUSED + { return mSprites.at(layer); } + + void removeSprite(const int layer); + + void clear(); + + void ensureSize(size_t layerCount); + + virtual void drawSprites(Graphics *const graphics, + int posX, int posY) const; + + virtual void drawSpritesSDL(Graphics *const graphics, + int posX, int posY) const; + + /** + * Returns the curent frame in the current animation of the given layer. + */ + virtual unsigned int getCurrentFrame(unsigned int layer) + const A_WARN_UNUSED; + + /** + * Returns the frame count in the current animation of the given layer. + */ + virtual unsigned int getFrameCount(unsigned int layer) A_WARN_UNUSED; + + virtual void setAlpha(float alpha) override; + + bool updateNumber(const unsigned num) override; + + static void setEnableDelay(bool b) + { mEnableDelay = b; } + +private: + void redraw() const; + + void updateImages() const; + + bool updateFromCache() const; + + void initCurrentCacheItem() const; + + typedef std::list ImagesCache; + mutable ImagesCache imagesCache; + mutable CompoundItem *mCacheItem; + + mutable Image *mImage; + mutable Image *mAlphaImage; + + mutable int mOffsetX; + mutable int mOffsetY; + std::vector mSprites; + mutable int mNextRedrawTime; + static bool mEnableDelay; + mutable bool mNeedsRedraw; + bool mEnableAlphaFix; + bool mDisableAdvBeingCaching; + bool mDisableBeingCaching; +}; + +#endif // BEING_COMPOUNDSPRITE_H diff --git a/src/being/localplayer.cpp b/src/being/localplayer.cpp new file mode 100644 index 000000000..6a0aa1b19 --- /dev/null +++ b/src/being/localplayer.cpp @@ -0,0 +1,4355 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/localplayer.h" + +#include "actorspritemanager.h" +#include "client.h" +#include "configuration.h" +#include "dropshortcut.h" +#include "effectmanager.h" +#include "guild.h" +#include "item.h" +#include "maplayer.h" +#include "party.h" +#include "simpleanimation.h" +#include "soundconsts.h" +#include "soundmanager.h" +#include "statuseffect.h" +#include "walklayer.h" + +#include "being/playerinfo.h" +#include "being/playerrelations.h" + +#include "particle/particle.h" + +#include "input/keyboardconfig.h" + +#include "gui/chatwindow.h" +#include "gui/gui.h" +#include "gui/ministatuswindow.h" +#include "gui/okdialog.h" +#include "gui/outfitwindow.h" +#include "gui/shopwindow.h" +#include "gui/sdlfont.h" +#include "gui/skilldialog.h" +#include "gui/socialwindow.h" +#include "gui/updaterwindow.h" +#include "gui/viewport.h" + +#include "gui/widgets/gmtab.h" +#include "gui/widgets/whispertab.h" + +#include "render/graphics.h" + +#include "net/beinghandler.h" +#include "net/chathandler.h" +#include "net/guildhandler.h" +#include "net/inventoryhandler.h" +#include "net/net.h" +#include "net/partyhandler.h" +#include "net/playerhandler.h" +#include "net/skillhandler.h" +#include "net/tradehandler.h" + +#include "resources/imageset.h" +#include "resources/iteminfo.h" +#include "resources/resourcemanager.h" + +#include "utils/gettext.h" + +#include "mumblemanager.h" + +#include + +#include "debug.h" + +static const int16_t awayLimitTimer = 60; +static const int MAX_TICK_VALUE = INT_MAX / 2; + +typedef std::map::const_iterator GuildMapCIter; + +LocalPlayer *player_node = nullptr; + +extern std::list beingInfoCache; +extern OkDialog *weightNotice; +extern int weightNoticeTime; +extern MiniStatusWindow *miniStatusWindow; +extern SkillDialog *skillDialog; + +LocalPlayer::LocalPlayer(const int id, const int subtype) : + Being(id, PLAYER, subtype, nullptr), + mGMLevel(0), + mInvertDirection(0), + mCrazyMoveType(config.getIntValue("crazyMoveType")), + mCrazyMoveState(0), + mAttackWeaponType(config.getIntValue("attackWeaponType")), + mQuickDropCounter(config.getIntValue("quickDropCounter")), + mMoveState(0), + mPickUpType(config.getIntValue("pickUpType")), + mMagicAttackType(config.getIntValue("magicAttackType")), + mPvpAttackType(config.getIntValue("pvpAttackType")), + mMoveToTargetType(config.getIntValue("moveToTargetType")), + mAttackType(config.getIntValue("attackType")), + mFollowMode(config.getIntValue("followMode")), + mImitationMode(config.getIntValue("imitationMode")), + mLastTargetX(0), + mLastTargetY(0), + mHomes(), + mTarget(nullptr), + mPlayerFollowed(), + mPlayerImitated(), + mNextDestX(0), + mNextDestY(0), + mPickUpTarget(nullptr), + mLastAction(-1), + mStatusEffectIcons(), + mLocalWalkTime(-1), + mMessages(), + mMessageTime(0), + mAwayListener(new AwayListener), + mAwayDialog(nullptr), + mPingSendTick(0), + mPingTime(0), + mAfkTime(0), + mActivityTime(0), + mNavigateX(0), + mNavigateY(0), + mNavigateId(0), + mCrossX(0), + mCrossY(0), + mOldX(0), + mOldY(0), + mOldTileX(0), + mOldTileY(0), + mNavigatePath(), + mLastHitFrom(), + mWaitFor(), + mAdvertTime(0), + mTestParticle(nullptr), + mTestParticleName(), + mTestParticleTime(0), + mTestParticleHash(0l), + mWalkingDir(0), + mUpdateName(true), + mBlockAdvert(false), + mTargetDeadPlayers(config.getBoolValue("targetDeadPlayers")), + mServerAttack(config.getBoolValue("serverAttack")), + mEnableAdvert(config.getBoolValue("enableAdvert")), + mTradebot(config.getBoolValue("tradebot")), + mTargetOnlyReachable(config.getBoolValue("targetOnlyReachable")), + mDisableGameModifiers(config.getBoolValue("disableGameModifiers")), + mIsServerBuggy(serverConfig.getValueBool("enableBuggyServers", true)), + mSyncPlayerMove(config.getBoolValue("syncPlayerMove")), + mDrawPath(config.getBoolValue("drawPath")), + mAttackMoving(config.getBoolValue("attackMoving")), + mAttackNext(config.getBoolValue("attackNext")), + mShowJobExp(config.getBoolValue("showJobExp")), + mNextStep(false), + mDisableCrazyMove(false), + mGoingToTarget(false), + mKeepAttacking(false), + mPathSetByMouse(false), + mWaitPing(false), + mAwayMode(false), + mPseudoAwayMode(false), + mShowNavigePath(false) +{ + logger->log1("LocalPlayer::LocalPlayer"); + + listen(CHANNEL_ATTRIBUTES); + + mAttackRange = 0; + mLevel = 1; + mAdvanced = true; + mTextColor = &Theme::getThemeColor(Theme::PLAYER); + if (userPalette) + mNameColor = &userPalette->getColor(UserPalette::SELF); + else + mNameColor = nullptr; + + PlayerInfo::setStatBase(PlayerInfo::WALK_SPEED, + static_cast(getWalkSpeed().x)); + PlayerInfo::setStatMod(PlayerInfo::WALK_SPEED, 0); + + loadHomes(); + + config.addListener("showownname", this); + config.addListener("targetDeadPlayers", this); + serverConfig.addListener("enableBuggyServers", this); + config.addListener("syncPlayerMove", this); + config.addListener("drawPath", this); + config.addListener("serverAttack", this); + config.addListener("attackMoving", this); + config.addListener("attackNext", this); + config.addListener("showJobExp", this); + config.addListener("enableAdvert", this); + config.addListener("tradebot", this); + config.addListener("targetOnlyReachable", this); + setShowName(config.getBoolValue("showownname")); +} + +LocalPlayer::~LocalPlayer() +{ + logger->log1("LocalPlayer::~LocalPlayer"); + + config.removeListeners(this); + serverConfig.removeListener("enableBuggyServers", this); + + if (mAwayDialog) + { + soundManager.volumeRestore(); + delete mAwayDialog; + mAwayDialog = nullptr; + } + delete mAwayListener; + mAwayListener = nullptr; +} + +void LocalPlayer::logic() +{ + BLOCK_START("LocalPlayer::logic") +#ifdef USE_MUMBLE + if (mumbleManager) + mumbleManager->setPos(mX, mY, mDirection); +#endif + + // Actions are allowed once per second + if (get_elapsed_time(mLastAction) >= 1000) + mLastAction = -1; + + if (mActivityTime == 0 || mLastAction != -1) + mActivityTime = cur_time; + + if ((mAction != MOVE || mNextStep) && !mNavigatePath.empty()) + { + mNextStep = false; + int dist = 5; + if (!mSyncPlayerMove) + dist = 20; + + if ((mNavigateX || mNavigateY) && + ((mCrossX + dist >= mX && mCrossX <= mX + dist + && mCrossY + dist >= mY && mCrossY <= mY + dist) + || (!mCrossX && !mCrossY))) + { + const Path::const_iterator i = mNavigatePath.begin(); + if ((*i).x == mX && (*i).y == mY) + mNavigatePath.pop_front(); + else + moveTo((*i).x, (*i).y); + } + } + + // Show XP messages + if (!mMessages.empty()) + { + if (mMessageTime == 0) + { + MessagePair info = mMessages.front(); + + if (particleEngine) + { + particleEngine->addTextRiseFadeOutEffect( + info.first, + getPixelX(), + getPixelY() - 48, + &userPalette->getColor(info.second), + gui->getInfoParticleFont(), true); + } + + mMessages.pop_front(); + mMessageTime = 30; + } + mMessageTime--; + } + +#ifdef MANASERV_SUPPORT + PlayerInfo::logic(); +#endif + + if (mTarget) + { + if (mTarget->getType() == ActorSprite::NPC) + { + // NPCs are always in range + mTarget->setTargetType(TCT_IN_RANGE); + } + else + { + // Find whether target is in range +#ifdef MANASERV_SUPPORT + const int rangeX = + (Net::getNetworkType() == ServerInfo::MANASERV) ? + static_cast(abs(static_cast(mTarget->getPosition().x + - getPosition().x))) : + static_cast(abs(mTarget->getTileX() - getTileX())); + const int rangeY = + (Net::getNetworkType() == ServerInfo::MANASERV) ? + static_cast(abs(static_cast(mTarget->getPosition().y + - getPosition().y))) : + static_cast(abs(mTarget->getTileY() - getTileY())); +#else + const int rangeX = static_cast( + abs(mTarget->getTileX() - getTileX())); + const int rangeY = static_cast( + abs(mTarget->getTileY() - getTileY())); +#endif + const int attackRange = getAttackRange(); + const TargetCursorType targetType = rangeX > attackRange || + rangeY > attackRange ? + TCT_NORMAL : TCT_IN_RANGE; + mTarget->setTargetType(targetType); + + if (!mTarget->isAlive() && (!mTargetDeadPlayers + || mTarget->getType() != Being::PLAYER)) + { + stopAttack(true); + } + + if (mKeepAttacking && mTarget) + attack(mTarget, true); + } + } + + Being::logic(); + BLOCK_END("LocalPlayer::logic") +} + +void LocalPlayer::slowLogic() +{ + BLOCK_START("LocalPlayer::slowLogic") + const int time = cur_time; + if (weightNotice && weightNoticeTime < time) + { + weightNotice->scheduleDelete(); + weightNotice = nullptr; + weightNoticeTime = 0; + } + + if (serverVersion < 4 && mEnableAdvert && !mBlockAdvert + && mAdvertTime < cur_time) + { + uint8_t smile = FLAG_SPECIAL; + if (mTradebot && shopWindow && !shopWindow->isShopEmpty()) + smile |= FLAG_SHOP; + + if (mAwayMode || mPseudoAwayMode) + smile |= FLAG_AWAY; + + if (mInactive) + smile |= FLAG_INACTIVE; + + if (emote(smile)) + mAdvertTime = time + 60; + else + mAdvertTime = time + 30; + } + + if (mTestParticleTime != time && !mTestParticleName.empty()) + { + unsigned long hash = UpdaterWindow::getFileHash(mTestParticleName); + if (hash != mTestParticleHash) + { + setTestParticle(mTestParticleName, false); + mTestParticleHash = hash; + } + mTestParticleTime = time; + } + + BLOCK_END("LocalPlayer::slowLogic") +} + +void LocalPlayer::setAction(const Action &action, const int attackType) +{ + if (action == DEAD) + { + if (!mLastHitFrom.empty()) + { + // TRANSLATORS: chat message after death + debugMsg(strprintf(_("You were killed by %s"), + mLastHitFrom.c_str())); + mLastHitFrom.clear(); + } + setTarget(nullptr); + } + + Being::setAction(action, attackType); +#ifdef USE_MUMBLE + if (mumbleManager) + mumbleManager->setAction(static_cast(action)); +#endif +} + +void LocalPlayer::setGMLevel(const int level) +{ + mGMLevel = level; + + if (level > 0) + { + setGM(true); + if (chatWindow) + { + chatWindow->loadGMCommands(); + if (!gmChatTab && config.getBoolValue("enableGmTab")) + gmChatTab = new GmTab(chatWindow); + } + } +} + +#ifdef MANASERV_SUPPORT +Position LocalPlayer::getNextWalkPosition(const unsigned char dir) const +{ + // Compute where the next tile will be set. + int dx = 0, dy = 0; + if (dir & Being::UP) + dy--; + if (dir & Being::DOWN) + dy++; + if (dir & Being::LEFT) + dx--; + if (dir & Being::RIGHT) + dx++; + + const Vector &pos = getPosition(); + + // If no map or no direction is given, give back the current player position + if (!mMap || (!dx && !dy)) + return Position(static_cast(pos.x), static_cast(pos.y)); + + const int posX = static_cast(pos.x); + const int posY = static_cast(pos.y); + // Get the current tile pos and its offset + const int tileX = posX / mMap->getTileWidth(); + const int tileY = posY / mMap->getTileHeight(); + const int offsetX = posX % mMap->getTileWidth(); + const int offsetY = posY % mMap->getTileHeight(); + const unsigned char walkMask = getWalkMask(); + const int radius = getCollisionRadius(); + + // Get the walkability of every surrounding tiles. + bool wTopLeft = mMap->getWalk(tileX - 1, tileY - 1, walkMask); + const bool wTop = mMap->getWalk(tileX, tileY - 1, walkMask); + bool wTopRight = mMap->getWalk(tileX + 1, tileY - 1, walkMask); + const bool wLeft = mMap->getWalk(tileX - 1, tileY, walkMask); + const bool wRight = mMap->getWalk(tileX + 1, tileY, walkMask); + bool wBottomLeft = mMap->getWalk(tileX - 1, tileY + 1, walkMask); + const bool wBottom = mMap->getWalk(tileX, tileY + 1, walkMask); + bool wBottomRight = mMap->getWalk(tileX + 1, tileY + 1, walkMask); + + // Make diagonals unwalkable when both straight directions are blocking + if (!wTop) + { + if (!wRight) + wTopRight = false; + if (!wLeft) + wTopLeft = false; + } + if (!wBottom) + { + if (!wRight) + wBottomRight = false; + if (!wLeft) + wBottomLeft = false; + } + + // We'll make tests for each desired direction + + // Handle diagonal cases by setting the way back to a straight direction + // when necessary. + if (dx && dy) + { + // Going top-right + if (dx > 0 && dy < 0) + { + if (!wTopRight) + { + // Choose a straight direction when diagonal target is blocked + if (!wTop && wRight) + { + dy = 0; + } + else if (wTop && !wRight) + { + dx = 0; + } + else if (!wTop && !wRight) + { + return Position(tileX * 32 + 32 - radius, + tileY * 32 + getCollisionRadius()); + } + else // Both straight direction are walkable + { + // Go right when below the corner + if (offsetY >= (offsetX / mMap->getTileHeight() + - (offsetX / mMap->getTileWidth() + * mMap->getTileHeight()) )) + { + dy = 0; + } + else // Go up otherwise + { + dx = 0; + } + } + } + else // The diagonal is walkable + { + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX + 32, posY - 32)); + } + } + + // Going top-left + if (dx < 0 && dy < 0) + { + if (!wTopLeft) + { + // Choose a straight direction when diagonal target is blocked + if (!wTop && wLeft) + { + dy = 0; + } + else if (wTop && !wLeft) + { + dx = 0; + } + else if (!wTop && !wLeft) + { + return Position(tileX * 32 + radius, + tileY * 32 + radius); + } + else // Both straight direction are walkable + { + // Go left when below the corner + if (offsetY >= (offsetX / mMap->getTileWidth() + * mMap->getTileHeight())) + { + dy = 0; + } + else // Go up otherwise + { + dx = 0; + } + } + } + else // The diagonal is walkable + { + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX - 32, posY - 32)); + } + } + + // Going bottom-left + if (dx < 0 && dy > 0) + { + if (!wBottomLeft) + { + // Choose a straight direction when diagonal target is blocked + if (!wBottom && wLeft) + { + dy = 0; + } + else if (wBottom && !wLeft) + { + dx = 0; + } + else if (!wBottom && !wLeft) + { + return Position(tileX * 32 + radius, + tileY * 32 + 32 - radius); + } + else // Both straight direction are walkable + { + // Go down when below the corner + if (offsetY >= (offsetX / mMap->getTileHeight() + - (offsetX / mMap->getTileWidth() + * mMap->getTileHeight()))) + { + dx = 0; + } + else // Go left otherwise + { + dy = 0; + } + } + } + else // The diagonal is walkable + { + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX - 32, posY + 32)); + } + } + + // Going bottom-right + if (dx > 0 && dy > 0) + { + if (!wBottomRight) + { + // Choose a straight direction when diagonal target is blocked + if (!wBottom && wRight) + { + dy = 0; + } + else if (wBottom && !wRight) + { + dx = 0; + } + else if (!wBottom && !wRight) + { + return Position(tileX * 32 + 32 - radius, + tileY * 32 + 32 - radius); + } + else // Both straight direction are walkable + { + // Go down when below the corner + if (offsetY >= (offsetX / mMap->getTileWidth() + * mMap->getTileHeight())) + { + dx = 0; + } + else // Go right otherwise + { + dy = 0; + } + } + } + else // The diagonal is walkable + { + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX + 32, posY + 32)); + } + } + } // End of diagonal cases + + // Straight directions + // Right direction + if (dx > 0 && !dy) + { + // If the straight destination is blocked, + // Make the player go the closest possible. + if (!wRight) + { + return Position(tileX * 32 + 32 - radius, posY); + } + else + { + if (!wTopRight) + { + // If we're going to collide with the top-right corner + if (offsetY - radius < 0) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + 32 - radius, + tileY * 32 + radius); + } + } + + if (!wBottomRight) + { + // If we're going to collide with the bottom-right corner + if (offsetY + radius > 32) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + 32 - radius, + tileY * 32 + 32 - radius); + } + } + // If the way is clear, step up one checked tile ahead. + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX + 32, posY)); + } + } + + // Left direction + if (dx < 0 && !dy) + { + // If the straight destination is blocked, + // Make the player go the closest possible. + if (!wLeft) + { + return Position(tileX * 32 + radius, posY); + } + else + { + if (!wTopLeft) + { + // If we're going to collide with the top-left corner + if (offsetY - radius < 0) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + radius, + tileY * 32 + radius); + } + } + + if (!wBottomLeft) + { + // If we're going to collide with the bottom-left corner + if (offsetY + radius > 32) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + radius, + tileY * 32 + 32 - radius); + } + } + // If the way is clear, step up one checked tile ahead. + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX - 32, posY)); + } + } + + // Up direction + if (!dx && dy < 0) + { + // If the straight destination is blocked, + // Make the player go the closest possible. + if (!wTop) + { + return Position(posX, tileY * 32 + radius); + } + else + { + if (!wTopLeft) + { + // If we're going to collide with the top-left corner + if (offsetX - radius < 0) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + radius, + tileY * 32 + radius); + } + } + + if (!wTopRight) + { + // If we're going to collide with the top-right corner + if (offsetX + radius > 32) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + 32 - radius, + tileY * 32 + radius); + } + } + // If the way is clear, step up one checked tile ahead. + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX, posY - 32)); + } + } + + // Down direction + if (!dx && dy > 0) + { + // If the straight destination is blocked, + // Make the player go the closest possible. + if (!wBottom) + { + return Position(posX, tileY * 32 + 32 - radius); + } + else + { + if (!wBottomLeft) + { + // If we're going to collide with the bottom-left corner + if (offsetX - radius < 0) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + radius, + tileY * 32 + 32 - radius); + } + } + + if (!wBottomRight) + { + // If we're going to collide with the bottom-right corner + if (offsetX + radius > 32) + { + // We make the player corrects its offset + // before going further + return Position(tileX * 32 + 32 - radius, + tileY * 32 + 32 - radius); + } + } + // If the way is clear, step up one checked tile ahead. + return mMap->checkNodeOffsets(radius, + walkMask, Position(posX, posY + 32)); + } + } + + // Return the current position if everything else has failed. + return Position(posX, posY); +} +#endif + +void LocalPlayer::nextTile(unsigned char dir A_UNUSED = 0) +{ +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + Party *const party = Party::getParty(1); + if (party) + { + PartyMember *const pm = party->getMember(getName()); + if (pm) + { + pm->setX(mX); + pm->setY(mY); + } + } + + if (mPath.empty()) + { + if (mPickUpTarget) + pickUp(mPickUpTarget); + + if (mWalkingDir) + startWalking(mWalkingDir); + } + else if (mPath.size() == 1) + { + if (mPickUpTarget) + pickUp(mPickUpTarget); + } + + if (mGoingToTarget && mTarget && withinAttackRange(mTarget)) + { + mAction = Being::STAND; + attack(mTarget, true); + mGoingToTarget = false; + mPath.clear(); + return; + } + else if (mGoingToTarget && !mTarget) + { + mGoingToTarget = false; + mPath.clear(); + } + + if (mPath.empty()) + { + if (mNavigatePath.empty() || mAction != MOVE) + setAction(STAND); + else + mNextStep = true; + } + else + { + Being::nextTile(); + } + } +#ifdef MANASERV_SUPPORT + else + { + if (!mMap || !dir) + return; + + const Vector &pos = getPosition(); + const Position destination = getNextWalkPosition(dir); + + if (static_cast(pos.x) != destination.x + || static_cast(pos.y) != destination.y) + { + setDestination(destination.x, destination.y); + } + else if (dir != mDirection) + { + Net::getPlayerHandler()->setDirection(dir); + setDirection(dir); + } + } +#endif +} + +bool LocalPlayer::pickUp(FloorItem *const item) +{ + if (!item) + return false; + + if (!client->limitPackets(PACKET_PICKUP)) + return false; + + const int dx = item->getTileX() - mX; + const int dy = item->getTileY() - mY; + int dist = 6; + + if (mPickUpType >= 4 && mPickUpType <= 6) + dist = 4; + + if (dx * dx + dy * dy < dist) + { + if (actorSpriteManager && actorSpriteManager->checkForPickup(item)) + { + Net::getPlayerHandler()->pickUp(item); + mPickUpTarget = nullptr; + } + } + else if (mPickUpType >= 4 && mPickUpType <= 6) + { +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + setDestination(item->getPixelX() + 16, item->getPixelY() + 16); + mPickUpTarget = item; + mPickUpTarget->addActorSpriteListener(this); + } + else +#endif + { + const Vector &playerPos = getPosition(); + const Path debugPath = mMap->findPath( + static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + item->getTileX(), item->getTileY(), getWalkMask(), 0); + if (!debugPath.empty()) + navigateTo(item->getTileX(), item->getTileY()); + else + setDestination(item->getTileX(), item->getTileY()); + + mPickUpTarget = item; + mPickUpTarget->addActorSpriteListener(this); + } + } + return true; +} + +void LocalPlayer::actorSpriteDestroyed(const ActorSprite &actorSprite) +{ + if (mPickUpTarget == &actorSprite) + mPickUpTarget = nullptr; +} + +Being *LocalPlayer::getTarget() const +{ + return mTarget; +} + +void LocalPlayer::setTarget(Being *const target) +{ + if (target == this && target) + return; + + if (target == mTarget) + return; + + Being *oldTarget = nullptr; + if (mTarget) + { + mTarget->untarget(); + oldTarget = mTarget; + } + + if (mTarget && mTarget->getType() == ActorSprite::MONSTER) + mTarget->setShowName(false); + + mTarget = target; + + if (oldTarget) + oldTarget->updateName(); + + if (mTarget) + { + mLastTargetX = mTarget->getTileX(); + mLastTargetY = mTarget->getTileY(); + mTarget->updateName(); + } + + if (target && target->getType() == ActorSprite::MONSTER) + target->setShowName(true); +} + +void LocalPlayer::setDestination(const int x, const int y) +{ + mActivityTime = cur_time; + + if (getAttackType() == 0 || !mAttackMoving) + mKeepAttacking = false; + + // Only send a new message to the server when destination changes + if (x != mDest.x || y != mDest.y) + { + if (mInvertDirection != 1) + { + Net::getPlayerHandler()->setDestination(x, y, mDirection); + Being::setDestination(x, y); + } + else if (mInvertDirection == 1) + { + uint8_t newDir = 0; + if (mDirection & UP) + newDir |= DOWN; + if (mDirection & LEFT) + newDir |= RIGHT; + if (mDirection & DOWN) + newDir |= UP; + if (mDirection & RIGHT) + newDir |= LEFT; + + Net::getPlayerHandler()->setDestination(x, y, newDir); + +// if (client->limitPackets(PACKET_DIRECTION)) + { + setDirection(newDir); + Net::getPlayerHandler()->setDirection(newDir); + } + + Being::setDestination(x, y); + } + else + { +#ifdef MANASERV_SUPPORT + // Manaserv: + // If the destination given to being class is accepted, + // we inform the Server. + if ((x == mDest.x && y == mDest.y) + || Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + Net::getPlayerHandler()->setDestination(x, y, mDirection); + } + } + } +} + +void LocalPlayer::setWalkingDir(const unsigned char dir) +{ + // This function is called by Game::handleInput() + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + // First if player is pressing key for the direction he is already + // going, do nothing more... + + // Else if he is pressing a key, and its different from what he has + // been pressing, stop (do not send this stop to the server) and + // start in the new direction + if (dir && (dir != getWalkingDir())) + stopWalking(false); + + // Else, he is not pressing a key, + // and the current path hasn't been sent by mouse, + // then, stop (sending to server). + else if (!dir) + { + if (!mPathSetByMouse) + stopWalking(true); + return; + } + + // If the delay to send another walk message to the server hasn't + // expired, don't do anything or we could get disconnected for + // spamming the server + if (get_elapsed_time(mLocalWalkTime) < walkingKeyboardDelay) + return; + } +#endif + + mWalkingDir = dir; + + // If we're not already walking, start walking. + if (mAction != MOVE && dir) + { + startWalking(dir); + } +#ifdef MANASERV_SUPPORT + else if (mAction == MOVE && (Net::getNetworkType() + == ServerInfo::MANASERV)) + { + nextTile(dir); + } +#endif +} + +void LocalPlayer::startWalking(const unsigned char dir) +{ + // This function is called by setWalkingDir(), + // but also by nextTile() for TMW-Athena... + if (!mMap || !dir) + return; + + mPickUpTarget = nullptr; + if (mAction == MOVE && !mPath.empty()) + { + // Just finish the current action, otherwise we get out of sync +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + const Vector &pos = getPosition(); + Being::setDestination(static_cast(pos.x), + static_cast(pos.y)); + } + else +#endif + { + Being::setDestination(mX, mY); + } + return; + } + + int dx = 0, dy = 0; + if (dir & UP) + dy--; + if (dir & DOWN) + dy++; + if (dir & LEFT) + dx--; + if (dir & RIGHT) + dx++; + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() != ServerInfo::MANASERV) +#endif + { + const unsigned char walkMask = getWalkMask(); + // Prevent skipping corners over colliding tiles + if (dx && !mMap->getWalk(mX + dx, mY, walkMask)) + dx = 0; + if (dy && !mMap->getWalk(mX, mY + dy, walkMask)) + dy = 0; + + // Choose a straight direction when diagonal target is blocked + if (dx && dy && !mMap->getWalk(mX + dx, mY + dy, walkMask)) + dx = 0; + + // Walk to where the player can actually go + if ((dx || dy) && mMap->getWalk(mX + dx, mY + dy, walkMask)) + { + setDestination(mX + dx, mY + dy); + } + else if (dir != mDirection) + { + // If the being can't move, just change direction + +// if (client->limitPackets(PACKET_DIRECTION)) + { + Net::getPlayerHandler()->setDirection(dir); + setDirection(dir); + } + } + } +#ifdef MANASERV_SUPPORT + else + { + nextTile(dir); + } +#endif +} + +void LocalPlayer::stopWalking(const bool sendToServer) +{ + if (mAction == MOVE && mWalkingDir) + { + mWalkingDir = 0; + mLocalWalkTime = 0; + mPickUpTarget = nullptr; + + setDestination(static_cast(getPosition().x), + static_cast(getPosition().y)); + if (sendToServer) + { + Net::getPlayerHandler()->setDestination( + static_cast(getPosition().x), + static_cast(getPosition().y), -1); + } + setAction(STAND); + } + + // No path set anymore, so we reset the path by mouse flag + mPathSetByMouse = false; + + clearPath(); + navigateClean(); +} + +bool LocalPlayer::toggleSit() const +{ + if (!client->limitPackets(PACKET_SIT)) + return false; + + Being::Action newAction; + switch (mAction) + { + case STAND: + case SPAWN: + newAction = SIT; + break; + case SIT: + newAction = STAND; + break; + case MOVE: + case ATTACK: + case DEAD: + case HURT: + default: + return true; + } + + Net::getPlayerHandler()->changeAction(newAction); + return true; +} + +bool LocalPlayer::updateSit() const +{ + if (!client->limitPackets(PACKET_SIT)) + return false; + + Net::getPlayerHandler()->changeAction(mAction); + return true; +} + +bool LocalPlayer::emote(const uint8_t emotion) +{ + if (!client->limitPackets(PACKET_EMOTE)) + return false; + + Net::getPlayerHandler()->emote(emotion); + return true; +} + +void LocalPlayer::attack(Being *const target, const bool keep, + const bool dontChangeEquipment) +{ +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + if (mLastAction != -1) + return; + + // Can only attack when standing still + if (mAction != STAND && mAction != ATTACK) + return; + } +#endif + + mKeepAttacking = keep; + + if (!target || target->getType() == ActorSprite::NPC) + return; + + if (mTarget != target || !mTarget) + setTarget(target); + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + const Vector &plaPos = this->getPosition(); + const Vector &tarPos = mTarget->getPosition(); + const int dist_x = static_cast(plaPos.x - tarPos.x); + const int dist_y = static_cast(plaPos.y - tarPos.y); + + if (abs(dist_y) >= abs(dist_x)) + { + if (dist_y < 0) + setDirection(DOWN); + else + setDirection(UP); + } + else + { + if (dist_x < 0) + setDirection(RIGHT); + else + setDirection(LEFT); + } + + mLastAction = tick_time; + } + else +#endif + { + const int dist_x = target->getTileX() - mX; + const int dist_y = target->getTileY() - mY; + + // Must be standing or sitting to attack + if (mAction != STAND && mAction != SIT) + return; + + if (abs(dist_y) >= abs(dist_x)) + { + if (dist_y > 0) + setDirection(DOWN); + else + setDirection(UP); + } + else + { + if (dist_x > 0) + setDirection(RIGHT); + else + setDirection(LEFT); + } + + mActionTime = tick_time; + } + + if (target->getType() != Being::PLAYER || checAttackPermissions(target)) + { + setAction(ATTACK); + + if (!client->limitPackets(PACKET_ATTACK)) + return; + + if (!dontChangeEquipment) + changeEquipmentBeforeAttack(target); + + Net::getPlayerHandler()->attack(target->getId(), mServerAttack); + } + +#ifdef MANASERV_SUPPORT + if ((Net::getNetworkType() != ServerInfo::MANASERV) && !keep) +#else + if (!keep) +#endif + stopAttack(); +} + +void LocalPlayer::stopAttack(const bool keepAttack) +{ + if (!client->limitPackets(PACKET_STOPATTACK)) + return; + + if (mServerAttack && mAction == ATTACK) + Net::getPlayerHandler()->stopAttack(); + + untarget(); + if (!keepAttack || !mAttackNext) + mKeepAttacking = false; +} + +void LocalPlayer::untarget() +{ + if (mAction == ATTACK) + setAction(STAND); + + if (mTarget) + setTarget(nullptr); +} + +void LocalPlayer::pickedUp(const ItemInfo &itemInfo, const int amount, + const unsigned char color, const int floorItemId, + const unsigned char fail) +{ + if (fail) + { + if (actorSpriteManager && floorItemId) + { + FloorItem *const item = actorSpriteManager->findItem(floorItemId); + if (item) + { + if (!item->getShowMsg()) + return; + item->setShowMsg(false); + } + } + const char* msg; + switch (fail) + { + case PICKUP_BAD_ITEM: + // TRANSLATORS: pickup error message + msg = N_("Tried to pick up nonexistent item."); + break; + case PICKUP_TOO_HEAVY: + // TRANSLATORS: pickup error message + msg = N_("Item is too heavy."); + break; + case PICKUP_TOO_FAR: + // TRANSLATORS: pickup error message + msg = N_("Item is too far away."); + break; + case PICKUP_INV_FULL: + // TRANSLATORS: pickup error message + msg = N_("Inventory is full."); + break; + case PICKUP_STACK_FULL: + // TRANSLATORS: pickup error message + msg = N_("Stack is too big."); + break; + case PICKUP_DROP_STEAL: + // TRANSLATORS: pickup error message + msg = N_("Item belongs to someone else."); + break; + default: + // TRANSLATORS: pickup error message + msg = N_("Unknown problem picking up item."); + break; + } + if (localChatTab && config.getBoolValue("showpickupchat")) + localChatTab->chatLog(gettext(msg), BY_SERVER); + + if (mMap && config.getBoolValue("showpickupparticle")) + { + // Show pickup notification + addMessageToQueue(gettext(msg), UserPalette::PICKUP_INFO); + } + } + else + { + std::string str; + if (serverVersion > 0) + str = itemInfo.getName(color); + else + str = itemInfo.getName(); + + if (config.getBoolValue("showpickupchat") && localChatTab) + { + // TRANSLATORS: %d is number, + // [@@%d|%s@@] - here player can see link to item + localChatTab->chatLog(strprintf(ngettext("You picked up %d " + "[@@%d|%s@@].", "You picked up %d [@@%d|%s@@].", amount), + amount, itemInfo.getId(), str.c_str()), BY_SERVER); + } + + if (mMap && config.getBoolValue("showpickupparticle")) + { + // Show pickup notification + if (amount > 1) + { + addMessageToQueue(strprintf("%d x %s", amount, + str.c_str()), UserPalette::PICKUP_INFO); + } + else + { + addMessageToQueue(str, UserPalette::PICKUP_INFO); + } + } + } +} + +int LocalPlayer::getAttackRange() const +{ + if (mAttackRange > -1) + { + return mAttackRange; + } + else + { + const Item *const weapon = PlayerInfo::getEquipment(EQUIP_FIGHT1_SLOT); + if (weapon) + { + const ItemInfo &info = weapon->getInfo(); + return info.getAttackRange(); + } + return 48; // unarmed range + } +} + +bool LocalPlayer::withinAttackRange(const Being *const target, + const bool fixDistance, + const int addRange) const +{ + if (!target) + return false; + + int range = getAttackRange() + addRange; + int dx; + int dy; + + if (fixDistance && range == 1) + range = 2; + +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + const Vector &targetPos = target->getPosition(); + const Vector &pos = getPosition(); + dx = static_cast(abs(static_cast(targetPos.x - pos.x))); + dy = static_cast(abs(static_cast(targetPos.y - pos.y))); + } + else +#endif + { + dx = static_cast(abs(target->getTileX() - mX)); + dy = static_cast(abs(target->getTileY() - mY)); + } + return !(dx > range || dy > range); +} + +void LocalPlayer::setGotoTarget(Being *const target) +{ + if (!target) + return; + + mPickUpTarget = nullptr; +#ifdef MANASERV_SUPPORT + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + mTarget = target; + mGoingToTarget = true; + const Vector &targetPos = target->getPosition(); + setDestination(static_cast(targetPos.x), + static_cast(targetPos.y)); + } + else +#endif + { + setTarget(target); + mGoingToTarget = true; + setDestination(target->getTileX(), target->getTileY()); + } +} + +void LocalPlayer::handleStatusEffect(StatusEffect *const effect, + const int effectId) +{ + Being::handleStatusEffect(effect, effectId); + + if (effect) + { + effect->deliverMessage(); + effect->playSFX(); + + AnimatedSprite *const sprite = effect->getIcon(); + + if (!sprite) + { + // delete sprite, if necessary + for (unsigned int i = 0; i < mStatusEffectIcons.size(); ) + { + if (mStatusEffectIcons[i] == effectId) + { + mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i); + if (miniStatusWindow) + miniStatusWindow->eraseIcon(i); + } + else + { + i++; + } + } + } + else + { + // replace sprite or append + bool found = false; + const unsigned int sz = mStatusEffectIcons.size(); + for (unsigned int i = 0; i < sz; i++) + { + if (mStatusEffectIcons[i] == effectId) + { + if (miniStatusWindow) + miniStatusWindow->setIcon(i, sprite); + found = true; + break; + } + } + + if (!found) + { // add new + const int offset = static_cast(mStatusEffectIcons.size()); + if (miniStatusWindow) + miniStatusWindow->setIcon(offset, sprite); + mStatusEffectIcons.push_back(effectId); + } + } + } +} + +void LocalPlayer::addMessageToQueue(const std::string &message, + const int color) +{ + if (mMessages.size() < 20) + mMessages.push_back(MessagePair(message, color)); +} + +void LocalPlayer::optionChanged(const std::string &value) +{ + if (value == "showownname") + setShowName(config.getBoolValue("showownname")); + else if (value == "targetDeadPlayers") + mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers"); + else if (value == "enableBuggyServers") + mIsServerBuggy = serverConfig.getBoolValue("enableBuggyServers"); + else if (value == "syncPlayerMove") + mSyncPlayerMove = config.getBoolValue("syncPlayerMove"); + else if (value == "drawPath") + mDrawPath = config.getBoolValue("drawPath"); + else if (value == "serverAttack") + mServerAttack = config.getBoolValue("serverAttack"); + else if (value == "attackMoving") + mAttackMoving = config.getBoolValue("attackMoving"); + else if (value == "attackNext") + mAttackNext = config.getBoolValue("attackNext"); + else if (value == "showJobExp") + mShowJobExp = config.getBoolValue("showJobExp"); + else if (value == "enableAdvert") + mEnableAdvert = config.getBoolValue("enableAdvert"); + else if (value == "tradebot") + mTradebot = config.getBoolValue("tradebot"); + else if (value == "targetOnlyReachable") + mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable"); +} + +void LocalPlayer::processEvent(Channels channel, + const DepricatedEvent &event) +{ + if (channel == CHANNEL_ATTRIBUTES) + { + if (event.getName() == EVENT_UPDATEATTRIBUTE) + { + switch (event.getInt("id")) + { + case PlayerInfo::EXP: + { + if (event.getInt("oldValue") > event.getInt("newValue")) + break; + + const int change = event.getInt("newValue") + - event.getInt("oldValue"); + + if (change != 0) + { + // TRANSLATORS: get xp message + addMessageToQueue(strprintf("%d %s", change, _("xp"))); + } + break; + } + case PlayerInfo::LEVEL: + mLevel = event.getInt("newValue"); + break; + default: + break; + }; + } + else if (event.getName() == EVENT_UPDATESTAT) + { + if (!mShowJobExp) + return; + + const int id = event.getInt("id"); + if (id == Net::getPlayerHandler()->getJobLocation()) + { + const std::pair exp + = PlayerInfo::getStatExperience(id); + if (event.getInt("oldValue1") > exp.first + || !event.getInt("oldValue2")) + { + return; + } + + const int change = exp.first - event.getInt("oldValue1"); + if (change != 0 && mMessages.size() < 20) + { + if (!mMessages.empty()) + { + MessagePair pair = mMessages.back(); + // TRANSLATORS: this is normal experience + if (pair.first.find(strprintf(" %s", + _("xp"))) == pair.first.size() + - strlen(_("xp")) - 1) + { + mMessages.pop_back(); + // TRANSLATORS: this is job experience + pair.first.append(strprintf(", %d %s", + change, _("job"))); + mMessages.push_back(pair); + } + else + { + // TRANSLATORS: this is job experience + addMessageToQueue(strprintf("%d %s", + change, _("job"))); + } + } + else + { + // TRANSLATORS: this is job experience + addMessageToQueue(strprintf( + "%d %s", change, _("job"))); + } + } + } + } + } +} + +void LocalPlayer::moveTo(const int x, const int y) +{ + setDestination(x, y); +} + +void LocalPlayer::move(const int dX, const int dY) +{ + mPickUpTarget = nullptr; + moveTo(mX + dX, mY + dY); +} + +void LocalPlayer::moveToTarget(int dist) +{ + bool gotPos(false); + Path debugPath; + + const Vector &playerPos = getPosition(); + unsigned int limit(0); + + if (dist == -1) + { + dist = mMoveToTargetType; + if (mMoveToTargetType == 0) + { + dist = 0; + } + else + { + switch (mMoveToTargetType) + { + case 1: + dist = 1; + break; + case 2: + dist = 2; + break; + case 3: + dist = 3; + break; + case 4: + dist = 5; + break; + case 5: + dist = 7; + break; + case 6: + case 7: + dist = mAttackRange; + if (dist == 1 && serverVersion < 1) + dist = 2; + break; + case 8: + dist = mAttackRange - 1; + if (dist < 1) + dist = 1; + if (dist == 1 && serverVersion < 1) + dist = 2; + break; + default: + break; + } + } + } + + if (mTarget) + { + if (mMap) + { + debugPath = mMap->findPath(static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + mTarget->getTileX(), mTarget->getTileY(), getWalkMask(), 0); + } + + const unsigned int sz = debugPath.size(); + if (sz < static_cast(dist)) + return; + limit = static_cast(sz) - dist; + gotPos = true; + } + else if (mNavigateX || mNavigateY) + { + debugPath = mNavigatePath; + limit = dist; + gotPos = true; + } + + if (gotPos) + { + if (dist == 0) + { + if (mTarget) + navigateTo(mTarget); + } + else + { + Position pos(0, 0); + unsigned int f = 0; + + for (Path::const_iterator i = debugPath.begin(), + i_end = debugPath.end(); + i != i_end && f < limit; ++i, f++) + { + pos = (*i); + } + navigateTo(pos.x, pos.y); + } + } + else if (mLastTargetX || mLastTargetY) + { + navigateTo(mLastTargetX, mLastTargetY); + } +} + +void LocalPlayer::moveToHome() +{ + mPickUpTarget = nullptr; + if ((mX != mCrossX || mY != mCrossY) && mCrossX && mCrossY) + { + moveTo(mCrossX, mCrossY); + } + else if (mMap) + { + const std::map::const_iterator iter = + mHomes.find(mMap->getProperty("_realfilename")); + + if (iter != mHomes.end()) + { + const Vector pos = mHomes[(*iter).first]; + if (mX == pos.x && mY == pos.y) + { + Net::getPlayerHandler()->setDestination( + static_cast(pos.x), + static_cast(pos.y), + static_cast(mDirection)); + } + else + { + navigateTo(static_cast(pos.x), static_cast(pos.y)); + } + } + } +} + +static const unsigned invertDirectionSize = 5; + +void LocalPlayer::changeMode(unsigned *const var, const unsigned limit, + const char *const conf, + std::string (LocalPlayer::*const func)(), + const unsigned def, + const bool save) +{ + if (!var) + return; + + (*var) ++; + if (*var >= limit) + *var = def; + if (save) + config.setValue(conf, *var); + if (miniStatusWindow) + miniStatusWindow->updateStatus(); + const std::string str = (this->*func)(); + if (str.size() > 4) + debugMsg(str.substr(4)); +} + +void LocalPlayer::invertDirection() +{ + mMoveState = 0; + changeMode(&mInvertDirection, invertDirectionSize, "invertMoveDirection", + &LocalPlayer::getInvertDirectionString, 0, false); +} + +static const char *const invertDirectionStrings[] = +{ + // TRANSLATORS: move type in status bar + N_("(D) default moves"), + // TRANSLATORS: move type in status bar + N_("(I) invert moves"), + // TRANSLATORS: move type in status bar + N_("(c) moves with some crazy moves"), + // TRANSLATORS: move type in status bar + N_("(C) moves with crazy moves"), + // TRANSLATORS: move type in status bar + N_("(d) double normal + crazy"), + // TRANSLATORS: move type in status bar + N_("(?) unknown move") +}; + +std::string LocalPlayer::getInvertDirectionString() +{ + return gettext(getVarItem(&invertDirectionStrings[0], + mInvertDirection, invertDirectionSize)); +} + +static const unsigned crazyMoveTypeSize = 11; + +void LocalPlayer::changeCrazyMoveType() +{ + mCrazyMoveState = 0; + changeMode(&mCrazyMoveType, crazyMoveTypeSize, "crazyMoveType", + &LocalPlayer::getCrazyMoveTypeString, 1); +} + +std::string LocalPlayer::getCrazyMoveTypeString() +{ + if (mCrazyMoveType < crazyMoveTypeSize - 1) + { + // TRANSLATORS: crazy move type in status bar + return strprintf(_("(%u) crazy move number %u"), + mCrazyMoveType, mCrazyMoveType); + } + else if (mCrazyMoveType == crazyMoveTypeSize - 1) + { + // TRANSLATORS: crazy move type in status bar + return _("(a) custom crazy move"); + } + else + { + // TRANSLATORS: crazy move type in status bar + return _("(?) crazy move"); + } +} + +static const unsigned moveToTargetTypeSize = 9; + +void LocalPlayer::changeMoveToTargetType() +{ + changeMode(&mMoveToTargetType, moveToTargetTypeSize, "moveToTargetType", + &LocalPlayer::getMoveToTargetTypeString); +} + +static const char *const moveToTargetTypeStrings[] = +{ + // TRANSLATORS: move to target type in status bar + N_("(0) default moves to target"), + // TRANSLATORS: move to target type in status bar + N_("(1) moves to target in distance 1"), + // TRANSLATORS: move to target type in status bar + N_("(2) moves to target in distance 2"), + // TRANSLATORS: move to target type in status bar + N_("(3) moves to target in distance 3"), + // TRANSLATORS: move to target type in status bar + N_("(5) moves to target in distance 5"), + // TRANSLATORS: move to target type in status bar + N_("(7) moves to target in distance 7"), + // TRANSLATORS: move to target type in status bar + N_("(A) moves to target in attack range"), + // TRANSLATORS: move to target type in status bar + N_("(a) archer attack range"), + // TRANSLATORS: move to target type in status bar + N_("(B) moves to target in attack range - 1"), + // TRANSLATORS: move to target type in status bar + N_("(?) move to target") +}; + +std::string LocalPlayer::getMoveToTargetTypeString() +{ + return gettext(getVarItem(&moveToTargetTypeStrings[0], + mMoveToTargetType, moveToTargetTypeSize)); +} + +static const unsigned followModeSize = 4; + +void LocalPlayer::changeFollowMode() +{ + changeMode(&mFollowMode, followModeSize, "followMode", + &LocalPlayer::getFollowModeString); +} + +static const char *const followModeStrings[] = +{ + // TRANSLATORS: folow mode in status bar + N_("(D) default follow"), + // TRANSLATORS: folow mode in status bar + N_("(R) relative follow"), + // TRANSLATORS: folow mode in status bar + N_("(M) mirror follow"), + // TRANSLATORS: folow mode in status bar + N_("(P) pet follow"), + // TRANSLATORS: folow mode in status bar + N_("(?) unknown follow") +}; + +std::string LocalPlayer::getFollowModeString() +{ + return gettext(getVarItem(&followModeStrings[0], + mFollowMode, followModeSize)); +} + +const unsigned attackWeaponTypeSize = 4; + +void LocalPlayer::changeAttackWeaponType() +{ + changeMode(&mAttackWeaponType, attackWeaponTypeSize, "attackWeaponType", + &LocalPlayer::getAttackWeaponTypeString, 1); +} + +static const char *attackWeaponTypeStrings[] = +{ + // TRANSLATORS: switch attack type in status bar + N_("(?) attack"), + // TRANSLATORS: switch attack type in status bar + N_("(D) default attack"), + // TRANSLATORS: switch attack type in status bar + N_("(s) switch attack without shield"), + // TRANSLATORS: switch attack type in status bar + N_("(S) switch attack with shield"), + // TRANSLATORS: switch attack type in status bar + N_("(?) attack") +}; + +std::string LocalPlayer::getAttackWeaponTypeString() +{ + return gettext(getVarItem(&attackWeaponTypeStrings[0], + mAttackWeaponType, attackWeaponTypeSize)); +} + +const unsigned attackTypeSize = 4; + +void LocalPlayer::changeAttackType() +{ + changeMode(&mAttackType, attackTypeSize, "attackType", + &LocalPlayer::getAttackTypeString); +} + +static const char *const attackTypeStrings[] = +{ + // TRANSLATORS: attack type in status bar + N_("(D) default attack"), + // TRANSLATORS: attack type in status bar + N_("(G) go and attack"), + // TRANSLATORS: attack type in status bar + N_("(A) go, attack, pickup"), + // TRANSLATORS: attack type in status bar + N_("(d) without auto attack"), + // TRANSLATORS: attack type in status bar + N_("(?) attack") +}; + +std::string LocalPlayer::getAttackTypeString() +{ + return gettext(getVarItem(&attackTypeStrings[0], + mAttackType, attackTypeSize)); +} + +const unsigned quickDropCounterSize = 31; + +void LocalPlayer::changeQuickDropCounter() +{ + changeMode(&mQuickDropCounter, quickDropCounterSize, "quickDropCounter", + &LocalPlayer::getQuickDropCounterString, 1); +} + +std::string LocalPlayer::getQuickDropCounterString() +{ + if (mQuickDropCounter > 9) + { + return strprintf("(%c) drop counter %u", static_cast( + 'a' + mQuickDropCounter - 10), mQuickDropCounter); + } + else + { + return strprintf("(%u) drop counter %u", + mQuickDropCounter, mQuickDropCounter); + } +} + +void LocalPlayer::setQuickDropCounter(const int n) +{ + if (n < 1 || n >= static_cast(quickDropCounterSize)) + return; + mQuickDropCounter = n; + config.setValue("quickDropCounter", mQuickDropCounter); + if (miniStatusWindow) + miniStatusWindow->updateStatus(); +} + +const unsigned pickUpTypeSize = 7; + +void LocalPlayer::changePickUpType() +{ + changeMode(&mPickUpType, pickUpTypeSize, "pickUpType", + &LocalPlayer::getPickUpTypeString); +} + +static const char *const pickUpTypeStrings[] = +{ + // TRANSLATORS: pickup size in status bar + N_("(S) small pick up 1x1 cells"), + // TRANSLATORS: pickup size in status bar + N_("(D) default pick up 2x1 cells"), + // TRANSLATORS: pickup size in status bar + N_("(F) forward pick up 2x3 cells"), + // TRANSLATORS: pickup size in status bar + N_("(3) pick up 3x3 cells"), + // TRANSLATORS: pickup size in status bar + N_("(g) go and pick up in distance 4"), + // TRANSLATORS: pickup size in status bar + N_("(G) go and pick up in distance 8"), + // TRANSLATORS: pickup size in status bar + N_("(A) go and pick up in max distance"), + // TRANSLATORS: pickup size in status bar + N_("(?) pick up") +}; + +std::string LocalPlayer::getPickUpTypeString() +{ + return gettext(getVarItem(&pickUpTypeStrings[0], + mPickUpType, pickUpTypeSize)); +} + +const unsigned debugPathSize = 5; + +static const char *const debugPathStrings[] = +{ + // TRANSLATORS: map view type in status bar + N_("(N) normal map view"), + // TRANSLATORS: map view type in status bar + N_("(D) debug map view"), + // TRANSLATORS: map view type in status bar + N_("(u) ultra map view"), + // TRANSLATORS: map view type in status bar + N_("(U) ultra map view 2"), + // TRANSLATORS: map view type in status bar + N_("(e) empty map view"), + // TRANSLATORS: map view type in status bar + N_("(b) black & white map view") +}; + +std::string LocalPlayer::getDebugPathString() const +{ + return gettext(getVarItem(&debugPathStrings[0], + viewport->getDebugPath(), debugPathSize)); +} + +const unsigned magicAttackSize = 5; + +void LocalPlayer::switchMagicAttack() +{ + changeMode(&mMagicAttackType, magicAttackSize, "magicAttackType", + &LocalPlayer::getMagicAttackString); +} + +static const char *const magicAttackStrings[] = +{ + // TRANSLATORS: magic attack in status bar + N_("(f) use #flar for magic attack"), + // TRANSLATORS: magic attack in status bar + N_("(c) use #chiza for magic attack"), + // TRANSLATORS: magic attack in status bar + N_("(I) use #ingrav for magic attack"), + // TRANSLATORS: magic attack in status bar + N_("(F) use #frillyar for magic attack"), + // TRANSLATORS: magic attack in status bar + N_("(U) use #upmarmu for magic attack"), + // TRANSLATORS: magic attack in status bar + N_("(?) magic attack") +}; + +std::string LocalPlayer::getMagicAttackString() +{ + return gettext(getVarItem(&magicAttackStrings[0], + mMagicAttackType, magicAttackSize)); +} + +const unsigned pvpAttackSize = 4; + +void LocalPlayer::switchPvpAttack() +{ + changeMode(&mPvpAttackType, pvpAttackSize, "pvpAttackType", + &LocalPlayer::getPvpAttackString); +} + +static const char *const pvpAttackStrings[] = +{ + // TRANSLATORS: player attack type in status bar + N_("(a) attack all players"), + // TRANSLATORS: player attack type in status bar + N_("(f) attack all except friends"), + // TRANSLATORS: player attack type in status bar + N_("(b) attack bad relations"), + // TRANSLATORS: player attack type in status bar + N_("(d) don't attack players"), + // TRANSLATORS: player attack type in status bar + N_("(?) pvp attack") +}; + +std::string LocalPlayer::getPvpAttackString() +{ + return gettext(getVarItem(&pvpAttackStrings[0], + mPvpAttackType, pvpAttackSize)); +} + +const unsigned imitationModeSize = 2; + +void LocalPlayer::changeImitationMode() +{ + changeMode(&mImitationMode, imitationModeSize, "imitationMode", + &LocalPlayer::getImitationModeString); +} + +static const char *const imitationModeStrings[] = +{ + // TRANSLATORS: imitation type in status bar + N_("(D) default imitation"), + // TRANSLATORS: imitation type in status bar + N_("(O) outfits imitation"), + // TRANSLATORS: imitation type in status bar + N_("(?) imitation") +}; + +std::string LocalPlayer::getImitationModeString() +{ + return gettext(getVarItem(&imitationModeStrings[0], + mImitationMode, imitationModeSize)); +} + +const unsigned awayModeSize = 2; + +void LocalPlayer::changeAwayMode() +{ + mAwayMode = !mAwayMode; + mAfkTime = 0; + mInactive = false; + updateName(); + if (miniStatusWindow) + miniStatusWindow->updateStatus(); + if (mAwayMode) + { + if (chatWindow) + chatWindow->clearAwayLog(); + + cancelFollow(); + navigateClean(); + if (outfitWindow) + outfitWindow->wearAwayOutfit(); + // TRANSLATORS: away message box header + mAwayDialog = new OkDialog(_("Away"), + config.getStringValue("afkMessage"), + DIALOG_SILENCE, true, false); + mAwayDialog->addActionListener(mAwayListener); + soundManager.volumeOff(); + addAfkEffect(); + } + else + { + mAwayDialog = nullptr; + soundManager.volumeRestore(); + if (chatWindow) + { + chatWindow->displayAwayLog(); + chatWindow->clearAwayLog(); + } + removeAfkEffect(); + } +} + +static const char *awayModeStrings[] = +{ + // TRANSLATORS: away type in status bar + N_("(O) on keyboard"), + // TRANSLATORS: away type in status bar + N_("(A) away"), + // TRANSLATORS: away type in status bar + N_("(?) away") +}; + +std::string LocalPlayer::getAwayModeString() +{ + return gettext(getVarItem(&awayModeStrings[0], + mAwayMode, awayModeSize)); +} + +const unsigned cameraModeSize = 2; + +static const char *cameraModeStrings[] = +{ + // TRANSLATORS: camera mode in status bar + N_("(G) game camera mode"), + // TRANSLATORS: camera mode in status bar + N_("(F) free camera mode"), + // TRANSLATORS: camera mode in status bar + N_("(?) away") +}; + +std::string LocalPlayer::getCameraModeString() const +{ + return gettext(getVarItem(&cameraModeStrings[0], + viewport->getCameraMode(), cameraModeSize)); +} + +const unsigned gameModifiersSize = 2; + +void LocalPlayer::switchGameModifiers() +{ + mDisableGameModifiers = !mDisableGameModifiers; + config.setValue("disableGameModifiers", mDisableGameModifiers); + miniStatusWindow->updateStatus(); + + const std::string str = getGameModifiersString(); + if (str.size() > 4) + debugMsg(str.substr(4)); +} + +static const char *const gameModifiersStrings[] = +{ + // TRANSLATORS: game modifiers state in status bar + N_("Game modifiers are enabled"), + // TRANSLATORS: game modifiers state in status bar + N_("Game modifiers are disabled"), + // TRANSLATORS: game modifiers state in status bar + N_("Game modifiers are unknown") +}; + +std::string LocalPlayer::getGameModifiersString() +{ + return gettext(getVarItem(&gameModifiersStrings[0], + mDisableGameModifiers, gameModifiersSize)); +} + + +void LocalPlayer::changeEquipmentBeforeAttack(const Being *const target) const +{ + if (mAttackWeaponType == 1 || !target || !PlayerInfo::getInventory()) + return; + + bool allowSword = false; + const int dx = target->getTileX() - mX; + const int dy = target->getTileY() - mY; + const Item *item = nullptr; + + if (dx * dx + dy * dy > 80) + return; + + if (dx * dx + dy * dy < 8) + allowSword = true; + + const Inventory *const inv = PlayerInfo::getInventory(); + if (!inv) + return; + + // if attack distance for sword + if (allowSword) + { + // finding sword + item = inv->findItem(571, 0); + + if (!item) + item = inv->findItem(570, 0); + + if (!item) + item = inv->findItem(579, 0); + + if (!item) + item = inv->findItem(867, 0); + + if (!item) + item = inv->findItem(536, 0); + + if (!item) + item = inv->findItem(758, 0); + + // no swords + if (!item) + return; + + // if sword not equiped + if (!item->isEquipped()) + Net::getInventoryHandler()->equipItem(item); + + // if need equip shield too + if (mAttackWeaponType == 3) + { + // finding shield + item = inv->findItem(601, 0); + if (!item) + item = inv->findItem(602, 0); + if (item && !item->isEquipped()) + Net::getInventoryHandler()->equipItem(item); + } + } + // big distance. allowed only bow + else + { + // finding bow + item = inv->findItem(545, 0); + + if (!item) + item = inv->findItem(530, 0); + + // no bow + if (!item) + return; + + if (!item->isEquipped()) + Net::getInventoryHandler()->equipItem(item); + } +} + +void LocalPlayer::crazyMove() +{ + const bool oldDisableCrazyMove = mDisableCrazyMove; + mDisableCrazyMove = true; + switch (mCrazyMoveType) + { + case 1: + crazyMove1(); + break; + case 2: + crazyMove2(); + break; + case 3: + crazyMove3(); + break; + case 4: + crazyMove4(); + break; + case 5: + crazyMove5(); + break; + case 6: + crazyMove6(); + break; + case 7: + crazyMove7(); + break; + case 8: + crazyMove8(); + break; + case 9: + crazyMove9(); + break; + case 10: + crazyMoveA(); + break; + default: + break; + } + mDisableCrazyMove = oldDisableCrazyMove; +} + +void LocalPlayer::crazyMove1() +{ + if (mAction == MOVE) + return; + +// if (!client->limitPackets(PACKET_DIRECTION)) +// return; + + if (mDirection == Being::UP) + { + setWalkingDir(Being::UP); + setDirection(Being::LEFT); + Net::getPlayerHandler()->setDirection(Being::LEFT); + } + else if (mDirection == Being::LEFT) + { + setWalkingDir(Being::LEFT); + setDirection(Being::DOWN); + Net::getPlayerHandler()->setDirection(Being::DOWN); + } + else if (mDirection == Being::DOWN) + { + setWalkingDir(Being::DOWN); + setDirection(Being::RIGHT); + Net::getPlayerHandler()->setDirection(Being::RIGHT); + } + else if (mDirection == Being::RIGHT) + { + setWalkingDir(Being::RIGHT); + setDirection(Being::UP); + Net::getPlayerHandler()->setDirection(Being::UP); + } +} + +void LocalPlayer::crazyMove2() +{ + if (mAction == MOVE) + return; + +// if (!client->limitPackets(PACKET_DIRECTION)) +// return; + + if (mDirection == Being::UP) + { + setWalkingDir(Being::UP | Being::LEFT); + setDirection(Being::RIGHT); + Net::getPlayerHandler()->setDirection(Being::DOWN | Being::RIGHT); + } + else if (mDirection == Being::RIGHT) + { + setWalkingDir(Being::UP | Being::RIGHT); + setDirection(Being::DOWN); + Net::getPlayerHandler()->setDirection(Being::DOWN | Being::LEFT); + } + else if (mDirection == Being::DOWN) + { + setWalkingDir(Being::DOWN | Being::RIGHT); + setDirection(Being::LEFT); + Net::getPlayerHandler()->setDirection(Being::UP | Being::LEFT); + } + else if (mDirection == Being::LEFT) + { + setWalkingDir(Being::DOWN | Being::LEFT); + setDirection(Being::UP); + Net::getPlayerHandler()->setDirection(Being::UP | Being::RIGHT); + } +} + +void LocalPlayer::crazyMove3() +{ + if (mAction == MOVE) + return; + + switch (mCrazyMoveState) + { + case 0: + move(1, 1); + mCrazyMoveState = 1; + break; + case 1: + move(1, -1); + mCrazyMoveState = 2; + break; + case 2: + move(-1, -1); + mCrazyMoveState = 3; + break; + case 3: + move(-1, 1); + mCrazyMoveState = 0; + break; + default: + break; + } + +// if (!client->limitPackets(PACKET_DIRECTION)) +// return; + + setDirection(Being::DOWN); + Net::getPlayerHandler()->setDirection(Being::DOWN); +} + +void LocalPlayer::crazyMove4() +{ + if (mAction == MOVE) + return; + + switch (mCrazyMoveState) + { + case 0: + move(7, 0); + mCrazyMoveState = 1; + break; + case 1: + move(-7, 0); + mCrazyMoveState = 0; + break; + default: + break; + } +} + +void LocalPlayer::crazyMove5() +{ + if (mAction == MOVE) + return; + + switch (mCrazyMoveState) + { + case 0: + move(0, 7); + mCrazyMoveState = 1; + break; + case 1: + move(0, -7); + mCrazyMoveState = 0; + break; + default: + break; + } +} + +void LocalPlayer::crazyMove6() +{ + if (mAction == MOVE) + return; + + switch (mCrazyMoveState) + { + case 0: + move(3, 0); + mCrazyMoveState = 1; + break; + case 1: + move(2, -2); + mCrazyMoveState = 2; + break; + case 2: + move(0, -3); + mCrazyMoveState = 3; + break; + case 3: + move(-2, -2); + mCrazyMoveState = 4; + break; + case 4: + move(-3, 0); + mCrazyMoveState = 5; + break; + case 5: + move(-2, 2); + mCrazyMoveState = 6; + break; + case 6: + move(0, 3); + mCrazyMoveState = 7; + break; + case 7: + move(2, 2); + mCrazyMoveState = 0; + break; + default: + break; + } +} + +void LocalPlayer::crazyMove7() +{ + if (mAction == MOVE) + return; + + switch (mCrazyMoveState) + { + case 0: + move(1, 1); + mCrazyMoveState = 1; + break; + case 1: + move(-1, 1); + mCrazyMoveState = 2; + break; + case 2: + move(-1, -1); + mCrazyMoveState = 3; + break; + case 3: + move(1, -1); + mCrazyMoveState = 0; + break; + default: + break; + } +} + +void LocalPlayer::crazyMove8() +{ + if (mAction == MOVE || !mMap) + return; + int idx = 0; + const int dist = 1; + +// look +// up, ri,do,le + static const int movesX[][4] = + { + {-1, 0, 1, 0}, // move left + { 0, 1, 0, -1}, // move up + { 1, 0, -1, 0}, // move right + { 0, -1, 0, 1} // move down + }; + +// look +// up, ri,do,le + static const int movesY[][4] = + { + { 0, -1, 0, 1}, // move left + {-1, 0, 1, 0}, // move up + { 0, 1, 0, -1}, // move right + { 1, 0, -1, 0} // move down + }; + + if (mDirection == Being::UP) + idx = 0; + else if (mDirection == Being::RIGHT) + idx = 1; + else if (mDirection == Being::DOWN) + idx = 2; + else if (mDirection == Being::LEFT) + idx = 3; + + + int mult = 1; + const unsigned char walkMask = getWalkMask(); + if (mMap->getWalk(mX + movesX[idx][0], + mY + movesY[idx][0], walkMask)) + { + while (mMap->getWalk(mX + movesX[idx][0] * mult, + mY + movesY[idx][0] * mult, + getWalkMask()) && mult <= dist) + { + mult ++; + } + move(movesX[idx][0] * (mult - 1), movesY[idx][0] * (mult - 1)); + } + else if (mMap->getWalk(mX + movesX[idx][1], + mY + movesY[idx][1], walkMask)) + { + while (mMap->getWalk(mX + movesX[idx][1] * mult, + mY + movesY[idx][1] * mult, + getWalkMask()) && mult <= dist) + { + mult ++; + } + move(movesX[idx][1] * (mult - 1), movesY[idx][1] * (mult - 1)); + } + else if (mMap->getWalk(mX + movesX[idx][2], + mY + movesY[idx][2], walkMask)) + { + while (mMap->getWalk(mX + movesX[idx][2] * mult, + mY + movesY[idx][2] * mult, + getWalkMask()) && mult <= dist) + { + mult ++; + } + move(movesX[idx][2] * (mult - 1), movesY[idx][2] * (mult - 1)); + } + else if (mMap->getWalk(mX + movesX[idx][3], + mY + movesY[idx][3], walkMask)) + { + while (mMap->getWalk(mX + movesX[idx][3] * mult, + mY + movesY[idx][3] * mult, + getWalkMask()) && mult <= dist) + { + mult ++; + } + move(movesX[idx][3] * (mult - 1), movesY[idx][3] * (mult - 1)); + } +} + +void LocalPlayer::crazyMove9() +{ + int dx = 0; + int dy = 0; + + if (mAction == MOVE) + return; + + switch (mCrazyMoveState) + { + case 0: + switch (mDirection) + { + case UP : dy = -1; break; + case DOWN : dy = 1; break; + case LEFT : dx = -1; break; + case RIGHT: dx = 1; break; + default: break; + } + move(dx, dy); + mCrazyMoveState = 1; + break; + case 1: + mCrazyMoveState = 2; + if (!allowAction()) + return; + Net::getPlayerHandler()->changeAction(SIT); + break; + case 2: + mCrazyMoveState = 3; + break; + case 3: + mCrazyMoveState = 0; + break; + default: + break; + } +} + +void LocalPlayer::crazyMoveA() +{ + const std::string mMoveProgram(config.getStringValue("crazyMoveProgram")); + + if (mAction == MOVE) + return; + + if (mMoveProgram.empty()) + return; + + if (mCrazyMoveState >= mMoveProgram.length()) + mCrazyMoveState = 0; + + // move command + if (mMoveProgram[mCrazyMoveState] == 'm') + { + mCrazyMoveState ++; + if (mCrazyMoveState < mMoveProgram.length()) + { + int dx = 0; + int dy = 0; + + signed char param = mMoveProgram[mCrazyMoveState++]; + if (param == '?') + { + const char cmd[] = {'l', 'r', 'u', 'd'}; + srand(tick_time); + param = cmd[rand() % 4]; + } + switch (param) + { + case 'd': + move(0, 1); + break; + case 'u': + move(0, -1); + break; + case 'l': + move(-1, 0); + break; + case 'r': + move(1, 0); + break; + case 'f': + switch (mDirection) + { + case UP : dy = -1; break; + case DOWN : dy = 1; break; + case LEFT : dx = -1; break; + case RIGHT: dx = 1; break; + default: break; + } + move(dx, dy); + break; + case 'b': + switch (mDirection) + { + case UP : dy = 1; break; + case DOWN : dy = -1; break; + case LEFT : dx = 1; break; + case RIGHT: dx = -1; break; + default: break; + } + move(dx, dy); + break; + default: + break; + } + } + } + // direction command + else if (mMoveProgram[mCrazyMoveState] == 'd') + { + mCrazyMoveState ++; + + if (mCrazyMoveState < mMoveProgram.length()) + { + signed char param = mMoveProgram[mCrazyMoveState++]; + if (param == '?') + { + const char cmd[] = {'l', 'r', 'u', 'd'}; + srand(tick_time); + param = cmd[rand() % 4]; + } + switch (param) + { + case 'd': + +// if (client->limitPackets(PACKET_DIRECTION)) + { + setDirection(Being::DOWN); + Net::getPlayerHandler()->setDirection(Being::DOWN); + } + break; + case 'u': +// if (client->limitPackets(PACKET_DIRECTION)) + { + setDirection(Being::UP); + Net::getPlayerHandler()->setDirection(Being::UP); + } + break; + case 'l': +// if (client->limitPackets(PACKET_DIRECTION)) + { + setDirection(Being::LEFT); + Net::getPlayerHandler()->setDirection(Being::LEFT); + } + break; + case 'r': +// if (client->limitPackets(PACKET_DIRECTION)) + { + setDirection(Being::RIGHT); + Net::getPlayerHandler()->setDirection(Being::RIGHT); + } + break; + case 'L': +// if (client->limitPackets(PACKET_DIRECTION)) + { + uint8_t dir = 0; + switch (mDirection) + { + case UP : dir = Being::LEFT; break; + case DOWN : dir = Being::RIGHT; break; + case LEFT : dir = Being::DOWN; break; + case RIGHT : dir = Being::UP; break; + default: break; + } + setDirection(dir); + Net::getPlayerHandler()->setDirection(dir); + } + break; + case 'R': +// if (client->limitPackets(PACKET_DIRECTION)) + { + uint8_t dir = 0; + switch (mDirection) + { + case UP : dir = Being::RIGHT; break; + case DOWN : dir = Being::LEFT; break; + case LEFT : dir = Being::UP; break; + case RIGHT : dir = Being::DOWN; break; + default: break; + } + setDirection(dir); + Net::getPlayerHandler()->setDirection(dir); + } + break; + case 'b': +// if (client->limitPackets(PACKET_DIRECTION)) + { + uint8_t dir = 0; + switch (mDirection) + { + case UP : dir = Being::DOWN; break; + case DOWN : dir = Being::UP; break; + case LEFT : dir = Being::RIGHT; break; + case RIGHT : dir = Being::LEFT; break; + default: break; + } + setDirection(dir); + Net::getPlayerHandler()->setDirection(dir); + } + break; + case '0': + dropShortcut->dropFirst(); + break; + case 'a': + dropShortcut->dropItems(); + break; + default: + break; + } + } + } + // sit command + else if (mMoveProgram[mCrazyMoveState] == 's') + { + mCrazyMoveState ++; + if (toggleSit()) + mCrazyMoveState ++; + } + // wear outfits + else if (mMoveProgram[mCrazyMoveState] == 'o') + { + mCrazyMoveState ++; + if (mCrazyMoveState < mMoveProgram.length()) + { + // wear next outfit + if (mMoveProgram[mCrazyMoveState] == 'n') + { + mCrazyMoveState ++; + outfitWindow->wearNextOutfit(); + } + // wear previous outfit + else if (mMoveProgram[mCrazyMoveState] == 'p') + { + mCrazyMoveState ++; + outfitWindow->wearPreviousOutfit(); + } + } + } + // pause + else if (mMoveProgram[mCrazyMoveState] == 'w') + { + mCrazyMoveState ++; + } + // pick up + else if (mMoveProgram[mCrazyMoveState] == 'p') + { + mCrazyMoveState ++; + pickUpItems(); + } + // emote + else if (mMoveProgram[mCrazyMoveState] == 'e') + { + mCrazyMoveState ++; + const signed char emo = mMoveProgram[mCrazyMoveState]; + if (emo == '?') + { + srand(tick_time); + emote(static_cast(1 + (rand() % 13))); + } + else + { + if (emo >= '0' && emo <= '9') + emote(static_cast(emo - '0' + 1)); + else if (emo >= 'a' && emo <= 'd') + emote(static_cast(emo - 'a' + 11)); + } + + mCrazyMoveState ++; + } + else + { + mCrazyMoveState ++; + } + + if (mCrazyMoveState >= mMoveProgram.length()) + mCrazyMoveState = 0; +} + +bool LocalPlayer::isReachable(Being *const being, + const int maxCost) +{ + if (!being || !mMap) + return false; + + if (being->isReachable() == Being::REACH_NO) + return false; + + if (being->getTileX() == mX + && being->getTileY() == mY) + { + being->setDistance(0); + being->setIsReachable(Being::REACH_YES); + return true; + } + else if (being->getTileX() - 1 <= mX + && being->getTileX() + 1 >= mX + && being->getTileY() - 1 <= mY + && being->getTileY() + 1 >= mY) + { + being->setDistance(1); + being->setIsReachable(Being::REACH_YES); + return true; + } + + const Vector &playerPos = getPosition(); + + const Path debugPath = mMap->findPath( + static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + being->getTileX(), being->getTileY(), getWalkMask(), maxCost); + + being->setDistance(static_cast(debugPath.size())); + if (!debugPath.empty()) + { + being->setIsReachable(Being::REACH_YES); + return true; + } + else + { + being->setIsReachable(Being::REACH_NO); + return false; + } +} + +bool LocalPlayer::isReachable(const int x, const int y, + const bool allowCollision) const +{ + const WalkLayer *const walk = mMap->getWalkLayer(); + if (!walk) + return false; + int num = walk->getDataAt(x, y); + if (allowCollision && num < 0) + num = -num; + + return walk->getDataAt(mX, mY) == num; +} + +bool LocalPlayer::pickUpItems(int pickUpType) +{ + if (!actorSpriteManager) + return false; + + bool status = false; + int x = mX; + int y = mY; + + // first pick up item on player position + FloorItem *item = + actorSpriteManager->findItem(x, y); + if (item) + status = pickUp(item); + + if (pickUpType == 0) + pickUpType = mPickUpType; + + if (pickUpType == 0) + return status; + + int x1, y1, x2, y2; + switch (pickUpType) + { + case 1: + switch (mDirection) + { + case UP : --y; break; + case DOWN : ++y; break; + case LEFT : --x; break; + case RIGHT: ++x; break; + default: break; + } + item = actorSpriteManager->findItem(x, y); + if (item) + status = pickUp(item); + break; + case 2: + switch (mDirection) + { + case UP : x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y; break; + case DOWN : x1 = x - 1; y1 = y; x2 = x + 1; y2 = y + 1; break; + case LEFT : x1 = x - 1; y1 = y - 1; x2 = x; y2 = y + 1; break; + case RIGHT: x1 = x; y1 = y - 1; x2 = x + 1; y2 = y + 1; break; + default: x1 = x; x2 = x; y1 = y; y2 = y; break; + } + if (actorSpriteManager->pickUpAll(x1, y1, x2, y2)) + status = true; + break; + case 3: + if (actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) + status = true; + break; + + case 4: + if (!actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) + { + if (actorSpriteManager->pickUpNearest(x, y, 4)) + status = true; + } + else + { + status = true; + } + break; + + case 5: + if (!actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) + { + if (actorSpriteManager->pickUpNearest(x, y, 8)) + status = true; + } + else + { + status = true; + } + break; + + case 6: + if (!actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) + { + if (actorSpriteManager->pickUpNearest(x, y, 90)) + status = true; + } + else + { + status = true; + } + break; + + default: + break; + } + return status; +} + + +void LocalPlayer::moveByDirection(const unsigned char dir) +{ + int dx = 0, dy = 0; +#ifdef MANASERV_SUPPORT + if (dir & UP) + dy -= 32; + if (dir & DOWN) + dy += 32; + if (dir & LEFT) + dx -= 32; + if (dir & RIGHT) + dx += 32; +#else + if (dir & UP) + dy--; + if (dir & DOWN) + dy++; + if (dir & LEFT) + dx--; + if (dir & RIGHT) + dx++; +#endif + + move(dx, dy); +} + +void LocalPlayer::specialMove(const unsigned char direction) +{ + if (direction && (mNavigateX || mNavigateY)) + navigateClean(); + + if (direction && (mInvertDirection >= 2 + && mInvertDirection <= 4) + && !mIsServerBuggy) + { + if (mAction == MOVE) + return; + + int max; + + if (mInvertDirection == 2) + max = 5; + else if (mInvertDirection == 4) + max = 1; + else + max = 3; + + if (getMoveState() < max) + { + moveByDirection(direction); + mMoveState ++; + } + else + { + mMoveState = 0; + crazyMove(); + } + } + else + { + setWalkingDir(direction); + } +} + +void LocalPlayer::debugMsg(const std::string &str) const +{ + if (debugChatTab) + debugChatTab->chatLog(str); +} + +void LocalPlayer::magicAttack() const +{ + if (!chatWindow || !isAlive() + || !Net::getPlayerHandler()->canUseMagic()) + { + return; + } + + switch (mMagicAttackType) + { + // flar W00 + case 0: + tryMagic("#flar", 1, 0, 10); + break; + // chiza W01 + case 1: + tryMagic("#chiza", 1, 0, 9); + break; + // ingrav W10 + case 2: + tryMagic("#ingrav", 2, 2, 20); + break; + // frillyar W11 + case 3: + tryMagic("#frillyar", 2, 2, 25); + break; + // upmarmu W12 + case 4: + tryMagic("#upmarmu", 2, 2, 20); + break; + default: + break; + } +} + +void LocalPlayer::tryMagic(const std::string &spell, const int baseMagic, + const int schoolMagic, const int mana) +{ + if (!chatWindow) + return; + + if (PlayerInfo::getSkillLevel(340) >= baseMagic + && PlayerInfo::getSkillLevel(342) >= schoolMagic) + { + if (PlayerInfo::getAttribute(PlayerInfo::MP) >= mana) + { + if (!client->limitPackets(PACKET_CHAT)) + return; + + chatWindow->localChatInput(spell); + } + } +} + +void LocalPlayer::loadHomes() +{ + if (serverVersion > 0) + return; + std::string buf; + std::stringstream ss(serverConfig.getValue("playerHomes", + "maps/018-1.tmx 71 76 maps/013-3.tmx 71 24")); + + while (ss >> buf) + { + Vector pos; + ss >> pos.x; + ss >> pos.y; + mHomes[buf] = pos; + } +} + +void LocalPlayer::setMap(Map *const map) +{ + if (map) + { + if (socialWindow) + socialWindow->updateActiveList(); + } + navigateClean(); + mCrossX = 0; + mCrossY = 0; + + Being::setMap(map); + updateNavigateList(); +} + +void LocalPlayer::setHome() +{ + if (!mMap || !socialWindow) + return; + + SpecialLayer *const specialLayer = mMap->getSpecialLayer(); + + if (!specialLayer) + return; + + const std::string key = mMap->getProperty("_realfilename"); + Vector pos = mHomes[key]; + + if (mAction == SIT) + { + const std::map::const_iterator + iter = mHomes.find(key); + + if (iter != mHomes.end()) + { + socialWindow->removePortal(static_cast(pos.x), + static_cast(pos.y)); + } + + if (iter != mHomes.end() && mX == static_cast(pos.x) + && mY == static_cast(pos.y)) + { + mMap->updatePortalTile("", MapItem::EMPTY, + static_cast(pos.x), static_cast(pos.y)); + + mHomes.erase(key); + socialWindow->removePortal(static_cast(pos.x), + static_cast(pos.y)); + } + else + { + if (specialLayer && iter != mHomes.end()) + { + specialLayer->setTile(static_cast(pos.x), + static_cast(pos.y), MapItem::EMPTY); + } + + pos.x = static_cast(mX); + pos.y = static_cast(mY); + mHomes[key] = pos; + mMap->updatePortalTile("home", MapItem::HOME, + mX, mY); + socialWindow->addPortal(mX, mY); + } + MapItem *const mapItem = specialLayer->getTile(mX, mY); + if (mapItem) + { + const int idx = socialWindow->getPortalIndex(mX, mY); + mapItem->setName(keyboard.getKeyShortString( + outfitWindow->keyName(idx))); + } + saveHomes(); + } + else + { + MapItem *mapItem = specialLayer->getTile(mX, mY); + int type = 0; + + const std::map::iterator iter = mHomes.find(key); + if (iter != mHomes.end() && mX == pos.x && mY == pos.y) + { + mHomes.erase(key); + saveHomes(); + } + + if (!mapItem || mapItem->getType() == MapItem::EMPTY) + { + if (mDirection & UP) + type = MapItem::ARROW_UP; + else if (mDirection & LEFT) + type = MapItem::ARROW_LEFT; + else if (mDirection & DOWN) + type = MapItem::ARROW_DOWN; + else if (mDirection & RIGHT) + type = MapItem::ARROW_RIGHT; + } + else + { + type = MapItem::EMPTY; + } + mMap->updatePortalTile("", type, mX, mY); + + if (type != MapItem::EMPTY) + { + socialWindow->addPortal(mX, mY); + mapItem = specialLayer->getTile(mX, mY); + if (mapItem) + { + const int idx = socialWindow->getPortalIndex(mX, mY); + mapItem->setName(keyboard.getKeyShortString( + outfitWindow->keyName(idx))); + } + } + else + { + specialLayer->setTile(mX, mY, MapItem::EMPTY); + socialWindow->removePortal(mX, mY); + } + } +} + +void LocalPlayer::saveHomes() +{ + std::stringstream ss; + + for (std::map::const_iterator iter = mHomes.begin(), + iter_end = mHomes.end(); iter != iter_end; ++iter ) + { + const Vector &pos = (*iter).second; + + if (iter != mHomes.begin()) + ss << " "; + ss << (*iter).first << " " << pos.x << " " << pos.y; + } + + serverConfig.setValue("playerHomes", ss.str()); +} + +void LocalPlayer::pingRequest() +{ + const int time = tick_time; + if (mWaitPing == true && mPingSendTick != 0) + { + if (time >= mPingSendTick && (time - mPingSendTick) > 1000) + return; + } + + mPingSendTick = time; + mWaitPing = true; + Net::getBeingHandler()->requestNameById(getId()); +} + +std::string LocalPlayer::getPingTime() const +{ + std::string str; + if (!mWaitPing) + { + if (!mPingTime) + str = "?"; + else + str = toString(mPingTime); + } + else + { + int time = tick_time; + if (time > mPingSendTick) + time -= mPingSendTick; + else + time += MAX_TICK_VALUE - mPingSendTick; + if (time <= mPingTime) + time = mPingTime; + if (mPingTime != time) + str = strprintf("%d (%d)", mPingTime, time); + else + str = toString(time); + } + return str; +} + +void LocalPlayer::pingResponse() +{ + if (mWaitPing == true && mPingSendTick > 0) + { + mWaitPing = false; + const int time = tick_time; + if (time < mPingSendTick) + { + mPingSendTick = 0; + mPingTime = 0; + } + else + { + mPingTime = (time - mPingSendTick) * 10; + } + } +} + +void LocalPlayer::tryPingRequest() +{ + if (mPingSendTick == 0 || tick_time < mPingSendTick + || (tick_time - mPingSendTick) > 200) + { + pingRequest(); + } +} + + +void LocalPlayer::setAway(const std::string &message) +{ + if (!message.empty()) + config.setValue("afkMessage", message); + changeAwayMode(); + updateStatus(); +} + +void LocalPlayer::setPseudoAway(const std::string &message) +{ + if (!message.empty()) + config.setValue("afkMessage", message); + mPseudoAwayMode = !mPseudoAwayMode; +} + +void LocalPlayer::afkRespond(ChatTab *const tab, const std::string &nick) +{ + if (mAwayMode) + { + const int time = cur_time; + if (mAfkTime == 0 || time < mAfkTime + || time - mAfkTime > awayLimitTimer) + { + const std::string msg = "*AFK*: " + + config.getStringValue("afkMessage"); + + if (!tab) + { + Net::getChatHandler()->privateMessage(nick, msg); + if (localChatTab) + { + localChatTab->chatLog(std::string(getName()).append( + " : ").append(msg), ACT_WHISPER, false); + } + } + else + { + if (tab->getNoAway()) + return; + Net::getChatHandler()->privateMessage(nick, msg); + tab->chatLog(getName(), msg); + } + mAfkTime = time; + } + } +} + +bool LocalPlayer::navigateTo(const int x, const int y) +{ + if (!mMap) + return false; + + SpecialLayer *const tmpLayer = mMap->getTempLayer(); + if (!tmpLayer) + return false; + + const Vector &playerPos = getPosition(); + mShowNavigePath = true; + mOldX = static_cast(playerPos.x); + mOldY = static_cast(playerPos.y); + mOldTileX = mX; + mOldTileY = mY; + mNavigateX = x; + mNavigateY = y; + mNavigateId = 0; + + mNavigatePath = mMap->findPath( + static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + x, y, getWalkMask(), 0); + + if (mDrawPath) + tmpLayer->addRoad(mNavigatePath); + return !mNavigatePath.empty(); +} + +void LocalPlayer::navigateTo(const Being *const being) +{ + if (!mMap || !being) + return; + + SpecialLayer *const tmpLayer = mMap->getTempLayer(); + if (!tmpLayer) + return; + + const Vector &playerPos = getPosition(); + mShowNavigePath = true; + mOldX = static_cast(playerPos.x); + mOldY = static_cast(playerPos.y); + mOldTileX = mX; + mOldTileY = mY; + mNavigateX = being->getTileX(); + mNavigateY = being->getTileY(); + + mNavigatePath = mMap->findPath( + static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + being->getTileX(), being->getTileY(), + getWalkMask(), 0); + + if (mDrawPath) + tmpLayer->addRoad(mNavigatePath); +} + +void LocalPlayer::navigateClean() +{ + if (!mMap) + return; + + mShowNavigePath = false; + mOldX = 0; + mOldY = 0; + mOldTileX = 0; + mOldTileY = 0; + mNavigateX = 0; + mNavigateY = 0; + mNavigateId = 0; + + mNavigatePath.clear(); + + const SpecialLayer *const tmpLayer = mMap->getTempLayer(); + if (!tmpLayer) + return; + + tmpLayer->clean(); +} + +void LocalPlayer::updateCoords() +{ + Being::updateCoords(); + + const Vector &playerPos = getPosition(); + // probably map not loaded. + if (!playerPos.x || !playerPos.y) + return; + + if (mX != mOldTileX || mY != mOldTileY) + { + if (socialWindow) + socialWindow->updatePortals(); + if (viewport) + viewport->hideBeingPopup(); + if (mMap) + { + std::string str = mMap->getObjectData(mX, mY, + MapItem::MUSIC); + if (str.empty()) + str = mMap->getMusicFile(); + if (str != soundManager.getCurrentMusicFile()) + { + if (str.empty()) + soundManager.fadeOutMusic(); + else + soundManager.fadeOutAndPlayMusic(str); + } + } + } + + if (mShowNavigePath) + { + if (mMap && (mX != mOldTileX || mY != mOldTileY)) + { + SpecialLayer *const tmpLayer = mMap->getTempLayer(); + if (!tmpLayer) + return; + + const int x = static_cast(playerPos.x - 16) / 32; + const int y = static_cast(playerPos.y - 32) / 32; + if (mNavigateId) + { + if (!actorSpriteManager) + { + navigateClean(); + return; + } + + const Being *const being = actorSpriteManager + ->findBeing(mNavigateId); + if (!being) + { + navigateClean(); + return; + } + mNavigateX = being->getTileX(); + mNavigateY = being->getTileY(); + } + + if (mNavigateX == x && mNavigateY == y) + { + navigateClean(); + return; + } + else + { + for (Path::const_iterator i = mNavigatePath.begin(), + i_end = mNavigatePath.end(); i != i_end; ++ i) + { + if ((*i).x == mX && (*i).y == mY) + { + mNavigatePath.pop_front(); + break; + } + } + + if (mDrawPath) + { + tmpLayer->clean(); + tmpLayer->addRoad(mNavigatePath); + } + } + } + } + mOldX = static_cast(playerPos.x); + mOldY = static_cast(playerPos.y); + mOldTileX = mX; + mOldTileY = mY; +} + +void LocalPlayer::targetMoved() const +{ +/* + if (mKeepAttacking) + { + if (mTarget && mServerAttack) + { + logger->log("LocalPlayer::targetMoved0"); + if (!client->limitPackets(PACKET_ATTACK)) + return; + logger->log("LocalPlayer::targetMoved"); + Net::getPlayerHandler()->attack(mTarget->getId(), mServerAttack); + } + } +*/ +} + +int LocalPlayer::getPathLength(const Being *const being) const +{ + if (!mMap || !being) + return 0; + + const Vector &playerPos = getPosition(); + + if (being->mX == mX && being->mY == mY) + return 0; + + if (being->mX - 1 <= mX && being->mX + 1 >= mX + && being->mY - 1 <= mY && being->mY + 1 >= mY) + { + return 1; + } + + if (mTargetOnlyReachable) + { + const Path debugPath = mMap->findPath( + static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + being->getTileX(), being->getTileY(), + getWalkMask(), 0); + return static_cast(debugPath.size()); + } + else + { + const int dx = static_cast(abs(being->mX - mX)); + const int dy = static_cast(abs(being->mY - mY)); + if (dx > dy) + return dx; + return dy; + } +} + +int LocalPlayer::getAttackRange2() const +{ + int range = getAttackRange(); + if (range == 1) + range = 2; + return range; +} + +void LocalPlayer::attack2(Being *const target, const bool keep, + const bool dontChangeEquipment) +{ + if (!dontChangeEquipment && target) + changeEquipmentBeforeAttack(target); + + // probably need cache getPathLength(target) + if ((!target || mAttackType == 0 || mAttackType == 3) + || (withinAttackRange(target, serverVersion < 1, + serverVersion < 1 ? 1 : 0) + && getPathLength(target) <= getAttackRange2())) + { + attack(target, keep); + if (mAttackType == 2) + { + if (!target) + { + if (pickUpItems()) + return; + } + else + { + pickUpItems(3); + } + } + } + else if (!mPickUpTarget) + { + if (mAttackType == 2) + { + if (pickUpItems()) + return; + } + setTarget(target); + if (target && target->getType() != Being::NPC) + { + mKeepAttacking = true; + moveToTarget(); + } + } +} + +void LocalPlayer::setFollow(const std::string &player) +{ + mPlayerFollowed = player; + if (!mPlayerFollowed.empty()) + { + // TRANSLATORS: follow command message + std::string msg = strprintf(_("Follow: %s"), player.c_str()); + debugMsg(msg); + } + else + { + // TRANSLATORS: follow command message + debugMsg(_("Follow canceled")); + } +} + +void LocalPlayer::setImitate(const std::string &player) +{ + mPlayerImitated = player; + if (!mPlayerImitated.empty()) + { + // TRANSLATORS: imitate command message + std::string msg = strprintf(_("Imitation: %s"), player.c_str()); + debugMsg(msg); + } + else + { + // TRANSLATORS: imitate command message + debugMsg(_("Imitation canceled")); + } +} + +void LocalPlayer::cancelFollow() +{ + if (!mPlayerFollowed.empty()) + { + // TRANSLATORS: cancel follow message + debugMsg(_("Follow canceled")); + } + if (!mPlayerImitated.empty()) + { + // TRANSLATORS: cancel follow message + debugMsg(_("Imitation canceled")); + } + mPlayerFollowed.clear(); + mPlayerImitated.clear(); +} + +void LocalPlayer::imitateEmote(const Being *const being, + const unsigned char action) const +{ + if (!being) + return; + + std::string player_imitated = getImitate(); + if (!player_imitated.empty() && being->getName() == player_imitated) + emote(action); +} + +void LocalPlayer::imitateAction(const Being *const being, + const Being::Action &action) +{ + if (!being) + return; + + if (!mPlayerImitated.empty() && being->getName() == mPlayerImitated) + { + setAction(action); + Net::getPlayerHandler()->changeAction(action); + } +} + +void LocalPlayer::imitateDirection(const Being *const being, + const unsigned char dir) +{ + if (!being) + return; + + if (!mPlayerImitated.empty() && being->getName() == mPlayerImitated) + { + if (!client->limitPackets(PACKET_DIRECTION)) + return; + + if (mFollowMode == 2) + { + uint8_t dir2 = 0; + if (dir & Being::LEFT) + dir2 |= Being::RIGHT; + else if (dir & Being::RIGHT) + dir2 |= Being::LEFT; + if (dir & Being::UP) + dir2 |= Being::DOWN; + else if (dir & Being::DOWN) + dir2 |= Being::UP; + + setDirection(dir2); + Net::getPlayerHandler()->setDirection(dir2); + } + else + { + setDirection(dir); + Net::getPlayerHandler()->setDirection(dir); + } + } +} + +void LocalPlayer::imitateOutfit(Being *const player, const int sprite) const +{ + if (!player) + return; + + if (mImitationMode == 1 && !mPlayerImitated.empty() + && player->getName() == mPlayerImitated) + { + if (sprite < 0 || sprite >= player->getNumberOfLayers()) + return; + + const AnimatedSprite *const equipmentSprite + = dynamic_cast( + player->getSprite(sprite)); + + if (equipmentSprite) + { +// logger->log("have equipmentSprite"); + const Inventory *const inv = PlayerInfo::getInventory(); + if (!inv) + return; + + const std::string &path = equipmentSprite->getIdPath(); + if (path.empty()) + return; + +// logger->log("idPath: " + path); + const Item *const item = inv->findItemBySprite(path, + player->getGender(), player->getSubType()); + if (item && !item->isEquipped()) + Net::getInventoryHandler()->equipItem(item); + } + else + { +// logger->log("have unequip %d", sprite); + const int equipmentSlot = Net::getInventoryHandler() + ->convertFromServerSlot(sprite); +// logger->log("equipmentSlot: " + toString(equipmentSlot)); + if (equipmentSlot == EQUIP_PROJECTILE_SLOT) + return; + + const Item *const item = PlayerInfo::getEquipment(equipmentSlot); + if (item) + { +// logger->log("unequiping"); + Net::getInventoryHandler()->unequipItem(item); + } + } + } +} + +void LocalPlayer::followMoveTo(const Being *const being, + const int x, const int y) +{ + if (being && !mPlayerFollowed.empty() + && being->getName() == mPlayerFollowed) + { + mPickUpTarget = nullptr; + setDestination(x, y); + } +} + +void LocalPlayer::followMoveTo(const Being *const being, + const int x1, const int y1, + const int x2, const int y2) +{ + if (!being) + return; + + mPickUpTarget = nullptr; + if (!mPlayerFollowed.empty() && being->getName() == mPlayerFollowed) + { + switch (mFollowMode) + { + case 0: + setDestination(x1, y1); + setNextDest(x2, y2); + break; + case 1: + if (x1 != x2 || y1 != y2) + { + setDestination(mX + x2 - x1, mY + y2 - y1); + setNextDest(mX + x2 - x1, mY + y2 - y1); + } + break; + case 2: + if (x1 != x2 || y1 != y2) + { + setDestination(mX + x1 - x2, mY + y1 - y2); + setNextDest(mX + x1 - x2, mY + y1 - y2); + } + break; + case 3: + if (!mTarget || mTarget->getName() != mPlayerFollowed) + { + if (actorSpriteManager) + { + Being *const b = actorSpriteManager->findBeingByName( + mPlayerFollowed, Being::PLAYER); + setTarget(b); + } + } + moveToTarget(); + setNextDest(x2, y2); + break; + default: + break; + } + } +} + +void LocalPlayer::setNextDest(const int x, const int y) +{ + mNextDestX = x; + mNextDestY = y; +} + +bool LocalPlayer::allowAction() +{ + if (mIsServerBuggy) + { + if (mLastAction != -1) + return false; + mLastAction = tick_time; + } + return true; +} + +/* +bool LocalPlayer::allowMove() const +{ + if (mIsServerBuggy) + { + if (mAction == MOVE) + return false; + } + return true; +} +*/ + +void LocalPlayer::fixPos(const int maxDist) +{ + if (!mCrossX && !mCrossY) + return; + + const int dx = abs(mX - mCrossX); + const int dy = abs(mY - mCrossY); + const int dest = (dx * dx) + (dy * dy); + const int time = cur_time; + + if (dest > maxDist && mActivityTime + && (time < mActivityTime || time - mActivityTime > 2)) + { + mActivityTime = time; + moveTo(mCrossX, mCrossY); + } +} + +void LocalPlayer::setRealPos(const int x, const int y) +{ + if (!mMap) + return; + + SpecialLayer *const layer = mMap->getTempLayer(); + if (layer) + { + fixPos(1); + + if ((mCrossX || mCrossY) && layer->getTile(mCrossX, mCrossY) + && layer->getTile(mCrossX, mCrossY)->getType() == MapItem::CROSS) + { + layer->setTile(mCrossX, mCrossY, MapItem::EMPTY); + } + + if (!layer->getTile(x, y) + || layer->getTile(x, y)->getType() == MapItem::EMPTY) + { + if (getTileX() != x && getTileY() != y) + layer->setTile(x, y, MapItem::CROSS); + } + + mCrossX = x; + mCrossY = y; + } + if (mMap->isCustom()) + mMap->setWalk(x, y, true); +} +void LocalPlayer::fixAttackTarget() +{ + if (!mMap || !mTarget) + return; + + if (mMoveToTargetType == 7 || !mAttackType + || !config.getBoolValue("autofixPos")) + { + return; + } + + const Vector &playerPos = getPosition(); + const Path debugPath = mMap->findPath( + static_cast(playerPos.x - 16) / 32, + static_cast(playerPos.y - 32) / 32, + mTarget->getTileX(), mTarget->getTileY(), + getWalkMask(), 0); + + if (!debugPath.empty()) + { + const Path::const_iterator i = debugPath.begin(); + moveTo((*i).x, (*i).y); + } +} + +void LocalPlayer::respawn() +{ + navigateClean(); +} + +int LocalPlayer::getLevel() const +{ + return PlayerInfo::getAttribute(PlayerInfo::LEVEL); +} + +void LocalPlayer::updateNavigateList() +{ + if (mMap) + { + const std::map::const_iterator iter = + mHomes.find(mMap->getProperty("_realfilename")); + + if (iter != mHomes.end()) + { + const Vector &pos = mHomes[(*iter).first]; + if (pos.x && pos.y) + { + mMap->addPortalTile("home", MapItem::HOME, + static_cast(pos.x), static_cast(pos.y)); + } + } + } +} + +void LocalPlayer::waitFor(const std::string &nick) +{ + mWaitFor = nick; +} + +void LocalPlayer::checkNewName(Being *const being) +{ + if (!being) + return; + + const std::string nick = being->getName(); + if (being->getType() == ActorSprite::PLAYER) + { + const Guild *const guild = getGuild(); + if (guild) + { + const GuildMember *const gm = guild->getMember(nick); + if (gm) + { + const int level = gm->getLevel(); + if (level > 1 && being->getLevel() != level) + { + being->setLevel(level); + being->updateName(); + } + } + } + if (chatWindow) + { + WhisperTab *const tab = chatWindow->getWhisperTab(nick); + if (tab) + tab->setWhisperTabColors(); + } + } + + if (!mWaitFor.empty() && mWaitFor == nick) + { + // TRANSLATORS: wait player/monster message + debugMsg(strprintf(_("You see %s"), mWaitFor.c_str())); + soundManager.playGuiSound(SOUND_INFO); + mWaitFor.clear(); + } +} + +void LocalPlayer::resetYellowBar() +{ + mInvertDirection = 0; + mCrazyMoveType = config.resetIntValue("crazyMoveType"); + mMoveToTargetType = config.resetIntValue("moveToTargetType"); + mFollowMode = config.resetIntValue("followMode"); + mAttackWeaponType = config.resetIntValue("attackWeaponType"); + mAttackType = config.resetIntValue("attackType"); + mMagicAttackType = config.resetIntValue("magicAttackType"); + mPvpAttackType = config.resetIntValue("pvpAttackType"); + mQuickDropCounter = config.resetIntValue("quickDropCounter"); + mPickUpType = config.resetIntValue("pickUpType"); + if (viewport) + { + viewport->setDebugPath(0); + if (viewport->getCameraMode()) + viewport->toggleCameraMode(); + } + if (mMap) + mMap->setDebugFlags(0); + mImitationMode = config.resetIntValue("imitationMode"); + mDisableGameModifiers = config.resetBoolValue("disableGameModifiers"); + + if (miniStatusWindow) + miniStatusWindow->updateStatus(); +} + +unsigned char LocalPlayer::getWalkMask() const +{ + // for now blocking all types of collisions + return Map::BLOCKMASK_WALL | Map::BLOCKMASK_AIR | Map::BLOCKMASK_WATER; +} + +void LocalPlayer::removeHome() +{ + if (!mMap) + return; + + const std::string key = mMap->getProperty("_realfilename"); + const std::map::iterator iter = mHomes.find(key); + + if (iter != mHomes.end()) + mHomes.erase(key); +} + +void LocalPlayer::stopAdvert() +{ + mBlockAdvert = true; +} + +bool LocalPlayer::checAttackPermissions(const Being *const target) const +{ + if (!target) + return false; + + switch (mPvpAttackType) + { + case 0: + return true; + case 1: + return !(player_relations.getRelation(target->getName()) + == PlayerRelation::FRIEND); + case 2: + return player_relations.checkBadRelation(target->getName()); + default: + case 3: + return false; + } +} + + +const char *LocalPlayer::getVarItem(const char *const *const arr, + const unsigned index, + const unsigned sz) const +{ + if (index < sz) + return arr[index]; + return arr[sz]; +} + +void LocalPlayer::updateStatus() const +{ + if (serverVersion >= 4 && mEnableAdvert) + { + uint8_t status = 0; + if (mTradebot && shopWindow && !shopWindow->isShopEmpty()) + status |= FLAG_SHOP; + + if (mAwayMode || mPseudoAwayMode) + status |= FLAG_AWAY; + + if (mInactive) + status |= FLAG_INACTIVE; + + Net::getPlayerHandler()->updateStatus(status); + } +} + +void LocalPlayer::setTestParticle(const std::string &fileName, bool updateHash) +{ + mTestParticleName = fileName; + mTestParticleTime = cur_time; + if (mTestParticle) + { + mChildParticleEffects.removeLocally(mTestParticle); + mTestParticle = nullptr; + } + if (!fileName.empty()) + { + mTestParticle = particleEngine->addEffect(fileName, 0, 0, false); + controlParticle(mTestParticle); + if (updateHash) + mTestParticleHash = UpdaterWindow::getFileHash(mTestParticleName); + } +} + +void AwayListener::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "ok" && player_node && player_node->getAway()) + { + player_node->changeAwayMode(); + player_node->updateStatus(); + if (outfitWindow) + outfitWindow->unwearAwayOutfit(); + if (miniStatusWindow) + miniStatusWindow->updateStatus(); + } +} diff --git a/src/being/localplayer.h b/src/being/localplayer.h new file mode 100644 index 000000000..4f57df0d7 --- /dev/null +++ b/src/being/localplayer.h @@ -0,0 +1,652 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_LOCALPLAYER_H +#define BEING_LOCALPLAYER_H + +#include "depricatedlistener.h" + +#include "being/actorspritelistener.h" +#include "being/being.h" + +#include "gui/userpalette.h" + +#include + +#include +#include + +#include "localconsts.h" + +class ChatTab; +class FloorItem; +class ImageSet; +class Item; +class Map; +class OkDialog; + +class AwayListener final : public gcn::ActionListener +{ + public: + void action(const gcn::ActionEvent &event) override; +}; + +/** + * Reasons an item can fail to be picked up. + */ +enum +{ + PICKUP_OKAY = 0, + PICKUP_BAD_ITEM, + PICKUP_TOO_HEAVY, + PICKUP_TOO_FAR, + PICKUP_INV_FULL, + PICKUP_STACK_FULL, + PICKUP_DROP_STEAL +}; + +/** + * The local player character. + */ +class LocalPlayer final : public Being, + public ActorSpriteListener, + public DepricatedListener +{ + public: + /** + * Constructor. + */ + explicit LocalPlayer(const int id = 65535, const int subtype = 0); + + A_DELETE_COPY(LocalPlayer) + + /** + * Destructor. + */ + ~LocalPlayer(); + + virtual void logic() override; + + void slowLogic(); + + virtual void setAction(const Action &action, + const int attackType = 0) override; + + /** + * Compute the next pathnode location when walking using keyboard. + * used by nextTile(). + */ + Position getNextWalkPosition(const unsigned char dir) + const A_WARN_UNUSED; + + /** + * Adds a new tile to the path when walking. + * @note Eathena + * Also, when specified, it picks up an item at the end of a path + * or attack target. + */ + virtual void nextTile() override + { nextTile(0); } + + virtual void nextTile(unsigned char dir); + + bool pickUp(FloorItem *const item); + + /** + * Called when an ActorSprite has been destroyed. + * @param actorSprite the ActorSprite being destroyed. + */ + void actorSpriteDestroyed(const ActorSprite &actorSprite) override; + + /** + * Gets the attack range. + */ + int getAttackRange() const A_WARN_UNUSED; + + int getAttackRange2() const A_WARN_UNUSED; + + void attack(Being *const target = nullptr, const bool keep = false, + const bool dontChangeEquipment = false); + + void attack2(Being *const target = nullptr, const bool keep = false, + const bool dontChangeEquipment = false); + + void setGMLevel(const int level); + + int getGMLevel() const A_WARN_UNUSED + { return mGMLevel; } + + void stopAttack(const bool keepAttack = false); + + void untarget(); + + /** + * Returns the current target of the player. Returns 0 if no being is + * currently targeted. + */ + Being *getTarget() const A_WARN_UNUSED; + + /** + * Sets the target being of the player. + */ + void setTarget(Being *const target); + + /** + * Sets a new destination for this being to walk to. + */ + virtual void setDestination(const int x, const int y); + + /** + * Sets a new direction to keep walking in. + */ + void setWalkingDir(const unsigned char dir); + + /** + * Gets the walking direction + */ + unsigned char getWalkingDir() const A_WARN_UNUSED + { return mWalkingDir; } + + /** + * Sets going to being to attack + */ + void setGotoTarget(Being *const target); + + /** + * Returns whether the target is in range to attack + */ + bool withinAttackRange(const Being *const target, + const bool fixDistance = false, + const int addRange = 0) const A_WARN_UNUSED; + + /** + * Stops the player dead in his tracks + */ + void stopWalking(const bool sendToServer = true); + + bool toggleSit() const; + + bool updateSit() const; + + static bool emote(const uint8_t emotion); + + /** + * Shows item pickup notifications. + */ + void pickedUp(const ItemInfo &itemInfo, const int amount, + const unsigned char color, const int floorItemId, + const unsigned char fail); + + int getLevel() const override A_WARN_UNUSED; + + /** Tells that the path has been set by mouse. */ + void pathSetByMouse() + { mPathSetByMouse = true; } + + /** Tells if the path has been set by mouse. */ + bool isPathSetByMouse() const A_WARN_UNUSED + { return mPathSetByMouse; } + + int getInvertDirection() const A_WARN_UNUSED + { return mInvertDirection; } + + void setInvertDirection(const int n) + { mInvertDirection = n; } + + void invertDirection(); + + int getAttackWeaponType() const A_WARN_UNUSED + { return mAttackWeaponType; } + + int getAttackType() const A_WARN_UNUSED + { return mAttackType; } + + int getFollowMode() const A_WARN_UNUSED + { return mFollowMode; } + + int getImitationMode() const A_WARN_UNUSED + { return mImitationMode; } + + void changeAttackWeaponType(); + + void changeAttackType(); + + void changeFollowMode(); + + void changeImitationMode(); + + void changePickUpType(); + + int getCrazyMoveType() const A_WARN_UNUSED + { return mCrazyMoveType; } + + int getPickUpType() const A_WARN_UNUSED + { return mPickUpType; } + + int getQuickDropCounter() const A_WARN_UNUSED + { return mQuickDropCounter; } + + void setQuickDropCounter(const int n); + + void changeQuickDropCounter(); + + int getMoveState() const A_WARN_UNUSED + { return mMoveState; } + + void setMoveState(const int n) + { mMoveState = n; } + + void switchMagicAttack(); + + void switchPvpAttack(); + + int getMagicAttackType() const A_WARN_UNUSED + { return mMagicAttackType; } + + int getPvpAttackType() const A_WARN_UNUSED + { return mPvpAttackType; } + + int getMoveToTargetType() const A_WARN_UNUSED + { return mMoveToTargetType; } + + int getDisableGameModifiers() const A_WARN_UNUSED + { return mDisableGameModifiers; } + + std::string getPingTime() const A_WARN_UNUSED; + + void tryPingRequest(); + + void changeMoveToTargetType(); + + void switchGameModifiers(); + + void magicAttack() const; + + void specialMove(const unsigned char direction); + + void moveByDirection(const unsigned char dir); + + bool pickUpItems(int pickUpType = 0); + + void changeCrazyMoveType(); + + void crazyMove(); + + void moveTo(const int x, const int y); + + void move(const int dX, const int dY); + + void moveToTarget(int dist = -1); + + void moveToHome(); + + void debugMsg(const std::string &str) const; + + bool isReachable(Being *const being, + const int maxCost = 0) A_WARN_UNUSED; + + bool isReachable(const int x, const int y, + const bool allowCollision) const A_WARN_UNUSED; + + void setHome(); + + void pingRequest(); + + void pingResponse(); + + void changeAwayMode(); + + void setAway(const std::string &message); + + void setPseudoAway(const std::string &message); + + bool getAway() const A_WARN_UNUSED + { return mAwayMode; } + + bool getPseudoAway() const A_WARN_UNUSED + { return mPseudoAwayMode; } + + void setHalfAway(const bool n) + { mInactive = n; } + + bool getHalfAway() const A_WARN_UNUSED + { return mInactive; } + + void afkRespond(ChatTab *const tab, const std::string &nick); + + bool navigateTo(const int x, const int y); + + void navigateTo(const Being *const being); + + void navigateClean(); + + void imitateEmote(const Being *const being, + const unsigned char emote) const; + + void imitateAction(const Being *const being, + const Being::Action &action); + + void imitateDirection(const Being *const being, + const unsigned char dir); + + void imitateOutfit(Being *const player, const int sprite = -1) const; + + void followMoveTo(const Being *const being, const int x, const int y); + + void followMoveTo(const Being *const being, const int x1, const int y1, + const int x2, const int y2); + + bool allowAction() A_WARN_UNUSED; + + void setRealPos(const int x, const int y); + + bool isServerBuggy() const A_WARN_UNUSED + { return mIsServerBuggy; } + + void fixPos(const int maxDist = 1); + + /** + * Sets the map the being is on + */ + void setMap(Map *const map); + + void addMessageToQueue(const std::string &message, + const int color = UserPalette::EXP_INFO); + + /** + * Called when a option (set with config.addListener()) is changed + */ + void optionChanged(const std::string &value) override; + + void processEvent(Channels channel, + const DepricatedEvent &event) override; + + /** + * set a following player. + */ + void setFollow(const std::string &player); + + /** + * set an imitation player. + */ + void setImitate(const std::string &player); + + /** + * setting the next destination of the following, in case of warp + */ + void setNextDest(const int x, const int y); + + int getNextDestX() const A_WARN_UNUSED + { return mNextDestX; } + + int getNextDestY() const A_WARN_UNUSED + { return mNextDestY; } + + void respawn(); + + const FloorItem *getPickUpTarget() const A_WARN_UNUSED + { return mPickUpTarget; } + + void unSetPickUpTarget() + { mPickUpTarget = nullptr; } + + /** + * Stop following a player. + */ + void cancelFollow(); + + /** + * Get the playername followed by the current player. + */ + const std::string &getFollow() const A_WARN_UNUSED + { return mPlayerFollowed; } + + /** + * Get the playername imitated by the current player. + */ + const std::string &getImitate() const A_WARN_UNUSED + { return mPlayerImitated; } + + /** + * Tells the engine whether to check + * if the Player Name is to be displayed. + */ + void setCheckNameSetting(const bool checked) + { mUpdateName = checked; } + + /** + * Gets if the engine has to check + * if the Player Name is to be displayed. + */ + bool getCheckNameSetting() const A_WARN_UNUSED + { return mUpdateName; } + + void fixAttackTarget(); + + void updateNavigateList(); + + int getPathLength(const Being *const being) const A_WARN_UNUSED; + + void targetMoved() const; + + void setLastHitFrom(const std::string &n) + { mLastHitFrom = n; } + + void waitFor(const std::string &nick); + + void checkNewName(Being *const being); + + void resetYellowBar(); + + unsigned char getWalkMask() const override A_WARN_UNUSED; + + void saveHomes(); + + void removeHome(); + + void stopAdvert(); + + bool checAttackPermissions(const Being *const target) + const A_WARN_UNUSED; + + void updateStatus() const; + + void setTestParticle(const std::string &fileName, + bool updateHash = true); + + std::string getInvertDirectionString(); + + std::string getCrazyMoveTypeString(); + + std::string getMoveToTargetTypeString(); + + std::string getFollowModeString(); + + std::string getAttackWeaponTypeString(); + + std::string getAttackTypeString(); + + std::string getQuickDropCounterString(); + + std::string getPickUpTypeString(); + + std::string getDebugPathString() const; + + std::string getMagicAttackString(); + + std::string getPvpAttackString(); + + std::string getImitationModeString(); + + std::string getAwayModeString(); + + std::string getCameraModeString() const; + + std::string getGameModifiersString(); + + protected: + void updateCoords() override; + + + virtual void handleStatusEffect(StatusEffect *const effect, + const int effectId); + + void startWalking(const unsigned char dir); + + void changeEquipmentBeforeAttack(const Being *const target) const; + + static void tryMagic(const std::string &spell, const int baseMagic, + const int schoolMagic, const int mana); + + const char *getVarItem(const char *const *const arr, + const unsigned index, + const unsigned sz) const A_WARN_UNUSED; + + void changeMode(unsigned *const var, const unsigned limit, + const char *const conf, + std::string (LocalPlayer::*const func)(), + const unsigned def = 0, + const bool save = true); + + void crazyMove1(); + void crazyMove2(); + void crazyMove3(); + void crazyMove4(); + void crazyMove5(); + void crazyMove6(); + void crazyMove7(); + void crazyMove8(); + void crazyMove9(); + void crazyMoveA(); + + void loadHomes(); + + int mGMLevel; + + // move type + unsigned int mInvertDirection; + // crazy move type + unsigned int mCrazyMoveType; + // crazy move state + unsigned int mCrazyMoveState; + // attack weapon type + unsigned int mAttackWeaponType; + // quick drop counter + unsigned int mQuickDropCounter; + // move state. used if mInvertDirection == 2 + unsigned int mMoveState; + // pick up type 1x1, normal aka 2x1, forward aka 2x3, 3x3, 3x3 + 1 + unsigned int mPickUpType; + // magic attack type + unsigned int mMagicAttackType; + // pvp attack type + unsigned int mPvpAttackType; + // type how move to target + unsigned int mMoveToTargetType; + unsigned int mAttackType; + unsigned int mFollowMode; + unsigned int mImitationMode; + + int mLastTargetX; + int mLastTargetY; + + std::map mHomes; + + Being *mTarget; + + /** Follow system **/ + std::string mPlayerFollowed; + std::string mPlayerImitated; + int mNextDestX; + int mNextDestY; + + FloorItem *mPickUpTarget; + + int mLastAction; // Time stamp of the last action, -1 if none. + + std::vector mStatusEffectIcons; + + int mLocalWalkTime; // Timestamp used to control keyboard walk + // messages flooding + + typedef std::pair MessagePair; + /** Queued messages*/ + std::list mMessages; + int mMessageTime; + AwayListener *mAwayListener; + OkDialog *mAwayDialog; + + int mPingSendTick; + int mPingTime; + int mAfkTime; + int mActivityTime; + int mNavigateX; + int mNavigateY; + int mNavigateId; + int mCrossX; + int mCrossY; + int mOldX; + int mOldY; + int mOldTileX; + int mOldTileY; + Path mNavigatePath; + + std::string mLastHitFrom; + std::string mWaitFor; + int mAdvertTime; + Particle *mTestParticle; + std::string mTestParticleName; + int mTestParticleTime; + unsigned long mTestParticleHash; + unsigned char mWalkingDir; // The direction the player is walking in. + /** Whether or not the name settings have changed */ + bool mUpdateName; + bool mBlockAdvert; + bool mTargetDeadPlayers; + bool mServerAttack; + bool mEnableAdvert; + bool mTradebot; + bool mTargetOnlyReachable; + bool mDisableGameModifiers; + bool mIsServerBuggy; + bool mSyncPlayerMove; + bool mDrawPath; + bool mAttackMoving; + bool mAttackNext; + bool mShowJobExp; + bool mNextStep; + // temporary disable crazy moves in moves + bool mDisableCrazyMove; + bool mGoingToTarget; + // Whether or not to continue to attack + bool mKeepAttacking; + // Tells if the path was set using mouse + bool mPathSetByMouse; + bool mWaitPing; + bool mAwayMode; + bool mPseudoAwayMode; + bool mShowNavigePath; +}; + +extern LocalPlayer *player_node; + +#endif // BEING_LOCALPLAYER_H diff --git a/src/being/playerinfo.cpp b/src/being/playerinfo.cpp new file mode 100644 index 000000000..9abd617a5 --- /dev/null +++ b/src/being/playerinfo.cpp @@ -0,0 +1,398 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/playerinfo.h" + +#include "client.h" +#include "configuration.h" +#include "depricatedevent.h" +#include "inventory.h" +#include "depricatedlistener.h" +#include "logger.h" + +#include "gui/inventorywindow.h" +#include "gui/npcdialog.h" +#include "gui/npcpostdialog.h" + +#include "resources/iteminfo.h" + +#include "net/inventoryhandler.h" +#include "net/playerhandler.h" + +#include "debug.h" + +namespace PlayerInfo +{ + +PlayerInfoBackend mData; +int mCharId = 0; + +Inventory *mInventory = nullptr; +Equipment *mEquipment = nullptr; + +#ifdef MANASERV_SUPPORT +std::map mSpecials; +signed char mSpecialRechargeUpdateNeeded = 0; +#endif + +bool mTrading = false; +int mLevelProgress = 0; +std::set mProtectedItems; + +// --- Triggers --------------------------------------------------------------- + +void triggerAttr(const int id, const int old) +{ + DepricatedEvent event(EVENT_UPDATEATTRIBUTE); + event.setInt("id", id); + event.setInt("oldValue", old); + event.setInt("newValue", mData.mAttributes.find(id)->second); + DepricatedEvent::trigger(CHANNEL_ATTRIBUTES, event); +} + +void triggerStat(const int id, const std::string &changed, + const int old1, const int old2) +{ + const StatMap::const_iterator it = mData.mStats.find(id); + if (it == mData.mStats.end()) + return; + + DepricatedEvent event(EVENT_UPDATESTAT); + event.setInt("id", id); + const Stat &stat = it->second; + event.setInt("base", stat.base); + event.setInt("mod", stat.mod); + event.setInt("exp", stat.exp); + event.setInt("expNeeded", stat.expNeed); + event.setString("changed", changed); + event.setInt("oldValue1", old1); + event.setInt("oldValue2", old2); + DepricatedEvent::trigger(CHANNEL_ATTRIBUTES, event); +} + +// --- Attributes ------------------------------------------------------------- + +int getAttribute(const int id) +{ + const IntMap::const_iterator it = mData.mAttributes.find(id); + if (it != mData.mAttributes.end()) + return it->second; + else + return 0; +} + +void setAttribute(const int id, const int value, const bool notify) +{ + const int old = mData.mAttributes[id]; + mData.mAttributes[id] = value; + if (notify) + triggerAttr(id, old); +} + +int getSkillLevel(const int id) +{ + const IntMap::const_iterator it = mData.mSkills.find(id); + if (it != mData.mSkills.end()) + return it->second; + else + return 0; +} + +void setSkillLevel(const int id, const int value) +{ + mData.mSkills[id] = value; +} + +// --- Stats ------------------------------------------------------------------ + +int getStatBase(const int id) +{ + const StatMap::const_iterator it = mData.mStats.find(id); + if (it != mData.mStats.end()) + return it->second.base; + else + return 0; +} + +void setStatBase(const int id, const int value, const bool notify) +{ + const int old = mData.mStats[id].base; + mData.mStats[id].base = value; + if (notify) + triggerStat(id, "base", old); +} + +int getStatMod(const int id) +{ + const StatMap::const_iterator it = mData.mStats.find(id); + if (it != mData.mStats.end()) + return it->second.mod; + else + return 0; +} + +void setStatMod(const int id, const int value, const bool notify) +{ + const int old = mData.mStats[id].mod; + mData.mStats[id].mod = value; + if (notify) + triggerStat(id, "mod", old); +} + +int getStatEffective(const int id) +{ + const StatMap::const_iterator it = mData.mStats.find(id); + if (it != mData.mStats.end()) + return it->second.base + it->second.mod; + else + return 0; +} + +const std::pair getStatExperience(const int id) +{ + const StatMap::const_iterator it = mData.mStats.find(id); + int a, b; + if (it != mData.mStats.end()) + { + a = it->second.exp; + b = it->second.expNeed; + } + else + { + a = 0; + b = 0; + } + return std::pair(a, b); +} + +void setStatExperience(const int id, const int have, + const int need, const bool notify) +{ + Stat &stat = mData.mStats[id]; + + const int oldExp = stat.exp; + const int oldExpNeed = stat.expNeed; + stat.exp = have; + stat.expNeed = need; + if (notify) + triggerStat(id, "exp", oldExp, oldExpNeed); +} + +// --- Inventory / Equipment -------------------------------------------------- + +Inventory *getInventory() +{ + return mInventory; +} + +Inventory *getStorageInventory() +{ + return Net::getInventoryHandler()->getStorage(); +} + +void clearInventory() +{ + if (mEquipment) + mEquipment->clear(); + if (mInventory) + mInventory->clear(); +} + +void setInventoryItem(const int index, const int id, + const int amount, const int refine) +{ + bool equipment = false; + const int itemType = ItemDB::get(id).getType(); + if (itemType != ITEM_UNUSABLE && itemType != ITEM_USABLE) + equipment = true; + if (mInventory) + mInventory->setItem(index, id, amount, refine, equipment); +} + +Equipment *getEquipment() +{ + return mEquipment; +} + +Item *getEquipment(const unsigned int slot) +{ + if (mEquipment) + return mEquipment->getEquipment(slot); + else + return nullptr; +} + +void setEquipmentBackend(Equipment::Backend *const backend) +{ + if (mEquipment) + mEquipment->setBackend(backend); +} + +// --- Misc ------------------------------------------------------------------- + +void setBackend(const PlayerInfoBackend &backend) +{ + mData = backend; +} + +void setCharId(const int charId) +{ + mCharId = charId; +} + +int getCharId() +{ + return mCharId; +} + +void logic() +{ +#ifdef MANASERV_SUPPORT + if ((mSpecialRechargeUpdateNeeded % 11) == 0) + { + mSpecialRechargeUpdateNeeded = 0; + FOR_EACH (SpecialsMap::iterator, it, mSpecials) + { + Special &special = it->second; + special.currentMana += special.recharge; + if (special.currentMana > special.neededMana) + special.currentMana = special.neededMana; + } + } + mSpecialRechargeUpdateNeeded++; +#endif +} + +bool isTrading() +{ + return mTrading; +} + +void setTrading(const bool trading) +{ + mTrading = trading; +} + +void updateAttrs() +{ + const Net::PlayerHandler *const handler = Net::getPlayerHandler(); + if (!handler) + return; + const int attr = handler->getAttackLocation(); + const int attackDelay = getStatBase(ATTACK_DELAY); + if (attr != -1 && attackDelay) + { + setStatBase(ATTACK_SPEED, getStatBase(attr) * 1000 + / attackDelay, false); + setStatMod(ATTACK_SPEED, getStatMod(attr) * 1000 + / attackDelay, true); + } + else + { + setStatBase(ATTACK_SPEED, 0, false); + setStatMod(ATTACK_SPEED, 0, true); + } +} + +void init() +{ +} + +void deinit() +{ + clearInventory(); +} + +void loadData() +{ + mProtectedItems.clear(); + splitToIntSet(mProtectedItems, + serverConfig.getStringValue("protectedItems"), ','); +} + +void clear() +{ + mData.mSkills.clear(); +} + +bool isTalking() +{ + return NpcDialog::isActive() || NpcPostDialog::isActive() + || InventoryWindow::isStorageActive(); +} + +void gameDestroyed() +{ + delete mInventory; + mInventory = nullptr; + delete mEquipment; + mEquipment = nullptr; +} + +void stateChange(const int state) +{ + if (state == STATE_GAME) + { + if (!mInventory) + { + mInventory = new Inventory(Inventory::INVENTORY); + mEquipment = new Equipment(); + } + } +} + +static void saveProtectedItems() +{ + std::string str; + std::set::const_iterator it = mProtectedItems.begin(); + std::set::const_iterator it_end = mProtectedItems.end(); + if (it != it_end) + { + str.append(toString(*it)); + ++ it; + } + while (it != it_end) + { + str.append(",").append(toString(*it)); + ++ it; + } + serverConfig.setValue("protectedItems", str); + serverConfig.write(); +} + +void protectItem(const int id) +{ + mProtectedItems.insert(id); + saveProtectedItems(); +} + +void unprotectItem(const int id) +{ + mProtectedItems.erase(id); + saveProtectedItems(); +} + +bool isItemProtected(const int id) +{ + return mProtectedItems.find(id) != mProtectedItems.end(); +} + +} // namespace PlayerInfo diff --git a/src/being/playerinfo.h b/src/being/playerinfo.h new file mode 100644 index 000000000..37aa9736c --- /dev/null +++ b/src/being/playerinfo.h @@ -0,0 +1,267 @@ +/* + * The ManaPlus Client + * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_PLAYERINFO_H +#define BEING_PLAYERINFO_H + +#include "equipment.h" + +#include +#include + + +/** + * Stat information storage structure. + */ +struct Stat +{ + int base; + int mod; + int exp; + int expNeed; +}; + +typedef std::map IntMap; +typedef std::map StatMap; + +/** + * Backend for core player information. + */ +struct PlayerInfoBackend final +{ + PlayerInfoBackend() : + mAttributes(), + mStats(), + mSkills() + { + } + + IntMap mAttributes; + StatMap mStats; + IntMap mSkills; +}; + +class Equipment; +class Inventory; +class Item; + +/** + * Special information storage structure. + */ +struct Special final +{ + int currentMana; + int neededMana; + int recharge; +}; + +typedef std::map SpecialsMap; + +/** + * A database like namespace which holds global info about the localplayer + * + * NOTE: 'bool notify' is used to determine if a event is to be triggered. + */ +namespace PlayerInfo +{ + /** + * Standard attributes for players. + */ + enum Attribute + { + LEVEL = 0, + HP, + MAX_HP, + MP, + MAX_MP, + EXP, + EXP_NEEDED, + MONEY, + TOTAL_WEIGHT, + MAX_WEIGHT, + SKILL_POINTS, + CHAR_POINTS, + CORR_POINTS, + ATTACK_DELAY = 100, + ATTACK_RANGE = 101, + WALK_SPEED = 102, + ATTACK_SPEED = 103 + }; + +// --- Attributes ------------------------------------------------------------- + + /** + * Returns the value of the given attribute. + */ + int getAttribute(const int id) A_WARN_UNUSED; + + /** + * Changes the value of the given attribute. + */ + void setAttribute(const int id, const int value, + const bool notify = true); + + int getSkillLevel(const int id) A_WARN_UNUSED; + + void setSkillLevel(const int id, const int value); + +// --- Stats ------------------------------------------------------------------ + + /** + * Returns the base value of the given stat. + */ + int getStatBase(const int id) A_WARN_UNUSED; + + /** + * Changes the base value of the given stat. + */ + void setStatBase(const int id, const int value, + const bool notify = true); + + /** + * Returns the modifier for the given stat. + */ + int getStatMod(const int id) A_WARN_UNUSED; + + /** + * Changes the modifier for the given stat. + */ + void setStatMod(const int id, const int value, + const bool notify = true); + + /** + * Returns the current effective value of the given stat. Effective is base + * + mod + */ + int getStatEffective(const int id) A_WARN_UNUSED; + + /** + * Changes the level of the given stat. + */ + void setStatLevel(int id, int value, bool notify = true); + + /** + * Returns the experience of the given stat. + */ + const std::pair getStatExperience(const int id) A_WARN_UNUSED; + + /** + * Changes the experience of the given stat. + */ + void setStatExperience(const int id, const int have, + const int need, const bool notify = true); + +// --- Inventory / Equipment -------------------------------------------------- + + /** + * Returns the player's inventory. + */ + Inventory *getInventory() A_WARN_UNUSED; + + Inventory *getStorageInventory() A_WARN_UNUSED; + + /** + * Clears the player's inventory and equipment. + */ + void clearInventory(); + + void clear(); + + /** + * Changes the inventory item at the given slot. + */ + void setInventoryItem(const int index, const int id, + const int amount, const int refine); + + /** + * Returns the player's equipment. + */ + Equipment *getEquipment() A_WARN_UNUSED; + + /** + * Returns the player's equipment at the given slot. + */ + Item *getEquipment(const unsigned int slot) A_WARN_UNUSED; + +// --- Misc ------------------------------------------------------------------- + + /** + * Changes the internal PlayerInfoBackend reference; + */ + void setBackend(const PlayerInfoBackend &backend); + + void setCharId(const int charId); + + int getCharId(); + + /** + * Does necessary updates every tick. + */ + void logic(); + + /** + * Returns true if the player is involved in a trade at the moment, false + * otherwise. + */ + bool isTrading(); + + /** + * Sets whether the player is currently involved in trade or not. + */ + void setTrading(const bool trading); + + void updateAttrs(); + + /** + * Initializes some internals. + */ + void init(); + + void deinit(); + + void loadData(); + + bool isTalking(); + + void gameDestroyed(); + + void stateChange(const int state); + + void triggerAttr(int id); + + void triggerAttr(const int id, const int old); + + void triggerStat(int id); + + void triggerStat(const int id, const std::string &changed, + const int old1, const int old2 = 0); + + void setEquipmentBackend(Equipment::Backend *const backend); + + void protectItem(const int id); + + void unprotectItem(const int id); + + bool isItemProtected(const int id); + +} // namespace PlayerInfo + +#endif // BEING_PLAYERINFO_H diff --git a/src/being/playerrelations.cpp b/src/being/playerrelations.cpp new file mode 100644 index 000000000..cdd9ca4bc --- /dev/null +++ b/src/being/playerrelations.cpp @@ -0,0 +1,630 @@ +/* + * The ManaPlus Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being/playerrelations.h" + +#include "actorspritemanager.h" +#include "configuration.h" + +#include "being/localplayer.h" + +#include "render/graphics.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" + +#include + +#include "debug.h" + +static const char *const PLAYER_IGNORE_STRATEGY_NOP = "nop"; +static const char *const PLAYER_IGNORE_STRATEGY_EMOTE0 = "emote0"; +static const char *const DEFAULT_IGNORE_STRATEGY + = PLAYER_IGNORE_STRATEGY_EMOTE0; + +static const char *const NAME = "name"; +static const char *const RELATION = "relation"; + +static const unsigned int IGNORE_EMOTE_TIME = 100; + +typedef std::map PlayerRelations; +typedef PlayerRelations::const_iterator PlayerRelationsCIter; +typedef std::list PlayerRelationListeners; +typedef PlayerRelationListeners::const_iterator PlayerRelationListenersCIter; + +class SortPlayersFunctor final +{ + public: + bool operator() (const std::string &str1, + const std::string &str2) const + { + std::string s1 = str1; + std::string s2 = str2; + toLower(s1); + toLower(s2); + if (s1 == s2) + return str1 < str2; + return s1 < s2; + } +} playersRelSorter; + +// (De)serialisation class +class PlayerConfSerialiser final : + public ConfigurationListManager, + std::map *> +{ +public: + virtual ConfigurationObject *writeConfigItem( + const std::pair &value, + ConfigurationObject *const cobj) const override + { + if (!cobj || !value.second) + return nullptr; + cobj->setValue(NAME, value.first); + cobj->setValue(RELATION, toString( + static_cast(value.second->mRelation))); + + return cobj; + } + + virtual std::map * + readConfigItem(const ConfigurationObject *const cobj, + std::map + *const container) const override + { + if (!cobj) + return container; + const std::string name = cobj->getValue(NAME, ""); + if (name.empty()) + return container; + + if (!(*container)[name]) + { + const int v = cobj->getValueInt(RELATION, + static_cast(PlayerRelation::NEUTRAL)); + + (*container)[name] = new PlayerRelation( + static_cast(v)); + } + // otherwise ignore the duplicate entry + + return container; + } +}; + +static PlayerConfSerialiser player_conf_serialiser; // stateless singleton + +const unsigned int PlayerRelation::RELATION_PERMISSIONS[RELATIONS_NR] = +{ + /* NEUTRAL */ 0, // we always fall back to the defaults anyway + /* FRIEND */ EMOTE | SPEECH_FLOAT | SPEECH_LOG | WHISPER | TRADE, + /* DISREGARDED*/ EMOTE | SPEECH_FLOAT, + /* IGNORED */ 0, + /* ERASED */ INVISIBLE, + /* BLACKLISTED */ SPEECH_LOG | WHISPER, + /* ENEMY2 */ EMOTE | SPEECH_FLOAT | SPEECH_LOG | WHISPER | TRADE +}; + +PlayerRelation::PlayerRelation(const Relation relation) : + mRelation(relation) +{ +} + +PlayerRelationsManager::PlayerRelationsManager() : + mPersistIgnores(false), + mDefaultPermissions(PlayerRelation::DEFAULT), + mIgnoreStrategy(nullptr), + mRelations(), + mListeners(), + mIgnoreStrategies() +{ +} + +PlayerRelationsManager::~PlayerRelationsManager() +{ + delete_all(mIgnoreStrategies); + + FOR_EACH (PlayerRelationsCIter, it, mRelations) + delete it->second; + mRelations.clear(); +} + +void PlayerRelationsManager::clear() +{ + StringVect *const names = getPlayers(); + FOR_EACHP (StringVectCIter, it, names) + removePlayer(*it); + delete names; +} + +static const char *const PERSIST_IGNORE_LIST = "persistent-player-list"; +static const char *const PLAYER_IGNORE_STRATEGY = "player-ignore-strategy"; +static const char *const DEFAULT_PERMISSIONS = "default-player-permissions"; + +int PlayerRelationsManager::getPlayerIgnoreStrategyIndex( + const std::string &name) +{ + const std::vector *const strategies + = getPlayerIgnoreStrategies(); + + if (!strategies) + return -1; + + const size_t sz = strategies->size(); + for (size_t i = 0; i < sz; i++) + { + if ((*strategies)[i]->mShortName == name) + return i; + } + + return -1; +} + +void PlayerRelationsManager::load(const bool oldConfig) +{ + Configuration *cfg; + if (oldConfig) + cfg = &config; + else + cfg = &serverConfig; + clear(); + + mPersistIgnores = cfg->getValue(PERSIST_IGNORE_LIST, 1); + mDefaultPermissions = static_cast(cfg->getValue(DEFAULT_PERMISSIONS, + mDefaultPermissions)); + + const std::string ignore_strategy_name = cfg->getValue( + PLAYER_IGNORE_STRATEGY, DEFAULT_IGNORE_STRATEGY); + const int ignore_strategy_index = getPlayerIgnoreStrategyIndex( + ignore_strategy_name); + + if (ignore_strategy_index >= 0) + { + setPlayerIgnoreStrategy((*getPlayerIgnoreStrategies()) + [ignore_strategy_index]); + } + + cfg->getList, + std::map *> + ("player", &(mRelations), &player_conf_serialiser); +} + + +void PlayerRelationsManager::init() +{ + load(); + + if (!mPersistIgnores) + { + clear(); // Yes, we still keep them around in the config file + // until the next update. + } + + FOR_EACH (PlayerRelationListenersCIter, it, mListeners) + (*it)->updateAll(); +} + +void PlayerRelationsManager::store() const +{ + serverConfig.setList::const_iterator, + std::pair, + std::map *> + ("player", mRelations.begin(), mRelations.end(), + &player_conf_serialiser); + + serverConfig.setValue(DEFAULT_PERMISSIONS, mDefaultPermissions); + serverConfig.setValue(PERSIST_IGNORE_LIST, mPersistIgnores); + serverConfig.setValue(PLAYER_IGNORE_STRATEGY, + mIgnoreStrategy ? mIgnoreStrategy->mShortName + : DEFAULT_IGNORE_STRATEGY); + + serverConfig.write(); +} + +void PlayerRelationsManager::signalUpdate(const std::string &name) +{ + FOR_EACH (PlayerRelationListenersCIter, it, mListeners) + (*it)->updatedPlayer(name); + + if (actorSpriteManager) + { + Being *const being = actorSpriteManager->findBeingByName( + name, Being::PLAYER); + + if (being && being->getType() == Being::PLAYER) + being->updateColors(); + } +} + +unsigned int PlayerRelationsManager::checkPermissionSilently( + const std::string &player_name, const unsigned int flags) const +{ + const std::map::const_iterator + it = mRelations.find(player_name); + if (it == mRelations.end()) + { + return mDefaultPermissions & flags; + } + else + { + const PlayerRelation *const r = (*it).second; + unsigned int permissions = + PlayerRelation::RELATION_PERMISSIONS[r->mRelation]; + + switch (r->mRelation) + { + case PlayerRelation::NEUTRAL: + permissions = mDefaultPermissions; + break; + + case PlayerRelation::FRIEND: + permissions |= mDefaultPermissions; // widen + break; + + case PlayerRelation::DISREGARDED: + case PlayerRelation::IGNORED: + case PlayerRelation::ERASED: + case PlayerRelation::BLACKLISTED: + case PlayerRelation::ENEMY2: + default: + permissions &= mDefaultPermissions; // narrow + } + + return permissions & flags; + } +} + +bool PlayerRelationsManager::hasPermission(const Being *const being, + const unsigned int flags) const +{ + if (!being) + return false; + + if (being->getType() == ActorSprite::PLAYER) + return hasPermission(being->getName(), flags) == flags; + return true; +} + +bool PlayerRelationsManager::hasPermission(const std::string &name, + const unsigned int flags) const +{ + if (!actorSpriteManager) + return false; + + const unsigned int rejections = flags + & ~checkPermissionSilently(name, flags); + const bool permitted = (rejections == 0); + + if (!permitted) + { + // execute `ignore' strategy, if possible + if (mIgnoreStrategy) + { + Being *const b = actorSpriteManager->findBeingByName( + name, ActorSprite::PLAYER); + + if (b && b->getType() == ActorSprite::PLAYER) + mIgnoreStrategy->ignore(b, rejections); + } + } + + return permitted; +} + +void PlayerRelationsManager::setRelation(const std::string &player_name, + const PlayerRelation::Relation + relation) +{ + if (!player_node || (relation != PlayerRelation::NEUTRAL + && player_node->getName() == player_name)) + { + return; + } + + PlayerRelation *const r = mRelations[player_name]; + if (!r) + mRelations[player_name] = new PlayerRelation(relation); + else + r->mRelation = relation; + + signalUpdate(player_name); +} + +StringVect *PlayerRelationsManager::getPlayers() const +{ + StringVect *const retval = new StringVect(); + + FOR_EACH (PlayerRelationsCIter, it, mRelations) + { + if (it->second) + retval->push_back(it->first); + } + + std::sort(retval->begin(), retval->end(), playersRelSorter); + + return retval; +} + +StringVect *PlayerRelationsManager::getPlayersByRelation( + const PlayerRelation::Relation rel) const +{ + StringVect *const retval = new StringVect(); + + FOR_EACH (PlayerRelationsCIter, it, mRelations) + { + if (it->second && it->second->mRelation == rel) + retval->push_back(it->first); + } + + std::sort(retval->begin(), retval->end(), playersRelSorter); + + return retval; +} + +void PlayerRelationsManager::removePlayer(const std::string &name) +{ + delete mRelations[name]; + mRelations.erase(name); + signalUpdate(name); +} + + +PlayerRelation::Relation PlayerRelationsManager::getRelation( + const std::string &name) const +{ + const std::map::const_iterator + it = mRelations.find(name); + if (it != mRelations.end()) + return (*it).second->mRelation; + + return PlayerRelation::NEUTRAL; +} + +//////////////////////////////////////// +// defaults + +unsigned int PlayerRelationsManager::getDefault() const +{ + return mDefaultPermissions; +} + +void PlayerRelationsManager::setDefault(const unsigned int permissions) +{ + mDefaultPermissions = permissions; + + store(); + signalUpdate(""); +} + +void PlayerRelationsManager::ignoreTrade(const std::string &name) +{ + if (name.empty()) + return; + + const PlayerRelation::Relation relation = getRelation(name); + + if (relation == PlayerRelation::IGNORED + || relation == PlayerRelation::DISREGARDED + || relation == PlayerRelation::BLACKLISTED + || relation == PlayerRelation::ERASED) + { + return; + } + else + { + player_relations.setRelation(name, PlayerRelation::BLACKLISTED); + } +} + +bool PlayerRelationsManager::checkBadRelation(const std::string &name) const +{ + if (name.empty()) + return true; + + const PlayerRelation::Relation relation = getRelation(name); + + if (relation == PlayerRelation::IGNORED + || relation == PlayerRelation::DISREGARDED + || relation == PlayerRelation::BLACKLISTED + || relation == PlayerRelation::ERASED + || relation == PlayerRelation::ENEMY2) + { + return true; + } + return false; +} + +//////////////////////////////////////// +// ignore strategies + + +class PIS_nothing final : public PlayerIgnoreStrategy +{ +public: + PIS_nothing() : + PlayerIgnoreStrategy() + { + // TRANSLATORS: ignore/unignore action + mDescription = _("Completely ignore"); + mShortName = PLAYER_IGNORE_STRATEGY_NOP; + } + + virtual void ignore(Being *const being A_UNUSED, + const unsigned int flags A_UNUSED) const override + { + } +}; + +class PIS_dotdotdot final : public PlayerIgnoreStrategy +{ +public: + PIS_dotdotdot() : + PlayerIgnoreStrategy() + { + // TRANSLATORS: ignore/unignore action + mDescription = _("Print '...'"); + mShortName = "dotdotdot"; + } + + virtual void ignore(Being *const being, + const unsigned int flags A_UNUSED) const override + { + if (!being) + return; + + logger->log("ignoring: " + being->getName()); + being->setSpeech("..."); + } +}; + + +class PIS_blinkname final : public PlayerIgnoreStrategy +{ +public: + PIS_blinkname() : + PlayerIgnoreStrategy() + { + // TRANSLATORS: ignore/unignore action + mDescription = _("Blink name"); + mShortName = "blinkname"; + } + + virtual void ignore(Being *const being, + const unsigned int flags A_UNUSED) const override + { + if (!being) + return; + + logger->log("ignoring: " + being->getName()); + being->flashName(200); + } +}; + +class PIS_emote final : public PlayerIgnoreStrategy +{ +public: + PIS_emote(const uint8_t emote_nr, const std::string &description, + const std::string &shortname) : + PlayerIgnoreStrategy(), + mEmotion(emote_nr) + { + mDescription = description; + mShortName = shortname; + } + + virtual void ignore(Being *const being, + const unsigned int flags A_UNUSED) const override + { + if (!being) + return; + + being->setEmote(mEmotion, IGNORE_EMOTE_TIME); + } + uint8_t mEmotion; +}; + +std::vector * +PlayerRelationsManager::getPlayerIgnoreStrategies() +{ + if (mIgnoreStrategies.empty()) + { + // not initialised yet? + mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE, + // TRANSLATORS: ignore strategi + _("Floating '...' bubble"), + PLAYER_IGNORE_STRATEGY_EMOTE0)); + mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE + 1, + // TRANSLATORS: ignore strategi + _("Floating bubble"), + "emote1")); + mIgnoreStrategies.push_back(new PIS_nothing); + mIgnoreStrategies.push_back(new PIS_dotdotdot); + mIgnoreStrategies.push_back(new PIS_blinkname); + } + return &mIgnoreStrategies; +} + +bool PlayerRelationsManager::isGoodName(const std::string &name) const +{ + const size_t size = name.size(); + + if (size < 3) + return true; + + const std::map::const_iterator + it = mRelations.find(name); + if (it != mRelations.end()) + return true; + + return checkName(name); +} + +bool PlayerRelationsManager::isGoodName(Being *const being) const +{ + if (!being) + return false; + if (being->getGoodStatus() != -1) + return (being->getGoodStatus() == 1); + + const std::string name = being->getName(); + const size_t size = name.size(); + + if (size < 3) + return true; + + const std::map::const_iterator + it = mRelations.find(name); + if (it != mRelations.end()) + return true; + + const bool status = checkName(name); + being->setGoodStatus(status ? 1 : 0); + return status; +} + +bool PlayerRelationsManager::checkName(const std::string &name) const +{ + const size_t size = name.size(); + const std::string check = config.getStringValue("unsecureChars"); + const std::string lastChar = name.substr(size - 1, 1); + + if (name.substr(0, 1) == " " || lastChar == " " || lastChar == "." + || name.find(" ") != std::string::npos) + { + return false; + } + else if (check.empty()) + { + return true; + } + else if (name.find_first_of(check) != std::string::npos) + { + return false; + } + else + { + return true; + } +} + +PlayerRelationsManager player_relations; diff --git a/src/being/playerrelations.h b/src/being/playerrelations.h new file mode 100644 index 000000000..f7e9eba60 --- /dev/null +++ b/src/being/playerrelations.h @@ -0,0 +1,285 @@ +/* + * The ManaPlus Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BEING_PLAYERRELATIONS_H +#define BEING_PLAYERRELATIONS_H + +#include "utils/stringvector.h" + +#include +#include + +#include "localconsts.h" + +class Being; + +struct PlayerRelation final +{ + static const unsigned int EMOTE = (1 << 0); + static const unsigned int SPEECH_FLOAT = (1 << 1); + static const unsigned int SPEECH_LOG = (1 << 2); + static const unsigned int WHISPER = (1 << 3); + static const unsigned int TRADE = (1 << 4); + static const unsigned int INVISIBLE = (1 << 5); + static const unsigned int BLACKLIST = (1 << 6); + static const unsigned int ENEMY = (1 << 7); + + static const unsigned int RELATIONS_NR = 7; + static const unsigned int RELATION_PERMISSIONS[RELATIONS_NR]; + + static const unsigned int DEFAULT = EMOTE + | SPEECH_FLOAT + | SPEECH_LOG + | WHISPER + | TRADE; + enum Relation + { + NEUTRAL = 0, + FRIEND = 1, + DISREGARDED = 2, + IGNORED = 3, + ERASED = 4, + BLACKLISTED = 5, + ENEMY2 = 6 + }; + + explicit PlayerRelation(const Relation relation); + + A_DELETE_COPY(PlayerRelation) + + Relation mRelation; // bitmask for all of the above +}; + + +/** + * Ignore strategy: describes how we should handle ignores. + */ +class PlayerIgnoreStrategy +{ + public: + std::string mDescription; + std::string mShortName; + + PlayerIgnoreStrategy() : + mDescription(), + mShortName() + { + } + + A_DELETE_COPY(PlayerIgnoreStrategy) + + virtual ~PlayerIgnoreStrategy() + { } + + /** + * Handle the ignoring of the indicated action by the indicated player. + */ + virtual void ignore(Being *const being, + const unsigned int flags) const = 0; +}; + +class PlayerRelationsListener +{ + public: + PlayerRelationsListener() + { } + + virtual ~PlayerRelationsListener() + { } + + virtual void updatedPlayer(const std::string &name) = 0; + + virtual void updateAll() = 0; +}; + +/** + * Player relations class, represents any particular relations and/or + * preferences the user of the local client has wrt other players (identified + * by std::string). + */ +class PlayerRelationsManager final +{ + public: + PlayerRelationsManager(); + + A_DELETE_COPY(PlayerRelationsManager) + + ~PlayerRelationsManager(); + + /** + * Initialise player relations manager (load config file etc.) + */ + void init(); + + /** + * Load configuration from our config file, or substitute defaults. + */ + void load(const bool oldConfig = false); + + /** + * Save configuration to our config file. + */ + void store() const; + + /** + * Determines whether the player in question is being ignored, filtered by + * the specified flags. + */ + unsigned int checkPermissionSilently(const std::string &player_name, + const unsigned int flags) + const A_WARN_UNUSED; + + /** + * Tests whether the player in question is being ignored for any of the + * actions in the specified flags. If so, trigger appropriate side effects + * if requested by the player. + */ + bool hasPermission(const Being *const being, + const unsigned int flags) const A_WARN_UNUSED; + + bool hasPermission(const std::string &being, + const unsigned int flags) const A_WARN_UNUSED; + + /** + * Updates the relationship with this player. + */ + void setRelation(const std::string &name, + const PlayerRelation::Relation relation); + + /** + * Updates the relationship with this player. + */ + PlayerRelation::Relation getRelation(const std::string &name) + const A_WARN_UNUSED; + + /** + * Deletes the information recorded for a player. + */ + void removePlayer(const std::string &name); + + /** + * Retrieves the default permissions. + */ + unsigned int getDefault() const A_WARN_UNUSED; + + /** + * Sets the default permissions. + */ + void setDefault(const unsigned int permissions); + + /** + * Retrieves all known player ignore strategies. + * + * The player ignore strategies are allocated statically and must + * not be deleted. + */ + std::vector *getPlayerIgnoreStrategies() + A_WARN_UNUSED; + + /** + * Return the current player ignore strategy. + * + * \return A player ignore strategy, or nullptr + */ + const PlayerIgnoreStrategy *getPlayerIgnoreStrategy() const + A_WARN_UNUSED + { return mIgnoreStrategy; } + + /** + * Sets the strategy to call when ignoring players. + */ + void setPlayerIgnoreStrategy(PlayerIgnoreStrategy *const strategy) + { mIgnoreStrategy = strategy; } + + /** + * For a given ignore strategy short name, find the appropriate index + * in the ignore strategies vector. + * + * \param The short name of the ignore strategy to look up + * \return The appropriate index, or -1 + */ + int getPlayerIgnoreStrategyIndex(const std::string &shortname) + A_WARN_UNUSED; + + /** + * Retrieves a sorted vector of all players for which we have any + * relations recorded. + */ + StringVect *getPlayers() const A_WARN_UNUSED; + + StringVect *getPlayersByRelation(const PlayerRelation::Relation rel) + const A_WARN_UNUSED; + + /** + * Removes all recorded player info. + */ + void clear(); + + /** + * Do we persist our `ignore' setup? + */ + bool getPersistIgnores() const + { return mPersistIgnores; } + + void ignoreTrade(const std::string &name); + + bool isGoodName(Being *const being) const A_WARN_UNUSED; + + bool isGoodName(const std::string &name) const A_WARN_UNUSED; + + /** + * Change the `ignore persist' flag. + * + * @param value Whether to persist ignores + */ + void setPersistIgnores(const bool value) + { mPersistIgnores = value; } + + void addListener(PlayerRelationsListener *const listener) + { mListeners.push_back(listener); } + + void removeListener(PlayerRelationsListener *const listener) + { mListeners.remove(listener); } + + bool checkBadRelation(const std::string &name) const A_WARN_UNUSED; + + private: + void signalUpdate(const std::string &name); + + bool mPersistIgnores; // If NOT set, we delete the + // ignored data upon reloading + unsigned int mDefaultPermissions; + + bool checkName(const std::string &name) const A_WARN_UNUSED; + + PlayerIgnoreStrategy *mIgnoreStrategy; + std::map mRelations; + std::list mListeners; + std::vector mIgnoreStrategies; +}; + + +extern PlayerRelationsManager player_relations; // singleton representation + // of player relations + + +#endif // BEING_PLAYERRELATIONS_H diff --git a/src/beingcacheentry.h b/src/beingcacheentry.h deleted file mode 100644 index d342b1c8d..000000000 --- a/src/beingcacheentry.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef BEINGCACHEENTRY_H -#define BEINGCACHEENTRY_H - -#include "localconsts.h" - -#include - -class BeingCacheEntry final -{ - public: - explicit BeingCacheEntry(const int id): - mName(), - mPartyName(), - mGuildName(), - mIp(), - mId(id), - mLevel(0), - mPvpRank(0), - mTime(0), - mFlags(0), - mIsAdvanced(false) - { - } - - A_DELETE_COPY(BeingCacheEntry) - - int getId() const - { return mId; } - - /** - * Returns the name of the being. - */ - const std::string &getName() const - { return mName; } - - /** - * Sets the name for the being. - * - * @param name The name that should appear. - */ - void setName(const std::string &name) - { mName = name; } - - /** - * Following are set from the server (mainly for players) - */ - void setPartyName(const std::string &name) - { mPartyName = name; } - - void setGuildName(const std::string &name) - { mGuildName = name; } - - const std::string &getPartyName() const - { return mPartyName; } - - const std::string &getGuildName() const - { return mGuildName; } - - void setLevel(const int n) - { mLevel = n; } - - int getLevel() const - { return mLevel; } - - void setTime(const int n) - { mTime = n; } - - int getTime() const - { return mTime; } - - unsigned getPvpRank() const - { return mPvpRank; } - - void setPvpRank(const int r) - { mPvpRank = r; } - - std::string getIp() const - { return mIp; } - - void setIp(std::string ip) - { mIp = ip; } - - bool isAdvanced() const - { return mIsAdvanced; } - - void setAdvanced(const bool a) - { mIsAdvanced = a; } - - int getFlags() const - { return mFlags; } - - void setFlags(const int flags) - { mFlags = flags; } - - protected: - std::string mName; /**< Name of character */ - std::string mPartyName; - std::string mGuildName; - std::string mIp; - int mId; /**< Unique sprite id */ - int mLevel; - unsigned int mPvpRank; - int mTime; - int mFlags; - bool mIsAdvanced; -}; - -#endif // BEINGCACHEENTRY_H diff --git a/src/beingequipbackend.cpp b/src/beingequipbackend.cpp index 392d23e0f..3092cd47e 100644 --- a/src/beingequipbackend.cpp +++ b/src/beingequipbackend.cpp @@ -20,7 +20,7 @@ #include "beingequipbackend.h" -#include "being.h" +#include "being/being.h" #include "net/inventoryhandler.h" #include "net/net.h" diff --git a/src/client.cpp b/src/client.cpp index 93f5d9a51..1dcdcfec3 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -37,13 +37,14 @@ #include "graphicsvertexes.h" #include "itemshortcut.h" #include "party.h" -#include "playerrelations.h" #include "soundconsts.h" #include "soundmanager.h" #include "statuseffect.h" #include "units.h" #include "touchmanager.h" +#include "being/playerrelations.h" + #include "input/inputmanager.h" #include "input/joystick.h" #include "input/keyboardconfig.h" diff --git a/src/commands.cpp b/src/commands.cpp index d16eeccec..ff8df5faa 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -28,10 +28,11 @@ #include "configuration.h" #include "game.h" #include "guildmanager.h" -#include "localplayer.h" #include "main.h" #include "party.h" +#include "being/localplayer.h" + #include "gui/buydialog.h" #include "gui/chatwindow.h" #include "gui/helpwindow.h" diff --git a/src/commands.h b/src/commands.h index 8308bd5f1..d196b9c51 100644 --- a/src/commands.h +++ b/src/commands.h @@ -23,7 +23,7 @@ #ifndef COMMANDS_H #define COMMANDS_H -#include "playerrelations.h" +#include "being/playerrelations.h" #include "input/keydata.h" diff --git a/src/compoundsprite.cpp b/src/compoundsprite.cpp deleted file mode 100644 index 9ca7ce8fb..000000000 --- a/src/compoundsprite.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "compoundsprite.h" - -#include "client.h" -#include "configuration.h" -#include "game.h" - -#ifdef USE_OPENGL -#include "main.h" -#endif - -#include "map.h" -#include "sdlshared.h" - -#include "render/surfacegraphics.h" - -#include "resources/image.h" -#include "resources/imagehelper.h" - -#include "utils/dtor.h" -#include "utils/sdlcheckutils.h" - -#include - -#include "debug.h" - -static const int BUFFER_WIDTH = 100; -static const int BUFFER_HEIGHT = 100; - -static const unsigned cache_max_size = 10; -static const unsigned cache_clean_part = 3; -bool CompoundSprite::mEnableDelay = true; - -CompoundSprite::CompoundSprite() : - imagesCache(), - mCacheItem(nullptr), - mImage(nullptr), - mAlphaImage(nullptr), - mOffsetX(0), - mOffsetY(0), - mSprites(), - mNextRedrawTime(0), - mNeedsRedraw(false), - mEnableAlphaFix(config.getBoolValue("enableAlphaFix")), - mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")), - mDisableBeingCaching(config.getBoolValue("disableBeingCaching")) -{ - mAlpha = 1.0f; -} - -CompoundSprite::~CompoundSprite() -{ - clear(); - mImage = nullptr; - mAlphaImage = nullptr; -} - -bool CompoundSprite::reset() -{ - bool ret = false; - FOR_EACH (SpriteIterator, it, mSprites) - { - if (*it) - ret |= (*it)->reset(); - } - mNeedsRedraw |= ret; - return ret; -} - -bool CompoundSprite::play(const std::string &action) -{ - bool ret = false; - FOR_EACH (SpriteIterator, it, mSprites) - { - if (*it) - ret |= (*it)->play(action); - } - mNeedsRedraw |= ret; - return ret; -} - -bool CompoundSprite::update(const int time) -{ - bool ret = false; - FOR_EACH (SpriteIterator, it, mSprites) - { - if (*it) - ret |= (*it)->update(time); - } - mNeedsRedraw |= ret; - return ret; -} - -bool CompoundSprite::draw(Graphics *const graphics, - const int posX, const int posY) const -{ - FUNC_BLOCK("CompoundSprite::draw", 1) - if (mNeedsRedraw) - updateImages(); - - if (mSprites.empty()) // Nothing to draw - return false; - - if (mAlpha == 1.0f && mImage) - { - return graphics->drawImage(mImage, posX + mOffsetX, posY + mOffsetY); - } - else if (mAlpha && mAlphaImage) - { - mAlphaImage->setAlpha(mAlpha); - return graphics->drawImage(mAlphaImage, - posX + mOffsetX, posY + mOffsetY); - } - else - { - drawSprites(graphics, posX, posY); - } - return false; -} - -void CompoundSprite::drawSprites(Graphics *const graphics, - const int posX, const int posY) const -{ - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - { - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, posX, posY); - } - } -} - -void CompoundSprite::drawSpritesSDL(Graphics *const graphics, - const int posX, const int posY) const -{ - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - (*it)->draw(graphics, posX, posY); - } -} - -int CompoundSprite::getWidth() const -{ - FOR_EACH (SpriteConstIterator, it, mSprites) - { - const Sprite *const base = *it; - if (base) - return base->getWidth(); - } - - return 0; -} - -int CompoundSprite::getHeight() const -{ - FOR_EACH (SpriteConstIterator, it, mSprites) - { - const Sprite *const base = *it; - if (base) - return base->getHeight(); - } - - return 0; -} - -const Image *CompoundSprite::getImage() const -{ - return mImage; -} - -bool CompoundSprite::setSpriteDirection(const SpriteDirection direction) -{ - bool ret = false; - FOR_EACH (SpriteIterator, it, mSprites) - { - if (*it) - ret |= (*it)->setSpriteDirection(direction); - } - mNeedsRedraw |= ret; - return ret; -} - -int CompoundSprite::getNumberOfLayers() const -{ - if (mImage || mAlphaImage) - return 1; - else - return static_cast(size()); -} - -unsigned int CompoundSprite::getCurrentFrame() const -{ - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - return (*it)->getCurrentFrame(); - } - return 0; -} - -unsigned int CompoundSprite::getFrameCount() const -{ - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - return (*it)->getFrameCount(); - } - return 0; -} - -void CompoundSprite::addSprite(Sprite *const sprite) -{ - mSprites.push_back(sprite); - mNeedsRedraw = true; -} - -void CompoundSprite::setSprite(const int layer, Sprite *const sprite) -{ - // Skip if it won't change anything - if (mSprites.at(layer) == sprite) - return; - - delete mSprites.at(layer); - mSprites[layer] = sprite; - mNeedsRedraw = true; -} - -void CompoundSprite::removeSprite(const int layer) -{ - // Skip if it won't change anything - if (!mSprites.at(layer)) - return; - - delete mSprites.at(layer); - mSprites.at(layer) = nullptr; - mNeedsRedraw = true; -} - -void CompoundSprite::clear() -{ - // Skip if it won't change anything - if (!mSprites.empty()) - { - delete_all(mSprites); - mSprites.clear(); - } - mNeedsRedraw = true; - delete_all(imagesCache); - imagesCache.clear(); - delete mCacheItem; - mCacheItem = nullptr; -} - -void CompoundSprite::ensureSize(size_t layerCount) -{ - // Skip if it won't change anything - if (mSprites.size() >= layerCount) - return; - -// resize(layerCount, nullptr); - mSprites.resize(layerCount); -} - -/** - * Returns the curent frame in the current animation of the given layer. - */ -unsigned int CompoundSprite::getCurrentFrame(unsigned int layer) const -{ - if (layer >= mSprites.size()) - return 0; - - const Sprite *const s = getSprite(layer); - if (s) - return s->getCurrentFrame(); - - return 0; -} - -/** - * Returns the frame count in the current animation of the given layer. - */ -unsigned int CompoundSprite::getFrameCount(unsigned int layer) -{ - if (layer >= mSprites.size()) - return 0; - - const Sprite *const s = getSprite(layer); - if (s) - return s->getFrameCount(); - - return 0; -} - -void CompoundSprite::redraw() const -{ -#ifndef USE_SDL2 - -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - const int rmask = 0xff000000; - const int gmask = 0x00ff0000; - const int bmask = 0x0000ff00; - const int amask = 0x000000ff; -#else - const int rmask = 0x000000ff; - const int gmask = 0x0000ff00; - const int bmask = 0x00ff0000; - const int amask = 0xff000000; -#endif - - SDL_Surface *const surface = MSDL_CreateRGBSurface(SDL_HWSURFACE, - BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask); - - if (!surface) - return; - - SurfaceGraphics *graphics = new SurfaceGraphics(); - graphics->setBlitMode(SurfaceGraphics::BLIT_GFX); - graphics->setTarget(surface); - graphics->_beginDraw(); - - int tileX = 32 / 2; - int tileY = 32; - - const Game *const game = Game::instance(); - if (game) - { - const Map *const map = game->getCurrentMap(); - if (map) - { - tileX = map->getTileWidth() / 2; - tileY = map->getTileWidth(); - } - } - - const int posX = BUFFER_WIDTH / 2 - tileX; - const int posY = BUFFER_HEIGHT - tileY; - - mOffsetX = tileX - BUFFER_WIDTH / 2; - mOffsetY = tileY - BUFFER_HEIGHT; - - drawSpritesSDL(graphics, posX, posY); - - delete graphics; - graphics = nullptr; - - SDL_Surface *const surfaceA = MSDL_CreateRGBSurface(SDL_HWSURFACE, - BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask); - -#ifdef USE_SDL2 - SDL_SetSurfaceAlphaMod(surface, 255); -#else - SDL_SetAlpha(surface, 0, SDL_ALPHA_OPAQUE); -#endif - SDL_BlitSurface(surface, nullptr, surfaceA, nullptr); - - delete mImage; - delete mAlphaImage; - - mImage = imageHelper->load(surface); - MSDL_FreeSurface(surface); - - if (ImageHelper::mEnableAlpha) - { - mAlphaImage = imageHelper->load(surfaceA); - MSDL_FreeSurface(surfaceA); - } - else - { - mAlphaImage = nullptr; - } -#endif -} - -void CompoundSprite::setAlpha(float alpha) -{ - if (alpha != mAlpha) - { -#ifdef USE_OPENGL - if (mEnableAlphaFix && imageHelper->useOpenGL() == 0 - && size() > 3) -#else - if (mEnableAlphaFix && size() > 3) -#endif - { - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - (*it)->setAlpha(alpha); - } - } - mAlpha = alpha; - } -} - -void CompoundSprite::updateImages() const -{ -#ifndef USE_SDL2 -#ifdef USE_OPENGL - if (imageHelper->useOpenGL()) - return; -#endif - - if (mEnableDelay) - { - if (get_elapsed_time1(mNextRedrawTime) < 10) - return; - mNextRedrawTime = tick_time; - } - mNeedsRedraw = false; - - if (!mDisableBeingCaching) - { - if (size() <= 3) - return; - - if (!mDisableAdvBeingCaching) - { - if (updateFromCache()) - return; - - redraw(); - - if (mImage) - initCurrentCacheItem(); - } - else - { - redraw(); - } - } -#endif -} - -bool CompoundSprite::updateFromCache() const -{ -#ifndef USE_SDL2 -// static int hits = 0; -// static int miss = 0; - - if (mCacheItem && mCacheItem->image) - { - imagesCache.push_front(mCacheItem); - mCacheItem = nullptr; - if (imagesCache.size() > cache_max_size) - { - for (unsigned f = 0; f < cache_clean_part; f ++) - { - CompoundItem *item = imagesCache.back(); - imagesCache.pop_back(); - delete item; - } - } - } - -// logger->log("cache size: %d, hit %d, miss %d", -// (int)imagesCache.size(), hits, miss); - - const size_t sz = size(); - FOR_EACH (ImagesCache::iterator, it, imagesCache) - { - CompoundItem *const ic = *it; - if (ic && ic->data.size() == sz) - { - bool fail(false); - VectorPointers::const_iterator it2 = ic->data.begin(); - const VectorPointers::const_iterator it2_end = ic->data.end(); - - for (SpriteConstIterator it1 = mSprites.begin(), - it1_end = mSprites.end(); - it1 != it1_end && it2 != it2_end; - ++ it1, ++ it2) - { - const void *ptr1 = nullptr; - const void *ptr2 = nullptr; - if (*it1) - ptr1 = (*it1)->getHash(); - if (*it2) - ptr2 = *it2; - if (ptr1 != ptr2) - { - fail = true; - break; - } - } - if (!fail) - { -// hits ++; - mImage = (*it)->image; - mAlphaImage = (*it)->alphaImage; - imagesCache.erase(it); - mCacheItem = ic; - return true; - } - } - } - mImage = nullptr; - mAlphaImage = nullptr; -// miss++; -#endif - return false; -} - -void CompoundSprite::initCurrentCacheItem() const -{ - delete mCacheItem; - mCacheItem = new CompoundItem(); - mCacheItem->image = mImage; - mCacheItem->alphaImage = mAlphaImage; -// mCacheItem->alpha = mAlpha; - - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - mCacheItem->data.push_back((*it)->getHash()); - else - mCacheItem->data.push_back(nullptr); - } -} - -bool CompoundSprite::updateNumber(unsigned num) -{ - bool res(false); - FOR_EACH (SpriteConstIterator, it, mSprites) - { - if (*it) - { - if ((*it)->updateNumber(num)) - res = true; - } - } - return res; -} - -CompoundItem::CompoundItem() : - data(), - image(nullptr), - alphaImage(nullptr) -{ -} - -CompoundItem::~CompoundItem() -{ - delete image; - delete alphaImage; -} diff --git a/src/compoundsprite.h b/src/compoundsprite.h deleted file mode 100644 index 0c7dc3034..000000000 --- a/src/compoundsprite.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef COMPOUNDSPRITE_H -#define COMPOUNDSPRITE_H - -#include "sprite.h" - -#include -#include - -#include "localconsts.h" - -class Image; - -typedef std::list VectorPointers; - -class CompoundItem final -{ - public: - CompoundItem(); - - A_DELETE_COPY(CompoundItem) - - ~CompoundItem(); - - VectorPointers data; - Image *image; - Image *alphaImage; -}; - -class CompoundSprite : public Sprite -{ -public: - typedef std::vector::iterator SpriteIterator; - typedef std::vector::const_iterator SpriteConstIterator; - - CompoundSprite(); - - A_DELETE_COPY(CompoundSprite) - - ~CompoundSprite(); - - virtual bool reset() override; - - virtual bool play(const std::string &action) override; - - virtual bool update(const int time) override; - - virtual bool draw(Graphics *const graphics, - const int posX, const int posY) const override; - - /** - * Gets the width in pixels of the first sprite in the list. - */ - virtual int getWidth() const override A_WARN_UNUSED; - - /** - * Gets the height in pixels of the first sprite in the list. - */ - virtual int getHeight() const override A_WARN_UNUSED; - - virtual const Image *getImage() const override A_WARN_UNUSED; - - virtual bool setSpriteDirection(const SpriteDirection direction) override; - - int getNumberOfLayers() const A_WARN_UNUSED; - - unsigned int getCurrentFrame() const override A_WARN_UNUSED; - - unsigned int getFrameCount() const override A_WARN_UNUSED; - - size_t size() const A_WARN_UNUSED - { return mSprites.size(); } - - bool empty() const A_WARN_UNUSED - { return mSprites.empty(); } - - void addSprite(Sprite *const sprite); - - void setSprite(const int layer, Sprite *const sprite); - - Sprite *getSprite(int layer) const A_WARN_UNUSED - { return mSprites.at(layer); } - - void removeSprite(const int layer); - - void clear(); - - void ensureSize(size_t layerCount); - - virtual void drawSprites(Graphics *const graphics, - int posX, int posY) const; - - virtual void drawSpritesSDL(Graphics *const graphics, - int posX, int posY) const; - - /** - * Returns the curent frame in the current animation of the given layer. - */ - virtual unsigned int getCurrentFrame(unsigned int layer) - const A_WARN_UNUSED; - - /** - * Returns the frame count in the current animation of the given layer. - */ - virtual unsigned int getFrameCount(unsigned int layer) A_WARN_UNUSED; - - virtual void setAlpha(float alpha) override; - - bool updateNumber(const unsigned num) override; - - static void setEnableDelay(bool b) - { mEnableDelay = b; } - -private: - void redraw() const; - - void updateImages() const; - - bool updateFromCache() const; - - void initCurrentCacheItem() const; - - typedef std::list ImagesCache; - mutable ImagesCache imagesCache; - mutable CompoundItem *mCacheItem; - - mutable Image *mImage; - mutable Image *mAlphaImage; - - mutable int mOffsetX; - mutable int mOffsetY; - std::vector mSprites; - mutable int mNextRedrawTime; - static bool mEnableDelay; - mutable bool mNeedsRedraw; - bool mEnableAlphaFix; - bool mDisableAdvBeingCaching; - bool mDisableBeingCaching; -}; - -#endif // COMPOUNDSPRITE_H diff --git a/src/defaults.cpp b/src/defaults.cpp index 4b2b6fa05..23d8995a5 100644 --- a/src/defaults.cpp +++ b/src/defaults.cpp @@ -21,10 +21,11 @@ #include "defaults.h" -#include "being.h" #include "client.h" #include "graphicsmanager.h" +#include "being/being.h" + #include "input/keydata.h" #include "render/graphics.h" diff --git a/src/dropshortcut.cpp b/src/dropshortcut.cpp index 325014823..585add656 100644 --- a/src/dropshortcut.cpp +++ b/src/dropshortcut.cpp @@ -26,8 +26,9 @@ #include "configuration.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/widgets/chattab.h" diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp index 9d41ac297..346a51d52 100644 --- a/src/effectmanager.cpp +++ b/src/effectmanager.cpp @@ -22,11 +22,12 @@ #include "effectmanager.h" -#include "being.h" #include "configuration.h" #include "logger.h" #include "soundmanager.h" +#include "being/being.h" + #include "particle/particle.h" #include "debug.h" diff --git a/src/emoteshortcut.cpp b/src/emoteshortcut.cpp index 9a908ca1b..8fce70aed 100644 --- a/src/emoteshortcut.cpp +++ b/src/emoteshortcut.cpp @@ -22,7 +22,8 @@ #include "emoteshortcut.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "resources/emotedb.h" diff --git a/src/flooritem.h b/src/flooritem.h index 86fb1703e..c77ed8ac3 100644 --- a/src/flooritem.h +++ b/src/flooritem.h @@ -23,7 +23,7 @@ #ifndef FLOORITEM_H #define FLOORITEM_H -#include "actorsprite.h" +#include "being/actorsprite.h" #include "resources/cursor.h" diff --git a/src/game.cpp b/src/game.cpp index a900eb6ed..03f26c981 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -33,12 +33,13 @@ #include "emoteshortcut.h" #include "guildmanager.h" #include "itemshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" #include "soundmanager.h" #include "spellshortcut.h" #include "touchmanager.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" + #include "particle/particle.h" #include "input/inputmanager.h" diff --git a/src/gui/beingpopup.cpp b/src/gui/beingpopup.cpp index e5fda9ecf..2297ec9d9 100644 --- a/src/gui/beingpopup.cpp +++ b/src/gui/beingpopup.cpp @@ -21,10 +21,11 @@ #include "gui/beingpopup.h" -#include "being.h" -#include "playerrelations.h" #include "units.h" +#include "being/being.h" +#include "being/playerrelations.h" + #include "gui/gui.h" #include "gui/sdlfont.h" diff --git a/src/gui/botcheckerwindow.cpp b/src/gui/botcheckerwindow.cpp index a18669da0..b378a22ba 100644 --- a/src/gui/botcheckerwindow.cpp +++ b/src/gui/botcheckerwindow.cpp @@ -39,7 +39,8 @@ #include "actorspritemanager.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "utils/gettext.h" diff --git a/src/gui/charcreatedialog.h b/src/gui/charcreatedialog.h index 82e7d7261..114481772 100644 --- a/src/gui/charcreatedialog.h +++ b/src/gui/charcreatedialog.h @@ -23,7 +23,7 @@ #ifndef GUI_CHARCREATEDIALOG_H #define GUI_CHARCREATEDIALOG_H -#include "being.h" +#include "being/being.h" #include "gui/charselectdialog.h" diff --git a/src/gui/charselectdialog.h b/src/gui/charselectdialog.h index 8078a2d21..2a07da188 100644 --- a/src/gui/charselectdialog.h +++ b/src/gui/charselectdialog.h @@ -23,9 +23,10 @@ #ifndef GUI_CHARSELECTDIALOG_H #define GUI_CHARSELECTDIALOG_H -#include "being.h" #include "main.h" +#include "being/being.h" + #include "gui/widgets/window.h" #include "net/charserverhandler.h" diff --git a/src/gui/chatwindow.cpp b/src/gui/chatwindow.cpp index 8705af925..1065501b7 100644 --- a/src/gui/chatwindow.cpp +++ b/src/gui/chatwindow.cpp @@ -29,12 +29,13 @@ #include "configuration.h" #include "game.h" #include "guild.h" -#include "localplayer.h" #include "party.h" -#include "playerinfo.h" #include "spellshortcut.h" #include "soundmanager.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" + #include "input/inputmanager.h" #include "input/keyevent.h" diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 2cb633589..0d7dee0c2 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -24,9 +24,10 @@ #include "client.h" #include "game.h" -#include "localplayer.h" #include "main.h" +#include "being/localplayer.h" + #include "particle/particle.h" #include "gui/setup.h" diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index 8511c7643..af010a2d6 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -22,11 +22,12 @@ #include "gui/equipmentwindow.h" -#include "being.h" #include "graphicsvertexes.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" + +#include "being/being.h" +#include "being/localplayer.h" #include "gui/itempopup.h" #include "gui/setup.h" diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index bcd2c5108..29b6c58f3 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -23,9 +23,10 @@ #include "gui/inventorywindow.h" #include "configuration.h" -#include "playerinfo.h" #include "units.h" +#include "being/playerinfo.h" + #include "input/inputmanager.h" #include "input/keyevent.h" diff --git a/src/gui/killstats.cpp b/src/gui/killstats.cpp index e1472c822..516d9b449 100644 --- a/src/gui/killstats.cpp +++ b/src/gui/killstats.cpp @@ -29,8 +29,9 @@ #include "actorspritemanager.h" #include "game.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "utils/gettext.h" diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index cca582099..79daa8bf4 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -25,9 +25,10 @@ #include "actorspritemanager.h" #include "client.h" #include "configuration.h" -#include "localplayer.h" #include "party.h" +#include "being/localplayer.h" + #include "gui/setup.h" #include "gui/viewport.h" #include "gui/textpopup.h" diff --git a/src/gui/ministatuswindow.cpp b/src/gui/ministatuswindow.cpp index 2c5e0c23a..43ace566c 100644 --- a/src/gui/ministatuswindow.cpp +++ b/src/gui/ministatuswindow.cpp @@ -23,8 +23,9 @@ #include "gui/ministatuswindow.h" #include "animatedsprite.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/chatwindow.h" #include "gui/gui.h" diff --git a/src/gui/npcdialog.cpp b/src/gui/npcdialog.cpp index 22cecebd2..f651d8b8e 100644 --- a/src/gui/npcdialog.cpp +++ b/src/gui/npcdialog.cpp @@ -23,7 +23,6 @@ #include "gui/npcdialog.h" #include "actorspritemanager.h" -#include "being.h" #include "configuration.h" #include "client.h" #include "inventory.h" @@ -31,6 +30,8 @@ #include "soundconsts.h" #include "soundmanager.h" +#include "being/being.h" + #include "gui/gui.h" #include "gui/inventorywindow.h" #include "gui/sdlfont.h" diff --git a/src/gui/outfitwindow.cpp b/src/gui/outfitwindow.cpp index c31f1c980..7ad826307 100644 --- a/src/gui/outfitwindow.cpp +++ b/src/gui/outfitwindow.cpp @@ -29,8 +29,9 @@ #include "game.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 54e6f4154..b579e719b 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -30,13 +30,14 @@ #include "guildmanager.h" #include "item.h" #include "itemshortcut.h" -#include "localplayer.h" #include "maplayer.h" #include "party.h" -#include "playerinfo.h" -#include "playerrelations.h" #include "spellmanager.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" +#include "being/playerrelations.h" + #include "input/inputmanager.h" #include "gui/buydialog.h" diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index bab29fe7c..3f623280c 100644 --- a/src/gui/popupmenu.h +++ b/src/gui/popupmenu.h @@ -26,7 +26,7 @@ #include "gui/widgets/linkhandler.h" #include "gui/widgets/popup.h" -#include "actorsprite.h" +#include "being/actorsprite.h" #include #include diff --git a/src/gui/questswindow.cpp b/src/gui/questswindow.cpp index d863e3dbd..896d66af3 100644 --- a/src/gui/questswindow.cpp +++ b/src/gui/questswindow.cpp @@ -23,9 +23,10 @@ #include "actorspritemanager.h" #include "configuration.h" #include "effectmanager.h" -#include "localplayer.h" #include "soundmanager.h" +#include "being/localplayer.h" + #include "gui/gui.h" #include "gui/sdlfont.h" diff --git a/src/gui/selldialog.cpp b/src/gui/selldialog.cpp index f57db6dbd..97b0fbdc1 100644 --- a/src/gui/selldialog.cpp +++ b/src/gui/selldialog.cpp @@ -22,10 +22,11 @@ #include "gui/selldialog.h" -#include "playerinfo.h" #include "shopitem.h" #include "units.h" +#include "being/playerinfo.h" + #include "gui/confirmdialog.h" #include "gui/setup.h" #include "gui/tradewindow.h" diff --git a/src/gui/setup_chat.cpp b/src/gui/setup_chat.cpp index 41000d5dd..91a9dd859 100644 --- a/src/gui/setup_chat.cpp +++ b/src/gui/setup_chat.cpp @@ -35,7 +35,8 @@ #include "gui/widgets/setupitem.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "utils/gettext.h" diff --git a/src/gui/setup_perfomance.cpp b/src/gui/setup_perfomance.cpp index c58240af9..c53f2a970 100644 --- a/src/gui/setup_perfomance.cpp +++ b/src/gui/setup_perfomance.cpp @@ -36,7 +36,8 @@ #include "gui/widgets/setupitem.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "utils/gettext.h" diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp index fb7b9861d..bd2b829fd 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -35,7 +35,8 @@ #include "gui/widgets/setupitem.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "utils/gettext.h" diff --git a/src/gui/setup_relations.cpp b/src/gui/setup_relations.cpp index 644817331..af6e0420a 100644 --- a/src/gui/setup_relations.cpp +++ b/src/gui/setup_relations.cpp @@ -24,7 +24,8 @@ #include "actorspritemanager.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "gui/editdialog.h" #include "gui/okdialog.h" diff --git a/src/gui/setup_relations.h b/src/gui/setup_relations.h index cac44c470..734b06943 100644 --- a/src/gui/setup_relations.h +++ b/src/gui/setup_relations.h @@ -23,7 +23,7 @@ #ifndef GUI_SETUP_RELATIONS_H #define GUI_SETUP_RELATIONS_H -#include "playerrelations.h" +#include "being/playerrelations.h" #include "gui/widgets/setuptab.h" diff --git a/src/gui/setup_theme.cpp b/src/gui/setup_theme.cpp index 200b86219..b7cd9bfa1 100644 --- a/src/gui/setup_theme.cpp +++ b/src/gui/setup_theme.cpp @@ -35,7 +35,8 @@ #include "gui/widgets/namesmodel.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "utils/gettext.h" diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 12ae9fe83..594d5ff4d 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -28,7 +28,7 @@ #include "graphicsmanager.h" -#include "localplayer.h" +#include "being/localplayer.h" #include "gui/gui.h" #include "gui/okdialog.h" diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index 2c0224f06..7974e8e3d 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -23,7 +23,7 @@ #ifndef GUI_SETUP_VIDEO_H #define GUI_SETUP_VIDEO_H -#include "being.h" +#include "being/being.h" #include "gui/widgets/setuptab.h" diff --git a/src/gui/shopwindow.cpp b/src/gui/shopwindow.cpp index 04b994b7f..237715141 100644 --- a/src/gui/shopwindow.cpp +++ b/src/gui/shopwindow.cpp @@ -45,14 +45,15 @@ #include "confirmdialog.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" -#include "playerinfo.h" -#include "playerrelations.h" #include "shopitem.h" #include "soundconsts.h" #include "soundmanager.h" #include "units.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" +#include "being/playerrelations.h" + #include "net/net.h" #include "net/chathandler.h" #include "net/npchandler.h" diff --git a/src/gui/socialwindow.cpp b/src/gui/socialwindow.cpp index baeaf847f..72bd920d6 100644 --- a/src/gui/socialwindow.cpp +++ b/src/gui/socialwindow.cpp @@ -24,10 +24,11 @@ #include "actorspritemanager.h" #include "guild.h" #include "guildmanager.h" -#include "localplayer.h" #include "maplayer.h" #include "party.h" -#include "playerrelations.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/gui/statuspopup.cpp b/src/gui/statuspopup.cpp index 3c1479594..2e6569031 100644 --- a/src/gui/statuspopup.cpp +++ b/src/gui/statuspopup.cpp @@ -30,9 +30,10 @@ #include "gui/widgets/layout.h" #include "gui/widgets/textbox.h" -#include "localplayer.h" #include "units.h" +#include "being/localplayer.h" + #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/gui/statuswindow.cpp b/src/gui/statuswindow.cpp index fe5ec917d..585c950e6 100644 --- a/src/gui/statuswindow.cpp +++ b/src/gui/statuswindow.cpp @@ -27,10 +27,11 @@ #include "equipment.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" -#include "playerinfo.h" #include "units.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" + #include "gui/equipmentwindow.h" #include "gui/setup.h" #include "gui/viewport.h" diff --git a/src/gui/textcommandeditor.cpp b/src/gui/textcommandeditor.cpp index 7ad27c50f..9ba33c830 100644 --- a/src/gui/textcommandeditor.cpp +++ b/src/gui/textcommandeditor.cpp @@ -24,10 +24,11 @@ #include "configuration.h" #include "item.h" -#include "localplayer.h" #include "main.h" #include "spellmanager.h" +#include "being/localplayer.h" + #include "input/keyboardconfig.h" #include "gui/chatwindow.h" diff --git a/src/gui/tradewindow.cpp b/src/gui/tradewindow.cpp index a71b26f60..e60472526 100644 --- a/src/gui/tradewindow.cpp +++ b/src/gui/tradewindow.cpp @@ -25,11 +25,12 @@ #include "configuration.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" -#include "playerinfo.h" -#include "playerrelations.h" #include "units.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" +#include "being/playerrelations.h" + #include "gui/gui.h" #include "gui/inventorywindow.h" #include "gui/itemamountwindow.h" diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index e8ae6bd6c..e210e043b 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -27,11 +27,12 @@ #include "configuration.h" #include "game.h" #include "itemshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" #include "sdlshared.h" #include "textmanager.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" + #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/gui/whoisonline.cpp b/src/gui/whoisonline.cpp index c1ea05881..d045b3876 100644 --- a/src/gui/whoisonline.cpp +++ b/src/gui/whoisonline.cpp @@ -36,10 +36,11 @@ #include "actorspritemanager.h" #include "client.h" #include "configuration.h" -#include "localplayer.h" -#include "playerrelations.h" #include "main.h" +#include "being/localplayer.h" +#include "being/playerrelations.h" + #include "net/download.h" #include "net/net.h" #include "net/playerhandler.h" diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp index 1bb3ca235..aacb3aabe 100644 --- a/src/gui/widgets/avatarlistbox.cpp +++ b/src/gui/widgets/avatarlistbox.cpp @@ -24,9 +24,10 @@ #include "actorspritemanager.h" #include "configuration.h" #include "guild.h" -#include "localplayer.h" #include "maplayer.h" +#include "being/localplayer.h" + #include "gui/chatwindow.h" #include "gui/gui.h" #include "gui/sdlfont.h" diff --git a/src/gui/widgets/battletab.cpp b/src/gui/widgets/battletab.cpp index 5cc770478..440977c37 100644 --- a/src/gui/widgets/battletab.cpp +++ b/src/gui/widgets/battletab.cpp @@ -24,9 +24,10 @@ #include "chatlogger.h" #include "configuration.h" -#include "localplayer.h" #include "main.h" +#include "being/localplayer.h" + #include "net/net.h" #include "resources/iteminfo.h" diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp index 1cb2e9d82..fba603ee6 100644 --- a/src/gui/widgets/chattab.cpp +++ b/src/gui/widgets/chattab.cpp @@ -27,10 +27,11 @@ #include "client.h" #include "commandhandler.h" #include "configuration.h" -#include "localplayer.h" #include "soundconsts.h" #include "soundmanager.h" +#include "being/localplayer.h" + #include "gui/helpwindow.h" #include "gui/widgets/scrollarea.h" diff --git a/src/gui/widgets/dropshortcutcontainer.cpp b/src/gui/widgets/dropshortcutcontainer.cpp index 96d068ade..89fcf1ecd 100644 --- a/src/gui/widgets/dropshortcutcontainer.cpp +++ b/src/gui/widgets/dropshortcutcontainer.cpp @@ -25,8 +25,9 @@ #include "client.h" #include "dragdrop.h" #include "dropshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "input/keyboardconfig.h" diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp index d72f5379d..0bdb0aa31 100644 --- a/src/gui/widgets/emoteshortcutcontainer.cpp +++ b/src/gui/widgets/emoteshortcutcontainer.cpp @@ -28,7 +28,8 @@ #include "inventory.h" #include "item.h" #include "itemshortcut.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/gui/widgets/gmtab.cpp b/src/gui/widgets/gmtab.cpp index 854a67692..ee9d7caa1 100644 --- a/src/gui/widgets/gmtab.cpp +++ b/src/gui/widgets/gmtab.cpp @@ -22,7 +22,8 @@ #include "chatlogger.h" #include "commands.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/chathandler.h" #include "net/net.h" diff --git a/src/gui/widgets/guildchattab.cpp b/src/gui/widgets/guildchattab.cpp index 3e09156fd..6394ac3ad 100644 --- a/src/gui/widgets/guildchattab.cpp +++ b/src/gui/widgets/guildchattab.cpp @@ -26,10 +26,11 @@ #include "configuration.h" #include "guild.h" #include "guildmanager.h" -#include "localplayer.h" #include "soundconsts.h" #include "soundmanager.h" +#include "being/localplayer.h" + #include "resources/iteminfo.h" #include "resources/itemdb.h" diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp index f3fb21336..99aedabcd 100644 --- a/src/gui/widgets/itemcontainer.cpp +++ b/src/gui/widgets/itemcontainer.cpp @@ -27,7 +27,8 @@ #include "inventory.h" #include "item.h" #include "itemshortcut.h" -#include "playerinfo.h" + +#include "being/playerinfo.h" #include "gui/chatwindow.h" #include "gui/gui.h" diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp index 40eaefcf9..0466706df 100644 --- a/src/gui/widgets/itemshortcutcontainer.cpp +++ b/src/gui/widgets/itemshortcutcontainer.cpp @@ -29,8 +29,9 @@ #include "item.h" #include "itemshortcut.h" #include "spellshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp index 6a5c815d4..31ee7c123 100644 --- a/src/gui/widgets/playerbox.cpp +++ b/src/gui/widgets/playerbox.cpp @@ -23,10 +23,11 @@ #include "gui/widgets/playerbox.h" #include "animatedsprite.h" -#include "being.h" #include "client.h" #include "configuration.h" +#include "being/being.h" + #include "resources/image.h" #include "utils/dtor.h" diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp index b7e0ad120..2a98b4441 100644 --- a/src/gui/widgets/shoplistbox.cpp +++ b/src/gui/widgets/shoplistbox.cpp @@ -24,9 +24,10 @@ #include "client.h" #include "configuration.h" -#include "playerinfo.h" #include "shopitem.h" +#include "being/playerinfo.h" + #include "gui/itempopup.h" #include "gui/viewport.h" diff --git a/src/gui/widgets/skillinfo.cpp b/src/gui/widgets/skillinfo.cpp index 809e34b0d..2f0dcb5b5 100644 --- a/src/gui/widgets/skillinfo.cpp +++ b/src/gui/widgets/skillinfo.cpp @@ -22,7 +22,7 @@ #include "gui/widgets/skillinfo.h" -#include "playerinfo.h" +#include "being/playerinfo.h" #include "gui/widgets/skillmodel.h" diff --git a/src/gui/widgets/skillmodel.cpp b/src/gui/widgets/skillmodel.cpp index 1792c41fd..c4a298640 100644 --- a/src/gui/widgets/skillmodel.cpp +++ b/src/gui/widgets/skillmodel.cpp @@ -25,8 +25,9 @@ #include "configuration.h" #include "effectmanager.h" #include "itemshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/setup.h" #include "gui/shortcutwindow.h" diff --git a/src/gui/widgets/skillmodel.h b/src/gui/widgets/skillmodel.h index da5779672..8e6debb7e 100644 --- a/src/gui/widgets/skillmodel.h +++ b/src/gui/widgets/skillmodel.h @@ -26,8 +26,9 @@ #include "configuration.h" #include "effectmanager.h" #include "itemshortcut.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/setup.h" #include "gui/shortcutwindow.h" diff --git a/src/gui/widgets/spellshortcutcontainer.cpp b/src/gui/widgets/spellshortcutcontainer.cpp index 0ffb25b1d..433001f3b 100644 --- a/src/gui/widgets/spellshortcutcontainer.cpp +++ b/src/gui/widgets/spellshortcutcontainer.cpp @@ -25,9 +25,10 @@ #include "client.h" #include "dragdrop.h" #include "itemshortcut.h" -#include "localplayer.h" #include "spellshortcut.h" +#include "being/localplayer.h" + #include "input/keyboardconfig.h" #include "gui/inventorywindow.h" diff --git a/src/gui/widgets/tradetab.cpp b/src/gui/widgets/tradetab.cpp index e2ef34536..61becb7da 100644 --- a/src/gui/widgets/tradetab.cpp +++ b/src/gui/widgets/tradetab.cpp @@ -23,7 +23,8 @@ #include "gui/widgets/tradetab.h" #include "chatlogger.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/net.h" diff --git a/src/gui/widgets/whispertab.cpp b/src/gui/widgets/whispertab.cpp index dbca226a8..b836ee729 100644 --- a/src/gui/widgets/whispertab.cpp +++ b/src/gui/widgets/whispertab.cpp @@ -24,7 +24,8 @@ #include "chatlogger.h" #include "commands.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/chathandler.h" #include "net/net.h" diff --git a/src/guildmanager.cpp b/src/guildmanager.cpp index 57c687e12..5d83a9fd7 100644 --- a/src/guildmanager.cpp +++ b/src/guildmanager.cpp @@ -24,9 +24,10 @@ #include "client.h" #include "configuration.h" #include "guild.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "gui/socialwindow.h" #include "gui/widgets/guildchattab.h" diff --git a/src/input/inputmanager.cpp b/src/input/inputmanager.cpp index 561437124..66bd67efb 100644 --- a/src/input/inputmanager.cpp +++ b/src/input/inputmanager.cpp @@ -23,9 +23,10 @@ #include "client.h" #include "configuration.h" #include "game.h" -#include "localplayer.h" #include "touchmanager.h" +#include "being/localplayer.h" + #include "input/joystick.h" #include "input/keyboardconfig.h" #include "input/keyboarddata.h" diff --git a/src/inventory.h b/src/inventory.h index 6f71c6682..f6dd11017 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -23,7 +23,7 @@ #ifndef INVENTORY_H #define INVENTORY_H -#include "being.h" +#include "being/being.h" #include #include diff --git a/src/itemshortcut.cpp b/src/itemshortcut.cpp index 2ba3cbe9a..9332211ce 100644 --- a/src/itemshortcut.cpp +++ b/src/itemshortcut.cpp @@ -26,9 +26,10 @@ #include "inventory.h" #include "item.h" #include "logger.h" -#include "playerinfo.h" #include "spellmanager.h" +#include "being/playerinfo.h" + #include "gui/skilldialog.h" #include "net/inventoryhandler.h" diff --git a/src/localplayer.cpp b/src/localplayer.cpp deleted file mode 100644 index 02c1d6ed9..000000000 --- a/src/localplayer.cpp +++ /dev/null @@ -1,4354 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "localplayer.h" - -#include "actorspritemanager.h" -#include "client.h" -#include "configuration.h" -#include "dropshortcut.h" -#include "effectmanager.h" -#include "guild.h" -#include "item.h" -#include "maplayer.h" -#include "party.h" -#include "playerinfo.h" -#include "playerrelations.h" -#include "simpleanimation.h" -#include "soundconsts.h" -#include "soundmanager.h" -#include "statuseffect.h" -#include "walklayer.h" - -#include "particle/particle.h" - -#include "input/keyboardconfig.h" - -#include "gui/chatwindow.h" -#include "gui/gui.h" -#include "gui/ministatuswindow.h" -#include "gui/okdialog.h" -#include "gui/outfitwindow.h" -#include "gui/shopwindow.h" -#include "gui/sdlfont.h" -#include "gui/skilldialog.h" -#include "gui/socialwindow.h" -#include "gui/updaterwindow.h" -#include "gui/viewport.h" - -#include "gui/widgets/gmtab.h" -#include "gui/widgets/whispertab.h" - -#include "render/graphics.h" - -#include "net/beinghandler.h" -#include "net/chathandler.h" -#include "net/guildhandler.h" -#include "net/inventoryhandler.h" -#include "net/net.h" -#include "net/partyhandler.h" -#include "net/playerhandler.h" -#include "net/skillhandler.h" -#include "net/tradehandler.h" - -#include "resources/imageset.h" -#include "resources/iteminfo.h" -#include "resources/resourcemanager.h" - -#include "utils/gettext.h" - -#include "mumblemanager.h" - -#include - -#include "debug.h" - -static const int16_t awayLimitTimer = 60; -static const int MAX_TICK_VALUE = INT_MAX / 2; - -typedef std::map::const_iterator GuildMapCIter; - -LocalPlayer *player_node = nullptr; - -extern std::list beingInfoCache; -extern OkDialog *weightNotice; -extern int weightNoticeTime; -extern MiniStatusWindow *miniStatusWindow; -extern SkillDialog *skillDialog; - -LocalPlayer::LocalPlayer(const int id, const int subtype) : - Being(id, PLAYER, subtype, nullptr), - mGMLevel(0), - mInvertDirection(0), - mCrazyMoveType(config.getIntValue("crazyMoveType")), - mCrazyMoveState(0), - mAttackWeaponType(config.getIntValue("attackWeaponType")), - mQuickDropCounter(config.getIntValue("quickDropCounter")), - mMoveState(0), - mPickUpType(config.getIntValue("pickUpType")), - mMagicAttackType(config.getIntValue("magicAttackType")), - mPvpAttackType(config.getIntValue("pvpAttackType")), - mMoveToTargetType(config.getIntValue("moveToTargetType")), - mAttackType(config.getIntValue("attackType")), - mFollowMode(config.getIntValue("followMode")), - mImitationMode(config.getIntValue("imitationMode")), - mLastTargetX(0), - mLastTargetY(0), - mHomes(), - mTarget(nullptr), - mPlayerFollowed(), - mPlayerImitated(), - mNextDestX(0), - mNextDestY(0), - mPickUpTarget(nullptr), - mLastAction(-1), - mStatusEffectIcons(), - mLocalWalkTime(-1), - mMessages(), - mMessageTime(0), - mAwayListener(new AwayListener), - mAwayDialog(nullptr), - mPingSendTick(0), - mPingTime(0), - mAfkTime(0), - mActivityTime(0), - mNavigateX(0), - mNavigateY(0), - mNavigateId(0), - mCrossX(0), - mCrossY(0), - mOldX(0), - mOldY(0), - mOldTileX(0), - mOldTileY(0), - mNavigatePath(), - mLastHitFrom(), - mWaitFor(), - mAdvertTime(0), - mTestParticle(nullptr), - mTestParticleName(), - mTestParticleTime(0), - mTestParticleHash(0l), - mWalkingDir(0), - mUpdateName(true), - mBlockAdvert(false), - mTargetDeadPlayers(config.getBoolValue("targetDeadPlayers")), - mServerAttack(config.getBoolValue("serverAttack")), - mEnableAdvert(config.getBoolValue("enableAdvert")), - mTradebot(config.getBoolValue("tradebot")), - mTargetOnlyReachable(config.getBoolValue("targetOnlyReachable")), - mDisableGameModifiers(config.getBoolValue("disableGameModifiers")), - mIsServerBuggy(serverConfig.getValueBool("enableBuggyServers", true)), - mSyncPlayerMove(config.getBoolValue("syncPlayerMove")), - mDrawPath(config.getBoolValue("drawPath")), - mAttackMoving(config.getBoolValue("attackMoving")), - mAttackNext(config.getBoolValue("attackNext")), - mShowJobExp(config.getBoolValue("showJobExp")), - mNextStep(false), - mDisableCrazyMove(false), - mGoingToTarget(false), - mKeepAttacking(false), - mPathSetByMouse(false), - mWaitPing(false), - mAwayMode(false), - mPseudoAwayMode(false), - mShowNavigePath(false) -{ - logger->log1("LocalPlayer::LocalPlayer"); - - listen(CHANNEL_ATTRIBUTES); - - mAttackRange = 0; - mLevel = 1; - mAdvanced = true; - mTextColor = &Theme::getThemeColor(Theme::PLAYER); - if (userPalette) - mNameColor = &userPalette->getColor(UserPalette::SELF); - else - mNameColor = nullptr; - - PlayerInfo::setStatBase(PlayerInfo::WALK_SPEED, - static_cast(getWalkSpeed().x)); - PlayerInfo::setStatMod(PlayerInfo::WALK_SPEED, 0); - - loadHomes(); - - config.addListener("showownname", this); - config.addListener("targetDeadPlayers", this); - serverConfig.addListener("enableBuggyServers", this); - config.addListener("syncPlayerMove", this); - config.addListener("drawPath", this); - config.addListener("serverAttack", this); - config.addListener("attackMoving", this); - config.addListener("attackNext", this); - config.addListener("showJobExp", this); - config.addListener("enableAdvert", this); - config.addListener("tradebot", this); - config.addListener("targetOnlyReachable", this); - setShowName(config.getBoolValue("showownname")); -} - -LocalPlayer::~LocalPlayer() -{ - logger->log1("LocalPlayer::~LocalPlayer"); - - config.removeListeners(this); - serverConfig.removeListener("enableBuggyServers", this); - - if (mAwayDialog) - { - soundManager.volumeRestore(); - delete mAwayDialog; - mAwayDialog = nullptr; - } - delete mAwayListener; - mAwayListener = nullptr; -} - -void LocalPlayer::logic() -{ - BLOCK_START("LocalPlayer::logic") -#ifdef USE_MUMBLE - if (mumbleManager) - mumbleManager->setPos(mX, mY, mDirection); -#endif - - // Actions are allowed once per second - if (get_elapsed_time(mLastAction) >= 1000) - mLastAction = -1; - - if (mActivityTime == 0 || mLastAction != -1) - mActivityTime = cur_time; - - if ((mAction != MOVE || mNextStep) && !mNavigatePath.empty()) - { - mNextStep = false; - int dist = 5; - if (!mSyncPlayerMove) - dist = 20; - - if ((mNavigateX || mNavigateY) && - ((mCrossX + dist >= mX && mCrossX <= mX + dist - && mCrossY + dist >= mY && mCrossY <= mY + dist) - || (!mCrossX && !mCrossY))) - { - const Path::const_iterator i = mNavigatePath.begin(); - if ((*i).x == mX && (*i).y == mY) - mNavigatePath.pop_front(); - else - moveTo((*i).x, (*i).y); - } - } - - // Show XP messages - if (!mMessages.empty()) - { - if (mMessageTime == 0) - { - MessagePair info = mMessages.front(); - - if (particleEngine) - { - particleEngine->addTextRiseFadeOutEffect( - info.first, - getPixelX(), - getPixelY() - 48, - &userPalette->getColor(info.second), - gui->getInfoParticleFont(), true); - } - - mMessages.pop_front(); - mMessageTime = 30; - } - mMessageTime--; - } - -#ifdef MANASERV_SUPPORT - PlayerInfo::logic(); -#endif - - if (mTarget) - { - if (mTarget->getType() == ActorSprite::NPC) - { - // NPCs are always in range - mTarget->setTargetType(TCT_IN_RANGE); - } - else - { - // Find whether target is in range -#ifdef MANASERV_SUPPORT - const int rangeX = - (Net::getNetworkType() == ServerInfo::MANASERV) ? - static_cast(abs(static_cast(mTarget->getPosition().x - - getPosition().x))) : - static_cast(abs(mTarget->getTileX() - getTileX())); - const int rangeY = - (Net::getNetworkType() == ServerInfo::MANASERV) ? - static_cast(abs(static_cast(mTarget->getPosition().y - - getPosition().y))) : - static_cast(abs(mTarget->getTileY() - getTileY())); -#else - const int rangeX = static_cast( - abs(mTarget->getTileX() - getTileX())); - const int rangeY = static_cast( - abs(mTarget->getTileY() - getTileY())); -#endif - const int attackRange = getAttackRange(); - const TargetCursorType targetType = rangeX > attackRange || - rangeY > attackRange ? - TCT_NORMAL : TCT_IN_RANGE; - mTarget->setTargetType(targetType); - - if (!mTarget->isAlive() && (!mTargetDeadPlayers - || mTarget->getType() != Being::PLAYER)) - { - stopAttack(true); - } - - if (mKeepAttacking && mTarget) - attack(mTarget, true); - } - } - - Being::logic(); - BLOCK_END("LocalPlayer::logic") -} - -void LocalPlayer::slowLogic() -{ - BLOCK_START("LocalPlayer::slowLogic") - const int time = cur_time; - if (weightNotice && weightNoticeTime < time) - { - weightNotice->scheduleDelete(); - weightNotice = nullptr; - weightNoticeTime = 0; - } - - if (serverVersion < 4 && mEnableAdvert && !mBlockAdvert - && mAdvertTime < cur_time) - { - uint8_t smile = FLAG_SPECIAL; - if (mTradebot && shopWindow && !shopWindow->isShopEmpty()) - smile |= FLAG_SHOP; - - if (mAwayMode || mPseudoAwayMode) - smile |= FLAG_AWAY; - - if (mInactive) - smile |= FLAG_INACTIVE; - - if (emote(smile)) - mAdvertTime = time + 60; - else - mAdvertTime = time + 30; - } - - if (mTestParticleTime != time && !mTestParticleName.empty()) - { - unsigned long hash = UpdaterWindow::getFileHash(mTestParticleName); - if (hash != mTestParticleHash) - { - setTestParticle(mTestParticleName, false); - mTestParticleHash = hash; - } - mTestParticleTime = time; - } - - BLOCK_END("LocalPlayer::slowLogic") -} - -void LocalPlayer::setAction(const Action &action, const int attackType) -{ - if (action == DEAD) - { - if (!mLastHitFrom.empty()) - { - // TRANSLATORS: chat message after death - debugMsg(strprintf(_("You were killed by %s"), - mLastHitFrom.c_str())); - mLastHitFrom.clear(); - } - setTarget(nullptr); - } - - Being::setAction(action, attackType); -#ifdef USE_MUMBLE - if (mumbleManager) - mumbleManager->setAction(static_cast(action)); -#endif -} - -void LocalPlayer::setGMLevel(const int level) -{ - mGMLevel = level; - - if (level > 0) - { - setGM(true); - if (chatWindow) - { - chatWindow->loadGMCommands(); - if (!gmChatTab && config.getBoolValue("enableGmTab")) - gmChatTab = new GmTab(chatWindow); - } - } -} - -#ifdef MANASERV_SUPPORT -Position LocalPlayer::getNextWalkPosition(const unsigned char dir) const -{ - // Compute where the next tile will be set. - int dx = 0, dy = 0; - if (dir & Being::UP) - dy--; - if (dir & Being::DOWN) - dy++; - if (dir & Being::LEFT) - dx--; - if (dir & Being::RIGHT) - dx++; - - const Vector &pos = getPosition(); - - // If no map or no direction is given, give back the current player position - if (!mMap || (!dx && !dy)) - return Position(static_cast(pos.x), static_cast(pos.y)); - - const int posX = static_cast(pos.x); - const int posY = static_cast(pos.y); - // Get the current tile pos and its offset - const int tileX = posX / mMap->getTileWidth(); - const int tileY = posY / mMap->getTileHeight(); - const int offsetX = posX % mMap->getTileWidth(); - const int offsetY = posY % mMap->getTileHeight(); - const unsigned char walkMask = getWalkMask(); - const int radius = getCollisionRadius(); - - // Get the walkability of every surrounding tiles. - bool wTopLeft = mMap->getWalk(tileX - 1, tileY - 1, walkMask); - const bool wTop = mMap->getWalk(tileX, tileY - 1, walkMask); - bool wTopRight = mMap->getWalk(tileX + 1, tileY - 1, walkMask); - const bool wLeft = mMap->getWalk(tileX - 1, tileY, walkMask); - const bool wRight = mMap->getWalk(tileX + 1, tileY, walkMask); - bool wBottomLeft = mMap->getWalk(tileX - 1, tileY + 1, walkMask); - const bool wBottom = mMap->getWalk(tileX, tileY + 1, walkMask); - bool wBottomRight = mMap->getWalk(tileX + 1, tileY + 1, walkMask); - - // Make diagonals unwalkable when both straight directions are blocking - if (!wTop) - { - if (!wRight) - wTopRight = false; - if (!wLeft) - wTopLeft = false; - } - if (!wBottom) - { - if (!wRight) - wBottomRight = false; - if (!wLeft) - wBottomLeft = false; - } - - // We'll make tests for each desired direction - - // Handle diagonal cases by setting the way back to a straight direction - // when necessary. - if (dx && dy) - { - // Going top-right - if (dx > 0 && dy < 0) - { - if (!wTopRight) - { - // Choose a straight direction when diagonal target is blocked - if (!wTop && wRight) - { - dy = 0; - } - else if (wTop && !wRight) - { - dx = 0; - } - else if (!wTop && !wRight) - { - return Position(tileX * 32 + 32 - radius, - tileY * 32 + getCollisionRadius()); - } - else // Both straight direction are walkable - { - // Go right when below the corner - if (offsetY >= (offsetX / mMap->getTileHeight() - - (offsetX / mMap->getTileWidth() - * mMap->getTileHeight()) )) - { - dy = 0; - } - else // Go up otherwise - { - dx = 0; - } - } - } - else // The diagonal is walkable - { - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX + 32, posY - 32)); - } - } - - // Going top-left - if (dx < 0 && dy < 0) - { - if (!wTopLeft) - { - // Choose a straight direction when diagonal target is blocked - if (!wTop && wLeft) - { - dy = 0; - } - else if (wTop && !wLeft) - { - dx = 0; - } - else if (!wTop && !wLeft) - { - return Position(tileX * 32 + radius, - tileY * 32 + radius); - } - else // Both straight direction are walkable - { - // Go left when below the corner - if (offsetY >= (offsetX / mMap->getTileWidth() - * mMap->getTileHeight())) - { - dy = 0; - } - else // Go up otherwise - { - dx = 0; - } - } - } - else // The diagonal is walkable - { - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX - 32, posY - 32)); - } - } - - // Going bottom-left - if (dx < 0 && dy > 0) - { - if (!wBottomLeft) - { - // Choose a straight direction when diagonal target is blocked - if (!wBottom && wLeft) - { - dy = 0; - } - else if (wBottom && !wLeft) - { - dx = 0; - } - else if (!wBottom && !wLeft) - { - return Position(tileX * 32 + radius, - tileY * 32 + 32 - radius); - } - else // Both straight direction are walkable - { - // Go down when below the corner - if (offsetY >= (offsetX / mMap->getTileHeight() - - (offsetX / mMap->getTileWidth() - * mMap->getTileHeight()))) - { - dx = 0; - } - else // Go left otherwise - { - dy = 0; - } - } - } - else // The diagonal is walkable - { - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX - 32, posY + 32)); - } - } - - // Going bottom-right - if (dx > 0 && dy > 0) - { - if (!wBottomRight) - { - // Choose a straight direction when diagonal target is blocked - if (!wBottom && wRight) - { - dy = 0; - } - else if (wBottom && !wRight) - { - dx = 0; - } - else if (!wBottom && !wRight) - { - return Position(tileX * 32 + 32 - radius, - tileY * 32 + 32 - radius); - } - else // Both straight direction are walkable - { - // Go down when below the corner - if (offsetY >= (offsetX / mMap->getTileWidth() - * mMap->getTileHeight())) - { - dx = 0; - } - else // Go right otherwise - { - dy = 0; - } - } - } - else // The diagonal is walkable - { - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX + 32, posY + 32)); - } - } - } // End of diagonal cases - - // Straight directions - // Right direction - if (dx > 0 && !dy) - { - // If the straight destination is blocked, - // Make the player go the closest possible. - if (!wRight) - { - return Position(tileX * 32 + 32 - radius, posY); - } - else - { - if (!wTopRight) - { - // If we're going to collide with the top-right corner - if (offsetY - radius < 0) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + 32 - radius, - tileY * 32 + radius); - } - } - - if (!wBottomRight) - { - // If we're going to collide with the bottom-right corner - if (offsetY + radius > 32) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + 32 - radius, - tileY * 32 + 32 - radius); - } - } - // If the way is clear, step up one checked tile ahead. - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX + 32, posY)); - } - } - - // Left direction - if (dx < 0 && !dy) - { - // If the straight destination is blocked, - // Make the player go the closest possible. - if (!wLeft) - { - return Position(tileX * 32 + radius, posY); - } - else - { - if (!wTopLeft) - { - // If we're going to collide with the top-left corner - if (offsetY - radius < 0) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + radius, - tileY * 32 + radius); - } - } - - if (!wBottomLeft) - { - // If we're going to collide with the bottom-left corner - if (offsetY + radius > 32) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + radius, - tileY * 32 + 32 - radius); - } - } - // If the way is clear, step up one checked tile ahead. - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX - 32, posY)); - } - } - - // Up direction - if (!dx && dy < 0) - { - // If the straight destination is blocked, - // Make the player go the closest possible. - if (!wTop) - { - return Position(posX, tileY * 32 + radius); - } - else - { - if (!wTopLeft) - { - // If we're going to collide with the top-left corner - if (offsetX - radius < 0) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + radius, - tileY * 32 + radius); - } - } - - if (!wTopRight) - { - // If we're going to collide with the top-right corner - if (offsetX + radius > 32) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + 32 - radius, - tileY * 32 + radius); - } - } - // If the way is clear, step up one checked tile ahead. - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX, posY - 32)); - } - } - - // Down direction - if (!dx && dy > 0) - { - // If the straight destination is blocked, - // Make the player go the closest possible. - if (!wBottom) - { - return Position(posX, tileY * 32 + 32 - radius); - } - else - { - if (!wBottomLeft) - { - // If we're going to collide with the bottom-left corner - if (offsetX - radius < 0) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + radius, - tileY * 32 + 32 - radius); - } - } - - if (!wBottomRight) - { - // If we're going to collide with the bottom-right corner - if (offsetX + radius > 32) - { - // We make the player corrects its offset - // before going further - return Position(tileX * 32 + 32 - radius, - tileY * 32 + 32 - radius); - } - } - // If the way is clear, step up one checked tile ahead. - return mMap->checkNodeOffsets(radius, - walkMask, Position(posX, posY + 32)); - } - } - - // Return the current position if everything else has failed. - return Position(posX, posY); -} -#endif - -void LocalPlayer::nextTile(unsigned char dir A_UNUSED = 0) -{ -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - Party *const party = Party::getParty(1); - if (party) - { - PartyMember *const pm = party->getMember(getName()); - if (pm) - { - pm->setX(mX); - pm->setY(mY); - } - } - - if (mPath.empty()) - { - if (mPickUpTarget) - pickUp(mPickUpTarget); - - if (mWalkingDir) - startWalking(mWalkingDir); - } - else if (mPath.size() == 1) - { - if (mPickUpTarget) - pickUp(mPickUpTarget); - } - - if (mGoingToTarget && mTarget && withinAttackRange(mTarget)) - { - mAction = Being::STAND; - attack(mTarget, true); - mGoingToTarget = false; - mPath.clear(); - return; - } - else if (mGoingToTarget && !mTarget) - { - mGoingToTarget = false; - mPath.clear(); - } - - if (mPath.empty()) - { - if (mNavigatePath.empty() || mAction != MOVE) - setAction(STAND); - else - mNextStep = true; - } - else - { - Being::nextTile(); - } - } -#ifdef MANASERV_SUPPORT - else - { - if (!mMap || !dir) - return; - - const Vector &pos = getPosition(); - const Position destination = getNextWalkPosition(dir); - - if (static_cast(pos.x) != destination.x - || static_cast(pos.y) != destination.y) - { - setDestination(destination.x, destination.y); - } - else if (dir != mDirection) - { - Net::getPlayerHandler()->setDirection(dir); - setDirection(dir); - } - } -#endif -} - -bool LocalPlayer::pickUp(FloorItem *const item) -{ - if (!item) - return false; - - if (!client->limitPackets(PACKET_PICKUP)) - return false; - - const int dx = item->getTileX() - mX; - const int dy = item->getTileY() - mY; - int dist = 6; - - if (mPickUpType >= 4 && mPickUpType <= 6) - dist = 4; - - if (dx * dx + dy * dy < dist) - { - if (actorSpriteManager && actorSpriteManager->checkForPickup(item)) - { - Net::getPlayerHandler()->pickUp(item); - mPickUpTarget = nullptr; - } - } - else if (mPickUpType >= 4 && mPickUpType <= 6) - { -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - setDestination(item->getPixelX() + 16, item->getPixelY() + 16); - mPickUpTarget = item; - mPickUpTarget->addActorSpriteListener(this); - } - else -#endif - { - const Vector &playerPos = getPosition(); - const Path debugPath = mMap->findPath( - static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - item->getTileX(), item->getTileY(), getWalkMask(), 0); - if (!debugPath.empty()) - navigateTo(item->getTileX(), item->getTileY()); - else - setDestination(item->getTileX(), item->getTileY()); - - mPickUpTarget = item; - mPickUpTarget->addActorSpriteListener(this); - } - } - return true; -} - -void LocalPlayer::actorSpriteDestroyed(const ActorSprite &actorSprite) -{ - if (mPickUpTarget == &actorSprite) - mPickUpTarget = nullptr; -} - -Being *LocalPlayer::getTarget() const -{ - return mTarget; -} - -void LocalPlayer::setTarget(Being *const target) -{ - if (target == this && target) - return; - - if (target == mTarget) - return; - - Being *oldTarget = nullptr; - if (mTarget) - { - mTarget->untarget(); - oldTarget = mTarget; - } - - if (mTarget && mTarget->getType() == ActorSprite::MONSTER) - mTarget->setShowName(false); - - mTarget = target; - - if (oldTarget) - oldTarget->updateName(); - - if (mTarget) - { - mLastTargetX = mTarget->getTileX(); - mLastTargetY = mTarget->getTileY(); - mTarget->updateName(); - } - - if (target && target->getType() == ActorSprite::MONSTER) - target->setShowName(true); -} - -void LocalPlayer::setDestination(const int x, const int y) -{ - mActivityTime = cur_time; - - if (getAttackType() == 0 || !mAttackMoving) - mKeepAttacking = false; - - // Only send a new message to the server when destination changes - if (x != mDest.x || y != mDest.y) - { - if (mInvertDirection != 1) - { - Net::getPlayerHandler()->setDestination(x, y, mDirection); - Being::setDestination(x, y); - } - else if (mInvertDirection == 1) - { - uint8_t newDir = 0; - if (mDirection & UP) - newDir |= DOWN; - if (mDirection & LEFT) - newDir |= RIGHT; - if (mDirection & DOWN) - newDir |= UP; - if (mDirection & RIGHT) - newDir |= LEFT; - - Net::getPlayerHandler()->setDestination(x, y, newDir); - -// if (client->limitPackets(PACKET_DIRECTION)) - { - setDirection(newDir); - Net::getPlayerHandler()->setDirection(newDir); - } - - Being::setDestination(x, y); - } - else - { -#ifdef MANASERV_SUPPORT - // Manaserv: - // If the destination given to being class is accepted, - // we inform the Server. - if ((x == mDest.x && y == mDest.y) - || Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - Net::getPlayerHandler()->setDestination(x, y, mDirection); - } - } - } -} - -void LocalPlayer::setWalkingDir(const unsigned char dir) -{ - // This function is called by Game::handleInput() - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - // First if player is pressing key for the direction he is already - // going, do nothing more... - - // Else if he is pressing a key, and its different from what he has - // been pressing, stop (do not send this stop to the server) and - // start in the new direction - if (dir && (dir != getWalkingDir())) - stopWalking(false); - - // Else, he is not pressing a key, - // and the current path hasn't been sent by mouse, - // then, stop (sending to server). - else if (!dir) - { - if (!mPathSetByMouse) - stopWalking(true); - return; - } - - // If the delay to send another walk message to the server hasn't - // expired, don't do anything or we could get disconnected for - // spamming the server - if (get_elapsed_time(mLocalWalkTime) < walkingKeyboardDelay) - return; - } -#endif - - mWalkingDir = dir; - - // If we're not already walking, start walking. - if (mAction != MOVE && dir) - { - startWalking(dir); - } -#ifdef MANASERV_SUPPORT - else if (mAction == MOVE && (Net::getNetworkType() - == ServerInfo::MANASERV)) - { - nextTile(dir); - } -#endif -} - -void LocalPlayer::startWalking(const unsigned char dir) -{ - // This function is called by setWalkingDir(), - // but also by nextTile() for TMW-Athena... - if (!mMap || !dir) - return; - - mPickUpTarget = nullptr; - if (mAction == MOVE && !mPath.empty()) - { - // Just finish the current action, otherwise we get out of sync -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - const Vector &pos = getPosition(); - Being::setDestination(static_cast(pos.x), - static_cast(pos.y)); - } - else -#endif - { - Being::setDestination(mX, mY); - } - return; - } - - int dx = 0, dy = 0; - if (dir & UP) - dy--; - if (dir & DOWN) - dy++; - if (dir & LEFT) - dx--; - if (dir & RIGHT) - dx++; - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - const unsigned char walkMask = getWalkMask(); - // Prevent skipping corners over colliding tiles - if (dx && !mMap->getWalk(mX + dx, mY, walkMask)) - dx = 0; - if (dy && !mMap->getWalk(mX, mY + dy, walkMask)) - dy = 0; - - // Choose a straight direction when diagonal target is blocked - if (dx && dy && !mMap->getWalk(mX + dx, mY + dy, walkMask)) - dx = 0; - - // Walk to where the player can actually go - if ((dx || dy) && mMap->getWalk(mX + dx, mY + dy, walkMask)) - { - setDestination(mX + dx, mY + dy); - } - else if (dir != mDirection) - { - // If the being can't move, just change direction - -// if (client->limitPackets(PACKET_DIRECTION)) - { - Net::getPlayerHandler()->setDirection(dir); - setDirection(dir); - } - } - } -#ifdef MANASERV_SUPPORT - else - { - nextTile(dir); - } -#endif -} - -void LocalPlayer::stopWalking(const bool sendToServer) -{ - if (mAction == MOVE && mWalkingDir) - { - mWalkingDir = 0; - mLocalWalkTime = 0; - mPickUpTarget = nullptr; - - setDestination(static_cast(getPosition().x), - static_cast(getPosition().y)); - if (sendToServer) - { - Net::getPlayerHandler()->setDestination( - static_cast(getPosition().x), - static_cast(getPosition().y), -1); - } - setAction(STAND); - } - - // No path set anymore, so we reset the path by mouse flag - mPathSetByMouse = false; - - clearPath(); - navigateClean(); -} - -bool LocalPlayer::toggleSit() const -{ - if (!client->limitPackets(PACKET_SIT)) - return false; - - Being::Action newAction; - switch (mAction) - { - case STAND: - case SPAWN: - newAction = SIT; - break; - case SIT: - newAction = STAND; - break; - case MOVE: - case ATTACK: - case DEAD: - case HURT: - default: - return true; - } - - Net::getPlayerHandler()->changeAction(newAction); - return true; -} - -bool LocalPlayer::updateSit() const -{ - if (!client->limitPackets(PACKET_SIT)) - return false; - - Net::getPlayerHandler()->changeAction(mAction); - return true; -} - -bool LocalPlayer::emote(const uint8_t emotion) -{ - if (!client->limitPackets(PACKET_EMOTE)) - return false; - - Net::getPlayerHandler()->emote(emotion); - return true; -} - -void LocalPlayer::attack(Being *const target, const bool keep, - const bool dontChangeEquipment) -{ -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - if (mLastAction != -1) - return; - - // Can only attack when standing still - if (mAction != STAND && mAction != ATTACK) - return; - } -#endif - - mKeepAttacking = keep; - - if (!target || target->getType() == ActorSprite::NPC) - return; - - if (mTarget != target || !mTarget) - setTarget(target); - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - const Vector &plaPos = this->getPosition(); - const Vector &tarPos = mTarget->getPosition(); - const int dist_x = static_cast(plaPos.x - tarPos.x); - const int dist_y = static_cast(plaPos.y - tarPos.y); - - if (abs(dist_y) >= abs(dist_x)) - { - if (dist_y < 0) - setDirection(DOWN); - else - setDirection(UP); - } - else - { - if (dist_x < 0) - setDirection(RIGHT); - else - setDirection(LEFT); - } - - mLastAction = tick_time; - } - else -#endif - { - const int dist_x = target->getTileX() - mX; - const int dist_y = target->getTileY() - mY; - - // Must be standing or sitting to attack - if (mAction != STAND && mAction != SIT) - return; - - if (abs(dist_y) >= abs(dist_x)) - { - if (dist_y > 0) - setDirection(DOWN); - else - setDirection(UP); - } - else - { - if (dist_x > 0) - setDirection(RIGHT); - else - setDirection(LEFT); - } - - mActionTime = tick_time; - } - - if (target->getType() != Being::PLAYER || checAttackPermissions(target)) - { - setAction(ATTACK); - - if (!client->limitPackets(PACKET_ATTACK)) - return; - - if (!dontChangeEquipment) - changeEquipmentBeforeAttack(target); - - Net::getPlayerHandler()->attack(target->getId(), mServerAttack); - } - -#ifdef MANASERV_SUPPORT - if ((Net::getNetworkType() != ServerInfo::MANASERV) && !keep) -#else - if (!keep) -#endif - stopAttack(); -} - -void LocalPlayer::stopAttack(const bool keepAttack) -{ - if (!client->limitPackets(PACKET_STOPATTACK)) - return; - - if (mServerAttack && mAction == ATTACK) - Net::getPlayerHandler()->stopAttack(); - - untarget(); - if (!keepAttack || !mAttackNext) - mKeepAttacking = false; -} - -void LocalPlayer::untarget() -{ - if (mAction == ATTACK) - setAction(STAND); - - if (mTarget) - setTarget(nullptr); -} - -void LocalPlayer::pickedUp(const ItemInfo &itemInfo, const int amount, - const unsigned char color, const int floorItemId, - const unsigned char fail) -{ - if (fail) - { - if (actorSpriteManager && floorItemId) - { - FloorItem *const item = actorSpriteManager->findItem(floorItemId); - if (item) - { - if (!item->getShowMsg()) - return; - item->setShowMsg(false); - } - } - const char* msg; - switch (fail) - { - case PICKUP_BAD_ITEM: - // TRANSLATORS: pickup error message - msg = N_("Tried to pick up nonexistent item."); - break; - case PICKUP_TOO_HEAVY: - // TRANSLATORS: pickup error message - msg = N_("Item is too heavy."); - break; - case PICKUP_TOO_FAR: - // TRANSLATORS: pickup error message - msg = N_("Item is too far away."); - break; - case PICKUP_INV_FULL: - // TRANSLATORS: pickup error message - msg = N_("Inventory is full."); - break; - case PICKUP_STACK_FULL: - // TRANSLATORS: pickup error message - msg = N_("Stack is too big."); - break; - case PICKUP_DROP_STEAL: - // TRANSLATORS: pickup error message - msg = N_("Item belongs to someone else."); - break; - default: - // TRANSLATORS: pickup error message - msg = N_("Unknown problem picking up item."); - break; - } - if (localChatTab && config.getBoolValue("showpickupchat")) - localChatTab->chatLog(gettext(msg), BY_SERVER); - - if (mMap && config.getBoolValue("showpickupparticle")) - { - // Show pickup notification - addMessageToQueue(gettext(msg), UserPalette::PICKUP_INFO); - } - } - else - { - std::string str; - if (serverVersion > 0) - str = itemInfo.getName(color); - else - str = itemInfo.getName(); - - if (config.getBoolValue("showpickupchat") && localChatTab) - { - // TRANSLATORS: %d is number, - // [@@%d|%s@@] - here player can see link to item - localChatTab->chatLog(strprintf(ngettext("You picked up %d " - "[@@%d|%s@@].", "You picked up %d [@@%d|%s@@].", amount), - amount, itemInfo.getId(), str.c_str()), BY_SERVER); - } - - if (mMap && config.getBoolValue("showpickupparticle")) - { - // Show pickup notification - if (amount > 1) - { - addMessageToQueue(strprintf("%d x %s", amount, - str.c_str()), UserPalette::PICKUP_INFO); - } - else - { - addMessageToQueue(str, UserPalette::PICKUP_INFO); - } - } - } -} - -int LocalPlayer::getAttackRange() const -{ - if (mAttackRange > -1) - { - return mAttackRange; - } - else - { - const Item *const weapon = PlayerInfo::getEquipment(EQUIP_FIGHT1_SLOT); - if (weapon) - { - const ItemInfo &info = weapon->getInfo(); - return info.getAttackRange(); - } - return 48; // unarmed range - } -} - -bool LocalPlayer::withinAttackRange(const Being *const target, - const bool fixDistance, - const int addRange) const -{ - if (!target) - return false; - - int range = getAttackRange() + addRange; - int dx; - int dy; - - if (fixDistance && range == 1) - range = 2; - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - const Vector &targetPos = target->getPosition(); - const Vector &pos = getPosition(); - dx = static_cast(abs(static_cast(targetPos.x - pos.x))); - dy = static_cast(abs(static_cast(targetPos.y - pos.y))); - } - else -#endif - { - dx = static_cast(abs(target->getTileX() - mX)); - dy = static_cast(abs(target->getTileY() - mY)); - } - return !(dx > range || dy > range); -} - -void LocalPlayer::setGotoTarget(Being *const target) -{ - if (!target) - return; - - mPickUpTarget = nullptr; -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - mTarget = target; - mGoingToTarget = true; - const Vector &targetPos = target->getPosition(); - setDestination(static_cast(targetPos.x), - static_cast(targetPos.y)); - } - else -#endif - { - setTarget(target); - mGoingToTarget = true; - setDestination(target->getTileX(), target->getTileY()); - } -} - -void LocalPlayer::handleStatusEffect(StatusEffect *const effect, - const int effectId) -{ - Being::handleStatusEffect(effect, effectId); - - if (effect) - { - effect->deliverMessage(); - effect->playSFX(); - - AnimatedSprite *const sprite = effect->getIcon(); - - if (!sprite) - { - // delete sprite, if necessary - for (unsigned int i = 0; i < mStatusEffectIcons.size(); ) - { - if (mStatusEffectIcons[i] == effectId) - { - mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i); - if (miniStatusWindow) - miniStatusWindow->eraseIcon(i); - } - else - { - i++; - } - } - } - else - { - // replace sprite or append - bool found = false; - const unsigned int sz = mStatusEffectIcons.size(); - for (unsigned int i = 0; i < sz; i++) - { - if (mStatusEffectIcons[i] == effectId) - { - if (miniStatusWindow) - miniStatusWindow->setIcon(i, sprite); - found = true; - break; - } - } - - if (!found) - { // add new - const int offset = static_cast(mStatusEffectIcons.size()); - if (miniStatusWindow) - miniStatusWindow->setIcon(offset, sprite); - mStatusEffectIcons.push_back(effectId); - } - } - } -} - -void LocalPlayer::addMessageToQueue(const std::string &message, - const int color) -{ - if (mMessages.size() < 20) - mMessages.push_back(MessagePair(message, color)); -} - -void LocalPlayer::optionChanged(const std::string &value) -{ - if (value == "showownname") - setShowName(config.getBoolValue("showownname")); - else if (value == "targetDeadPlayers") - mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers"); - else if (value == "enableBuggyServers") - mIsServerBuggy = serverConfig.getBoolValue("enableBuggyServers"); - else if (value == "syncPlayerMove") - mSyncPlayerMove = config.getBoolValue("syncPlayerMove"); - else if (value == "drawPath") - mDrawPath = config.getBoolValue("drawPath"); - else if (value == "serverAttack") - mServerAttack = config.getBoolValue("serverAttack"); - else if (value == "attackMoving") - mAttackMoving = config.getBoolValue("attackMoving"); - else if (value == "attackNext") - mAttackNext = config.getBoolValue("attackNext"); - else if (value == "showJobExp") - mShowJobExp = config.getBoolValue("showJobExp"); - else if (value == "enableAdvert") - mEnableAdvert = config.getBoolValue("enableAdvert"); - else if (value == "tradebot") - mTradebot = config.getBoolValue("tradebot"); - else if (value == "targetOnlyReachable") - mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable"); -} - -void LocalPlayer::processEvent(Channels channel, - const DepricatedEvent &event) -{ - if (channel == CHANNEL_ATTRIBUTES) - { - if (event.getName() == EVENT_UPDATEATTRIBUTE) - { - switch (event.getInt("id")) - { - case PlayerInfo::EXP: - { - if (event.getInt("oldValue") > event.getInt("newValue")) - break; - - const int change = event.getInt("newValue") - - event.getInt("oldValue"); - - if (change != 0) - { - // TRANSLATORS: get xp message - addMessageToQueue(strprintf("%d %s", change, _("xp"))); - } - break; - } - case PlayerInfo::LEVEL: - mLevel = event.getInt("newValue"); - break; - default: - break; - }; - } - else if (event.getName() == EVENT_UPDATESTAT) - { - if (!mShowJobExp) - return; - - const int id = event.getInt("id"); - if (id == Net::getPlayerHandler()->getJobLocation()) - { - const std::pair exp - = PlayerInfo::getStatExperience(id); - if (event.getInt("oldValue1") > exp.first - || !event.getInt("oldValue2")) - { - return; - } - - const int change = exp.first - event.getInt("oldValue1"); - if (change != 0 && mMessages.size() < 20) - { - if (!mMessages.empty()) - { - MessagePair pair = mMessages.back(); - // TRANSLATORS: this is normal experience - if (pair.first.find(strprintf(" %s", - _("xp"))) == pair.first.size() - - strlen(_("xp")) - 1) - { - mMessages.pop_back(); - // TRANSLATORS: this is job experience - pair.first.append(strprintf(", %d %s", - change, _("job"))); - mMessages.push_back(pair); - } - else - { - // TRANSLATORS: this is job experience - addMessageToQueue(strprintf("%d %s", - change, _("job"))); - } - } - else - { - // TRANSLATORS: this is job experience - addMessageToQueue(strprintf( - "%d %s", change, _("job"))); - } - } - } - } - } -} - -void LocalPlayer::moveTo(const int x, const int y) -{ - setDestination(x, y); -} - -void LocalPlayer::move(const int dX, const int dY) -{ - mPickUpTarget = nullptr; - moveTo(mX + dX, mY + dY); -} - -void LocalPlayer::moveToTarget(int dist) -{ - bool gotPos(false); - Path debugPath; - - const Vector &playerPos = getPosition(); - unsigned int limit(0); - - if (dist == -1) - { - dist = mMoveToTargetType; - if (mMoveToTargetType == 0) - { - dist = 0; - } - else - { - switch (mMoveToTargetType) - { - case 1: - dist = 1; - break; - case 2: - dist = 2; - break; - case 3: - dist = 3; - break; - case 4: - dist = 5; - break; - case 5: - dist = 7; - break; - case 6: - case 7: - dist = mAttackRange; - if (dist == 1 && serverVersion < 1) - dist = 2; - break; - case 8: - dist = mAttackRange - 1; - if (dist < 1) - dist = 1; - if (dist == 1 && serverVersion < 1) - dist = 2; - break; - default: - break; - } - } - } - - if (mTarget) - { - if (mMap) - { - debugPath = mMap->findPath(static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - mTarget->getTileX(), mTarget->getTileY(), getWalkMask(), 0); - } - - const unsigned int sz = debugPath.size(); - if (sz < static_cast(dist)) - return; - limit = static_cast(sz) - dist; - gotPos = true; - } - else if (mNavigateX || mNavigateY) - { - debugPath = mNavigatePath; - limit = dist; - gotPos = true; - } - - if (gotPos) - { - if (dist == 0) - { - if (mTarget) - navigateTo(mTarget); - } - else - { - Position pos(0, 0); - unsigned int f = 0; - - for (Path::const_iterator i = debugPath.begin(), - i_end = debugPath.end(); - i != i_end && f < limit; ++i, f++) - { - pos = (*i); - } - navigateTo(pos.x, pos.y); - } - } - else if (mLastTargetX || mLastTargetY) - { - navigateTo(mLastTargetX, mLastTargetY); - } -} - -void LocalPlayer::moveToHome() -{ - mPickUpTarget = nullptr; - if ((mX != mCrossX || mY != mCrossY) && mCrossX && mCrossY) - { - moveTo(mCrossX, mCrossY); - } - else if (mMap) - { - const std::map::const_iterator iter = - mHomes.find(mMap->getProperty("_realfilename")); - - if (iter != mHomes.end()) - { - const Vector pos = mHomes[(*iter).first]; - if (mX == pos.x && mY == pos.y) - { - Net::getPlayerHandler()->setDestination( - static_cast(pos.x), - static_cast(pos.y), - static_cast(mDirection)); - } - else - { - navigateTo(static_cast(pos.x), static_cast(pos.y)); - } - } - } -} - -static const unsigned invertDirectionSize = 5; - -void LocalPlayer::changeMode(unsigned *const var, const unsigned limit, - const char *const conf, - std::string (LocalPlayer::*const func)(), - const unsigned def, - const bool save) -{ - if (!var) - return; - - (*var) ++; - if (*var >= limit) - *var = def; - if (save) - config.setValue(conf, *var); - if (miniStatusWindow) - miniStatusWindow->updateStatus(); - const std::string str = (this->*func)(); - if (str.size() > 4) - debugMsg(str.substr(4)); -} - -void LocalPlayer::invertDirection() -{ - mMoveState = 0; - changeMode(&mInvertDirection, invertDirectionSize, "invertMoveDirection", - &LocalPlayer::getInvertDirectionString, 0, false); -} - -static const char *const invertDirectionStrings[] = -{ - // TRANSLATORS: move type in status bar - N_("(D) default moves"), - // TRANSLATORS: move type in status bar - N_("(I) invert moves"), - // TRANSLATORS: move type in status bar - N_("(c) moves with some crazy moves"), - // TRANSLATORS: move type in status bar - N_("(C) moves with crazy moves"), - // TRANSLATORS: move type in status bar - N_("(d) double normal + crazy"), - // TRANSLATORS: move type in status bar - N_("(?) unknown move") -}; - -std::string LocalPlayer::getInvertDirectionString() -{ - return gettext(getVarItem(&invertDirectionStrings[0], - mInvertDirection, invertDirectionSize)); -} - -static const unsigned crazyMoveTypeSize = 11; - -void LocalPlayer::changeCrazyMoveType() -{ - mCrazyMoveState = 0; - changeMode(&mCrazyMoveType, crazyMoveTypeSize, "crazyMoveType", - &LocalPlayer::getCrazyMoveTypeString, 1); -} - -std::string LocalPlayer::getCrazyMoveTypeString() -{ - if (mCrazyMoveType < crazyMoveTypeSize - 1) - { - // TRANSLATORS: crazy move type in status bar - return strprintf(_("(%u) crazy move number %u"), - mCrazyMoveType, mCrazyMoveType); - } - else if (mCrazyMoveType == crazyMoveTypeSize - 1) - { - // TRANSLATORS: crazy move type in status bar - return _("(a) custom crazy move"); - } - else - { - // TRANSLATORS: crazy move type in status bar - return _("(?) crazy move"); - } -} - -static const unsigned moveToTargetTypeSize = 9; - -void LocalPlayer::changeMoveToTargetType() -{ - changeMode(&mMoveToTargetType, moveToTargetTypeSize, "moveToTargetType", - &LocalPlayer::getMoveToTargetTypeString); -} - -static const char *const moveToTargetTypeStrings[] = -{ - // TRANSLATORS: move to target type in status bar - N_("(0) default moves to target"), - // TRANSLATORS: move to target type in status bar - N_("(1) moves to target in distance 1"), - // TRANSLATORS: move to target type in status bar - N_("(2) moves to target in distance 2"), - // TRANSLATORS: move to target type in status bar - N_("(3) moves to target in distance 3"), - // TRANSLATORS: move to target type in status bar - N_("(5) moves to target in distance 5"), - // TRANSLATORS: move to target type in status bar - N_("(7) moves to target in distance 7"), - // TRANSLATORS: move to target type in status bar - N_("(A) moves to target in attack range"), - // TRANSLATORS: move to target type in status bar - N_("(a) archer attack range"), - // TRANSLATORS: move to target type in status bar - N_("(B) moves to target in attack range - 1"), - // TRANSLATORS: move to target type in status bar - N_("(?) move to target") -}; - -std::string LocalPlayer::getMoveToTargetTypeString() -{ - return gettext(getVarItem(&moveToTargetTypeStrings[0], - mMoveToTargetType, moveToTargetTypeSize)); -} - -static const unsigned followModeSize = 4; - -void LocalPlayer::changeFollowMode() -{ - changeMode(&mFollowMode, followModeSize, "followMode", - &LocalPlayer::getFollowModeString); -} - -static const char *const followModeStrings[] = -{ - // TRANSLATORS: folow mode in status bar - N_("(D) default follow"), - // TRANSLATORS: folow mode in status bar - N_("(R) relative follow"), - // TRANSLATORS: folow mode in status bar - N_("(M) mirror follow"), - // TRANSLATORS: folow mode in status bar - N_("(P) pet follow"), - // TRANSLATORS: folow mode in status bar - N_("(?) unknown follow") -}; - -std::string LocalPlayer::getFollowModeString() -{ - return gettext(getVarItem(&followModeStrings[0], - mFollowMode, followModeSize)); -} - -const unsigned attackWeaponTypeSize = 4; - -void LocalPlayer::changeAttackWeaponType() -{ - changeMode(&mAttackWeaponType, attackWeaponTypeSize, "attackWeaponType", - &LocalPlayer::getAttackWeaponTypeString, 1); -} - -static const char *attackWeaponTypeStrings[] = -{ - // TRANSLATORS: switch attack type in status bar - N_("(?) attack"), - // TRANSLATORS: switch attack type in status bar - N_("(D) default attack"), - // TRANSLATORS: switch attack type in status bar - N_("(s) switch attack without shield"), - // TRANSLATORS: switch attack type in status bar - N_("(S) switch attack with shield"), - // TRANSLATORS: switch attack type in status bar - N_("(?) attack") -}; - -std::string LocalPlayer::getAttackWeaponTypeString() -{ - return gettext(getVarItem(&attackWeaponTypeStrings[0], - mAttackWeaponType, attackWeaponTypeSize)); -} - -const unsigned attackTypeSize = 4; - -void LocalPlayer::changeAttackType() -{ - changeMode(&mAttackType, attackTypeSize, "attackType", - &LocalPlayer::getAttackTypeString); -} - -static const char *const attackTypeStrings[] = -{ - // TRANSLATORS: attack type in status bar - N_("(D) default attack"), - // TRANSLATORS: attack type in status bar - N_("(G) go and attack"), - // TRANSLATORS: attack type in status bar - N_("(A) go, attack, pickup"), - // TRANSLATORS: attack type in status bar - N_("(d) without auto attack"), - // TRANSLATORS: attack type in status bar - N_("(?) attack") -}; - -std::string LocalPlayer::getAttackTypeString() -{ - return gettext(getVarItem(&attackTypeStrings[0], - mAttackType, attackTypeSize)); -} - -const unsigned quickDropCounterSize = 31; - -void LocalPlayer::changeQuickDropCounter() -{ - changeMode(&mQuickDropCounter, quickDropCounterSize, "quickDropCounter", - &LocalPlayer::getQuickDropCounterString, 1); -} - -std::string LocalPlayer::getQuickDropCounterString() -{ - if (mQuickDropCounter > 9) - { - return strprintf("(%c) drop counter %u", static_cast( - 'a' + mQuickDropCounter - 10), mQuickDropCounter); - } - else - { - return strprintf("(%u) drop counter %u", - mQuickDropCounter, mQuickDropCounter); - } -} - -void LocalPlayer::setQuickDropCounter(const int n) -{ - if (n < 1 || n >= static_cast(quickDropCounterSize)) - return; - mQuickDropCounter = n; - config.setValue("quickDropCounter", mQuickDropCounter); - if (miniStatusWindow) - miniStatusWindow->updateStatus(); -} - -const unsigned pickUpTypeSize = 7; - -void LocalPlayer::changePickUpType() -{ - changeMode(&mPickUpType, pickUpTypeSize, "pickUpType", - &LocalPlayer::getPickUpTypeString); -} - -static const char *const pickUpTypeStrings[] = -{ - // TRANSLATORS: pickup size in status bar - N_("(S) small pick up 1x1 cells"), - // TRANSLATORS: pickup size in status bar - N_("(D) default pick up 2x1 cells"), - // TRANSLATORS: pickup size in status bar - N_("(F) forward pick up 2x3 cells"), - // TRANSLATORS: pickup size in status bar - N_("(3) pick up 3x3 cells"), - // TRANSLATORS: pickup size in status bar - N_("(g) go and pick up in distance 4"), - // TRANSLATORS: pickup size in status bar - N_("(G) go and pick up in distance 8"), - // TRANSLATORS: pickup size in status bar - N_("(A) go and pick up in max distance"), - // TRANSLATORS: pickup size in status bar - N_("(?) pick up") -}; - -std::string LocalPlayer::getPickUpTypeString() -{ - return gettext(getVarItem(&pickUpTypeStrings[0], - mPickUpType, pickUpTypeSize)); -} - -const unsigned debugPathSize = 5; - -static const char *const debugPathStrings[] = -{ - // TRANSLATORS: map view type in status bar - N_("(N) normal map view"), - // TRANSLATORS: map view type in status bar - N_("(D) debug map view"), - // TRANSLATORS: map view type in status bar - N_("(u) ultra map view"), - // TRANSLATORS: map view type in status bar - N_("(U) ultra map view 2"), - // TRANSLATORS: map view type in status bar - N_("(e) empty map view"), - // TRANSLATORS: map view type in status bar - N_("(b) black & white map view") -}; - -std::string LocalPlayer::getDebugPathString() const -{ - return gettext(getVarItem(&debugPathStrings[0], - viewport->getDebugPath(), debugPathSize)); -} - -const unsigned magicAttackSize = 5; - -void LocalPlayer::switchMagicAttack() -{ - changeMode(&mMagicAttackType, magicAttackSize, "magicAttackType", - &LocalPlayer::getMagicAttackString); -} - -static const char *const magicAttackStrings[] = -{ - // TRANSLATORS: magic attack in status bar - N_("(f) use #flar for magic attack"), - // TRANSLATORS: magic attack in status bar - N_("(c) use #chiza for magic attack"), - // TRANSLATORS: magic attack in status bar - N_("(I) use #ingrav for magic attack"), - // TRANSLATORS: magic attack in status bar - N_("(F) use #frillyar for magic attack"), - // TRANSLATORS: magic attack in status bar - N_("(U) use #upmarmu for magic attack"), - // TRANSLATORS: magic attack in status bar - N_("(?) magic attack") -}; - -std::string LocalPlayer::getMagicAttackString() -{ - return gettext(getVarItem(&magicAttackStrings[0], - mMagicAttackType, magicAttackSize)); -} - -const unsigned pvpAttackSize = 4; - -void LocalPlayer::switchPvpAttack() -{ - changeMode(&mPvpAttackType, pvpAttackSize, "pvpAttackType", - &LocalPlayer::getPvpAttackString); -} - -static const char *const pvpAttackStrings[] = -{ - // TRANSLATORS: player attack type in status bar - N_("(a) attack all players"), - // TRANSLATORS: player attack type in status bar - N_("(f) attack all except friends"), - // TRANSLATORS: player attack type in status bar - N_("(b) attack bad relations"), - // TRANSLATORS: player attack type in status bar - N_("(d) don't attack players"), - // TRANSLATORS: player attack type in status bar - N_("(?) pvp attack") -}; - -std::string LocalPlayer::getPvpAttackString() -{ - return gettext(getVarItem(&pvpAttackStrings[0], - mPvpAttackType, pvpAttackSize)); -} - -const unsigned imitationModeSize = 2; - -void LocalPlayer::changeImitationMode() -{ - changeMode(&mImitationMode, imitationModeSize, "imitationMode", - &LocalPlayer::getImitationModeString); -} - -static const char *const imitationModeStrings[] = -{ - // TRANSLATORS: imitation type in status bar - N_("(D) default imitation"), - // TRANSLATORS: imitation type in status bar - N_("(O) outfits imitation"), - // TRANSLATORS: imitation type in status bar - N_("(?) imitation") -}; - -std::string LocalPlayer::getImitationModeString() -{ - return gettext(getVarItem(&imitationModeStrings[0], - mImitationMode, imitationModeSize)); -} - -const unsigned awayModeSize = 2; - -void LocalPlayer::changeAwayMode() -{ - mAwayMode = !mAwayMode; - mAfkTime = 0; - mInactive = false; - updateName(); - if (miniStatusWindow) - miniStatusWindow->updateStatus(); - if (mAwayMode) - { - if (chatWindow) - chatWindow->clearAwayLog(); - - cancelFollow(); - navigateClean(); - if (outfitWindow) - outfitWindow->wearAwayOutfit(); - // TRANSLATORS: away message box header - mAwayDialog = new OkDialog(_("Away"), - config.getStringValue("afkMessage"), - DIALOG_SILENCE, true, false); - mAwayDialog->addActionListener(mAwayListener); - soundManager.volumeOff(); - addAfkEffect(); - } - else - { - mAwayDialog = nullptr; - soundManager.volumeRestore(); - if (chatWindow) - { - chatWindow->displayAwayLog(); - chatWindow->clearAwayLog(); - } - removeAfkEffect(); - } -} - -static const char *awayModeStrings[] = -{ - // TRANSLATORS: away type in status bar - N_("(O) on keyboard"), - // TRANSLATORS: away type in status bar - N_("(A) away"), - // TRANSLATORS: away type in status bar - N_("(?) away") -}; - -std::string LocalPlayer::getAwayModeString() -{ - return gettext(getVarItem(&awayModeStrings[0], - mAwayMode, awayModeSize)); -} - -const unsigned cameraModeSize = 2; - -static const char *cameraModeStrings[] = -{ - // TRANSLATORS: camera mode in status bar - N_("(G) game camera mode"), - // TRANSLATORS: camera mode in status bar - N_("(F) free camera mode"), - // TRANSLATORS: camera mode in status bar - N_("(?) away") -}; - -std::string LocalPlayer::getCameraModeString() const -{ - return gettext(getVarItem(&cameraModeStrings[0], - viewport->getCameraMode(), cameraModeSize)); -} - -const unsigned gameModifiersSize = 2; - -void LocalPlayer::switchGameModifiers() -{ - mDisableGameModifiers = !mDisableGameModifiers; - config.setValue("disableGameModifiers", mDisableGameModifiers); - miniStatusWindow->updateStatus(); - - const std::string str = getGameModifiersString(); - if (str.size() > 4) - debugMsg(str.substr(4)); -} - -static const char *const gameModifiersStrings[] = -{ - // TRANSLATORS: game modifiers state in status bar - N_("Game modifiers are enabled"), - // TRANSLATORS: game modifiers state in status bar - N_("Game modifiers are disabled"), - // TRANSLATORS: game modifiers state in status bar - N_("Game modifiers are unknown") -}; - -std::string LocalPlayer::getGameModifiersString() -{ - return gettext(getVarItem(&gameModifiersStrings[0], - mDisableGameModifiers, gameModifiersSize)); -} - - -void LocalPlayer::changeEquipmentBeforeAttack(const Being *const target) const -{ - if (mAttackWeaponType == 1 || !target || !PlayerInfo::getInventory()) - return; - - bool allowSword = false; - const int dx = target->getTileX() - mX; - const int dy = target->getTileY() - mY; - const Item *item = nullptr; - - if (dx * dx + dy * dy > 80) - return; - - if (dx * dx + dy * dy < 8) - allowSword = true; - - const Inventory *const inv = PlayerInfo::getInventory(); - if (!inv) - return; - - // if attack distance for sword - if (allowSword) - { - // finding sword - item = inv->findItem(571, 0); - - if (!item) - item = inv->findItem(570, 0); - - if (!item) - item = inv->findItem(579, 0); - - if (!item) - item = inv->findItem(867, 0); - - if (!item) - item = inv->findItem(536, 0); - - if (!item) - item = inv->findItem(758, 0); - - // no swords - if (!item) - return; - - // if sword not equiped - if (!item->isEquipped()) - Net::getInventoryHandler()->equipItem(item); - - // if need equip shield too - if (mAttackWeaponType == 3) - { - // finding shield - item = inv->findItem(601, 0); - if (!item) - item = inv->findItem(602, 0); - if (item && !item->isEquipped()) - Net::getInventoryHandler()->equipItem(item); - } - } - // big distance. allowed only bow - else - { - // finding bow - item = inv->findItem(545, 0); - - if (!item) - item = inv->findItem(530, 0); - - // no bow - if (!item) - return; - - if (!item->isEquipped()) - Net::getInventoryHandler()->equipItem(item); - } -} - -void LocalPlayer::crazyMove() -{ - const bool oldDisableCrazyMove = mDisableCrazyMove; - mDisableCrazyMove = true; - switch (mCrazyMoveType) - { - case 1: - crazyMove1(); - break; - case 2: - crazyMove2(); - break; - case 3: - crazyMove3(); - break; - case 4: - crazyMove4(); - break; - case 5: - crazyMove5(); - break; - case 6: - crazyMove6(); - break; - case 7: - crazyMove7(); - break; - case 8: - crazyMove8(); - break; - case 9: - crazyMove9(); - break; - case 10: - crazyMoveA(); - break; - default: - break; - } - mDisableCrazyMove = oldDisableCrazyMove; -} - -void LocalPlayer::crazyMove1() -{ - if (mAction == MOVE) - return; - -// if (!client->limitPackets(PACKET_DIRECTION)) -// return; - - if (mDirection == Being::UP) - { - setWalkingDir(Being::UP); - setDirection(Being::LEFT); - Net::getPlayerHandler()->setDirection(Being::LEFT); - } - else if (mDirection == Being::LEFT) - { - setWalkingDir(Being::LEFT); - setDirection(Being::DOWN); - Net::getPlayerHandler()->setDirection(Being::DOWN); - } - else if (mDirection == Being::DOWN) - { - setWalkingDir(Being::DOWN); - setDirection(Being::RIGHT); - Net::getPlayerHandler()->setDirection(Being::RIGHT); - } - else if (mDirection == Being::RIGHT) - { - setWalkingDir(Being::RIGHT); - setDirection(Being::UP); - Net::getPlayerHandler()->setDirection(Being::UP); - } -} - -void LocalPlayer::crazyMove2() -{ - if (mAction == MOVE) - return; - -// if (!client->limitPackets(PACKET_DIRECTION)) -// return; - - if (mDirection == Being::UP) - { - setWalkingDir(Being::UP | Being::LEFT); - setDirection(Being::RIGHT); - Net::getPlayerHandler()->setDirection(Being::DOWN | Being::RIGHT); - } - else if (mDirection == Being::RIGHT) - { - setWalkingDir(Being::UP | Being::RIGHT); - setDirection(Being::DOWN); - Net::getPlayerHandler()->setDirection(Being::DOWN | Being::LEFT); - } - else if (mDirection == Being::DOWN) - { - setWalkingDir(Being::DOWN | Being::RIGHT); - setDirection(Being::LEFT); - Net::getPlayerHandler()->setDirection(Being::UP | Being::LEFT); - } - else if (mDirection == Being::LEFT) - { - setWalkingDir(Being::DOWN | Being::LEFT); - setDirection(Being::UP); - Net::getPlayerHandler()->setDirection(Being::UP | Being::RIGHT); - } -} - -void LocalPlayer::crazyMove3() -{ - if (mAction == MOVE) - return; - - switch (mCrazyMoveState) - { - case 0: - move(1, 1); - mCrazyMoveState = 1; - break; - case 1: - move(1, -1); - mCrazyMoveState = 2; - break; - case 2: - move(-1, -1); - mCrazyMoveState = 3; - break; - case 3: - move(-1, 1); - mCrazyMoveState = 0; - break; - default: - break; - } - -// if (!client->limitPackets(PACKET_DIRECTION)) -// return; - - setDirection(Being::DOWN); - Net::getPlayerHandler()->setDirection(Being::DOWN); -} - -void LocalPlayer::crazyMove4() -{ - if (mAction == MOVE) - return; - - switch (mCrazyMoveState) - { - case 0: - move(7, 0); - mCrazyMoveState = 1; - break; - case 1: - move(-7, 0); - mCrazyMoveState = 0; - break; - default: - break; - } -} - -void LocalPlayer::crazyMove5() -{ - if (mAction == MOVE) - return; - - switch (mCrazyMoveState) - { - case 0: - move(0, 7); - mCrazyMoveState = 1; - break; - case 1: - move(0, -7); - mCrazyMoveState = 0; - break; - default: - break; - } -} - -void LocalPlayer::crazyMove6() -{ - if (mAction == MOVE) - return; - - switch (mCrazyMoveState) - { - case 0: - move(3, 0); - mCrazyMoveState = 1; - break; - case 1: - move(2, -2); - mCrazyMoveState = 2; - break; - case 2: - move(0, -3); - mCrazyMoveState = 3; - break; - case 3: - move(-2, -2); - mCrazyMoveState = 4; - break; - case 4: - move(-3, 0); - mCrazyMoveState = 5; - break; - case 5: - move(-2, 2); - mCrazyMoveState = 6; - break; - case 6: - move(0, 3); - mCrazyMoveState = 7; - break; - case 7: - move(2, 2); - mCrazyMoveState = 0; - break; - default: - break; - } -} - -void LocalPlayer::crazyMove7() -{ - if (mAction == MOVE) - return; - - switch (mCrazyMoveState) - { - case 0: - move(1, 1); - mCrazyMoveState = 1; - break; - case 1: - move(-1, 1); - mCrazyMoveState = 2; - break; - case 2: - move(-1, -1); - mCrazyMoveState = 3; - break; - case 3: - move(1, -1); - mCrazyMoveState = 0; - break; - default: - break; - } -} - -void LocalPlayer::crazyMove8() -{ - if (mAction == MOVE || !mMap) - return; - int idx = 0; - const int dist = 1; - -// look -// up, ri,do,le - static const int movesX[][4] = - { - {-1, 0, 1, 0}, // move left - { 0, 1, 0, -1}, // move up - { 1, 0, -1, 0}, // move right - { 0, -1, 0, 1} // move down - }; - -// look -// up, ri,do,le - static const int movesY[][4] = - { - { 0, -1, 0, 1}, // move left - {-1, 0, 1, 0}, // move up - { 0, 1, 0, -1}, // move right - { 1, 0, -1, 0} // move down - }; - - if (mDirection == Being::UP) - idx = 0; - else if (mDirection == Being::RIGHT) - idx = 1; - else if (mDirection == Being::DOWN) - idx = 2; - else if (mDirection == Being::LEFT) - idx = 3; - - - int mult = 1; - const unsigned char walkMask = getWalkMask(); - if (mMap->getWalk(mX + movesX[idx][0], - mY + movesY[idx][0], walkMask)) - { - while (mMap->getWalk(mX + movesX[idx][0] * mult, - mY + movesY[idx][0] * mult, - getWalkMask()) && mult <= dist) - { - mult ++; - } - move(movesX[idx][0] * (mult - 1), movesY[idx][0] * (mult - 1)); - } - else if (mMap->getWalk(mX + movesX[idx][1], - mY + movesY[idx][1], walkMask)) - { - while (mMap->getWalk(mX + movesX[idx][1] * mult, - mY + movesY[idx][1] * mult, - getWalkMask()) && mult <= dist) - { - mult ++; - } - move(movesX[idx][1] * (mult - 1), movesY[idx][1] * (mult - 1)); - } - else if (mMap->getWalk(mX + movesX[idx][2], - mY + movesY[idx][2], walkMask)) - { - while (mMap->getWalk(mX + movesX[idx][2] * mult, - mY + movesY[idx][2] * mult, - getWalkMask()) && mult <= dist) - { - mult ++; - } - move(movesX[idx][2] * (mult - 1), movesY[idx][2] * (mult - 1)); - } - else if (mMap->getWalk(mX + movesX[idx][3], - mY + movesY[idx][3], walkMask)) - { - while (mMap->getWalk(mX + movesX[idx][3] * mult, - mY + movesY[idx][3] * mult, - getWalkMask()) && mult <= dist) - { - mult ++; - } - move(movesX[idx][3] * (mult - 1), movesY[idx][3] * (mult - 1)); - } -} - -void LocalPlayer::crazyMove9() -{ - int dx = 0; - int dy = 0; - - if (mAction == MOVE) - return; - - switch (mCrazyMoveState) - { - case 0: - switch (mDirection) - { - case UP : dy = -1; break; - case DOWN : dy = 1; break; - case LEFT : dx = -1; break; - case RIGHT: dx = 1; break; - default: break; - } - move(dx, dy); - mCrazyMoveState = 1; - break; - case 1: - mCrazyMoveState = 2; - if (!allowAction()) - return; - Net::getPlayerHandler()->changeAction(SIT); - break; - case 2: - mCrazyMoveState = 3; - break; - case 3: - mCrazyMoveState = 0; - break; - default: - break; - } -} - -void LocalPlayer::crazyMoveA() -{ - const std::string mMoveProgram(config.getStringValue("crazyMoveProgram")); - - if (mAction == MOVE) - return; - - if (mMoveProgram.empty()) - return; - - if (mCrazyMoveState >= mMoveProgram.length()) - mCrazyMoveState = 0; - - // move command - if (mMoveProgram[mCrazyMoveState] == 'm') - { - mCrazyMoveState ++; - if (mCrazyMoveState < mMoveProgram.length()) - { - int dx = 0; - int dy = 0; - - signed char param = mMoveProgram[mCrazyMoveState++]; - if (param == '?') - { - const char cmd[] = {'l', 'r', 'u', 'd'}; - srand(tick_time); - param = cmd[rand() % 4]; - } - switch (param) - { - case 'd': - move(0, 1); - break; - case 'u': - move(0, -1); - break; - case 'l': - move(-1, 0); - break; - case 'r': - move(1, 0); - break; - case 'f': - switch (mDirection) - { - case UP : dy = -1; break; - case DOWN : dy = 1; break; - case LEFT : dx = -1; break; - case RIGHT: dx = 1; break; - default: break; - } - move(dx, dy); - break; - case 'b': - switch (mDirection) - { - case UP : dy = 1; break; - case DOWN : dy = -1; break; - case LEFT : dx = 1; break; - case RIGHT: dx = -1; break; - default: break; - } - move(dx, dy); - break; - default: - break; - } - } - } - // direction command - else if (mMoveProgram[mCrazyMoveState] == 'd') - { - mCrazyMoveState ++; - - if (mCrazyMoveState < mMoveProgram.length()) - { - signed char param = mMoveProgram[mCrazyMoveState++]; - if (param == '?') - { - const char cmd[] = {'l', 'r', 'u', 'd'}; - srand(tick_time); - param = cmd[rand() % 4]; - } - switch (param) - { - case 'd': - -// if (client->limitPackets(PACKET_DIRECTION)) - { - setDirection(Being::DOWN); - Net::getPlayerHandler()->setDirection(Being::DOWN); - } - break; - case 'u': -// if (client->limitPackets(PACKET_DIRECTION)) - { - setDirection(Being::UP); - Net::getPlayerHandler()->setDirection(Being::UP); - } - break; - case 'l': -// if (client->limitPackets(PACKET_DIRECTION)) - { - setDirection(Being::LEFT); - Net::getPlayerHandler()->setDirection(Being::LEFT); - } - break; - case 'r': -// if (client->limitPackets(PACKET_DIRECTION)) - { - setDirection(Being::RIGHT); - Net::getPlayerHandler()->setDirection(Being::RIGHT); - } - break; - case 'L': -// if (client->limitPackets(PACKET_DIRECTION)) - { - uint8_t dir = 0; - switch (mDirection) - { - case UP : dir = Being::LEFT; break; - case DOWN : dir = Being::RIGHT; break; - case LEFT : dir = Being::DOWN; break; - case RIGHT : dir = Being::UP; break; - default: break; - } - setDirection(dir); - Net::getPlayerHandler()->setDirection(dir); - } - break; - case 'R': -// if (client->limitPackets(PACKET_DIRECTION)) - { - uint8_t dir = 0; - switch (mDirection) - { - case UP : dir = Being::RIGHT; break; - case DOWN : dir = Being::LEFT; break; - case LEFT : dir = Being::UP; break; - case RIGHT : dir = Being::DOWN; break; - default: break; - } - setDirection(dir); - Net::getPlayerHandler()->setDirection(dir); - } - break; - case 'b': -// if (client->limitPackets(PACKET_DIRECTION)) - { - uint8_t dir = 0; - switch (mDirection) - { - case UP : dir = Being::DOWN; break; - case DOWN : dir = Being::UP; break; - case LEFT : dir = Being::RIGHT; break; - case RIGHT : dir = Being::LEFT; break; - default: break; - } - setDirection(dir); - Net::getPlayerHandler()->setDirection(dir); - } - break; - case '0': - dropShortcut->dropFirst(); - break; - case 'a': - dropShortcut->dropItems(); - break; - default: - break; - } - } - } - // sit command - else if (mMoveProgram[mCrazyMoveState] == 's') - { - mCrazyMoveState ++; - if (toggleSit()) - mCrazyMoveState ++; - } - // wear outfits - else if (mMoveProgram[mCrazyMoveState] == 'o') - { - mCrazyMoveState ++; - if (mCrazyMoveState < mMoveProgram.length()) - { - // wear next outfit - if (mMoveProgram[mCrazyMoveState] == 'n') - { - mCrazyMoveState ++; - outfitWindow->wearNextOutfit(); - } - // wear previous outfit - else if (mMoveProgram[mCrazyMoveState] == 'p') - { - mCrazyMoveState ++; - outfitWindow->wearPreviousOutfit(); - } - } - } - // pause - else if (mMoveProgram[mCrazyMoveState] == 'w') - { - mCrazyMoveState ++; - } - // pick up - else if (mMoveProgram[mCrazyMoveState] == 'p') - { - mCrazyMoveState ++; - pickUpItems(); - } - // emote - else if (mMoveProgram[mCrazyMoveState] == 'e') - { - mCrazyMoveState ++; - const signed char emo = mMoveProgram[mCrazyMoveState]; - if (emo == '?') - { - srand(tick_time); - emote(static_cast(1 + (rand() % 13))); - } - else - { - if (emo >= '0' && emo <= '9') - emote(static_cast(emo - '0' + 1)); - else if (emo >= 'a' && emo <= 'd') - emote(static_cast(emo - 'a' + 11)); - } - - mCrazyMoveState ++; - } - else - { - mCrazyMoveState ++; - } - - if (mCrazyMoveState >= mMoveProgram.length()) - mCrazyMoveState = 0; -} - -bool LocalPlayer::isReachable(Being *const being, - const int maxCost) -{ - if (!being || !mMap) - return false; - - if (being->isReachable() == Being::REACH_NO) - return false; - - if (being->getTileX() == mX - && being->getTileY() == mY) - { - being->setDistance(0); - being->setIsReachable(Being::REACH_YES); - return true; - } - else if (being->getTileX() - 1 <= mX - && being->getTileX() + 1 >= mX - && being->getTileY() - 1 <= mY - && being->getTileY() + 1 >= mY) - { - being->setDistance(1); - being->setIsReachable(Being::REACH_YES); - return true; - } - - const Vector &playerPos = getPosition(); - - const Path debugPath = mMap->findPath( - static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - being->getTileX(), being->getTileY(), getWalkMask(), maxCost); - - being->setDistance(static_cast(debugPath.size())); - if (!debugPath.empty()) - { - being->setIsReachable(Being::REACH_YES); - return true; - } - else - { - being->setIsReachable(Being::REACH_NO); - return false; - } -} - -bool LocalPlayer::isReachable(const int x, const int y, - const bool allowCollision) const -{ - const WalkLayer *const walk = mMap->getWalkLayer(); - if (!walk) - return false; - int num = walk->getDataAt(x, y); - if (allowCollision && num < 0) - num = -num; - - return walk->getDataAt(mX, mY) == num; -} - -bool LocalPlayer::pickUpItems(int pickUpType) -{ - if (!actorSpriteManager) - return false; - - bool status = false; - int x = mX; - int y = mY; - - // first pick up item on player position - FloorItem *item = - actorSpriteManager->findItem(x, y); - if (item) - status = pickUp(item); - - if (pickUpType == 0) - pickUpType = mPickUpType; - - if (pickUpType == 0) - return status; - - int x1, y1, x2, y2; - switch (pickUpType) - { - case 1: - switch (mDirection) - { - case UP : --y; break; - case DOWN : ++y; break; - case LEFT : --x; break; - case RIGHT: ++x; break; - default: break; - } - item = actorSpriteManager->findItem(x, y); - if (item) - status = pickUp(item); - break; - case 2: - switch (mDirection) - { - case UP : x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y; break; - case DOWN : x1 = x - 1; y1 = y; x2 = x + 1; y2 = y + 1; break; - case LEFT : x1 = x - 1; y1 = y - 1; x2 = x; y2 = y + 1; break; - case RIGHT: x1 = x; y1 = y - 1; x2 = x + 1; y2 = y + 1; break; - default: x1 = x; x2 = x; y1 = y; y2 = y; break; - } - if (actorSpriteManager->pickUpAll(x1, y1, x2, y2)) - status = true; - break; - case 3: - if (actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) - status = true; - break; - - case 4: - if (!actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) - { - if (actorSpriteManager->pickUpNearest(x, y, 4)) - status = true; - } - else - { - status = true; - } - break; - - case 5: - if (!actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) - { - if (actorSpriteManager->pickUpNearest(x, y, 8)) - status = true; - } - else - { - status = true; - } - break; - - case 6: - if (!actorSpriteManager->pickUpAll(x - 1, y - 1, x + 1, y + 1)) - { - if (actorSpriteManager->pickUpNearest(x, y, 90)) - status = true; - } - else - { - status = true; - } - break; - - default: - break; - } - return status; -} - - -void LocalPlayer::moveByDirection(const unsigned char dir) -{ - int dx = 0, dy = 0; -#ifdef MANASERV_SUPPORT - if (dir & UP) - dy -= 32; - if (dir & DOWN) - dy += 32; - if (dir & LEFT) - dx -= 32; - if (dir & RIGHT) - dx += 32; -#else - if (dir & UP) - dy--; - if (dir & DOWN) - dy++; - if (dir & LEFT) - dx--; - if (dir & RIGHT) - dx++; -#endif - - move(dx, dy); -} - -void LocalPlayer::specialMove(const unsigned char direction) -{ - if (direction && (mNavigateX || mNavigateY)) - navigateClean(); - - if (direction && (mInvertDirection >= 2 - && mInvertDirection <= 4) - && !mIsServerBuggy) - { - if (mAction == MOVE) - return; - - int max; - - if (mInvertDirection == 2) - max = 5; - else if (mInvertDirection == 4) - max = 1; - else - max = 3; - - if (getMoveState() < max) - { - moveByDirection(direction); - mMoveState ++; - } - else - { - mMoveState = 0; - crazyMove(); - } - } - else - { - setWalkingDir(direction); - } -} - -void LocalPlayer::debugMsg(const std::string &str) const -{ - if (debugChatTab) - debugChatTab->chatLog(str); -} - -void LocalPlayer::magicAttack() const -{ - if (!chatWindow || !isAlive() - || !Net::getPlayerHandler()->canUseMagic()) - { - return; - } - - switch (mMagicAttackType) - { - // flar W00 - case 0: - tryMagic("#flar", 1, 0, 10); - break; - // chiza W01 - case 1: - tryMagic("#chiza", 1, 0, 9); - break; - // ingrav W10 - case 2: - tryMagic("#ingrav", 2, 2, 20); - break; - // frillyar W11 - case 3: - tryMagic("#frillyar", 2, 2, 25); - break; - // upmarmu W12 - case 4: - tryMagic("#upmarmu", 2, 2, 20); - break; - default: - break; - } -} - -void LocalPlayer::tryMagic(const std::string &spell, const int baseMagic, - const int schoolMagic, const int mana) -{ - if (!chatWindow) - return; - - if (PlayerInfo::getSkillLevel(340) >= baseMagic - && PlayerInfo::getSkillLevel(342) >= schoolMagic) - { - if (PlayerInfo::getAttribute(PlayerInfo::MP) >= mana) - { - if (!client->limitPackets(PACKET_CHAT)) - return; - - chatWindow->localChatInput(spell); - } - } -} - -void LocalPlayer::loadHomes() -{ - if (serverVersion > 0) - return; - std::string buf; - std::stringstream ss(serverConfig.getValue("playerHomes", - "maps/018-1.tmx 71 76 maps/013-3.tmx 71 24")); - - while (ss >> buf) - { - Vector pos; - ss >> pos.x; - ss >> pos.y; - mHomes[buf] = pos; - } -} - -void LocalPlayer::setMap(Map *const map) -{ - if (map) - { - if (socialWindow) - socialWindow->updateActiveList(); - } - navigateClean(); - mCrossX = 0; - mCrossY = 0; - - Being::setMap(map); - updateNavigateList(); -} - -void LocalPlayer::setHome() -{ - if (!mMap || !socialWindow) - return; - - SpecialLayer *const specialLayer = mMap->getSpecialLayer(); - - if (!specialLayer) - return; - - const std::string key = mMap->getProperty("_realfilename"); - Vector pos = mHomes[key]; - - if (mAction == SIT) - { - const std::map::const_iterator - iter = mHomes.find(key); - - if (iter != mHomes.end()) - { - socialWindow->removePortal(static_cast(pos.x), - static_cast(pos.y)); - } - - if (iter != mHomes.end() && mX == static_cast(pos.x) - && mY == static_cast(pos.y)) - { - mMap->updatePortalTile("", MapItem::EMPTY, - static_cast(pos.x), static_cast(pos.y)); - - mHomes.erase(key); - socialWindow->removePortal(static_cast(pos.x), - static_cast(pos.y)); - } - else - { - if (specialLayer && iter != mHomes.end()) - { - specialLayer->setTile(static_cast(pos.x), - static_cast(pos.y), MapItem::EMPTY); - } - - pos.x = static_cast(mX); - pos.y = static_cast(mY); - mHomes[key] = pos; - mMap->updatePortalTile("home", MapItem::HOME, - mX, mY); - socialWindow->addPortal(mX, mY); - } - MapItem *const mapItem = specialLayer->getTile(mX, mY); - if (mapItem) - { - const int idx = socialWindow->getPortalIndex(mX, mY); - mapItem->setName(keyboard.getKeyShortString( - outfitWindow->keyName(idx))); - } - saveHomes(); - } - else - { - MapItem *mapItem = specialLayer->getTile(mX, mY); - int type = 0; - - const std::map::iterator iter = mHomes.find(key); - if (iter != mHomes.end() && mX == pos.x && mY == pos.y) - { - mHomes.erase(key); - saveHomes(); - } - - if (!mapItem || mapItem->getType() == MapItem::EMPTY) - { - if (mDirection & UP) - type = MapItem::ARROW_UP; - else if (mDirection & LEFT) - type = MapItem::ARROW_LEFT; - else if (mDirection & DOWN) - type = MapItem::ARROW_DOWN; - else if (mDirection & RIGHT) - type = MapItem::ARROW_RIGHT; - } - else - { - type = MapItem::EMPTY; - } - mMap->updatePortalTile("", type, mX, mY); - - if (type != MapItem::EMPTY) - { - socialWindow->addPortal(mX, mY); - mapItem = specialLayer->getTile(mX, mY); - if (mapItem) - { - const int idx = socialWindow->getPortalIndex(mX, mY); - mapItem->setName(keyboard.getKeyShortString( - outfitWindow->keyName(idx))); - } - } - else - { - specialLayer->setTile(mX, mY, MapItem::EMPTY); - socialWindow->removePortal(mX, mY); - } - } -} - -void LocalPlayer::saveHomes() -{ - std::stringstream ss; - - for (std::map::const_iterator iter = mHomes.begin(), - iter_end = mHomes.end(); iter != iter_end; ++iter ) - { - const Vector &pos = (*iter).second; - - if (iter != mHomes.begin()) - ss << " "; - ss << (*iter).first << " " << pos.x << " " << pos.y; - } - - serverConfig.setValue("playerHomes", ss.str()); -} - -void LocalPlayer::pingRequest() -{ - const int time = tick_time; - if (mWaitPing == true && mPingSendTick != 0) - { - if (time >= mPingSendTick && (time - mPingSendTick) > 1000) - return; - } - - mPingSendTick = time; - mWaitPing = true; - Net::getBeingHandler()->requestNameById(getId()); -} - -std::string LocalPlayer::getPingTime() const -{ - std::string str; - if (!mWaitPing) - { - if (!mPingTime) - str = "?"; - else - str = toString(mPingTime); - } - else - { - int time = tick_time; - if (time > mPingSendTick) - time -= mPingSendTick; - else - time += MAX_TICK_VALUE - mPingSendTick; - if (time <= mPingTime) - time = mPingTime; - if (mPingTime != time) - str = strprintf("%d (%d)", mPingTime, time); - else - str = toString(time); - } - return str; -} - -void LocalPlayer::pingResponse() -{ - if (mWaitPing == true && mPingSendTick > 0) - { - mWaitPing = false; - const int time = tick_time; - if (time < mPingSendTick) - { - mPingSendTick = 0; - mPingTime = 0; - } - else - { - mPingTime = (time - mPingSendTick) * 10; - } - } -} - -void LocalPlayer::tryPingRequest() -{ - if (mPingSendTick == 0 || tick_time < mPingSendTick - || (tick_time - mPingSendTick) > 200) - { - pingRequest(); - } -} - - -void LocalPlayer::setAway(const std::string &message) -{ - if (!message.empty()) - config.setValue("afkMessage", message); - changeAwayMode(); - updateStatus(); -} - -void LocalPlayer::setPseudoAway(const std::string &message) -{ - if (!message.empty()) - config.setValue("afkMessage", message); - mPseudoAwayMode = !mPseudoAwayMode; -} - -void LocalPlayer::afkRespond(ChatTab *const tab, const std::string &nick) -{ - if (mAwayMode) - { - const int time = cur_time; - if (mAfkTime == 0 || time < mAfkTime - || time - mAfkTime > awayLimitTimer) - { - const std::string msg = "*AFK*: " - + config.getStringValue("afkMessage"); - - if (!tab) - { - Net::getChatHandler()->privateMessage(nick, msg); - if (localChatTab) - { - localChatTab->chatLog(std::string(getName()).append( - " : ").append(msg), ACT_WHISPER, false); - } - } - else - { - if (tab->getNoAway()) - return; - Net::getChatHandler()->privateMessage(nick, msg); - tab->chatLog(getName(), msg); - } - mAfkTime = time; - } - } -} - -bool LocalPlayer::navigateTo(const int x, const int y) -{ - if (!mMap) - return false; - - SpecialLayer *const tmpLayer = mMap->getTempLayer(); - if (!tmpLayer) - return false; - - const Vector &playerPos = getPosition(); - mShowNavigePath = true; - mOldX = static_cast(playerPos.x); - mOldY = static_cast(playerPos.y); - mOldTileX = mX; - mOldTileY = mY; - mNavigateX = x; - mNavigateY = y; - mNavigateId = 0; - - mNavigatePath = mMap->findPath( - static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - x, y, getWalkMask(), 0); - - if (mDrawPath) - tmpLayer->addRoad(mNavigatePath); - return !mNavigatePath.empty(); -} - -void LocalPlayer::navigateTo(const Being *const being) -{ - if (!mMap || !being) - return; - - SpecialLayer *const tmpLayer = mMap->getTempLayer(); - if (!tmpLayer) - return; - - const Vector &playerPos = getPosition(); - mShowNavigePath = true; - mOldX = static_cast(playerPos.x); - mOldY = static_cast(playerPos.y); - mOldTileX = mX; - mOldTileY = mY; - mNavigateX = being->getTileX(); - mNavigateY = being->getTileY(); - - mNavigatePath = mMap->findPath( - static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - being->getTileX(), being->getTileY(), - getWalkMask(), 0); - - if (mDrawPath) - tmpLayer->addRoad(mNavigatePath); -} - -void LocalPlayer::navigateClean() -{ - if (!mMap) - return; - - mShowNavigePath = false; - mOldX = 0; - mOldY = 0; - mOldTileX = 0; - mOldTileY = 0; - mNavigateX = 0; - mNavigateY = 0; - mNavigateId = 0; - - mNavigatePath.clear(); - - const SpecialLayer *const tmpLayer = mMap->getTempLayer(); - if (!tmpLayer) - return; - - tmpLayer->clean(); -} - -void LocalPlayer::updateCoords() -{ - Being::updateCoords(); - - const Vector &playerPos = getPosition(); - // probably map not loaded. - if (!playerPos.x || !playerPos.y) - return; - - if (mX != mOldTileX || mY != mOldTileY) - { - if (socialWindow) - socialWindow->updatePortals(); - if (viewport) - viewport->hideBeingPopup(); - if (mMap) - { - std::string str = mMap->getObjectData(mX, mY, - MapItem::MUSIC); - if (str.empty()) - str = mMap->getMusicFile(); - if (str != soundManager.getCurrentMusicFile()) - { - if (str.empty()) - soundManager.fadeOutMusic(); - else - soundManager.fadeOutAndPlayMusic(str); - } - } - } - - if (mShowNavigePath) - { - if (mMap && (mX != mOldTileX || mY != mOldTileY)) - { - SpecialLayer *const tmpLayer = mMap->getTempLayer(); - if (!tmpLayer) - return; - - const int x = static_cast(playerPos.x - 16) / 32; - const int y = static_cast(playerPos.y - 32) / 32; - if (mNavigateId) - { - if (!actorSpriteManager) - { - navigateClean(); - return; - } - - const Being *const being = actorSpriteManager - ->findBeing(mNavigateId); - if (!being) - { - navigateClean(); - return; - } - mNavigateX = being->getTileX(); - mNavigateY = being->getTileY(); - } - - if (mNavigateX == x && mNavigateY == y) - { - navigateClean(); - return; - } - else - { - for (Path::const_iterator i = mNavigatePath.begin(), - i_end = mNavigatePath.end(); i != i_end; ++ i) - { - if ((*i).x == mX && (*i).y == mY) - { - mNavigatePath.pop_front(); - break; - } - } - - if (mDrawPath) - { - tmpLayer->clean(); - tmpLayer->addRoad(mNavigatePath); - } - } - } - } - mOldX = static_cast(playerPos.x); - mOldY = static_cast(playerPos.y); - mOldTileX = mX; - mOldTileY = mY; -} - -void LocalPlayer::targetMoved() const -{ -/* - if (mKeepAttacking) - { - if (mTarget && mServerAttack) - { - logger->log("LocalPlayer::targetMoved0"); - if (!client->limitPackets(PACKET_ATTACK)) - return; - logger->log("LocalPlayer::targetMoved"); - Net::getPlayerHandler()->attack(mTarget->getId(), mServerAttack); - } - } -*/ -} - -int LocalPlayer::getPathLength(const Being *const being) const -{ - if (!mMap || !being) - return 0; - - const Vector &playerPos = getPosition(); - - if (being->mX == mX && being->mY == mY) - return 0; - - if (being->mX - 1 <= mX && being->mX + 1 >= mX - && being->mY - 1 <= mY && being->mY + 1 >= mY) - { - return 1; - } - - if (mTargetOnlyReachable) - { - const Path debugPath = mMap->findPath( - static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - being->getTileX(), being->getTileY(), - getWalkMask(), 0); - return static_cast(debugPath.size()); - } - else - { - const int dx = static_cast(abs(being->mX - mX)); - const int dy = static_cast(abs(being->mY - mY)); - if (dx > dy) - return dx; - return dy; - } -} - -int LocalPlayer::getAttackRange2() const -{ - int range = getAttackRange(); - if (range == 1) - range = 2; - return range; -} - -void LocalPlayer::attack2(Being *const target, const bool keep, - const bool dontChangeEquipment) -{ - if (!dontChangeEquipment && target) - changeEquipmentBeforeAttack(target); - - // probably need cache getPathLength(target) - if ((!target || mAttackType == 0 || mAttackType == 3) - || (withinAttackRange(target, serverVersion < 1, - serverVersion < 1 ? 1 : 0) - && getPathLength(target) <= getAttackRange2())) - { - attack(target, keep); - if (mAttackType == 2) - { - if (!target) - { - if (pickUpItems()) - return; - } - else - { - pickUpItems(3); - } - } - } - else if (!mPickUpTarget) - { - if (mAttackType == 2) - { - if (pickUpItems()) - return; - } - setTarget(target); - if (target && target->getType() != Being::NPC) - { - mKeepAttacking = true; - moveToTarget(); - } - } -} - -void LocalPlayer::setFollow(const std::string &player) -{ - mPlayerFollowed = player; - if (!mPlayerFollowed.empty()) - { - // TRANSLATORS: follow command message - std::string msg = strprintf(_("Follow: %s"), player.c_str()); - debugMsg(msg); - } - else - { - // TRANSLATORS: follow command message - debugMsg(_("Follow canceled")); - } -} - -void LocalPlayer::setImitate(const std::string &player) -{ - mPlayerImitated = player; - if (!mPlayerImitated.empty()) - { - // TRANSLATORS: imitate command message - std::string msg = strprintf(_("Imitation: %s"), player.c_str()); - debugMsg(msg); - } - else - { - // TRANSLATORS: imitate command message - debugMsg(_("Imitation canceled")); - } -} - -void LocalPlayer::cancelFollow() -{ - if (!mPlayerFollowed.empty()) - { - // TRANSLATORS: cancel follow message - debugMsg(_("Follow canceled")); - } - if (!mPlayerImitated.empty()) - { - // TRANSLATORS: cancel follow message - debugMsg(_("Imitation canceled")); - } - mPlayerFollowed.clear(); - mPlayerImitated.clear(); -} - -void LocalPlayer::imitateEmote(const Being *const being, - const unsigned char action) const -{ - if (!being) - return; - - std::string player_imitated = getImitate(); - if (!player_imitated.empty() && being->getName() == player_imitated) - emote(action); -} - -void LocalPlayer::imitateAction(const Being *const being, - const Being::Action &action) -{ - if (!being) - return; - - if (!mPlayerImitated.empty() && being->getName() == mPlayerImitated) - { - setAction(action); - Net::getPlayerHandler()->changeAction(action); - } -} - -void LocalPlayer::imitateDirection(const Being *const being, - const unsigned char dir) -{ - if (!being) - return; - - if (!mPlayerImitated.empty() && being->getName() == mPlayerImitated) - { - if (!client->limitPackets(PACKET_DIRECTION)) - return; - - if (mFollowMode == 2) - { - uint8_t dir2 = 0; - if (dir & Being::LEFT) - dir2 |= Being::RIGHT; - else if (dir & Being::RIGHT) - dir2 |= Being::LEFT; - if (dir & Being::UP) - dir2 |= Being::DOWN; - else if (dir & Being::DOWN) - dir2 |= Being::UP; - - setDirection(dir2); - Net::getPlayerHandler()->setDirection(dir2); - } - else - { - setDirection(dir); - Net::getPlayerHandler()->setDirection(dir); - } - } -} - -void LocalPlayer::imitateOutfit(Being *const player, const int sprite) const -{ - if (!player) - return; - - if (mImitationMode == 1 && !mPlayerImitated.empty() - && player->getName() == mPlayerImitated) - { - if (sprite < 0 || sprite >= player->getNumberOfLayers()) - return; - - const AnimatedSprite *const equipmentSprite - = dynamic_cast( - player->getSprite(sprite)); - - if (equipmentSprite) - { -// logger->log("have equipmentSprite"); - const Inventory *const inv = PlayerInfo::getInventory(); - if (!inv) - return; - - const std::string &path = equipmentSprite->getIdPath(); - if (path.empty()) - return; - -// logger->log("idPath: " + path); - const Item *const item = inv->findItemBySprite(path, - player->getGender(), player->getSubType()); - if (item && !item->isEquipped()) - Net::getInventoryHandler()->equipItem(item); - } - else - { -// logger->log("have unequip %d", sprite); - const int equipmentSlot = Net::getInventoryHandler() - ->convertFromServerSlot(sprite); -// logger->log("equipmentSlot: " + toString(equipmentSlot)); - if (equipmentSlot == EQUIP_PROJECTILE_SLOT) - return; - - const Item *const item = PlayerInfo::getEquipment(equipmentSlot); - if (item) - { -// logger->log("unequiping"); - Net::getInventoryHandler()->unequipItem(item); - } - } - } -} - -void LocalPlayer::followMoveTo(const Being *const being, - const int x, const int y) -{ - if (being && !mPlayerFollowed.empty() - && being->getName() == mPlayerFollowed) - { - mPickUpTarget = nullptr; - setDestination(x, y); - } -} - -void LocalPlayer::followMoveTo(const Being *const being, - const int x1, const int y1, - const int x2, const int y2) -{ - if (!being) - return; - - mPickUpTarget = nullptr; - if (!mPlayerFollowed.empty() && being->getName() == mPlayerFollowed) - { - switch (mFollowMode) - { - case 0: - setDestination(x1, y1); - setNextDest(x2, y2); - break; - case 1: - if (x1 != x2 || y1 != y2) - { - setDestination(mX + x2 - x1, mY + y2 - y1); - setNextDest(mX + x2 - x1, mY + y2 - y1); - } - break; - case 2: - if (x1 != x2 || y1 != y2) - { - setDestination(mX + x1 - x2, mY + y1 - y2); - setNextDest(mX + x1 - x2, mY + y1 - y2); - } - break; - case 3: - if (!mTarget || mTarget->getName() != mPlayerFollowed) - { - if (actorSpriteManager) - { - Being *const b = actorSpriteManager->findBeingByName( - mPlayerFollowed, Being::PLAYER); - setTarget(b); - } - } - moveToTarget(); - setNextDest(x2, y2); - break; - default: - break; - } - } -} - -void LocalPlayer::setNextDest(const int x, const int y) -{ - mNextDestX = x; - mNextDestY = y; -} - -bool LocalPlayer::allowAction() -{ - if (mIsServerBuggy) - { - if (mLastAction != -1) - return false; - mLastAction = tick_time; - } - return true; -} - -/* -bool LocalPlayer::allowMove() const -{ - if (mIsServerBuggy) - { - if (mAction == MOVE) - return false; - } - return true; -} -*/ - -void LocalPlayer::fixPos(const int maxDist) -{ - if (!mCrossX && !mCrossY) - return; - - const int dx = abs(mX - mCrossX); - const int dy = abs(mY - mCrossY); - const int dest = (dx * dx) + (dy * dy); - const int time = cur_time; - - if (dest > maxDist && mActivityTime - && (time < mActivityTime || time - mActivityTime > 2)) - { - mActivityTime = time; - moveTo(mCrossX, mCrossY); - } -} - -void LocalPlayer::setRealPos(const int x, const int y) -{ - if (!mMap) - return; - - SpecialLayer *const layer = mMap->getTempLayer(); - if (layer) - { - fixPos(1); - - if ((mCrossX || mCrossY) && layer->getTile(mCrossX, mCrossY) - && layer->getTile(mCrossX, mCrossY)->getType() == MapItem::CROSS) - { - layer->setTile(mCrossX, mCrossY, MapItem::EMPTY); - } - - if (!layer->getTile(x, y) - || layer->getTile(x, y)->getType() == MapItem::EMPTY) - { - if (getTileX() != x && getTileY() != y) - layer->setTile(x, y, MapItem::CROSS); - } - - mCrossX = x; - mCrossY = y; - } - if (mMap->isCustom()) - mMap->setWalk(x, y, true); -} -void LocalPlayer::fixAttackTarget() -{ - if (!mMap || !mTarget) - return; - - if (mMoveToTargetType == 7 || !mAttackType - || !config.getBoolValue("autofixPos")) - { - return; - } - - const Vector &playerPos = getPosition(); - const Path debugPath = mMap->findPath( - static_cast(playerPos.x - 16) / 32, - static_cast(playerPos.y - 32) / 32, - mTarget->getTileX(), mTarget->getTileY(), - getWalkMask(), 0); - - if (!debugPath.empty()) - { - const Path::const_iterator i = debugPath.begin(); - moveTo((*i).x, (*i).y); - } -} - -void LocalPlayer::respawn() -{ - navigateClean(); -} - -int LocalPlayer::getLevel() const -{ - return PlayerInfo::getAttribute(PlayerInfo::LEVEL); -} - -void LocalPlayer::updateNavigateList() -{ - if (mMap) - { - const std::map::const_iterator iter = - mHomes.find(mMap->getProperty("_realfilename")); - - if (iter != mHomes.end()) - { - const Vector &pos = mHomes[(*iter).first]; - if (pos.x && pos.y) - { - mMap->addPortalTile("home", MapItem::HOME, - static_cast(pos.x), static_cast(pos.y)); - } - } - } -} - -void LocalPlayer::waitFor(const std::string &nick) -{ - mWaitFor = nick; -} - -void LocalPlayer::checkNewName(Being *const being) -{ - if (!being) - return; - - const std::string nick = being->getName(); - if (being->getType() == ActorSprite::PLAYER) - { - const Guild *const guild = getGuild(); - if (guild) - { - const GuildMember *const gm = guild->getMember(nick); - if (gm) - { - const int level = gm->getLevel(); - if (level > 1 && being->getLevel() != level) - { - being->setLevel(level); - being->updateName(); - } - } - } - if (chatWindow) - { - WhisperTab *const tab = chatWindow->getWhisperTab(nick); - if (tab) - tab->setWhisperTabColors(); - } - } - - if (!mWaitFor.empty() && mWaitFor == nick) - { - // TRANSLATORS: wait player/monster message - debugMsg(strprintf(_("You see %s"), mWaitFor.c_str())); - soundManager.playGuiSound(SOUND_INFO); - mWaitFor.clear(); - } -} - -void LocalPlayer::resetYellowBar() -{ - mInvertDirection = 0; - mCrazyMoveType = config.resetIntValue("crazyMoveType"); - mMoveToTargetType = config.resetIntValue("moveToTargetType"); - mFollowMode = config.resetIntValue("followMode"); - mAttackWeaponType = config.resetIntValue("attackWeaponType"); - mAttackType = config.resetIntValue("attackType"); - mMagicAttackType = config.resetIntValue("magicAttackType"); - mPvpAttackType = config.resetIntValue("pvpAttackType"); - mQuickDropCounter = config.resetIntValue("quickDropCounter"); - mPickUpType = config.resetIntValue("pickUpType"); - if (viewport) - { - viewport->setDebugPath(0); - if (viewport->getCameraMode()) - viewport->toggleCameraMode(); - } - if (mMap) - mMap->setDebugFlags(0); - mImitationMode = config.resetIntValue("imitationMode"); - mDisableGameModifiers = config.resetBoolValue("disableGameModifiers"); - - if (miniStatusWindow) - miniStatusWindow->updateStatus(); -} - -unsigned char LocalPlayer::getWalkMask() const -{ - // for now blocking all types of collisions - return Map::BLOCKMASK_WALL | Map::BLOCKMASK_AIR | Map::BLOCKMASK_WATER; -} - -void LocalPlayer::removeHome() -{ - if (!mMap) - return; - - const std::string key = mMap->getProperty("_realfilename"); - const std::map::iterator iter = mHomes.find(key); - - if (iter != mHomes.end()) - mHomes.erase(key); -} - -void LocalPlayer::stopAdvert() -{ - mBlockAdvert = true; -} - -bool LocalPlayer::checAttackPermissions(const Being *const target) const -{ - if (!target) - return false; - - switch (mPvpAttackType) - { - case 0: - return true; - case 1: - return !(player_relations.getRelation(target->getName()) - == PlayerRelation::FRIEND); - case 2: - return player_relations.checkBadRelation(target->getName()); - default: - case 3: - return false; - } -} - - -const char *LocalPlayer::getVarItem(const char *const *const arr, - const unsigned index, - const unsigned sz) const -{ - if (index < sz) - return arr[index]; - return arr[sz]; -} - -void LocalPlayer::updateStatus() const -{ - if (serverVersion >= 4 && mEnableAdvert) - { - uint8_t status = 0; - if (mTradebot && shopWindow && !shopWindow->isShopEmpty()) - status |= FLAG_SHOP; - - if (mAwayMode || mPseudoAwayMode) - status |= FLAG_AWAY; - - if (mInactive) - status |= FLAG_INACTIVE; - - Net::getPlayerHandler()->updateStatus(status); - } -} - -void LocalPlayer::setTestParticle(const std::string &fileName, bool updateHash) -{ - mTestParticleName = fileName; - mTestParticleTime = cur_time; - if (mTestParticle) - { - mChildParticleEffects.removeLocally(mTestParticle); - mTestParticle = nullptr; - } - if (!fileName.empty()) - { - mTestParticle = particleEngine->addEffect(fileName, 0, 0, false); - controlParticle(mTestParticle); - if (updateHash) - mTestParticleHash = UpdaterWindow::getFileHash(mTestParticleName); - } -} - -void AwayListener::action(const gcn::ActionEvent &event) -{ - if (event.getId() == "ok" && player_node && player_node->getAway()) - { - player_node->changeAwayMode(); - player_node->updateStatus(); - if (outfitWindow) - outfitWindow->unwearAwayOutfit(); - if (miniStatusWindow) - miniStatusWindow->updateStatus(); - } -} diff --git a/src/localplayer.h b/src/localplayer.h deleted file mode 100644 index 0b036d96e..000000000 --- a/src/localplayer.h +++ /dev/null @@ -1,650 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef LOCALPLAYER_H -#define LOCALPLAYER_H - -#include "actorspritelistener.h" -#include "being.h" -#include "depricatedlistener.h" -#include "localconsts.h" - -#include "gui/userpalette.h" - -#include - -#include -#include - -class ChatTab; -class FloorItem; -class ImageSet; -class Item; -class Map; -class OkDialog; - -class AwayListener final : public gcn::ActionListener -{ - public: - void action(const gcn::ActionEvent &event) override; -}; - -/** - * Reasons an item can fail to be picked up. - */ -enum -{ - PICKUP_OKAY = 0, - PICKUP_BAD_ITEM, - PICKUP_TOO_HEAVY, - PICKUP_TOO_FAR, - PICKUP_INV_FULL, - PICKUP_STACK_FULL, - PICKUP_DROP_STEAL -}; - -/** - * The local player character. - */ -class LocalPlayer final : public Being, - public ActorSpriteListener, - public DepricatedListener -{ - public: - /** - * Constructor. - */ - explicit LocalPlayer(const int id = 65535, const int subtype = 0); - - A_DELETE_COPY(LocalPlayer) - - /** - * Destructor. - */ - ~LocalPlayer(); - - virtual void logic() override; - - void slowLogic(); - - virtual void setAction(const Action &action, - const int attackType = 0) override; - - /** - * Compute the next pathnode location when walking using keyboard. - * used by nextTile(). - */ - Position getNextWalkPosition(const unsigned char dir) - const A_WARN_UNUSED; - - /** - * Adds a new tile to the path when walking. - * @note Eathena - * Also, when specified, it picks up an item at the end of a path - * or attack target. - */ - virtual void nextTile() override - { nextTile(0); } - - virtual void nextTile(unsigned char dir); - - bool pickUp(FloorItem *const item); - - /** - * Called when an ActorSprite has been destroyed. - * @param actorSprite the ActorSprite being destroyed. - */ - void actorSpriteDestroyed(const ActorSprite &actorSprite) override; - - /** - * Gets the attack range. - */ - int getAttackRange() const A_WARN_UNUSED; - - int getAttackRange2() const A_WARN_UNUSED; - - void attack(Being *const target = nullptr, const bool keep = false, - const bool dontChangeEquipment = false); - - void attack2(Being *const target = nullptr, const bool keep = false, - const bool dontChangeEquipment = false); - - void setGMLevel(const int level); - - int getGMLevel() const A_WARN_UNUSED - { return mGMLevel; } - - void stopAttack(const bool keepAttack = false); - - void untarget(); - - /** - * Returns the current target of the player. Returns 0 if no being is - * currently targeted. - */ - Being *getTarget() const A_WARN_UNUSED; - - /** - * Sets the target being of the player. - */ - void setTarget(Being *const target); - - /** - * Sets a new destination for this being to walk to. - */ - virtual void setDestination(const int x, const int y); - - /** - * Sets a new direction to keep walking in. - */ - void setWalkingDir(const unsigned char dir); - - /** - * Gets the walking direction - */ - unsigned char getWalkingDir() const A_WARN_UNUSED - { return mWalkingDir; } - - /** - * Sets going to being to attack - */ - void setGotoTarget(Being *const target); - - /** - * Returns whether the target is in range to attack - */ - bool withinAttackRange(const Being *const target, - const bool fixDistance = false, - const int addRange = 0) const A_WARN_UNUSED; - - /** - * Stops the player dead in his tracks - */ - void stopWalking(const bool sendToServer = true); - - bool toggleSit() const; - - bool updateSit() const; - - static bool emote(const uint8_t emotion); - - /** - * Shows item pickup notifications. - */ - void pickedUp(const ItemInfo &itemInfo, const int amount, - const unsigned char color, const int floorItemId, - const unsigned char fail); - - int getLevel() const override A_WARN_UNUSED; - - /** Tells that the path has been set by mouse. */ - void pathSetByMouse() - { mPathSetByMouse = true; } - - /** Tells if the path has been set by mouse. */ - bool isPathSetByMouse() const A_WARN_UNUSED - { return mPathSetByMouse; } - - int getInvertDirection() const A_WARN_UNUSED - { return mInvertDirection; } - - void setInvertDirection(const int n) - { mInvertDirection = n; } - - void invertDirection(); - - int getAttackWeaponType() const A_WARN_UNUSED - { return mAttackWeaponType; } - - int getAttackType() const A_WARN_UNUSED - { return mAttackType; } - - int getFollowMode() const A_WARN_UNUSED - { return mFollowMode; } - - int getImitationMode() const A_WARN_UNUSED - { return mImitationMode; } - - void changeAttackWeaponType(); - - void changeAttackType(); - - void changeFollowMode(); - - void changeImitationMode(); - - void changePickUpType(); - - int getCrazyMoveType() const A_WARN_UNUSED - { return mCrazyMoveType; } - - int getPickUpType() const A_WARN_UNUSED - { return mPickUpType; } - - int getQuickDropCounter() const A_WARN_UNUSED - { return mQuickDropCounter; } - - void setQuickDropCounter(const int n); - - void changeQuickDropCounter(); - - int getMoveState() const A_WARN_UNUSED - { return mMoveState; } - - void setMoveState(const int n) - { mMoveState = n; } - - void switchMagicAttack(); - - void switchPvpAttack(); - - int getMagicAttackType() const A_WARN_UNUSED - { return mMagicAttackType; } - - int getPvpAttackType() const A_WARN_UNUSED - { return mPvpAttackType; } - - int getMoveToTargetType() const A_WARN_UNUSED - { return mMoveToTargetType; } - - int getDisableGameModifiers() const A_WARN_UNUSED - { return mDisableGameModifiers; } - - std::string getPingTime() const A_WARN_UNUSED; - - void tryPingRequest(); - - void changeMoveToTargetType(); - - void switchGameModifiers(); - - void magicAttack() const; - - void specialMove(const unsigned char direction); - - void moveByDirection(const unsigned char dir); - - bool pickUpItems(int pickUpType = 0); - - void changeCrazyMoveType(); - - void crazyMove(); - - void moveTo(const int x, const int y); - - void move(const int dX, const int dY); - - void moveToTarget(int dist = -1); - - void moveToHome(); - - void debugMsg(const std::string &str) const; - - bool isReachable(Being *const being, - const int maxCost = 0) A_WARN_UNUSED; - - bool isReachable(const int x, const int y, - const bool allowCollision) const A_WARN_UNUSED; - - void setHome(); - - void pingRequest(); - - void pingResponse(); - - void changeAwayMode(); - - void setAway(const std::string &message); - - void setPseudoAway(const std::string &message); - - bool getAway() const A_WARN_UNUSED - { return mAwayMode; } - - bool getPseudoAway() const A_WARN_UNUSED - { return mPseudoAwayMode; } - - void setHalfAway(const bool n) - { mInactive = n; } - - bool getHalfAway() const A_WARN_UNUSED - { return mInactive; } - - void afkRespond(ChatTab *const tab, const std::string &nick); - - bool navigateTo(const int x, const int y); - - void navigateTo(const Being *const being); - - void navigateClean(); - - void imitateEmote(const Being *const being, - const unsigned char emote) const; - - void imitateAction(const Being *const being, - const Being::Action &action); - - void imitateDirection(const Being *const being, - const unsigned char dir); - - void imitateOutfit(Being *const player, const int sprite = -1) const; - - void followMoveTo(const Being *const being, const int x, const int y); - - void followMoveTo(const Being *const being, const int x1, const int y1, - const int x2, const int y2); - - bool allowAction() A_WARN_UNUSED; - - void setRealPos(const int x, const int y); - - bool isServerBuggy() const A_WARN_UNUSED - { return mIsServerBuggy; } - - void fixPos(const int maxDist = 1); - - /** - * Sets the map the being is on - */ - void setMap(Map *const map); - - void addMessageToQueue(const std::string &message, - const int color = UserPalette::EXP_INFO); - - /** - * Called when a option (set with config.addListener()) is changed - */ - void optionChanged(const std::string &value) override; - - void processEvent(Channels channel, - const DepricatedEvent &event) override; - - /** - * set a following player. - */ - void setFollow(const std::string &player); - - /** - * set an imitation player. - */ - void setImitate(const std::string &player); - - /** - * setting the next destination of the following, in case of warp - */ - void setNextDest(const int x, const int y); - - int getNextDestX() const A_WARN_UNUSED - { return mNextDestX; } - - int getNextDestY() const A_WARN_UNUSED - { return mNextDestY; } - - void respawn(); - - const FloorItem *getPickUpTarget() const A_WARN_UNUSED - { return mPickUpTarget; } - - void unSetPickUpTarget() - { mPickUpTarget = nullptr; } - - /** - * Stop following a player. - */ - void cancelFollow(); - - /** - * Get the playername followed by the current player. - */ - const std::string &getFollow() const A_WARN_UNUSED - { return mPlayerFollowed; } - - /** - * Get the playername imitated by the current player. - */ - const std::string &getImitate() const A_WARN_UNUSED - { return mPlayerImitated; } - - /** - * Tells the engine whether to check - * if the Player Name is to be displayed. - */ - void setCheckNameSetting(const bool checked) - { mUpdateName = checked; } - - /** - * Gets if the engine has to check - * if the Player Name is to be displayed. - */ - bool getCheckNameSetting() const A_WARN_UNUSED - { return mUpdateName; } - - void fixAttackTarget(); - - void updateNavigateList(); - - int getPathLength(const Being *const being) const A_WARN_UNUSED; - - void targetMoved() const; - - void setLastHitFrom(const std::string &n) - { mLastHitFrom = n; } - - void waitFor(const std::string &nick); - - void checkNewName(Being *const being); - - void resetYellowBar(); - - unsigned char getWalkMask() const override A_WARN_UNUSED; - - void saveHomes(); - - void removeHome(); - - void stopAdvert(); - - bool checAttackPermissions(const Being *const target) - const A_WARN_UNUSED; - - void updateStatus() const; - - void setTestParticle(const std::string &fileName, - bool updateHash = true); - - std::string getInvertDirectionString(); - - std::string getCrazyMoveTypeString(); - - std::string getMoveToTargetTypeString(); - - std::string getFollowModeString(); - - std::string getAttackWeaponTypeString(); - - std::string getAttackTypeString(); - - std::string getQuickDropCounterString(); - - std::string getPickUpTypeString(); - - std::string getDebugPathString() const; - - std::string getMagicAttackString(); - - std::string getPvpAttackString(); - - std::string getImitationModeString(); - - std::string getAwayModeString(); - - std::string getCameraModeString() const; - - std::string getGameModifiersString(); - - protected: - void updateCoords() override; - - - virtual void handleStatusEffect(StatusEffect *const effect, - const int effectId); - - void startWalking(const unsigned char dir); - - void changeEquipmentBeforeAttack(const Being *const target) const; - - static void tryMagic(const std::string &spell, const int baseMagic, - const int schoolMagic, const int mana); - - const char *getVarItem(const char *const *const arr, - const unsigned index, - const unsigned sz) const A_WARN_UNUSED; - - void changeMode(unsigned *const var, const unsigned limit, - const char *const conf, - std::string (LocalPlayer::*const func)(), - const unsigned def = 0, - const bool save = true); - - void crazyMove1(); - void crazyMove2(); - void crazyMove3(); - void crazyMove4(); - void crazyMove5(); - void crazyMove6(); - void crazyMove7(); - void crazyMove8(); - void crazyMove9(); - void crazyMoveA(); - - void loadHomes(); - - int mGMLevel; - - // move type - unsigned int mInvertDirection; - // crazy move type - unsigned int mCrazyMoveType; - // crazy move state - unsigned int mCrazyMoveState; - // attack weapon type - unsigned int mAttackWeaponType; - // quick drop counter - unsigned int mQuickDropCounter; - // move state. used if mInvertDirection == 2 - unsigned int mMoveState; - // pick up type 1x1, normal aka 2x1, forward aka 2x3, 3x3, 3x3 + 1 - unsigned int mPickUpType; - // magic attack type - unsigned int mMagicAttackType; - // pvp attack type - unsigned int mPvpAttackType; - // type how move to target - unsigned int mMoveToTargetType; - unsigned int mAttackType; - unsigned int mFollowMode; - unsigned int mImitationMode; - - int mLastTargetX; - int mLastTargetY; - - std::map mHomes; - - Being *mTarget; - - /** Follow system **/ - std::string mPlayerFollowed; - std::string mPlayerImitated; - int mNextDestX; - int mNextDestY; - - FloorItem *mPickUpTarget; - - int mLastAction; // Time stamp of the last action, -1 if none. - - std::vector mStatusEffectIcons; - - int mLocalWalkTime; // Timestamp used to control keyboard walk - // messages flooding - - typedef std::pair MessagePair; - /** Queued messages*/ - std::list mMessages; - int mMessageTime; - AwayListener *mAwayListener; - OkDialog *mAwayDialog; - - int mPingSendTick; - int mPingTime; - int mAfkTime; - int mActivityTime; - int mNavigateX; - int mNavigateY; - int mNavigateId; - int mCrossX; - int mCrossY; - int mOldX; - int mOldY; - int mOldTileX; - int mOldTileY; - Path mNavigatePath; - - std::string mLastHitFrom; - std::string mWaitFor; - int mAdvertTime; - Particle *mTestParticle; - std::string mTestParticleName; - int mTestParticleTime; - unsigned long mTestParticleHash; - unsigned char mWalkingDir; // The direction the player is walking in. - /** Whether or not the name settings have changed */ - bool mUpdateName; - bool mBlockAdvert; - bool mTargetDeadPlayers; - bool mServerAttack; - bool mEnableAdvert; - bool mTradebot; - bool mTargetOnlyReachable; - bool mDisableGameModifiers; - bool mIsServerBuggy; - bool mSyncPlayerMove; - bool mDrawPath; - bool mAttackMoving; - bool mAttackNext; - bool mShowJobExp; - bool mNextStep; - // temporary disable crazy moves in moves - bool mDisableCrazyMove; - bool mGoingToTarget; - // Whether or not to continue to attack - bool mKeepAttacking; - // Tells if the path was set using mouse - bool mPathSetByMouse; - bool mWaitPing; - bool mAwayMode; - bool mPseudoAwayMode; - bool mShowNavigePath; -}; - -extern LocalPlayer *player_node; - -#endif // LOCALPLAYER_H diff --git a/src/map.cpp b/src/map.cpp index 30508e07a..189981f80 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -24,13 +24,14 @@ #include "client.h" #include "configuration.h" -#include "localplayer.h" #include "maplayer.h" #include "notifymanager.h" #include "simpleanimation.h" #include "tileset.h" #include "walklayer.h" +#include "being/localplayer.h" + #include "particle/particle.h" #include "resources/ambientlayer.h" diff --git a/src/map.h b/src/map.h index e14b3c18d..2a954b7c4 100644 --- a/src/map.h +++ b/src/map.h @@ -23,11 +23,12 @@ #ifndef MAP_H #define MAP_H -#include "actor.h" #include "configlistener.h" #include "position.h" #include "properties.h" +#include "being/actor.h" + #include #include diff --git a/src/maplayer.cpp b/src/maplayer.cpp index 10aa6b4c9..332bf3547 100644 --- a/src/maplayer.cpp +++ b/src/maplayer.cpp @@ -29,7 +29,7 @@ #include "render/graphics.h" #endif -#include "localplayer.h" +#include "being/localplayer.h" #include "resources/resourcemanager.h" diff --git a/src/maplayer.h b/src/maplayer.h index b36ab0cc7..d20531e98 100644 --- a/src/maplayer.h +++ b/src/maplayer.h @@ -23,11 +23,12 @@ #ifndef MAPLAYER_H #define MAPLAYER_H -#include "actor.h" #include "configlistener.h" #include "position.h" #include "main.h" +#include "being/actor.h" + #include #include diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h index 31a31fcc2..cf5b285b1 100644 --- a/src/net/beinghandler.h +++ b/src/net/beinghandler.h @@ -22,7 +22,8 @@ #ifndef NET_BEINGHANDLER_H #define NET_BEINGHANDLER_H -#include "being.h" +#include "being/being.h" + #include "net/messagein.h" namespace Net diff --git a/src/net/charserverhandler.h b/src/net/charserverhandler.h index 2dee5c05e..aa05ba7f5 100644 --- a/src/net/charserverhandler.h +++ b/src/net/charserverhandler.h @@ -23,8 +23,8 @@ #ifndef NET_CHARSERVERHANDLER_H #define NET_CHARSERVERHANDLER_H -#include "localplayer.h" -#include "playerinfo.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" #include #include diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 3dee60dfa..7c5c2598c 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -26,14 +26,15 @@ #include "actorspritemanager.h" #include "client.h" +#include "configuration.h" #include "effectmanager.h" #include "game.h" #include "guild.h" #include "guildmanager.h" -#include "localplayer.h" #include "party.h" -#include "playerrelations.h" -#include "configuration.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "particle/particle.h" diff --git a/src/net/ea/buysellhandler.cpp b/src/net/ea/buysellhandler.cpp index 6a68355dd..892422f76 100644 --- a/src/net/ea/buysellhandler.cpp +++ b/src/net/ea/buysellhandler.cpp @@ -26,9 +26,10 @@ #include "client.h" #include "configuration.h" #include "inventory.h" -#include "localplayer.h" #include "notifymanager.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/buydialog.h" #include "gui/buyselldialog.h" diff --git a/src/net/ea/buysellhandler.h b/src/net/ea/buysellhandler.h index 8cb4b2951..f46a8dcb4 100644 --- a/src/net/ea/buysellhandler.h +++ b/src/net/ea/buysellhandler.h @@ -25,7 +25,7 @@ #include "net/buysellhandler.h" -#include "being.h" +#include "being/being.h" #include "net/net.h" diff --git a/src/net/ea/chathandler.cpp b/src/net/ea/chathandler.cpp index 72a1e49b7..c8259bc91 100644 --- a/src/net/ea/chathandler.cpp +++ b/src/net/ea/chathandler.cpp @@ -25,9 +25,10 @@ #include "actorspritemanager.h" #include "configuration.h" #include "guildmanager.h" -#include "localplayer.h" #include "notifymanager.h" -#include "playerrelations.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "gui/chatwindow.h" #include "gui/shopwindow.h" diff --git a/src/net/ea/gamehandler.cpp b/src/net/ea/gamehandler.cpp index df2c4ec47..1909cd6df 100644 --- a/src/net/ea/gamehandler.cpp +++ b/src/net/ea/gamehandler.cpp @@ -24,9 +24,10 @@ #include "client.h" #include "game.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "gui/okdialog.h" #include "debug.h" diff --git a/src/net/ea/gui/guildtab.cpp b/src/net/ea/gui/guildtab.cpp index 802236212..615d0334b 100644 --- a/src/net/ea/gui/guildtab.cpp +++ b/src/net/ea/gui/guildtab.cpp @@ -25,10 +25,11 @@ #include "chatlogger.h" #include "configuration.h" #include "guild.h" -#include "localplayer.h" #include "soundconsts.h" #include "soundmanager.h" +#include "being/localplayer.h" + #include "net/net.h" #include "net/ea/guildhandler.h" diff --git a/src/net/ea/gui/partytab.cpp b/src/net/ea/gui/partytab.cpp index d21e82d20..c41fad098 100644 --- a/src/net/ea/gui/partytab.cpp +++ b/src/net/ea/gui/partytab.cpp @@ -24,11 +24,12 @@ #include "chatlogger.h" #include "commandhandler.h" -#include "localplayer.h" #include "party.h" #include "soundconsts.h" #include "soundmanager.h" +#include "being/localplayer.h" + #include "net/net.h" #include "net/partyhandler.h" diff --git a/src/net/ea/guildhandler.cpp b/src/net/ea/guildhandler.cpp index e19b44592..4dc0f7f6e 100644 --- a/src/net/ea/guildhandler.cpp +++ b/src/net/ea/guildhandler.cpp @@ -23,9 +23,10 @@ #include "actorspritemanager.h" #include "configuration.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "gui/socialwindow.h" #include "debug.h" diff --git a/src/net/ea/inventoryhandler.cpp b/src/net/ea/inventoryhandler.cpp index 65f0d87a0..d2bcd6a23 100644 --- a/src/net/ea/inventoryhandler.cpp +++ b/src/net/ea/inventoryhandler.cpp @@ -22,9 +22,10 @@ #include "net/ea/inventoryhandler.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "gui/ministatuswindow.h" #include "net/ea/eaprotocol.h" diff --git a/src/net/ea/inventoryhandler.h b/src/net/ea/inventoryhandler.h index b21ba2fbc..7c3af78ec 100644 --- a/src/net/ea/inventoryhandler.h +++ b/src/net/ea/inventoryhandler.h @@ -26,7 +26,8 @@ #include "equipment.h" #include "inventory.h" #include "localconsts.h" -#include "playerinfo.h" + +#include "being/playerinfo.h" #include "gui/inventorywindow.h" diff --git a/src/net/ea/partyhandler.cpp b/src/net/ea/partyhandler.cpp index 7eee0d4e7..0a87bf370 100644 --- a/src/net/ea/partyhandler.cpp +++ b/src/net/ea/partyhandler.cpp @@ -23,9 +23,10 @@ #include "actorspritemanager.h" #include "configuration.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "gui/socialwindow.h" #include "debug.h" diff --git a/src/net/ea/playerhandler.cpp b/src/net/ea/playerhandler.cpp index 2d477ad4a..5106e0ecc 100644 --- a/src/net/ea/playerhandler.cpp +++ b/src/net/ea/playerhandler.cpp @@ -23,11 +23,12 @@ #include "net/ea/playerhandler.h" #include "game.h" -#include "localplayer.h" #include "party.h" #include "notifymanager.h" #include "units.h" +#include "being/localplayer.h" + #include "gui/ministatuswindow.h" #include "gui/okdialog.h" #include "gui/npcdialog.h" diff --git a/src/net/ea/skillhandler.cpp b/src/net/ea/skillhandler.cpp index 68597dd85..e363385e1 100644 --- a/src/net/ea/skillhandler.cpp +++ b/src/net/ea/skillhandler.cpp @@ -23,9 +23,10 @@ #include "net/ea/skillhandler.h" #include "logger.h" -#include "localplayer.h" #include "notifymanager.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/skilldialog.h" diff --git a/src/net/ea/token.h b/src/net/ea/token.h index c358ef8a7..0de35d379 100644 --- a/src/net/ea/token.h +++ b/src/net/ea/token.h @@ -20,7 +20,7 @@ * along with this program. If not, see . */ -#include "being.h" +#include "being/being.h" #ifndef NET_EA_TOKEN_H #define NET_EA_TOKEN_H diff --git a/src/net/ea/tradehandler.cpp b/src/net/ea/tradehandler.cpp index 4fa92e7d8..d22bf71ea 100644 --- a/src/net/ea/tradehandler.cpp +++ b/src/net/ea/tradehandler.cpp @@ -26,8 +26,9 @@ #include "item.h" #include "logger.h" #include "notifymanager.h" -#include "playerinfo.h" -#include "playerrelations.h" + +#include "being/playerinfo.h" +#include "being/playerrelations.h" #include "gui/confirmdialog.h" #include "gui/tradewindow.h" diff --git a/src/net/eathena/adminhandler.cpp b/src/net/eathena/adminhandler.cpp index 4661cd5b8..e52945a98 100644 --- a/src/net/eathena/adminhandler.cpp +++ b/src/net/eathena/adminhandler.cpp @@ -25,7 +25,8 @@ #include "actorspritemanager.h" #include "game.h" #include "notifymanager.h" -#include "playerrelations.h" + +#include "being/playerrelations.h" #include "net/chathandler.h" diff --git a/src/net/eathena/beinghandler.cpp b/src/net/eathena/beinghandler.cpp index 68c3f1a02..bc4d6d442 100644 --- a/src/net/eathena/beinghandler.cpp +++ b/src/net/eathena/beinghandler.cpp @@ -24,13 +24,14 @@ #include "actorspritemanager.h" #include "client.h" +#include "configuration.h" #include "effectmanager.h" #include "guild.h" #include "guildmanager.h" -#include "localplayer.h" #include "party.h" -#include "playerrelations.h" -#include "configuration.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "input/keyboardconfig.h" diff --git a/src/net/eathena/buysellhandler.cpp b/src/net/eathena/buysellhandler.cpp index 0b1c4ad06..8b44ac409 100644 --- a/src/net/eathena/buysellhandler.cpp +++ b/src/net/eathena/buysellhandler.cpp @@ -25,9 +25,10 @@ #include "actorspritemanager.h" #include "configuration.h" #include "inventory.h" -#include "localplayer.h" #include "notifymanager.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/buydialog.h" #include "gui/buyselldialog.h" diff --git a/src/net/eathena/chathandler.cpp b/src/net/eathena/chathandler.cpp index eae8e7ffc..ebcc900de 100644 --- a/src/net/eathena/chathandler.cpp +++ b/src/net/eathena/chathandler.cpp @@ -25,8 +25,9 @@ #include "actorspritemanager.h" #include "configuration.h" #include "game.h" -#include "localplayer.h" -#include "playerrelations.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "gui/chatwindow.h" #include "gui/shopwindow.h" diff --git a/src/net/eathena/gamehandler.cpp b/src/net/eathena/gamehandler.cpp index 4b9bcb80f..9f501a4e0 100644 --- a/src/net/eathena/gamehandler.cpp +++ b/src/net/eathena/gamehandler.cpp @@ -24,7 +24,8 @@ #include "client.h" #include "game.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/eathena/loginhandler.h" #include "net/eathena/network.h" diff --git a/src/net/eathena/gui/guildtab.cpp b/src/net/eathena/gui/guildtab.cpp index f946e1f8e..542a9327b 100644 --- a/src/net/eathena/gui/guildtab.cpp +++ b/src/net/eathena/gui/guildtab.cpp @@ -24,7 +24,8 @@ #include "chatlogger.h" #include "guild.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/net.h" #include "net/guildhandler.h" diff --git a/src/net/eathena/gui/partytab.cpp b/src/net/eathena/gui/partytab.cpp index 157869a96..420e19fea 100644 --- a/src/net/eathena/gui/partytab.cpp +++ b/src/net/eathena/gui/partytab.cpp @@ -23,9 +23,10 @@ #include "net/eathena/gui/partytab.h" #include "chatlogger.h" -#include "localplayer.h" #include "party.h" +#include "being/localplayer.h" + #include "net/net.h" #include "net/partyhandler.h" diff --git a/src/net/eathena/guildhandler.cpp b/src/net/eathena/guildhandler.cpp index 5deb326fa..22d6bc360 100644 --- a/src/net/eathena/guildhandler.cpp +++ b/src/net/eathena/guildhandler.cpp @@ -22,8 +22,9 @@ #include "net/eathena/guildhandler.h" #include "actorspritemanager.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "net/eathena/messagein.h" #include "net/eathena/protocol.h" diff --git a/src/net/eathena/npchandler.cpp b/src/net/eathena/npchandler.cpp index 2543c3b41..ea480103e 100644 --- a/src/net/eathena/npchandler.cpp +++ b/src/net/eathena/npchandler.cpp @@ -22,7 +22,7 @@ #include "net/eathena/npchandler.h" -#include "localplayer.h" +#include "being/localplayer.h" #include "gui/npcdialog.h" #include "gui/viewport.h" diff --git a/src/net/eathena/partyhandler.cpp b/src/net/eathena/partyhandler.cpp index 26e5ed981..e00fd4119 100644 --- a/src/net/eathena/partyhandler.cpp +++ b/src/net/eathena/partyhandler.cpp @@ -22,9 +22,10 @@ #include "net/eathena/partyhandler.h" #include "actorspritemanager.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "net/eathena/protocol.h" #include "net/eathena/gui/partytab.h" diff --git a/src/net/eathena/tradehandler.cpp b/src/net/eathena/tradehandler.cpp index df0364af5..3698153da 100644 --- a/src/net/eathena/tradehandler.cpp +++ b/src/net/eathena/tradehandler.cpp @@ -23,7 +23,8 @@ #include "net/eathena/tradehandler.h" #include "item.h" -#include "playerinfo.h" + +#include "being/playerinfo.h" #include "net/eathena/protocol.h" diff --git a/src/net/logindata.h b/src/net/logindata.h index abe6b6ee7..9964c9842 100644 --- a/src/net/logindata.h +++ b/src/net/logindata.h @@ -23,7 +23,7 @@ #ifndef NET_LOGINDATA_H #define NET_LOGINDATA_H -#include "being.h" +#include "being/being.h" #include diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h index 7c20224ac..3bb1df208 100644 --- a/src/net/playerhandler.h +++ b/src/net/playerhandler.h @@ -23,9 +23,10 @@ #ifndef NET_PLAYERHANDLER_H #define NET_PLAYERHANDLER_H -#include "being.h" #include "flooritem.h" -#include "playerinfo.h" + +#include "being/being.h" +#include "being/playerinfo.h" namespace Net { diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp index c156b9f51..1c48284b7 100644 --- a/src/net/tmwa/adminhandler.cpp +++ b/src/net/tmwa/adminhandler.cpp @@ -25,7 +25,8 @@ #include "actorspritemanager.h" #include "game.h" #include "notifymanager.h" -#include "playerrelations.h" + +#include "being/playerrelations.h" #include "net/chathandler.h" diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp index dea944932..678be6cc4 100644 --- a/src/net/tmwa/beinghandler.cpp +++ b/src/net/tmwa/beinghandler.cpp @@ -24,13 +24,14 @@ #include "actorspritemanager.h" #include "client.h" +#include "configuration.h" #include "effectmanager.h" #include "guild.h" #include "guildmanager.h" -#include "localplayer.h" #include "party.h" -#include "playerrelations.h" -#include "configuration.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "input/keyboardconfig.h" diff --git a/src/net/tmwa/buysellhandler.cpp b/src/net/tmwa/buysellhandler.cpp index 8bb426bef..46a95b44d 100644 --- a/src/net/tmwa/buysellhandler.cpp +++ b/src/net/tmwa/buysellhandler.cpp @@ -25,9 +25,10 @@ #include "actorspritemanager.h" #include "configuration.h" #include "inventory.h" -#include "localplayer.h" #include "notifymanager.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/buydialog.h" #include "gui/buyselldialog.h" diff --git a/src/net/tmwa/chathandler.cpp b/src/net/tmwa/chathandler.cpp index 8c455a186..aeea1b58e 100644 --- a/src/net/tmwa/chathandler.cpp +++ b/src/net/tmwa/chathandler.cpp @@ -25,8 +25,9 @@ #include "actorspritemanager.h" #include "configuration.h" #include "game.h" -#include "localplayer.h" -#include "playerrelations.h" + +#include "being/localplayer.h" +#include "being/playerrelations.h" #include "gui/chatwindow.h" #include "gui/shopwindow.h" diff --git a/src/net/tmwa/gamehandler.cpp b/src/net/tmwa/gamehandler.cpp index b066db36b..57771559d 100644 --- a/src/net/tmwa/gamehandler.cpp +++ b/src/net/tmwa/gamehandler.cpp @@ -24,7 +24,8 @@ #include "client.h" #include "game.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/tmwa/loginhandler.h" #include "net/tmwa/network.h" diff --git a/src/net/tmwa/gui/guildtab.cpp b/src/net/tmwa/gui/guildtab.cpp index 08a42eadc..cc907295f 100644 --- a/src/net/tmwa/gui/guildtab.cpp +++ b/src/net/tmwa/gui/guildtab.cpp @@ -24,7 +24,8 @@ #include "chatlogger.h" #include "guild.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "net/net.h" #include "net/guildhandler.h" diff --git a/src/net/tmwa/gui/partytab.cpp b/src/net/tmwa/gui/partytab.cpp index 348e5cbfb..e0c28ab4a 100644 --- a/src/net/tmwa/gui/partytab.cpp +++ b/src/net/tmwa/gui/partytab.cpp @@ -23,9 +23,10 @@ #include "net/tmwa/gui/partytab.h" #include "chatlogger.h" -#include "localplayer.h" #include "party.h" +#include "being/localplayer.h" + #include "net/net.h" #include "net/partyhandler.h" diff --git a/src/net/tmwa/guildhandler.cpp b/src/net/tmwa/guildhandler.cpp index 6761105e1..73b82bfbc 100644 --- a/src/net/tmwa/guildhandler.cpp +++ b/src/net/tmwa/guildhandler.cpp @@ -22,8 +22,9 @@ #include "net/tmwa/guildhandler.h" #include "actorspritemanager.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "net/tmwa/messagein.h" #include "net/tmwa/protocol.h" diff --git a/src/net/tmwa/npchandler.cpp b/src/net/tmwa/npchandler.cpp index e22036619..2f4fc2e70 100644 --- a/src/net/tmwa/npchandler.cpp +++ b/src/net/tmwa/npchandler.cpp @@ -22,7 +22,7 @@ #include "net/tmwa/npchandler.h" -#include "localplayer.h" +#include "being/localplayer.h" #include "gui/npcdialog.h" #include "gui/viewport.h" diff --git a/src/net/tmwa/partyhandler.cpp b/src/net/tmwa/partyhandler.cpp index 39a580ebf..1b29cde46 100644 --- a/src/net/tmwa/partyhandler.cpp +++ b/src/net/tmwa/partyhandler.cpp @@ -22,9 +22,10 @@ #include "net/tmwa/partyhandler.h" #include "actorspritemanager.h" -#include "localplayer.h" #include "notifymanager.h" +#include "being/localplayer.h" + #include "net/tmwa/protocol.h" #include "net/tmwa/gui/partytab.h" diff --git a/src/net/tmwa/questhandler.cpp b/src/net/tmwa/questhandler.cpp index 378f53f97..9a5f52330 100644 --- a/src/net/tmwa/questhandler.cpp +++ b/src/net/tmwa/questhandler.cpp @@ -20,7 +20,7 @@ #include "net/tmwa/questhandler.h" -#include "localplayer.h" +#include "being/localplayer.h" #include "gui/skilldialog.h" #include "gui/questswindow.h" diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp index 33c08382e..3d49a63d0 100644 --- a/src/net/tmwa/tradehandler.cpp +++ b/src/net/tmwa/tradehandler.cpp @@ -23,7 +23,8 @@ #include "net/tmwa/tradehandler.h" #include "item.h" -#include "playerinfo.h" + +#include "being/playerinfo.h" #include "net/tmwa/protocol.h" diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h index f896e190c..801eba9c1 100644 --- a/src/net/tradehandler.h +++ b/src/net/tradehandler.h @@ -23,7 +23,7 @@ #ifndef NET_TRADEHANDLER_H #define NET_TRADEHANDLER_H -#include "being.h" +#include "being/being.h" #include diff --git a/src/notifymanager.cpp b/src/notifymanager.cpp index 483ec921a..6c99532f7 100644 --- a/src/notifymanager.cpp +++ b/src/notifymanager.cpp @@ -21,9 +21,10 @@ #include "notifymanager.h" #include "guildmanager.h" -#include "localplayer.h" #include "soundmanager.h" +#include "being/localplayer.h" + #include "gui/widgets/chattab.h" #include "net/guildhandler.h" diff --git a/src/particle/particle.h b/src/particle/particle.h index 29cc7c0b4..116dd875a 100644 --- a/src/particle/particle.h +++ b/src/particle/particle.h @@ -23,7 +23,8 @@ #ifndef PARTICLE_PARTICLE_H #define PARTICLE_PARTICLE_H -#include "actor.h" +#include "being/actor.h" + #include "localconsts.h" #include diff --git a/src/playerinfo.cpp b/src/playerinfo.cpp deleted file mode 100644 index eb48c4b30..000000000 --- a/src/playerinfo.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "playerinfo.h" - -#include "client.h" -#include "configuration.h" -#include "depricatedevent.h" -#include "inventory.h" -#include "depricatedlistener.h" -#include "logger.h" - -#include "gui/inventorywindow.h" -#include "gui/npcdialog.h" -#include "gui/npcpostdialog.h" - -#include "resources/iteminfo.h" - -#include "net/inventoryhandler.h" -#include "net/playerhandler.h" - -#include "debug.h" - -namespace PlayerInfo -{ - -PlayerInfoBackend mData; -int mCharId = 0; - -Inventory *mInventory = nullptr; -Equipment *mEquipment = nullptr; - -#ifdef MANASERV_SUPPORT -std::map mSpecials; -signed char mSpecialRechargeUpdateNeeded = 0; -#endif - -bool mTrading = false; -int mLevelProgress = 0; -std::set mProtectedItems; - -// --- Triggers --------------------------------------------------------------- - -void triggerAttr(const int id, const int old) -{ - DepricatedEvent event(EVENT_UPDATEATTRIBUTE); - event.setInt("id", id); - event.setInt("oldValue", old); - event.setInt("newValue", mData.mAttributes.find(id)->second); - DepricatedEvent::trigger(CHANNEL_ATTRIBUTES, event); -} - -void triggerStat(const int id, const std::string &changed, - const int old1, const int old2) -{ - const StatMap::const_iterator it = mData.mStats.find(id); - if (it == mData.mStats.end()) - return; - - DepricatedEvent event(EVENT_UPDATESTAT); - event.setInt("id", id); - const Stat &stat = it->second; - event.setInt("base", stat.base); - event.setInt("mod", stat.mod); - event.setInt("exp", stat.exp); - event.setInt("expNeeded", stat.expNeed); - event.setString("changed", changed); - event.setInt("oldValue1", old1); - event.setInt("oldValue2", old2); - DepricatedEvent::trigger(CHANNEL_ATTRIBUTES, event); -} - -// --- Attributes ------------------------------------------------------------- - -int getAttribute(const int id) -{ - const IntMap::const_iterator it = mData.mAttributes.find(id); - if (it != mData.mAttributes.end()) - return it->second; - else - return 0; -} - -void setAttribute(const int id, const int value, const bool notify) -{ - const int old = mData.mAttributes[id]; - mData.mAttributes[id] = value; - if (notify) - triggerAttr(id, old); -} - -int getSkillLevel(const int id) -{ - const IntMap::const_iterator it = mData.mSkills.find(id); - if (it != mData.mSkills.end()) - return it->second; - else - return 0; -} - -void setSkillLevel(const int id, const int value) -{ - mData.mSkills[id] = value; -} - -// --- Stats ------------------------------------------------------------------ - -int getStatBase(const int id) -{ - const StatMap::const_iterator it = mData.mStats.find(id); - if (it != mData.mStats.end()) - return it->second.base; - else - return 0; -} - -void setStatBase(const int id, const int value, const bool notify) -{ - const int old = mData.mStats[id].base; - mData.mStats[id].base = value; - if (notify) - triggerStat(id, "base", old); -} - -int getStatMod(const int id) -{ - const StatMap::const_iterator it = mData.mStats.find(id); - if (it != mData.mStats.end()) - return it->second.mod; - else - return 0; -} - -void setStatMod(const int id, const int value, const bool notify) -{ - const int old = mData.mStats[id].mod; - mData.mStats[id].mod = value; - if (notify) - triggerStat(id, "mod", old); -} - -int getStatEffective(const int id) -{ - const StatMap::const_iterator it = mData.mStats.find(id); - if (it != mData.mStats.end()) - return it->second.base + it->second.mod; - else - return 0; -} - -const std::pair getStatExperience(const int id) -{ - const StatMap::const_iterator it = mData.mStats.find(id); - int a, b; - if (it != mData.mStats.end()) - { - a = it->second.exp; - b = it->second.expNeed; - } - else - { - a = 0; - b = 0; - } - return std::pair(a, b); -} - -void setStatExperience(const int id, const int have, - const int need, const bool notify) -{ - Stat &stat = mData.mStats[id]; - - const int oldExp = stat.exp; - const int oldExpNeed = stat.expNeed; - stat.exp = have; - stat.expNeed = need; - if (notify) - triggerStat(id, "exp", oldExp, oldExpNeed); -} - -// --- Inventory / Equipment -------------------------------------------------- - -Inventory *getInventory() -{ - return mInventory; -} - -Inventory *getStorageInventory() -{ - return Net::getInventoryHandler()->getStorage(); -} - -void clearInventory() -{ - if (mEquipment) - mEquipment->clear(); - if (mInventory) - mInventory->clear(); -} - -void setInventoryItem(const int index, const int id, - const int amount, const int refine) -{ - bool equipment = false; - const int itemType = ItemDB::get(id).getType(); - if (itemType != ITEM_UNUSABLE && itemType != ITEM_USABLE) - equipment = true; - if (mInventory) - mInventory->setItem(index, id, amount, refine, equipment); -} - -Equipment *getEquipment() -{ - return mEquipment; -} - -Item *getEquipment(const unsigned int slot) -{ - if (mEquipment) - return mEquipment->getEquipment(slot); - else - return nullptr; -} - -void setEquipmentBackend(Equipment::Backend *const backend) -{ - if (mEquipment) - mEquipment->setBackend(backend); -} - -// --- Misc ------------------------------------------------------------------- - -void setBackend(const PlayerInfoBackend &backend) -{ - mData = backend; -} - -void setCharId(const int charId) -{ - mCharId = charId; -} - -int getCharId() -{ - return mCharId; -} - -void logic() -{ -#ifdef MANASERV_SUPPORT - if ((mSpecialRechargeUpdateNeeded % 11) == 0) - { - mSpecialRechargeUpdateNeeded = 0; - FOR_EACH (SpecialsMap::iterator, it, mSpecials) - { - Special &special = it->second; - special.currentMana += special.recharge; - if (special.currentMana > special.neededMana) - special.currentMana = special.neededMana; - } - } - mSpecialRechargeUpdateNeeded++; -#endif -} - -bool isTrading() -{ - return mTrading; -} - -void setTrading(const bool trading) -{ - mTrading = trading; -} - -void updateAttrs() -{ - const Net::PlayerHandler *const handler = Net::getPlayerHandler(); - if (!handler) - return; - const int attr = handler->getAttackLocation(); - const int attackDelay = getStatBase(ATTACK_DELAY); - if (attr != -1 && attackDelay) - { - setStatBase(ATTACK_SPEED, getStatBase(attr) * 1000 - / attackDelay, false); - setStatMod(ATTACK_SPEED, getStatMod(attr) * 1000 - / attackDelay, true); - } - else - { - setStatBase(ATTACK_SPEED, 0, false); - setStatMod(ATTACK_SPEED, 0, true); - } -} - -void init() -{ -} - -void deinit() -{ - clearInventory(); -} - -void loadData() -{ - mProtectedItems.clear(); - splitToIntSet(mProtectedItems, - serverConfig.getStringValue("protectedItems"), ','); -} - -void clear() -{ - mData.mSkills.clear(); -} - -bool isTalking() -{ - return NpcDialog::isActive() || NpcPostDialog::isActive() - || InventoryWindow::isStorageActive(); -} - -void gameDestroyed() -{ - delete mInventory; - mInventory = nullptr; - delete mEquipment; - mEquipment = nullptr; -} - -void stateChange(const int state) -{ - if (state == STATE_GAME) - { - if (!mInventory) - { - mInventory = new Inventory(Inventory::INVENTORY); - mEquipment = new Equipment(); - } - } -} - -static void saveProtectedItems() -{ - std::string str; - std::set::const_iterator it = mProtectedItems.begin(); - std::set::const_iterator it_end = mProtectedItems.end(); - if (it != it_end) - { - str.append(toString(*it)); - ++ it; - } - while (it != it_end) - { - str.append(",").append(toString(*it)); - ++ it; - } - serverConfig.setValue("protectedItems", str); - serverConfig.write(); -} - -void protectItem(const int id) -{ - mProtectedItems.insert(id); - saveProtectedItems(); -} - -void unprotectItem(const int id) -{ - mProtectedItems.erase(id); - saveProtectedItems(); -} - -bool isItemProtected(const int id) -{ - return mProtectedItems.find(id) != mProtectedItems.end(); -} - -} // namespace PlayerInfo diff --git a/src/playerinfo.h b/src/playerinfo.h deleted file mode 100644 index 1c6cbc353..000000000 --- a/src/playerinfo.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef PLAYERINFO_H -#define PLAYERINFO_H - -#include "equipment.h" - -#include -#include - - -/** - * Stat information storage structure. - */ -struct Stat -{ - int base; - int mod; - int exp; - int expNeed; -}; - -typedef std::map IntMap; -typedef std::map StatMap; - -/** - * Backend for core player information. - */ -struct PlayerInfoBackend final -{ - PlayerInfoBackend() : - mAttributes(), - mStats(), - mSkills() - { - } - - IntMap mAttributes; - StatMap mStats; - IntMap mSkills; -}; - -class Equipment; -class Inventory; -class Item; - -/** - * Special information storage structure. - */ -struct Special final -{ - int currentMana; - int neededMana; - int recharge; -}; - -typedef std::map SpecialsMap; - -/** - * A database like namespace which holds global info about the localplayer - * - * NOTE: 'bool notify' is used to determine if a event is to be triggered. - */ -namespace PlayerInfo -{ - /** - * Standard attributes for players. - */ - enum Attribute - { - LEVEL = 0, - HP, - MAX_HP, - MP, - MAX_MP, - EXP, - EXP_NEEDED, - MONEY, - TOTAL_WEIGHT, - MAX_WEIGHT, - SKILL_POINTS, - CHAR_POINTS, - CORR_POINTS, - ATTACK_DELAY = 100, - ATTACK_RANGE = 101, - WALK_SPEED = 102, - ATTACK_SPEED = 103 - }; - -// --- Attributes ------------------------------------------------------------- - - /** - * Returns the value of the given attribute. - */ - int getAttribute(const int id) A_WARN_UNUSED; - - /** - * Changes the value of the given attribute. - */ - void setAttribute(const int id, const int value, - const bool notify = true); - - int getSkillLevel(const int id) A_WARN_UNUSED; - - void setSkillLevel(const int id, const int value); - -// --- Stats ------------------------------------------------------------------ - - /** - * Returns the base value of the given stat. - */ - int getStatBase(const int id) A_WARN_UNUSED; - - /** - * Changes the base value of the given stat. - */ - void setStatBase(const int id, const int value, - const bool notify = true); - - /** - * Returns the modifier for the given stat. - */ - int getStatMod(const int id) A_WARN_UNUSED; - - /** - * Changes the modifier for the given stat. - */ - void setStatMod(const int id, const int value, - const bool notify = true); - - /** - * Returns the current effective value of the given stat. Effective is base - * + mod - */ - int getStatEffective(const int id) A_WARN_UNUSED; - - /** - * Changes the level of the given stat. - */ - void setStatLevel(int id, int value, bool notify = true); - - /** - * Returns the experience of the given stat. - */ - const std::pair getStatExperience(const int id) A_WARN_UNUSED; - - /** - * Changes the experience of the given stat. - */ - void setStatExperience(const int id, const int have, - const int need, const bool notify = true); - -// --- Inventory / Equipment -------------------------------------------------- - - /** - * Returns the player's inventory. - */ - Inventory *getInventory() A_WARN_UNUSED; - - Inventory *getStorageInventory() A_WARN_UNUSED; - - /** - * Clears the player's inventory and equipment. - */ - void clearInventory(); - - void clear(); - - /** - * Changes the inventory item at the given slot. - */ - void setInventoryItem(const int index, const int id, - const int amount, const int refine); - - /** - * Returns the player's equipment. - */ - Equipment *getEquipment() A_WARN_UNUSED; - - /** - * Returns the player's equipment at the given slot. - */ - Item *getEquipment(const unsigned int slot) A_WARN_UNUSED; - -// --- Misc ------------------------------------------------------------------- - - /** - * Changes the internal PlayerInfoBackend reference; - */ - void setBackend(const PlayerInfoBackend &backend); - - void setCharId(const int charId); - - int getCharId(); - - /** - * Does necessary updates every tick. - */ - void logic(); - - /** - * Returns true if the player is involved in a trade at the moment, false - * otherwise. - */ - bool isTrading(); - - /** - * Sets whether the player is currently involved in trade or not. - */ - void setTrading(const bool trading); - - void updateAttrs(); - - /** - * Initializes some internals. - */ - void init(); - - void deinit(); - - void loadData(); - - bool isTalking(); - - void gameDestroyed(); - - void stateChange(const int state); - - void triggerAttr(int id); - - void triggerAttr(const int id, const int old); - - void triggerStat(int id); - - void triggerStat(const int id, const std::string &changed, - const int old1, const int old2 = 0); - - void setEquipmentBackend(Equipment::Backend *const backend); - - void protectItem(const int id); - - void unprotectItem(const int id); - - bool isItemProtected(const int id); - -} // namespace PlayerInfo - -#endif // PLAYERINFO_H diff --git a/src/playerrelations.cpp b/src/playerrelations.cpp deleted file mode 100644 index d20153cad..000000000 --- a/src/playerrelations.cpp +++ /dev/null @@ -1,629 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2008-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "playerrelations.h" - -#include "actorspritemanager.h" -#include "configuration.h" -#include "localplayer.h" - -#include "render/graphics.h" - -#include "utils/dtor.h" -#include "utils/gettext.h" - -#include - -#include "debug.h" - -static const char *const PLAYER_IGNORE_STRATEGY_NOP = "nop"; -static const char *const PLAYER_IGNORE_STRATEGY_EMOTE0 = "emote0"; -static const char *const DEFAULT_IGNORE_STRATEGY - = PLAYER_IGNORE_STRATEGY_EMOTE0; - -static const char *const NAME = "name"; -static const char *const RELATION = "relation"; - -static const unsigned int IGNORE_EMOTE_TIME = 100; - -typedef std::map PlayerRelations; -typedef PlayerRelations::const_iterator PlayerRelationsCIter; -typedef std::list PlayerRelationListeners; -typedef PlayerRelationListeners::const_iterator PlayerRelationListenersCIter; - -class SortPlayersFunctor final -{ - public: - bool operator() (const std::string &str1, - const std::string &str2) const - { - std::string s1 = str1; - std::string s2 = str2; - toLower(s1); - toLower(s2); - if (s1 == s2) - return str1 < str2; - return s1 < s2; - } -} playersRelSorter; - -// (De)serialisation class -class PlayerConfSerialiser final : - public ConfigurationListManager, - std::map *> -{ -public: - virtual ConfigurationObject *writeConfigItem( - const std::pair &value, - ConfigurationObject *const cobj) const override - { - if (!cobj || !value.second) - return nullptr; - cobj->setValue(NAME, value.first); - cobj->setValue(RELATION, toString( - static_cast(value.second->mRelation))); - - return cobj; - } - - virtual std::map * - readConfigItem(const ConfigurationObject *const cobj, - std::map - *const container) const override - { - if (!cobj) - return container; - const std::string name = cobj->getValue(NAME, ""); - if (name.empty()) - return container; - - if (!(*container)[name]) - { - const int v = cobj->getValueInt(RELATION, - static_cast(PlayerRelation::NEUTRAL)); - - (*container)[name] = new PlayerRelation( - static_cast(v)); - } - // otherwise ignore the duplicate entry - - return container; - } -}; - -static PlayerConfSerialiser player_conf_serialiser; // stateless singleton - -const unsigned int PlayerRelation::RELATION_PERMISSIONS[RELATIONS_NR] = -{ - /* NEUTRAL */ 0, // we always fall back to the defaults anyway - /* FRIEND */ EMOTE | SPEECH_FLOAT | SPEECH_LOG | WHISPER | TRADE, - /* DISREGARDED*/ EMOTE | SPEECH_FLOAT, - /* IGNORED */ 0, - /* ERASED */ INVISIBLE, - /* BLACKLISTED */ SPEECH_LOG | WHISPER, - /* ENEMY2 */ EMOTE | SPEECH_FLOAT | SPEECH_LOG | WHISPER | TRADE -}; - -PlayerRelation::PlayerRelation(const Relation relation) : - mRelation(relation) -{ -} - -PlayerRelationsManager::PlayerRelationsManager() : - mPersistIgnores(false), - mDefaultPermissions(PlayerRelation::DEFAULT), - mIgnoreStrategy(nullptr), - mRelations(), - mListeners(), - mIgnoreStrategies() -{ -} - -PlayerRelationsManager::~PlayerRelationsManager() -{ - delete_all(mIgnoreStrategies); - - FOR_EACH (PlayerRelationsCIter, it, mRelations) - delete it->second; - mRelations.clear(); -} - -void PlayerRelationsManager::clear() -{ - StringVect *const names = getPlayers(); - FOR_EACHP (StringVectCIter, it, names) - removePlayer(*it); - delete names; -} - -static const char *const PERSIST_IGNORE_LIST = "persistent-player-list"; -static const char *const PLAYER_IGNORE_STRATEGY = "player-ignore-strategy"; -static const char *const DEFAULT_PERMISSIONS = "default-player-permissions"; - -int PlayerRelationsManager::getPlayerIgnoreStrategyIndex( - const std::string &name) -{ - const std::vector *const strategies - = getPlayerIgnoreStrategies(); - - if (!strategies) - return -1; - - const size_t sz = strategies->size(); - for (size_t i = 0; i < sz; i++) - { - if ((*strategies)[i]->mShortName == name) - return i; - } - - return -1; -} - -void PlayerRelationsManager::load(const bool oldConfig) -{ - Configuration *cfg; - if (oldConfig) - cfg = &config; - else - cfg = &serverConfig; - clear(); - - mPersistIgnores = cfg->getValue(PERSIST_IGNORE_LIST, 1); - mDefaultPermissions = static_cast(cfg->getValue(DEFAULT_PERMISSIONS, - mDefaultPermissions)); - - const std::string ignore_strategy_name = cfg->getValue( - PLAYER_IGNORE_STRATEGY, DEFAULT_IGNORE_STRATEGY); - const int ignore_strategy_index = getPlayerIgnoreStrategyIndex( - ignore_strategy_name); - - if (ignore_strategy_index >= 0) - { - setPlayerIgnoreStrategy((*getPlayerIgnoreStrategies()) - [ignore_strategy_index]); - } - - cfg->getList, - std::map *> - ("player", &(mRelations), &player_conf_serialiser); -} - - -void PlayerRelationsManager::init() -{ - load(); - - if (!mPersistIgnores) - { - clear(); // Yes, we still keep them around in the config file - // until the next update. - } - - FOR_EACH (PlayerRelationListenersCIter, it, mListeners) - (*it)->updateAll(); -} - -void PlayerRelationsManager::store() const -{ - serverConfig.setList::const_iterator, - std::pair, - std::map *> - ("player", mRelations.begin(), mRelations.end(), - &player_conf_serialiser); - - serverConfig.setValue(DEFAULT_PERMISSIONS, mDefaultPermissions); - serverConfig.setValue(PERSIST_IGNORE_LIST, mPersistIgnores); - serverConfig.setValue(PLAYER_IGNORE_STRATEGY, - mIgnoreStrategy ? mIgnoreStrategy->mShortName - : DEFAULT_IGNORE_STRATEGY); - - serverConfig.write(); -} - -void PlayerRelationsManager::signalUpdate(const std::string &name) -{ - FOR_EACH (PlayerRelationListenersCIter, it, mListeners) - (*it)->updatedPlayer(name); - - if (actorSpriteManager) - { - Being *const being = actorSpriteManager->findBeingByName( - name, Being::PLAYER); - - if (being && being->getType() == Being::PLAYER) - being->updateColors(); - } -} - -unsigned int PlayerRelationsManager::checkPermissionSilently( - const std::string &player_name, const unsigned int flags) const -{ - const std::map::const_iterator - it = mRelations.find(player_name); - if (it == mRelations.end()) - { - return mDefaultPermissions & flags; - } - else - { - const PlayerRelation *const r = (*it).second; - unsigned int permissions = - PlayerRelation::RELATION_PERMISSIONS[r->mRelation]; - - switch (r->mRelation) - { - case PlayerRelation::NEUTRAL: - permissions = mDefaultPermissions; - break; - - case PlayerRelation::FRIEND: - permissions |= mDefaultPermissions; // widen - break; - - case PlayerRelation::DISREGARDED: - case PlayerRelation::IGNORED: - case PlayerRelation::ERASED: - case PlayerRelation::BLACKLISTED: - case PlayerRelation::ENEMY2: - default: - permissions &= mDefaultPermissions; // narrow - } - - return permissions & flags; - } -} - -bool PlayerRelationsManager::hasPermission(const Being *const being, - const unsigned int flags) const -{ - if (!being) - return false; - - if (being->getType() == ActorSprite::PLAYER) - return hasPermission(being->getName(), flags) == flags; - return true; -} - -bool PlayerRelationsManager::hasPermission(const std::string &name, - const unsigned int flags) const -{ - if (!actorSpriteManager) - return false; - - const unsigned int rejections = flags - & ~checkPermissionSilently(name, flags); - const bool permitted = (rejections == 0); - - if (!permitted) - { - // execute `ignore' strategy, if possible - if (mIgnoreStrategy) - { - Being *const b = actorSpriteManager->findBeingByName( - name, ActorSprite::PLAYER); - - if (b && b->getType() == ActorSprite::PLAYER) - mIgnoreStrategy->ignore(b, rejections); - } - } - - return permitted; -} - -void PlayerRelationsManager::setRelation(const std::string &player_name, - const PlayerRelation::Relation - relation) -{ - if (!player_node || (relation != PlayerRelation::NEUTRAL - && player_node->getName() == player_name)) - { - return; - } - - PlayerRelation *const r = mRelations[player_name]; - if (!r) - mRelations[player_name] = new PlayerRelation(relation); - else - r->mRelation = relation; - - signalUpdate(player_name); -} - -StringVect *PlayerRelationsManager::getPlayers() const -{ - StringVect *const retval = new StringVect(); - - FOR_EACH (PlayerRelationsCIter, it, mRelations) - { - if (it->second) - retval->push_back(it->first); - } - - std::sort(retval->begin(), retval->end(), playersRelSorter); - - return retval; -} - -StringVect *PlayerRelationsManager::getPlayersByRelation( - const PlayerRelation::Relation rel) const -{ - StringVect *const retval = new StringVect(); - - FOR_EACH (PlayerRelationsCIter, it, mRelations) - { - if (it->second && it->second->mRelation == rel) - retval->push_back(it->first); - } - - std::sort(retval->begin(), retval->end(), playersRelSorter); - - return retval; -} - -void PlayerRelationsManager::removePlayer(const std::string &name) -{ - delete mRelations[name]; - mRelations.erase(name); - signalUpdate(name); -} - - -PlayerRelation::Relation PlayerRelationsManager::getRelation( - const std::string &name) const -{ - const std::map::const_iterator - it = mRelations.find(name); - if (it != mRelations.end()) - return (*it).second->mRelation; - - return PlayerRelation::NEUTRAL; -} - -//////////////////////////////////////// -// defaults - -unsigned int PlayerRelationsManager::getDefault() const -{ - return mDefaultPermissions; -} - -void PlayerRelationsManager::setDefault(const unsigned int permissions) -{ - mDefaultPermissions = permissions; - - store(); - signalUpdate(""); -} - -void PlayerRelationsManager::ignoreTrade(const std::string &name) -{ - if (name.empty()) - return; - - const PlayerRelation::Relation relation = getRelation(name); - - if (relation == PlayerRelation::IGNORED - || relation == PlayerRelation::DISREGARDED - || relation == PlayerRelation::BLACKLISTED - || relation == PlayerRelation::ERASED) - { - return; - } - else - { - player_relations.setRelation(name, PlayerRelation::BLACKLISTED); - } -} - -bool PlayerRelationsManager::checkBadRelation(const std::string &name) const -{ - if (name.empty()) - return true; - - const PlayerRelation::Relation relation = getRelation(name); - - if (relation == PlayerRelation::IGNORED - || relation == PlayerRelation::DISREGARDED - || relation == PlayerRelation::BLACKLISTED - || relation == PlayerRelation::ERASED - || relation == PlayerRelation::ENEMY2) - { - return true; - } - return false; -} - -//////////////////////////////////////// -// ignore strategies - - -class PIS_nothing final : public PlayerIgnoreStrategy -{ -public: - PIS_nothing() : - PlayerIgnoreStrategy() - { - // TRANSLATORS: ignore/unignore action - mDescription = _("Completely ignore"); - mShortName = PLAYER_IGNORE_STRATEGY_NOP; - } - - virtual void ignore(Being *const being A_UNUSED, - const unsigned int flags A_UNUSED) const override - { - } -}; - -class PIS_dotdotdot final : public PlayerIgnoreStrategy -{ -public: - PIS_dotdotdot() : - PlayerIgnoreStrategy() - { - // TRANSLATORS: ignore/unignore action - mDescription = _("Print '...'"); - mShortName = "dotdotdot"; - } - - virtual void ignore(Being *const being, - const unsigned int flags A_UNUSED) const override - { - if (!being) - return; - - logger->log("ignoring: " + being->getName()); - being->setSpeech("..."); - } -}; - - -class PIS_blinkname final : public PlayerIgnoreStrategy -{ -public: - PIS_blinkname() : - PlayerIgnoreStrategy() - { - // TRANSLATORS: ignore/unignore action - mDescription = _("Blink name"); - mShortName = "blinkname"; - } - - virtual void ignore(Being *const being, - const unsigned int flags A_UNUSED) const override - { - if (!being) - return; - - logger->log("ignoring: " + being->getName()); - being->flashName(200); - } -}; - -class PIS_emote final : public PlayerIgnoreStrategy -{ -public: - PIS_emote(const uint8_t emote_nr, const std::string &description, - const std::string &shortname) : - PlayerIgnoreStrategy(), - mEmotion(emote_nr) - { - mDescription = description; - mShortName = shortname; - } - - virtual void ignore(Being *const being, - const unsigned int flags A_UNUSED) const override - { - if (!being) - return; - - being->setEmote(mEmotion, IGNORE_EMOTE_TIME); - } - uint8_t mEmotion; -}; - -std::vector * -PlayerRelationsManager::getPlayerIgnoreStrategies() -{ - if (mIgnoreStrategies.empty()) - { - // not initialised yet? - mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE, - // TRANSLATORS: ignore strategi - _("Floating '...' bubble"), - PLAYER_IGNORE_STRATEGY_EMOTE0)); - mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE + 1, - // TRANSLATORS: ignore strategi - _("Floating bubble"), - "emote1")); - mIgnoreStrategies.push_back(new PIS_nothing); - mIgnoreStrategies.push_back(new PIS_dotdotdot); - mIgnoreStrategies.push_back(new PIS_blinkname); - } - return &mIgnoreStrategies; -} - -bool PlayerRelationsManager::isGoodName(const std::string &name) const -{ - const size_t size = name.size(); - - if (size < 3) - return true; - - const std::map::const_iterator - it = mRelations.find(name); - if (it != mRelations.end()) - return true; - - return checkName(name); -} - -bool PlayerRelationsManager::isGoodName(Being *const being) const -{ - if (!being) - return false; - if (being->getGoodStatus() != -1) - return (being->getGoodStatus() == 1); - - const std::string name = being->getName(); - const size_t size = name.size(); - - if (size < 3) - return true; - - const std::map::const_iterator - it = mRelations.find(name); - if (it != mRelations.end()) - return true; - - const bool status = checkName(name); - being->setGoodStatus(status ? 1 : 0); - return status; -} - -bool PlayerRelationsManager::checkName(const std::string &name) const -{ - const size_t size = name.size(); - const std::string check = config.getStringValue("unsecureChars"); - const std::string lastChar = name.substr(size - 1, 1); - - if (name.substr(0, 1) == " " || lastChar == " " || lastChar == "." - || name.find(" ") != std::string::npos) - { - return false; - } - else if (check.empty()) - { - return true; - } - else if (name.find_first_of(check) != std::string::npos) - { - return false; - } - else - { - return true; - } -} - -PlayerRelationsManager player_relations; diff --git a/src/playerrelations.h b/src/playerrelations.h deleted file mode 100644 index 68ec1066a..000000000 --- a/src/playerrelations.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2008-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef PLAYERRELATIONS_H -#define PLAYERRELATIONS_H - -#include "utils/stringvector.h" - -#include -#include - -#include "localconsts.h" - -class Being; - -struct PlayerRelation final -{ - static const unsigned int EMOTE = (1 << 0); - static const unsigned int SPEECH_FLOAT = (1 << 1); - static const unsigned int SPEECH_LOG = (1 << 2); - static const unsigned int WHISPER = (1 << 3); - static const unsigned int TRADE = (1 << 4); - static const unsigned int INVISIBLE = (1 << 5); - static const unsigned int BLACKLIST = (1 << 6); - static const unsigned int ENEMY = (1 << 7); - - static const unsigned int RELATIONS_NR = 7; - static const unsigned int RELATION_PERMISSIONS[RELATIONS_NR]; - - static const unsigned int DEFAULT = EMOTE - | SPEECH_FLOAT - | SPEECH_LOG - | WHISPER - | TRADE; - enum Relation - { - NEUTRAL = 0, - FRIEND = 1, - DISREGARDED = 2, - IGNORED = 3, - ERASED = 4, - BLACKLISTED = 5, - ENEMY2 = 6 - }; - - explicit PlayerRelation(const Relation relation); - - A_DELETE_COPY(PlayerRelation) - - Relation mRelation; // bitmask for all of the above -}; - - -/** - * Ignore strategy: describes how we should handle ignores. - */ -class PlayerIgnoreStrategy -{ - public: - std::string mDescription; - std::string mShortName; - - PlayerIgnoreStrategy() : - mDescription(), - mShortName() - { - } - - A_DELETE_COPY(PlayerIgnoreStrategy) - - virtual ~PlayerIgnoreStrategy() - { } - - /** - * Handle the ignoring of the indicated action by the indicated player. - */ - virtual void ignore(Being *const being, - const unsigned int flags) const = 0; -}; - -class PlayerRelationsListener -{ - public: - PlayerRelationsListener() - { } - - virtual ~PlayerRelationsListener() - { } - - virtual void updatedPlayer(const std::string &name) = 0; - - virtual void updateAll() = 0; -}; - -/** - * Player relations class, represents any particular relations and/or - * preferences the user of the local client has wrt other players (identified - * by std::string). - */ -class PlayerRelationsManager final -{ - public: - PlayerRelationsManager(); - - A_DELETE_COPY(PlayerRelationsManager) - - ~PlayerRelationsManager(); - - /** - * Initialise player relations manager (load config file etc.) - */ - void init(); - - /** - * Load configuration from our config file, or substitute defaults. - */ - void load(const bool oldConfig = false); - - /** - * Save configuration to our config file. - */ - void store() const; - - /** - * Determines whether the player in question is being ignored, filtered by - * the specified flags. - */ - unsigned int checkPermissionSilently(const std::string &player_name, - const unsigned int flags) - const A_WARN_UNUSED; - - /** - * Tests whether the player in question is being ignored for any of the - * actions in the specified flags. If so, trigger appropriate side effects - * if requested by the player. - */ - bool hasPermission(const Being *const being, - const unsigned int flags) const A_WARN_UNUSED; - - bool hasPermission(const std::string &being, - const unsigned int flags) const A_WARN_UNUSED; - - /** - * Updates the relationship with this player. - */ - void setRelation(const std::string &name, - const PlayerRelation::Relation relation); - - /** - * Updates the relationship with this player. - */ - PlayerRelation::Relation getRelation(const std::string &name) - const A_WARN_UNUSED; - - /** - * Deletes the information recorded for a player. - */ - void removePlayer(const std::string &name); - - /** - * Retrieves the default permissions. - */ - unsigned int getDefault() const A_WARN_UNUSED; - - /** - * Sets the default permissions. - */ - void setDefault(const unsigned int permissions); - - /** - * Retrieves all known player ignore strategies. - * - * The player ignore strategies are allocated statically and must - * not be deleted. - */ - std::vector *getPlayerIgnoreStrategies() - A_WARN_UNUSED; - - /** - * Return the current player ignore strategy. - * - * \return A player ignore strategy, or nullptr - */ - const PlayerIgnoreStrategy *getPlayerIgnoreStrategy() const - A_WARN_UNUSED - { return mIgnoreStrategy; } - - /** - * Sets the strategy to call when ignoring players. - */ - void setPlayerIgnoreStrategy(PlayerIgnoreStrategy *const strategy) - { mIgnoreStrategy = strategy; } - - /** - * For a given ignore strategy short name, find the appropriate index - * in the ignore strategies vector. - * - * \param The short name of the ignore strategy to look up - * \return The appropriate index, or -1 - */ - int getPlayerIgnoreStrategyIndex(const std::string &shortname) - A_WARN_UNUSED; - - /** - * Retrieves a sorted vector of all players for which we have any - * relations recorded. - */ - StringVect *getPlayers() const A_WARN_UNUSED; - - StringVect *getPlayersByRelation(const PlayerRelation::Relation rel) - const A_WARN_UNUSED; - - /** - * Removes all recorded player info. - */ - void clear(); - - /** - * Do we persist our `ignore' setup? - */ - bool getPersistIgnores() const - { return mPersistIgnores; } - - void ignoreTrade(const std::string &name); - - bool isGoodName(Being *const being) const A_WARN_UNUSED; - - bool isGoodName(const std::string &name) const A_WARN_UNUSED; - - /** - * Change the `ignore persist' flag. - * - * @param value Whether to persist ignores - */ - void setPersistIgnores(const bool value) - { mPersistIgnores = value; } - - void addListener(PlayerRelationsListener *const listener) - { mListeners.push_back(listener); } - - void removeListener(PlayerRelationsListener *const listener) - { mListeners.remove(listener); } - - bool checkBadRelation(const std::string &name) const A_WARN_UNUSED; - - private: - void signalUpdate(const std::string &name); - - bool mPersistIgnores; // If NOT set, we delete the - // ignored data upon reloading - unsigned int mDefaultPermissions; - - bool checkName(const std::string &name) const A_WARN_UNUSED; - - PlayerIgnoreStrategy *mIgnoreStrategy; - std::map mRelations; - std::list mListeners; - std::vector mIgnoreStrategies; -}; - - -extern PlayerRelationsManager player_relations; // singleton representation - // of player relations - - -#endif // PLAYERRELATIONS_H diff --git a/src/resources/beinginfo.h b/src/resources/beinginfo.h index e9f1a6055..a475a573a 100644 --- a/src/resources/beinginfo.h +++ b/src/resources/beinginfo.h @@ -23,7 +23,7 @@ #ifndef RESOURCES_BEINGINFO_H #define RESOURCES_BEINGINFO_H -#include "actorsprite.h" +#include "being/actorsprite.h" #include "resources/colordb.h" #include "resources/cursor.h" diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index a38f32e66..d0b305c49 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -23,7 +23,7 @@ #ifndef RESOURCES_ITEMINFO_H #define RESOURCES_ITEMINFO_H -#include "being.h" +#include "being/being.h" #include "resources/colordb.h" diff --git a/src/soundmanager.cpp b/src/soundmanager.cpp index 8575edd2b..c7328b78f 100644 --- a/src/soundmanager.cpp +++ b/src/soundmanager.cpp @@ -23,7 +23,8 @@ #include "soundmanager.h" #include "configuration.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "resources/sdlmusic.h" #include "resources/resourcemanager.h" diff --git a/src/spellmanager.cpp b/src/spellmanager.cpp index 23eb90ad1..b72d8ec40 100644 --- a/src/spellmanager.cpp +++ b/src/spellmanager.cpp @@ -23,8 +23,9 @@ #include "spellmanager.h" #include "configuration.h" -#include "localplayer.h" -#include "playerinfo.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" #include "gui/chatwindow.h" diff --git a/src/spellmanager.h b/src/spellmanager.h index d2320e8a1..db4abff1c 100644 --- a/src/spellmanager.h +++ b/src/spellmanager.h @@ -25,9 +25,10 @@ #include #include + #include "textcommand.h" -#include "being.h" +#include "being/being.h" const int SPELL_MIN_ID = 100000; const unsigned int SPELL_SHORTCUT_ITEMS = 49; diff --git a/src/spellshortcut.cpp b/src/spellshortcut.cpp index ffcc4ca49..fa76d2fd1 100644 --- a/src/spellshortcut.cpp +++ b/src/spellshortcut.cpp @@ -25,7 +25,8 @@ #include "configuration.h" #include "inventory.h" #include "item.h" -#include "localplayer.h" + +#include "being/localplayer.h" #include "gui/widgets/chattab.h" -- cgit v1.2.3-70-g09d2