diff options
Diffstat (limited to 'src/being.cpp')
-rw-r--r-- | src/being.cpp | 405 |
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; -} |