summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp405
1 files changed, 143 insertions, 262 deletions
diff --git a/src/being.cpp b/src/being.cpp
index 7c6d91e7..d98af29c 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -18,31 +18,36 @@
* along with The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "being.h"
-#include <algorithm>
#include <cassert>
+#include <cmath>
#include "animatedsprite.h"
+#include "being.h"
+#include "configuration.h"
#include "equipment.h"
#include "game.h"
#include "graphics.h"
+#include "localplayer.h"
#include "log.h"
#include "map.h"
#include "particle.h"
#include "sound.h"
-#include "localplayer.h"
#include "text.h"
-#include "resources/itemdb.h"
-#include "resources/resourcemanager.h"
#include "resources/imageset.h"
+#include "resources/itemdb.h"
#include "resources/iteminfo.h"
+#include "resources/resourcemanager.h"
#include "gui/gui.h"
+#include "gui/speechbubble.h"
#include "utils/dtor.h"
#include "utils/tostring.h"
+#include "utils/xml.h"
+
+#define BEING_EFFECTS_FILE "effects.xml"
#include "utils/xml.h"
@@ -50,6 +55,7 @@
#define HAIR_FILE "hair.xml"
int Being::instances = 0;
+int Being::mNumberOfHairstyles = 1;
ImageSet *Being::emotionSet = NULL;
static const int X_SPEECH_OFFSET = 18;
@@ -58,7 +64,7 @@ static const int Y_SPEECH_OFFSET = 60;
Being::Being(int id, int job, Map *map):
mJob(job),
mX(0), mY(0),
- mAction(0),
+ mAction(STAND),
mWalkTime(0),
mEmotion(0), mEmotionTime(0),
mAttackSpeed(350),
@@ -67,6 +73,8 @@ Being::Being(int id, int job, Map *map):
mWalkSpeed(150),
mDirection(DOWN),
mMap(NULL),
+ mIsGM(false),
+ mParticleEffects(config.getValue("particleeffects", 1)),
mEquippedWeapon(NULL),
mHairStyle(1), mHairColor(0),
mGender(2),
@@ -80,21 +88,32 @@ Being::Being(int id, int job, Map *map):
{
setMap(map);
+ mSpeechBubble = new SpeechBubble();
+
if (instances == 0)
{
// Load the emotion set
ResourceManager *rm = ResourceManager::getInstance();
emotionSet = rm->getImageSet("graphics/gui/emotions.png", 30, 32);
if (!emotionSet) logger->error("Unable to load emotions!");
+
+ // Hairstyles are encoded as negative numbers. Count how far negative we can go.
+ int hairstyles = 1;
+ while (ItemDB::get(-hairstyles).getSprite(0) != "error.xml")
+ {
+ hairstyles++;
+ }
+ mNumberOfHairstyles = hairstyles;
}
instances++;
- mSpeech = 0;
+ mSpeech = "";
+ mText = 0;
}
Being::~Being()
{
- std::for_each(mSprites.begin(), mSprites.end(), make_dtor(mSprites));
+ delete_all(mSprites);
clearPath();
setMap(NULL);
@@ -107,11 +126,11 @@ Being::~Being()
emotionSet = NULL;
}
- delete mSpeech;
+ delete mSpeechBubble;
+ delete mText;
}
-void
-Being::setDestination(Uint16 destX, Uint16 destY)
+void Being::setDestination(Uint16 destX, Uint16 destY)
{
if (mMap)
{
@@ -119,14 +138,12 @@ Being::setDestination(Uint16 destX, Uint16 destY)
}
}
-void
-Being::clearPath()
+void Being::clearPath()
{
mPath.clear();
}
-void
-Being::setPath(const Path &path)
+void Being::setPath(const Path &path)
{
mPath = path;
@@ -137,35 +154,27 @@ Being::setPath(const Path &path)
}
}
-void
-Being::setHairStyle(int style, int color)
+void Being::setHairStyle(int style, int color)
{
- mHairStyle = style < 0 ? mHairStyle : style % getHairStylesNr();
- mHairColor = color < 0 ? mHairColor : color % getHairColorsNr();
+ mHairStyle = style < 0 ? mHairStyle : style % mNumberOfHairstyles;
+ mHairColor = color < 0 ? mHairColor : color % ColorDB::size();
}
-void
-Being::setSprite(int slot, int id, std::string color)
+void Being::setSprite(int slot, int id, std::string color)
{
assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE);
mSpriteIDs[slot] = id;
mSpriteColors[slot] = color;
}
-void
-Being::setSpeech(const std::string &text, Uint32 time)
+void Being::setSpeech(const std::string &text, Uint32 time)
{
- // don't introduce a memory leak
- delete mSpeech;
+ mSpeech = text;
- mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET,
- gcn::Graphics::CENTER, speechFont,
- gcn::Color(255, 255, 255));
mSpeechTime = 500;
}
-void
-Being::takeDamage(int amount)
+void Being::takeDamage(int amount)
{
gcn::Font *font;
std::string damage = amount ? toString(amount) : "miss";
@@ -177,10 +186,6 @@ Being::takeDamage(int amount)
}
else
{
- // Hit particle effect
- controlParticle(particleEngine->addEffect(
- "graphics/particles/hit.particle.xml", 0, 0));
-
if (getType() == MONSTER)
{
font = hitBlueFont;
@@ -196,16 +201,34 @@ Being::takeDamage(int amount)
mPx + 16, mPy + 16);
}
-void
-Being::handleAttack(Being *victim, int damage)
+void Being::showCrit()
+{
+ gcn::Font *font;
+ std::string text = "crit!";
+
+ // Selecting the right color
+ if (getType() == MONSTER)
+ {
+ font = hitBlueFont;
+ }
+ else
+ {
+ font = hitRedFont;
+ }
+
+ // Show crit notice
+ particleEngine->addTextSplashEffect(text, 255, 255, 255, font,
+ mPx + 16, mPy + 16);
+}
+
+void Being::handleAttack(Being *victim, int damage)
{
setAction(Being::ATTACK);
mFrame = 0;
mWalkTime = tick_time;
}
-void
-Being::setMap(Map *map)
+void Being::setMap(Map *map)
{
// Remove sprite from potential previous map
if (mMap)
@@ -225,16 +248,15 @@ Being::setMap(Map *map)
mChildParticleEffects.clear();
}
-void
-Being::controlParticle(Particle *particle)
+void Being::controlParticle(Particle *particle)
{
mChildParticleEffects.addLocally(particle);
}
-void
-Being::setAction(Uint8 action)
+void Being::setAction(Action action)
{
SpriteAction currentAction = ACTION_INVALID;
+
switch (action)
{
case WALK:
@@ -248,7 +270,8 @@ Being::setAction(Uint8 action)
{
currentAction = mEquippedWeapon->getAttackType();
}
- else {
+ else
+ {
currentAction = ACTION_ATTACK;
}
for (int i = 0; i < VECTOREND_SPRITE; i++)
@@ -285,10 +308,11 @@ Being::setAction(Uint8 action)
}
}
-
-void
-Being::setDirection(Uint8 direction)
+void Being::setDirection(Uint8 direction)
{
+ if (mDirection == direction)
+ return;
+
mDirection = direction;
SpriteDirection dir = getSpriteDirection();
@@ -299,8 +323,7 @@ Being::setDirection(Uint8 direction)
}
}
-SpriteDirection
-Being::getSpriteDirection() const
+SpriteDirection Being::getSpriteDirection() const
{
SpriteDirection dir;
@@ -316,15 +339,15 @@ Being::getSpriteDirection() const
{
dir = DIRECTION_RIGHT;
}
- else {
- dir = DIRECTION_LEFT;
+ else
+ {
+ dir = DIRECTION_LEFT;
}
-
+
return dir;
}
-void
-Being::nextStep()
+void Being::nextStep()
{
if (mPath.empty())
{
@@ -359,32 +382,31 @@ Being::nextStep()
mWalkTime += mWalkSpeed / 10;
}
-void
-Being::logic()
+void Being::logic()
{
// Reduce the time that speech is still displayed
- if (mSpeechTime > 0 && mSpeech)
+ if (mSpeechTime > 0)
+ mSpeechTime--;
+
+ // Remove text if speech boxes aren't being used
+ if (mSpeechTime == 0 && mText)
{
- if (--mSpeechTime == 0)
- {
- delete mSpeech;
- mSpeech = 0;
- }
+ delete mText;
+ mText = 0;
}
int oldPx = mPx;
int oldPy = mPy;
+
// Update pixel coordinates
mPx = mX * 32 + getXOffset();
mPy = mY * 32 + getYOffset();
+
if (mPx != oldPx || mPy != oldPy)
{
- if (mSpeech)
- {
- mSpeech->adviseXY(mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET);
- }
updateCoords();
}
+
if (mEmotion != 0)
{
mEmotionTime--;
@@ -406,8 +428,7 @@ Being::logic()
mChildParticleEffects.setPositions((float)mPx + 16.0f, (float)mPy + 32.0f);
}
-void
-Being::draw(Graphics *graphics, int offsetX, int offsetY) const
+void Being::draw(Graphics *graphics, int offsetX, int offsetY) const
{
int px = mPx + offsetX;
int py = mPy + offsetY;
@@ -421,8 +442,7 @@ Being::draw(Graphics *graphics, int offsetX, int offsetY) const
}
}
-void
-Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY)
+void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY)
{
if (!mEmotion)
return;
@@ -435,17 +455,56 @@ Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY)
graphics->drawImage(emotionSet->get(emotionIndex), px, py);
}
-Being::Type
-Being::getType() const
+void Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY)
+{
+ int px = mPx + offsetX;
+ int py = mPy + offsetY;
+
+ // Draw speech above this being
+ if (mSpeechTime > 0 && config.getValue("speechbubble", 1))
+ {
+ if (mText)
+ {
+ delete mText;
+ mText = 0;
+ }
+
+ mSpeechBubble->setCaption(mName);
+ mSpeechBubble->setWindowName(mName);
+ // 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->getNumRows()*14));
+ mSpeechBubble->setVisible(true);
+ }
+ else if (mSpeechTime > 0 && !config.getValue("speechbubble", 1))
+ {
+ mSpeechBubble->setVisible(false);
+ // don't introduce a memory leak
+ if (mText)
+ delete mText;
+
+ mText = new Text(mSpeech, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET,
+ gcn::Graphics::CENTER, speechFont,
+ gcn::Color(255, 255, 255));
+ }
+ else if (mSpeechTime == 0)
+ {
+ mSpeechBubble->setVisible(false);
+ }
+}
+
+Being::Type Being::getType() const
{
return UNKNOWN;
}
-int
-Being::getOffset(char pos, char neg) const
+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;
}
@@ -453,220 +512,42 @@ 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;
}
return offset;
}
-
-int
-Being::getWidth() const
+int Being::getWidth() const
{
if (mSprites[BASE_SPRITE])
{
return mSprites[BASE_SPRITE]->getWidth();
}
- else {
+ else
+ {
return 0;
}
}
-int
-Being::getHeight() const
+int Being::getHeight() const
{
if (mSprites[BASE_SPRITE])
{
return mSprites[BASE_SPRITE]->getHeight();
}
- else {
- return 0;
- }
-}
-
-
-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)
+ else
{
- 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);
+ return 0;
}
}
-
-
-
-static int hairStylesNr;
-static int hairColorsNr;
-static std::vector<std::string> hairColors;
-
-static void
-initializeHair(void);
-
-int
-Being::getHairStylesNr(void)
-{
- initializeHair();
- return hairStylesNr;
-}
-
-int
-Being::getHairColorsNr(void)
-{
- 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(void)
-{
- if (hairInitialized)
- return;
-
- // Hairstyles are encoded as negative numbers. Count how far negative we can go.
- int hairstylesCtr = -1;
- while (ItemDB::get(hairstylesCtr).getSprite(0) != "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;
-}