summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp249
1 files changed, 192 insertions, 57 deletions
diff --git a/src/being.cpp b/src/being.cpp
index 7036b8cd..63c60390 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -22,44 +22,52 @@
#include "being.h"
#include "animatedsprite.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 "statuseffect.h"
-#include "resources/itemdb.h"
-#include "resources/resourcemanager.h"
+#include "resources/emotedb.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/gettext.h"
#include "utils/tostring.h"
-
#include "utils/xml.h"
#include <cassert>
+#include <cmath>
#define BEING_EFFECTS_FILE "effects.xml"
#define HAIR_FILE "hair.xml"
int Being::instances = 0;
-ImageSet *Being::emotionSet = NULL;
+int Being::mNumberOfHairstyles = 1;
+std::vector<AnimatedSprite*> Being::emotionSet;
static const int X_SPEECH_OFFSET = 18;
static const int Y_SPEECH_OFFSET = 60;
+static const int DEFAULT_WIDTH = 32;
+static const int DEFAULT_HEIGHT = 32;
+
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),
@@ -68,6 +76,9 @@ Being::Being(int id, int job, Map *map):
mWalkSpeed(150),
mDirection(DOWN),
mMap(NULL),
+ mName(""),
+ mIsGM(false),
+ mParticleEffects(config.getValue("particleeffects", 1)),
mEquippedWeapon(NULL),
mHairStyle(1), mHairColor(0),
mGender(GENDER_UNSPECIFIED),
@@ -83,16 +94,33 @@ 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!");
+ // Setup emote sprites
+ for (int i = 0; i <= EmoteDB::getLast(); i++)
+ {
+ EmoteInfo info = EmoteDB::get(i);
+
+ std::string file = "graphics/sprites/" + info.sprites.front()->sprite;
+ int variant = info.sprites.front()->variant;
+ emotionSet.push_back(AnimatedSprite::load(file, variant));
+ }
+
+ // Hairstyles are encoded as negative numbers. Count how far negative we can go.
+ int hairstyles = 1;
+ while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE) != "error.xml")
+ {
+ hairstyles++;
+ }
+ mNumberOfHairstyles = hairstyles;
}
instances++;
- mSpeech = 0;
+ mSpeech = "";
+ mNameColor = 0x202020;
+ mText = 0;
}
Being::~Being()
@@ -106,11 +134,11 @@ Being::~Being()
if (instances == 0)
{
- emotionSet->decRef();
- emotionSet = NULL;
+ delete_all(emotionSet);
}
- delete mSpeech;
+ delete mSpeechBubble;
+ delete mText;
}
void Being::setDestination(Uint16 destX, Uint16 destY)
@@ -139,8 +167,8 @@ void Being::setPath(const Path &path)
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)
@@ -152,13 +180,48 @@ void Being::setSprite(int slot, int id, std::string color)
void Being::setSpeech(const std::string &text, Uint32 time)
{
- // don't introduce a memory leak
- delete mSpeech;
+ mSpeech = text;
+
+ // Trim whitespace
+ while (mSpeech[0] == ' ')
+ {
+ mSpeech = mSpeech.substr(1, mSpeech.size());
+ }
+ while (mSpeech[mSpeech.size()] == ' ')
+ {
+ mSpeech = mSpeech.substr(0, mSpeech.size() - 1);
+ }
+
+ // check for links
+ std::string::size_type start = mSpeech.find('[');
+ std::string::size_type end = mSpeech.find(']', start);
+
+ 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) &&
+ (mSpeech.find('[', start + 1) < end))
+ {
+ start = mSpeech.find('[', start + 1);
+ }
- mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET,
- gcn::Graphics::CENTER,
- gcn::Color(255, 255, 255), true);
- mSpeechTime = 500;
+ std::string::size_type position = mSpeech.find('|');
+ mSpeech.erase(end, 1);
+ mSpeech.erase(start, (position - start) + 1);
+ position = mSpeech.find('@');
+
+ while (position != std::string::npos)
+ {
+ mSpeech.erase(position, 2);
+ position = mSpeech.find('@');
+ }
+
+ start = mSpeech.find('[', start + 1);
+ end = mSpeech.find(']', start);
+ }
+
+ if (mSpeech != "")
+ mSpeechTime = 500;
}
void Being::takeDamage(int amount)
@@ -173,10 +236,6 @@ void Being::takeDamage(int amount)
}
else
{
- // Hit particle effect
- controlParticle(particleEngine->addEffect(
- "graphics/particles/hit.particle.xml", 0, 0));
-
if (getType() == MONSTER)
{
font = hitBlueFont;
@@ -192,6 +251,26 @@ void Being::takeDamage(int amount)
mPx + 16, mPy + 16);
}
+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);
@@ -225,9 +304,10 @@ 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:
@@ -241,7 +321,8 @@ void Being::setAction(Uint8 action)
{
currentAction = mEquippedWeapon->getAttackType();
}
- else {
+ else
+ {
currentAction = ACTION_ATTACK;
}
for (int i = 0; i < VECTOREND_SPRITE; i++)
@@ -278,9 +359,11 @@ void Being::setAction(Uint8 action)
}
}
-
void Being::setDirection(Uint8 direction)
{
+ if (mDirection == direction)
+ return;
+
mDirection = direction;
SpriteDirection dir = getSpriteDirection();
@@ -307,10 +390,11 @@ SpriteDirection Being::getSpriteDirection() const
{
dir = DIRECTION_RIGHT;
}
- else {
- dir = DIRECTION_LEFT;
+ else
+ {
+ dir = DIRECTION_LEFT;
}
-
+
return dir;
}
@@ -352,28 +436,28 @@ void Being::nextStep()
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--;
@@ -431,8 +515,47 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY)
const int py = mPy + offsetY - 60;
const int emotionIndex = mEmotion - 1;
- if (emotionIndex >= 0 && emotionIndex < (int) emotionSet->size())
- graphics->drawImage(emotionSet->get(emotionIndex), px, py);
+ if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast())
+ emotionSet[emotionIndex]->draw(graphics, px, py);
+}
+
+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, 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.
+ 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, gcn::Color(255, 255, 255));
+ }
+ else if (mSpeechTime == 0)
+ {
+ mSpeechBubble->setVisible(false);
+ }
}
Being::Type Being::getType() const
@@ -455,9 +578,11 @@ void Being::handleStatusEffect(StatusEffect *effect, int effectId)
if (!effect)
return;
- SpriteAction action = effect->getAction();
- if (action != ACTION_INVALID)
- setAction(action);
+ // 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();
@@ -497,7 +622,8 @@ void Being::setStatusEffect(int index, bool active)
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;
}
@@ -505,27 +631,32 @@ 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;
}
return offset;
}
-
int Being::getWidth() const
{
if (mSprites[BASE_SPRITE])
{
- return mSprites[BASE_SPRITE]->getWidth();
+ const int width = mSprites[BASE_SPRITE]->getWidth() > DEFAULT_WIDTH ?
+ mSprites[BASE_SPRITE]->getWidth() :
+ DEFAULT_WIDTH;
+ return width;
}
- else {
- return 0;
+ else
+ {
+ return DEFAULT_WIDTH;
}
}
@@ -534,10 +665,14 @@ int Being::getHeight() const
{
if (mSprites[BASE_SPRITE])
{
- return mSprites[BASE_SPRITE]->getHeight();
+ const int height = mSprites[BASE_SPRITE]->getHeight() > DEFAULT_HEIGHT ?
+ mSprites[BASE_SPRITE]->getHeight() :
+ DEFAULT_HEIGHT;
+ return height;
}
- else {
- return 0;
+ else
+ {
+ return DEFAULT_HEIGHT;
}
}