From 11a6f342e579c26320334b9ae9735701386e3b25 Mon Sep 17 00:00:00 2001 From: Jared Adams Date: Tue, 13 Apr 2010 22:48:28 -0600 Subject: Add race support for eAthena The job/class field is used to select the race. If the given race isn't defined, it falls back on the first race (so servers can use jobs/classes without races). Also rename job to subtype for Being and subclasses, and begin support for changing monster and NPC subtypes on the fly (particle effects still need to be reset when they change). Reviewed-by: Bertram --- src/being.cpp | 4 +-- src/being.h | 14 ++++---- src/localplayer.cpp | 4 +-- src/localplayer.h | 2 +- src/monster.cpp | 69 +++++++++++++++++++++++----------------- src/monster.h | 4 ++- src/net/ea/beinghandler.cpp | 4 +-- src/net/ea/charserverhandler.cpp | 2 +- src/npc.cpp | 40 +++++++++++++---------- src/npc.h | 4 ++- src/player.cpp | 21 ++++++++---- src/player.h | 4 ++- src/resources/itemdb.cpp | 11 +++++++ src/resources/itemdb.h | 2 ++ src/resources/iteminfo.h | 28 ++++++++-------- 15 files changed, 129 insertions(+), 84 deletions(-) diff --git a/src/being.cpp b/src/being.cpp index 44293732..47c31c36 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -67,14 +67,14 @@ static const int DEFAULT_BEING_HEIGHT = 32; int Being::mNumberOfHairstyles = 1; // TODO: mWalkTime used by eAthena only -Being::Being(int id, int job, Map *map): +Being::Being(int id, int subtype, Map *map): mFrame(0), mWalkTime(0), mEmotion(0), mEmotionTime(0), mSpeechTime(0), mAttackSpeed(350), mAction(STAND), - mJob(job), + mSubType(subtype), mId(id), mDirection(DOWN), mSpriteDirection(DIRECTION_DOWN), diff --git a/src/being.h b/src/being.h index 3479a9a8..3d3dfa71 100644 --- a/src/being.h +++ b/src/being.h @@ -118,11 +118,11 @@ class Being : public Sprite, public ConfigListener /** * Constructor. * - * @param id a unique being id - * @param job partly determines the type of the being - * @param map the map the being is on + * @param id a unique being id + * @param subtype partly determines the type of the being + * @param map the map the being is on */ - Being(int id, int job, Map *map); + Being(int id, int subtype, Map *map); virtual ~Being(); @@ -284,12 +284,12 @@ class Being : public Sprite, public ConfigListener /** * Return Being's current Job (player job, npc, monster, creature ) */ - Uint16 getJob() const { return mJob; } + Uint16 getSubType() const { return mSubType; } /** * Set Being's current Job (player job, npc, monster, creature ) */ - void setJob(Uint16 job) { mJob = job; } + virtual void setSubtype(Uint16 subtype) { mSubType = subtype; } /** * Sets the walk speed. @@ -611,7 +611,7 @@ class Being : public Sprite, public ConfigListener int mAttackSpeed; /**< Attack speed */ Action mAction; /**< Action the being is performing */ - Uint16 mJob; /**< Job (player job, npc, monster, creature ) */ + Uint16 mSubType; /**< Subtype (graphical view, basically) */ int mId; /**< Unique sprite id */ Uint8 mDirection; /**< Facing direction */ diff --git a/src/localplayer.cpp b/src/localplayer.cpp index ef2d7e4e..961248dc 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -75,8 +75,8 @@ const short walkingKeyboardDelay = 1000; LocalPlayer *player_node = NULL; -LocalPlayer::LocalPlayer(int id, int job): - Player(id, job, 0), +LocalPlayer::LocalPlayer(int id, int subtype): + Player(id, subtype, 0), mEquipment(new Equipment), mAttackRange(0), mTargetTime(-1), diff --git a/src/localplayer.h b/src/localplayer.h index 65653d50..dd7b94d4 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -105,7 +105,7 @@ class LocalPlayer : public Player /** * Constructor. */ - LocalPlayer(int id= 65535, int job = 0); + LocalPlayer(int id= 65535, int subtype = 0); /** * Destructor. diff --git a/src/monster.cpp b/src/monster.cpp index 23be9395..0876b08d 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -35,37 +35,11 @@ #include "resources/monsterdb.h" #include "resources/monsterinfo.h" -Monster::Monster(int id, int job, Map *map): - Being(id, job, map), +Monster::Monster(int id, int subtype, Map *map): + Being(id, subtype, map), mAttackType(1) { - const MonsterInfo &info = getInfo(); - - // Setup Monster sprites - const std::list &sprites = info.getSprites(); - - for (std::list::const_iterator i = sprites.begin(); - i != sprites.end(); i++) - { - std::string file = "graphics/sprites/" + *i; - mSprites.push_back(AnimatedSprite::load(file)); - } - - // Ensure that something is shown - if (mSprites.size() == 0) - { - mSprites.push_back(AnimatedSprite::load("graphics/sprites/error.xml")); - } - - if (Particle::enabled) - { - const std::list &particleEffects = info.getParticleEffects(); - for (std::list::const_iterator i = particleEffects.begin(); - i != particleEffects.end(); i++) - { - controlParticle(particleEngine->addEffect((*i), 0, 0)); - } - } + setSubtype(subtype); mNameColor = &userPalette->getColor(UserPalette::MONSTER); mTextColor = &userPalette->getColor(UserPalette::MONSTER); @@ -86,6 +60,7 @@ void Monster::logic() Being::logic(); } + void Monster::setAction(Action action, int attackType) { SpriteAction currentAction = ACTION_INVALID; @@ -144,6 +119,40 @@ void Monster::setAction(Action action, int attackType) } } +void Monster::setSubtype(Uint16 subtype) +{ + Being::setSubtype(subtype); + + const MonsterInfo &info = getInfo(); + + // Setup Monster sprites + const std::list &sprites = info.getSprites(); + + mSprites.clear(); + for (std::list::const_iterator i = sprites.begin(); + i != sprites.end(); i++) + { + std::string file = "graphics/sprites/" + *i; + mSprites.push_back(AnimatedSprite::load(file)); + } + + // Ensure that something is shown + if (mSprites.size() == 0) + { + mSprites.push_back(AnimatedSprite::load("graphics/sprites/error.xml")); + } + + if (Particle::enabled) + { + const std::list &particleEffects = info.getParticleEffects(); + for (std::list::const_iterator i = particleEffects.begin(); + i != particleEffects.end(); i++) + { + controlParticle(particleEngine->addEffect((*i), 0, 0)); + } + } +} + void Monster::handleAttack(Being *victim, int damage, AttackType type) { Being::handleAttack(victim, damage, type); @@ -170,7 +179,7 @@ Being::TargetCursorSize Monster::getTargetCursorSize() const const MonsterInfo &Monster::getInfo() const { - return MonsterDB::get(mJob); + return MonsterDB::get(mSubType); } void Monster::updateCoords() diff --git a/src/monster.h b/src/monster.h index 6fb82c7f..9bb8e3b9 100644 --- a/src/monster.h +++ b/src/monster.h @@ -30,7 +30,7 @@ class Text; class Monster : public Being { public: - Monster(int id, int job, Map *map); + Monster(int id, int subtype, Map *map); virtual void logic(); @@ -38,6 +38,8 @@ class Monster : public Being virtual Type getType() const { return MONSTER; } + virtual void setSubtype(Uint16 subtype); + virtual TargetCursorSize getTargetCursorSize() const; diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 6dcf9631..649e93e8 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -167,7 +167,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) if (speed == 0) { speed = 150; } dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setJob(job); + dstBeing->setSubtype(job); hairStyle = msg.readInt16(); weapon = msg.readInt16(); headBottom = msg.readInt16(); @@ -550,7 +550,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) } dstBeing->setWalkSpeed(Vector(speed, speed, 0)); - dstBeing->setJob(job); + dstBeing->setSubtype(job); hairStyle = msg.readInt16(); weapon = msg.readInt16(); shield = msg.readInt16(); diff --git a/src/net/ea/charserverhandler.cpp b/src/net/ea/charserverhandler.cpp index 64e1d427..8706727a 100644 --- a/src/net/ea/charserverhandler.cpp +++ b/src/net/ea/charserverhandler.cpp @@ -220,7 +220,7 @@ LocalPlayer *CharServerHandler::readPlayerData(Net::MessageIn &msg, int *slot) tempPlayer->setMP(msg.readInt16()); tempPlayer->setMaxMP(msg.readInt16()); msg.readInt16(); // speed - msg.readInt16(); // class + tempPlayer->setSubtype(msg.readInt16()); // class (used for race) int hairStyle = msg.readInt16(); Uint16 weapon = msg.readInt16(); tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", true); diff --git a/src/npc.cpp b/src/npc.cpp index 29a6c61c..cdfe5193 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -37,11 +37,32 @@ #include "resources/npcdb.h" -NPC::NPC(int id, int job, Map *map): - Player(id, job, map, true) +NPC::NPC(int id, int subtype, Map *map): + Player(id, subtype, map, true) { - NPCInfo info = NPCDB::get(job); + setSubtype(subtype); + setShowName(true); +} + +void NPC::setName(const std::string &name) +{ + const std::string displayName = name.substr(0, name.find('#', 0)); + + Being::setName(displayName); + + mNameColor = &userPalette->getColor(UserPalette::NPC); + + mDispName->setColor(mNameColor); +} + +void NPC::setSubtype(Uint16 subtype) +{ + Being::setSubtype(subtype); + + NPCInfo info = NPCDB::get(subtype); + + mSprites.clear(); // Setup NPC sprites for (std::list::const_iterator i = info.sprites.begin(); i != info.sprites.end(); @@ -65,19 +86,6 @@ NPC::NPC(int id, int job, Map *map): this->controlParticle(p); } } - - setShowName(true); -} - -void NPC::setName(const std::string &name) -{ - const std::string displayName = name.substr(0, name.find('#', 0)); - - Being::setName(displayName); - - mNameColor = &userPalette->getColor(UserPalette::NPC); - - mDispName->setColor(mNameColor); } void NPC::talk() diff --git a/src/npc.h b/src/npc.h index c7db58f1..0abd2395 100644 --- a/src/npc.h +++ b/src/npc.h @@ -30,12 +30,14 @@ class Text; class NPC : public Player { public: - NPC(int id, int job, Map *map); + NPC(int id, int subtype, Map *map); void setName(const std::string &name); virtual Type getType() const { return Being::NPC; } + virtual void setSubtype(Uint16 subtype); + void talk(); void setSprite(unsigned int slot, int id, diff --git a/src/player.cpp b/src/player.cpp index ad8c1db4..9113b4ab 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -43,8 +43,8 @@ #include "utils/stringutils.h" -Player::Player(int id, int job, Map *map, bool isNPC): - Being(id, job, map), +Player::Player(int id, int subtype, Map *map, bool isNPC): + Being(id, subtype, map), mGender(GENDER_UNSPECIFIED), mParty(NULL), mIsGM(false) @@ -58,11 +58,7 @@ Player::Player(int id, int job, Map *map, bool isNPC): mSpriteColors.push_back(""); } - /* Human base sprite. When implementing different races remove this - * line and set the base sprite when setting the race of the player - * character. - */ - setSprite(Net::getCharHandler()->baseSprite(), -100); + setSubtype(subtype); } mShowName = config.getValue("visiblenames", 1); config.addListener("visiblenames", this); @@ -137,6 +133,17 @@ void Player::logic() Being::logic(); } +void Player::setSubtype(Uint16 subtype) +{ + Being::setSubtype(subtype); + + int id = -100 - subtype; + if (ItemDB::exists(id)) // Prevent showing errors when sprite doesn't exist + setSprite(Net::getCharHandler()->baseSprite(), id); + else + setSprite(Net::getCharHandler()->baseSprite(), -100); +} + void Player::setGender(Gender gender) { if (gender != mGender) diff --git a/src/player.h b/src/player.h index 670f6d84..e75870a0 100644 --- a/src/player.h +++ b/src/player.h @@ -47,7 +47,7 @@ class Player : public Being /** * Constructor. */ - Player(int id, int job, Map *map, bool isNPC = false); + Player(int id, int subtype, Map *map, bool isNPC = false); ~Player(); @@ -55,6 +55,8 @@ class Player : public Being virtual Type getType() const { return PLAYER; } + virtual void setSubtype(Uint16 subtype); + /** * Sets the gender of this being. */ diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index d60e38da..4638f00e 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -78,6 +78,8 @@ static ItemType itemTypeFromString(const std::string &name, int id = 0) else if (name=="equip-necklace") return ITEM_EQUIPMENT_NECKLACE; else if (name=="equip-feet") return ITEM_EQUIPMENT_FEET; else if (name=="equip-ammo") return ITEM_EQUIPMENT_AMMO; + else if (name=="racesprite") return ITEM_SPRITE_RACE; + else if (name=="hairsprite") return ITEM_SPRITE_HAIR; else return ITEM_UNUSABLE; } @@ -255,6 +257,15 @@ void ItemDB::unload() mLoaded = false; } +bool ItemDB::exists(int id) +{ + assert(mLoaded); + + ItemInfos::const_iterator i = mItemInfos.find(id); + + return i != mItemInfos.end(); +} + const ItemInfo &ItemDB::get(int id) { assert(mLoaded); diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h index 63c016ba..be023073 100644 --- a/src/resources/itemdb.h +++ b/src/resources/itemdb.h @@ -43,6 +43,8 @@ namespace ItemDB */ void unload(); + bool exists(int id); + const ItemInfo &get(int id); const ItemInfo &get(const std::string &name); diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index e14d62b8..a7c0ddca 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -72,19 +72,21 @@ enum EquipmentSlot enum ItemType { ITEM_UNUSABLE = 0, - ITEM_USABLE, // 1 - ITEM_EQUIPMENT_ONE_HAND_WEAPON, // 2 - ITEM_EQUIPMENT_TWO_HANDS_WEAPON,// 3 - ITEM_EQUIPMENT_TORSO,// 4 - ITEM_EQUIPMENT_ARMS,// 5 - ITEM_EQUIPMENT_HEAD,// 6 - ITEM_EQUIPMENT_LEGS,// 7 - ITEM_EQUIPMENT_SHIELD,// 8 - ITEM_EQUIPMENT_RING,// 9 - ITEM_EQUIPMENT_NECKLACE,// 10 - ITEM_EQUIPMENT_FEET,// 11 - ITEM_EQUIPMENT_AMMO,// 12 - ITEM_EQUIPMENT_CHARM// 13 + ITEM_USABLE, + ITEM_EQUIPMENT_ONE_HAND_WEAPON, + ITEM_EQUIPMENT_TWO_HANDS_WEAPON, + ITEM_EQUIPMENT_TORSO, + ITEM_EQUIPMENT_ARMS, // 5 + ITEM_EQUIPMENT_HEAD, + ITEM_EQUIPMENT_LEGS, + ITEM_EQUIPMENT_SHIELD, + ITEM_EQUIPMENT_RING, + ITEM_EQUIPMENT_NECKLACE, // 10 + ITEM_EQUIPMENT_FEET, + ITEM_EQUIPMENT_AMMO, + ITEM_EQUIPMENT_CHARM, + ITEM_SPRITE_RACE, + ITEM_SPRITE_HAIR // 15 }; /** -- cgit v1.2.3-70-g09d2