summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
authorJared Adams <jaxad0127@gmail.com>2010-05-17 19:16:11 -0600
committerJared Adams <jaxad0127@gmail.com>2010-05-17 21:49:16 -0600
commit3b4e7795c9ab3036988a1a7dfe6f5ed2ad12199d (patch)
treeed0e8cc5b608c31ac79e6d1de03d9ddfc74fa339 /src/being.cpp
parent51945aa1af6f2a6dc15027ef40cf0ccf424d46e1 (diff)
downloadmana-client-3b4e7795c9ab3036988a1a7dfe6f5ed2ad12199d.tar.gz
mana-client-3b4e7795c9ab3036988a1a7dfe6f5ed2ad12199d.tar.bz2
mana-client-3b4e7795c9ab3036988a1a7dfe6f5ed2ad12199d.tar.xz
mana-client-3b4e7795c9ab3036988a1a7dfe6f5ed2ad12199d.zip
Remove Monster, Player, and NPC classes
Instead of having these three subclasses with minor differences, this commit merges them back into Being. In the future, we can make Beings that are talkable to some, attackable by others, etc. This also puts back support for monster equipment. Also changes remaining references to Being::Type and the constants to refer to ActorSprite::Type. Reviewed-by: Freeyorp
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp507
1 files changed, 469 insertions, 38 deletions
diff --git a/src/being.cpp b/src/being.cpp
index 2ce34f94..a2180c84 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -26,31 +26,44 @@
#include "configuration.h"
#include "effectmanager.h"
#include "graphics.h"
+#include "guild.h"
#include "localplayer.h"
#include "log.h"
#include "map.h"
#include "particle.h"
+#include "party.h"
#include "simpleanimation.h"
#include "sound.h"
#include "sprite.h"
#include "text.h"
#include "statuseffect.h"
+#include "gui/buy.h"
+#include "gui/buysell.h"
#include "gui/gui.h"
+#include "gui/npcdialog.h"
+#include "gui/npcpostdialog.h"
+#include "gui/sell.h"
+#include "gui/socialwindow.h"
#include "gui/speechbubble.h"
#include "gui/theme.h"
#include "gui/userpalette.h"
+#include "net/charhandler.h"
+#include "net/net.h"
+#include "net/npchandler.h"
+#include "net/playerhandler.h"
+
+#include "resources/beinginfo.h"
#include "resources/colordb.h"
#include "resources/emotedb.h"
#include "resources/image.h"
#include "resources/itemdb.h"
#include "resources/iteminfo.h"
+#include "resources/monsterdb.h"
+#include "resources/npcdb.h"
#include "resources/resourcemanager.h"
-#include "net/net.h"
-#include "net/playerhandler.h"
-
#include "utils/dtor.h"
#include "utils/stringutils.h"
#include "utils/xml.h"
@@ -62,36 +75,49 @@
static const int DEFAULT_BEING_WIDTH = 32;
static const int DEFAULT_BEING_HEIGHT = 32;
-
-
int Being::mNumberOfHairstyles = 1;
// TODO: mWalkTime used by eAthena only
-Being::Being(int id, int subtype, Map *map):
+Being::Being(int id, Type type, int subtype, Map *map):
ActorSprite(id),
+ mInfo(BeingInfo::Unknown),
mFrame(0),
mWalkTime(0),
mEmotion(0), mEmotionTime(0),
mSpeechTime(0),
+ mAttackType(1),
mAttackSpeed(350),
mAction(STAND),
- mSubType(subtype),
+ mSubType(0xFFFF),
mDirection(DOWN),
mSpriteDirection(DIRECTION_DOWN),
mDispName(0),
mShowName(false),
mEquippedWeapon(NULL),
mText(0),
+ mGender(GENDER_UNSPECIFIED),
+ mParty(NULL),
+ mIsGM(false),
+ mType(type),
mX(0), mY(0),
mDamageTaken(0)
{
setMap(map);
+ setSubtype(subtype);
mSpeechBubble = new SpeechBubble;
- mNameColor = &userPalette->getColor(UserPalette::NPC);
- mTextColor = &Theme::getThemeColor(Theme::CHAT);
mWalkSpeed = Net::getPlayerHandler()->getDefaultWalkSpeed();
+
+ if (getType() == PLAYER)
+ mShowName = config.getValue("visiblenames", 1);
+
+ config.addListener("visiblenames", this);
+
+ if (getType() == PLAYER || getType() == NPC)
+ setShowName(true);
+
+ updateColors();
}
Being::~Being()
@@ -99,6 +125,53 @@ Being::~Being()
delete mSpeechBubble;
delete mDispName;
delete mText;
+
+ config.removeListener("visiblenames", this);
+}
+
+void Being::setSubtype(Uint16 subtype)
+{
+ if (subtype == mSubType)
+ return;
+
+ mSubType = subtype;
+
+ if (getType() == MONSTER)
+ {
+ mInfo = MonsterDB::get(mSubType);
+ setName(mInfo->getName());
+ setupSpriteDisplay(mInfo->getDisplay());
+ }
+ else if (getType() == NPC)
+ {
+ mInfo = NPCDB::get(mSubType);
+ setupSpriteDisplay(mInfo->getDisplay(), false);
+ }
+ else if (getType() == PLAYER)
+ {
+ int id = -100 - subtype;
+
+ // Prevent showing errors when sprite doesn't exist
+ if (!ItemDB::exists(id))
+ id = -100;
+
+ setSprite(Net::getCharHandler()->baseSprite(), id);
+ }
+}
+
+ActorSprite::TargetCursorSize Being::getTargetCursorSize() const
+{
+ return mInfo->getTargetCursorSize();
+}
+
+unsigned char Being::getWalkMask() const
+{
+ return mInfo->getWalkMask();
+}
+
+Map::BlockType Being::getBlockType() const
+{
+ return mInfo->getBlockType();
}
void Being::setPosition(const Vector &pos)
@@ -277,6 +350,8 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type)
if (amount > 0)
{
+ sound.playSfx(mInfo->getSound(SOUND_EVENT_HURT));
+
if (getType() == MONSTER)
{
mDamageTaken += amount;
@@ -302,35 +377,48 @@ void Being::handleAttack(Being *victim, int damage, AttackType type)
fireMissile(victim, mEquippedWeapon->getMissileParticle());
}
}
+ else
+ fireMissile(victim, mInfo->getAttack(mAttackType)->missileParticle);
+
if (Net::getNetworkType() == ServerInfo::TMWATHENA)
{
mFrame = 0;
mWalkTime = tick_time;
}
+
+ sound.playSfx(mInfo->getSound((damage > 0) ?
+ SOUND_EVENT_HIT : SOUND_EVENT_MISS));
}
void Being::setName(const std::string &name)
{
- mName = name;
-
- if (getShowName())
+ if (getType() == NPC)
+ {
+ mName = name.substr(0, name.find('#', 0));
showName();
+ }
+ else
+ {
+ mName = name;
+
+ if (getType() == PLAYER && getShowName())
+ showName();
+ }
}
void Being::setShowName(bool doShowName)
{
- bool oldShow = mShowName;
+ if (mShowName == doShowName)
+ return;
+
mShowName = doShowName;
- if (doShowName != oldShow)
+ if (doShowName)
+ showName();
+ else
{
- if (doShowName)
- showName();
- else
- {
- delete mDispName;
- mDispName = 0;
- }
+ delete mDispName;
+ mDispName = 0;
}
}
@@ -345,6 +433,101 @@ void Being::setGuildPos(const std::string &pos)
logger->log("Got guild position \"%s\" for being %s(%i)", pos.c_str(), mName.c_str(), mId);
}
+void Being::addGuild(Guild *guild)
+{
+ mGuilds[guild->getId()] = guild;
+ guild->addMember(mId, mName);
+
+ if (this == player_node && socialWindow)
+ {
+ socialWindow->addTab(guild);
+ }
+}
+
+void Being::removeGuild(int id)
+{
+ if (this == player_node && socialWindow)
+ {
+ socialWindow->removeTab(mGuilds[id]);
+ }
+
+ mGuilds[id]->removeMember(mId);
+ mGuilds.erase(id);
+}
+
+Guild *Being::getGuild(const std::string &guildName) const
+{
+ std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end();
+ for (itr = mGuilds.begin(); itr != itr_end; ++itr)
+ {
+ Guild *guild = itr->second;
+ if (guild->getName() == guildName)
+ {
+ return guild;
+ }
+ }
+
+ return NULL;
+}
+
+Guild *Being::getGuild(int id) const
+{
+ std::map<int, Guild*>::const_iterator itr;
+ itr = mGuilds.find(id);
+ if (itr != mGuilds.end())
+ {
+ return itr->second;
+ }
+
+ return NULL;
+}
+
+void Being::clearGuilds()
+{
+ std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end();
+ for (itr = mGuilds.begin(); itr != itr_end; ++itr)
+ {
+ Guild *guild = itr->second;
+
+ if (this == player_node && socialWindow)
+ socialWindow->removeTab(guild);
+
+ guild->removeMember(mId);
+ }
+
+ mGuilds.clear();
+}
+
+void Being::setParty(Party *party)
+{
+ if (party == mParty)
+ return;
+
+ Party *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::fireMissile(Being *victim, const std::string &particle)
{
if (!victim || particle.empty())
@@ -379,11 +562,37 @@ void Being::setAction(Action action, int attackType)
break;
case ATTACK:
if (mEquippedWeapon)
+ {
currentAction = mEquippedWeapon->getAttackType();
+ reset();
+ }
else
- currentAction = ACTION_ATTACK;
+ {
+ mAttackType = attackType;
+ currentAction = mInfo->getAttack(attackType)->action;
+ reset();
+
+ int rotation = 0;
+ //attack particle effect
+ std::string particleEffect = mInfo->getAttack(attackType)
+ ->particleEffect;
+ if (!particleEffect.empty() && Particle::enabled)
+ {
+ switch (mSpriteDirection)
+ {
+ case DIRECTION_DOWN: rotation = 0; break;
+ case DIRECTION_LEFT: rotation = 90; break;
+ case DIRECTION_UP: rotation = 180; break;
+ case DIRECTION_RIGHT: rotation = 270; break;
+ default: break;
+ }
+ Particle *p;
+ p = particleEngine->addEffect(particleEffect, 0, 0,
+ rotation);
+ controlParticle(p);
+ }
+ }
- reset();
break;
case HURT:
//currentAction = ACTION_HURT; // Buggy: makes the player stop
@@ -392,6 +601,7 @@ void Being::setAction(Action action, int attackType)
break;
case DEAD:
currentAction = ACTION_DEAD;
+ sound.playSfx(mInfo->getSound(SOUND_EVENT_DIE));
break;
case STAND:
currentAction = ACTION_STAND;
@@ -564,6 +774,70 @@ void Being::logic()
}
else if (Net::getNetworkType() == ServerInfo::TMWATHENA)
{
+ if (getType() == MONSTER && (mAction != STAND))
+ {
+ mFrame = (int) ((get_elapsed_time(mWalkTime) * 4) / getWalkSpeed().x);
+
+ if (mFrame >= 4 && mAction != DEAD)
+ nextTile();
+ }
+ else if (getType() == PLAYER)
+ {
+ switch (mAction)
+ {
+ case STAND:
+ case SIT:
+ case DEAD:
+ case HURT:
+ break;
+
+ case WALK:
+ mFrame = (int) ((get_elapsed_time(mWalkTime) * 6)
+ / getWalkSpeed().x);
+ if (mFrame >= 6)
+ nextTile();
+ break;
+
+ case ATTACK:
+ int rotation = 0;
+ std::string particleEffect = "";
+ int frames = 4;
+
+ if (mEquippedWeapon &&
+ mEquippedWeapon->getAttackType() == ACTION_ATTACK_BOW)
+ {
+ frames = 5;
+ }
+
+ mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed;
+
+ //attack particle effect
+ if (mEquippedWeapon)
+ particleEffect = mEquippedWeapon->getParticleEffect();
+
+ if (!particleEffect.empty() && Particle::enabled && mFrame == 1)
+ {
+ switch (mDirection)
+ {
+ case DOWN: rotation = 0; break;
+ case LEFT: rotation = 90; break;
+ case UP: rotation = 180; break;
+ case RIGHT: rotation = 270; break;
+ default: break;
+ }
+ Particle *p;
+ p = particleEngine->addEffect("graphics/particles/" +
+ particleEffect, 0, 0, rotation);
+ controlParticle(p);
+ }
+
+ if (mFrame >= frames)
+ nextTile();
+
+ break;
+ }
+ }
+
// Update pixel coordinates
setPosition(mX * 32 + 16 + getXOffset(),
mY * 32 + 32 + getYOffset());
@@ -688,9 +962,22 @@ int Being::getHeight() const
void Being::updateCoords()
{
- if (mDispName)
- {
+ if (!mDispName)
+ return;
+
+ // Monster names show above the sprite instead of below it
+ if (getType() == MONSTER)
+ mDispName->adviseXY(getPixelX(),
+ getPixelY() - getHeight() - mDispName->getHeight());
+ else
mDispName->adviseXY(getPixelX(), getPixelY());
+}
+
+void Being::optionChanged(const std::string &value)
+{
+ if (getType() == PLAYER && value == "visiblenames")
+ {
+ setShowName(config.getValue("visiblenames", 1));
}
}
@@ -706,21 +993,15 @@ void Being::showName()
mDispName = 0;
std::string mDisplayName(mName);
- if (getType() == PLAYER)
+ if (config.getValue("showgender", false))
{
- if (config.getValue("showgender", false))
- {
- Player* player = static_cast<Player*>(this);
- if (player)
- {
- if (player->getGender() == GENDER_FEMALE)
- mDisplayName += " \u2640";
- else
- mDisplayName += " \u2642";
- }
- }
+ if (getGender() == GENDER_FEMALE)
+ mDisplayName += " \u2640";
+ else if (getGender() == GENDER_MALE)
+ mDisplayName += " \u2642";
}
- else if (getType() == MONSTER)
+
+ if (getType() == MONSTER)
{
if (config.getValue("showMonstersTakedDamage", false))
{
@@ -730,6 +1011,115 @@ void Being::showName()
mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(),
gcn::Graphics::CENTER, mNameColor);
+
+ updateCoords();
+}
+
+void Being::updateColors()
+{
+ if (getType() == MONSTER)
+ {
+ mNameColor = &userPalette->getColor(UserPalette::MONSTER);
+ mTextColor = &userPalette->getColor(UserPalette::MONSTER);
+ }
+ else if (getType() == 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 = &userPalette->getColor(Theme::PLAYER);
+
+ if (mIsGM)
+ {
+ mTextColor = &userPalette->getColor(UserPalette::GM);
+ mNameColor = &userPalette->getColor(UserPalette::GM);
+ }
+ else if (mParty && mParty == player_node->getParty())
+ {
+ mNameColor = &userPalette->getColor(UserPalette::PARTY);
+ }
+ else
+ {
+ mNameColor = &userPalette->getColor(UserPalette::PC);
+ }
+ }
+
+ if (mDispName)
+ {
+ mDispName->setColor(mNameColor);
+ }
+}
+
+void Being::setSprite(unsigned int slot, int id, const std::string &color,
+ bool isWeapon)
+{
+ assert(slot < Net::getCharHandler()->maxSprite());
+
+ if (slot >= size())
+ resize(slot + 1, NULL);
+
+ if (slot >= mSpriteIDs.size())
+ mSpriteIDs.resize(slot + 1, 0);
+
+ if (slot >= mSpriteColors.size())
+ mSpriteColors.resize(slot + 1, "");
+
+ // id = 0 means unequip
+ if (id == 0)
+ {
+ delete at(slot);
+ at(slot) = NULL;
+
+ if (isWeapon)
+ mEquippedWeapon = NULL;
+ }
+ else
+ {
+ std::string filename = ItemDB::get(id).getSprite(mGender);
+ AnimatedSprite *equipmentSprite = NULL;
+
+ if (!filename.empty())
+ {
+ if (!color.empty())
+ filename += "|" + color;
+
+ equipmentSprite = AnimatedSprite::load("graphics/sprites/" +
+ filename);
+ }
+
+ if (equipmentSprite)
+ equipmentSprite->setDirection(getSpriteDirection());
+
+ if (at(slot))
+ delete at(slot);
+
+ at(slot) = equipmentSprite;
+
+ if (isWeapon)
+ mEquippedWeapon = &ItemDB::get(id);
+
+ setAction(mAction);
+ }
+
+ mSpriteIDs[slot] = id;
+ mSpriteColors[slot] = color;
+}
+
+void Being::setSpriteID(unsigned int slot, int id)
+{
+ setSprite(slot, id, mSpriteColors[slot]);
+}
+
+void Being::setSpriteColor(unsigned int slot, const std::string &color)
+{
+ setSprite(slot, mSpriteIDs[slot], color);
}
int Being::getNumberOfLayers() const
@@ -754,3 +1144,44 @@ void Being::updateName()
if (mShowName)
showName();
}
+
+void Being::setGender(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(bool gm)
+{
+ mIsGM = gm;
+
+ updateColors();
+}
+
+bool Being::canTalk()
+{
+ return mType == NPC;
+}
+
+void Being::talkTo()
+{
+ Net::getNpcHandler()->talk(mId);
+}
+
+bool Being::isTalking()
+{
+ return NpcDialog::isActive() || BuyDialog::isActive() ||
+ SellDialog::isActive() || BuySellDialog::isActive() ||
+ NpcPostDialog::isActive();
+}