summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBjørn Lindeijer <bjorn@lindeijer.nl>2007-06-04 21:48:47 +0000
committerBjørn Lindeijer <bjorn@lindeijer.nl>2007-06-04 21:48:47 +0000
commita353543dd4da3c489a84f6f17125fdd0e1be2349 (patch)
tree5e447a0f9a3fd50f58a48f39bbb77d392d78af36 /src
parentab072dddb231895ba7b6762eda9fa70af961b0fb (diff)
downloadmana-a353543dd4da3c489a84f6f17125fdd0e1be2349.tar.gz
mana-a353543dd4da3c489a84f6f17125fdd0e1be2349.tar.bz2
mana-a353543dd4da3c489a84f6f17125fdd0e1be2349.tar.xz
mana-a353543dd4da3c489a84f6f17125fdd0e1be2349.zip
Merged 0.0 changes from revision 3234 to 3317 to trunk.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt14
-rw-r--r--src/Makefile.am16
-rw-r--r--src/animatedsprite.cpp24
-rw-r--r--src/animatedsprite.h12
-rw-r--r--src/animationparticle.cpp51
-rw-r--r--src/animationparticle.h49
-rw-r--r--src/being.cpp122
-rw-r--r--src/being.h36
-rw-r--r--src/engine.cpp7
-rw-r--r--src/game.cpp24
-rw-r--r--src/graphics.cpp2
-rw-r--r--src/gui/buy.cpp117
-rw-r--r--src/gui/buy.h6
-rw-r--r--src/gui/chat.cpp4
-rw-r--r--src/gui/chat.h4
-rw-r--r--src/gui/debugwindow.cpp10
-rw-r--r--src/gui/debugwindow.h1
-rw-r--r--src/gui/inventorywindow.cpp7
-rw-r--r--src/gui/sell.cpp159
-rw-r--r--src/gui/sell.h6
-rw-r--r--src/gui/updatewindow.cpp7
-rw-r--r--src/gui/viewport.cpp108
-rw-r--r--src/gui/viewport.h20
-rw-r--r--src/gui/widgets/resizegrip.cpp65
-rw-r--r--src/gui/widgets/resizegrip.h61
-rw-r--r--src/gui/window.cpp185
-rw-r--r--src/gui/window.h66
-rw-r--r--src/imageparticle.cpp69
-rw-r--r--src/imageparticle.h61
-rw-r--r--src/main.cpp26
-rw-r--r--src/map.cpp23
-rw-r--r--src/map.h25
-rw-r--r--src/monster.cpp6
-rw-r--r--src/monster.h3
-rw-r--r--src/net/beinghandler.cpp17
-rw-r--r--src/openglgraphics.cpp24
-rw-r--r--src/particle.cpp370
-rw-r--r--src/particle.h288
-rw-r--r--src/particleemitter.cpp327
-rw-r--r--src/particleemitter.h120
-rw-r--r--src/resources/animation.h2
-rw-r--r--src/resources/image.cpp25
-rw-r--r--src/resources/mapreader.cpp24
-rw-r--r--src/resources/monsterdb.cpp21
-rw-r--r--src/resources/monsterinfo.h14
-rw-r--r--src/simpleanimation.cpp85
-rw-r--r--src/simpleanimation.h15
-rw-r--r--src/sprite.h16
-rw-r--r--src/textparticle.cpp64
-rw-r--r--src/textparticle.h53
-rw-r--r--src/utils/fastsqrt.h24
-rw-r--r--src/utils/minmax.h47
-rw-r--r--src/utils/wingettimeofday.h226
-rw-r--r--src/utils/xml.cpp23
-rw-r--r--src/utils/xml.h11
55 files changed, 2696 insertions, 496 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e28e235e..7a4da70d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -47,6 +47,8 @@ MARK_AS_ADVANCED(SDL_INCLUDE_DIR)
MARK_AS_ADVANCED(SDL_LIBRARY)
SET(SRCS
+ gui/widgets/resizegrip.cpp
+ gui/widgets/resizegrip.h
gui/box.cpp
gui/box.h
gui/browserbox.cpp
@@ -275,11 +277,15 @@ SET(SRCS
utils/base64.cpp
utils/base64.h
utils/dtor.h
+ utils/fastsqrt.h
+ utils/minmax.h
utils/tostring.h
utils/xml.cpp
utils/xml.h
animatedsprite.cpp
animatedsprite.h
+ animationparticle.cpp
+ animationparticle.h
being.cpp
being.h
beingmanager.cpp
@@ -304,6 +310,8 @@ SET(SRCS
graphics.cpp
graphics.h
guichanfwd.h
+ imageparticle.cpp
+ imageparticle.h
inventory.cpp
inventory.h
item.cpp
@@ -326,6 +334,10 @@ SET(SRCS
npc.h
openglgraphics.cpp
openglgraphics.h
+ particle.cpp
+ particle.h
+ particleemitter.cpp
+ particleemitter.h
player.cpp
player.h
properties.h
@@ -335,6 +347,8 @@ SET(SRCS
sound.cpp
sound.h
sprite.h
+ textparticle.cpp
+ textparticle.h
tileset.h
)
diff --git a/src/Makefile.am b/src/Makefile.am
index f1f51b79..92c1b1b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,6 +3,8 @@ AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = tmw
tmw_SOURCES = gui/widgets/dropdown.cpp \
gui/widgets/dropdown.h \
+ gui/widgets/resizegrip.cpp \
+ gui/widgets/resizegrip.h \
gui/browserbox.cpp \
gui/browserbox.h \
gui/buddywindow.cpp \
@@ -229,11 +231,15 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \
utils/base64.cpp \
utils/base64.h \
utils/dtor.h \
+ utils/fastsqrt.h \
+ utils/minmax.h \
utils/tostring.h \
utils/xml.cpp \
utils/xml.h \
animatedsprite.cpp \
animatedsprite.h \
+ animationparticle.cpp \
+ animationparticle.h \
being.cpp \
being.h \
beingmanager.cpp \
@@ -258,6 +264,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \
graphics.cpp \
graphics.h \
guichanfwd.h \
+ imageparticle.cpp \
+ imageparticle.h \
inventory.cpp \
inventory.h \
item.cpp \
@@ -280,15 +288,21 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \
npc.h \
openglgraphics.cpp\
openglgraphics.h \
+ particle.cpp \
+ particle.h \
+ particleemitter.cpp \
+ particleemitter.h \
player.cpp \
player.h \
properties.h \
serverinfo.h \
simpleanimation.cpp \
- simpleanimation.h \
+ simpleanimation.h \
sound.cpp \
sound.h \
sprite.h \
+ textparticle.cpp \
+ textparticle.h \
tileset.h
# set the include path found by configure
diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp
index 7260a512..c1e89ff0 100644
--- a/src/animatedsprite.cpp
+++ b/src/animatedsprite.cpp
@@ -197,3 +197,27 @@ AnimatedSprite::setDirection(SpriteDirection direction)
}
}
}
+
+int
+AnimatedSprite::getWidth() const
+{
+ if (mFrame)
+ {
+ return mFrame->image->getWidth();
+ }
+ else {
+ return 0;
+ }
+}
+
+int
+AnimatedSprite::getHeight() const
+{
+ if (mFrame)
+ {
+ return mFrame->image->getHeight();
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/src/animatedsprite.h b/src/animatedsprite.h
index 2257c0f0..42c0a743 100644
--- a/src/animatedsprite.h
+++ b/src/animatedsprite.h
@@ -86,6 +86,18 @@ class AnimatedSprite
draw(Graphics* graphics, int posX, int posY) const;
/**
+ * gets the width in pixels of the image of the current frame
+ */
+ int
+ getWidth() const;
+
+ /**
+ * gets the height in pixels of the image of the current frame
+ */
+ int
+ getHeight() const;
+
+ /**
* Sets the direction.
*/
void
diff --git a/src/animationparticle.cpp b/src/animationparticle.cpp
new file mode 100644
index 00000000..30c33da7
--- /dev/null
+++ b/src/animationparticle.cpp
@@ -0,0 +1,51 @@
+/*
+ * The Mana World
+ * Copyright 2006 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
+ *
+ */
+
+#include "animationparticle.h"
+
+#include "graphics.h"
+#include "simpleanimation.h"
+
+AnimationParticle::AnimationParticle(Map *map, Animation *animation):
+ ImageParticle(map, 0),
+ mAnimation(new SimpleAnimation(animation))
+{
+}
+
+AnimationParticle::AnimationParticle(Map *map, xmlNodePtr animationNode):
+ ImageParticle(map, 0),
+ mAnimation(new SimpleAnimation(animationNode))
+{
+}
+
+AnimationParticle::~AnimationParticle()
+{
+ delete mAnimation;
+}
+
+bool AnimationParticle::update()
+{
+ mAnimation->update(10); // particle engine is updated every 10ms
+ mImage = mAnimation->getCurrentImage();
+
+ return Particle::update();
+}
diff --git a/src/animationparticle.h b/src/animationparticle.h
new file mode 100644
index 00000000..054b1b73
--- /dev/null
+++ b/src/animationparticle.h
@@ -0,0 +1,49 @@
+/*
+ * The Mana World
+ * Copyright 2006 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
+ *
+ */
+
+#ifndef _ANIMATION_PARTICLE
+#define _ANIMATION_PARTICLE
+
+#include <libxml/tree.h>
+
+#include "imageparticle.h"
+
+class Animation;
+class Map;
+class SimpleAnimation;
+
+class AnimationParticle : public ImageParticle
+{
+ public:
+ AnimationParticle(Map *map, Animation *animation);
+
+ AnimationParticle(Map *map, xmlNodePtr animationNode);
+
+ ~AnimationParticle();
+
+ virtual bool update();
+
+ private:
+ SimpleAnimation *mAnimation; /**< Used animation for this particle */
+};
+
+#endif
diff --git a/src/being.cpp b/src/being.cpp
index 682daddb..7ba9ddb9 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -32,12 +32,16 @@
#include "graphics.h"
#include "log.h"
#include "map.h"
+#include "particle.h"
#include "resources/resourcemanager.h"
#include "resources/imageset.h"
#include "gui/gui.h"
+#include "resources/resourcemanager.h"
+#include "resources/imageset.h"
+
#include "utils/dtor.h"
#include "utils/tostring.h"
@@ -61,7 +65,6 @@ Being::Being(Uint16 id, Uint16 job, Map *map):
mMap(NULL),
mHairStyle(0), mHairColor(0),
mSpeechTime(0),
- mDamageTime(0),
mPx(0), mPy(0),
mSprites(VECTOREND_SPRITE, NULL),
mEquipmentSpriteIDs(VECTOREND_SPRITE, 0)
@@ -85,6 +88,13 @@ Being::~Being()
clearPath();
setMap(NULL);
+ for ( std::list<Particle *>::iterator i = mChildParticleEffects.begin();
+ i != mChildParticleEffects.end();
+ i++)
+ {
+ (*i)->kill();
+ }
+
instances--;
if (instances == 0)
@@ -292,8 +302,32 @@ Being::setSpeech(const std::string &text, Uint32 time)
void
Being::takeDamage(int amount)
{
- mDamage = amount ? toString(amount) : "miss";
- mDamageTime = 300;
+ gcn::Font* font;
+ std::string damage = amount ? toString(amount) : "miss";
+
+ // Selecting the right color
+ if (damage == "miss")
+ {
+ font = hitYellowFont;
+ }
+ else
+ {
+ // hit particle effect
+ controlParticle(particleEngine->addEffect("graphics/particles/hit.particle.xml", 0, 0));
+
+ if (getType() == MONSTER)
+ {
+ font = hitBlueFont;
+ }
+ else
+ {
+ font = hitRedFont;
+ }
+ }
+
+ // show damage number
+ particleEngine->addTextSplashEffect(damage, 255, 255, 255, font,
+ mPx + 16, mPy + 16);
}
void
@@ -318,6 +352,19 @@ Being::setMap(Map *map)
{
mSpriteIterator = mMap->addSprite(this);
}
+
+ //clear particle effect list because child particles became invalid
+ mChildParticleEffects.clear();
+}
+
+void
+Being::controlParticle(Particle *particle)
+{
+ if (particle)
+ {
+ particle->disableAutoDelete(); //the effect may not die without the beings permission or we segvault
+ mChildParticleEffects.push_back(particle);
+ }
}
void
@@ -465,10 +512,6 @@ Being::logic()
if (mSpeechTime > 0)
mSpeechTime--;
- // Reduce the time that damage is still displayed
- if (mDamageTime > 0)
- mDamageTime--;
-
// Update pixel coordinates
mPx = mX - 16 + getXOffset();
mPy = mY - 16 + getYOffset();
@@ -489,6 +532,14 @@ Being::logic()
mSprites[i]->update(tick_time * 10);
}
}
+
+ //Update particle effects
+ for ( std::list<Particle *>::iterator i = mChildParticleEffects.begin();
+ i != mChildParticleEffects.end();
+ i++)
+ {
+ (*i)->setPosition((float)mPx + 16.0f, (float)mPy + 32.0f);
+ }
}
void
@@ -531,37 +582,6 @@ Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY)
graphics->setColor(gcn::Color(255, 255, 255));
graphics->drawText(mSpeech, px + 18, py - 60, gcn::Graphics::CENTER);
}
-
- // Draw damage above this being
- if (mDamageTime > 0 && mDamageTime < 275)
- {
- // Selecting the right color
- if (mDamage == "miss")
- {
- graphics->setFont(hitYellowFont);
- }
- else if (getType() == MONSTER)
- {
- graphics->setFont(hitBlueFont);
- }
- else
- {
- graphics->setFont(hitRedFont);
- }
-
- int textY = (getType() == MONSTER) ? 32 : 70;
- int ft = 150 - mDamageTime;
- float a = (ft > 0) ? 1.0 - ft / 150.0 : 1.0;
-
- graphics->setColor(gcn::Color(255, 255, 255, (int)(255 * a)));
- graphics->drawText(mDamage,
- px + 16,
- py - textY - (300 - mDamageTime) / 10,
- gcn::Graphics::CENTER);
-
- // Reset alpha value
- graphics->setColor(gcn::Color(255, 255, 255));
- }
}
Being::Type
@@ -632,3 +652,29 @@ int Being::getOffset(int step) const
return offset;
}
+
+
+int
+Being::getWidth() const
+{
+ if (mSprites[BASE_SPRITE])
+ {
+ return mSprites[BASE_SPRITE]->getWidth();
+ }
+ else {
+ return 0;
+ }
+}
+
+
+int
+Being::getHeight() const
+{
+ if (mSprites[BASE_SPRITE])
+ {
+ return mSprites[BASE_SPRITE]->getHeight();
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/src/being.h b/src/being.h
index 332f1b4a..8820c30a 100644
--- a/src/being.h
+++ b/src/being.h
@@ -42,6 +42,7 @@ class Item;
class Map;
class Graphics;
class ImageSet;
+class Particle;
/**
* A position along a being's path.
@@ -97,6 +98,13 @@ class Being : public Sprite
VECTOREND_SPRITE
};
+ enum TargetCursorSize {
+ TC_SMALL = 0,
+ TC_MEDIUM,
+ TC_LARGE,
+ NUM_TC
+ };
+
/**
* Directions, to be used as bitmask values
@@ -359,8 +367,33 @@ class Being : public Sprite
int
getYOffset() const { return getOffset(mStepY); }
+ /**
+ * Returns the horizontal size of the current base sprite of the being
+ */
+ virtual int
+ getWidth() const;
+
+ /**
+ * Returns the vertical size of the current base sprite of the being
+ */
+ virtual int
+ getHeight() const;
+
+ /**
+ * Returns the required size of a target cursor for this being
+ */
+ virtual Being::TargetCursorSize
+ getTargetCursorSize() const
+ { return TC_MEDIUM; }
+
std::auto_ptr<Equipment> mEquipment;
+ /**
+ * Take control of a particle
+ */
+ void
+ controlParticle(Particle *particle);
+
protected:
/**
* Sets the new path for this being.
@@ -380,14 +413,13 @@ class Being : public Sprite
Path mPath;
std::string mSpeech;
- std::string mDamage;
Uint16 mHairStyle, mHairColor;
Uint32 mSpeechTime;
- Uint32 mDamageTime;
Sint32 mPx, mPy; /**< Pixel coordinates */
std::vector<AnimatedSprite*> mSprites;
std::vector<int> mEquipmentSpriteIDs;
+ std::list<Particle *> mChildParticleEffects;
private:
int
diff --git a/src/engine.cpp b/src/engine.cpp
index 31bec9ff..3b5fb403 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -35,6 +35,7 @@
#include "localplayer.h"
#include "log.h"
#include "map.h"
+#include "particle.h"
#include "sound.h"
#include "gui/gui.h"
@@ -63,6 +64,7 @@ void Engine::changeMap(const std::string &mapPath)
floorItemManager->clear();
beingManager->clear();
+ particleEngine->clear();
// Store full map path in global var
map_path = "maps/" + mapPath;
@@ -83,8 +85,12 @@ void Engine::changeMap(const std::string &mapPath)
}
minimap->setMapImage(mapImage);
beingManager->setMap(newMap);
+ particleEngine->setMap(newMap);
viewport->setMap(newMap);
+ // Initialize map-based particle effects
+ newMap->initializeParticleEffects(particleEngine);
+
// Start playing new music file when necessary
std::string oldMusic = "";
@@ -106,5 +112,6 @@ void Engine::changeMap(const std::string &mapPath)
void Engine::logic()
{
beingManager->logic();
+ particleEngine->update();
gui->logic();
}
diff --git a/src/game.cpp b/src/game.cpp
index ba4e2b9d..1afc530b 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -41,6 +41,7 @@
#include "localplayer.h"
#include "log.h"
#include "npc.h"
+#include "particle.h"
#include "gui/buy.h"
#include "gui/buysell.h"
@@ -121,6 +122,8 @@ BeingManager *beingManager = NULL;
FloorItemManager *floorItemManager = NULL;
ChannelManager *channelManager = NULL;
+Particle *particleEngine = NULL;
+
const int MAX_TIME = 10000;
/**
@@ -242,6 +245,9 @@ Game::Game():
floorItemManager = new FloorItemManager();
channelManager = new ChannelManager();
+ particleEngine = new Particle(NULL);
+ particleEngine->setupEngine();
+
// Initialize timers
tick_time = 0;
mLogicCounterId = SDL_AddTimer(10, nextTick, NULL); //Logic counter
@@ -286,6 +292,7 @@ Game::~Game()
delete floorItemManager;
delete channelManager;
delete joystick;
+ delete particleEngine;
beingManager = NULL;
floorItemManager = NULL;
@@ -308,7 +315,9 @@ bool saveScreenshot(SDL_Surface *screenshot)
screenshotCount++;
filename.str("");
#if (defined __USE_UNIX98 || defined __FreeBSD__ || defined __APPLE__)
- filename << PHYSFS_getUserDir() << "/";
+ filename << PHYSFS_getUserDir() << ".tmw/";
+#elif defined __APPLE__
+ filename << PHYSFS_getUserDir() << "Desktop/";
#endif
filename << "TMW_Screenshot_" << screenshotCount << ".png";
testExists.open(filename.str().c_str(), std::ios::in);
@@ -316,7 +325,18 @@ bool saveScreenshot(SDL_Surface *screenshot)
testExists.close();
} while (!found);
- return ImageWriter::writePNG(screenshot, filename.str());
+ if (ImageWriter::writePNG(screenshot, filename.str()))
+ {
+ std::stringstream chatlogentry;
+ chatlogentry << "Screenshot saved to " << filename.str().c_str();
+ chatWindow->chatLog(chatlogentry.str(), BY_SERVER);
+ return true;
+ }
+ else
+ {
+ chatWindow->chatLog("Saving screenshot failed!", BY_SERVER);
+ return false;
+ }
}
void Game::optionChanged(const std::string &name)
diff --git a/src/graphics.cpp b/src/graphics.cpp
index f007470a..1e31f903 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -136,8 +136,6 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY,
srcX += image->mBounds.x;
srcY += image->mBounds.y;
-
-
SDL_Rect dstRect;
SDL_Rect srcRect;
dstRect.x = dstX; dstRect.y = dstY;
diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp
index cb07da22..2a8616f8 100644
--- a/src/gui/buy.cpp
+++ b/src/gui/buy.cpp
@@ -111,27 +111,21 @@ void BuyDialog::setMoney(int amount)
{
mMoney = amount;
mShopItemList->setPlayersMoney(amount);
- mMoneyLabel->setCaption("Price: 0 GP / " + toString(mMoney) + " GP");
- mMoneyLabel->adjustSize();
+
+ updateButtonsAndLabels();
}
void BuyDialog::reset()
{
mShopItems->clear();
+ mShopItemList->adjustSize();
mMoney = 0;
mSlider->setValue(0.0);
- mAmountItems = 0;
// Reset Previous Selected Items to prevent failing asserts
mShopItemList->setSelected(-1);
- mIncreaseButton->setEnabled(false);
- mDecreaseButton->setEnabled(false);
- mQuantityLabel->setCaption("0");
- mQuantityLabel->adjustSize();
- mMoneyLabel->setCaption("Price: 0 GP / " + toString(mMoney) + " GP");
- mMoneyLabel->adjustSize();
- mItemDescLabel->setCaption("");
- mItemEffectLabel->setCaption("");
+
+ updateButtonsAndLabels();
}
void BuyDialog::addItem(short id, int price)
@@ -151,45 +145,36 @@ void BuyDialog::action(const gcn::ActionEvent &event)
}
// The following actions require a valid selection
- if (selectedItem < 0 || selectedItem >= int(mShopItems->getNumberOfElements()))
+ if (selectedItem < 0 ||
+ selectedItem >= (int) mShopItems->getNumberOfElements())
{
return;
}
- bool updateButtonsAndLabels = false;
-
if (event.getId() == "slider")
{
mAmountItems = (int)(mSlider->getValue() * mMaxItems);
- updateButtonsAndLabels = true;
+ updateButtonsAndLabels();
}
- else if (event.getId() == "+")
+ else if (event.getId() == "+" && mAmountItems < mMaxItems)
{
- if (mAmountItems < mMaxItems) {
- mAmountItems++;
- } else {
- mAmountItems = mMaxItems;
- }
+ mAmountItems++;
- mSlider->setValue(double(mAmountItems)/double(mMaxItems));
- updateButtonsAndLabels = true;
+ mSlider->setValue((double) mAmountItems / (double) mMaxItems);
+ updateButtonsAndLabels();
}
- else if (event.getId() == "-")
+ else if (event.getId() == "-" && mAmountItems > 0)
{
- if (mAmountItems > 0) {
- mAmountItems--;
- } else {
- mAmountItems = 0;
- }
+ mAmountItems--;
- mSlider->setValue(double(mAmountItems)/double(mMaxItems));
- updateButtonsAndLabels = true;
+ mSlider->setValue((double) mAmountItems / (double) mMaxItems);
+ updateButtonsAndLabels();
}
// TODO: Actually we'd have a bug elsewhere if this check for the number
// of items to be bought ever fails, Bertram removed the assertions, is
// there a better way to ensure this fails in an _obivous_ way in C++?
- else if (event.getId() == "buy" && (mAmountItems > 0 &&
- mAmountItems <= mMaxItems))
+ else if (event.getId() == "buy" && mAmountItems > 0 &&
+ mAmountItems <= mMaxItems)
{
// XXX Convert for new server
/*
@@ -199,38 +184,15 @@ void BuyDialog::action(const gcn::ActionEvent &event)
outMsg.writeShort(mShopItems->at(selectedItem).id);
*/
- // update money !
+ // Update money and adjust the max number of items that can be bought
mMoney -= mAmountItems * mShopItems->at(selectedItem).price;
- // Update number of items that can be bought at max
mMaxItems -= mAmountItems;
- if (!mMaxItems) {
- mSlider->setEnabled(false);
- }
-
// Reset selection
mAmountItems = 0;
- mSlider->setValue(0);
-
- updateButtonsAndLabels = true;
- }
+ mSlider->setValue(0.0);
- // If anything has changed, we have to update the buttons and labels
- if (updateButtonsAndLabels)
- {
- // Update buttons
- mIncreaseButton->setEnabled(mAmountItems < mMaxItems);
- mDecreaseButton->setEnabled(mAmountItems > 0);
- mBuyButton->setEnabled(mAmountItems > 0);
-
- // Update labels
- mQuantityLabel->setCaption(toString(mAmountItems));
- mQuantityLabel->adjustSize();
-
- int price = mAmountItems * mShopItems->at(selectedItem).price;
- mMoneyLabel->setCaption("Price: " + toString(price) + " GP / "
- + toString(mMoney) + " GP" );
- mMoneyLabel->adjustSize();
+ updateButtonsAndLabels();
}
}
@@ -238,17 +200,16 @@ void BuyDialog::selectionChanged(const SelectionEvent &event)
{
// Reset amount of items and update labels
mAmountItems = 0;
- mSlider->setValue(0);
- mQuantityLabel->setCaption("0");
- mQuantityLabel->adjustSize();
- mMoneyLabel->setCaption("Price: 0 GP / " + toString(mMoney) + " GP");
- mMoneyLabel->adjustSize();
+ mSlider->setValue(0.0);
- // Disable buttons for buying and decreasing
- mBuyButton->setEnabled(false);
- mDecreaseButton->setEnabled(false);
+ updateButtonsAndLabels();
+}
+void
+BuyDialog::updateButtonsAndLabels()
+{
int selectedItem = mShopItemList->getSelected();
+ int price = 0;
if (selectedItem > -1)
{
@@ -259,16 +220,32 @@ void BuyDialog::selectionChanged(const SelectionEvent &event)
// Calculate how many the player can afford
mMaxItems = mMoney / mShopItems->at(selectedItem).price;
+ if (mAmountItems > mMaxItems)
+ {
+ mAmountItems = mMaxItems;
+ }
+
+ // Calculate price of pending purchase
+ price = mAmountItems * mShopItems->at(selectedItem).price;
}
else
{
mItemDescLabel->setCaption("Description:");
mItemEffectLabel->setCaption("Effect:");
mMaxItems = 0;
+ mAmountItems = 0;
}
- // When at least one item can be bought, enable the slider and the
- // increase button
- mIncreaseButton->setEnabled(mMaxItems > 0);
+ // Enable or disable buttons and slider
+ mIncreaseButton->setEnabled(mAmountItems < mMaxItems);
+ mDecreaseButton->setEnabled(mAmountItems > 0);
+ mBuyButton->setEnabled(mAmountItems > 0);
mSlider->setEnabled(mMaxItems > 0);
+
+ // Update quantity and money labels
+ mQuantityLabel->setCaption(toString(mAmountItems));
+ mQuantityLabel->adjustSize();
+ mMoneyLabel->setCaption("Price: " + toString(price) + " GP / "
+ + toString(mMoney - price) + " GP" );
+ mMoneyLabel->adjustSize();
}
diff --git a/src/gui/buy.h b/src/gui/buy.h
index 13116b6e..7834a434 100644
--- a/src/gui/buy.h
+++ b/src/gui/buy.h
@@ -92,6 +92,12 @@ class BuyDialog : public Window, public gcn::ActionListener, SelectionListener
*/
std::string getElementAt(int i);
+ /**
+ * Updates the state of buttons and labels.
+ */
+ void
+ updateButtonsAndLabels();
+
private:
gcn::Button *mBuyButton;
gcn::Button *mQuitButton;
diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp
index eb6b7612..48df9efe 100644
--- a/src/gui/chat.cpp
+++ b/src/gui/chat.cpp
@@ -160,7 +160,7 @@ ChatWindow::chatLog(std::string line, int own, std::string channelName)
break;
case BY_OTHER:
tmp.nick += CAT_NORMAL;
- lineColor = "##4"; // Equiv. to BrowserBox::ORANGE
+ lineColor = "##0"; // Equiv. to BrowserBox::BLACK
break;
case BY_SERVER:
tmp.nick += std::string("Server: ");
@@ -195,7 +195,7 @@ ChatWindow::chatLog(std::string line, int own, std::string channelName)
// We look if the Vertical Scroll Bar is set at the max before
// adding a row, otherwise the max will always be a row higher
// at comparison.
- if (mScrollArea->getVerticalScrollAmount() == mScrollArea->getVerticalMaxScroll() )
+ if (mScrollArea->getVerticalScrollAmount() == mScrollArea->getVerticalMaxScroll())
{
mTextOutput->addRow(line);
mScrollArea->setVerticalScrollAmount(mScrollArea->getVerticalMaxScroll());
diff --git a/src/gui/chat.h b/src/gui/chat.h
index c7e51ebf..6f9b91fe 100644
--- a/src/gui/chat.h
+++ b/src/gui/chat.h
@@ -168,13 +168,13 @@ class ChatWindow : public Window, public gcn::ActionListener,
/*
* Determines whether to send a command or an ordinary message, then
- * contructs packets & sends them
+ * contructs packets & sends them.
*
* @param nick The character's name to display in front.
* @param msg The message text which is to be send.
*
* NOTE:
- * the nickname is required by the server, if not specified
+ * The nickname is required by the server, if not specified
* the message may not be sent unless a command was intended
* which requires another packet to be constructed! you can
* achieve this by putting a slash ("/") infront of the
diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp
index 563f380f..ebf7d974 100644
--- a/src/gui/debugwindow.cpp
+++ b/src/gui/debugwindow.cpp
@@ -33,6 +33,7 @@
#include "../game.h"
#include "../engine.h"
+#include "../particle.h"
#include "../map.h"
#include "../utils/tostring.h"
@@ -58,6 +59,9 @@ DebugWindow::DebugWindow():
mTileMouseLabel = new gcn::Label("[Mouse: 0, 0]");
mTileMouseLabel->setPosition(100, 0);
+ mParticleCountLabel = new gcn::Label("[Particle count: 0]");
+ mParticleCountLabel->setPosition(100, 60);
+
Button *closeButton = new Button("Close", "close", this);
closeButton->setPosition(5, 60);
@@ -65,6 +69,7 @@ DebugWindow::DebugWindow():
add(mMusicFileLabel);
add(mMapFileLabel);
add(mTileMouseLabel);
+ add(mParticleCountLabel);
add(closeButton);
}
@@ -97,6 +102,11 @@ DebugWindow::logic()
mMapFileLabel->setCaption(minimap);
mMapFileLabel->adjustSize();
}
+
+ mParticleCountLabel->setCaption("[Particle count: " +
+ toString(Particle::particleCount)
+ +"]");
+ mParticleCountLabel->adjustSize();
}
void
diff --git a/src/gui/debugwindow.h b/src/gui/debugwindow.h
index 4fd33d83..d082b2ca 100644
--- a/src/gui/debugwindow.h
+++ b/src/gui/debugwindow.h
@@ -58,6 +58,7 @@ class DebugWindow : public Window, public gcn::ActionListener
private:
gcn::Label *mMusicFileLabel, *mMapFileLabel;
gcn::Label *mTileMouseLabel, *mFPSLabel;
+ gcn::Label *mParticleCountLabel;
};
#endif
diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp
index 44d042a6..2018c75a 100644
--- a/src/gui/inventorywindow.cpp
+++ b/src/gui/inventorywindow.cpp
@@ -165,8 +165,8 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event)
if (!item) return;
- /* Convert relative to the window coordinates to
- * absolute screen coordinates.
+ /* Convert relative to the window coordinates to absolute screen
+ * coordinates.
*/
int mx = event.getX() + getX();
int my = event.getY() + getY();
@@ -178,7 +178,8 @@ void InventoryWindow::mouseDragged(gcn::MouseEvent &event)
{
int tmpWidth = getWidth(), tmpHeight = getHeight();
Window::mouseDragged(event);
- if (getWidth() != tmpWidth || getHeight() != tmpHeight) {
+ if (getWidth() != tmpWidth || getHeight() != tmpHeight)
+ {
updateWidgets();
}
}
diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp
index c9878c84..478371b1 100644
--- a/src/gui/sell.cpp
+++ b/src/gui/sell.cpp
@@ -88,7 +88,7 @@ SellDialog::SellDialog():
quitButton->setPosition(208, 186);
mShopItemList->setActionEventId("item");
- mSlider->setActionEventId("mSlider");
+ mSlider->setActionEventId("slider");
mShopItemList->setPriceCheck(false);
@@ -119,20 +119,11 @@ void SellDialog::reset()
{
mShopItems->clear();
mSlider->setValue(0.0);
- mAmountItems = 0;
-
- mQuantityLabel->setCaption("0");
- mQuantityLabel->adjustSize();
- mMoneyLabel->setCaption("Money: 0 GP / Total: "
- + toString(mPlayerMoney) + " GP");
- mMoneyLabel->adjustSize();
- mItemDescLabel->setCaption("");
- mItemEffectLabel->setCaption("");
- // Reset Previous Selected Items to prevent failing asserts
+ // Reset previous selected item to prevent failing asserts
mShopItemList->setSelected(-1);
- mIncreaseButton->setEnabled(false);
- mDecreaseButton->setEnabled(false);
+
+ updateButtonsAndLabels();
}
void SellDialog::addItem(Item *item, int price)
@@ -162,25 +153,7 @@ void SellDialog::action(const gcn::ActionEvent &event)
{
mAmountItems = 0;
mSlider->setValue(0);
- mDecreaseButton->setEnabled(false);
- mSellButton->setEnabled(false);
-
- mQuantityLabel->adjustSize();
- mMoneyLabel->setCaption("Money: 0 GP / Total: "
- + toString(mPlayerMoney) + " GP");
- mMoneyLabel->adjustSize();
-
- if (selectedItem > -1) {
- mSlider->setEnabled(true);
- mIncreaseButton->setEnabled(true);
- mMaxItems = mShopItems->at(selectedItem).quantity;
- mQuantityLabel->setCaption("0 / " + toString(mMaxItems));
- } else {
- mSlider->setEnabled(false);
- mIncreaseButton->setEnabled(false);
- mQuantityLabel->setCaption("0");
- }
- mQuantityLabel->adjustSize();
+ updateButtonsAndLabels();
}
else if (event.getId() == "quit")
{
@@ -189,40 +162,35 @@ void SellDialog::action(const gcn::ActionEvent &event)
}
// The following actions require a valid item selection
- if (selectedItem == -1 || selectedItem >= int(mShopItems->getNumberOfElements())) {
+ if (selectedItem == -1 ||
+ selectedItem >= (int) mShopItems->getNumberOfElements())
+ {
return;
}
- bool updateButtonsAndLabels = false;
-
- if (event.getId() == "mSlider")
+ if (event.getId() == "slider")
{
- mAmountItems = (int)(mSlider->getValue() * mMaxItems);
-
- updateButtonsAndLabels = true;
+ mAmountItems = (int) (mSlider->getValue() * mMaxItems);
+ updateButtonsAndLabels();
}
- else if (event.getId() == "+")
+ else if (event.getId() == "+" && mAmountItems < mMaxItems)
{
- assert(mAmountItems < mMaxItems);
mAmountItems++;
- mSlider->setValue(double(mAmountItems)/double(mMaxItems));
- updateButtonsAndLabels = true;
+ mSlider->setValue((double) mAmountItems /(double) mMaxItems);
+ updateButtonsAndLabels();
}
- else if (event.getId() == "-")
+ else if (event.getId() == "-" && mAmountItems > 0)
{
- assert(mAmountItems > 0);
mAmountItems--;
- mSlider->setValue(double(mAmountItems)/double(mMaxItems));
-
- updateButtonsAndLabels = true;
+ mSlider->setValue((double) mAmountItems / (double) mMaxItems);
+ updateButtonsAndLabels();
}
- else if (event.getId() == "sell")
+ else if (event.getId() == "sell" && mAmountItems > 0
+ && mAmountItems <= mMaxItems)
{
// Attempt sell
- assert(mAmountItems > 0 && mAmountItems <= mMaxItems);
-
// XXX Convert for new server
/*
MessageOut outMsg(CMSG_NPC_SELL_REQUEST);
@@ -236,60 +204,77 @@ void SellDialog::action(const gcn::ActionEvent &event)
mPlayerMoney += (mAmountItems * mShopItems->at(selectedItem).price);
mAmountItems = 0;
mSlider->setValue(0);
- mSlider->setEnabled(mMaxItems != 0);
-
- // All were sold
- if (!mMaxItems) {
+ if (!mMaxItems)
+ {
+ // All were sold
mShopItemList->setSelected(-1);
- mShopItems->getShop()->erase(mShopItems->getShop()->begin() + selectedItem);
+ mShopItems->getShop()->erase(
+ mShopItems->getShop()->begin() + selectedItem);
+ }
+ else
+ {
+ // Update only when there are items left, the entry doesn't exist
+ // otherwise and can't be updated
+ updateButtonsAndLabels();
}
-
- // Update only when there are items left, the entry doesn't exist
- // otherwise and can't be updated
- updateButtonsAndLabels = bool(mMaxItems);
- }
-
- // If anything changed, we need to update the buttons and labels
- if (updateButtonsAndLabels)
- {
- // Update labels
- mQuantityLabel->setCaption(toString(mAmountItems) + " / " + toString(mMaxItems));
- mQuantityLabel->adjustSize();
-
- int price = mAmountItems * mShopItems->at(selectedItem).price;
- mMoneyLabel->setCaption("Money: " + toString(price) + " GP / Total: "
- + toString(price + mPlayerMoney) + " GP");
- mMoneyLabel->adjustSize();
-
- // Update Buttons
- mSellButton->setEnabled(mAmountItems > 0);
- mDecreaseButton->setEnabled(mAmountItems > 0);
- mIncreaseButton->setEnabled(mAmountItems < mMaxItems);
}
}
void SellDialog::selectionChanged(const SelectionEvent &event)
{
+ // Reset amount of items and update labels
+ mAmountItems = 0;
+ mSlider->setValue(0);
+
+ updateButtonsAndLabels();
+}
+
+void SellDialog::setMoney(int amount)
+{
+ mPlayerMoney = amount;
+ mShopItemList->setPlayersMoney(amount);
+}
+
+void
+SellDialog::updateButtonsAndLabels()
+{
int selectedItem = mShopItemList->getSelected();
+ int income = 0;
if (selectedItem > -1)
{
- const ItemInfo &info =
- ItemDB::get(mShopItems->at(selectedItem).id);
+ mMaxItems = mShopItems->at(selectedItem).quantity;
+ if (mAmountItems > mMaxItems)
+ {
+ mAmountItems = mMaxItems;
+ }
+ income = mAmountItems * mShopItems->at(selectedItem).price;
+
+ const ItemInfo &info = ItemDB::get(mShopItems->at(selectedItem).id);
mItemDescLabel->setCaption("Description: " + info.getDescription());
mItemEffectLabel->setCaption("Effect: " + info.getEffect());
}
else
{
- mItemDescLabel->setCaption("Description");
- mItemEffectLabel->setCaption("Effect");
+ mMaxItems = 0;
+ mAmountItems = 0;
+ mItemDescLabel->setCaption("Description:");
+ mItemEffectLabel->setCaption("Effect:");
}
-}
-void SellDialog::setMoney(int amount)
-{
- mPlayerMoney = amount;
- mShopItemList->setPlayersMoney(amount);
+ // Update Buttons and slider
+ mSellButton->setEnabled(mAmountItems > 0);
+ mDecreaseButton->setEnabled(mAmountItems > 0);
+ mIncreaseButton->setEnabled(mAmountItems < mMaxItems);
+ mSlider->setEnabled(selectedItem > -1);
+
+ // Update the quantity and money labels
+ mQuantityLabel->setCaption(
+ toString(mAmountItems) + " / " + toString(mMaxItems));
+ mQuantityLabel->adjustSize();
+ mMoneyLabel->setCaption("Money: " + toString(income) + " GP / Total: "
+ + toString(mPlayerMoney + income) + " GP");
+ mMoneyLabel->adjustSize();
}
diff --git a/src/gui/sell.h b/src/gui/sell.h
index 68bd7b8b..b8385a6f 100644
--- a/src/gui/sell.h
+++ b/src/gui/sell.h
@@ -82,6 +82,12 @@ class SellDialog : public Window, gcn::ActionListener, SelectionListener
*/
void setMoney(int amount);
+ /**
+ * Updates the state of buttons and labels.
+ */
+ void
+ updateButtonsAndLabels();
+
private:
gcn::Button *mSellButton;
gcn::Button *mIncreaseButton;
diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp
index d8130cd3..ed75e5b3 100644
--- a/src/gui/updatewindow.cpp
+++ b/src/gui/updatewindow.cpp
@@ -157,6 +157,7 @@ void UpdaterWindow::setLabel(const std::string &str)
void UpdaterWindow::enable()
{
+ mCancelButton->setEnabled(false);
mPlayButton->setEnabled(true);
mPlayButton->requestFocus();
}
@@ -168,11 +169,7 @@ void UpdaterWindow::action(const gcn::ActionEvent &event)
// Register the user cancel
mUserCancel = true;
// Skip the updating process
- if (mDownloadStatus == UPDATE_COMPLETE)
- {
- state = STATE_EXIT;
- }
- else
+ if (mDownloadStatus != UPDATE_COMPLETE)
{
mDownloadStatus = UPDATE_ERROR;
}
diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp
index f3e9031c..2af1d960 100644
--- a/src/gui/viewport.cpp
+++ b/src/gui/viewport.cpp
@@ -41,10 +41,13 @@
#include "../resources/animation.h"
#include "../resources/monsterinfo.h"
#include "../resources/resourcemanager.h"
+#include "../resources/image.h"
#include "../resources/imageset.h"
#include "../utils/tostring.h"
+#include <cassert>
+
Viewport::Viewport():
mMap(0),
mViewX(0.0f),
@@ -67,37 +70,62 @@ Viewport::Viewport():
mPopupMenu = new PopupMenu();
// Load target cursors
+ loadTargetCursor("graphics/gui/target-cursor-blue-s.png", 44, 35,
+ false, Being::TC_SMALL);
+ loadTargetCursor("graphics/gui/target-cursor-red-s.png", 44, 35,
+ true, Being::TC_SMALL);
+ loadTargetCursor("graphics/gui/target-cursor-blue-m.png", 62, 44,
+ false, Being::TC_MEDIUM);
+ loadTargetCursor("graphics/gui/target-cursor-red-m.png", 62, 44,
+ true, Being::TC_MEDIUM);
+ loadTargetCursor("graphics/gui/target-cursor-blue-l.png", 82, 60,
+ false, Being::TC_LARGE);
+ loadTargetCursor("graphics/gui/target-cursor-red-l.png", 82, 60,
+ true, Being::TC_LARGE);
+}
+
+void
+Viewport::loadTargetCursor(std::string filename, int width, int height,
+ bool outRange, Being::TargetCursorSize size)
+{
+ assert(size > -1);
+ assert(size < 3);
+
+ ImageSet* currentImageSet;
+ SimpleAnimation* currentCursor;
+
ResourceManager *resman = ResourceManager::getInstance();
- mInRangeImages = resman->getImageSet(
- "graphics/gui/target-cursor-blue.png", 44, 35);
- mOutRangeImages = resman->getImageSet(
- "graphics/gui/target-cursor-red.png", 44, 35);
- Animation *animInRange = new Animation();
- Animation *animOutRange = new Animation();
-
- for (unsigned int i = 0; i < mInRangeImages->size(); ++i)
+
+ currentImageSet = resman->getImageSet(filename, width, height);
+ Animation *anim = new Animation();
+ for (unsigned int i = 0; i < currentImageSet->size(); ++i)
{
- animInRange->addFrame(mInRangeImages->get(i), 75, 0, 0);
+ anim->addFrame(currentImageSet->get(i), 75, 0, 0);
}
+ currentCursor = new SimpleAnimation(anim);
- for (unsigned int j = 0; j < mOutRangeImages->size(); ++j)
+ if (outRange)
{
- animOutRange->addFrame(mOutRangeImages->get(j), 75, 0, 0);
+ mOutRangeImages[size] = currentImageSet;
+ mTargetCursorOutRange[size] = currentCursor;
+ }
+ else {
+ mInRangeImages[size] = currentImageSet;
+ mTargetCursorInRange[size] = currentCursor;
}
-
- mTargetCursorInRange = new SimpleAnimation(animInRange);
- mTargetCursorOutRange = new SimpleAnimation(animOutRange);
}
Viewport::~Viewport()
{
delete mPopupMenu;
- delete mTargetCursorInRange;
- delete mTargetCursorOutRange;
-
- mInRangeImages->decRef();
- mOutRangeImages->decRef();
+ for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++)
+ {
+ delete mTargetCursorInRange[i];
+ delete mTargetCursorOutRange[i];
+ mInRangeImages[i]->decRef();
+ mOutRangeImages[i]->decRef();
+ }
}
void
@@ -259,8 +287,11 @@ Viewport::logic()
mWalkTime = player_node->mWalkTime;
}
- mTargetCursorInRange->update(10);
- mTargetCursorOutRange->update(10);
+ for (int i = 0; i < 3; i++)
+ {
+ mTargetCursorInRange[i]->update(10);
+ mTargetCursorOutRange[i]->update(10);
+ }
}
void
@@ -270,26 +301,31 @@ Viewport::drawTargetCursor(Graphics *graphics)
Being *target = player_node->getTarget();
if (target)
{
+ // Calculate target circle position
+
// Find whether target is in range
int rangeX = abs(target->mX - player_node->mX);
int rangeY = abs(target->mY - player_node->mY);
int attackRange = player_node->getAttackRange();
- // Draw the target cursor, which one depends if the target is in range
+ // Get the correct target cursors graphic
+ Being::TargetCursorSize cursorSize = target->getTargetCursorSize();
+ Image* targetCursor;
if (rangeX > attackRange || rangeY > attackRange)
{
- // Draw the out of range cursor
- graphics->drawImage(mTargetCursorOutRange->getCurrentImage(),
- target->getPixelX() - mCameraX,
- target->getPixelY() - mCameraY);
+ targetCursor = mTargetCursorOutRange[cursorSize]->getCurrentImage();
}
- else
- {
- // Draw the in range cursor
- graphics->drawImage(mTargetCursorInRange->getCurrentImage(),
- target->getPixelX() - mCameraX,
- target->getPixelY() - mCameraY);
+ else {
+ targetCursor = mTargetCursorInRange[cursorSize]->getCurrentImage();
}
+
+ // Draw the target cursor at the correct position
+ int posX = target->getPixelX() + 16 -
+ targetCursor->getWidth() / 2 - mCameraX;
+ int posY = target->getPixelY() + 16 -
+ targetCursor->getHeight() / 2 - mCameraY;
+
+ graphics->drawImage(targetCursor, posX, posY);
}
}
@@ -304,10 +340,10 @@ Viewport::drawTargetName(Graphics *graphics)
graphics->setColor(gcn::Color(255, 32, 32));
const MonsterInfo &mi = static_cast<Monster*>(target)->getInfo();
- graphics->drawText(mi.getName(),
- target->getPixelX() - mCameraX + 15,
- target->getPixelY() - mCameraY - 42,
- gcn::Graphics::CENTER);
+ int posX = target->getPixelX() + 16 - mCameraX;
+ int posY = target->getPixelY() + 16 - target->getHeight() - mCameraY;
+
+ graphics->drawText(mi.getName(), posX, posY, gcn::Graphics::CENTER);
}
}
diff --git a/src/gui/viewport.h b/src/gui/viewport.h
index 84efeff3..22d0f249 100644
--- a/src/gui/viewport.h
+++ b/src/gui/viewport.h
@@ -29,9 +29,9 @@
#include "windowcontainer.h"
#include "../configlistener.h"
+#include "../being.h"
class Map;
-class Being;
class FloorItem;
class ImageSet;
class Item;
@@ -142,6 +142,13 @@ class Viewport : public WindowContainer, public gcn::MouseListener,
void showPopup(int x, int y, Being *being);
/**
+ * Helper function for loading target cursors
+ */
+ void
+ loadTargetCursor(std::string filename, int width, int height,
+ bool outRange, Being::TargetCursorSize size);
+
+ /**
* Draws range based target cursor
*/
void
@@ -164,14 +171,17 @@ class Viewport : public WindowContainer, public gcn::MouseListener,
int mCameraY; /**< Current viewpoint in tiles. */
bool mShowDebugPath; /**< Show a path from player to pointer. */
- ImageSet *mInRangeImages; /**< Images of in range target cursor. */
- ImageSet *mOutRangeImages; /**< Images of out of range target cursor.*/
+ /** Images of in range target cursor. */
+ ImageSet *mInRangeImages[Being::NUM_TC];
+
+ /** Images of out of range target cursor. */
+ ImageSet *mOutRangeImages[Being::NUM_TC];
/** Animated in range target cursor. */
- SimpleAnimation *mTargetCursorInRange;
+ SimpleAnimation *mTargetCursorInRange[Being::NUM_TC];
/** Animated out of range target cursor. */
- SimpleAnimation *mTargetCursorOutRange;
+ SimpleAnimation *mTargetCursorOutRange[Being::NUM_TC];
bool mPlayerFollowMouse;
int mWalkTime;
diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp
new file mode 100644
index 00000000..50a6fce4
--- /dev/null
+++ b/src/gui/widgets/resizegrip.cpp
@@ -0,0 +1,65 @@
+/*
+ * The Mana World
+ * Copyright 2004 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 "resizegrip.h"
+
+#include <guichan/graphics.hpp>
+
+#include "../../graphics.h"
+
+#include "../../resources/image.h"
+#include "../../resources/resourcemanager.h"
+
+Image *ResizeGrip::gripImage = 0;
+int ResizeGrip::mInstances = 0;
+
+ResizeGrip::ResizeGrip()
+{
+ if (mInstances == 0)
+ {
+ // Load the grip image
+ ResourceManager *resman = ResourceManager::getInstance();
+ gripImage = resman->getImage("graphics/gui/resize.png");
+ }
+
+ mInstances++;
+
+ setWidth(gripImage->getWidth() + 2);
+ setHeight(gripImage->getHeight() + 2);
+}
+
+ResizeGrip::~ResizeGrip()
+{
+ mInstances--;
+
+ if (mInstances == 0)
+ {
+ gripImage->decRef();
+ }
+}
+
+void
+ResizeGrip::draw(gcn::Graphics *graphics)
+{
+ dynamic_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0);
+}
diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h
new file mode 100644
index 00000000..04be3db3
--- /dev/null
+++ b/src/gui/widgets/resizegrip.h
@@ -0,0 +1,61 @@
+/*
+ * The Mana World
+ * Copyright 2004 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_RESIZEGRIP_H
+#define _TMW_RESIZEGRIP_H
+
+#include <guichan/widget.hpp>
+
+class Image;
+
+/**
+ * Resize grip. The resize grip is part of a resizable Window. It relies on the
+ * fact that uncaught mouse events are automatically routed to the parent
+ * window.
+ *
+ * \ingroup GUI
+ */
+class ResizeGrip : public gcn::Widget
+{
+ public:
+ /**
+ * Constructor.
+ */
+ ResizeGrip();
+
+ /**
+ * Destructor.
+ */
+ ~ResizeGrip();
+
+ /**
+ * Draws the resize grip.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ private:
+ static Image *gripImage; /**< Resize grip image */
+ static int mInstances; /**< Number of resize grip instances */
+};
+
+#endif
diff --git a/src/gui/window.cpp b/src/gui/window.cpp
index bb60c6ff..8a052bba 100644
--- a/src/gui/window.cpp
+++ b/src/gui/window.cpp
@@ -24,10 +24,13 @@
#include "window.h"
#include <guichan/exception.hpp>
+#include <guichan/widgets/icon.hpp>
#include "gccontainer.h"
#include "windowcontainer.h"
+#include "widgets/resizegrip.h"
+
#include "../configlistener.h"
#include "../configuration.h"
#include "../graphics.h"
@@ -36,11 +39,10 @@
#include "../resources/image.h"
#include "../resources/resourcemanager.h"
-ConfigListener *Window::windowConfigListener = NULL;
-WindowContainer *Window::windowContainer = NULL;
+ConfigListener *Window::windowConfigListener = 0;
+WindowContainer *Window::windowContainer = 0;
int Window::instances = 0;
ImageRect Window::border;
-Image *Window::resizeGrip;
class WindowConfigListener : public ConfigListener
{
@@ -54,16 +56,16 @@ class WindowConfigListener : public ConfigListener
Window::Window(const std::string& caption, bool modal, Window *parent):
gcn::Window(caption),
+ mGrip(0),
mParent(parent),
mWindowName("window"),
- mSnapSize(8),
mShowTitle(true),
mModal(modal),
mResizable(false),
- mMouseResize(false),
+ mMouseResize(0),
mSticky(false),
mMinWinWidth(100),
- mMinWinHeight(28),
+ mMinWinHeight(40),
mMaxWinWidth(INT_MAX),
mMaxWinHeight(INT_MAX)
{
@@ -87,7 +89,6 @@ Window::Window(const std::string& caption, bool modal, Window *parent):
border.grid[6] = dBorders->getSubImage(0, 15, 4, 4);
border.grid[7] = dBorders->getSubImage(4, 15, 3, 4);
border.grid[8] = dBorders->getSubImage(7, 15, 4, 4);
- resizeGrip = resman->getImage("graphics/gui/resize.png");
dBorders->decRef();
windowConfigListener = new WindowConfigListener();
// Send GUI alpha changed for initialization
@@ -151,10 +152,10 @@ Window::~Window()
delete border.grid[6];
delete border.grid[7];
delete border.grid[8];
- resizeGrip->decRef();
}
delete mChrome;
+ delete mGrip;
}
void Window::setWindowContainer(WindowContainer *wc)
@@ -162,22 +163,15 @@ void Window::setWindowContainer(WindowContainer *wc)
windowContainer = wc;
}
-void Window::draw(gcn::Graphics* graphics)
+void Window::draw(gcn::Graphics *graphics)
{
- Graphics *g = (Graphics*)graphics;
+ Graphics *g = static_cast<Graphics*>(graphics);
g->drawImageRect(0, 0, getWidth(), getHeight(), border);
- // Draw grip
- if (mResizable)
- {
- g->drawImage(Window::resizeGrip,
- getWidth() - resizeGrip->getWidth(),
- getHeight() - resizeGrip->getHeight());
- }
-
// Draw title
- if (mShowTitle) {
+ if (mShowTitle)
+ {
graphics->setFont(getFont());
graphics->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT);
}
@@ -188,13 +182,13 @@ void Window::draw(gcn::Graphics* graphics)
void Window::setContentWidth(int width)
{
mChrome->setWidth(width);
- resizeToContent();
+ setWidth(width + 2 * getPadding());
}
void Window::setContentHeight(int height)
{
mChrome->setHeight(height);
- resizeToContent();
+ setHeight(height + getPadding() + getTitleBarHeight());
}
void Window::setContentSize(int width, int height)
@@ -203,6 +197,37 @@ void Window::setContentSize(int width, int height)
setContentHeight(height);
}
+void Window::setWidth(int width)
+{
+ gcn::Window::setWidth(width);
+
+ if (mGrip)
+ {
+ mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x);
+ }
+}
+
+void Window::setHeight(int height)
+{
+ gcn::Window::setHeight(height);
+
+ if (mGrip)
+ {
+ mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y);
+ }
+}
+
+void Window::setDimension(const gcn::Rectangle &dimension)
+{
+ gcn::Window::setDimension(dimension);
+
+ if (mGrip)
+ {
+ mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x);
+ mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y);
+ }
+}
+
void Window::setLocationRelativeTo(gcn::Widget *widget)
{
int wx, wy;
@@ -238,6 +263,19 @@ void Window::setMaxHeight(unsigned int height)
void Window::setResizable(bool r)
{
mResizable = r;
+
+ if (mResizable)
+ {
+ mGrip = new ResizeGrip();
+ mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x);
+ mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y);
+ gcn::Window::add(mGrip);
+ }
+ else
+ {
+ delete mGrip;
+ mGrip = 0;
+ }
}
bool Window::isResizable()
@@ -287,15 +325,27 @@ void Window::mousePressed(gcn::MouseEvent &event)
// Let Guichan move window to top and figure out title bar drag
gcn::Window::mousePressed(event);
- int x = event.getX();
- int y = event.getY();
+ const int x = event.getX();
+ const int y = event.getY();
+ mMouseResize = 0;
- // Activate resizing if the left mouse button was pressed on the grip
- mMouseResize =
- isResizable() &&
- event.getButton() == gcn::MouseEvent::LEFT &&
- getGripDimension().isPointInRect(x, y) &&
- !getChildrenArea().isPointInRect(x, y);
+ // Activate resizing handles as appropriate
+ if (event.getSource() == this && isResizable() &&
+ event.getButton() == gcn::MouseEvent::LEFT &&
+ !getChildrenArea().isPointInRect(x, y))
+ {
+ mMouseResize |= (x > getWidth() - resizeBorderWidth) ? RIGHT :
+ (x < resizeBorderWidth) ? LEFT : 0;
+ mMouseResize |= (y > getHeight() - resizeBorderWidth) ? BOTTOM :
+ (y < resizeBorderWidth) ? TOP : 0;
+ }
+ else if (event.getSource() == mGrip)
+ {
+ mDragOffsetX = x + mGrip->getX();
+ mDragOffsetY = y + mGrip->getY();
+ mMouseResize |= BOTTOM | RIGHT;
+ mIsMoving = false;
+ }
}
void Window::mouseDragged(gcn::MouseEvent &event)
@@ -303,20 +353,47 @@ void Window::mouseDragged(gcn::MouseEvent &event)
// Let Guichan handle title bar drag
gcn::Window::mouseDragged(event);
- // Keep guichan window inside screen
- int newX = std::max(0, getX());
- int newY = std::max(0, getY());
- newX = std::min(windowContainer->getWidth() - getWidth(), newX);
- newY = std::min(windowContainer->getHeight() - getHeight(), newY);
- setPosition(newX, newY);
+ // Keep guichan window inside screen when it may be moved
+ if (isMovable() && mIsMoving)
+ {
+ int newX = std::max(0, getX());
+ int newY = std::max(0, getY());
+ newX = std::min(windowContainer->getWidth() - getWidth(), newX);
+ newY = std::min(windowContainer->getHeight() - getHeight(), newY);
+ setPosition(newX, newY);
+ }
if (mMouseResize && !mIsMoving)
{
+ const int dx = event.getX() - mDragOffsetX +
+ ((event.getSource() == mGrip) ? mGrip->getX() : 0);
+ const int dy = event.getY() - mDragOffsetY +
+ ((event.getSource() == mGrip) ? mGrip->getY() : 0);
gcn::Rectangle newDim = getDimension();
- // We're dragging bottom right
- newDim.width += event.getX() - mDragOffsetX;
- newDim.height += event.getY() - mDragOffsetY;
+ if (mMouseResize & (TOP | BOTTOM))
+ {
+ int newHeight = newDim.height + ((mMouseResize & TOP) ? -dy : dy);
+ newDim.height = std::min(mMaxWinHeight,
+ std::max(mMinWinHeight, newHeight));
+
+ if (mMouseResize & TOP)
+ {
+ newDim.y -= newDim.height - getHeight();
+ }
+ }
+
+ if (mMouseResize & (LEFT | RIGHT))
+ {
+ int newWidth = newDim.width + ((mMouseResize & LEFT) ? -dx : dx);
+ newDim.width = std::min(mMaxWinWidth,
+ std::max(mMinWinWidth, newWidth));
+
+ if (mMouseResize & LEFT)
+ {
+ newDim.x -= newDim.width - getWidth();
+ }
+ }
// Keep guichan window inside screen (supports resizing any side)
if (newDim.x < 0)
@@ -338,29 +415,16 @@ void Window::mouseDragged(gcn::MouseEvent &event)
newDim.height = windowContainer->getHeight() - newDim.y;
}
- // Keep the window at least its minimum size
- if (newDim.width < mMinWinWidth)
- {
- newDim.width = mMinWinWidth;
- }
- else if (newDim.width > mMaxWinWidth)
- {
- newDim.width = mMaxWinWidth;
- }
-
- if (newDim.height < mMinWinHeight)
+ // Update mouse offset when dragging bottom or right border
+ if (mMouseResize & BOTTOM)
{
- newDim.height = mMinWinHeight;
+ mDragOffsetY += newDim.height - getHeight();
}
- else if (newDim.height > mMaxWinHeight)
+ if (mMouseResize & RIGHT)
{
- newDim.height = mMaxWinHeight;
+ mDragOffsetX += newDim.width - getWidth();
}
- // Update mouse offset when dragging bottom or right border
- mDragOffsetX += newDim.width - getWidth();
- mDragOffsetY += newDim.height - getHeight();
-
// Set the new window and content dimensions
setDimension(newDim);
const gcn::Rectangle area = getChildrenArea();
@@ -368,15 +432,6 @@ void Window::mouseDragged(gcn::MouseEvent &event)
}
}
-gcn::Rectangle
-Window::getGripDimension()
-{
- return gcn::Rectangle(getWidth() - resizeGrip->getWidth(),
- getHeight() - resizeGrip->getHeight(),
- getWidth(),
- getHeight());
-}
-
void
Window::loadWindowState()
{
diff --git a/src/gui/window.h b/src/gui/window.h
index 31c260cf..03248908 100644
--- a/src/gui/window.h
+++ b/src/gui/window.h
@@ -30,11 +30,10 @@
class ConfigListener;
class GCContainer;
-class Image;
class ImageRect;
+class ResizeGrip;
class WindowContainer;
-
/**
* A window. This window can be dragged around and has a title bar. Windows are
* invisible by default.
@@ -100,6 +99,21 @@ class Window : public gcn::Window
void setContentSize(int width, int height);
/**
+ * Sets the width of this window.
+ */
+ void setWidth(int width);
+
+ /**
+ * Sets the height of this window.
+ */
+ void setHeight(int height);
+
+ /**
+ * Sets the position and size of this window.
+ */
+ void setDimension(const gcn::Rectangle &dimension);
+
+ /**
* Sets the location relative to the given widget.
*/
void setLocationRelativeTo(gcn::Widget *widget);
@@ -168,16 +182,15 @@ class Window : public gcn::Window
void scheduleDelete();
/**
- * Window dragging and resizing mouse related. These methods also makes
- * sure the window is not dragged/resized outside of the screen.
+ * Starts window resizing when appropriate.
*/
void mousePressed(gcn::MouseEvent &event);
- void mouseDragged(gcn::MouseEvent &event);
/**
- * Gets the position of the resize grip.
+ * Implements window resizing and makes sure the window is not
+ * dragged/resized outside of the screen.
*/
- gcn::Rectangle getGripDimension();
+ void mouseDragged(gcn::MouseEvent &event);
/**
* Sets the name of the window. This is not the window title.
@@ -214,37 +227,50 @@ class Window : public gcn::Window
*/
virtual void resetToDefaultSize();
+ enum ResizeHandles
+ {
+ TOP = 0x01,
+ RIGHT = 0x02,
+ BOTTOM = 0x04,
+ LEFT = 0x08
+ };
+
protected:
- GCContainer *mChrome; /**< Contained container */
+ GCContainer *mChrome; /**< Contained container */
+ ResizeGrip *mGrip; /**< Resize grip */
Window *mParent; /**< The parent window */
std::string mWindowName; /**< Name of the window */
- int mSnapSize; /**< Snap distance to window edge */
bool mShowTitle; /**< Window has a title bar */
bool mModal; /**< Window is modal */
- bool mResizable; /**< Window can be resized */
- bool mMouseResize; /**< Window is being resized */
- bool mSticky; /**< Window resists minimzation */
- int mMinWinWidth; /**< Minimum window width */
- int mMinWinHeight; /**< Minimum window height */
- int mMaxWinWidth; /**< Maximum window width */
- int mMaxWinHeight; /**< Maximum window height */
+ bool mResizable; /**< Window can be resized */
+ int mMouseResize; /**< Window is being resized */
+ bool mSticky; /**< Window resists minimization */
+ int mMinWinWidth; /**< Minimum window width */
+ int mMinWinHeight; /**< Minimum window height */
+ int mMaxWinWidth; /**< Maximum window width */
+ int mMaxWinHeight; /**< Maximum window height */
int mDefaultX; /**< Default window X position */
int mDefaultY; /**< Default window Y position */
int mDefaultWidth; /**< Default window width */
int mDefaultHeight; /**< Default window height */
/** The window container windows add themselves to. */
- static WindowContainer* windowContainer;
+ static WindowContainer *windowContainer;
/**
- * The config listener that listens to changes relevant to all
- * windows
+ * The config listener that listens to changes relevant to all windows.
*/
static ConfigListener *windowConfigListener;
static int instances; /**< Number of Window instances */
static ImageRect border; /**< The window border and background */
- static Image *resizeGrip; /**< The grip to resize window */
+
+ /**
+ * The width of the resize border. Is independent of the actual window
+ * border width, and determines mostly the size of the corner area
+ * where two borders are moved at the same time.
+ */
+ static const int resizeBorderWidth = 10;
};
#endif
diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp
new file mode 100644
index 00000000..35cc21ad
--- /dev/null
+++ b/src/imageparticle.cpp
@@ -0,0 +1,69 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 "imageparticle.h"
+
+#include "graphics.h"
+
+#include "resources/image.h"
+
+ImageParticle::ImageParticle(Map *map, Image *image):
+ Particle(map),
+ mImage(image)
+{
+ mImage->incRef();
+}
+
+ImageParticle::~ImageParticle()
+{
+ mImage->decRef();
+}
+
+void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const
+{
+ if (!mAlive)
+ return;
+
+ int screenX = (int) mPosX + offsetX - mImage->getWidth() / 2;
+ int screenY = (int) mPosY - (int) mPosZ + offsetY - mImage->getHeight()/2;
+
+ // Check if on screen
+ if (screenX + mImage->getWidth() < 0 ||
+ screenX > graphics->getWidth() ||
+ screenY + mImage->getHeight() < 0 ||
+ screenY > graphics->getHeight())
+ {
+ return;
+ }
+
+ float alphafactor = 1.0f;
+
+ if (mLifetimeLeft > -1 && mLifetimeLeft < mFadeOut)
+ alphafactor *= (float) mLifetimeLeft / (float) mFadeOut;
+
+ if (mLifetimePast < mFadeIn)
+ alphafactor *= (float) mLifetimePast / (float) mFadeIn;
+
+ mImage->setAlpha(alphafactor);
+ graphics->drawImage(mImage, screenX, screenY);
+}
diff --git a/src/imageparticle.h b/src/imageparticle.h
new file mode 100644
index 00000000..0ad515cc
--- /dev/null
+++ b/src/imageparticle.h
@@ -0,0 +1,61 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 _IMAGEPARTICLE_H
+#define _IMAGEPARTICLE_H
+
+#include "particle.h"
+
+class Image;
+class Map;
+
+/**
+ * A particle that uses an image for its visualization.
+ */
+class ImageParticle : public Particle
+{
+ public:
+ /**
+ * Constructor. The image is reference counted by this particle.
+ *
+ * @param map the map this particle appears on
+ * @param image an Image instance, may not be NULL
+ */
+ ImageParticle(Map *map, Image *image);
+
+ /**
+ * Destructor.
+ */
+ ~ImageParticle();
+
+ /**
+ * Draws the particle image
+ */
+ virtual void
+ draw(Graphics *graphics, int offsetX, int offsetY) const;
+
+ protected:
+ Image *mImage; /**< The image used for this particle. */
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index bbbf0fd1..58c563ab 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -40,7 +40,7 @@
#include <libxml/parser.h>
-#if (defined __USE_UNIX98 || defined __FreeBSD__ || defined __APPLE__)
+#ifndef WIN32
#include <cerrno>
#include <sys/stat.h>
#endif
@@ -148,21 +148,21 @@ struct Options
*/
void initHomeDir()
{
-#if !(defined __USE_UNIX98 || defined __FreeBSD__ || defined __APPLE__)
- homeDir = ".";
-#else
homeDir = std::string(PHYSFS_getUserDir()) + "/.tmw";
-
+#if defined WIN32
+ if (!CreateDirectory(homeDir.c_str(), 0) &&
+ GetLastError() != ERROR_ALREADY_EXISTS)
+#else
// Checking if /home/user/.tmw folder exists.
if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) &&
(errno != EEXIST))
+#endif
{
std::cout << homeDir
- << " can't be made, but it doesn't exist! Exiting."
+ << " can't be created, but it doesn't exist! Exiting."
<< std::endl;
exit(1);
}
-#endif
}
/**
@@ -236,11 +236,17 @@ void initEngine()
static SDL_SysWMinfo pInfo;
SDL_GetWMInfo(&pInfo);
HICON icon = LoadIcon(GetModuleHandle(NULL), "A");
- SetClassLong(pInfo.window, GCL_HICON, (LONG) icon);
+ if (icon)
+ {
+ SetClassLong(pInfo.window, GCL_HICON, (LONG) icon);
+ }
#else
SDL_Surface *icon = IMG_Load(TMW_DATADIR "data/icons/tmw.png");
- SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
- SDL_WM_SetIcon(icon, NULL);
+ if (icon)
+ {
+ SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
+ SDL_WM_SetIcon(icon, NULL);
+ }
#endif
ResourceManager *resman = ResourceManager::getInstance();
diff --git a/src/map.cpp b/src/map.cpp
index a6beb951..ac570627 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -29,6 +29,7 @@
#include "beingmanager.h"
#include "game.h"
#include "graphics.h"
+#include "particle.h"
#include "sprite.h"
#include "tileset.h"
@@ -530,3 +531,25 @@ Map::findPath(int startX, int startY, int destX, int destY)
return path;
}
+
+void
+Map::addParticleEffect (std::string effectFile, int x, int y)
+{
+ ParticleEffectData newEffect;
+ newEffect.file = effectFile;
+ newEffect.x = x;
+ newEffect.y = y;
+ particleEffects.push_back(newEffect);
+}
+
+void
+Map::initializeParticleEffects(Particle* particleEngine)
+{
+ for (std::list<ParticleEffectData>::iterator i = particleEffects.begin();
+ i != particleEffects.end();
+ i++
+ )
+ {
+ particleEngine->addEffect(i->file, i->x, i->y);
+ }
+}
diff --git a/src/map.h b/src/map.h
index c8a6fdb3..17772847 100644
--- a/src/map.h
+++ b/src/map.h
@@ -32,8 +32,9 @@
class AmbientOverlay;
class Graphics;
class Image;
-class Tileset;
+class Particle;
class Sprite;
+class Tileset;
struct PATH_NODE;
@@ -185,6 +186,17 @@ class Map : public Properties
void
removeSprite(SpriteIterator iterator);
+ /**
+ * Adds a particle effect
+ */
+ void addParticleEffect (std::string effectFile, int x, int y);
+
+ /**
+ * Initializes all added particle effects
+ */
+ void
+ initializeParticleEffects(Particle* particleEngine);
+
private:
/**
* Converts a global tile id to the Image* pointing to the associated
@@ -220,10 +232,19 @@ class Map : public Properties
// Pathfinding members
int mOnClosedList, mOnOpenList;
- //overlay Data
+ // Overlay Data
std::list<AmbientOverlay*> mOverlays;
float mLastScrollX;
float mLastScrollY;
+
+ // Particle effect data
+ struct ParticleEffectData
+ {
+ std::string file;
+ int x;
+ int y;
+ };
+ std::list<ParticleEffectData> particleEffects;
};
#endif
diff --git a/src/monster.cpp b/src/monster.cpp
index bea5b7a5..768bf16a 100644
--- a/src/monster.cpp
+++ b/src/monster.cpp
@@ -93,6 +93,12 @@ Monster::handleAttack()
sound.playSfx(mi.getSound(EVENT_HIT));
}
+Being::TargetCursorSize
+Monster::getTargetCursorSize() const
+{
+ return getInfo().getTargetCursorSize();
+}
+
const MonsterInfo&
Monster::getInfo() const
{
diff --git a/src/monster.h b/src/monster.h
index 39bbf3d7..4915520d 100644
--- a/src/monster.h
+++ b/src/monster.h
@@ -37,6 +37,9 @@ class Monster : public Being
virtual Type getType() const;
+ virtual TargetCursorSize
+ getTargetCursorSize() const;
+
/**
* Handles an attack of another being by this monster. Plays a hit or
* miss sound when appropriate.
diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp
index 53746671..f82a0fa8 100644
--- a/src/net/beinghandler.cpp
+++ b/src/net/beinghandler.cpp
@@ -34,6 +34,7 @@
#include "../localplayer.h"
#include "../log.h"
#include "../main.h"
+#include "../particle.h"
#include "../sound.h"
const int EMOTION_TIME = 150; /**< Duration of emotion icon */
@@ -240,13 +241,23 @@ void BeingHandler::handleMessage(MessageIn &msg)
break;
case SMSG_BEING_LEVELUP:
- if ((Uint32) msg.readLong() == player_node->getId()) {
+ id = (Uint32) msg->readLong();
+
+ if (id == player_node->getId()) {
logger->log("Level up");
sound.playSfx("sfx/levelup.ogg");
- } else {
+ }
+ else {
logger->log("Someone else went level up");
}
- msg.readLong(); // type
+ Particle *levelupFX;
+ if (msg->readLong() == 0) { // type
+ levelupFX = particleEngine->addEffect("graphics/particles/levelup.particle.xml", 0, 0);
+ }
+ else {
+ levelupFX = particleEngine->addEffect("graphics/particles/skillup.particle.xml", 0, 0);
+ }
+ beingManager->findBeing(id)->controlParticle(levelupFX);
break;
case SMSG_BEING_EMOTION:
diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp
index ec6c1ee3..f4cda0b9 100644
--- a/src/openglgraphics.cpp
+++ b/src/openglgraphics.cpp
@@ -261,7 +261,7 @@ void OpenGLGraphics::drawPoint(int x, int y)
setTexturingAndBlending(false);
glBegin(GL_POINTS);
- glVertex3i(x, y, 0);
+ glVertex2i(x, y);
glEnd();
}
@@ -270,12 +270,12 @@ void OpenGLGraphics::drawLine(int x1, int y1, int x2, int y2)
setTexturingAndBlending(false);
glBegin(GL_LINES);
- glVertex3f(x1 + 0.5f, y1 + 0.5f, 0);
- glVertex3f(x2 + 0.5f, y2 + 0.5f, 0);
+ glVertex2f(x1 + 0.5f, y1 + 0.5f);
+ glVertex2f(x2 + 0.5f, y2 + 0.5f);
glEnd();
glBegin(GL_POINTS);
- glVertex3f(x2 + 0.5f, y2 + 0.5f, 0);
+ glVertex2f(x2 + 0.5f, y2 + 0.5f);
glEnd();
}
@@ -329,10 +329,10 @@ void OpenGLGraphics::drawRectangle(const gcn::Rectangle& rect, bool filled)
setTexturingAndBlending(false);
glBegin(filled ? GL_QUADS : GL_LINE_LOOP);
- glVertex3f(rect.x + offset, rect.y + offset, 0);
- glVertex3f(rect.x + rect.width - offset, rect.y + offset, 0);
- glVertex3f(rect.x + rect.width - offset, rect.y + rect.height - offset, 0);
- glVertex3f(rect.x + offset, rect.y + rect.height - offset, 0);
+ glVertex2f(rect.x + offset, rect.y + offset);
+ glVertex2f(rect.x + rect.width - offset, rect.y + offset);
+ glVertex2f(rect.x + rect.width - offset, rect.y + rect.height - offset);
+ glVertex2f(rect.x + offset, rect.y + rect.height - offset);
glEnd();
}
@@ -344,16 +344,16 @@ void OpenGLGraphics::drawTexedQuad(int x, int y, int w, int h,
// Draw a textured quad
glBegin(GL_QUADS);
glTexCoord2f(texX1, texY1);
- glVertex3i(x, y, 0);
+ glVertex2i(x, y);
glTexCoord2f(texX2, texY1);
- glVertex3i(x + w, y, 0);
+ glVertex2i(x + w, y);
glTexCoord2f(texX2, texY2);
- glVertex3i(x + w, y + h, 0);
+ glVertex2i(x + w, y + h);
glTexCoord2f(texX1, texY2);
- glVertex3i(x, y + h, 0);
+ glVertex2i(x, y + h);
glEnd();
}
diff --git a/src/particle.cpp b/src/particle.cpp
new file mode 100644
index 00000000..509c20ee
--- /dev/null
+++ b/src/particle.cpp
@@ -0,0 +1,370 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 "particle.h"
+
+#include <cmath>
+
+#include "animationparticle.h"
+#include "configuration.h"
+#include "imageparticle.h"
+#include "log.h"
+#include "map.h"
+#include "particleemitter.h"
+#include "textparticle.h"
+
+#include "resources/resourcemanager.h"
+
+#include "utils/dtor.h"
+#include "utils/fastsqrt.h"
+#include "utils/xml.h"
+
+class Graphics;
+class Image;
+
+int Particle::particleCount = 0;
+int Particle::maxCount = 0;
+int Particle::fastPhysics = 0;
+int Particle::emitterSkip = 1;
+const float Particle::PARTICLE_SKY = 800.0f;
+
+Particle::Particle(Map *map):
+ mAlive(true),
+ mPosX(0.0f), mPosY(0.0f), mPosZ(0.0f),
+ mLifetimeLeft(-1),
+ mLifetimePast(0),
+ mFadeOut(0),
+ mFadeIn(0),
+ mAutoDelete(true),
+ mMap(map),
+ mVectorX(0.0f), mVectorY(0.0f), mVectorZ(0.0f),
+ mGravity(0.0f),
+ mRandomnes(0),
+ mBounce(0.0f),
+ mTarget(NULL),
+ mAcceleration(0.0f),
+ mInvDieDistance(-1.0f),
+ mMomentum(1.0f)
+{
+ Particle::particleCount++;
+ if (mMap) setSpriteIterator(mMap->addSprite(this));
+}
+
+
+void
+Particle::setupEngine()
+{
+ Particle::maxCount = (int)config.getValue("particleMaxCount", 3000);
+ Particle::fastPhysics = (int)config.getValue("particleFastPhysics", 0);
+ Particle::emitterSkip = (int)config.getValue("particleEmitterSkip", 0) + 1;
+ disableAutoDelete();
+ logger->log("Particle engine set up");
+}
+
+bool
+Particle::update()
+{
+ if (!mMap) return false;
+
+ if (mLifetimeLeft == 0)
+ {
+ mAlive = false;
+ }
+
+ if (mAlive)
+ {
+ // Update child emitters
+ if (mLifetimePast%Particle::emitterSkip == 0)
+ {
+ for ( EmitterIterator e = mChildEmitters.begin();
+ e != mChildEmitters.end();
+ e++
+ )
+ {
+ Particles newParticles = (*e)->createParticles();
+ for ( ParticleIterator p = newParticles.begin();
+ p != newParticles.end();
+ p++
+ )
+ {
+ (*p)->moveBy(mPosX, mPosY, mPosZ);
+ mChildParticles.push_back (*p);
+ }
+ }
+ }
+
+ if (mMomentum != 1.0f)
+ {
+ mVectorX *= mMomentum;
+ mVectorY *= mMomentum;
+ mVectorZ *= mMomentum;
+ }
+
+ if (mTarget && mAcceleration != 0.0f)
+ {
+ float distX = mPosX - mTarget->getPosX();
+ float distY = mPosY - mTarget->getPosY();
+ float distZ = mPosZ - mTarget->getPosZ();
+ float invHypotenuse;
+
+ switch(Particle::fastPhysics)
+ {
+ case 1:
+ invHypotenuse = fastInvSqrt(
+ distX * distX + distY * distY + distZ * distZ);
+ break;
+ case 2:
+ invHypotenuse = 2.0f /
+ fabs(distX) + fabs(distY) + fabs(distZ);
+ break;
+ default:
+ invHypotenuse = 1.0f / sqrt(
+ distX * distX + distY * distY + distZ * distZ);
+ break;
+ }
+
+ if (invHypotenuse)
+ {
+ if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance)
+ {
+ mAlive = false;
+ }
+ float accFactor = invHypotenuse * mAcceleration;
+ mVectorX -= distX * accFactor;
+ mVectorY -= distY * accFactor;
+ mVectorZ -= distZ * accFactor;
+ }
+ }
+
+ if (mRandomnes > 0)
+ {
+ mVectorX += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f;
+ mVectorY += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f;
+ mVectorZ += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f;
+ }
+
+ mVectorZ -= mGravity;
+
+ // Update position
+ mPosX += mVectorX;
+ mPosY += mVectorY;
+ mPosZ += mVectorZ;
+
+ // Update other stuff
+ if (mLifetimeLeft > 0)
+ {
+ mLifetimeLeft--;
+ }
+ mLifetimePast++;
+
+ if (mPosZ > PARTICLE_SKY || mPosZ < 0.0f)
+ {
+ if (mBounce > 0.0f)
+ {
+ mPosZ *= -mBounce;
+ mVectorX *= mBounce;
+ mVectorY *= mBounce;
+ mVectorZ *= -mBounce;
+ }
+ else {
+ mAlive = false;
+ }
+ }
+ }
+
+ // Update child particles
+ for ( ParticleIterator p = mChildParticles.begin();
+ p != mChildParticles.end();
+
+ )
+ {
+ if ((*p)->update())
+ {
+ p++;
+ } else {
+ delete (*p);
+ p = mChildParticles.erase(p);
+ }
+ }
+
+ if (!mAlive && mChildParticles.empty() && mAutoDelete)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+void Particle::draw(Graphics *graphics, int offsetX, int offsetY) const
+{
+}
+
+
+Particle*
+Particle::addEffect(std::string particleEffectFile, int pixelX, int pixelY)
+{
+ Particle *newParticle = NULL;
+
+ // XML parser initialisation stuff
+ int size;
+ ResourceManager *resman = ResourceManager::getInstance();
+ char *data = (char*) resman->loadFile(particleEffectFile.c_str(), size);
+
+ if (!data) {
+ logger->log("Warning: Particle engine could not find %s !",
+ particleEffectFile.c_str());
+ return NULL;
+ }
+
+ xmlDocPtr doc = xmlParseMemory(data, size);
+ free(data);
+
+ if (!doc) {
+ logger->log("Warning: Particle engine found syntax error in %s!",
+ particleEffectFile.c_str());
+ return NULL;
+ }
+
+ xmlNodePtr rootNode = xmlDocGetRootElement(doc);
+ if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "effect"))
+ {
+ logger->log("Warning: %s is not a valid particle effect definition file!",
+ particleEffectFile.c_str());
+ return NULL;
+ }
+
+ // Parse particles
+ for_each_xml_child_node(effectChildNode, rootNode)
+ {
+ // We're only interested in particles
+ if (!xmlStrEqual(effectChildNode->name, BAD_CAST "particle"))
+ continue;
+
+ // Determine the exact particle type
+ xmlNodePtr node;
+
+ // Animation
+ if ((node = XML::findFirstChildByName(
+ effectChildNode, "animation"))) {
+ newParticle = new AnimationParticle(mMap, node);
+ }
+ // Image
+ else if ((node = XML::findFirstChildByName(
+ effectChildNode, "image"))) {
+ Image *img= resman->getImage((const char*)
+ node->xmlChildrenNode->content);
+
+ newParticle = new ImageParticle(mMap, img);
+ }
+ // Other
+ else {
+ newParticle = new Particle(mMap);
+ }
+
+ // Read and set the basic properties of the particle
+ int offsetX = XML::getProperty(effectChildNode, "position-x", 0);
+ int offsetY = XML::getProperty(effectChildNode, "position-y", 0);
+ int offsetZ = XML::getProperty(effectChildNode, "position-z", 0);
+
+ int particleX = (int)mPosX + pixelX + offsetX;
+ int particleY = (int)mPosY + pixelY + offsetY;
+ int particleZ = (int)mPosZ + offsetZ;
+
+ int lifetime = XML::getProperty(effectChildNode, "lifetime", -1);
+
+ newParticle->setPosition(particleX, particleY, particleZ);
+ newParticle->setLifetime(lifetime);
+
+ // Look for additional emitters for this particle
+ for_each_xml_child_node(emitterNode, effectChildNode)
+ {
+ if (!xmlStrEqual(emitterNode->name, BAD_CAST "emitter"))
+ continue;
+
+ ParticleEmitter *newEmitter;
+ newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap);
+ newParticle->addEmitter(newEmitter);
+ }
+
+ mChildParticles.push_back(newParticle);
+ }
+
+ return newParticle;
+}
+
+
+Particle*
+Particle::addTextSplashEffect(std::string text,
+ int colorR, int colorG, int colorB,
+ gcn::Font *font, int x, int y)
+{
+ Particle *newParticle = new TextParticle(mMap, text, colorR, colorG, colorB,
+ font);
+ newParticle->setPosition(x, y, 0);
+ newParticle->setVector ( ((rand()%100) - 50) / 200.0f, // X vector
+ ((rand()%100) - 50) / 200.0f, // Y vector
+ ((rand()%100) / 200.0f) + 4.0f // Z vector
+ );
+ newParticle->setGravity(0.1f);
+ newParticle->setBounce(0.5f);
+ newParticle->setLifetime(200);
+ newParticle->setFadeOut(100);
+
+ mChildParticles.push_back(newParticle);
+
+ return newParticle;
+}
+
+
+void
+Particle::setMap(Map *map)
+{
+ mMap = map;
+ if (mMap) setSpriteIterator(mMap->addSprite(this));
+
+ // TODO: Create map emitters based on emitter data in map data
+}
+
+
+Particle::~Particle()
+{
+ // Remove from map sprite list
+ if (mMap) mMap->removeSprite(mSpriteIterator);
+ // Delete child emitters and child particles
+ clear();
+ Particle::particleCount--;
+}
+
+
+void
+Particle::clear()
+{
+ std::for_each(mChildEmitters.begin(), mChildEmitters.end(),
+ make_dtor(mChildEmitters));
+ mChildEmitters.clear();
+
+ std::for_each(mChildParticles.begin(), mChildParticles.end(),
+ make_dtor(mChildParticles));
+ mChildParticles.clear();
+}
diff --git a/src/particle.h b/src/particle.h
new file mode 100644
index 00000000..7a747a5f
--- /dev/null
+++ b/src/particle.h
@@ -0,0 +1,288 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 _PARTICLE_H
+#define _PARTICLE_H
+
+#include <list>
+#include <string>
+
+#include <guichan/color.hpp>
+
+#include "guichanfwd.h"
+#include "sprite.h"
+
+
+class Map;
+class Particle;
+class ParticleEmitter;
+
+typedef std::list<Particle *> Particles;
+typedef Particles::iterator ParticleIterator;
+typedef std::list<ParticleEmitter *> Emitters;
+typedef Emitters::iterator EmitterIterator;
+
+/**
+ * A particle spawned by a ParticleEmitter.
+ */
+class Particle : public Sprite
+{
+ public:
+ static const float PARTICLE_SKY; /**< Maximum Z position of particles */
+ static int fastPhysics; /**< Mode of squareroot calculation */
+ static int particleCount; /**< Current number of particles */
+ static int maxCount; /**< Maximum number of particles */
+ static int emitterSkip; /**< Duration of pause between two emitter updates in ticks */
+
+ /**
+ * Constructor.
+ *
+ * @param map the map this particle will add itself to, may be NULL
+ */
+ Particle(Map *map);
+
+ /**
+ * Destructor.
+ */
+ ~Particle();
+
+ /**
+ * Deletes all child particles and emitters
+ */
+ void
+ clear();
+
+ /**
+ * Gives a particle the properties of an engine root particle and loads
+ * the particle-related config settings
+ */
+ void
+ setupEngine();
+
+ /**
+ * Updates particle position, returns false when the particle should
+ * be deleted
+ */
+ virtual bool
+ update();
+
+ /**
+ * Draws the particle image
+ */
+ virtual void
+ draw(Graphics *graphics, int offsetX, int offsetY) const;
+
+ /**
+ * Necessary for sorting with the other sprites
+ */
+ virtual int
+ getPixelY() const
+ {
+ return (int)(mPosY + mPosZ) - 64;
+ };
+
+ /*
+ Basic Particle properties:
+ */
+
+ /**
+ * Sets the map the particle is on.
+ */
+ void setMap(Map *map);
+
+ /**
+ * Creates a child particle that hosts some emitters described in the
+ * particleEffectFile.
+ */
+ Particle*
+ addEffect(std::string particleEffectFile, int pixelX, int pixelY);
+
+ /**
+ * Creates a standalone text particle.
+ */
+ Particle*
+ addTextSplashEffect(std::string text, int colorR, int colorG, int colorB,
+ gcn::Font *font, int x, int y);
+
+ /**
+ * Adds an emitter to the particle.
+ */
+ void
+ addEmitter (ParticleEmitter* emitter)
+ { mChildEmitters.push_back(emitter); }
+
+ /**
+ * Sets the position in 3 dimensional space in pixels relative to map
+ */
+ void
+ setPosition(float x, float y, float z)
+ { mPosX = x; mPosY = y; mPosZ = z; }
+
+ /**
+ * Sets the position in 2 dimensional space in pixels relative to map
+ */
+ void
+ setPosition(float x, float y)
+ { mPosX = x; mPosY = y; }
+
+ float getPosX() const
+ { return mPosX; }
+
+ float getPosY() const
+ { return mPosY; }
+
+ float getPosZ() const
+ { return mPosZ; }
+
+ /**
+ * Changes the particle position relative
+ */
+ void
+ moveBy(float x, float y, float z)
+ { mPosX += x; mPosY += y; mPosZ += z; }
+
+ /**
+ * Sets the time in game ticks until the particle is destroyed.
+ */
+ void
+ setLifetime(int lifetime)
+ { mLifetimeLeft = lifetime; mLifetimePast = 0; }
+
+ /**
+ * Sets the age of the pixel in game ticks where the particle has
+ * faded in completely
+ */
+ void
+ setFadeOut (int fadeOut)
+ { mFadeOut = fadeOut; }
+
+ /**
+ * Sets the remaining particle lifetime where the particle starts to
+ * fade out
+ */
+ void
+ setFadeIn (int fadeIn)
+ { mFadeIn = fadeIn; }
+
+ /**
+ * Sets the sprite iterator of the particle on the current map to make
+ * it easier to remove the particle from the map when it is destroyed
+ */
+ void
+ setSpriteIterator(std::list<Sprite*>::iterator spriteIterator)
+ { mSpriteIterator = spriteIterator; }
+
+ /**
+ * Gets the sprite iterator of the particle on the current map
+ */
+ std::list<Sprite*>::iterator
+ getSpriteIterator() const
+ { return mSpriteIterator; }
+
+ /**
+ * Sets the current velocity in 3 dimensional space
+ */
+ void
+ setVector(float x, float y, float z)
+ { mVectorX = x; mVectorY = y; mVectorZ = z; }
+
+ /**
+ * Sets the downward acceleration
+ */
+ void
+ setGravity(float g)
+ { mGravity = g; }
+
+ /**
+ * Sets the ammount of random vector changes
+ */
+ void
+ setRandomnes(int r)
+ { mRandomnes = r; }
+
+ /**
+ * Sets the ammount of velocity particles retain after
+ * hitting the ground.
+ */
+ void
+ setBounce(float bouncieness)
+ { mBounce = bouncieness; }
+
+ /**
+ * Makes the particle move toward another particle with a
+ * given acceleration and momentum
+ */
+ void setDestination(Particle *target, float accel, float moment)
+ { mTarget = target; mAcceleration = accel; mMomentum = moment; }
+
+ /**
+ * Sets the distance in pixel the particle can come near the target
+ * particle before it is destroyed. Does only make sense after a
+ * target particle has been set using setDestination.
+ */
+ void setDieDistance(float dist)
+ { mInvDieDistance = 1.0f / dist; }
+
+ /**
+ * Manually marks the particle for deletion
+ */
+ void kill()
+ { mAlive = false; mAutoDelete = true; }
+
+ /**
+ * After calling this function the particle will only request
+ * deletion when kill() is called
+ */
+ void disableAutoDelete()
+ { mAutoDelete = false; }
+
+ protected:
+ bool mAlive; /**< Is the particle supposed to be drawn and updated?*/
+ float mPosX, mPosY, mPosZ; /**< Position in 3 dimensonal space - pixel based relative to map */
+ int mLifetimeLeft; /**< Lifetime left in game ticks*/
+ int mLifetimePast; /**< Age of the particle in game ticks*/
+ int mFadeOut; /**< Lifetime in game ticks left where fading out begins*/
+ int mFadeIn; /**< Age in game ticks where fading in is finished*/
+
+ private:
+ // generic properties
+ bool mAutoDelete; /**< May the particle request its deletion by the parent particle?*/
+ Map *mMap; /**< Map the particle is on*/
+ std::list<Sprite*>::iterator mSpriteIterator; /**< iterator of the particle on the current map */
+ Emitters mChildEmitters; /**< List of child emitters*/
+ Particles mChildParticles; /**< List of particles controlled by this particle*/
+ //dynamic particle
+ float mVectorX, mVectorY, mVectorZ; /**< Speed in 3 dimensional space in pixels per game-tick */
+ float mGravity; /**< Downward acceleration in pixels per game-tick²*/
+ int mRandomnes; /**< Ammount of random vector change*/
+ float mBounce; /**< How much the particle bounces off when hitting the ground*/
+ //follow-point particles
+ Particle *mTarget; /**< The particle that attracts this particle*/
+ float mAcceleration; /**< Acceleration towards the target particle in pixels per game-tick²*/
+ float mInvDieDistance; /**< Distance in pixels from the target particle that causes the destruction of the particle*/
+ float mMomentum; /**< How much speed the particle retains after each game tick*/
+};
+
+extern Particle *particleEngine;
+
+#endif
diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp
new file mode 100644
index 00000000..2387c652
--- /dev/null
+++ b/src/particleemitter.cpp
@@ -0,0 +1,327 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 "particleemitter.h"
+
+#include "animationparticle.h"
+#include "imageparticle.h"
+#include "log.h"
+#include "particle.h"
+
+#include "resources/animation.h"
+#include "resources/image.h"
+#include "resources/resourcemanager.h"
+#include "resources/imageset.h"
+
+#include <cmath>
+
+#define SIN45 0.707106781f
+#define DEG_RAD_FACTOR 0.017453293f
+
+ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map):
+ mParticleImage(0)
+{
+ mMap = map;
+ mParticleTarget = target;
+
+ //initializing default values
+ mParticlePosX.set(0.0f);
+ mParticlePosY.set(0.0f);
+ mParticlePosZ.set(0.0f);
+ mParticleAngleHorizontal.set(0.0f);
+ mParticleAngleVertical.set(0.0f);
+ mParticlePower.set(0.0f);
+ mParticleGravity.set(0.0f);
+ mParticleRandomnes.set(0);
+ mParticleBounce.set(0.0f);
+ mParticleAcceleration.set(0.0f);
+ mParticleDieDistance.set(-1.0f);
+ mParticleMomentum.set(1.0f);
+ mParticleLifetime.set(-1);
+ mParticleFadeOut.set(0);
+ mParticleFadeIn.set(0);
+ mOutput.set(1);
+
+ for_each_xml_child_node(propertyNode, emitterNode)
+ {
+ if (xmlStrEqual(propertyNode->name, BAD_CAST "property"))
+ {
+ std::string name = XML::getProperty(propertyNode, "name", "");
+
+ if (name == "position-x")
+ {
+ mParticlePosX = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "position-y")
+ {
+
+ mParticlePosY = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "position-z")
+ {
+ mParticlePosZ = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "image")
+ {
+ std::string image = XML::getProperty(propertyNode, "value", "");
+ // Don't leak when multiple images are defined
+ if (image != "" && !mParticleImage)
+ {
+ ResourceManager *resman = ResourceManager::getInstance();
+ mParticleImage = resman->getImage(image);
+ }
+ }
+ else if (name == "horizontal-angle")
+ {
+ mParticleAngleHorizontal = readMinMax(propertyNode, 0.0f);
+ mParticleAngleHorizontal.minVal *= DEG_RAD_FACTOR;
+ mParticleAngleHorizontal.maxVal *= DEG_RAD_FACTOR;
+ }
+ else if (name == "vertical-angle")
+ {
+ mParticleAngleVertical = readMinMax(propertyNode, 0.0f);
+ mParticleAngleVertical.minVal *= DEG_RAD_FACTOR;
+ mParticleAngleVertical.maxVal *= DEG_RAD_FACTOR;
+ }
+ else if (name == "power")
+ {
+ mParticlePower = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "gravity")
+ {
+ mParticleGravity = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "randomnes")
+ {
+ mParticleRandomnes = readMinMax(propertyNode, 0);
+ }
+ else if (name == "bounce")
+ {
+ mParticleBounce = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "lifetime")
+ {
+ mParticleLifetime = readMinMax(propertyNode, 0);
+ mParticleLifetime.minVal += 1;
+ }
+ else if (name == "output")
+ {
+ mOutput = readMinMax(propertyNode, 0);
+ mOutput.maxVal +=1;
+ }
+ else if (name == "acceleration")
+ {
+ mParticleAcceleration = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "die-distance")
+ {
+ mParticleDieDistance = readMinMax(propertyNode, 0.0f);
+ }
+ else if (name == "momentum")
+ {
+ mParticleMomentum = readMinMax(propertyNode, 1.0f);
+ }
+ else if (name == "fade-out")
+ {
+ mParticleFadeOut = readMinMax(propertyNode, 0);
+ }
+ else if (name == "fade-in")
+ {
+ mParticleFadeIn = readMinMax(propertyNode, 0);
+ }
+ else
+ {
+ logger->log("Particle Engine: Warning, unknown emitter property \"%s\"",
+ name.c_str()
+ );
+ }
+ }
+ else if (xmlStrEqual(propertyNode->name, BAD_CAST "emitter"))
+ {
+ ParticleEmitter newEmitter(propertyNode, mParticleTarget, map);
+ mParticleChildEmitters.push_back(newEmitter);
+ }
+ else if (xmlStrEqual(propertyNode->name, BAD_CAST "animation"))
+ {
+ ImageSet *imageset = ResourceManager::getInstance()->getImageSet(
+ XML::getProperty(propertyNode, "imageset", ""),
+ XML::getProperty(propertyNode, "width", 0),
+ XML::getProperty(propertyNode, "height", 0)
+ );
+
+ // Get animation frames
+ for_each_xml_child_node(frameNode, propertyNode)
+ {
+ int delay = XML::getProperty(frameNode, "delay", 0);
+ int offsetX = XML::getProperty(frameNode, "offsetX", 0);
+ int offsetY = XML::getProperty(frameNode, "offsetY", 0);
+ offsetY -= imageset->getHeight() - 32;
+ offsetX -= imageset->getWidth() / 2 - 16;
+
+ if (xmlStrEqual(frameNode->name, BAD_CAST "frame"))
+ {
+ int index = XML::getProperty(frameNode, "index", -1);
+
+ if (index < 0)
+ {
+ logger->log("No valid value for 'index'");
+ continue;
+ }
+
+ Image *img = imageset->get(index);
+
+ if (!img)
+ {
+ logger->log("No image at index " + (index));
+ continue;
+ }
+
+ mParticleAnimation.addFrame(img, delay, offsetX, offsetY);
+ }
+ else if (xmlStrEqual(frameNode->name, BAD_CAST "sequence"))
+ {
+ int start = XML::getProperty(frameNode, "start", -1);
+ int end = XML::getProperty(frameNode, "end", -1);
+
+ if (start < 0 || end < 0)
+ {
+ logger->log("No valid value for 'start' or 'end'");
+ continue;
+ }
+
+ while (end >= start)
+ {
+ Image *img = imageset->get(start);
+
+ if (!img)
+ {
+ logger->log("No image at index " +
+ (start));
+ continue;
+ }
+
+ mParticleAnimation.addFrame(img, delay, offsetX, offsetY);
+ start++;
+ }
+ }
+ else if (xmlStrEqual(frameNode->name, BAD_CAST "end"))
+ {
+ mParticleAnimation.addTerminator();
+ }
+ } // for frameNode
+ }
+ }
+}
+
+
+ParticleEmitter::~ParticleEmitter()
+{
+ if (mParticleImage)
+ {
+ mParticleImage->decRef();
+ }
+}
+
+
+template <typename T> MinMax<T>
+ParticleEmitter::readMinMax(xmlNodePtr propertyNode, T def)
+{
+ MinMax<T> retval;
+
+ def = (T)XML::getFloatProperty(propertyNode, "value", (double)def);
+ retval.set ( (T)XML::getFloatProperty(propertyNode, "min", (double)def),
+ (T)XML::getFloatProperty(propertyNode, "max", (double)def)
+ );
+
+ return retval;
+}
+
+
+std::list<Particle *>
+ParticleEmitter::createParticles()
+{
+ std::list<Particle *> newParticles;
+
+ for (int i = mOutput.value(); i > 0; i--)
+ {
+ // Limit maximum particles
+ if (Particle::particleCount > Particle::maxCount) break;
+
+ Particle *newParticle;
+ if (mParticleImage)
+ {
+ newParticle = new ImageParticle(mMap, mParticleImage);
+ }
+ else if (mParticleAnimation.getLength() > 0)
+ {
+ Animation *newAnimation = new Animation(mParticleAnimation);
+ newParticle = new AnimationParticle(mMap, newAnimation);
+ }
+ else
+ {
+ newParticle = new Particle(mMap);
+ }
+
+
+ newParticle->setPosition(
+ mParticlePosX.value(),
+ mParticlePosY.value(),
+ mParticlePosZ.value()
+ );
+
+ float angleH = mParticleAngleHorizontal.value();
+ float angleV = mParticleAngleVertical.value();
+ float power = mParticlePower.value();
+ newParticle->setVector(
+ cos(angleH) * cos(angleV) * power,
+ sin(angleH) * cos(angleV) * SIN45 * power,
+ sin(angleV) * SIN45 * power
+ );
+
+ newParticle->setRandomnes(mParticleRandomnes.value());
+ newParticle->setGravity(mParticleGravity.value());
+ newParticle->setBounce(mParticleBounce.value());
+
+ newParticle->setDestination(mParticleTarget,
+ mParticleAcceleration.value(),
+ mParticleMomentum.value()
+ );
+ newParticle->setDieDistance(mParticleDieDistance.value());
+
+ newParticle->setLifetime(mParticleLifetime.value());
+ newParticle->setFadeOut(mParticleFadeOut.value());
+ newParticle->setFadeIn(mParticleFadeIn.value());
+
+ for ( std::list<ParticleEmitter>::iterator i = mParticleChildEmitters.begin();
+ i != mParticleChildEmitters.end();
+ i++
+ )
+ {
+ newParticle->addEmitter(new ParticleEmitter(*i));
+ }
+
+ newParticles.push_back(newParticle);
+ }
+
+ return newParticles;
+}
diff --git a/src/particleemitter.h b/src/particleemitter.h
new file mode 100644
index 00000000..ca6d8622
--- /dev/null
+++ b/src/particleemitter.h
@@ -0,0 +1,120 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 _PARTICLEEMITTER_H
+#define _PARTICLEEMITTER_H
+
+#include <list>
+
+#include "utils/xml.h"
+#include "utils/minmax.h"
+
+#include "resources/animation.h"
+
+class Image;
+class Map;
+class Particle;
+
+/**
+ * Every Particle can have one or more particle emitters that create new
+ * particles when they are updated
+ */
+class ParticleEmitter
+{
+ public:
+ /**
+ * Constructor.
+ */
+ ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map);
+
+ /**
+ * Destructor.
+ */
+ ~ParticleEmitter();
+
+ /**
+ * Spawns new particles
+ * @return: a list of created particles
+ */
+ std::list<Particle *> createParticles();
+
+ /**
+ * Sets the target of the particles that are created
+ */
+ void
+ setTarget(Particle *target)
+ { mParticleTarget = target; };
+
+ private:
+ template <typename T> MinMax<T> readMinMax(xmlNodePtr propertyNode, T def);
+
+ /**
+ * initial position of particles:
+ */
+ MinMax<float> mParticlePosX, mParticlePosY, mParticlePosZ;
+
+ /**
+ * initial vector of particles:
+ */
+ MinMax<float> mParticleAngleHorizontal, mParticleAngleVertical;
+
+ /**
+ * Initial velocity of particles
+ */
+ MinMax<float> mParticlePower;
+
+ /*
+ * Vector changing of particles:
+ */
+ MinMax<float> mParticleGravity;
+ MinMax<int> mParticleRandomnes;
+ MinMax<float> mParticleBounce;
+
+ /*
+ * Properties of targeting particles:
+ */
+ Particle *mParticleTarget;
+ MinMax<float> mParticleAcceleration;
+ MinMax<float> mParticleDieDistance;
+ MinMax<float> mParticleMomentum;
+
+ /*
+ * Behavior over time of the particles:
+ */
+ MinMax<int> mParticleLifetime;
+ MinMax<int> mParticleFadeOut;
+ MinMax<int> mParticleFadeIn;
+
+ Map *mMap; /**< Map the particles are spawned on */
+
+ MinMax<int> mOutput; /**< Number of particles spawned per update */
+
+ Image *mParticleImage; /**< Particle image, if used */
+
+ /** Filename of particle animation file */
+ Animation mParticleAnimation;
+
+ /** List of emitters the spawned particles are equipped with */
+ std::list<ParticleEmitter> mParticleChildEmitters;
+};
+#endif
diff --git a/src/resources/animation.h b/src/resources/animation.h
index d0d11c69..aad93cda 100644
--- a/src/resources/animation.h
+++ b/src/resources/animation.h
@@ -54,7 +54,7 @@ class Animation
Animation();
/**
- * Appends a new animation at the end of the sequence
+ * Appends a new animation at the end of the sequence.
*/
void
addFrame(Image *image, unsigned int delay, int offsetX, int offsetY);
diff --git a/src/resources/image.cpp b/src/resources/image.cpp
index a27783d4..d7d4e64b 100644
--- a/src/resources/image.cpp
+++ b/src/resources/image.cpp
@@ -187,19 +187,22 @@ Image* Image::load(void *buffer, unsigned int bufferSize,
bool hasAlpha = false;
- // Figure out whether the image uses its alpha layer
- for (int i = 0; i < tmpImage->w * tmpImage->h; ++i)
+ if (tmpImage->format->BitsPerPixel == 32)
{
- Uint8 r, g, b, a;
- SDL_GetRGBA(
- ((Uint32*) tmpImage->pixels)[i],
- tmpImage->format,
- &r, &g, &b, &a);
-
- if (a != 255)
+ // Figure out whether the image uses its alpha layer
+ for (int i = 0; i < tmpImage->w * tmpImage->h; ++i)
{
- hasAlpha = true;
- break;
+ Uint8 r, g, b, a;
+ SDL_GetRGBA(
+ ((Uint32*) tmpImage->pixels)[i],
+ tmpImage->format,
+ &r, &g, &b, &a);
+
+ if (a != 255)
+ {
+ hasAlpha = true;
+ break;
+ }
}
}
diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp
index fda8916d..2230cb6a 100644
--- a/src/resources/mapreader.cpp
+++ b/src/resources/mapreader.cpp
@@ -231,6 +231,30 @@ MapReader::readMap(xmlNodePtr node, const std::string &path)
{
readProperties(childNode, map);
}
+ else if (xmlStrEqual(childNode->name, BAD_CAST "objectgroup"))
+ {
+ for_each_xml_child_node(objectNode, childNode)
+ {
+ if (xmlStrEqual(objectNode->name, BAD_CAST "object"))
+ {
+ std::string objName = XML::getProperty(objectNode, "name", "");
+ std::string objType = XML::getProperty(objectNode, "type", "");
+ int objX = XML::getProperty(objectNode, "x", 0);
+ int objY = XML::getProperty(objectNode, "y", 0);
+
+ logger->log("- Loading object name: %s type: %s at %d:%d",
+ objName.c_str(), objType.c_str(), objX, objY);
+ if (objType == "PARTICLE_EFFECT")
+ {
+ map->addParticleEffect(objName, objX, objY);
+ }
+ else
+ {
+ logger->log(" Warning: Unknown object type");
+ }
+ }
+ }
+ }
}
map->initializeOverlays();
diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp
index ac3ac3bc..89afc549 100644
--- a/src/resources/monsterdb.cpp
+++ b/src/resources/monsterdb.cpp
@@ -83,6 +83,27 @@ MonsterDB::load()
currentInfo->setName (XML::getProperty(monsterNode, "name", "unnamed"));
+ std::string targetCursor;
+ targetCursor = XML::getProperty(monsterNode, "targetCursor", "medium");
+ if (targetCursor == "small")
+ {
+ currentInfo->setTargetCursorSize(Being::TC_SMALL);
+ }
+ else if (targetCursor == "medium")
+ {
+ currentInfo->setTargetCursorSize(Being::TC_MEDIUM);
+ }
+ else if (targetCursor == "large")
+ {
+ currentInfo->setTargetCursorSize(Being::TC_LARGE);
+ }
+ else
+ {
+ logger->log("MonsterDB: Unknown target cursor type \"%s\" for %s - using medium sized one",
+ targetCursor.c_str(), currentInfo->getName().c_str());
+ currentInfo->setTargetCursorSize(Being::TC_MEDIUM);
+ }
+
//iterate <sprite>s and <sound>s
for_each_xml_child_node(spriteNode, monsterNode)
{
diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h
index 05d4c014..d2a0a2c8 100644
--- a/src/resources/monsterinfo.h
+++ b/src/resources/monsterinfo.h
@@ -28,6 +28,9 @@
#include <string>
#include <vector>
+#include "../being.h"
+
+
enum SoundEvent
{
EVENT_HIT,
@@ -62,7 +65,11 @@ class MonsterInfo
setSprite(std::string filename) { mSprite = filename; }
void
- addSound (SoundEvent event, std::string filename);
+ setTargetCursorSize(Being::TargetCursorSize targetCursorSize)
+ { mTargetCursorSize = targetCursorSize; }
+
+ void
+ addSound(SoundEvent event, std::string filename);
const std::string&
getName () const { return mName; };
@@ -70,13 +77,16 @@ class MonsterInfo
const std::string&
getSprite () const { return mSprite; };
+ const Being::TargetCursorSize
+ getTargetCursorSize() const { return mTargetCursorSize; }
+
std::string
getSound (SoundEvent event) const;
private:
std::string mName;
std::string mSprite;
-
+ Being::TargetCursorSize mTargetCursorSize;
std::map<SoundEvent, std::vector<std::string>* > mSounds;
};
diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp
index 5fc35bcd..18e732ef 100644
--- a/src/simpleanimation.cpp
+++ b/src/simpleanimation.cpp
@@ -23,6 +23,91 @@
#include "simpleanimation.h"
+#include "graphics.h"
+#include "log.h"
+
+#include "resources/image.h"
+#include "resources/resourcemanager.h"
+#include "resources/imageset.h"
+
+
+SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode):
+ mAnimationTime(0),
+ mAnimationPhase(0)
+{
+ mAnimation = new Animation();
+
+ ImageSet *imageset = ResourceManager::getInstance()->getImageSet(
+ XML::getProperty(animationNode, "imageset", ""),
+ XML::getProperty(animationNode, "width", 0),
+ XML::getProperty(animationNode, "height", 0)
+ );
+
+ // Get animation frames
+ for ( xmlNodePtr frameNode = animationNode->xmlChildrenNode;
+ frameNode != NULL;
+ frameNode = frameNode->next)
+ {
+ int delay = XML::getProperty(frameNode, "delay", 0);
+ int offsetX = XML::getProperty(frameNode, "offsetX", 0);
+ int offsetY = XML::getProperty(frameNode, "offsetY", 0);
+ offsetY -= imageset->getHeight() - 32;
+ offsetX -= imageset->getWidth() / 2 - 16;
+
+ if (xmlStrEqual(frameNode->name, BAD_CAST "frame"))
+ {
+ int index = XML::getProperty(frameNode, "index", -1);
+
+ if (index < 0)
+ {
+ logger->log("No valid value for 'index'");
+ continue;
+ }
+
+ Image *img = imageset->get(index);
+
+ if (!img)
+ {
+ logger->log("No image at index " + (index));
+ continue;
+ }
+
+ mAnimation->addFrame(img, delay, offsetX, offsetY);
+ }
+ else if (xmlStrEqual(frameNode->name, BAD_CAST "sequence"))
+ {
+ int start = XML::getProperty(frameNode, "start", -1);
+ int end = XML::getProperty(frameNode, "end", -1);
+
+ if (start < 0 || end < 0)
+ {
+ logger->log("No valid value for 'start' or 'end'");
+ continue;
+ }
+
+ while (end >= start)
+ {
+ Image *img = imageset->get(start);
+
+ if (!img)
+ {
+ logger->log("No image at index " +
+ (start));
+ continue;
+ }
+
+ mAnimation->addFrame(img, delay, offsetX, offsetY);
+ start++;
+ }
+ }
+ else if (xmlStrEqual(frameNode->name, BAD_CAST "end"))
+ {
+ mAnimation->addTerminator();
+ }
+ }
+
+ mCurrentFrame = mAnimation->getFrame(0);
+}
void SimpleAnimation::update(unsigned int timePassed)
{
diff --git a/src/simpleanimation.h b/src/simpleanimation.h
index a56c31da..561c540d 100644
--- a/src/simpleanimation.h
+++ b/src/simpleanimation.h
@@ -26,7 +26,10 @@
#include "resources/animation.h"
+#include "utils/xml.h"
+
class Frame;
+class Graphics;
/**
* This class is a leightweight alternative to the AnimatedSprite class.
@@ -35,6 +38,9 @@ class Frame;
class SimpleAnimation
{
public:
+ /**
+ * Creates a simple animation with an already created animation.
+ */
SimpleAnimation(Animation *animation):
mAnimation(animation),
mAnimationTime(0),
@@ -42,6 +48,11 @@ class SimpleAnimation
mCurrentFrame(mAnimation->getFrame(0))
{};
+ /**
+ * Creates a simple animation that creates its animation from XML Data.
+ */
+ SimpleAnimation(xmlNodePtr animationNode);
+
~SimpleAnimation();
void update(unsigned int timePassed);
@@ -55,10 +66,10 @@ class SimpleAnimation
/** Time in game ticks the current frame is shown. */
unsigned int mAnimationTime;
- /** Index of current animation frame. */
+ /** Index of current animation phase. */
unsigned int mAnimationPhase;
- /** Current animation frame. */
+ /** Current animation phase. */
Frame *mCurrentFrame;
};
diff --git a/src/sprite.h b/src/sprite.h
index 51811149..89780519 100644
--- a/src/sprite.h
+++ b/src/sprite.h
@@ -50,6 +50,22 @@ class Sprite
draw(Graphics *graphics, int offsetX, int offsetY) const = 0;
/**
+ * Returns the horizontal size of the sprites graphical representation
+ * in pixels or 0 when it is undefined.
+ */
+ virtual int
+ getWidth() const
+ { return 0; }
+
+ /**
+ * Returns the vertical size of the sprites graphical representation
+ * in pixels or 0 when it is undefined.
+ */
+ virtual int
+ getHeight() const
+ { return 0; }
+
+ /**
* Returns the pixel Y coordinate of the sprite.
*/
virtual int
diff --git a/src/textparticle.cpp b/src/textparticle.cpp
new file mode 100644
index 00000000..dd01d2fe
--- /dev/null
+++ b/src/textparticle.cpp
@@ -0,0 +1,64 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 "textparticle.h"
+
+#include "graphics.h"
+
+TextParticle::TextParticle(Map *map, const std::string &text,
+ int colorR, int colorG, int colorB, gcn::Font *font):
+ Particle(map),
+ mText(text),
+ mTextFont(font),
+ mColorR(colorR),
+ mColorG(colorG),
+ mColorB(colorB)
+{
+}
+
+void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const
+{
+ if (!mAlive)
+ return;
+
+ int screenX = (int)mPosX + offsetX;
+ int screenY = (int)mPosY - int(mPosZ) + offsetY;
+
+ int alpha = 255;
+
+ if (mLifetimeLeft > -1 && mLifetimeLeft < mFadeOut)
+ {
+ alpha *= mLifetimeLeft;
+ alpha /= mFadeOut;
+ };
+
+ if (mLifetimePast < mFadeIn)
+ {
+ alpha *= mLifetimePast;
+ alpha /= mFadeIn;
+ }
+
+ graphics->setFont(mTextFont);
+ graphics->setColor(gcn::Color (mColorR, mColorG, mColorB, alpha));
+ graphics->drawText(mText, screenX, screenY, gcn::Graphics::CENTER);
+}
diff --git a/src/textparticle.h b/src/textparticle.h
new file mode 100644
index 00000000..b365c885
--- /dev/null
+++ b/src/textparticle.h
@@ -0,0 +1,53 @@
+/*
+ * The Mana World
+ * Copyright 2006 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 _TEXTPARTICLE_H
+#define _TEXTPARTICLE_H
+
+#include "particle.h"
+
+#include <guichan/color.hpp>
+
+#include "guichanfwd.h"
+
+class TextParticle : public Particle
+{
+ public:
+ TextParticle(Map *map, const std::string &text,
+ int colorR, int colorG, int colorB, gcn::Font *font);
+ /**
+ * Draws the particle image
+ */
+ virtual void
+ draw(Graphics *graphics, int offsetX, int offsetY) const;
+
+ // hack to improve text visibility
+ virtual int getPixelY() const { return (int)(mPosY + mPosZ); }
+
+ private:
+ std::string mText; /**< Text of the particle */
+ gcn::Font *mTextFont; /**< Font used for drawing the text */
+ int mColorR, mColorG, mColorB; /**< Color used for drawing the text */
+};
+
+#endif
diff --git a/src/utils/fastsqrt.h b/src/utils/fastsqrt.h
new file mode 100644
index 00000000..8da1d354
--- /dev/null
+++ b/src/utils/fastsqrt.h
@@ -0,0 +1,24 @@
+/* A very fast function to calculate the approximate inverse square root of a
+ * floating point value and a helper function that uses it for getting the
+ * normal squareroot. For an explanation of the inverse squareroot function
+ * read:
+ * http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf
+ *
+ * Unfortunately the original creator of this function seems to be unknown.
+ */
+
+float fastInvSqrt(float x)
+{
+ union { int i; float x; } tmp;
+ float xhalf = 0.5f * x;
+ tmp.x = x;
+ tmp.i = 0x5f375a86 - (tmp.i >> 1);
+ x = tmp.x;
+ x = x * (1.5f - xhalf * x * x);
+ return x;
+}
+
+float fastSqrt(float x)
+{
+ return 1.0f / fastInvSqrt(x);
+}
diff --git a/src/utils/minmax.h b/src/utils/minmax.h
new file mode 100644
index 00000000..1add2b7e
--- /dev/null
+++ b/src/utils/minmax.h
@@ -0,0 +1,47 @@
+/*
+ * The Mana World
+ * Copyright 2006 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
+ *
+ */
+
+/**
+ * Returns a random numeric value that is larger than or equal min and smaller
+ * than max
+ */
+
+template <typename T> struct MinMax
+{
+ void set(T min, T max)
+ {
+ minVal=min; maxVal=max;
+ }
+
+ void set(T val)
+ {
+ set(val, val);
+ }
+
+ T value()
+ {
+ return (T)(minVal + (maxVal - minVal) * (rand() / ((double) RAND_MAX + 1)));
+ }
+
+ T minVal;
+ T maxVal;
+};
diff --git a/src/utils/wingettimeofday.h b/src/utils/wingettimeofday.h
index a5537f39..28afb7e5 100644
--- a/src/utils/wingettimeofday.h
+++ b/src/utils/wingettimeofday.h
@@ -1,113 +1,113 @@
-/*
- * The Mana World
- * Copyright 2004 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_WINGETTIMEOFDAY_H_
-#define _TMW_WINGETTIMEOFDAY_H_
-
-#ifdef WIN32
-
-#include <windows.h>
-
-/*
- * the function gettimeofday() is available on UNIX but not on windows.
- * this header defines a windows implementation as a
- * GetSystemTimeAsFileTime() wrapper.
- */
-
-int gettimeofday(struct timeval* tv, void *tz)
-/*---------------------------------------------------------------
- * Copyright (c) 1999,2000,2001,2002,2003
- * The Board of Trustees of the University of Illinois
- * All Rights Reserved.
- *---------------------------------------------------------------
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software (Iperf) and associated
- * documentation files (the "Software"), to deal in the Software
- * without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute,
- * sublicense, and/or sell copies of the Software, and to permit
- * persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- *
- * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and
- * the following disclaimers.
- *
- *
- * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimers in the documentation and/or other materials
- * provided with the distribution.
- *
- *
- * Neither the names of the University of Illinois, NCSA,
- * nor the names of its contributors may be used to endorse
- * or promote products derived from this Software without
- * specific prior written permission.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- * ________________________________________________________________
- * National Laboratory for Applied Network Research
- * National Center for Supercomputing Applications
- * University of Illinois at Urbana-Champaign
- * http://www.ncsa.uiuc.edu
- * ________________________________________________________________
- *
- * gettimeofday.c
- * by Mark Gates <mgates@nlanr.net>
- * -------------------------------------------------------------------
- * A (hack) implementation of gettimeofday for Windows.
- * Since I send sec/usec in UDP packets, this made the most sense.
- * ------------------------------------------------------------------- */
-{
- FILETIME time;
- double timed;
-
- GetSystemTimeAsFileTime( &time );
-
- // Apparently Win32 has units of 1e-7 sec (tenths of microsecs)
- // 4294967296 is 2^32, to shift high word over
- // 11644473600 is the number of seconds between
- // the Win32 epoch 1601-Jan-01 and the Unix epoch 1970-Jan-01
- // Tests found floating point to be 10x faster than 64bit int math.
-
- timed = ((time.dwHighDateTime * 4294967296e-7) - 11644473600.0) +
- (time.dwLowDateTime * 1e-7);
-
- tv->tv_sec = (long) timed;
- tv->tv_usec = (long) ((timed - tv->tv_sec) * 1e6);
-
- return 0;
-}
-
-
-#endif // WIN32
-#endif
+/*
+ * The Mana World
+ * Copyright 2004 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_WINGETTIMEOFDAY_H_
+#define _TMW_WINGETTIMEOFDAY_H_
+
+#ifdef WIN32
+
+#include <windows.h>
+
+/*
+ * the function gettimeofday() is available on UNIX but not on windows.
+ * this header defines a windows implementation as a
+ * GetSystemTimeAsFileTime() wrapper.
+ */
+
+int gettimeofday(struct timeval* tv, void *tz)
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software (Iperf) and associated
+ * documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and
+ * the following disclaimers.
+ *
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimers in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ *
+ * Neither the names of the University of Illinois, NCSA,
+ * nor the names of its contributors may be used to endorse
+ * or promote products derived from this Software without
+ * specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * gettimeofday.c
+ * by Mark Gates <mgates@nlanr.net>
+ * -------------------------------------------------------------------
+ * A (hack) implementation of gettimeofday for Windows.
+ * Since I send sec/usec in UDP packets, this made the most sense.
+ * ------------------------------------------------------------------- */
+{
+ FILETIME time;
+ double timed;
+
+ GetSystemTimeAsFileTime( &time );
+
+ // Apparently Win32 has units of 1e-7 sec (tenths of microsecs)
+ // 4294967296 is 2^32, to shift high word over
+ // 11644473600 is the number of seconds between
+ // the Win32 epoch 1601-Jan-01 and the Unix epoch 1970-Jan-01
+ // Tests found floating point to be 10x faster than 64bit int math.
+
+ timed = ((time.dwHighDateTime * 4294967296e-7) - 11644473600.0) +
+ (time.dwLowDateTime * 1e-7);
+
+ tv->tv_sec = (long) timed;
+ tv->tv_usec = (long) ((timed - tv->tv_sec) * 1e6);
+
+ return 0;
+}
+
+
+#endif // WIN32
+#endif
diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp
index 7c917dc0..e30450f0 100644
--- a/src/utils/xml.cpp
+++ b/src/utils/xml.cpp
@@ -39,6 +39,20 @@ namespace XML
return ret;
}
+ double
+ getFloatProperty(xmlNodePtr node, const char* name, double def)
+ {
+ double &ret = def;
+
+ xmlChar *prop = xmlGetProp(node, BAD_CAST name);
+ if (prop) {
+ ret = atof((char*)prop);
+ xmlFree(prop);
+ }
+
+ return ret;
+ }
+
std::string
getProperty(xmlNodePtr node, const char *name, const std::string &def)
{
@@ -51,4 +65,13 @@ namespace XML
return def;
}
+
+ xmlNodePtr findFirstChildByName(xmlNodePtr parent, const char *name)
+ {
+ for_each_xml_child_node(child, parent)
+ if (xmlStrEqual(child->name, BAD_CAST name))
+ return child;
+
+ return NULL;
+ }
}
diff --git a/src/utils/xml.h b/src/utils/xml.h
index db4c264a..ef3bad3d 100644
--- a/src/utils/xml.h
+++ b/src/utils/xml.h
@@ -40,10 +40,21 @@ namespace XML
getProperty(xmlNodePtr node, const char *name, int def);
/**
+ * Gets an floating point property from an xmlNodePtr.
+ */
+ double
+ getFloatProperty(xmlNodePtr node, const char *name, double def);
+
+ /**
* Gets a string property from an xmlNodePtr.
*/
std::string
getProperty(xmlNodePtr node, const char *name, const std::string &def);
+
+ /**
+ * Finds the first child node with the given name
+ */
+ xmlNodePtr findFirstChildByName(xmlNodePtr parent, const char *name);
}
#define for_each_xml_child_node(var, parent) \