summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp281
1 files changed, 259 insertions, 22 deletions
diff --git a/src/being.cpp b/src/being.cpp
index c944bcf9..96bce027 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"
@@ -50,11 +48,17 @@
#include "gui/speechbubble.h"
#include "utils/dtor.h"
-#include "utils/gettext.h"
-#include "utils/tostring.h"
+#include "utils/gettext.h"
+#include "utils/tostring.h"
#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,16 +88,18 @@ 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),
+ mMustResetParticles(false),
mUsedTargetCursor(NULL)
{
setMap(map);
- mSpeechBubble = new SpeechBubble();
+ mSpeechBubble = new SpeechBubble;
if (instances == 0)
{
@@ -241,22 +247,13 @@ void Being::takeDamage(int amount)
// Show damage number
particleEngine->addTextSplashEffect(damage, 255, 255, 255, font,
mPx + 16, mPy + 16);
+ effectManager->trigger(26, this);
}
void Being::showCrit()
{
- gcn::Font *font;
- std::string text = "crit!";
-
- // Selecting the right color
- if (getType() == MONSTER)
- font = hitBlueFont;
- else
- font = hitRedFont;
+ effectManager->trigger(28, this);
- // Show crit notice
- particleEngine->addTextSplashEffect(text, 255, 255, 255, font,
- mPx + 16, mPy + 16);
}
void Being::handleAttack(Being *victim, int damage)
@@ -280,6 +277,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)
@@ -445,7 +443,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++)
@@ -454,6 +452,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);
@@ -465,7 +474,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);
}
@@ -548,6 +557,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
@@ -609,3 +674,175 @@ void Being::setTargetAnimation(SimpleAnimation* animation)
mUsedTargetCursor = 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);
+ }
+}
+
+
+
+
+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.empty()) {
+ if (index >= hairColorsNr) {
+ hairColorsNr = index + 1;
+ hairColors.resize(hairColorsNr, "#000000");
+ }
+ hairColors[index] = value;
+ }
+ }
+ }
+ } // done initializing
+
+ if (hairColorsNr == 0) { // No colors -> black only
+ hairColorsNr = 1;
+ hairColors.resize(hairColorsNr, "#000000");
+ }
+
+ hairInitialized = 1;
+}