diff options
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | src/actorsprite.h | 3 | ||||
-rw-r--r-- | src/being.cpp | 134 | ||||
-rw-r--r-- | src/being.h | 22 | ||||
-rw-r--r-- | src/compoundsprite.cpp | 15 | ||||
-rw-r--r-- | src/compoundsprite.h | 3 | ||||
-rw-r--r-- | src/localplayer.h | 3 | ||||
-rw-r--r-- | src/net/tmwa/adminhandler.cpp | 3 | ||||
-rw-r--r-- | src/net/tmwa/beinghandler.cpp | 58 | ||||
-rw-r--r-- | src/net/tmwa/charserverhandler.cpp | 25 | ||||
-rw-r--r-- | src/net/tmwa/charserverhandler.h | 2 |
11 files changed, 160 insertions, 111 deletions
@@ -7,9 +7,10 @@ - Added support for reading most client-data settings from settings.xml - Added support for XML includes, both absolute and relative - Added support for map/layer mask -- Added support for sprite replacements +- Added support for item sprite replacements - Added support for particle effects on equipment - Added support for hit/miss sounds on equipment for all players +- Added support for players changing into monsters or NPCs - Added online player list to Social window - Added notification sound on receiving whisper - Added default ports when connecting to a custom server diff --git a/src/actorsprite.h b/src/actorsprite.h index 58780ffb..a25481ec 100644 --- a/src/actorsprite.h +++ b/src/actorsprite.h @@ -40,7 +40,8 @@ public: PLAYER, NPC, MONSTER, - FLOOR_ITEM + FLOOR_ITEM, + PORTAL }; enum TargetCursorSize diff --git a/src/being.cpp b/src/being.cpp index aec9deb0..e62ac0e8 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -62,23 +62,15 @@ Being::Being(int id, Type type, int subtype, Map *map): ActorSprite(id), - mInfo(BeingInfo::Unknown), - mType(type) + mInfo(BeingInfo::Unknown) { setMap(map); - setSubtype(subtype); + setType(type, subtype); mSpeechBubble = new SpeechBubble; mMoveSpeed = Net::getPlayerHandler()->getDefaultMoveSpeed(); - if (getType() == PLAYER) - mShowName = config.visibleNames; - - if (getType() == PLAYER || getType() == NPC) - setShowName(true); - - updateColors(); listen(Event::ConfigChannel); listen(Event::ChatChannel); } @@ -91,17 +83,27 @@ Being::~Being() mSpeechBubble = nullptr; mDispName = nullptr; mText = nullptr; - - removeAllSpriteParticles(); } -void Being::setSubtype(Uint16 subtype) +/** + * Can be used to change the type of the being. + * + * Practical use: players (usually GMs) can change into monsters and back. + */ +void Being::setType(Type type, int subtype) { - if (subtype == mSubType) + if (mType == type && mSubType == subtype) return; + mType = type; mSubType = subtype; + for (auto &spriteState : mSpriteStates) + { + spriteState.visibleId = 0; + spriteState.particles.clear(); + } + switch (getType()) { case MONSTER: @@ -112,8 +114,12 @@ void Being::setSubtype(Uint16 subtype) case NPC: mInfo = NPCDB::get(mSubType); setupSpriteDisplay(mInfo->display, false); + mShowName = true; break; case PLAYER: { + clear(); + mChildParticleEffects.clear(); + int id = -100 - subtype; // Prevent showing errors when sprite doesn't exist @@ -121,12 +127,22 @@ void Being::setSubtype(Uint16 subtype) id = -100; setSprite(Net::getCharHandler()->baseSprite(), id); + restoreAllSpriteParticles(); + mShowName = this == local_player ? config.showOwnName + : config.visibleNames; break; } case FLOOR_ITEM: + case PORTAL: case UNKNOWN: break; } + + doRedraw(); + + updateName(); + updateNamePosition(); + updateColors(); } bool Being::isTargetSelection() const @@ -172,7 +188,7 @@ void Being::setPosition(const Vector &pos) { Actor::setPosition(pos); - updateCoords(); + updateNamePosition(); if (mText) mText->adviseXY(getPixelX(), getSpeechTextYPosition()); @@ -303,8 +319,8 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type, int attackId) { gcn::Font *font; - std::string damage = amount ? toString(amount) : type == FLEE ? - "dodge" : "miss"; + std::string damage = amount ? toString(amount) + : (type == FLEE ? "dodge" : "miss"); const gcn::Color *color; font = gui->getInfoParticleFont(); @@ -449,17 +465,11 @@ void Being::handleAttack(Being *victim, int damage, int attackId) void Being::setName(const std::string &name) { if (getType() == NPC) - { mName = name.substr(0, name.find('#', 0)); - showName(); - } else - { mName = name; - if (getType() == PLAYER && getShowName()) - showName(); - } + updateName(); } void Being::setShowName(bool doShowName) @@ -468,14 +478,7 @@ void Being::setShowName(bool doShowName) return; mShowName = doShowName; - - if (doShowName) - showName(); - else - { - delete mDispName; - mDispName = nullptr; - } + updateName(); } void Being::setGuildName(const std::string &name) @@ -744,14 +747,14 @@ void Being::lookAt(const Vector &destPos) } } -void Being::setDirection(Uint8 direction) +void Being::setDirection(uint8_t direction) { if (!direction || mDirection == direction) return; mDirection = direction; - SpriteDirection dir; + SpriteDirection dir = DIRECTION_DEFAULT; if (mDirection & UP) dir = DIRECTION_UP; else if (mDirection & DOWN) @@ -762,7 +765,7 @@ void Being::setDirection(Uint8 direction) dir = DIRECTION_LEFT; mSpriteDirection = dir; - updateSprites(); + updatePlayerSprites(); CompoundSprite::setDirection(dir); } @@ -954,7 +957,7 @@ void Being::drawSpeech(int offsetX, int offsetY) } } -void Being::updateCoords() +void Being::updateNamePosition() { if (!mDispName) return; @@ -972,10 +975,14 @@ void Being::flashName(int time) mDispName->flash(time); } -void Being::showName() +void Being::updateName() { delete mDispName; mDispName = nullptr; + + if (!mShowName) + return; + std::string mDisplayName(mName); if (getType() == PLAYER) @@ -1014,7 +1021,7 @@ void Being::showName() mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(), gcn::Graphics::CENTER, mNameColor, font); - updateCoords(); + updateNamePosition(); } void Being::addSpriteParticles(SpriteState &spriteState, const SpriteDisplay &display) @@ -1048,6 +1055,9 @@ void Being::removeAllSpriteParticles() void Being::restoreAllSpriteParticles() { + if (mType != PLAYER) + return; + for (auto &spriteState : mSpriteStates) { if (spriteState.id) @@ -1095,17 +1105,18 @@ void Being::updateColors() } if (mDispName) - { mDispName->setColor(mNameColor); - } } /** - * Updates the visible sprite IDs of the being, taking into account the item + * Updates the visible sprite IDs of the player, taking into account the item * replacements. */ -void Being::updateSprites() +void Being::updatePlayerSprites() { + if (mType != PLAYER) + return; + // hack for allow different logic in dead player const int direction = mAction == DEAD ? DIRECTION_DEAD : mSpriteDirection; @@ -1163,6 +1174,8 @@ void Being::updateSprites() // Set the new sprites bool newSpriteSet = false; + ensureSize(mSpriteStates.size()); + for (size_t i = 0; i < mSpriteStates.size(); i++) { auto &spriteState = mSpriteStates[i]; @@ -1207,9 +1220,6 @@ void Being::updateSprites() void Being::setSprite(unsigned slot, int id, const std::string &color, bool isWeapon) { - if (slot >= size()) - ensureSize(slot + 1); - if (slot >= mSpriteStates.size()) mSpriteStates.resize(slot + 1); @@ -1220,7 +1230,7 @@ void Being::setSprite(unsigned slot, int id, const std::string &color, removeSpriteParticles(spriteState); // Clear the current sprite when the color changes - if (spriteState.color != color) + if (spriteState.color != color && spriteState.visibleId) { spriteState.visibleId = 0; CompoundSprite::setSprite(slot, nullptr); @@ -1238,13 +1248,14 @@ void Being::setSprite(unsigned slot, int id, const std::string &color, { auto &itemInfo = itemDb->get(id); - addSpriteParticles(spriteState, itemInfo.display); + if (mType == PLAYER) + addSpriteParticles(spriteState, itemInfo.display); if (isWeapon) mEquippedWeapon = &itemInfo; } - updateSprites(); + updatePlayerSprites(); } void Being::setSpriteID(unsigned slot, int id) @@ -1265,12 +1276,6 @@ bool Being::drawnWhenBehind() const return CompoundSprite::getNumberOfLayers() == 1; } -void Being::updateName() -{ - if (mShowName) - showName(); -} - void Being::setGender(Gender gender) { if (gender != mGender) @@ -1288,8 +1293,10 @@ void Being::setGender(Gender gender) } } - updateSprites(); - updateName(); + updatePlayerSprites(); + + if (config.showGender) + updateName(); } } @@ -1300,6 +1307,17 @@ void Being::setGM(bool gm) updateColors(); } +void Being::setIp(int ip) +{ + if (mIp == ip) + return; + + mIp = ip; + + if (local_player && local_player->getShowIp()) + updateName(); +} + bool Being::canTalk() { return mType == NPC; @@ -1340,7 +1358,9 @@ void Being::event(Event::Channel channel, const Event &event) void Being::setMap(Map *map) { // Remove sprite particles because ActorSprite is going to kill them all - removeAllSpriteParticles(); + for (auto &spriteState : mSpriteStates) + spriteState.particles.clear(); + mRestoreSpriteParticlesOnLogic = true; ActorSprite::setMap(map); diff --git a/src/being.h b/src/being.h index 0f32e60f..4a29c739 100644 --- a/src/being.h +++ b/src/being.h @@ -109,7 +109,8 @@ class Being : public ActorSprite, public EventListener * Constructor. * * @param id a unique being id - * @param subtype partly determines the type of the being + * @param type type of being + * @param subtype refers to a specific npc, monster, species, etc. * @param map the map the being is on */ Being(int id, Type type, int subtype, Map *map); @@ -119,6 +120,8 @@ class Being : public ActorSprite, public EventListener Type getType() const final { return mType; } + void setType(Type type, int subtype); + /** * Removes all path nodes from this being. */ @@ -283,11 +286,6 @@ class Being : public ActorSprite, public EventListener uint16_t getSubType() const { return mSubType; } - /** - * Set Being's subtype (mostly for view for monsters and NPCs) - */ - void setSubtype(uint16_t subtype); - const BeingInfo &getInfo() const { return *mInfo; } @@ -416,7 +414,7 @@ class Being : public ActorSprite, public EventListener * Sets the IP or an IP hash. * The TMW-Athena server sends this information only to GMs. */ - void setIp(int ip) { mIp = ip; } + void setIp(int ip); /** * Returns the player's IP or an IP hash. @@ -455,9 +453,7 @@ class Being : public ActorSprite, public EventListener /** * Updates name's location. */ - void updateCoords(); - - void showName(); + void updateNamePosition(); void addSpriteParticles(SpriteState &spriteState, const SpriteDisplay &display); void removeSpriteParticles(SpriteState &spriteState); @@ -465,7 +461,7 @@ class Being : public ActorSprite, public EventListener void restoreAllSpriteParticles(); void updateColors(); - void updateSprites(); + void updatePlayerSprites(); /** * Gets the advised Y chat text position. @@ -487,7 +483,7 @@ class Being : public ActorSprite, public EventListener int mAttackSpeed = 350; /**< Attack speed */ Action mAction = STAND; /**< Action the being is performing */ - uint16_t mSubType = 0xFFFF; /**< Subtype (graphical view, basically) */ + int mSubType = 0xFFFF; /**< Subtype (graphical view, basically) */ uint8_t mDirection = DOWN; /**< Facing direction */ uint8_t mSpriteDirection = DIRECTION_DOWN; /**< Facing direction */ @@ -525,7 +521,7 @@ class Being : public ActorSprite, public EventListener private: void updateMovement(); - const Type mType; + Type mType = UNKNOWN; /** Speech Bubble components */ SpeechBubble *mSpeechBubble; diff --git a/src/compoundsprite.cpp b/src/compoundsprite.cpp index 24a166a8..76039bbd 100644 --- a/src/compoundsprite.cpp +++ b/src/compoundsprite.cpp @@ -29,9 +29,7 @@ #include <SDL.h> -CompoundSprite::CompoundSprite() -{ -} +CompoundSprite::CompoundSprite() = default; CompoundSprite::~CompoundSprite() { @@ -178,6 +176,12 @@ void CompoundSprite::ensureSize(size_t layerCount) mSprites.resize(layerCount); } +void CompoundSprite::doRedraw() +{ + if (mNeedsRedraw) + redraw(); +} + int CompoundSprite::getDuration() const { int duration = 0; @@ -209,8 +213,9 @@ static void updateValues(int &dimension, int &pos, int imgDimUL, int imgDimRD, i void CompoundSprite::redraw() const { #if 1 // TODO_SDL2: Does it make sense to implement CompoundSprite? - mWidth = mSprites.at(0)->getWidth(); - mHeight = mSprites.at(0)->getHeight(); + auto baseSprite = mSprites.empty() ? nullptr : mSprites.at(0); + mWidth = baseSprite ? baseSprite->getWidth() : 0; + mHeight = baseSprite ? baseSprite->getHeight() : 0; mOffsetX = 0; mOffsetY = 0; mNeedsRedraw = false; diff --git a/src/compoundsprite.h b/src/compoundsprite.h index 17e2dd6c..f32e4e6c 100644 --- a/src/compoundsprite.h +++ b/src/compoundsprite.h @@ -81,8 +81,7 @@ public: void ensureSize(size_t layerCount); - void doRedraw() - { mNeedsRedraw = true; } + void doRedraw(); private: void redraw() const; diff --git a/src/localplayer.h b/src/localplayer.h index 0b53964c..c88faaea 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -61,8 +61,7 @@ enum class LocalPlayer final : public Being { public: - LocalPlayer(int id= 65535, int subtype = 0); - + LocalPlayer(int id = 65535, int subtype = 0); ~LocalPlayer() override; void logic() override; diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp index c60050bd..4c4ecdad 100644 --- a/src/net/tmwa/adminhandler.cpp +++ b/src/net/tmwa/adminhandler.cpp @@ -68,10 +68,7 @@ void AdminHandler::handleMessage(MessageIn &msg) id = msg.readInt32(); int ip = msg.readInt32(); if (Being *player = actorSpriteManager->findBeing(id)) - { player->setIp(ip); - player->updateName(); - } break; } } diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp index e2c4f158..ba983542 100644 --- a/src/net/tmwa/beinghandler.cpp +++ b/src/net/tmwa/beinghandler.cpp @@ -78,29 +78,51 @@ BeingHandler::BeingHandler(bool enableSync): handledMessages = _messages; } -static Being *createBeing(int id, short job) +static ActorSprite::Type typeFromJob(short job) { - ActorSprite::Type type = ActorSprite::UNKNOWN; if (job <= 25 || (job >= 4001 && job <= 4049)) - type = ActorSprite::PLAYER; - else if (job >= 46 && job <= 1000) - type = ActorSprite::NPC; - else if (job > 1000 && job <= 2000) - type = ActorSprite::MONSTER; - else if (job == 45) + return ActorSprite::PLAYER; + if (job >= 46 && job <= 1000) + return ActorSprite::NPC; + if (job > 1000 && job <= 2000) + return ActorSprite::MONSTER; + if (job == 45) + return ActorSprite::PORTAL; + + return ActorSprite::UNKNOWN; +} + +static Being *createBeing(int id, short job) +{ + const auto type = typeFromJob(job); + if (type == ActorSprite::PORTAL) return nullptr; // Skip portals Being *being = actorSpriteManager->createBeing(id, type, job); if (type == ActorSprite::PLAYER || type == ActorSprite::NPC) { - MessageOut outMsg(0x0094); - outMsg.writeInt32(id);//readLong(2)); + MessageOut outMsg(CMSG_NAME_REQUEST); + outMsg.writeInt32(id); } return being; } +static void updateBeingType(Being *being, short job) +{ + const auto type = typeFromJob(job); + const bool typeChanged = being->getType() != type; + + being->setType(type, job); + + if (typeChanged && type == ActorSprite::PLAYER) + { + MessageOut outMsg(CMSG_NAME_REQUEST); + outMsg.writeInt32(being->getId()); + } +} + static void handleMoveMessage(Map *map, Being *dstBeing, Uint16 srcX, Uint16 srcY, Uint16 dstX, Uint16 dstY) @@ -209,7 +231,7 @@ void BeingHandler::handleMessage(MessageIn &msg) speed = 150.0f; // In ticks per tile * 10 dstBeing->setMoveSpeed(Vector(speed / 10, speed / 10)); - dstBeing->setSubtype(job); + updateBeingType(dstBeing, job); hairStyle = msg.readInt16(); weapon = msg.readInt16(); headBottom = msg.readInt16(); @@ -417,6 +439,9 @@ void BeingHandler::handleMessage(MessageIn &msg) switch (type) { + case LOOK::BASE: + updateBeingType(dstBeing, id); + break; case LOOK::HAIR: { // const int look = id / 256; @@ -531,7 +556,7 @@ void BeingHandler::handleMessage(MessageIn &msg) else dstBeing->setMoveSpeed(Net::getPlayerHandler()->getDefaultMoveSpeed()); - dstBeing->setSubtype(job); + updateBeingType(dstBeing, job); hairStyle = msg.readInt16(); weapon = msg.readInt16(); shield = msg.readInt16(); @@ -545,8 +570,9 @@ void BeingHandler::handleMessage(MessageIn &msg) headTop = msg.readInt16(); headMid = msg.readInt16(); hairColor = msg.readInt16(); - shoes = msg.readInt16(); - gloves = msg.readInt16(); + msg.readInt16(); // clothes_color + msg.readInt8(); // head_dir + msg.readInt8(); // unused2 msg.readInt32(); // guild msg.readInt16(); // emblem msg.readInt16(); // manner @@ -601,11 +627,11 @@ void BeingHandler::handleMessage(MessageIn &msg) } else if (msg.getId() == SMSG_PLAYER_MOVE) { - msg.readInt8(); // unknown + msg.readInt8(); // five } msg.readInt8(); // Lv - msg.readInt8(); // unknown + msg.readInt8(); // unused dstBeing->setStunMode(stunMode); dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp index 636b58ce..39155201 100644 --- a/src/net/tmwa/charserverhandler.cpp +++ b/src/net/tmwa/charserverhandler.cpp @@ -28,7 +28,6 @@ #include "gui/charcreatedialog.h" #include "gui/okdialog.h" -#include "net/logindata.h" #include "net/net.h" #include "net/tmwa/gamehandler.h" @@ -210,21 +209,20 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character const Token &token = static_cast<LoginHandler*>(Net::getLoginHandler())->getToken(); - auto *tempPlayer = new LocalPlayer(msg.readInt32(), 0); - tempPlayer->setGender(token.sex); + const int id = msg.readInt32(); character->data.mAttributes[EXP] = msg.readInt32(); character->data.mAttributes[MONEY] = msg.readInt32(); character->data.mStats[JOB].exp = msg.readInt32(); - int temp = msg.readInt32(); + const int temp = msg.readInt32(); character->data.mStats[JOB].base = temp; character->data.mStats[JOB].mod = temp; - tempPlayer->setSprite(SPRITE_SHOE, msg.readInt16()); - tempPlayer->setSprite(SPRITE_GLOVES, msg.readInt16()); - tempPlayer->setSprite(SPRITE_CAPE, msg.readInt16()); - tempPlayer->setSprite(SPRITE_MISC1, msg.readInt16()); + const int shoe = msg.readInt16(); + const int gloves = msg.readInt16(); + const int cape = msg.readInt16(); + const int misc1 = msg.readInt16(); msg.readInt32(); // option msg.readInt32(); // karma @@ -240,8 +238,15 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character const uint16_t race = msg.readInt16(); // class (used for race) int hairStyle = msg.readInt8(); msg.readInt8(); // look - tempPlayer->setSubtype(race); - Uint16 weapon = msg.readInt16(); + const uint16_t weapon = msg.readInt16(); + + auto *tempPlayer = new LocalPlayer(id, race); + tempPlayer->setGender(token.sex); + + tempPlayer->setSprite(SPRITE_SHOE, shoe); + tempPlayer->setSprite(SPRITE_GLOVES, gloves); + tempPlayer->setSprite(SPRITE_CAPE, cape); + tempPlayer->setSprite(SPRITE_MISC1, misc1); tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", true); character->data.mAttributes[LEVEL] = msg.readInt16(); diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h index 646a545e..b0d3e970 100644 --- a/src/net/tmwa/charserverhandler.h +++ b/src/net/tmwa/charserverhandler.h @@ -74,7 +74,7 @@ class CharServerHandler final : public MessageHandler, public Net::CharHandler void connect(); private: - void readPlayerData(MessageIn &msg, Net::Character *character); + static void readPlayerData(MessageIn &msg, Net::Character *character); }; } // namespace TmwAthena |