summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--src/actorsprite.h3
-rw-r--r--src/being.cpp134
-rw-r--r--src/being.h22
-rw-r--r--src/compoundsprite.cpp15
-rw-r--r--src/compoundsprite.h3
-rw-r--r--src/localplayer.h3
-rw-r--r--src/net/tmwa/adminhandler.cpp3
-rw-r--r--src/net/tmwa/beinghandler.cpp58
-rw-r--r--src/net/tmwa/charserverhandler.cpp25
-rw-r--r--src/net/tmwa/charserverhandler.h2
11 files changed, 160 insertions, 111 deletions
diff --git a/NEWS b/NEWS
index ac74291c..ed8d1c7f 100644
--- a/NEWS
+++ b/NEWS
@@ -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