diff options
Diffstat (limited to 'src/being.cpp')
-rw-r--r-- | src/being.cpp | 283 |
1 files changed, 265 insertions, 18 deletions
diff --git a/src/being.cpp b/src/being.cpp index 9b8ede27..e1ff5591 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -19,11 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cassert> -#include <cmath> +#include "being.h" #include "animatedsprite.h" -#include "being.h" #include "configuration.h" #include "effectmanager.h" #include "game.h" @@ -34,6 +32,7 @@ #include "particle.h" #include "sound.h" #include "text.h" +#include "statuseffect.h" #include "gui/speechbubble.h" @@ -55,6 +54,12 @@ #include "utils/trim.h" #include "utils/xml.h" +#include <cassert> +#include <cmath> + +#define BEING_EFFECTS_FILE "effects.xml" +#define HAIR_FILE "hair.xml" + int Being::instances = 0; int Being::mNumberOfHairstyles = 1; std::vector<AnimatedSprite*> Being::emotionSet; @@ -84,11 +89,13 @@ Being::Being(int id, int job, Map *map): mGender(GENDER_UNSPECIFIED), mSpeechTime(0), mPx(0), mPy(0), + mStunMode(0), mSprites(VECTOREND_SPRITE, NULL), mSpriteIDs(VECTOREND_SPRITE, 0), mSpriteColors(VECTOREND_SPRITE, ""), mStatusParticleEffects(&mStunParticleEffects, false), - mChildParticleEffects(&mStatusParticleEffects, false) + mChildParticleEffects(&mStatusParticleEffects, false), + mMustResetParticles(false) { setMap(map); @@ -190,7 +197,7 @@ void Being::setSpeech(const std::string &text, Uint32 time) while (start != std::string::npos && end != std::string::npos) { // Catch multiple embeds and ignore them so it doesn't crash the client. - while ((mSpeech.find('[', start + 1) != std::string::npos) && + while ((mSpeech.find('[', start + 1) != std::string::npos) && (mSpeech.find('[', start + 1) < end)) { start = mSpeech.find('[', start + 1); @@ -290,6 +297,7 @@ void Being::setMap(Map *map) // Clear particle effect list because child particles became invalid mChildParticleEffects.clear(); + mMustResetParticles = true; // Reset status particles on next redraw } void Being::controlParticle(Particle *particle) @@ -314,7 +322,7 @@ void Being::setAction(Action action) { currentAction = mEquippedWeapon->getAttackType(); } - else + else { currentAction = ACTION_ATTACK; } @@ -383,11 +391,11 @@ SpriteDirection Being::getSpriteDirection() const { dir = DIRECTION_RIGHT; } - else + else { dir = DIRECTION_LEFT; } - + return dir; } @@ -468,6 +476,17 @@ void Being::logic() } } + // 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((float) mPx + 16.0f, (float) mPy + 32.0f); @@ -517,10 +536,10 @@ void Being::drawSpeech(int offsetX, int offsetY) mSpeechBubble->setCaption(mName, mNameColor); - // Not quite centered, but close enough. However, it's not too important to get - // it right right now, as it doesn't take bubble collision into account yet. + // Not quite centered, but close enough. However, it's not too important to get + // it right right now, as it doesn't take bubble collision into account yet. mSpeechBubble->setText(mSpeech); - mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() * 4 / 11), py - 70 - + mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() * 4 / 11), py - 70 - (mSpeechBubble->getNumRows()*14)); mSpeechBubble->setVisible(true); } @@ -545,10 +564,66 @@ Being::Type Being::getType() const return UNKNOWN; } +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); + } +} + 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 != WALK || !(mDirection & (pos | neg))) { return 0; } @@ -557,13 +632,13 @@ int Being::getOffset(char pos, char neg) const // We calculate the offset _from_ the _target_ location offset -= 32; - if (offset > 0) + if (offset > 0) { offset = 0; } // Going into negative direction? Invert the offset. - if (mDirection & pos) + if (mDirection & pos) { offset = -offset; } @@ -576,11 +651,11 @@ int Being::getWidth() const if (mSprites[BASE_SPRITE]) { const int width = mSprites[BASE_SPRITE]->getWidth() > DEFAULT_WIDTH ? - mSprites[BASE_SPRITE]->getWidth() : + mSprites[BASE_SPRITE]->getWidth() : DEFAULT_WIDTH; return width; } - else + else { return DEFAULT_WIDTH; } @@ -592,13 +667,185 @@ int Being::getHeight() const if (mSprites[BASE_SPRITE]) { const int height = mSprites[BASE_SPRITE]->getHeight() > DEFAULT_HEIGHT ? - mSprites[BASE_SPRITE]->getHeight() : + mSprites[BASE_SPRITE]->getHeight() : DEFAULT_HEIGHT; return height; } - else + else { return DEFAULT_HEIGHT; } } + +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) +{ + 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; +} + +static EffectDescription *getEffectDescription(int effectId) +{ + if (!effects_initialized) + { + XML::Document doc(BEING_EFFECTS_FILE); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) + { + logger->log("Error loading being effects file: " + BEING_EFFECTS_FILE); + return NULL; + } + + for_each_xml_child_node(node, root) + { + 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; + } + } + + effects_initialized = true; + } // done initializing + + EffectDescription *ed = effects[effectId]; + + if (!ed) + return default_effect; + else + return ed; +} + +void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) +{ + logger->log("Special effect #%d on %s", effectId, + getId() == player_node->getId() ? "self" : "other"); + + EffectDescription *ed = getEffectDescription(effectId); + + if (!ed) { + logger->log("Unknown special effect and no default recorded"); + return; + } + + if (gfx && ed->mGFXEffect != "") { + Particle *selfFX; + + selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); + controlParticle(selfFX); + } + + if (sfx && ed->mSFXEffect != "") { + sound.playSfx(ed->mSFXEffect); + } +} + + + + +static int hairStylesNr; +static int hairColorsNr; +static std::vector<std::string> hairColors; + +static void initializeHair(); + +int Being::getHairStylesNr() +{ + initializeHair(); + return hairStylesNr; +} + +int Being::getHairColorsNr() +{ + initializeHair(); + return hairColorsNr; +} + +std::string Being::getHairColor(int index) +{ + initializeHair(); + if (index < 0 || index >= hairColorsNr) + return "#000000"; + + return hairColors[index]; +} + +static bool hairInitialized = false; + +static void initializeHair() +{ + if (hairInitialized) + return; + + // Hairstyles are encoded as negative numbers. Count how far negative we + // can go. + int hairstylesCtr = -1; + while (ItemDB::get(hairstylesCtr).getSprite(GENDER_MALE) != "error.xml") + --hairstylesCtr; + + hairStylesNr = -hairstylesCtr; // done. + if (hairStylesNr == 0) + hairStylesNr = 1; // No hair style -> no hair + + hairColorsNr = 0; + + XML::Document doc(HAIR_FILE); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "colors")) + { + logger->log("Error loading being hair configuration file"); + } else { + for_each_xml_child_node(node, root) + { + if (xmlStrEqual(node->name, BAD_CAST "color")) + { + int index = atoi(XML::getProperty(node, "id", "-1").c_str()); + std::string value = XML::getProperty(node, "value", ""); + + if (index >= 0 && value != "") { + if (index >= hairColorsNr) { + hairColorsNr = index + 1; + hairColors.resize(hairColorsNr, "#000000"); + } + hairColors[index] = value; + } + } + } + } // done initializing + + if (hairColorsNr == 0) { // No colours -> black only + hairColorsNr = 1; + hairColors.resize(hairColorsNr, "#000000"); + } + + hairInitialized = 1; +} |