From bdb09d26fc6c489228cb2c28be75024f9a49eb75 Mon Sep 17 00:00:00 2001 From: Bjørn Lindeijer Date: Thu, 28 Aug 2008 20:29:06 +0000 Subject: Accepted Patch by Scraggy that moves text in such a way that no text overlaps --- ChangeLog | 11 +++ src/Makefile.am | 4 ++ src/being.cpp | 50 ++++++++----- src/being.h | 28 ++++---- src/gui/viewport.cpp | 28 ++------ src/localplayer.cpp | 28 +++++++- src/localplayer.h | 11 +-- src/monster.cpp | 45 +++++++++++- src/monster.h | 20 ++++++ src/npc.cpp | 39 +++++++---- src/npc.h | 11 ++- src/player.cpp | 65 +++++++++-------- src/player.h | 47 ++++--------- src/player_relations.cpp | 32 ++------- src/text.cpp | 104 +++++++++++++++++++++++++++ src/text.h | 96 +++++++++++++++++++++++++ src/textmanager.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++++++ src/textmanager.h | 78 +++++++++++++++++++++ 18 files changed, 703 insertions(+), 173 deletions(-) create mode 100644 src/text.cpp create mode 100644 src/text.h create mode 100644 src/textmanager.cpp create mode 100644 src/textmanager.h diff --git a/ChangeLog b/ChangeLog index 70ae300b..a89dc660 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-08-28 Douglas Boffey + + * src/localplayer.cpp, src/text.h, src/gui/viewport.cpp, src/npc.cpp, + src/textmanager.h, src/player.cpp, src/monster.h, src/textmanager.cpp, + src/being.cpp, src/npc.h, src/monster.cpp, src/player.h, + src/localplayer.h, src/player_relations.cpp, src/text.cpp, + src/Makefile.am, src/being.h: Move any chat, character names, NPC + names or targetted mob names so that no text will obscure any other + text. If there is no decent place for the text, it will default to + overlapping. + 2008-08-28 Fate * src/engine.h, src/gui/ministatus.cpp, src/engine.cpp, diff --git a/src/Makefile.am b/src/Makefile.am index 3156a800..340f1947 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -291,6 +291,10 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ sound.cpp \ sound.h \ sprite.h \ + text.cpp \ + text.h \ + textmanager.cpp \ + textmanager.h \ textparticle.cpp \ textparticle.h \ tileset.h \ diff --git a/src/being.cpp b/src/being.cpp index 1880e7c0..dca87677 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -34,6 +34,7 @@ #include "particle.h" #include "sound.h" #include "localplayer.h" +#include "text.h" #include "resources/resourcemanager.h" #include "resources/imageset.h" @@ -51,6 +52,9 @@ int Being::instances = 0; ImageSet *Being::emotionSet = NULL; +static const int X_SPEECH_OFFSET = 18; +static const int Y_SPEECH_OFFSET = 60; + Being::Being(int id, int job, Map *map): mJob(job), mX(0), mY(0), @@ -83,6 +87,7 @@ Being::Being(int id, int job, Map *map): } instances++; + mSpeech = 0; } Being::~Being() @@ -106,6 +111,8 @@ Being::~Being() emotionSet->decRef(); emotionSet = NULL; } + + delete mSpeech; } void @@ -153,7 +160,12 @@ Being::setSprite(int slot, int id, std::string color) void Being::setSpeech(const std::string &text, Uint32 time) { - mSpeech = text; + // don't introduce a memory leak + delete mSpeech; + + mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, + gcn::Graphics::CENTER, speechFont, + gcn::Color(255, 255, 255)); mSpeechTime = 500; } @@ -361,13 +373,28 @@ void Being::logic() { // Reduce the time that speech is still displayed - if (mSpeechTime > 0) - mSpeechTime--; + if (mSpeechTime > 0 && mSpeech) + { + if (--mSpeechTime == 0) + { + delete mSpeech; + mSpeech = 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--; @@ -432,21 +459,6 @@ Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) graphics->drawImage(emotionSet->get(emotionIndex), 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) - { - graphics->setFont(speechFont); - graphics->setColor(gcn::Color(255, 255, 255)); - graphics->drawText(mSpeech, px + 18, py - 60, gcn::Graphics::CENTER); - } -} - Being::Type Being::getType() const { diff --git a/src/being.h b/src/being.h index c4f34503..d5fe7790 100644 --- a/src/being.h +++ b/src/being.h @@ -47,6 +47,7 @@ class Map; class Graphics; class ImageSet; class Particle; +class Text; /** * A position along a being's path. @@ -187,7 +188,7 @@ class Being : public Sprite * * @param name The name that should appear. */ - void + virtual void setName(const std::string &name) { mName = name; } /** @@ -238,24 +239,12 @@ class Being : public Sprite virtual void logic(); - /** - * Draws the speech text above the being. - */ - void - drawSpeech(Graphics *graphics, int offsetX, int offsetY); - /** * Draws the emotion picture above the being. */ void drawEmotion(Graphics *graphics, int offsetX, int offsetY); - /** - * Draws the name text below the being. - */ - virtual void - drawName(Graphics *, int, int) {}; - /** * Returns the type of the being. */ @@ -363,7 +352,11 @@ class Being : public Sprite */ void controlParticle(Particle *particle); - void setEmote(Uint8 emotion, Uint8 emote_time) { mEmotion = emotion; mEmotionTime = emote_time; } + void setEmote(Uint8 emotion, Uint8 emote_time) + { + mEmotion = emotion; + mEmotionTime = emote_time; + } /** * Triggers a visual effect, such as `level up' @@ -383,6 +376,11 @@ class Being : public Sprite */ void setPath(const Path &path); + /** + * Let the sub-classes react to a replacement + */ + virtual void updateCoords() {} + /** * Returns the sprite direction of this being. */ @@ -409,7 +407,7 @@ class Being : public Sprite const ItemInfo* mEquippedWeapon; Path mPath; - std::string mSpeech; + Text *mSpeech; Uint16 mHairStyle, mHairColor; Uint8 mGender; Uint32 mSpeechTime; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 9d1e5a1d..e8293acf 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -37,6 +37,7 @@ #include "../map.h" #include "../monster.h" #include "../npc.h" +#include "../textmanager.h" #include "../resources/animation.h" #include "../resources/monsterinfo.h" @@ -226,7 +227,6 @@ Viewport::draw(gcn::Graphics *gcnGraphics) { mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY); drawTargetCursor(graphics); // TODO: Draw the cursor with the sprite - drawTargetName(graphics); } // Find a path from the player to the mouse, and draw it. This is for debug @@ -257,12 +257,16 @@ Viewport::draw(gcn::Graphics *gcnGraphics) } } + // Draw text + if (textManager) + { + textManager->draw(graphics, mPixelViewX, mPixelViewY); + } + // Draw player names, speech, and emotion sprite as needed Beings &beings = beingManager->getAll(); for (BeingIterator i = beings.begin(); i != beings.end(); i++) { - (*i)->drawSpeech(graphics, -(int) mPixelViewX, -(int) mPixelViewY); - (*i)->drawName(graphics, -(int) mPixelViewX, -(int) mPixelViewY); (*i)->drawEmotion(graphics, -(int) mPixelViewX, -(int) mPixelViewY); } @@ -329,24 +333,6 @@ Viewport::drawTargetCursor(Graphics *graphics) } } -void -Viewport::drawTargetName(Graphics *graphics) -{ - // Draw target marker if needed - Being *target = player_node->getTarget(); - if (target && target->getType() == Being::MONSTER) - { - graphics->setFont(speechFont); - graphics->setColor(gcn::Color(255, 32, 32)); - - const MonsterInfo &mi = static_cast(target)->getInfo(); - int posX = target->getPixelX() + 16 - (int)mPixelViewX; - int posY = target->getPixelY() + 16 - target->getHeight() - (int)mPixelViewY; - - graphics->drawText(mi.getName(), posX, posY, gcn::Graphics::CENTER); - } -} - void Viewport::mousePressed(gcn::MouseEvent &event) { diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 3929da8b..3caa5ead 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -31,6 +31,7 @@ #include "main.h" #include "particle.h" #include "sound.h" +#include "monster.h" #include "gui/gui.h" @@ -226,6 +227,23 @@ void LocalPlayer::walk(unsigned char dir) } } +void LocalPlayer::setTarget(Being *target) +{ + if (target == mTarget) + { + return; + } + if (mTarget && mTarget->getType() == Being::MONSTER) + { + static_cast(mTarget)->showName(false); + } + mTarget = target; + if (target && target->getType() == Being::MONSTER) + { + static_cast(target)->showName(true); + } +} + void LocalPlayer::setDestination(Uint16 x, Uint16 y) { // Only send a new message to the server when destination changes @@ -363,9 +381,13 @@ void LocalPlayer::attack(Being *target, bool keep) return; if (keep && target) - mTarget = target; + { + setTarget(target); + } else if (mTarget) + { target = mTarget; + } if (!target) return; @@ -411,7 +433,7 @@ void LocalPlayer::attack(Being *target, bool keep) void LocalPlayer::stopAttack() { - mTarget = NULL; + setTarget(NULL); } Being* LocalPlayer::getTarget() const @@ -454,7 +476,7 @@ bool LocalPlayer::withinAttackRange(Being *target) void LocalPlayer::setGotoTarget(Being *target) { - mTarget = target; + setTarget(target); mGoingToTarget = true; setDestination(target->mX, target->mY); } diff --git a/src/localplayer.h b/src/localplayer.h index 5ce94081..493e0846 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -54,8 +54,9 @@ class LocalPlayer : public Player */ ~LocalPlayer(); + void setName(const std::string &name) {Being::setName(name); } void setNetwork(Network *network) { mNetwork = network; } - + Network *getNetwork() {return mNetwork; } virtual void logic(); /** @@ -64,12 +65,6 @@ class LocalPlayer : public Player */ virtual void nextStep(); - /** - * Draws the name text below the being. - */ - virtual void - drawName(Graphics *graphics, Sint32 offsetX, Sint32 offsetY) {}; - /** * Returns the player's inventory. */ @@ -140,7 +135,7 @@ class LocalPlayer : public Player /** * Sets the target being of the player. */ - void setTarget(Being* target) { mTarget = target; } + void setTarget(Being* target); /** * Sets a new destination for this being to walk to. diff --git a/src/monster.cpp b/src/monster.cpp index e2a07e86..bea37b3f 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -27,14 +27,21 @@ #include "game.h" #include "sound.h" #include "particle.h" +#include "text.h" +#include "localplayer.h" + +#include "gui/gui.h" #include "resources/monsterdb.h" #include "utils/tostring.h" +static const int NAME_X_OFFSET = 16; +static const int NAME_Y_OFFSET = 16; Monster::Monster(Uint32 id, Uint16 job, Map *map): - Being(id, job, map) + Being(id, job, map), + mText(0) { const MonsterInfo& info = MonsterDB::get(job - 1002); @@ -59,6 +66,14 @@ Monster::Monster(Uint32 id, Uint16 job, Map *map): } } +Monster::~Monster() +{ + if (mText) + { + player_node->setTarget(0); + } +} + void Monster::logic() { @@ -120,7 +135,8 @@ Monster::handleAttack(Being *victim, int damage) Being::handleAttack(victim, damage); const MonsterInfo &mi = getInfo(); - sound.playSfx(mi.getSound((damage > 0) ? MONSTER_EVENT_HIT : MONSTER_EVENT_MISS)); + sound.playSfx(mi.getSound((damage > 0) ? + MONSTER_EVENT_HIT : MONSTER_EVENT_MISS)); } void @@ -141,3 +157,28 @@ Monster::getInfo() const { return MonsterDB::get(mJob - 1002); } + +void Monster::showName(bool show) +{ + delete mText; + if (show) + { + mText = new Text(getInfo().getName(), mPx + NAME_X_OFFSET, + mPy + NAME_Y_OFFSET - getHeight(), + gcn::Graphics::CENTER, + speechFont, gcn::Color(255, 32, 32)); + } + else + { + mText = 0; + } +} + +void Monster::updateCoords() +{ + if (mText) + { + mText->adviseXY(mPx + NAME_X_OFFSET, + mPy + NAME_Y_OFFSET - getHeight()); + } +} diff --git a/src/monster.h b/src/monster.h index 39556b44..cd34886b 100644 --- a/src/monster.h +++ b/src/monster.h @@ -27,12 +27,15 @@ #include "being.h" class MonsterInfo; +class Text; class Monster : public Being { public: Monster(Uint32 id, Uint16 job, Map *map); + ~Monster(); + virtual void logic(); virtual void setAction(Uint8 action); @@ -63,6 +66,23 @@ class Monster : public Being */ const MonsterInfo& getInfo() const; + + /** + * Determine whether the mob should show it's name + */ + void showName(bool show); + + protected: + /** + * Update the text when the monster moves + */ + void updateCoords(); + + private: + /** + * holds a text object when the mod displays it's name, 0 otherwise + */ + Text *mText; }; #endif diff --git a/src/npc.cpp b/src/npc.cpp index 2177aedc..ab3c6970 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -26,6 +26,7 @@ #include "animatedsprite.h" #include "graphics.h" #include "particle.h" +#include "text.h" #include "net/messageout.h" #include "net/protocol.h" @@ -35,12 +36,15 @@ NPC *current_npc = 0; +static const int NAME_X_OFFSET = 15; +static const int NAME_Y_OFFSET = 30; + NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): Being(id, job, map), mNetwork(network) { NPCInfo info = NPCDB::get(job); - //setup NPC sprites + // Setup NPC sprites int c = BASE_SPRITE; for (std::list::const_iterator i = info.sprites.begin(); i != info.sprites.end(); @@ -54,7 +58,7 @@ NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): c++; } - //setup particle effects + // Setup particle effects for (std::list::const_iterator i = info.particles.begin(); i != info.particles.end(); i++) @@ -62,23 +66,26 @@ NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): Particle *p = particleEngine->addEffect(*i, 0, 0); this->controlParticle(p); } + mName = 0; } -Being::Type -NPC::getType() const +NPC::~NPC() { - return Being::NPC; + delete mName; } -void -NPC::drawName(Graphics *graphics, Sint32 offsetX, Sint32 offsetY) +void NPC::setName(const std::string &name) { - int px = mPx + offsetX; - int py = mPy + offsetY; + delete mName; + mName = new Text(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, speechFont, + gcn::Color(200, 200, 255)); +} - graphics->setFont(speechFont); - graphics->setColor(gcn::Color(200, 200, 255)); - graphics->drawText(mName, px + 15, py + 30, gcn::Graphics::CENTER); +Being::Type +NPC::getType() const +{ + return Being::NPC; } void @@ -129,3 +136,11 @@ NPC::sell() outMsg.writeInt32(mId); outMsg.writeInt8(1); } + +void NPC::updateCoords() +{ + if (mName) + { + mName->adviseXY(mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET); + } +} diff --git a/src/npc.h b/src/npc.h index 0d9966bd..2f9bbef4 100644 --- a/src/npc.h +++ b/src/npc.h @@ -28,18 +28,20 @@ class Network; class Graphics; +class Text; class NPC : public Being { public: NPC(Uint32 id, Uint16 job, Map *map, Network *network); + ~NPC(); + + void setName(const std::string &name); + virtual Type getType() const; - virtual void - drawName(Graphics *graphics, Sint32 offsetX, Sint32 offsetY); - void talk(); void nextDialog(); void dialogChoice(char choice); @@ -49,6 +51,9 @@ class NPC : public Being protected: Network *mNetwork; + void updateCoords(); + private: + Text *mName; }; extern NPC *current_npc; diff --git a/src/player.cpp b/src/player.cpp index d0c6bdc6..f86e7179 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -34,11 +34,34 @@ #include "utils/strprintf.h" #include "gui/gui.h" +#include + +static const int NAME_X_OFFSET = 15; +static const int NAME_Y_OFFSET = 30; Player::Player(int id, int job, Map *map): - Being(id, job, map), - mDrawStrategy(NULL) + Being(id, job, map) { + mName = 0; +} + +Player::~Player() +{ + if (mName) + { + delete mName; + } +} + +void Player::setName(const std::string &name) +{ + if (mName == 0) + { + mName = new FlashText(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, + speechFont, gcn::Color(255, 255, 255)); + Being::setName(name); + } } void @@ -75,37 +98,13 @@ Player::getType() const return PLAYER; } - void -Player::setNameDrawStrategy(PlayerNameDrawStrategy *draw_strategy) +Player::flash(int time) { - if (mDrawStrategy) - delete mDrawStrategy; - mDrawStrategy = draw_strategy; -} - -class -DefaultPlayerNameDrawStrategy : public PlayerNameDrawStrategy -{ -public: - virtual void draw(Player *player, Graphics *graphics, int px, int py) + if (mName) { - graphics->setFont(speechFont); - graphics->setColor(gcn::Color(255, 255, 255)); - graphics->drawText(player->getName(), px + 15, py + 30, gcn::Graphics::CENTER); + mName->flash(time); } -}; - -void -Player::drawName(Graphics *graphics, int offsetX, int offsetY) -{ - int px = mPx + offsetX; - int py = mPy + offsetY; - - if (mDrawStrategy) - mDrawStrategy->draw(this, graphics, px, py); - else - DefaultPlayerNameDrawStrategy().draw(this, graphics, px, py); } void Player::setGender(int gender) @@ -202,3 +201,11 @@ void Player::setSprite(int slot, int id, std::string color) Being::setSprite(slot, id, color); } + +void Player::updateCoords() +{ + if (mName) + { + mName->adviseXY(mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET); + } +} diff --git a/src/player.h b/src/player.h index fe24ceed..6347b0a6 100644 --- a/src/player.h +++ b/src/player.h @@ -25,23 +25,11 @@ #define _TMW_PLAYER_H #include "being.h" +#include "text.h" class Graphics; class Map; -class Player; - -class PlayerNameDrawStrategy -{ -public: - virtual ~PlayerNameDrawStrategy(void) {} - - /** - * Draw the player's name - */ - virtual void draw(Player *p, Graphics *graphics, int px, int py) = 0; -}; - /** * A player being. Players have their name drawn beneath them. This class also * implements player-specific loading of base sprite, hair sprite and equipment @@ -52,15 +40,20 @@ class Player : public Being public: Player(int id, int job, Map *map); + ~Player(); + + /** + * Set up mName to be the character's name + */ + virtual void + setName(const std::string &name); + virtual void logic(); virtual Type getType() const; - virtual void - drawName(Graphics *graphics, int offsetX, int offsetY); - virtual void setGender(int gender); @@ -83,27 +76,15 @@ class Player : public Being setSprite(int slot, int id, std::string color = ""); /** - * Sets the strategy responsible for drawing the player's name - * - * \param draw_strategy A strategy describing how the player's name - * should be drawn, or NULL for default + * Flash the player's name */ - virtual void - setNameDrawStrategy(PlayerNameDrawStrategy *draw_strategy); - - virtual PlayerNameDrawStrategy * - getNameDrawStrategy(void) const { return mDrawStrategy; } + void flash(int time); - /** - * Triggers a visual/audio effect, such as `level up' - * - * \param effect_id ID of the effect to trigger - */ - virtual void - triggerEffect(int effectId) { internalTriggerEffect(effectId, true, true); } + protected: + void updateCoords(); private: - PlayerNameDrawStrategy *mDrawStrategy; + FlashText *mName; }; #endif diff --git a/src/player_relations.cpp b/src/player_relations.cpp index 610f5d04..c494dc74 100644 --- a/src/player_relations.cpp +++ b/src/player_relations.cpp @@ -88,7 +88,7 @@ PlayerRelationsManager::PlayerRelationsManager() : mDefaultPermissions(PlayerRelation::DEFAULT), mIgnoreStrategy(NULL) { -} +} void PlayerRelationsManager::clear() @@ -325,29 +325,6 @@ public: }; -class -BlinkPlayerNameDrawStrategy : public PlayerNameDrawStrategy -{ -public: - BlinkPlayerNameDrawStrategy(int count) : - mCount(count) - { - } - - virtual void draw(Player *player, Graphics *graphics, int px, int py) - { - graphics->setFont(speechFont); - if (mCount & 4) - graphics->drawText(player->getName(), px + 15, py + 30, gcn::Graphics::CENTER); - - if (mCount-- <= 0) - player->setNameDrawStrategy(NULL); - } -private: - int mCount; // Number of steps to blink -}; - - class PIS_blinkname : public PlayerIgnoreStrategy { public: @@ -359,9 +336,9 @@ public: virtual void ignore(Player *player, unsigned int flags) - { - player->setNameDrawStrategy(new BlinkPlayerNameDrawStrategy(200)); - } + { + player->flash(200); + } }; class PIS_emote : public PlayerIgnoreStrategy @@ -407,4 +384,3 @@ PlayerRelationsManager::getPlayerIgnoreStrategies() PlayerRelationsManager player_relations; - diff --git a/src/text.cpp b/src/text.cpp new file mode 100644 index 00000000..4212c5c8 --- /dev/null +++ b/src/text.cpp @@ -0,0 +1,104 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "text.h" + +#include + +#include + +#include "textmanager.h" + +int Text::mInstances = 0; + + +Text::Text(const std::string &text, int x, int y, + gcn::Graphics::Alignment alignment, gcn::Font *font, + gcn::Color colour) : + mText(text), mColour(colour) +{ + if (textManager == 0) + { + textManager = new TextManager(); + } + ++mInstances; + mHeight = font->getHeight(); + mWidth = font->getWidth(text); + switch (alignment) + { + case gcn::Graphics::LEFT: + mXOffset = 0; + break; + case gcn::Graphics::CENTER: + mXOffset = mWidth / 2; + break; + case gcn::Graphics::RIGHT: + mXOffset = mWidth; + break; + } + mX = x - mXOffset; + mY = y; + textManager->addText(this); + mFont = font; +} + +void Text::adviseXY(int x, int y) +{ + textManager->moveText(this, x - mXOffset, y); +} + +Text::~Text() +{ + textManager->removeText(this); + if (--mInstances == 0) + { + delete textManager; + textManager = 0; + } +} + +void Text::draw(Graphics *graphics, int xOff, int yOff) +{ + graphics->setFont(mFont); + graphics->setColor(mColour); + graphics->drawText(mText, mX - xOff, mY - yOff, gcn::Graphics::LEFT); +} + +FlashText::FlashText(const std::string &text, int x, int y, + gcn::Graphics::Alignment alignment, gcn::Font *font, + gcn::Color colour) : + Text(text, x, y, alignment, font, colour), mTime(0) +{ +} + +void FlashText::draw(Graphics *graphics, int xOff, int yOff) +{ + if (mTime) + { + if ((--mTime & 4) == 0) + { + return; + } + } + Text::draw(graphics, xOff, yOff); +} diff --git a/src/text.h b/src/text.h new file mode 100644 index 00000000..7ea96dee --- /dev/null +++ b/src/text.h @@ -0,0 +1,96 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_TEXT_H +#define _TMW_TEXT_H + +#include "graphics.h" + +#include + +class TextManager; + +class Text +{ + friend class TextManager; + public: + /** + * Constructor creates a text object to display on the screen + */ + Text(const std::string &text, int x, int y, + gcn::Graphics::Alignment alignment, gcn::Font *font, + gcn::Color colour); + + /** + * Allows the originator of the text to specify the ideal coordinates + */ + void + adviseXY(int x, int y); + + /** + * Remove the text from the screen + */ + ~Text(); + + /** + * Draws the text + */ + virtual void + draw(Graphics *graphics, int xOff, int yOff); + + private: + + int mX; /**< Actual x-value of left of text written */ + int mY; /**< Actual y-value of top of text written */ + int mWidth; /**< The width of the text */ + int mHeight; /**< The height of the text */ + int mXOffset; /**< The offset of mX from the desired x */ + static int mInstances; /**< Instances of text */ + gcn::Font *mFont; /**< The font used */ + std::string mText; /**< The text to display */ + gcn::Color mColour; /**< The colour of the text */ +}; + +class FlashText : public Text +{ + public: + FlashText(const std::string &text, int x, int y, + gcn::Graphics::Alignment alignment, gcn::Font *font, + gcn::Color colour); + + /** + * Flash the text for so many refreshes + */ + void flash(int time) {mTime = time; } + + /** + * Draws the text + */ + virtual void + draw(Graphics *graphics, int xOff, int yOff); + + private: + int mTime; /**< Time left for flashing */ +}; + +#endif // _TMW_TEXT_H diff --git a/src/textmanager.cpp b/src/textmanager.cpp new file mode 100644 index 00000000..b4135763 --- /dev/null +++ b/src/textmanager.cpp @@ -0,0 +1,179 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "textmanager.h" + +#include + +#include "text.h" + +TextManager *textManager = 0; + +TextManager::TextManager() +{ +} + +void TextManager::addText(Text *text) +{ + place(text, 0, text->mX, text->mY, text->mHeight); + mTextList.push_back(text); +} + +void TextManager::moveText(Text *text, int x, int y) +{ + text->mX = x; + text->mY = y; + place(text, text, text->mX, text->mY, text->mHeight); +} + +void TextManager::removeText(const Text *text) +{ + for (TextList::iterator ptr = mTextList.begin(), + pEnd = mTextList.end(); ptr != pEnd; ++ptr) + { + if (*ptr == text) + { + mTextList.erase(ptr); + return; + } + } +} + +TextManager::~TextManager() +{ +} + +void TextManager::draw(Graphics *graphics, int xOff, int yOff) +{ + for (TextList::iterator bPtr = mTextList.begin(), ePtr = mTextList.end(); + bPtr != ePtr; ++bPtr) + { + (*bPtr)->draw(graphics, xOff, yOff); + } +} + +void TextManager::place(const Text *textObj, const Text *omit, + int &x, int &y, int h) +{ + int xLeft = textObj->mX; + int xRight = xLeft + textObj->mWidth - 1; + const int TEST = 100; // Number of lines to test for text + bool occupied[TEST]; // is some other text obscuring this line? + std::memset(&occupied, 0, sizeof(occupied)); // set all to false + int wantedTop = (TEST - h) / 2; // Entry in occupied at top of text + int occupiedTop = y - wantedTop; // Line in map representing to of occupied + + for (TextList::const_iterator ptr = mTextList.begin(), + pEnd = mTextList.end(); ptr != pEnd; ++ptr) + { + if (*ptr != omit && + (*ptr)->mX <= xRight && + (*ptr)->mX + (*ptr)->mWidth > xLeft) + { + int from = (*ptr)->mY - occupiedTop; + int to = from + (*ptr)->mHeight - 1; + if (to < 0 || from >= TEST) // out of range considered + { + continue; + } + if (from < 0) + { + from = 0; + } + if (to >= TEST) + { + to = TEST - 1; + } + for (int i = from; i <= to; ++i) + { + occupied[i] = true; + } + } + } + bool ok = true; + for (int i = wantedTop; i < wantedTop + h; ++i) + { + ok = ok && !occupied[i]; + } + if (ok) + { + return; + } + // Have to move it up or down, so find nearest spaces either side + int consec = 0; + int upSlot = -1; // means not found + for (int seek = wantedTop + h - 2; seek >= 0; --seek) + { + if (occupied[seek]) + { + consec = 0; + } + else + { + if (++consec == h) + { + upSlot = seek; + break; + } + } + } + int downSlot = -1; + consec = 0; + for (int seek = wantedTop + 1; seek < TEST; ++seek) + { + if (occupied[seek]) + { + consec = 0; + } + else + { + if (++consec == h) + { + downSlot = seek - h + 1; + break; + } + } + } + if (upSlot == -1 && downSlot == -1) // no good solution, so leave as is + { + return; + } + if (upSlot == -1) // must go down + { + y += downSlot - wantedTop; + return; + } + if (downSlot == -1) // must go up + { + y -= wantedTop - upSlot; + return; + } + if (wantedTop - upSlot > downSlot - wantedTop) // down is better + { + y += downSlot - wantedTop; + } + else + { + y -= wantedTop - upSlot; + } +} diff --git a/src/textmanager.h b/src/textmanager.h new file mode 100644 index 00000000..f7f1247c --- /dev/null +++ b/src/textmanager.h @@ -0,0 +1,78 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_TEXTMANAGER_H +#define _TMW_TEXTMANAGER_H + +#include + +class Text; +class Graphics; + +class TextManager +{ + public: + /** + * Constructor + */ + TextManager(); + + /** + * Add text to the manager + */ + void addText(Text *text); + + /** + * Move the text around the screen + */ + void moveText(Text *text, int x, int y); + + /** + * Remove the text from the manager + */ + void removeText(const Text *text); + + /** + * Destroy the manager + */ + ~TextManager(); + + /** + * Draw the text + */ + void draw(Graphics *graphics, int xOff, int yOff); + + private: + /** + * Position the text so as to avoid conflict + */ + void place(const Text *textObj, const Text *omit, + int &x, int &y, int h); + + typedef std::list TextList; /**< The container type */ + TextList mTextList; /**< The container */ +}; + +extern TextManager *textManager; + +#endif // _TMW_TEXTMANAGER_H -- cgit v1.2.3-70-g09d2