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