diff options
Diffstat (limited to 'src/being.cpp')
-rw-r--r-- | src/being.cpp | 866 |
1 files changed, 526 insertions, 340 deletions
diff --git a/src/being.cpp b/src/being.cpp index d2dfc855..1b95b17d 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -21,104 +21,163 @@ #include "being.h" +#include "actorspritemanager.h" #include "animatedsprite.h" #include "client.h" #include "configuration.h" #include "effectmanager.h" +#include "event.h" #include "graphics.h" +#include "guild.h" #include "localplayer.h" #include "log.h" #include "map.h" #include "particle.h" +#include "party.h" +#include "playerrelations.h" #include "simpleanimation.h" #include "sound.h" +#include "sprite.h" #include "text.h" #include "statuseffect.h" #include "gui/gui.h" +#include "gui/socialwindow.h" #include "gui/speechbubble.h" -#include "gui/theme.h" -#include "gui/userpalette.h" +#include "net/charhandler.h" +#include "net/gamehandler.h" +#include "net/net.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 "resources/theme.h" +#include "resources/userpalette.h" #include "utils/dtor.h" #include "utils/stringutils.h" #include "utils/xml.h" -#include "net/net.h" -#include "net/playerhandler.h" #include <cassert> #include <cmath> -#define BEING_EFFECTS_FILE "effects.xml" #define HAIR_FILE "hair.xml" 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): - mFrame(0), - mWalkTime(0), +Being::Being(int id, Type type, int subtype, Map *map): + ActorSprite(id), + mInfo(BeingInfo::Unknown), + mActionTime(0), mEmotion(0), mEmotionTime(0), mSpeechTime(0), + mAttackType(1), mAttackSpeed(350), mAction(STAND), - mSubType(subtype), - mId(id), + mSubType(0xFFFF), mDirection(DOWN), mSpriteDirection(DIRECTION_DOWN), - mMap(NULL), mDispName(0), mShowName(false), mEquippedWeapon(NULL), mText(0), - mStunMode(0), - mAlpha(1.0f), - mStatusParticleEffects(&mStunParticleEffects, false), - mChildParticleEffects(&mStatusParticleEffects, false), - mMustResetParticles(false), + mGender(GENDER_UNSPECIFIED), + mParty(NULL), + mIsGM(false), + mType(type), mX(0), mY(0), - mDamageTaken(0), - mUsedTargetCursor(NULL) + 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.getBoolValue("visiblenames"); + + config.addListener("visiblenames", this); + + if (getType() == PLAYER || getType() == NPC) + setShowName(true); + + updateColors(); + listen("Chat"); } Being::~Being() { - mUsedTargetCursor = NULL; - delete_all(mSprites); - - if (player_node && player_node->getTarget() == this) - player_node->setTarget(NULL); - - setMap(NULL); + config.removeListener("visiblenames", this); delete mSpeechBubble; delete mDispName; delete mText; + mSpeechBubble = 0; + mDispName = 0; + mText = 0; +} + +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) { - mPos = pos; + Actor::setPosition(pos); updateCoords(); @@ -152,8 +211,9 @@ void Being::setDestination(int dstX, int dstY) Position dest = mMap->checkNodeOffsets(getCollisionRadius(), getWalkMask(), dstX, dstY); - Path thisPath = mMap->findPixelPath(mPos.x, mPos.y, dest.x, dest.y, - getCollisionRadius(), getWalkMask()); + Path thisPath = mMap->findPixelPath((int) mPos.x, (int) mPos.y, + dest.x, dest.y, + getCollisionRadius(), getWalkMask()); if (thisPath.empty()) { @@ -186,10 +246,10 @@ void Being::setPath(const Path &path) mPath = path; if ((Net::getNetworkType() == ServerInfo::TMWATHENA) && - mAction != WALK && mAction != DEAD) + mAction != MOVE && mAction != DEAD) { nextTile(); - mWalkTime = tick_time; + mActionTime = tick_time; } } @@ -235,7 +295,7 @@ void Being::setSpeech(const std::string &text, int time) if (!mSpeech.empty()) mSpeechTime = time <= SPEECH_MAX_TIME ? time : SPEECH_MAX_TIME; - const int speech = (int) config.getValue("speech", TEXT_OVERHEAD); + const int speech = config.getIntValue("speech"); if (speech == TEXT_OVERHEAD) { if (mText) @@ -292,6 +352,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; @@ -310,42 +372,50 @@ void Being::handleAttack(Being *victim, int damage, AttackType type) if (this != player_node) setAction(Being::ATTACK, 1); - if (getType() == PLAYER && victim) - { - if (mEquippedWeapon) - { - fireMissile(victim, mEquippedWeapon->getMissileParticle()); - } - } + if (getType() == PLAYER && victim && mEquippedWeapon) + fireMissile(victim, mEquippedWeapon->getMissileParticle()); + else + fireMissile(victim, mInfo->getAttack(mAttackType)->missileParticle); + if (Net::getNetworkType() == ServerInfo::TMWATHENA) { - mFrame = 0; - mWalkTime = tick_time; + reset(); + mActionTime = 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; } } @@ -360,26 +430,99 @@ 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::setMap(Map *map) +void Being::addGuild(Guild *guild) { - // Remove sprite from potential previous map - if (mMap) - mMap->removeSprite(mMapSprite); + mGuilds[guild->getId()] = guild; + guild->addMember(mId, mName); - mMap = map; + if (this == player_node && socialWindow) + { + socialWindow->addTab(guild); + } +} - // Add sprite to potential new map - if (mMap) - mMapSprite = mMap->addSprite(this); +void Being::removeGuild(int id) +{ + if (this == player_node && socialWindow) + { + socialWindow->removeTab(mGuilds[id]); + } + + mGuilds[id]->removeMember(mId); + mGuilds.erase(id); +} - // Clear particle effect list because child particles became invalid - mChildParticleEffects.clear(); - mMustResetParticles = true; // Reset status particles on next redraw +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; } -void Being::controlParticle(Particle *particle) +Guild *Being::getGuild(int id) const { - mChildParticleEffects.addLocally(particle); + 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) @@ -404,46 +547,79 @@ void Being::fireMissile(Being *victim, const std::string &particle) void Being::setAction(Action action, int attackType) { - SpriteAction currentAction = ACTION_INVALID; + std::string currentAction = SpriteAction::INVALID; switch (action) { - case WALK: - currentAction = ACTION_WALK; + case MOVE: + currentAction = SpriteAction::MOVE; + // Note: When adding a run action, + // Differentiate walk and run with action name, + // while using only the ACTION_MOVE. break; case SIT: - currentAction = ACTION_SIT; + currentAction = SpriteAction::SIT; break; case ATTACK: if (mEquippedWeapon) - currentAction = mEquippedWeapon->getAttackType(); + { + currentAction = mEquippedWeapon->getAttackAction(); + reset(); + } else - currentAction = ACTION_ATTACK; + { + mAttackType = attackType; + currentAction = mInfo->getAttack(attackType)->action; + reset(); + + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + 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); + } + } + } - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->reset(); break; case HURT: - //currentAction = ACTION_HURT; // Buggy: makes the player stop + //currentAction = SpriteAction::HURT;// Buggy: makes the player stop // attacking and unable to attack - // again until he moves + // again until he moves. + // TODO: fix this! break; case DEAD: - currentAction = ACTION_DEAD; + currentAction = SpriteAction::DEAD; + sound.playSfx(mInfo->getSound(SOUND_EVENT_DIE)); break; case STAND: - currentAction = ACTION_STAND; + currentAction = SpriteAction::STAND; break; } - if (currentAction != ACTION_INVALID) + if (currentAction != SpriteAction::INVALID) { - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->play(currentAction); + play(currentAction); mAction = action; } + + if (currentAction != SpriteAction::MOVE) + mActionTime = tick_time; } void Being::setDirection(Uint8 direction) @@ -469,9 +645,7 @@ void Being::setDirection(Uint8 direction) dir = DIRECTION_LEFT; mSpriteDirection = dir; - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->setDirection(dir); + CompoundSprite::setDirection(dir); } /** TODO: Used by eAthena only */ @@ -506,8 +680,8 @@ void Being::nextTile() mX = pos.x; mY = pos.y; - setAction(WALK); - mWalkTime += (int)(mWalkSpeed.x / 10); + setAction(MOVE); + mActionTime += (int)(mWalkSpeed.x / 10); } int Being::getCollisionRadius() const @@ -570,8 +744,8 @@ void Being::logic() else setPosition(mPos + diff); - if (mAction != WALK) - setAction(WALK); + if (mAction != MOVE) + setAction(MOVE); // Update the player sprite direction. // N.B.: We only change this if the distance is more than one pixel. @@ -600,13 +774,76 @@ void Being::logic() // remove it and go to the next one. mPath.pop_front(); } - else if (mAction == WALK) + else if (mAction == MOVE) { setAction(STAND); } } else if (Net::getNetworkType() == ServerInfo::TMWATHENA) { + int frameCount = getFrameCount(); + + switch (mAction) + { + case STAND: + case SIT: + case DEAD: + case HURT: + break; + + case MOVE: + if ((int) ((get_elapsed_time(mActionTime) * frameCount) + / getWalkSpeed().x) >= frameCount) + nextTile(); + break; + + case ATTACK: + int rotation = 0; + std::string particleEffect = ""; + + int curFrame = (get_elapsed_time(mActionTime) * frameCount) + / mAttackSpeed; + + //attack particle effect + if (mEquippedWeapon) + { + particleEffect = mEquippedWeapon->getParticleEffect(); + + if (!particleEffect.empty() && + findSameSubstring(particleEffect, + paths.getStringValue("particles")).empty()) + particleEffect = paths.getStringValue("particles") + + particleEffect; + } + else + { + particleEffect = mInfo->getAttack(mAttackType) + ->particleEffect; + } + + if (!particleEffect.empty() && Particle::enabled + && curFrame == 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(particleEffect, 0, 0, + rotation); + controlParticle(p); + } + + if (curFrame >= frameCount) + nextTile(); + + break; + } + // Update pixel coordinates setPosition(mX * 32 + 16 + getXOffset(), mY * 32 + 32 + getYOffset()); @@ -619,68 +856,18 @@ void Being::logic() mEmotion = 0; } - // Update sprite animations - if (mUsedTargetCursor) - mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); - - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->update(tick_time * MILLISECONDS_IN_A_TICK); - - // Restart status/particle effects, if needed - if (mMustResetParticles) - { - mMustResetParticles = false; - for (std::set<int>::iterator it = mStatusEffects.begin(); - it != mStatusEffects.end(); it++) - { - const StatusEffect *effect = StatusEffect::getStatusEffect(*it, true); - if (effect && effect->particleEffectIsPersistent()) - updateStatusEffect(*it, true); - } - } - - // Update particle effects - mChildParticleEffects.moveTo(mPos.x, mPos.y); -} - -void Being::draw(Graphics *graphics, int offsetX, int offsetY) const -{ - // TODO: Eventually, we probably should fix all sprite offsets so that - // these translations aren't necessary anymore. The sprites know - // best where their base point should be. - const int px = getPixelX() + offsetX - 16; - // Temporary fix to the Y offset. - const int py = getPixelY() + offsetY - - ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); - - if (mUsedTargetCursor) - mUsedTargetCursor->draw(graphics, px, py); - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - { - if (*it) - { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, px, py); - } - } -} + ActorSprite::logic(); -void Being::drawSpriteAt(Graphics *graphics, int x, int y) const -{ - const int px = x - 16; - const int py = y - 32; + int frameCount = getFrameCount(); + if (frameCount < 10) + frameCount = 10; - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (!isAlive() && Net::getGameHandler()->removeDeadBeings() && + (int) ((get_elapsed_time(mActionTime) + / getWalkSpeed().x) >= frameCount)) { - if (*it) - { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, px, py); - } + if (getType() != PLAYER) + actorSpriteManager->destroy(this); } } @@ -701,7 +888,7 @@ void Being::drawSpeech(int offsetX, int offsetY) { const int px = getPixelX() - offsetX; const int py = getPixelY() - offsetY; - const int speech = (int) config.getValue("speech", TEXT_OVERHEAD); + const int speech = config.getIntValue("speech"); // Draw speech above this being if (mSpeechTime == 0) @@ -751,72 +938,11 @@ void Being::drawSpeech(int offsetX, int offsetY) } } -void Being::setStatusEffectBlock(int offset, Uint16 newEffects) -{ - for (int i = 0; i < STATUS_EFFECTS; i++) - { - int index = StatusEffect::blockEffectIndexToEffectIndex(offset + i); - - if (index != -1) - setStatusEffect(index, (newEffects & (1 << i)) > 0); - } -} - -void Being::handleStatusEffect(StatusEffect *effect, int effectId) -{ - if (!effect) - return; - - // TODO: Find out how this is meant to be used - // (SpriteAction != Being::Action) - //SpriteAction action = effect->getAction(); - //if (action != ACTION_INVALID) - // setAction(action); - - Particle *particle = effect->getParticle(); - - if (effectId >= 0) - { - mStatusParticleEffects.setLocally(effectId, particle); - } - else - { - mStunParticleEffects.clearLocally(); - if (particle) - mStunParticleEffects.addLocally(particle); - } -} - -void Being::updateStunMode(int oldMode, int newMode) -{ - handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); - handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); -} - -void Being::updateStatusEffect(int index, bool newStatus) -{ - handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); -} - -void Being::setStatusEffect(int index, 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); - } -} - /** TODO: eAthena only */ int Being::getOffset(char pos, char neg) const { // Check whether we're walking in the requested direction - if (mAction != WALK || !(mDirection & (pos | neg))) + if (mAction != MOVE || !(mDirection & (pos | neg))) return 0; int offset = 0; @@ -824,9 +950,9 @@ int Being::getOffset(char pos, char neg) const if (mMap) { offset = (pos == LEFT && neg == RIGHT) ? - (int)((get_elapsed_time(mWalkTime) + (int)((get_elapsed_time(mActionTime) * mMap->getTileWidth()) / mWalkSpeed.x) : - (int)((get_elapsed_time(mWalkTime) + (int)((get_elapsed_time(mActionTime) * mMap->getTileHeight()) / mWalkSpeed.y); } @@ -844,176 +970,182 @@ int Being::getOffset(char pos, char neg) const int Being::getWidth() const { - AnimatedSprite *base = NULL; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - if ((base = (*it))) - break; - - if (base) - return std::max(base->getWidth(), DEFAULT_BEING_WIDTH); - - return DEFAULT_BEING_WIDTH; + return std::max(CompoundSprite::getWidth(), DEFAULT_BEING_WIDTH); } int Being::getHeight() const { - AnimatedSprite *base = NULL; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - if ((base = (*it))) - break; + return std::max(CompoundSprite::getHeight(), DEFAULT_BEING_HEIGHT); +} - if (base) - return std::max(base->getHeight(), DEFAULT_BEING_HEIGHT); +void Being::updateCoords() +{ + if (!mDispName) + return; - return DEFAULT_BEING_HEIGHT; + // 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::setTargetAnimation(SimpleAnimation *animation) +void Being::optionChanged(const std::string &value) { - mUsedTargetCursor = animation; - mUsedTargetCursor->reset(); + if (getType() == PLAYER && value == "visiblenames") + { + setShowName(config.getBoolValue("visiblenames")); + } } -struct EffectDescription { - std::string mGFXEffect; - std::string mSFXEffect; -}; - -static EffectDescription *default_effect = NULL; -static std::map<int, EffectDescription *> effects; -static bool effects_initialized = false; - -static EffectDescription *getEffectDescription(xmlNodePtr node, int *id) +void Being::flashName(int time) { - EffectDescription *ed = new EffectDescription; - - *id = atoi(XML::getProperty(node, "id", "-1").c_str()); - ed->mSFXEffect = XML::getProperty(node, "audio", ""); - ed->mGFXEffect = XML::getProperty(node, "particle", ""); - - return ed; + if (mDispName) + mDispName->flash(time); } -static EffectDescription *getEffectDescription(int effectId) +void Being::showName() { - if (!effects_initialized) - { - XML::Document doc(BEING_EFFECTS_FILE); - xmlNodePtr root = doc.rootNode(); + delete mDispName; + mDispName = 0; + std::string mDisplayName(mName); - if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) - { - logger->log("Error loading being effects file: " - BEING_EFFECTS_FILE); - return NULL; - } + if (config.getBoolValue("showgender")) + { + if (getGender() == GENDER_FEMALE) + mDisplayName += " \u2640"; + else if (getGender() == GENDER_MALE) + mDisplayName += " \u2642"; + } - for_each_xml_child_node(node, root) + if (getType() == MONSTER) + { + if (config.getBoolValue("showMonstersTakedDamage")) { - int id; - - if (xmlStrEqual(node->name, BAD_CAST "effect")) - { - EffectDescription *EffectDescription = - getEffectDescription(node, &id); - effects[id] = EffectDescription; - } - else if (xmlStrEqual(node->name, BAD_CAST "default")) - { - EffectDescription *effectDescription = - getEffectDescription(node, &id); - - if (default_effect) - delete default_effect; - - default_effect = effectDescription; - } + mDisplayName += ", " + toString(getDamageTaken()); } + } - effects_initialized = true; - } // done initializing + gcn::Font *font = 0; + if (player_node && player_node->getTarget() == this + && getType() != MONSTER) + { + font = boldFont; + } - EffectDescription *ed = effects[effectId]; + mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(), + gcn::Graphics::CENTER, mNameColor, font); - return ed ? ed : default_effect; + updateCoords(); } -void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) +void Being::updateColors() { - logger->log("Special effect #%d on %s", effectId, - getId() == player_node->getId() ? "self" : "other"); - - EffectDescription *ed = getEffectDescription(effectId); - - if (!ed) + if (getType() == MONSTER) { - logger->log("Unknown special effect and no default recorded"); - return; + mNameColor = &userPalette->getColor(UserPalette::MONSTER); + mTextColor = &userPalette->getColor(UserPalette::MONSTER); } - - if (gfx && !ed->mGFXEffect.empty()) + else if (getType() == NPC) { - Particle *selfFX; - - selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); - controlParticle(selfFX); + 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 (sfx && !ed->mSFXEffect.empty()) - sound.playSfx(ed->mSFXEffect); -} + 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); + } + } -void Being::updateCoords() -{ if (mDispName) { - mDispName->adviseXY(getPixelX(), getPixelY()); + mDispName->setColor(mNameColor); } } -void Being::flashName(int time) +void Being::setSprite(unsigned int slot, int id, const std::string &color, + bool isWeapon) { - if (mDispName) - mDispName->flash(time); -} + assert(slot < Net::getCharHandler()->maxSprite()); -void Being::showName() -{ - delete mDispName; - mDispName = 0; - std::string mDisplayName(mName); + if (slot >= size()) + ensureSize(slot + 1); - if (getType() == PLAYER) + if (slot >= mSpriteIDs.size()) + mSpriteIDs.resize(slot + 1, 0); + + if (slot >= mSpriteColors.size()) + mSpriteColors.resize(slot + 1, ""); + + // id = 0 means unequip + if (id == 0) { - if (config.getValue("showgender", false)) - { - Player* player = static_cast<Player*>(this); - if (player) - { - if (player->getGender() == GENDER_FEMALE) - mDisplayName += " \u2640"; - else - mDisplayName += " \u2642"; - } - } + removeSprite(slot); + + if (isWeapon) + mEquippedWeapon = NULL; } - else if (getType() == MONSTER) + else { - if (config.getValue("showMonstersTakedDamage", false)) + std::string filename = ItemDB::get(id).getSprite(mGender); + AnimatedSprite *equipmentSprite = NULL; + + if (!filename.empty()) { - mDisplayName += ", " + toString(getDamageTaken()); + if (!color.empty()) + filename += "|" + color; + + equipmentSprite = AnimatedSprite::load( + paths.getStringValue("sprites") + filename); } + + if (equipmentSprite) + equipmentSprite->setDirection(getSpriteDirection()); + + CompoundSprite::setSprite(slot, equipmentSprite); + + if (isWeapon) + mEquippedWeapon = &ItemDB::get(id); + + setAction(mAction); } - mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(), - gcn::Graphics::CENTER, mNameColor); + 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 { - return mSprites.size(); + return CompoundSprite::getNumberOfLayers(); } void Being::load() @@ -1023,7 +1155,7 @@ void Being::load() int hairstyles = 1; while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE) != - paths.getValue("spriteErrorFile", "error.xml")) + paths.getStringValue("spriteErrorFile")) hairstyles++; mNumberOfHairstyles = hairstyles; @@ -1034,3 +1166,57 @@ 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() +{ + Mana::Event event("doTalk"); + event.setInt("npcId", mId); + event.trigger("NPC"); +} + +void Being::event(const std::string &channel, const Mana::Event &event) +{ + if (channel == "Chat" && + (event.getName() == "Being" || event.getName() == "Player") && + event.getInt("permissions") & PlayerRelation::SPEECH_FLOAT) + { + try + { + if (mId == event.getInt("beingId")) + { + setSpeech(event.getString("text")); + } + } + catch (Mana::BadEvent badEvent) + {} + } +} |