summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/being.cpp50
-rw-r--r--src/being.h28
-rw-r--r--src/gui/viewport.cpp28
-rw-r--r--src/localplayer.cpp28
-rw-r--r--src/localplayer.h11
-rw-r--r--src/monster.cpp45
-rw-r--r--src/monster.h20
-rw-r--r--src/npc.cpp39
-rw-r--r--src/npc.h11
-rw-r--r--src/player.cpp65
-rw-r--r--src/player.h47
-rw-r--r--src/player_relations.cpp32
-rw-r--r--src/text.cpp104
-rw-r--r--src/text.h96
-rw-r--r--src/textmanager.cpp179
-rw-r--r--src/textmanager.h78
17 files changed, 692 insertions, 173 deletions
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; }
/**
@@ -239,24 +240,12 @@ class Being : public Sprite
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.
*/
virtual Type getType() const;
@@ -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'
@@ -384,6 +377,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.
*/
SpriteDirection getSpriteDirection() const;
@@ -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);
}
@@ -330,24 +334,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<Monster*>(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)
{
// Check if we are alive and kickin'
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<Monster *>(mTarget)->showName(false);
+ }
+ mTarget = target;
+ if (target && target->getType() == Being::MONSTER)
+ {
+ static_cast<Monster *>(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();
/**
@@ -65,12 +66,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.
*/
Inventory* getInventory() const { return mInventory; }
@@ -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<NPCsprite*>::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<std::string>::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 <iostream>
+
+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,6 +40,14 @@ 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();
@@ -59,9 +55,6 @@ class Player : public Being
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 <cstring>
+
+#include <guichan/font.hpp>
+
+#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 <list>
+
+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 <cstring>
+
+#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 <list>
+
+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<Text *> TextList; /**< The container type */
+ TextList mTextList; /**< The container */
+};
+
+extern TextManager *textManager;
+
+#endif // _TMW_TEXTMANAGER_H