summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp255
1 files changed, 237 insertions, 18 deletions
diff --git a/src/being.cpp b/src/being.cpp
index 3a772fbd..f14fd64e 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -1,9 +1,8 @@
/*
- * Aethyra
+ * The Mana World
* Copyright (C) 2004 The Mana World Development Team
*
- * This file is part of Aethyra based on original code
- * from The Mana World.
+ * This file is part of The Mana World.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,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"
@@ -36,6 +33,7 @@
#include "simpleanimation.h"
#include "sound.h"
#include "text.h"
+#include "statuseffect.h"
#include "gui/speechbubble.h"
@@ -53,8 +51,17 @@
#include "utils/dtor.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
+#include "utils/xml.h"
+
+#include <cassert>
+#include <cmath>
+#define BEING_EFFECTS_FILE "effects.xml"
+#define HAIR_FILE "hair.xml"
+
+int Being::mNumberOfHairColors = 1;
int Being::mNumberOfHairstyles = 1;
+std::vector<std::string> Being::hairColors;
static const int X_SPEECH_OFFSET = 18;
static const int Y_SPEECH_OFFSET = 60;
@@ -81,15 +88,18 @@ Being::Being(int id, int job, Map *map):
mHairStyle(1), mHairColor(0),
mGender(GENDER_UNSPECIFIED),
mPx(0), mPy(0),
+ mStunMode(0),
mSprites(VECTOREND_SPRITE, NULL),
mSpriteIDs(VECTOREND_SPRITE, 0),
mSpriteColors(VECTOREND_SPRITE, ""),
- mChildParticleEffects(),
+ mStatusParticleEffects(&mStunParticleEffects, false),
+ mChildParticleEffects(&mStatusParticleEffects, false),
+ mMustResetParticles(false),
mUsedTargetCursor(NULL)
{
setMap(map);
- mSpeechBubble = new SpeechBubble();
+ mSpeechBubble = new SpeechBubble;
mSpeech = "";
mNameColor = &guiPalette->getColor(Palette::CHAT);
@@ -224,15 +234,21 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type)
color = &guiPalette->getColor(Palette::HIT_MONSTER_PLAYER);
}
- if (amount > 0 && type == CRITICAL)
- {
- particleEngine->addTextSplashEffect("crit!", mPx + 16, mPy + 16,
- color, font, true);
- }
-
// Show damage number
particleEngine->addTextSplashEffect(damage, mPx + 16, mPy + 16,
- color, font, true);
+ color, font, true);
+
+ if (amount > 0)
+ {
+ if (type != CRITICAL)
+ {
+ effectManager->trigger(26, this);
+ }
+ else
+ {
+ effectManager->trigger(28, this);
+ }
+ }
}
void Being::handleAttack(Being *victim, int damage, AttackType type)
@@ -256,6 +272,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)
@@ -410,7 +427,7 @@ void Being::logic()
}
// Update sprite animations
- if (mUsedTargetCursor != NULL)
+ if (mUsedTargetCursor)
mUsedTargetCursor->update(tick_time * 10);
for (int i = 0; i < VECTOREND_SPRITE; i++)
@@ -419,6 +436,17 @@ void Being::logic()
mSprites[i]->update(tick_time * 10);
}
+ // 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);
}
@@ -428,7 +456,7 @@ void Being::draw(Graphics *graphics, int offsetX, int offsetY) const
int px = mPx + offsetX;
int py = mPy + offsetY;
- if (mUsedTargetCursor != NULL)
+ if (mUsedTargetCursor)
mUsedTargetCursor->draw(graphics, px, py);
for (int i = 0; i < VECTOREND_SPRITE; i++)
@@ -512,6 +540,62 @@ 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
@@ -568,6 +652,114 @@ void Being::setTargetAnimation(SimpleAnimation* animation)
mUsedTargetCursor->reset();
}
+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.empty()) {
+ Particle *selfFX;
+
+ selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0);
+ controlParticle(selfFX);
+ }
+
+ if (sfx && !ed->mSFXEffect.empty()) {
+ sound.playSfx(ed->mSFXEffect);
+ }
+}
+
+int Being::getHairStyleCount()
+{
+ return mNumberOfHairstyles;
+}
+
+int Being::getHairColorCount()
+{
+ return mNumberOfHairColors;
+}
+
+std::string Being::getHairColor(int index)
+{
+ if (index < 0 || index >= mNumberOfHairColors)
+ return "#000000";
+
+ return hairColors[index];
+}
+
void Being::load()
{
// Hairstyles are encoded as negative numbers. Count how far negative
@@ -579,5 +771,32 @@ void Being::load()
hairstyles++;
}
mNumberOfHairstyles = hairstyles;
-}
+ XML::Document doc(HAIR_FILE);
+ xmlNodePtr root = doc.rootNode();
+
+ // Add an initial hair color
+ hairColors.resize(1, "#000000");
+
+ 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.empty()) {
+ if (index >= mNumberOfHairColors) {
+ mNumberOfHairColors = index + 1;
+ hairColors.resize(mNumberOfHairColors, "#000000");
+ }
+ hairColors[index] = value;
+ }
+ }
+ }
+ }
+}