summaryrefslogtreecommitdiff
path: root/src/resources
diff options
context:
space:
mode:
Diffstat (limited to 'src/resources')
-rw-r--r--src/resources/beinginfo.cpp107
-rw-r--r--src/resources/beinginfo.h132
-rw-r--r--src/resources/emotedb.cpp111
-rw-r--r--src/resources/emotedb.h21
-rw-r--r--src/resources/image.h2
-rw-r--r--src/resources/imageset.cpp6
-rw-r--r--src/resources/imageset.h2
-rw-r--r--src/resources/itemdb.cpp571
-rw-r--r--src/resources/itemdb.h194
-rw-r--r--src/resources/iteminfo.cpp32
-rw-r--r--src/resources/iteminfo.h246
-rw-r--r--src/resources/mapreader.cpp13
-rw-r--r--src/resources/monsterdb.cpp76
-rw-r--r--src/resources/monsterdb.h9
-rw-r--r--src/resources/monsterinfo.cpp100
-rw-r--r--src/resources/monsterinfo.h106
-rw-r--r--src/resources/npcdb.cpp54
-rw-r--r--src/resources/npcdb.h22
-rw-r--r--src/resources/specialdb.cpp132
-rw-r--r--src/resources/specialdb.h72
-rw-r--r--src/resources/spritedef.cpp128
-rw-r--r--src/resources/spritedef.h83
-rw-r--r--src/resources/theme.cpp582
-rw-r--r--src/resources/theme.h252
-rw-r--r--src/resources/userpalette.cpp247
-rw-r--r--src/resources/userpalette.h210
-rw-r--r--src/resources/wallpaper.cpp10
27 files changed, 2649 insertions, 871 deletions
diff --git a/src/resources/beinginfo.cpp b/src/resources/beinginfo.cpp
new file mode 100644
index 00000000..e8824391
--- /dev/null
+++ b/src/resources/beinginfo.cpp
@@ -0,0 +1,107 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/beinginfo.h"
+
+#include "log.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+
+BeingInfo *BeingInfo::Unknown = new BeingInfo;
+
+BeingInfo::BeingInfo():
+ mName(_("unnamed")),
+ mTargetCursorSize(ActorSprite::TC_MEDIUM),
+ mWalkMask(Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER
+ | Map::BLOCKMASK_MONSTER),
+ mBlockType(Map::BLOCKTYPE_CHARACTER)
+{
+ SpriteDisplay display;
+ display.sprites.push_back(SpriteReference::Empty);
+
+ setDisplay(display);
+}
+
+BeingInfo::~BeingInfo()
+{
+ delete_all(mSounds);
+ delete_all(mAttacks);
+ mSounds.clear();
+}
+
+void BeingInfo::setDisplay(SpriteDisplay display)
+{
+ mDisplay = display;
+}
+
+void BeingInfo::setTargetCursorSize(const std::string &size)
+{
+ if (size == "small")
+ setTargetCursorSize(ActorSprite::TC_SMALL);
+ else if (size == "medium")
+ setTargetCursorSize(ActorSprite::TC_MEDIUM);
+ else if (size == "large")
+ setTargetCursorSize(ActorSprite::TC_LARGE);
+ else
+ {
+ logger->log("Unknown target cursor type \"%s\" for %s - using medium "
+ "sized one", size.c_str(), getName().c_str());
+ setTargetCursorSize(ActorSprite::TC_MEDIUM);
+ }
+}
+
+void BeingInfo::addSound(SoundEvent event, const std::string &filename)
+{
+ if (mSounds.find(event) == mSounds.end())
+ {
+ mSounds[event] = new std::vector<std::string>;
+ }
+
+ mSounds[event]->push_back("sfx/" + filename);
+}
+
+const std::string &BeingInfo::getSound(SoundEvent event) const
+{
+ static std::string empty("");
+
+ SoundEvents::const_iterator i = mSounds.find(event);
+ return (i == mSounds.end()) ? empty :
+ i->second->at(rand() % i->second->size());
+}
+
+const Attack *BeingInfo::getAttack(int type) const
+{
+ static Attack *empty = new Attack(SpriteAction::ATTACK, "", "");
+
+ Attacks::const_iterator i = mAttacks.find(type);
+ return (i == mAttacks.end()) ? empty : (*i).second;
+}
+
+void BeingInfo::addAttack(int id, std::string action,
+ const std::string &particleEffect,
+ const std::string &missileParticle)
+{
+ if (mAttacks[id])
+ delete mAttacks[id];
+
+ mAttacks[id] = new Attack(action, particleEffect, missileParticle);
+}
diff --git a/src/resources/beinginfo.h b/src/resources/beinginfo.h
new file mode 100644
index 00000000..52390976
--- /dev/null
+++ b/src/resources/beinginfo.h
@@ -0,0 +1,132 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BEINGINFO_H
+#define BEINGINFO_H
+
+#include "actorsprite.h"
+
+#include "resources/spritedef.h"
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+struct Attack {
+ std::string action;
+ std::string particleEffect;
+ std::string missileParticle;
+
+ Attack(std::string action, std::string particleEffect,
+ std::string missileParticle)
+ {
+ this->action = action;
+ this->particleEffect = particleEffect;
+ this->missileParticle = missileParticle;
+ }
+};
+
+typedef std::map<int, Attack*> Attacks;
+
+enum SoundEvent
+{
+ SOUND_EVENT_HIT,
+ SOUND_EVENT_MISS,
+ SOUND_EVENT_HURT,
+ SOUND_EVENT_DIE
+};
+
+typedef std::map<SoundEvent, std::vector<std::string>* > SoundEvents;
+
+/**
+ * Holds information about a certain type of monster. This includes the name
+ * of the monster, the sprite to display and the sounds the monster makes.
+ *
+ * @see MonsterDB
+ * @see NPCDB
+ */
+class BeingInfo
+{
+ public:
+ static BeingInfo *Unknown;
+
+ BeingInfo();
+
+ ~BeingInfo();
+
+ void setName(const std::string &name) { mName = name; }
+
+ const std::string &getName() const
+ { return mName; }
+
+ void setDisplay(SpriteDisplay display);
+
+ const SpriteDisplay &getDisplay() const
+ { return mDisplay; }
+
+ void setTargetCursorSize(const std::string &size);
+
+ void setTargetCursorSize(ActorSprite::TargetCursorSize targetSize)
+ { mTargetCursorSize = targetSize; }
+
+ ActorSprite::TargetCursorSize getTargetCursorSize() const
+ { return mTargetCursorSize; }
+
+ void addSound(SoundEvent event, const std::string &filename);
+
+ const std::string &getSound(SoundEvent event) const;
+
+ void addAttack(int id, std::string action,
+ const std::string &particleEffect,
+ const std::string &missileParticle);
+
+ const Attack *getAttack(int type) const;
+
+ void setWalkMask(unsigned char mask)
+ { mWalkMask = mask; }
+
+ /**
+ * Gets the way the being is blocked by other objects
+ */
+ unsigned char getWalkMask() const
+ { return mWalkMask; }
+
+ void setBlockType(Map::BlockType blockType)
+ { mBlockType = blockType; }
+
+ Map::BlockType getBlockType() const
+ { return mBlockType; }
+
+ private:
+ SpriteDisplay mDisplay;
+ std::string mName;
+ ActorSprite::TargetCursorSize mTargetCursorSize;
+ SoundEvents mSounds;
+ Attacks mAttacks;
+ unsigned char mWalkMask;
+ Map::BlockType mBlockType;
+};
+
+typedef std::map<int, BeingInfo*> BeingInfos;
+typedef BeingInfos::iterator BeingInfoIterator;
+
+#endif // BEINGINFO_H
diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp
index c24e760b..c542f7d0 100644
--- a/src/resources/emotedb.cpp
+++ b/src/resources/emotedb.cpp
@@ -20,16 +20,20 @@
#include "resources/emotedb.h"
-#include "animatedsprite.h"
+#include "configuration.h"
#include "log.h"
+#include "imagesprite.h"
+
+#include "resources/resourcemanager.h"
+#include "resources/image.h"
+#include "resources/imageset.h"
#include "utils/xml.h"
-#include "configuration.h"
namespace
{
- EmoteInfos mEmoteInfos;
- EmoteInfo mUnknown;
+ Emotes mEmotes;
+ Emote mUnknown;
bool mLoaded = false;
int mLastEmote = 0;
}
@@ -39,13 +43,12 @@ void EmoteDB::load()
if (mLoaded)
unload();
- mLastEmote = 0;
+ mUnknown.name = "unknown";
+ mUnknown.effect = 0;
+ mUnknown.sprite = new ImageSprite(
+ ResourceManager::getInstance()->getImage("graphics/sprites/error.png"));
- EmoteSprite *unknownSprite = new EmoteSprite;
- unknownSprite->sprite = AnimatedSprite::load(
- paths.getValue("spriteErrorFile", "error.xml") );
- unknownSprite->name = "unknown";
- mUnknown.sprites.push_back(unknownSprite);
+ mLastEmote = 0;
logger->log("Initializing emote database...");
@@ -71,28 +74,46 @@ void EmoteDB::load()
continue;
}
- EmoteInfo *currentInfo = new EmoteInfo;
+ Emote *currentEmote = new Emote;
+
+ currentEmote->name = XML::getProperty(emoteNode, "name", "unknown");
+ currentEmote->effect = XML::getProperty(emoteNode, "effectid", -1);
+
+ if (currentEmote->effect == -1)
+ {
+ logger->log("Emote Database: Warning: Emote with no attached effect!");
+ delete currentEmote;
+ continue;
+ }
+
+ const std::string imageName = XML::getProperty(emoteNode, "image", "");
+ const int width = XML::getProperty(emoteNode, "width", 0);
+ const int height = XML::getProperty(emoteNode, "height", 0);
- for_each_xml_child_node(spriteNode, emoteNode)
+ if (imageName.empty() || !(width > 0) || !(height > 0))
{
- if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite"))
- {
- EmoteSprite *currentSprite = new EmoteSprite;
- std::string file = paths.getValue("sprites",
- "graphics/sprites/")
- + (std::string) (const char*)
- spriteNode->xmlChildrenNode->content;
- currentSprite->sprite = AnimatedSprite::load(file,
- XML::getProperty(spriteNode, "variant", 0));
- currentInfo->sprites.push_back(currentSprite);
- }
- else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx"))
- {
- std::string particlefx = (const char*) spriteNode->xmlChildrenNode->content;
- currentInfo->particles.push_back(particlefx);
- }
+ logger->log("Emote Database: Warning: Emote with bad imageset values");
+ delete currentEmote;
+ continue;
}
- mEmoteInfos[id] = currentInfo;
+
+ ImageSet *is = ResourceManager::getInstance()->getImageSet(imageName,
+ width,
+ height);
+ if (!is || !(is->size() > 0))
+ {
+ logger->log("Emote Database: Error loading imageset");
+ delete is;
+ delete currentEmote;
+ continue;
+ }
+ else
+ {
+ // For now we just use the first image in the animation
+ currentEmote->sprite = new ImageSprite(is->get(0));
+ }
+
+ mEmotes[id] = currentEmote;
if (id > mLastEmote)
mLastEmote = id;
}
@@ -102,36 +123,22 @@ void EmoteDB::load()
void EmoteDB::unload()
{
- for (EmoteInfos::const_iterator i = mEmoteInfos.begin();
- i != mEmoteInfos.end();
- i++)
+ Emotes::iterator i;
+ for (i = mEmotes.begin(); i != mEmotes.end(); i++)
{
- while (!i->second->sprites.empty())
- {
- delete i->second->sprites.front()->sprite;
- delete i->second->sprites.front();
- i->second->sprites.pop_front();
- }
delete i->second;
}
- mEmoteInfos.clear();
-
- while (!mUnknown.sprites.empty())
- {
- delete mUnknown.sprites.front()->sprite;
- delete mUnknown.sprites.front();
- mUnknown.sprites.pop_front();
- }
+ mEmotes.clear();
mLoaded = false;
}
-const EmoteInfo *EmoteDB::get(int id)
+const Emote *EmoteDB::get(int id)
{
- EmoteInfos::const_iterator i = mEmoteInfos.find(id);
+ Emotes::const_iterator i = mEmotes.find(id);
- if (i == mEmoteInfos.end())
+ if (i == mEmotes.end())
{
logger->log("EmoteDB: Warning, unknown emote ID %d requested", id);
return &mUnknown;
@@ -142,12 +149,6 @@ const EmoteInfo *EmoteDB::get(int id)
}
}
-const AnimatedSprite *EmoteDB::getAnimation(int id)
-{
- const EmoteInfo *info = get(id);
- return info->sprites.front()->sprite;
-}
-
const int &EmoteDB::getLast()
{
return mLastEmote;
diff --git a/src/resources/emotedb.h b/src/resources/emotedb.h
index 3d80ce8f..a2887a74 100644
--- a/src/resources/emotedb.h
+++ b/src/resources/emotedb.h
@@ -25,21 +25,16 @@
#include <map>
#include <string>
-class AnimatedSprite;
+class ImageSprite;
-struct EmoteSprite
+struct Emote
{
- const AnimatedSprite *sprite;
std::string name;
+ ImageSprite *sprite;
+ int effect;
};
-struct EmoteInfo
-{
- std::list<EmoteSprite*> sprites;
- std::list<std::string> particles;
-};
-
-typedef std::map<int, EmoteInfo*> EmoteInfos;
+typedef std::map<int, Emote*> Emotes;
/**
* Emote information database.
@@ -50,13 +45,11 @@ namespace EmoteDB
void unload();
- const EmoteInfo *get(int id);
-
- const AnimatedSprite *getAnimation(int id);
+ const Emote *get(int id);
const int &getLast();
- typedef EmoteInfos::iterator EmoteInfosIterator;
+ typedef Emotes::iterator EmotesIterator;
}
#endif
diff --git a/src/resources/image.h b/src/resources/image.h
index 1db52ca0..9fdc0997 100644
--- a/src/resources/image.h
+++ b/src/resources/image.h
@@ -22,8 +22,6 @@
#ifndef IMAGE_H
#define IMAGE_H
-#include "main.h"
-
#include "resources/resource.h"
#include <SDL.h>
diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp
index bd28cd6e..d33fac32 100644
--- a/src/resources/imageset.cpp
+++ b/src/resources/imageset.cpp
@@ -27,11 +27,11 @@
#include "utils/dtor.h"
-ImageSet::ImageSet(Image *img, int width, int height)
+ImageSet::ImageSet(Image *img, int width, int height, int margin, int spacing)
{
- for (int y = 0; y + height <= img->getHeight(); y += height)
+ for (int y = margin; y + height <= img->getHeight() - margin; y += height + spacing)
{
- for (int x = 0; x + width <= img->getWidth(); x += width)
+ for (int x = margin; x + width <= img->getWidth() - margin; x += width + spacing)
{
mImages.push_back(img->getSubImage(x, y, width, height));
}
diff --git a/src/resources/imageset.h b/src/resources/imageset.h
index 763b9880..3289788f 100644
--- a/src/resources/imageset.h
+++ b/src/resources/imageset.h
@@ -37,7 +37,7 @@ class ImageSet : public Resource
/**
* Cuts the passed image in a grid of sub images.
*/
- ImageSet(Image *img, int w, int h);
+ ImageSet(Image *img, int w, int h, int margin = 0, int spacing = 0);
/**
* Destructor.
diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp
index cb91e8d3..2271abef 100644
--- a/src/resources/itemdb.cpp
+++ b/src/resources/itemdb.cpp
@@ -23,6 +23,8 @@
#include "log.h"
+#include "net/net.h"
+
#include "resources/iteminfo.h"
#include "resources/resourcemanager.h"
@@ -36,29 +38,7 @@
#include <cassert>
-namespace
-{
- ItemDB::ItemInfos mItemInfos;
- ItemDB::NamedItemInfos mNamedItemInfos;
- ItemInfo *mUnknown;
- bool mLoaded = false;
-}
-
-// Forward declarations
-static void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node);
-static void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node);
-
-static char const *const fields[][2] =
-{
- { "attack", N_("Attack %+d") },
- { "defense", N_("Defense %+d") },
- { "hp", N_("HP %+d") },
- { "mp", N_("MP %+d") }
-};
-
-static std::list<ItemDB::Stat> extraStats;
-
-void ItemDB::setStatsList(const std::list<ItemDB::Stat> &stats)
+void setStatsList(const std::list<ItemStat> &stats)
{
extraStats = stats;
}
@@ -84,119 +64,172 @@ static ItemType itemTypeFromString(const std::string &name, int id = 0)
else return ITEM_UNUSABLE;
}
-static WeaponType weaponTypeFromString(const std::string &name, int id = 0)
+/*
+ * Common itemDB functions
+ */
+
+bool ItemDB::exists(int id)
{
- if (name=="knife") return WPNTYPE_KNIFE;
- else if (name=="sword") return WPNTYPE_SWORD;
- else if (name=="polearm") return WPNTYPE_POLEARM;
- else if (name=="staff") return WPNTYPE_STAFF;
- else if (name=="whip") return WPNTYPE_WHIP;
- else if (name=="bow") return WPNTYPE_BOW;
- else if (name=="shooting") return WPNTYPE_SHOOTING;
- else if (name=="mace") return WPNTYPE_MACE;
- else if (name=="axe") return WPNTYPE_AXE;
- else if (name=="thrown") return WPNTYPE_THROWN;
-
- else return WPNTYPE_NONE;
+ assert(mLoaded);
+
+ ItemInfos::const_iterator i = mItemInfos.find(id);
+
+ return i != mItemInfos.end();
}
-static std::string normalized(const std::string &name)
+const ItemInfo &ItemDB::get(int id)
{
- std::string normalized = name;
- return toLower(trim(normalized));;
+ assert(mLoaded);
+
+ ItemInfos::const_iterator i = mItemInfos.find(id);
+
+ if (i == mItemInfos.end())
+ {
+ logger->log("ItemDB: Warning, unknown item ID# %d", id);
+ return *mUnknown;
+ }
+
+ return *(i->second);
}
-void ItemDB::load()
+const ItemInfo &ItemDB::get(const std::string &name)
{
- if (mLoaded)
- unload();
+ assert(mLoaded);
- logger->log("Initializing item database...");
+ NamedItemInfos::const_iterator i = mNamedItemInfos.find(normalize(name));
- mUnknown = new ItemInfo;
- mUnknown->setName(_("Unknown item"));
- mUnknown->setImageName("");
- std::string errFile = paths.getValue("spriteErrorFile", "error.xml");
- mUnknown->setSprite(errFile, GENDER_MALE);
- mUnknown->setSprite(errFile, GENDER_FEMALE);
+ if (i == mNamedItemInfos.end())
+ {
+ if (!name.empty())
+ {
+ logger->log("ItemDB: Warning, unknown item name \"%s\"",
+ name.c_str());
+ }
+ return *mUnknown;
+ }
- XML::Document doc("items.xml");
- xmlNodePtr rootNode = doc.rootNode();
+ return *(i->second);
+}
- if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items"))
+void ItemDB::loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node)
+{
+ std::string gender = XML::getProperty(node, "gender", "unisex");
+ std::string filename = (const char*) node->xmlChildrenNode->content;
+
+ if (gender == "male" || gender == "unisex")
+ {
+ itemInfo->setSprite(filename, GENDER_MALE);
+ }
+ if (gender == "female" || gender == "unisex")
+ {
+ itemInfo->setSprite(filename, GENDER_FEMALE);
+ }
+}
+
+void ItemDB::loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node)
+{
+ std::string event = XML::getProperty(node, "event", "");
+ std::string filename = (const char*) node->xmlChildrenNode->content;
+
+ if (event == "hit")
{
- logger->error("ItemDB: Error while loading items.xml!");
+ itemInfo->addSound(EQUIP_EVENT_HIT, filename);
}
+ else if (event == "strike")
+ {
+ itemInfo->addSound(EQUIP_EVENT_STRIKE, filename);
+ }
+ else
+ {
+ logger->log("ItemDB: Ignoring unknown sound event '%s'",
+ event.c_str());
+ }
+}
- for_each_xml_child_node(node, rootNode)
+void ItemDB::loadFloorSprite(SpriteDisplay *display, xmlNodePtr floorNode)
+{
+ for_each_xml_child_node(spriteNode, floorNode)
{
+ if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite"))
+ {
+ SpriteReference *currentSprite = new SpriteReference;
+ currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content;
+ currentSprite->variant = XML::getProperty(spriteNode, "variant", 0);
+ display->sprites.push_back(currentSprite);
+ }
+ else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx"))
+ {
+ std::string particlefx = (const char*)spriteNode->xmlChildrenNode->content;
+ display->particles.push_back(particlefx);
+ }
+ }
+}
+
+void ItemDB::unload()
+{
+ logger->log("Unloading item database...");
+
+ delete mUnknown;
+ mUnknown = NULL;
+
+ delete_all(mItemInfos);
+ mItemInfos.clear();
+ mNamedItemInfos.clear();
+ mLoaded = false;
+}
+
+void ItemDB::loadCommonRef(ItemInfo *itemInfo, xmlNodePtr node)
+{
if (!xmlStrEqual(node->name, BAD_CAST "item"))
- continue;
+ return;
int id = XML::getProperty(node, "id", 0);
- if (id == 0)
+ if (!id)
{
- logger->log("ItemDB: Invalid or missing item ID in items.xml!");
- continue;
+ logger->log("ItemDB: Invalid or missing item Id in "
+ ITEMS_DB_FILE "!");
+ return;
}
else if (mItemInfos.find(id) != mItemInfos.end())
- {
- logger->log("ItemDB: Redefinition of item ID %d", id);
- }
+ logger->log("ItemDB: Redefinition of item Id %d", id);
- std::string typeStr = XML::getProperty(node, "type", "other");
- int weight = XML::getProperty(node, "weight", 0);
int view = XML::getProperty(node, "view", 0);
std::string name = XML::getProperty(node, "name", "");
std::string image = XML::getProperty(node, "image", "");
std::string description = XML::getProperty(node, "description", "");
- int weaponType = weaponTypeFromString(XML::getProperty(node, "weapon-type", ""));
+ std::string attackAction = XML::getProperty(node, "attack-action", "");
int attackRange = XML::getProperty(node, "attack-range", 0);
std::string missileParticle = XML::getProperty(node, "missile-particle", "");
- ItemInfo *itemInfo = new ItemInfo;
- itemInfo->setId(id);
- itemInfo->setImageName(image);
- itemInfo->setName(name.empty() ? _("unnamed") : name);
- itemInfo->setDescription(description);
- itemInfo->setType(itemTypeFromString(typeStr));
- itemInfo->setView(view);
- itemInfo->setWeight(weight);
- itemInfo->setWeaponType(weaponType);
- itemInfo->setAttackRange(attackRange);
- itemInfo->setMissileParticle(missileParticle);
+ // Load Ta Item Type
+ std::string typeStr = XML::getProperty(node, "type", "other");
+ itemInfo->mType = itemTypeFromString(typeStr);
- std::string effect;
- for (int i = 0; i < int(sizeof(fields) / sizeof(fields[0])); ++i)
- {
- int value = XML::getProperty(node, fields[i][0], 0);
- if (!value) continue;
- if (!effect.empty()) effect += " / ";
- effect += strprintf(gettext(fields[i][1]), value);
- }
- for (std::list<Stat>::iterator it = extraStats.begin();
- it != extraStats.end(); it++)
- {
- int value = XML::getProperty(node, it->tag.c_str(), 0);
- if (!value) continue;
- if (!effect.empty()) effect += " / ";
- effect += strprintf(it->format.c_str(), value);
- }
- std::string temp = XML::getProperty(node, "effect", "");
- if (!effect.empty() && !temp.empty())
- effect += " / ";
- effect += temp;
- itemInfo->setEffect(effect);
+ int weight = XML::getProperty(node, "weight", 0);
+ itemInfo->mWeight = weight > 0 ? weight : 0;
+
+ SpriteDisplay display;
+ display.image = image;
+
+ itemInfo->mId = id;
+ itemInfo->mName = name;
+ itemInfo->mDescription = description;
+ itemInfo->mView = view;
+ itemInfo->mWeight = weight;
+ itemInfo->setAttackAction(attackAction);
+ itemInfo->mAttackRange = attackRange;
+ itemInfo->setMissileParticle(missileParticle);
+ // Load <sprite>, <sound>, and <floor>
for_each_xml_child_node(itemChild, node)
{
if (xmlStrEqual(itemChild->name, BAD_CAST "sprite"))
{
std::string attackParticle = XML::getProperty(
itemChild, "particle-effect", "");
- itemInfo->setParticleEffect(attackParticle);
+ itemInfo->mParticle = attackParticle;
loadSpriteRef(itemInfo, itemChild);
}
@@ -204,136 +237,306 @@ void ItemDB::load()
{
loadSoundRef(itemInfo, itemChild);
}
- }
-
- mItemInfos[id] = itemInfo;
- if (!name.empty())
- {
- std::string temp = normalized(name);
-
- NamedItemInfos::const_iterator itr = mNamedItemInfos.find(temp);
- if (itr == mNamedItemInfos.end())
- {
- mNamedItemInfos[temp] = itemInfo;
- }
- else
+ else if (xmlStrEqual(itemChild->name, BAD_CAST "floor"))
{
- logger->log("ItemDB: Duplicate name of item found item %d", id);
+ loadFloorSprite(&display, itemChild);
}
+
}
- if (weaponType > 0)
- if (attackRange == 0)
- logger->log("ItemDB: Missing attack range from weapon %i!", id);
+ // If the item has got a floor image, we bind the good reference.
+ itemInfo->mDisplay = display;
+}
-#define CHECK_PARAM(param, error_value) \
- if (param == error_value) \
- logger->log("ItemDB: Missing " #param " attribute for item %i!",id)
+void ItemDB::addItem(ItemInfo *itemInfo)
+{
+ std::string itemName = itemInfo->mName;
+ itemInfo->mName = itemName.empty() ? _("unnamed") : itemName;
+ mItemInfos[itemInfo->mId] = itemInfo;
+ if (!itemName.empty())
+ {
+ std::string temp = normalize(itemName);
- if (id >= 0)
- {
- CHECK_PARAM(name, "");
- CHECK_PARAM(description, "");
- CHECK_PARAM(image, "");
- }
- // CHECK_PARAM(effect, "");
- // CHECK_PARAM(type, 0);
- // CHECK_PARAM(weight, 0);
- // CHECK_PARAM(slot, 0);
+ NamedItemInfos::const_iterator itr = mNamedItemInfos.find(temp);
+ if (itr == mNamedItemInfos.end())
+ mNamedItemInfos[temp] = itemInfo;
+ else
+ logger->log("ItemDB: Duplicate name (%s) for item id %d found.",
+ temp.c_str(), itemInfo->mId);
-#undef CHECK_PARAM
}
-
- mLoaded = true;
}
-void ItemDB::unload()
+template <class T>
+static void checkParameter(int id, const T param, const T errorValue)
{
- logger->log("Unloading item database...");
+ if (param == errorValue)
+ {
+ std::stringstream errMsg;
+ errMsg << "ItemDB: Missing " << param << " attribute for item id "
+ << id << "!";
+ logger->log(errMsg.str().c_str());
+ }
+}
- delete mUnknown;
- mUnknown = NULL;
+void ItemDB::checkItemInfo(ItemInfo* itemInfo)
+{
+ int id = itemInfo->mId;
+ if (!itemInfo->getAttackAction().empty())
+ if (itemInfo->mAttackRange == 0)
+ logger->log("ItemDB: Missing attack range from weapon %i!", id);
- delete_all(mItemInfos);
- mItemInfos.clear();
- mNamedItemInfos.clear();
- mLoaded = false;
+ if (id >= 0)
+ {
+ checkParameter(id, itemInfo->mName, std::string(""));
+ checkParameter(id, itemInfo->mDescription, std::string(""));
+ checkParameter(id, itemInfo->mDisplay.image, std::string(""));
+ checkParameter(id, itemInfo->mWeight, 0);
+ }
}
-bool ItemDB::exists(int id)
-{
- assert(mLoaded);
+namespace TmwAthena {
- ItemInfos::const_iterator i = mItemInfos.find(id);
+// Description fields used by TaItemDB *itemInfo->mEffect.
- return i != mItemInfos.end();
-}
+static char const *const fields[][2] =
+{
+ { "attack", N_("Attack %+d") },
+ { "defense", N_("Defense %+d") },
+ { "hp", N_("HP %+d") },
+ { "mp", N_("MP %+d") }
+};
-const ItemInfo &ItemDB::get(int id)
+void TaItemDB::load()
{
- assert(mLoaded);
+ if (mLoaded)
+ unload();
- ItemInfos::const_iterator i = mItemInfos.find(id);
+ logger->log("Initializing TmwAthena item database...");
- if (i == mItemInfos.end())
+ mUnknown = new TaItemInfo;
+ mUnknown->mName = _("Unknown item");
+ mUnknown->mDisplay = SpriteDisplay();
+ std::string errFile = paths.getStringValue("spriteErrorFile");
+ mUnknown->setSprite(errFile, GENDER_MALE);
+ mUnknown->setSprite(errFile, GENDER_FEMALE);
+
+ XML::Document doc(ITEMS_DB_FILE);
+ xmlNodePtr rootNode = doc.rootNode();
+
+ if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items"))
{
- logger->log("ItemDB: Warning, unknown item ID# %d", id);
- return *mUnknown;
+ logger->error("ItemDB: Error while loading " ITEMS_DB_FILE "!");
+ return;
}
- return *(i->second);
-}
+ for_each_xml_child_node(node, rootNode)
+ {
+ TaItemInfo *itemInfo = new TaItemInfo;
-const ItemInfo &ItemDB::get(const std::string &name)
-{
- assert(mLoaded);
+ loadCommonRef(itemInfo, node);
- NamedItemInfos::const_iterator i = mNamedItemInfos.find(normalized(name));
+ // Everything not unusable or usable is equippable by the Ta type system.
+ itemInfo->mEquippable = itemInfo->mType != ITEM_UNUSABLE
+ && itemInfo->mType != ITEM_USABLE;
- if (i == mNamedItemInfos.end())
- {
- if (!name.empty())
+ // Load nano description
+ std::vector<std::string> effect;
+ for (int i = 0; i < int(sizeof(fields) / sizeof(fields[0])); ++i)
{
- logger->log("ItemDB: Warning, unknown item name \"%s\"",
- name.c_str());
+ int value = XML::getProperty(node, fields[i][0], 0);
+ if (!value)
+ continue;
+ effect.push_back(strprintf(gettext(fields[i][1]), value));
}
- return *mUnknown;
+ for (std::list<ItemStat>::iterator it = extraStats.begin();
+ it != extraStats.end(); it++)
+ {
+ int value = XML::getProperty(node, it->mTag.c_str(), 0);
+ if (!value)
+ continue;
+ effect.push_back(strprintf(it->mFormat.c_str(), value));
+ }
+ std::string temp = XML::getProperty(node, "effect", "");
+ if (!temp.empty())
+ effect.push_back(temp);
+
+ itemInfo->mEffect = effect;
+
+ checkItemInfo(itemInfo);
+
+ addItem(itemInfo);
}
- return *(i->second);
+ checkHairWeaponsRacesSpecialIds();
+
+ mLoaded = true;
}
-void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node)
+void TaItemDB::checkItemInfo(ItemInfo* itemInfo)
{
- std::string gender = XML::getProperty(node, "gender", "unisex");
- std::string filename = (const char*) node->xmlChildrenNode->content;
+ ItemDB::checkItemInfo(itemInfo);
- if (gender == "male" || gender == "unisex")
- {
- itemInfo->setSprite(filename, GENDER_MALE);
- }
- if (gender == "female" || gender == "unisex")
+ // Check for unusable items?
+ //checkParameter(id, itemInfo->mType, 0);
+}
+
+}; // namespace TmwAthena
+
+namespace ManaServ {
+
+static std::map<std::string, const char* > triggerTable;
+
+static void initTriggerTable()
+{
+ if (triggerTable.empty())
{
- itemInfo->setSprite(filename, GENDER_FEMALE);
+ // FIXME: This should ideally be softcoded via XML or similar.
+ logger->log("Initializing ManaServ trigger table...");
+ triggerTable["existence"] = " when it is in the inventory";
+ triggerTable["activation"] = " upon activation";
+ triggerTable["equip"] = " upon successful equip";
+ triggerTable["leave-inventory"] = " when it leaves the inventory";
+ triggerTable["unequip"] = " when it is unequipped";
+ triggerTable["equip-change"] = " when it changes the way it is equipped";
}
}
-void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node)
+void ManaServItemDB::load()
{
- std::string event = XML::getProperty(node, "event", "");
- std::string filename = (const char*) node->xmlChildrenNode->content;
+ if (mLoaded)
+ unload();
- if (event == "hit")
- {
- itemInfo->addSound(EQUIP_EVENT_HIT, filename);
- }
- else if (event == "strike")
+ // Initialize the trigger table for effect descriptions
+ initTriggerTable();
+
+ logger->log("Initializing ManaServ item database...");
+
+ mUnknown = new ManaServItemInfo;
+ mUnknown->mName = _("Unknown item");
+ mUnknown->mDisplay = SpriteDisplay();
+ std::string errFile = paths.getStringValue("spriteErrorFile");
+ mUnknown->setSprite(errFile, GENDER_MALE);
+ mUnknown->setSprite(errFile, GENDER_FEMALE);
+
+ XML::Document doc(ITEMS_DB_FILE);
+ xmlNodePtr rootNode = doc.rootNode();
+
+ if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items"))
{
- itemInfo->addSound(EQUIP_EVENT_STRIKE, filename);
+ logger->log("ItemDB: Error while loading " ITEMS_DB_FILE "!");
+ return;
}
- else
+
+ for_each_xml_child_node(node, rootNode)
{
- logger->log("ItemDB: Ignoring unknown sound event '%s'",
- event.c_str());
+ ManaServItemInfo *itemInfo = new ManaServItemInfo;
+
+ loadCommonRef(itemInfo, node);
+
+ // We default eqippable and activatable to false as their actual value will be set
+ // within the <equip> and <effect> sub-nodes..
+ itemInfo->mActivatable = false;
+ itemInfo->mEquippable = false;
+
+ // Load <equip>, and <effect> sub nodes.
+ std::vector<std::string> effect;
+ for_each_xml_child_node(itemChild, node)
+ {
+ if (xmlStrEqual(itemChild->name, BAD_CAST "equip"))
+ {
+ // The fact that there is a way to equip is enough.
+ // Discard any details, but mark the item as equippable.
+ itemInfo->mEquippable = true;
+ }
+ else if (xmlStrEqual(itemChild->name, BAD_CAST "effect"))
+ {
+ std::string trigger = XML::getProperty(
+ itemChild, "trigger", "");
+ if (trigger.empty())
+ {
+ logger->log("Found empty trigger effect label, skipping.");
+ continue;
+ }
+
+ if (trigger == "activation")
+ itemInfo->mActivatable = true;
+
+ std::map<std::string, const char* >::const_iterator triggerLabel =
+ triggerTable.find(trigger);
+ if (triggerLabel == triggerTable.end())
+ {
+ logger->log("Warning: unknown trigger %s in item %d!",
+ trigger.c_str(), itemInfo->mId);
+ continue;
+ }
+
+ for_each_xml_child_node(effectChild, itemChild)
+ {
+ if (xmlStrEqual(effectChild->name, BAD_CAST "modifier"))
+ {
+ std::string attribute = XML::getProperty(
+ effectChild, "attribute", "");
+ double value = XML::getFloatProperty(
+ effectChild, "value", 0.0);
+ int duration = XML::getProperty(
+ effectChild, "duration", 0);
+ if (attribute.empty() || !value)
+ {
+ logger->log("Warning: incomplete modifier definition, skipping.");
+ continue;
+ }
+ std::list<ItemStat>::const_iterator
+ it = extraStats.begin(),
+ it_end = extraStats.end();
+ while (it != it_end && !(*it == attribute))
+ ++it;
+ if (it == extraStats.end())
+ {
+ logger->log("Warning: unknown modifier tag %s, skipping.", attribute.c_str());
+ continue;
+ }
+ effect.push_back(
+ strprintf(strprintf(
+ duration ?
+ strprintf("%%s%%s. This effect lasts %d ticks.", duration).c_str()
+ : "%s%s.", it->mFormat.c_str(), triggerLabel->second).c_str(), value));
+ }
+ else if (xmlStrEqual(effectChild->name, BAD_CAST "modifier"))
+ effect.push_back(strprintf("Provides an autoattack%s.",
+ triggerLabel->second));
+ else if (xmlStrEqual(effectChild->name, BAD_CAST "consumes"))
+ effect.push_back(strprintf("This will be consumed%s.",
+ triggerLabel->second));
+ else if (xmlStrEqual(effectChild->name, BAD_CAST "label"))
+ effect.push_back(
+ (const char*)effectChild->xmlChildrenNode->content);
+ }
+ }
+ // Set Item Type based on subnodes info
+ // TODO: Improve it once the itemTypes are loaded through xml
+ itemInfo->mType = ITEM_UNUSABLE;
+ if (itemInfo->mActivatable)
+ itemInfo->mType = ITEM_USABLE;
+ else if (itemInfo->mEquippable)
+ itemInfo->mType = ITEM_EQUIPMENT_TORSO;
+ } // end for_each_xml_child_node(itemChild, node)
+
+ itemInfo->mEffect = effect;
+
+ checkItemInfo(itemInfo);
+
+ addItem(itemInfo);
}
+
+ mLoaded = true;
+}
+
+void ManaServItemDB::checkItemInfo(ItemInfo* itemInfo)
+{
+ ItemDB::checkItemInfo(itemInfo);
+
+ // Add specific Manaserv checks here
}
+
+}; // namespace ManaServ
diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h
index be023073..e80f2cd0 100644
--- a/src/resources/itemdb.h
+++ b/src/resources/itemdb.h
@@ -26,45 +26,185 @@
#include <map>
#include <string>
+#include "utils/xml.h"
+
+#define ITEMS_DB_FILE "items.xml"
+
class ItemInfo;
+class SpriteDisplay;
+
+// Used to make the compiler uderstand the iteminfo friendship.
+namespace TmwAthena { class TaItemDB; };
+namespace ManaServ { class ManaServItemDB; };
/**
- * Item information database.
+ * Nano-description functions
*/
-namespace ItemDB
+class ItemStat
{
- /**
- * Loads the item data from <code>items.xml</code>.
- */
- void load();
+ friend class ItemDB;
+ friend class TmwAthena::TaItemDB;
+ friend class ManaServ::ManaServItemDB;
- /**
- * Frees item data.
- */
- void unload();
+ public:
+ ItemStat(const std::string &tag,
+ const std::string &format):
+ mTag(tag), mFormat(format) {}
- bool exists(int id);
+ bool operator ==(std::string &name) const
+ { return mTag == name; }
- const ItemInfo &get(int id);
- const ItemInfo &get(const std::string &name);
+ private:
+ std::string mTag;
+ std::string mFormat;
+};
- struct Stat
- {
- Stat(const std::string &tag,
- const std::string &format):
- tag(tag),
- format(format)
+// Used to set nano-description
+static std::list<ItemStat> extraStats;
+void setStatsList(const std::list<ItemStat> &stats);
+
+/**
+ * Item information database generic definition.
+ */
+class ItemDB
+{
+ public:
+ ItemDB() :
+ mUnknown(0),
+ mLoaded(false)
+ {}
+
+ ~ItemDB()
+ {}
+
+ /**
+ * Loads the item data from <code>items.xml</code>.
+ */
+ virtual void load() = 0;
+
+ /**
+ * Frees item data.
+ */
+ virtual void unload();
+
+ /**
+ * Tells whether the item database is loaded.
+ */
+ bool isLoaded() const
+ { return mLoaded; }
+
+ bool exists(int id);
+
+ const ItemInfo &get(int id);
+ const ItemInfo &get(const std::string &name);
+
+ protected:
+ /**
+ * Permits to load item definitions which are common
+ * for each protocols to avoid code duplication.
+ */
+ void loadCommonRef(ItemInfo *itemInfo, xmlNodePtr node);
+
+ /**
+ * Checks the items parameters consistency.
+ */
+ virtual void checkItemInfo(ItemInfo* itemInfo);
+
+ /**
+ * Register the item to mItemInfos and mNamedItemsInfos
+ */
+ void addItem(ItemInfo *itemInfo);
+
+ // Default unknown reference
+ ItemInfo *mUnknown;
+
+ bool mLoaded;
+
+ private:
+ /**
+ * Loads the sprite references contained in a <sprite> tag.
+ */
+ void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node);
+
+ /**
+ * Loads the sound references contained in a <sound> tag.
+ */
+ void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node);
+
+ /**
+ * Loads the floor item references contained in a <floor> tag.
+ */
+ void loadFloorSprite(SpriteDisplay *display, xmlNodePtr node);
+
+ // Items database
+ typedef std::map<int, ItemInfo*> ItemInfos;
+ typedef std::map<std::string, ItemInfo*> NamedItemInfos;
+
+ ItemInfos mItemInfos;
+ NamedItemInfos mNamedItemInfos;
+};
+
+namespace TmwAthena {
+
+class TaItemInfo;
+
+/**
+ * Item information database TmwAthena specific class.
+ */
+class TaItemDB: public ItemDB
+{
+ public:
+ TaItemDB() : ItemDB()
+ { load(); }
+
+ ~TaItemDB()
+ { unload(); }
+
+ /**
+ * Loads the item data from <code>items.xml</code>.
+ */
+ void load();
+
+ private:
+ /**
+ * Check items id specific hard limits and log errors found.
+ * TODO: Do it.
+ */
+ void checkHairWeaponsRacesSpecialIds()
{}
- std::string tag;
- std::string format;
- };
+ void checkItemInfo(ItemInfo* itemInfo);
+};
+
+}; // namespace TmwAthena
+
+namespace ManaServ {
+
+class ManaServItemInfo;
+
+/**
+ * Item information database TmwAthena specific class.
+ */
+class ManaServItemDB: public ItemDB
+{
+ public:
+ ManaServItemDB() : ItemDB()
+ { load(); }
+
+ ~ManaServItemDB()
+ { unload(); }
+
+ /**
+ * Loads the item data from <code>items.xml</code>.
+ */
+ void load();
+
+ private:
+ void checkItemInfo(ItemInfo* itemInfo);
+};
- void setStatsList(const std::list<Stat> &stats);
+}; // namespace ManaServ
- // Items database
- typedef std::map<int, ItemInfo*> ItemInfos;
- typedef std::map<std::string, ItemInfo*> NamedItemInfos;
-}
+extern ItemDB *itemDb;
#endif
diff --git a/src/resources/iteminfo.cpp b/src/resources/iteminfo.cpp
index 4b1d82ea..32331e35 100644
--- a/src/resources/iteminfo.cpp
+++ b/src/resources/iteminfo.cpp
@@ -29,7 +29,7 @@ const std::string &ItemInfo::getSprite(Gender gender) const
if (mView)
{
// Forward the request to the item defining how to view this item
- return ItemDB::get(mView).getSprite(gender);
+ return itemDb->get(mView).getSprite(gender);
}
else
{
@@ -41,35 +41,17 @@ const std::string &ItemInfo::getSprite(Gender gender) const
}
}
-void ItemInfo::setWeaponType(int type)
+void ItemInfo::setAttackAction(std::string attackAction)
{
- // See server item.hpp file for type values.
- switch (type)
- {
- case WPNTYPE_NONE:
- mAttackType = ACTION_DEFAULT;
- break;
- case WPNTYPE_KNIFE:
- case WPNTYPE_SWORD:
- mAttackType = ACTION_ATTACK_STAB;
- break;
- case WPNTYPE_THROWN:
- mAttackType = ACTION_ATTACK_THROW;
- break;
- case WPNTYPE_BOW:
- mAttackType = ACTION_ATTACK_BOW;
- break;
- case WPNTYPE_POLEARM:
- mAttackType = ACTION_ATTACK_SWING;
- break;
- default:
- mAttackType = ACTION_ATTACK;
- }
+ if (attackAction.empty())
+ mAttackAction = SpriteAction::ATTACK; // (Equal to unarmed animation)
+ else
+ mAttackAction = attackAction;
}
void ItemInfo::addSound(EquipmentSoundEvent event, const std::string &filename)
{
- mSounds[event].push_back(paths.getValue("sfx", "sfx/") + filename);
+ mSounds[event].push_back(paths.getStringValue("sfx") + filename);
}
const std::string &ItemInfo::getSound(EquipmentSoundEvent event) const
diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h
index a7c0ddca..50633f71 100644
--- a/src/resources/iteminfo.h
+++ b/src/resources/iteminfo.h
@@ -22,7 +22,7 @@
#ifndef ITEMINFO_H
#define ITEMINFO_H
-#include "player.h"
+#include "being.h"
#include "resources/spritedef.h"
@@ -36,38 +36,9 @@ enum EquipmentSoundEvent
EQUIP_EVENT_HIT
};
-enum EquipmentSlot
-{
- // Equipment rules:
- // 1 Brest equipment
- EQUIP_TORSO_SLOT = 0,
- // 1 arms equipment
- EQUIP_ARMS_SLOT = 1,
- // 1 head equipment
- EQUIP_HEAD_SLOT = 2,
- // 1 legs equipment
- EQUIP_LEGS_SLOT = 3,
- // 1 feet equipment
- EQUIP_FEET_SLOT = 4,
- // 2 rings
- EQUIP_RING1_SLOT = 5,
- EQUIP_RING2_SLOT = 6,
- // 1 necklace
- EQUIP_NECKLACE_SLOT = 7,
- // Fight:
- // 2 one-handed weapons
- // or 1 two-handed weapon
- // or 1 one-handed weapon + 1 shield.
- EQUIP_FIGHT1_SLOT = 8,
- EQUIP_FIGHT2_SLOT = 9,
- // Projectile:
- // this item does not amount to one, it only indicates the chosen projectile.
- EQUIP_PROJECTILE_SLOT = 10
-};
-
-
/**
* Enumeration of available Item types.
+ * TODO: Dynamise this using an xml.
*/
enum ItemType
{
@@ -89,30 +60,21 @@ enum ItemType
ITEM_SPRITE_HAIR // 15
};
-/**
- * Enumeration of available weapon's types.
- */
-enum WeaponType
-{
- WPNTYPE_NONE = 0,
- WPNTYPE_KNIFE,
- WPNTYPE_SWORD,
- WPNTYPE_POLEARM,
- WPNTYPE_STAFF,
- WPNTYPE_WHIP,
- WPNTYPE_BOW,
- WPNTYPE_SHOOTING,
- WPNTYPE_MACE,
- WPNTYPE_AXE,
- WPNTYPE_THROWN
-};
+// Used to make the compiler uderstand the iteminfo friendship.
+namespace TmwAthena { class TaItemDB; };
+namespace ManaServ { class ManaServItemDB; };
/**
- * Defines a class for storing item infos. This includes information used when
- * the item is equipped.
+ * Defines a class for storing generic item infos.
+ * Specialized version for one or another protocol are defined below.
*/
class ItemInfo
{
+ friend class ItemDB;
+ friend void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node);
+ friend class TmwAthena::TaItemDB;
+ friend class ManaServ::ManaServItemDB;
+
public:
/**
* Constructor.
@@ -122,98 +84,86 @@ class ItemInfo
mWeight(0),
mView(0),
mId(0),
- mAttackType(ACTION_DEFAULT)
+ mAttackAction(SpriteAction::INVALID)
{
}
- void setId(int id)
- { mId = id; }
-
int getId() const
{ return mId; }
- void setName(const std::string &name)
- { mName = name; }
-
const std::string &getName() const
{ return mName; }
- void setParticleEffect(const std::string &particleEffect)
- { mParticle = particleEffect; }
-
- std::string getParticleEffect() const { return mParticle; }
+ std::string getParticleEffect() const
+ { return mParticle; }
- void setImageName(const std::string &imageName)
- { mImageName = imageName; }
-
- const std::string &getImageName() const
- { return mImageName; }
-
- void setDescription(const std::string &description)
- { mDescription = description; }
+ const SpriteDisplay &getDisplay() const
+ { return mDisplay; }
const std::string &getDescription() const
{ return mDescription; }
- void setEffect(const std::string &effect)
- { mEffect = effect; }
-
- const std::string &getEffect() const { return mEffect; }
-
- void setType(ItemType type)
- { mType = type; }
-
- ItemType getType() const
- { return mType; }
-
- void setWeight(int weight)
- { mWeight = weight; }
+ const std::vector<std::string> &getEffect() const
+ { return mEffect; }
int getWeight() const
{ return mWeight; }
- void setView(int view)
- { mView = view; }
-
- void setSprite(const std::string &animationFile, Gender gender)
- { mAnimationFiles[gender] = animationFile; }
-
const std::string &getSprite(Gender gender) const;
- void setWeaponType(int);
+ void setAttackAction(std::string attackAction);
// Handlers for seting and getting the string used for particles when attacking
- void setMissileParticle(std::string s) { mMissileParticle = s; }
+ void setMissileParticle(std::string s)
+ { mMissileParticle = s; }
- std::string getMissileParticle() const { return mMissileParticle; }
+ std::string getMissileParticle() const
+ { return mMissileParticle; }
- SpriteAction getAttackType() const
- { return mAttackType; }
+ std::string getAttackAction() const
+ { return mAttackAction; }
int getAttackRange() const
{ return mAttackRange; }
- void setAttackRange(int r)
- { mAttackRange = r; }
+ const std::string &getSound(EquipmentSoundEvent event) const;
- void addSound(EquipmentSoundEvent event, const std::string &filename);
+ bool getEquippable() const
+ { return mEquippable; }
- const std::string &getSound(EquipmentSoundEvent event) const;
+ bool getActivatable() const
+ { return mActivatable; }
+
+ ItemType getItemType() const
+ { return mType; }
+
+ private:
+
+ void setSprite(const std::string &animationFile, Gender gender)
+ { mAnimationFiles[gender] = animationFile; }
+
+ void addSound(EquipmentSoundEvent event, const std::string &filename);
- protected:
- std::string mImageName; /**< The filename of the icon image. */
+ SpriteDisplay mDisplay; /**< Display info (like icon) */
std::string mName;
- std::string mDescription; /**< Short description. */
- std::string mEffect; /**< Description of effects. */
- ItemType mType; /**< Item type. */
- std::string mParticle; /**< Particle effect used with this item */
- int mWeight; /**< Weight in grams. */
- int mView; /**< Item ID of how this item looks. */
- int mId; /**< Item ID */
-
- // Equipment related members
- SpriteAction mAttackType; /**< Attack type, in case of weapon. */
- int mAttackRange; /**< Attack range, will be zero if non weapon. */
+ std::string mDescription; /**< Short description. */
+ std::vector<std::string> mEffect; /**< Description of effects. */
+ ItemType mType; /**< Item type. */
+ std::string mParticle; /**< Particle effect used with this item */
+ int mWeight; /**< Weight in grams. */
+ int mView; /**< Item ID of how this item looks. */
+ int mId; /**< Item ID */
+
+ bool mEquippable; /**< Whether this item can be equipped. */
+ bool mActivatable; /**< Whether this item can be activated. */
+
+ // Equipment related members.
+ /** Attack type, in case of weapon.
+ * See SpriteAction in spritedef.h for more info.
+ * Attack action sub-types (bow, sword, ...) are defined in items.xml.
+ */
+ std::string mAttackAction;
+ int mAttackRange; /**< Attack range, will be zero if non weapon. */
// Particle to be shown when weapon attacks
std::string mMissileParticle;
@@ -225,4 +175,80 @@ class ItemInfo
std::map< EquipmentSoundEvent, std::vector<std::string> > mSounds;
};
+/*
+ * TmwAthena specialization of the itemInfo for TmwAthena
+ */
+namespace TmwAthena {
+
+enum EquipmentSlot
+{
+ // Equipment rules:
+ // 1 Brest equipment
+ EQUIP_TORSO_SLOT = 0,
+ // 1 arms equipment
+ EQUIP_ARMS_SLOT = 1,
+ // 1 head equipment
+ EQUIP_HEAD_SLOT = 2,
+ // 1 legs equipment
+ EQUIP_LEGS_SLOT = 3,
+ // 1 feet equipment
+ EQUIP_FEET_SLOT = 4,
+ // 2 rings
+ EQUIP_RING1_SLOT = 5,
+ EQUIP_RING2_SLOT = 6,
+ // 1 necklace
+ EQUIP_NECKLACE_SLOT = 7,
+ // Fight:
+ // 2 one-handed weapons
+ // or 1 two-handed weapon
+ // or 1 one-handed weapon + 1 shield.
+ EQUIP_FIGHT1_SLOT = 8,
+ EQUIP_FIGHT2_SLOT = 9,
+ // Projectile:
+ // this item does not amount to one, it only indicates the chosen projectile.
+ EQUIP_PROJECTILE_SLOT = 10,
+ EQUIP_VECTOR_END = 11
+};
+
+/**
+ * Defines a class for storing TmwAthena specific item infos.
+ * Specialized version for one or another protocol are defined below.
+ */
+class TaItemInfo: public ItemInfo
+{
+ friend class TaItemDB;
+
+ public:
+ /**
+ * Constructor.
+ */
+ TaItemInfo():ItemInfo()
+ {}
+
+ // Declare TmwAthena Specific item info here
+};
+
+}; // namespace TmwAthena
+
+namespace ManaServ {
+
+/**
+ * Defines a class for storing Manaserv Specific item infos.
+ * Specialized version for one or another protocol are defined below.
+ */
+class ManaServItemInfo: public ItemInfo
+{
+ public:
+ /**
+ * Constructor.
+ */
+ ManaServItemInfo():ItemInfo()
+ {}
+
+ // Declare Manaserv Specific item info here
+};
+
+
+}; // namespace ManaServ
+
#endif
diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp
index b7c4fd72..28feaba8 100644
--- a/src/resources/mapreader.cpp
+++ b/src/resources/mapreader.cpp
@@ -185,11 +185,9 @@ Map *MapReader::readMap(xmlNodePtr node, const std::string &path)
if (config.getValue("showWarps", 1))
{
map->addParticleEffect(
- paths.getValue("particles",
- "graphics/particles/")
- + paths.getValue("portalEffectFile",
- "warparea.particle.xml"),
- objX, objY, objW, objH);
+ paths.getStringValue("particles")
+ + paths.getStringValue("portalEffectFile"),
+ objX, objY, objW, objH);
}
}
else
@@ -394,6 +392,8 @@ Tileset *MapReader::readTileset(xmlNodePtr node, const std::string &path,
Map *map)
{
int firstGid = XML::getProperty(node, "firstgid", 0);
+ int margin = XML::getProperty(node, "margin", 0);
+ int spacing = XML::getProperty(node, "spacing", 0);
XML::Document* doc = NULL;
Tileset *set = NULL;
std::string pathDir(path);
@@ -428,7 +428,8 @@ Tileset *MapReader::readTileset(xmlNodePtr node, const std::string &path,
if (tilebmp)
{
- set = new Tileset(tilebmp, tw, th, firstGid);
+ set = new Tileset(tilebmp, tw, th, firstGid, margin,
+ spacing);
tilebmp->decRef();
}
else
diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp
index b08d87f2..6bc64a89 100644
--- a/src/resources/monsterdb.cpp
+++ b/src/resources/monsterdb.cpp
@@ -23,21 +23,21 @@
#include "log.h"
-#include "resources/monsterinfo.h"
+#include "net/net.h"
+
+#include "resources/beinginfo.h"
#include "utils/dtor.h"
#include "utils/gettext.h"
#include "utils/xml.h"
-#include "net/net.h"
#include "configuration.h"
#define OLD_TMWATHENA_OFFSET 1002
namespace
{
- MonsterDB::MonsterInfos mMonsterInfos;
- MonsterInfo mUnknown;
+ BeingInfos mMonsterInfos;
bool mLoaded = false;
}
@@ -46,8 +46,6 @@ void MonsterDB::load()
if (mLoaded)
unload();
- mUnknown.addSprite(paths.getValue("spriteErrorFile", "error.xml"));
-
logger->log("Initializing monster database...");
XML::Document doc("monsters.xml");
@@ -69,39 +67,29 @@ void MonsterDB::load()
continue;
}
- MonsterInfo *currentInfo = new MonsterInfo;
+ BeingInfo *currentInfo = new BeingInfo;
+
+ currentInfo->setWalkMask(Map::BLOCKMASK_WALL
+ | Map::BLOCKMASK_CHARACTER
+ | Map::BLOCKMASK_MONSTER);
+ currentInfo->setBlockType(Map::BLOCKTYPE_MONSTER);
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);
- }
+ currentInfo->setTargetCursorSize(XML::getProperty(monsterNode,
+ "targetCursor", "medium"));
+
+ SpriteDisplay display;
//iterate <sprite>s and <sound>s
for_each_xml_child_node(spriteNode, monsterNode)
{
if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite"))
{
- currentInfo->addSprite(
- (const char*) spriteNode->xmlChildrenNode->content);
+ SpriteReference *currentSprite = new SpriteReference;
+ currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content;
+ currentSprite->variant = XML::getProperty(spriteNode, "variant", 0);
+ display.sprites.push_back(currentSprite);
}
else if (xmlStrEqual(spriteNode->name, BAD_CAST "sound"))
{
@@ -111,19 +99,19 @@ void MonsterDB::load()
if (event == "hit")
{
- currentInfo->addSound(MONSTER_EVENT_HIT, filename);
+ currentInfo->addSound(SOUND_EVENT_HIT, filename);
}
else if (event == "miss")
{
- currentInfo->addSound(MONSTER_EVENT_MISS, filename);
+ currentInfo->addSound(SOUND_EVENT_MISS, filename);
}
else if (event == "hurt")
{
- currentInfo->addSound(MONSTER_EVENT_HURT, filename);
+ currentInfo->addSound(SOUND_EVENT_HURT, filename);
}
else if (event == "die")
{
- currentInfo->addSound(MONSTER_EVENT_DIE, filename);
+ currentInfo->addSound(SOUND_EVENT_DIE, filename);
}
else
{
@@ -138,18 +126,22 @@ void MonsterDB::load()
const int id = XML::getProperty(spriteNode, "id", 0);
const std::string particleEffect = XML::getProperty(
spriteNode, "particle-effect", "");
- SpriteAction spriteAction = SpriteDef::makeSpriteAction(
- XML::getProperty(spriteNode, "action", "attack"));
+ const std::string spriteAction = XML::getProperty(spriteNode,
+ "action",
+ "attack");
const std::string missileParticle = XML::getProperty(
spriteNode, "missile-particle", "");
- currentInfo->addMonsterAttack(id, particleEffect, spriteAction, missileParticle);
+ currentInfo->addAttack(id, spriteAction,
+ particleEffect, missileParticle);
}
else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx"))
{
- currentInfo->addParticleEffect(
+ display.particles.push_back(
(const char*) spriteNode->xmlChildrenNode->content);
}
}
+ currentInfo->setDisplay(display);
+
mMonsterInfos[XML::getProperty(monsterNode, "id", 0) + offset] = currentInfo;
}
@@ -165,17 +157,17 @@ void MonsterDB::unload()
}
-const MonsterInfo &MonsterDB::get(int id)
+BeingInfo *MonsterDB::get(int id)
{
- MonsterInfoIterator i = mMonsterInfos.find(id);
+ BeingInfoIterator i = mMonsterInfos.find(id);
if (i == mMonsterInfos.end())
{
logger->log("MonsterDB: Warning, unknown monster ID %d requested", id);
- return mUnknown;
+ return BeingInfo::Unknown;
}
else
{
- return *(i->second);
+ return i->second;
}
}
diff --git a/src/resources/monsterdb.h b/src/resources/monsterdb.h
index 0fc8d2cf..50f70438 100644
--- a/src/resources/monsterdb.h
+++ b/src/resources/monsterdb.h
@@ -22,9 +22,7 @@
#ifndef MONSTER_DB_H
#define MONSTER_DB_H
-#include <map>
-
-class MonsterInfo;
+class BeingInfo;
/**
* Monster information database.
@@ -35,10 +33,7 @@ namespace MonsterDB
void unload();
- const MonsterInfo &get(int id);
-
- typedef std::map<int, MonsterInfo*> MonsterInfos;
- typedef MonsterInfos::iterator MonsterInfoIterator;
+ BeingInfo *get(int id);
}
#endif
diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp
deleted file mode 100644
index 12cdbe3e..00000000
--- a/src/resources/monsterinfo.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2004-2009 The Mana World Development Team
- * Copyright (C) 2009-2010 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program 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.
- *
- * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "resources/monsterinfo.h"
-
-#include "utils/dtor.h"
-#include "utils/gettext.h"
-#include "configuration.h"
-
-MonsterInfo::MonsterInfo():
- mName(_("unnamed")),
- mTargetCursorSize(Being::TC_MEDIUM)
-{
-}
-
-MonsterInfo::~MonsterInfo()
-{
- // kill vectors in mSoundEffects
- delete_all(mSounds);
- delete_all(mMonsterAttacks);
- mSounds.clear();
-}
-
-void MonsterInfo::addSound(MonsterSoundEvent event, const std::string &filename)
-{
- if (mSounds.find(event) == mSounds.end())
- {
- mSounds[event] = new std::vector<std::string>;
- }
-
- mSounds[event]->push_back(paths.getValue("sfx", "sfx/")
- + filename);
-}
-
-const std::string &MonsterInfo::getSound(MonsterSoundEvent event) const
-{
- static std::string empty("");
- std::map<MonsterSoundEvent, std::vector<std::string>* >::const_iterator i =
- mSounds.find(event);
- return (i == mSounds.end()) ? empty :
- i->second->at(rand() % i->second->size());
-}
-
-const std::string &MonsterInfo::getAttackParticleEffect(int attackType) const
-{
- static std::string empty("");
- std::map<int, MonsterAttack*>::const_iterator i =
- mMonsterAttacks.find(attackType);
- return (i == mMonsterAttacks.end()) ? empty : (*i).second->particleEffect;
-}
-
-const std::string &MonsterInfo::getAttackMissileParticle(int attackType) const
-{
- static std::string empty("");
- std::map<int, MonsterAttack*>::const_iterator i =
- mMonsterAttacks.find(attackType);
- return (i == mMonsterAttacks.end()) ? empty : (*i).second->missileParticle;
-}
-
-SpriteAction MonsterInfo::getAttackAction(int attackType) const
-{
- std::map<int, MonsterAttack*>::const_iterator i =
- mMonsterAttacks.find(attackType);
- return (i == mMonsterAttacks.end()) ? ACTION_ATTACK : (*i).second->action;
-}
-
-void MonsterInfo::addMonsterAttack(int id,
- const std::string &particleEffect,
- SpriteAction action,
- const std::string &missileParticle)
-{
- MonsterAttack *a = new MonsterAttack;
- a->particleEffect = particleEffect;
- a->missileParticle = missileParticle;
- a->action = action;
- mMonsterAttacks[id] = a;
-}
-
-void MonsterInfo::addParticleEffect(const std::string &filename)
-{
- mParticleEffects.push_back(filename);
-}
diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h
deleted file mode 100644
index f074254a..00000000
--- a/src/resources/monsterinfo.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2004-2009 The Mana World Development Team
- * Copyright (C) 2009-2010 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program 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.
- *
- * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MONSTERINFO_H
-#define MONSTERINFO_H
-
-#include "being.h"
-
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-enum MonsterSoundEvent
-{
- MONSTER_EVENT_HIT,
- MONSTER_EVENT_MISS,
- MONSTER_EVENT_HURT,
- MONSTER_EVENT_DIE
-};
-
-struct MonsterAttack
-{
- std::string missileParticle;
- std::string particleEffect;
- SpriteAction action;
-};
-
-/**
- * Holds information about a certain type of monster. This includes the name
- * of the monster, the sprite to display and the sounds the monster makes.
- *
- * @see MonsterDB
- */
-class MonsterInfo
-{
- public:
- MonsterInfo();
-
- ~MonsterInfo();
-
- void setName(const std::string &name) { mName = name; }
-
- void addSprite(const std::string &filename)
- { mSprites.push_back(filename); }
-
- void setTargetCursorSize(Being::TargetCursorSize targetCursorSize)
- { mTargetCursorSize = targetCursorSize; }
-
- void addSound(MonsterSoundEvent event, const std::string &filename);
-
- void addParticleEffect(const std::string &filename);
-
- const std::string &getName() const
- { return mName; }
-
- const std::list<std::string>& getSprites() const
- { return mSprites; }
-
- Being::TargetCursorSize getTargetCursorSize() const
- { return mTargetCursorSize; }
-
- const std::string &getSound(MonsterSoundEvent event) const;
-
- void addMonsterAttack(int id,
- const std::string &particleEffect,
- SpriteAction action,
- const std::string &missileParticle);
-
- const std::string &getAttackParticleEffect(int attackType) const;
-
- const std::string &getAttackMissileParticle(int attackType) const;
-
- SpriteAction getAttackAction(int attackType) const;
-
- const std::list<std::string>& getParticleEffects() const
- { return mParticleEffects; }
-
- private:
- std::string mName;
- std::list<std::string> mSprites;
- Being::TargetCursorSize mTargetCursorSize;
- std::map<MonsterSoundEvent, std::vector<std::string>* > mSounds;
- std::map<int, MonsterAttack*> mMonsterAttacks;
- std::list<std::string> mParticleEffects;
-};
-
-#endif // MONSTERINFO_H
diff --git a/src/resources/npcdb.cpp b/src/resources/npcdb.cpp
index 21a2e9a1..ec22c225 100644
--- a/src/resources/npcdb.cpp
+++ b/src/resources/npcdb.cpp
@@ -23,13 +23,15 @@
#include "log.h"
+#include "resources/beinginfo.h"
+
+#include "utils/dtor.h"
#include "utils/xml.h"
#include "configuration.h"
namespace
{
- NPCInfos mNPCInfos;
- NPCInfo mUnknown;
+ BeingInfos mNPCInfos;
bool mLoaded = false;
}
@@ -38,12 +40,6 @@ void NPCDB::load()
if (mLoaded)
unload();
- NPCsprite *unknownSprite = new NPCsprite;
- unknownSprite->sprite = paths.getValue("spriteErrorFile",
- "error.xml");
- unknownSprite->variant = 0;
- mUnknown.sprites.push_back(unknownSprite);
-
logger->log("Initializing NPC database...");
XML::Document doc("npcs.xml");
@@ -67,23 +63,30 @@ void NPCDB::load()
continue;
}
- NPCInfo *currentInfo = new NPCInfo;
+ BeingInfo *currentInfo = new BeingInfo;
+ currentInfo->setTargetCursorSize(XML::getProperty(npcNode,
+ "targetCursor", "medium"));
+
+ SpriteDisplay display;
for_each_xml_child_node(spriteNode, npcNode)
{
if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite"))
{
- NPCsprite *currentSprite = new NPCsprite;
+ SpriteReference *currentSprite = new SpriteReference;
currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content;
currentSprite->variant = XML::getProperty(spriteNode, "variant", 0);
- currentInfo->sprites.push_back(currentSprite);
+ display.sprites.push_back(currentSprite);
}
else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx"))
{
std::string particlefx = (const char*)spriteNode->xmlChildrenNode->content;
- currentInfo->particles.push_back(particlefx);
+ display.particles.push_back(particlefx);
}
}
+
+ currentInfo->setDisplay(display);
+
mNPCInfos[id] = currentInfo;
}
@@ -92,40 +95,23 @@ void NPCDB::load()
void NPCDB::unload()
{
- for ( NPCInfosIterator i = mNPCInfos.begin();
- i != mNPCInfos.end();
- i++)
- {
- while (!i->second->sprites.empty())
- {
- delete i->second->sprites.front();
- i->second->sprites.pop_front();
- }
- delete i->second;
- }
-
+ delete_all(mNPCInfos);
mNPCInfos.clear();
- while (!mUnknown.sprites.empty())
- {
- delete mUnknown.sprites.front();
- mUnknown.sprites.pop_front();
- }
-
mLoaded = false;
}
-const NPCInfo& NPCDB::get(int id)
+BeingInfo *NPCDB::get(int id)
{
- NPCInfosIterator i = mNPCInfos.find(id);
+ BeingInfoIterator i = mNPCInfos.find(id);
if (i == mNPCInfos.end())
{
logger->log("NPCDB: Warning, unknown NPC ID %d requested", id);
- return mUnknown;
+ return BeingInfo::Unknown;
}
else
{
- return *(i->second);
+ return i->second;
}
}
diff --git a/src/resources/npcdb.h b/src/resources/npcdb.h
index 9da873e4..b0c89c80 100644
--- a/src/resources/npcdb.h
+++ b/src/resources/npcdb.h
@@ -22,23 +22,7 @@
#ifndef NPC_DB_H
#define NPC_DB_H
-#include <list>
-#include <map>
-#include <string>
-
-struct NPCsprite
-{
- std::string sprite;
- int variant;
-};
-
-struct NPCInfo
-{
- std::list<NPCsprite*> sprites;
- std::list<std::string> particles;
-};
-
-typedef std::map<int, NPCInfo*> NPCInfos;
+class BeingInfo;
/**
* NPC information database.
@@ -49,9 +33,7 @@ namespace NPCDB
void unload();
- const NPCInfo& get(int id);
-
- typedef NPCInfos::iterator NPCInfosIterator;
+ BeingInfo *get(int id);
}
#endif
diff --git a/src/resources/specialdb.cpp b/src/resources/specialdb.cpp
new file mode 100644
index 00000000..ac591c4f
--- /dev/null
+++ b/src/resources/specialdb.cpp
@@ -0,0 +1,132 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/specialdb.h"
+
+#include "log.h"
+
+#include "utils/dtor.h"
+#include "utils/xml.h"
+
+
+namespace
+{
+ SpecialInfos mSpecialInfos;
+ bool mLoaded = false;
+}
+
+SpecialInfo::TargetMode SpecialDB::targetModeFromString(const std::string& str)
+{
+ if (str=="self") return SpecialInfo::TARGET_SELF;
+ else if (str=="friend") return SpecialInfo::TARGET_FRIEND;
+ else if (str=="enemy") return SpecialInfo::TARGET_ENEMY;
+ else if (str=="being") return SpecialInfo::TARGET_BEING;
+ else if (str=="point") return SpecialInfo::TARGET_POINT;
+
+ logger->log("SpecialDB: Warning, unknown target mode \"%s\"", str.c_str() );
+ return SpecialInfo::TARGET_SELF;
+}
+
+void SpecialDB::load()
+{
+ if (mLoaded)
+ unload();
+
+ logger->log("Initializing special database...");
+
+ XML::Document doc("specials.xml");
+ xmlNodePtr root = doc.rootNode();
+
+ if (!root || !xmlStrEqual(root->name, BAD_CAST "specials"))
+ {
+ logger->log("Error loading specials file specials.xml");
+ return;
+ }
+
+ std::string setName;
+
+ for_each_xml_child_node(set, root)
+ {
+ if (xmlStrEqual(set->name, BAD_CAST "set"))
+ {
+ setName = XML::getProperty(set, "name", "Actions");
+
+
+ for_each_xml_child_node(special, set)
+ {
+ if (xmlStrEqual(special->name, BAD_CAST "special"))
+ {
+ SpecialInfo *info = new SpecialInfo();
+ int id = XML::getProperty(special, "id", 0);
+ info->id = id;
+ info->set = setName;
+ info->name = XML::getProperty(special, "name", "");
+ info->icon = XML::getProperty(special, "icon", "");
+
+ info->isActive = XML::getBoolProperty(special, "active", false);
+ info->targetMode = targetModeFromString(XML::getProperty(special, "target", "self"));
+
+ info->level = XML::getProperty(special, "level", -1);
+ info->hasLevel = info->level > -1;
+
+ info->hasRechargeBar = XML::getBoolProperty(special, "recharge", false);
+ info->rechargeNeeded = 0;
+ info->rechargeCurrent = 0;
+
+ if (mSpecialInfos.find(id) != mSpecialInfos.end())
+ {
+ logger->log("SpecialDB: Duplicate special ID %d (ignoring)", id);
+ } else {
+ mSpecialInfos[id] = info;
+ }
+ }
+ }
+ }
+ }
+
+ mLoaded = true;
+}
+
+void SpecialDB::unload()
+{
+
+ delete_all(mSpecialInfos);
+ mSpecialInfos.clear();
+
+ mLoaded = false;
+}
+
+
+SpecialInfo *SpecialDB::get(int id)
+{
+
+ SpecialInfos::iterator i = mSpecialInfos.find(id);
+
+ if (i == mSpecialInfos.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return i->second;
+ }
+ return NULL;
+}
+
diff --git a/src/resources/specialdb.h b/src/resources/specialdb.h
new file mode 100644
index 00000000..38612c2a
--- /dev/null
+++ b/src/resources/specialdb.h
@@ -0,0 +1,72 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPECIAL_DB_H
+#define SPECIAL_DB_H
+
+#include <string>
+#include <map>
+
+struct SpecialInfo
+{
+ enum TargetMode
+ {
+ TARGET_SELF, // no target selection
+ TARGET_FRIEND, // target friendly being
+ TARGET_ENEMY, // target hostile being
+ TARGET_BEING, // target any being
+ TARGET_POINT // target map location
+ };
+ int id;
+ std::string set; // tab on which the special is shown
+ std::string name; // displayed name of special
+ std::string icon; // filename of graphical icon
+
+ bool isActive; // true when the special can be used
+ TargetMode targetMode; // target mode
+
+ bool hasLevel; // true when the special has levels
+ int level; // level of special when applicable
+
+ bool hasRechargeBar; // true when the special has a recharge bar
+ int rechargeNeeded; // maximum recharge when applicable
+ int rechargeCurrent; // current recharge when applicable
+};
+
+/**
+ * Special information database.
+ */
+namespace SpecialDB
+{
+ void load();
+
+ void unload();
+
+ /** gets the special info for ID. Will return 0 when it is
+ * a server-specific special.
+ */
+ SpecialInfo *get(int id);
+
+ SpecialInfo::TargetMode targetModeFromString(const std::string& str);
+}
+
+typedef std::map<int, SpecialInfo *> SpecialInfos;
+
+#endif
diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp
index c524c43c..311c9d1a 100644
--- a/src/resources/spritedef.cpp
+++ b/src/resources/spritedef.cpp
@@ -36,13 +36,16 @@
#include <set>
-Action *SpriteDef::getAction(SpriteAction action) const
+SpriteReference *SpriteReference::Empty = new SpriteReference(
+ paths.getStringValue("spriteErrorFile"), 0);
+
+Action *SpriteDef::getAction(const std::string &action) const
{
Actions::const_iterator i = mActions.find(action);
if (i == mActions.end())
{
- logger->log("Warning: no action \"%u\" defined!", action);
+ logger->log("Warning: no action \"%s\" defined!", action.c_str());
return NULL;
}
@@ -63,9 +66,8 @@ SpriteDef *SpriteDef::load(const std::string &animationFile, int variant)
{
logger->log("Error, failed to parse %s", animationFile.c_str());
- std::string errorFile = paths.getValue("sprites", "graphics/sprites")
- + paths.getValue("spriteErrorFile",
- "error.xml");
+ std::string errorFile = paths.getStringValue("sprites")
+ + paths.getStringValue("spriteErrorFile");
if (animationFile != errorFile)
{
return load(errorFile, 0);
@@ -82,22 +84,29 @@ SpriteDef *SpriteDef::load(const std::string &animationFile, int variant)
return def;
}
+void SpriteDef::substituteAction(std::string complete, std::string with)
+{
+ if (mActions.find(complete) == mActions.end())
+ {
+ Actions::iterator i = mActions.find(with);
+ if (i != mActions.end())
+ {
+ mActions[complete] = i->second;
+ }
+ }
+}
+
void SpriteDef::substituteActions()
{
- substituteAction(ACTION_STAND, ACTION_DEFAULT);
- substituteAction(ACTION_WALK, ACTION_STAND);
- substituteAction(ACTION_WALK, ACTION_RUN);
- substituteAction(ACTION_ATTACK, ACTION_STAND);
- substituteAction(ACTION_ATTACK_SWING, ACTION_ATTACK);
- substituteAction(ACTION_ATTACK_STAB, ACTION_ATTACK_SWING);
- substituteAction(ACTION_ATTACK_BOW, ACTION_ATTACK_STAB);
- substituteAction(ACTION_ATTACK_THROW, ACTION_ATTACK_SWING);
- substituteAction(ACTION_CAST_MAGIC, ACTION_ATTACK_SWING);
- substituteAction(ACTION_USE_ITEM, ACTION_CAST_MAGIC);
- substituteAction(ACTION_SIT, ACTION_STAND);
- substituteAction(ACTION_SLEEP, ACTION_SIT);
- substituteAction(ACTION_HURT, ACTION_STAND);
- substituteAction(ACTION_DEAD, ACTION_HURT);
+ substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT);
+ substituteAction(SpriteAction::MOVE, SpriteAction::STAND);
+ substituteAction(SpriteAction::ATTACK, SpriteAction::STAND);
+ substituteAction(SpriteAction::CAST_MAGIC, SpriteAction::ATTACK);
+ substituteAction(SpriteAction::USE_ITEM, SpriteAction::CAST_MAGIC);
+ substituteAction(SpriteAction::SIT, SpriteAction::STAND);
+ substituteAction(SpriteAction::SLEEP, SpriteAction::SIT);
+ substituteAction(SpriteAction::HURT, SpriteAction::STAND);
+ substituteAction(SpriteAction::DEAD, SpriteAction::HURT);
}
void SpriteDef::loadSprite(xmlNodePtr spriteNode, int variant,
@@ -169,20 +178,19 @@ void SpriteDef::loadAction(xmlNodePtr node, int variant_offset)
}
ImageSet *imageSet = si->second;
- SpriteAction actionType = makeSpriteAction(actionName);
- if (actionType == ACTION_INVALID)
+ if (actionName == SpriteAction::INVALID)
{
logger->log("Warning: Unknown action \"%s\" defined in %s",
actionName.c_str(), getIdPath().c_str());
return;
}
Action *action = new Action;
- mActions[actionType] = action;
+ mActions[actionName] = action;
// When first action set it as default direction
- if (mActions.empty())
+ if (mActions.size() == 1)
{
- mActions[ACTION_DEFAULT] = action;
+ mActions[SpriteAction::DEFAULT] = action;
}
// Load animations
@@ -283,8 +291,7 @@ void SpriteDef::includeSprite(xmlNodePtr includeNode)
if (filename.empty())
return;
- XML::Document doc(paths.getValue("sprites", "graphics/sprites/")
- + filename);
+ XML::Document doc(paths.getStringValue("sprites") + filename);
xmlNodePtr rootNode = doc.rootNode();
if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite"))
@@ -296,18 +303,6 @@ void SpriteDef::includeSprite(xmlNodePtr includeNode)
loadSprite(rootNode, 0);
}
-void SpriteDef::substituteAction(SpriteAction complete, SpriteAction with)
-{
- if (mActions.find(complete) == mActions.end())
- {
- Actions::iterator i = mActions.find(with);
- if (i != mActions.end())
- {
- mActions[complete] = i->second;
- }
- }
-}
-
SpriteDef::~SpriteDef()
{
// Actions are shared, so ensure they are deleted only once.
@@ -331,63 +326,6 @@ SpriteDef::~SpriteDef()
}
}
-SpriteAction SpriteDef::makeSpriteAction(const std::string &action)
-{
- if (action.empty() || action == "default")
- return ACTION_DEFAULT;
-
- if (action == "stand")
- return ACTION_STAND;
- else if (action == "walk")
- return ACTION_WALK;
- else if (action == "run")
- return ACTION_RUN;
- else if (action == "attack")
- return ACTION_ATTACK;
- else if (action == "attack_swing")
- return ACTION_ATTACK_SWING;
- else if (action == "attack_stab")
- return ACTION_ATTACK_STAB;
- else if (action == "attack_bow")
- return ACTION_ATTACK_BOW;
- else if (action == "attack_throw")
- return ACTION_ATTACK_THROW;
- else if (action == "special0")
- return ACTION_SPECIAL_0;
- else if (action == "special1")
- return ACTION_SPECIAL_1;
- else if (action == "special2")
- return ACTION_SPECIAL_2;
- else if (action == "special3")
- return ACTION_SPECIAL_3;
- else if (action == "special4")
- return ACTION_SPECIAL_4;
- else if (action == "special5")
- return ACTION_SPECIAL_5;
- else if (action == "special6")
- return ACTION_SPECIAL_6;
- else if (action == "special7")
- return ACTION_SPECIAL_7;
- else if (action == "special8")
- return ACTION_SPECIAL_8;
- else if (action == "special9")
- return ACTION_SPECIAL_9;
- else if (action == "cast_magic")
- return ACTION_CAST_MAGIC;
- else if (action == "use_item")
- return ACTION_USE_ITEM;
- else if (action == "sit")
- return ACTION_SIT;
- else if (action == "sleep")
- return ACTION_SLEEP;
- else if (action == "hurt")
- return ACTION_HURT;
- else if (action == "dead")
- return ACTION_DEAD;
- else
- return ACTION_INVALID;
-}
-
SpriteDirection SpriteDef::makeSpriteDirection(const std::string &direction)
{
if (direction.empty() || direction == "default")
diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h
index 5bb6078e..18a70c9b 100644
--- a/src/resources/spritedef.h
+++ b/src/resources/spritedef.h
@@ -26,42 +26,60 @@
#include <libxml/tree.h>
+#include <list>
#include <map>
#include <string>
class Action;
class ImageSet;
-enum SpriteAction
+struct SpriteReference
{
- ACTION_DEFAULT = 0,
- ACTION_STAND,
- ACTION_WALK,
- ACTION_RUN,
- ACTION_ATTACK,
- ACTION_ATTACK_SWING,
- ACTION_ATTACK_STAB,
- ACTION_ATTACK_BOW,
- ACTION_ATTACK_THROW,
- ACTION_SPECIAL_0,
- ACTION_SPECIAL_1,
- ACTION_SPECIAL_2,
- ACTION_SPECIAL_3,
- ACTION_SPECIAL_4,
- ACTION_SPECIAL_5,
- ACTION_SPECIAL_6,
- ACTION_SPECIAL_7,
- ACTION_SPECIAL_8,
- ACTION_SPECIAL_9,
- ACTION_CAST_MAGIC,
- ACTION_USE_ITEM,
- ACTION_SIT,
- ACTION_SLEEP,
- ACTION_HURT,
- ACTION_DEAD,
- ACTION_INVALID
+ static SpriteReference *Empty;
+
+ SpriteReference() {}
+
+ SpriteReference(std::string sprite, int variant)
+ { this->sprite = sprite; this->variant = variant; }
+
+ std::string sprite;
+ int variant;
};
+struct SpriteDisplay
+{
+ std::string image;
+ std::list<SpriteReference*> sprites;
+ std::list<std::string> particles;
+};
+
+typedef std::list<SpriteReference*>::const_iterator SpriteRefs;
+
+/*
+ * Remember those are the main action.
+ * Action subtypes, e.g.: "attack_bow" are to be passed by items.xml after
+ * an ACTION_ATTACK call.
+ * Which special to be use to to be passed with the USE_SPECIAL call.
+ * Running, walking, ... is a sub-type of moving.
+ * ...
+ * Please don't add hard-coded subtypes here!
+ */
+namespace SpriteAction
+{
+ static const std::string DEFAULT = "stand";
+ static const std::string STAND = "stand";
+ static const std::string SIT = "sit";
+ static const std::string SLEEP = "sleep";
+ static const std::string DEAD = "dead";
+ static const std::string MOVE = "walk";
+ static const std::string ATTACK = "attack";
+ static const std::string HURT = "hurt";
+ static const std::string USE_SPECIAL = "special";
+ static const std::string CAST_MAGIC = "magic";
+ static const std::string USE_ITEM = "item";
+ static const std::string INVALID = "";
+}
+
enum SpriteDirection
{
DIRECTION_DEFAULT = 0,
@@ -86,12 +104,7 @@ class SpriteDef : public Resource
/**
* Returns the specified action.
*/
- Action *getAction(SpriteAction action) const;
-
- /**
- * Converts a string into a SpriteAction enum.
- */
- static SpriteAction makeSpriteAction(const std::string &action);
+ Action *getAction(const std::string &action) const;
/**
* Converts a string into a SpriteDirection enum.
@@ -147,12 +160,12 @@ class SpriteDef : public Resource
* When there are no animations defined for the action "complete", its
* animations become a copy of those of the action "with".
*/
- void substituteAction(SpriteAction complete, SpriteAction with);
+ void substituteAction(std::string complete, std::string with);
typedef std::map<std::string, ImageSet*> ImageSets;
typedef ImageSets::iterator ImageSetIterator;
- typedef std::map<SpriteAction, Action*> Actions;
+ typedef std::map<std::string, Action*> Actions;
ImageSets mImageSets;
Actions mActions;
diff --git a/src/resources/theme.cpp b/src/resources/theme.cpp
new file mode 100644
index 00000000..8de275ae
--- /dev/null
+++ b/src/resources/theme.cpp
@@ -0,0 +1,582 @@
+/*
+ * Gui Skinning
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2009 Aethyra Development Team
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/theme.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "log.h"
+
+#include "resources/dye.h"
+#include "resources/image.h"
+#include "resources/imageset.h"
+#include "resources/resourcemanager.h"
+
+#include "utils/dtor.h"
+#include "utils/stringutils.h"
+#include "utils/xml.h"
+
+#include <physfs.h>
+
+#include <algorithm>
+
+static std::string defaultThemePath;
+std::string Theme::mThemePath;
+Theme *Theme::mInstance = 0;
+
+// Set the theme path...
+static void initDefaultThemePath()
+{
+ ResourceManager *resman = ResourceManager::getInstance();
+ defaultThemePath = branding.getStringValue("guiThemePath");
+
+ if (!defaultThemePath.empty() && resman->isDirectory(defaultThemePath))
+ return;
+ else
+ defaultThemePath = "graphics/gui/";
+}
+
+Skin::Skin(ImageRect skin, Image *close, Image *stickyUp, Image *stickyDown,
+ const std::string &filePath,
+ const std::string &name):
+ instances(0),
+ mFilePath(filePath),
+ mName(name),
+ mBorder(skin),
+ mCloseImage(close),
+ mStickyImageUp(stickyUp),
+ mStickyImageDown(stickyDown)
+{}
+
+Skin::~Skin()
+{
+ // Clean up static resources
+ for (int i = 0; i < 9; i++)
+ delete mBorder.grid[i];
+
+ mCloseImage->decRef();
+ delete mStickyImageUp;
+ delete mStickyImageDown;
+}
+
+void Skin::updateAlpha(float minimumOpacityAllowed)
+{
+ const float alpha = std::max(minimumOpacityAllowed,
+ config.getFloatValue("guialpha"));
+
+ for_each(mBorder.grid, mBorder.grid + 9,
+ std::bind2nd(std::mem_fun(&Image::setAlpha), alpha));
+
+ mCloseImage->setAlpha(alpha);
+ mStickyImageUp->setAlpha(alpha);
+ mStickyImageDown->setAlpha(alpha);
+}
+
+int Skin::getMinWidth() const
+{
+ return mBorder.grid[ImageRect::UPPER_LEFT]->getWidth() +
+ mBorder.grid[ImageRect::UPPER_RIGHT]->getWidth();
+}
+
+int Skin::getMinHeight() const
+{
+ return mBorder.grid[ImageRect::UPPER_LEFT]->getHeight() +
+ mBorder.grid[ImageRect::LOWER_LEFT]->getHeight();
+}
+
+Theme::Theme():
+ Palette(THEME_COLORS_END),
+ mMinimumOpacity(-1.0f),
+ mProgressColors(ProgressColors(THEME_PROG_END))
+{
+ initDefaultThemePath();
+
+ listen(CHANNEL_CONFIG);
+ loadColors();
+
+ mColors[HIGHLIGHT].ch = 'H';
+ mColors[CHAT].ch = 'C';
+ mColors[GM].ch = 'G';
+ mColors[PLAYER].ch = 'Y';
+ mColors[WHISPER].ch = 'W';
+ mColors[IS].ch = 'I';
+ mColors[PARTY].ch = 'P';
+ mColors[GUILD].ch = 'U';
+ mColors[SERVER].ch = 'S';
+ mColors[LOGGER].ch = 'L';
+ mColors[HYPERLINK].ch = '<';
+}
+
+Theme::~Theme()
+{
+ delete_all(mSkins);
+ delete_all(mProgressColors);
+}
+
+Theme *Theme::instance()
+{
+ if (!mInstance)
+ mInstance = new Theme;
+
+ return mInstance;
+}
+
+void Theme::deleteInstance()
+{
+ delete mInstance;
+ mInstance = 0;
+}
+
+gcn::Color Theme::getProgressColor(int type, float progress)
+{
+ DyePalette *dye = mInstance->mProgressColors[type];
+
+ int color[3] = {0, 0, 0};
+ dye->getColor(progress, color);
+
+ return gcn::Color(color[0], color[1], color[2]);
+}
+
+Skin *Theme::load(const std::string &filename, const std::string &defaultPath)
+{
+ // Check if this skin was already loaded
+ SkinIterator skinIterator = mSkins.find(filename);
+ if (mSkins.end() != skinIterator)
+ {
+ skinIterator->second->instances++;
+ return skinIterator->second;
+ }
+
+ Skin *skin = readSkin(filename);
+
+ if (!skin)
+ {
+ // Try falling back on the defaultPath if this makes sense
+ if (filename != defaultPath)
+ {
+ logger->log("Error loading skin '%s', falling back on default.",
+ filename.c_str());
+
+ skin = readSkin(defaultPath);
+ }
+
+ if (!skin)
+ {
+ logger->error(strprintf("Error: Loading default skin '%s' failed. "
+ "Make sure the skin file is valid.",
+ defaultPath.c_str()));
+ }
+ }
+
+ // Add the skin to the loaded skins
+ mSkins[filename] = skin;
+
+ return skin;
+}
+
+void Theme::setMinimumOpacity(float minimumOpacity)
+{
+ if (minimumOpacity > 1.0f) return;
+
+ mMinimumOpacity = minimumOpacity;
+ updateAlpha();
+}
+
+void Theme::updateAlpha()
+{
+ for (SkinIterator iter = mSkins.begin(); iter != mSkins.end(); ++iter)
+ iter->second->updateAlpha(mMinimumOpacity);
+}
+
+void Theme::event(Channels channel, const Mana::Event &event)
+{
+ if (channel == CHANNEL_CONFIG &&
+ event.getName() == EVENT_CONFIGOPTIONCHANGED &&
+ event.getString("option") == "guialpha")
+ {
+ updateAlpha();
+ }
+}
+
+Skin *Theme::readSkin(const std::string &filename)
+{
+ if (filename.empty())
+ return 0;
+
+ logger->log("Loading skin '%s'.", filename.c_str());
+
+ XML::Document doc(resolveThemePath(filename));
+ xmlNodePtr rootNode = doc.rootNode();
+
+ if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset"))
+ return 0;
+
+ const std::string skinSetImage = XML::getProperty(rootNode, "image", "");
+
+ if (skinSetImage.empty())
+ {
+ logger->log("Theme::readSkin(): Skinset does not define an image!");
+ return 0;
+ }
+
+ logger->log("Theme::load(): <skinset> defines '%s' as a skin image.",
+ skinSetImage.c_str());
+
+ Image *dBorders = Theme::getImageFromTheme(skinSetImage);
+ ImageRect border;
+ memset(&border, 0, sizeof(ImageRect));
+
+ // iterate <widget>'s
+ for_each_xml_child_node(widgetNode, rootNode)
+ {
+ if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget"))
+ continue;
+
+ const std::string widgetType =
+ XML::getProperty(widgetNode, "type", "unknown");
+ if (widgetType == "Window")
+ {
+ // Iterate through <part>'s
+ // LEEOR / TODO:
+ // We need to make provisions to load in a CloseButton image. For
+ // now it can just be hard-coded.
+ for_each_xml_child_node(partNode, widgetNode)
+ {
+ if (!xmlStrEqual(partNode->name, BAD_CAST "part"))
+ continue;
+
+ const std::string partType =
+ XML::getProperty(partNode, "type", "unknown");
+ // TOP ROW
+ const int xPos = XML::getProperty(partNode, "xpos", 0);
+ const int yPos = XML::getProperty(partNode, "ypos", 0);
+ const int width = XML::getProperty(partNode, "width", 1);
+ const int height = XML::getProperty(partNode, "height", 1);
+
+ if (partType == "top-left-corner")
+ border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height);
+ else if (partType == "top-edge")
+ border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height);
+ else if (partType == "top-right-corner")
+ border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height);
+
+ // MIDDLE ROW
+ else if (partType == "left-edge")
+ border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height);
+ else if (partType == "bg-quad")
+ border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height);
+ else if (partType == "right-edge")
+ border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height);
+
+ // BOTTOM ROW
+ else if (partType == "bottom-left-corner")
+ border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height);
+ else if (partType == "bottom-edge")
+ border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height);
+ else if (partType == "bottom-right-corner")
+ border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height);
+
+ else
+ logger->log("Theme::readSkin(): Unknown part type '%s'",
+ partType.c_str());
+ }
+ }
+ else
+ {
+ logger->log("Theme::readSkin(): Unknown widget type '%s'",
+ widgetType.c_str());
+ }
+ }
+
+ dBorders->decRef();
+
+ logger->log("Finished loading skin.");
+
+ // Hard-coded for now until we update the above code to look for window buttons
+ Image *closeImage = Theme::getImageFromTheme("close_button.png");
+ Image *sticky = Theme::getImageFromTheme("sticky_button.png");
+ Image *stickyImageUp = sticky->getSubImage(0, 0, 15, 15);
+ Image *stickyImageDown = sticky->getSubImage(15, 0, 15, 15);
+ sticky->decRef();
+
+ Skin *skin = new Skin(border, closeImage, stickyImageUp, stickyImageDown,
+ filename);
+ skin->updateAlpha(mMinimumOpacity);
+ return skin;
+}
+
+bool Theme::tryThemePath(std::string themePath)
+{
+ if (!themePath.empty())
+ {
+ themePath = defaultThemePath + themePath;
+
+ if (PHYSFS_exists(themePath.c_str()))
+ {
+ mThemePath = themePath;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Theme::prepareThemePath()
+{
+ // Ensure the Theme object has been created
+ instance();
+
+ // Try theme from settings
+ if (!tryThemePath(config.getStringValue("theme")))
+ // Try theme from branding
+ if (!tryThemePath(branding.getStringValue("theme")))
+ // Use default
+ mThemePath = defaultThemePath;
+
+ instance()->loadColors(mThemePath);
+}
+
+std::string Theme::resolveThemePath(const std::string &path)
+{
+ // Need to strip off any dye info for the existence tests
+ int pos = path.find('|');
+ std::string file;
+ if (pos > 0)
+ file = path.substr(0, pos);
+ else
+ file = path;
+
+ // Might be a valid path already
+ if (PHYSFS_exists(file.c_str()))
+ return path;
+
+ // Try the theme
+ file = getThemePath() + "/" + file;
+ if (PHYSFS_exists(file.c_str()))
+ return getThemePath() + "/" + path;
+
+ // Backup
+ return std::string(defaultThemePath) + "/" + path;
+}
+
+Image *Theme::getImageFromTheme(const std::string &path)
+{
+ ResourceManager *resman = ResourceManager::getInstance();
+ return resman->getImage(resolveThemePath(path));
+}
+
+ImageSet *Theme::getImageSetFromTheme(const std::string &path,
+ int w, int h)
+{
+ ResourceManager *resman = ResourceManager::getInstance();
+ return resman->getImageSet(resolveThemePath(path), w, h);
+}
+
+static int readColorType(const std::string &type)
+{
+ static std::string colors[] = {
+ "TEXT",
+ "SHADOW",
+ "OUTLINE",
+ "PROGRESS_BAR",
+ "BUTTON",
+ "BUTTON_DISABLED",
+ "TAB",
+ "PARTY_CHAT_TAB",
+ "PARTY_SOCIAL_TAB",
+ "BACKGROUND",
+ "HIGHLIGHT",
+ "TAB_FLASH",
+ "SHOP_WARNING",
+ "ITEM_EQUIPPED",
+ "CHAT",
+ "GM",
+ "PLAYER",
+ "WHISPER",
+ "IS",
+ "PARTY",
+ "GUILD",
+ "SERVER",
+ "LOGGER",
+ "HYPERLINK",
+ "UNKNOWN_ITEM",
+ "GENERIC",
+ "HEAD",
+ "USABLE",
+ "TORSO",
+ "ONEHAND",
+ "LEGS",
+ "FEET",
+ "TWOHAND",
+ "SHIELD",
+ "RING",
+ "NECKLACE",
+ "ARMS",
+ "AMMO",
+ "SERVER_VERSION_NOT_SUPPORTED"
+ };
+
+ if (type.empty())
+ return -1;
+
+ for (int i = 0; i < Theme::THEME_COLORS_END; i++)
+ {
+ if (compareStrI(type, colors[i]) == 0)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static gcn::Color readColor(const std::string &description)
+{
+ int size = description.length();
+ if (size < 7 || description[0] != '#')
+ {
+ error:
+ logger->log("Error, invalid theme color palette: %s",
+ description.c_str());
+ return Palette::BLACK;
+ }
+
+ int v = 0;
+ for (int i = 1; i < 7; ++i)
+ {
+ char c = description[i];
+ int n;
+
+ if ('0' <= c && c <= '9')
+ n = c - '0';
+ else if ('A' <= c && c <= 'F')
+ n = c - 'A' + 10;
+ else if ('a' <= c && c <= 'f')
+ n = c - 'a' + 10;
+ else
+ goto error;
+
+ v = (v << 4) | n;
+ }
+
+ return gcn::Color(v);
+}
+
+static Palette::GradientType readColorGradient(const std::string &grad)
+{
+ static std::string grads[] = {
+ "STATIC",
+ "PULSE",
+ "SPECTRUM",
+ "RAINBOW"
+ };
+
+ if (grad.empty())
+ return Palette::STATIC;
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (compareStrI(grad, grads[i]))
+ return (Palette::GradientType) i;
+ }
+
+ return Palette::STATIC;
+}
+
+static int readProgressType(const std::string &type)
+{
+ static std::string colors[] = {
+ "DEFAULT",
+ "HP",
+ "MP",
+ "NO_MP",
+ "EXP",
+ "INVY_SLOTS",
+ "WEIGHT",
+ "JOB"
+ };
+
+ if (type.empty())
+ return -1;
+
+ for (int i = 0; i < Theme::THEME_PROG_END; i++)
+ {
+ if (compareStrI(type, colors[i]) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void Theme::loadColors(std::string file)
+{
+ if (file == defaultThemePath)
+ return; // No need to reload
+
+ if (file == "")
+ file = defaultThemePath;
+
+ file += "/colors.xml";
+
+ XML::Document doc(file);
+ xmlNodePtr root = doc.rootNode();
+
+ if (!root || !xmlStrEqual(root->name, BAD_CAST "colors"))
+ {
+ logger->log("Error loading colors file: %s", file.c_str());
+ return;
+ }
+
+ int type;
+ std::string temp;
+ gcn::Color color;
+ GradientType grad;
+
+ for_each_xml_child_node(node, root)
+ {
+ if (xmlStrEqual(node->name, BAD_CAST "color"))
+ {
+ type = readColorType(XML::getProperty(node, "id", ""));
+ if (type < 0) // invalid or no type given
+ continue;
+
+ temp = XML::getProperty(node, "color", "");
+ if (temp.empty()) // no color set, so move on
+ continue;
+
+ color = readColor(temp);
+ grad = readColorGradient(XML::getProperty(node, "effect", ""));
+
+ mColors[type].set(type, color, grad, 10);
+ }
+ else if (xmlStrEqual(node->name, BAD_CAST "progressbar"))
+ {
+ type = readProgressType(XML::getProperty(node, "id", ""));
+ if (type < 0) // invalid or no type given
+ continue;
+
+ mProgressColors[type] = new DyePalette(XML::getProperty(node,
+ "color", ""));
+ }
+ }
+}
diff --git a/src/resources/theme.h b/src/resources/theme.h
new file mode 100644
index 00000000..f830c94f
--- /dev/null
+++ b/src/resources/theme.h
@@ -0,0 +1,252 @@
+/*
+ * Gui Skinning
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2009 Aethyra Development Team
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKIN_H
+#define SKIN_H
+
+#include "graphics.h"
+#include "listener.h"
+
+#include "gui/palette.h"
+
+#include <map>
+#include <string>
+
+class DyePalette;
+class Image;
+class ImageSet;
+class ProgressBar;
+
+class Skin
+{
+ public:
+ Skin(ImageRect skin, Image *close, Image *stickyUp, Image *stickyDown,
+ const std::string &filePath,
+ const std::string &name = "");
+
+ ~Skin();
+
+ /**
+ * Returns the skin's name. Useful for giving a human friendly skin
+ * name if a dialog for skin selection for a specific window type is
+ * done.
+ */
+ const std::string &getName() const { return mName; }
+
+ /**
+ * Returns the skin's xml file path.
+ */
+ const std::string &getFilePath() const { return mFilePath; }
+
+ /**
+ * Returns the background skin.
+ */
+ const ImageRect &getBorder() const { return mBorder; }
+
+ /**
+ * Returns the image used by a close button for this skin.
+ */
+ Image *getCloseImage() const { return mCloseImage; }
+
+ /**
+ * Returns the image used by a sticky button for this skin.
+ */
+ Image *getStickyImage(bool state) const
+ { return state ? mStickyImageDown : mStickyImageUp; }
+
+ /**
+ * Returns the minimum width which can be used with this skin.
+ */
+ int getMinWidth() const;
+
+ /**
+ * Returns the minimum height which can be used with this skin.
+ */
+ int getMinHeight() const;
+
+ /**
+ * Updates the alpha value of the skin
+ */
+ void updateAlpha(float minimumOpacityAllowed = 0.0f);
+
+ int instances;
+
+ private:
+ std::string mFilePath; /**< File name path for the skin */
+ std::string mName; /**< Name of the skin to use */
+ ImageRect mBorder; /**< The window border and background */
+ Image *mCloseImage; /**< Close Button Image */
+ Image *mStickyImageUp; /**< Sticky Button Image */
+ Image *mStickyImageDown; /**< Sticky Button Image */
+};
+
+class Theme : public Palette, public Mana::Listener
+{
+ public:
+ static Theme *instance();
+ static void deleteInstance();
+
+ static void prepareThemePath();
+ static std::string getThemePath() { return mThemePath; }
+
+ /**
+ * Returns the patch to the given gui resource relative to the theme
+ * or, if it isn't in the theme, relative to 'graphics/gui'.
+ */
+ static std::string resolveThemePath(const std::string &path);
+
+ static Image *getImageFromTheme(const std::string &path);
+ static ImageSet *getImageSetFromTheme(const std::string &path,
+ int w, int h);
+
+ enum ThemePalette {
+ TEXT,
+ SHADOW,
+ OUTLINE,
+ PROGRESS_BAR,
+ BUTTON,
+ BUTTON_DISABLED,
+ TAB,
+ PARTY_CHAT_TAB,
+ PARTY_SOCIAL_TAB,
+ BACKGROUND,
+ HIGHLIGHT,
+ TAB_FLASH,
+ SHOP_WARNING,
+ ITEM_EQUIPPED,
+ CHAT,
+ GM,
+ PLAYER,
+ WHISPER,
+ IS,
+ PARTY,
+ GUILD,
+ SERVER,
+ LOGGER,
+ HYPERLINK,
+ UNKNOWN_ITEM,
+ GENERIC,
+ HEAD,
+ USABLE,
+ TORSO,
+ ONEHAND,
+ LEGS,
+ FEET,
+ TWOHAND,
+ SHIELD,
+ RING,
+ NECKLACE,
+ ARMS,
+ AMMO,
+ SERVER_VERSION_NOT_SUPPORTED,
+ THEME_COLORS_END
+ };
+
+ enum ProgressPalette {
+ PROG_DEFAULT,
+ PROG_HP,
+ PROG_MP,
+ PROG_NO_MP,
+ PROG_EXP,
+ PROG_INVY_SLOTS,
+ PROG_WEIGHT,
+ PROG_JOB,
+ THEME_PROG_END
+ };
+
+ /**
+ * Gets the color associated with the type. Sets the alpha channel
+ * before returning.
+ *
+ * @param type the color type requested
+ * @param alpha alpha channel to use
+ *
+ * @return the requested color
+ */
+ inline static const gcn::Color &getThemeColor(int type, int alpha = 255)
+ {
+ return mInstance->getColor(type, alpha);
+ }
+
+ const static gcn::Color &getThemeColor(char c, bool &valid)
+ {
+ return mInstance->getColor(c, valid);
+ }
+
+ static gcn::Color getProgressColor(int type, float progress);
+
+ /**
+ * Loads a skin.
+ */
+ Skin *load(const std::string &filename,
+ const std::string &defaultPath = getThemePath());
+
+ /**
+ * Updates the alpha values of all of the skins.
+ */
+ void updateAlpha();
+
+ /**
+ * Get the minimum opacity allowed to skins.
+ */
+ float getMinimumOpacity()
+ { return mMinimumOpacity; }
+
+ /**
+ * Set the minimum opacity allowed to skins.
+ * Set a negative value to free the minimum allowed.
+ */
+ void setMinimumOpacity(float minimumOpacity);
+
+ void event(Channels channel, const Mana::Event &event);
+
+ private:
+ Theme();
+ ~Theme();
+
+ Skin *readSkin(const std::string &filename);
+
+ // Map containing all window skins
+ typedef std::map<std::string, Skin*> Skins;
+ typedef Skins::iterator SkinIterator;
+
+ Skins mSkins;
+
+ static std::string mThemePath;
+ static Theme *mInstance;
+
+ static bool tryThemePath(std::string themePath);
+
+ void loadColors(std::string file = "");
+
+ /**
+ * Tells if the current skins opacity
+ * should not get less than the given value
+ */
+ float mMinimumOpacity;
+
+ typedef std::vector<DyePalette*> ProgressColors;
+ ProgressColors mProgressColors;
+};
+
+#endif
diff --git a/src/resources/userpalette.cpp b/src/resources/userpalette.cpp
new file mode 100644
index 00000000..a6b5bc03
--- /dev/null
+++ b/src/resources/userpalette.cpp
@@ -0,0 +1,247 @@
+/*
+ * Configurable text colors
+ * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net>
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/userpalette.h"
+
+#include "configuration.h"
+#include "client.h"
+
+#include "gui/gui.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <math.h>
+
+const std::string ColorTypeNames[] = {
+ "ColorBeing",
+ "ColorPlayer",
+ "ColorSelf",
+ "ColorGM",
+ "ColorNPC",
+ "ColorMonster",
+ "ColorParty",
+ "ColorGuild",
+ "ColorParticle",
+ "ColorExperience",
+ "ColorPickup",
+ "ColorHitPlayerMonster",
+ "ColorHitMonsterPlayer",
+ "ColorHitCritical",
+ "ColorHitLocalPlayerMonster",
+ "ColorHitLocalPlayerCritical",
+ "ColorHitLocalPlayerMiss",
+ "ColorMiss"
+};
+
+std::string UserPalette::getConfigName(const std::string &typeName)
+{
+ std::string res = "Color" + typeName;
+
+ int pos = 5;
+ for (size_t i = 0; i < typeName.length(); i++)
+ {
+ if (i == 0 || typeName[i] == '_')
+ {
+ if (i > 0)
+ i++;
+
+ res[pos] = typeName[i];
+ }
+ else
+ {
+ res[pos] = tolower(typeName[i]);
+ }
+ pos++;
+ }
+ res.erase(pos, res.length() - pos);
+
+ return res;
+}
+
+UserPalette::UserPalette():
+ Palette(USER_COLOR_LAST)
+{
+ mColors[BEING] = ColorElem();
+ mColors[PC] = ColorElem();
+ mColors[SELF] = ColorElem();
+ mColors[GM] = ColorElem();
+ mColors[NPC] = ColorElem();
+ mColors[MONSTER] = ColorElem();
+
+ addColor(BEING, 0xffffff, STATIC, _("Being"));
+ addColor(PC, 0xffffff, STATIC, _("Other Players' Names"));
+ addColor(SELF, 0xff8040, STATIC, _("Own Name"));
+ addColor(GM, 0x00ff00, STATIC, _("GM Names"));
+ addColor(NPC, 0xc8c8ff, STATIC, _("NPCs"));
+ addColor(MONSTER, 0xff4040, STATIC, _("Monsters"));
+ addColor(PARTY, 0xff00d8, STATIC, _("Party Members"));
+ addColor(GUILD, 0xff00d8, STATIC, _("Guild Members"));
+ addColor(PARTICLE, 0xffffff, STATIC, _("Particle Effects"));
+ addColor(PICKUP_INFO, 0x28dc28, STATIC, _("Pickup Notification"));
+ addColor(EXP_INFO, 0xffff00, STATIC, _("Exp Notification"));
+ addColor(HIT_PLAYER_MONSTER, 0x0064ff, STATIC,
+ _("Other Player Hits Monster"));
+ addColor(HIT_MONSTER_PLAYER, 0xff3232, STATIC, _("Monster Hits Player"));
+ addColor(HIT_CRITICAL, 0xff0000, RAINBOW, _("Critical Hit"));
+ addColor(HIT_LOCAL_PLAYER_MONSTER, 0x00ff00, STATIC,
+ _("Local Player Hits Monster"));
+ addColor(HIT_LOCAL_PLAYER_CRITICAL, 0xff0000, RAINBOW,
+ _("Local Player Critical Hit"));
+ addColor(HIT_LOCAL_PLAYER_MISS, 0x00ffa6, STATIC,
+ _("Local Player Miss"));
+ addColor(MISS, 0xffff00, STATIC, _("Misses"));
+ commit(true);
+}
+
+UserPalette::~UserPalette()
+{
+ for (Colors::iterator col = mColors.begin(),
+ colEnd = mColors.end(); col != colEnd; ++col)
+ {
+ const std::string &configName = ColorTypeNames[col->type];
+ config.setValue(configName + "Gradient", col->committedGrad);
+
+ if (col->grad != STATIC)
+ config.setValue(configName + "Delay", col->delay);
+
+ if (col->grad == STATIC || col->grad == PULSE)
+ {
+ char buffer[20];
+ sprintf(buffer, "0x%06x", col->getRGB());
+ config.setValue(configName, std::string(buffer));
+ }
+ }
+}
+
+void UserPalette::setColor(int type, int r, int g, int b)
+{
+ mColors[type].color.r = r;
+ mColors[type].color.g = g;
+ mColors[type].color.b = b;
+}
+
+void UserPalette::setGradient(int type, GradientType grad)
+{
+ ColorElem *elem = &mColors[type];
+ if (elem->grad != STATIC && grad == STATIC)
+ {
+ for (size_t i = 0; i < mGradVector.size(); i++)
+ {
+ if (mGradVector[i] == elem)
+ {
+ mGradVector.erase(mGradVector.begin() + i);
+ break;
+ }
+ }
+ }
+ else if (elem->grad == STATIC && grad != STATIC)
+ {
+ mGradVector.push_back(elem);
+ }
+
+ if (elem->grad != grad)
+ {
+ elem->grad = grad;
+ }
+}
+
+std::string UserPalette::getElementAt(int i)
+{
+ if (i < 0 || i >= getNumberOfElements())
+ {
+ return "";
+ }
+ return mColors[i].text;
+}
+
+void UserPalette::commit(bool commitNonStatic)
+{
+ for (Colors::iterator i = mColors.begin(), iEnd = mColors.end();
+ i != iEnd; ++i)
+ {
+ i->committedGrad = i->grad;
+ i->committedDelay = i->delay;
+ if (commitNonStatic || i->grad == STATIC)
+ {
+ i->committedColor = i->color;
+ }
+ else if (i->grad == PULSE)
+ {
+ i->committedColor = i->testColor;
+ }
+ }
+}
+
+void UserPalette::rollback()
+{
+ for (Colors::iterator i = mColors.begin(), iEnd = mColors.end();
+ i != iEnd; ++i)
+ {
+ if (i->grad != i->committedGrad)
+ {
+ setGradient(i->type, i->committedGrad);
+ }
+ setGradientDelay(i->type, i->committedDelay);
+ setColor(i->type, i->committedColor.r,
+ i->committedColor.g, i->committedColor.b);
+ if (i->grad == PULSE)
+ {
+ i->testColor.r = i->committedColor.r;
+ i->testColor.g = i->committedColor.g;
+ i->testColor.b = i->committedColor.b;
+ }
+ }
+}
+
+int UserPalette::getColorTypeAt(int i)
+{
+ if (i < 0 || i >= getNumberOfElements())
+ {
+ return BEING;
+ }
+
+ return mColors[i].type;
+}
+
+void UserPalette::addColor(int type, int rgb, Palette::GradientType grad,
+ const std::string &text, int delay)
+{
+ const std::string &configName = ColorTypeNames[type];
+ char buffer[20];
+ sprintf(buffer, "0x%06x", rgb);
+ const std::string rgbString = config.getValue(configName,
+ std::string(buffer));
+ unsigned int rgbValue = 0;
+ if (rgbString.length() == 8 && rgbString[0] == '0' && rgbString[1] == 'x')
+ rgbValue = atox(rgbString);
+ else
+ rgbValue = atoi(rgbString.c_str());
+ gcn::Color trueCol = rgbValue;
+ grad = (GradientType) config.getValue(configName + "Gradient", grad);
+ delay = (int) config.getValue(configName + "Delay", delay);
+ mColors[type].set(type, trueCol, grad, delay);
+ mColors[type].text = text;
+
+ if (grad != STATIC)
+ mGradVector.push_back(&mColors[type]);
+}
diff --git a/src/resources/userpalette.h b/src/resources/userpalette.h
new file mode 100644
index 00000000..be02db10
--- /dev/null
+++ b/src/resources/userpalette.h
@@ -0,0 +1,210 @@
+/*
+ * Configurable text colors
+ * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net>
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef USER_PALETTE_H
+#define USER_PALETTE_H
+
+#include "gui/palette.h"
+
+#include <guichan/listmodel.hpp>
+
+/**
+ * Class controlling the game's color palette.
+ */
+class UserPalette : public Palette, public gcn::ListModel
+{
+ public:
+ /** List of all colors that are configurable. */
+ enum {
+ BEING,
+ PC,
+ SELF,
+ GM,
+ NPC,
+ MONSTER,
+ PARTY,
+ GUILD,
+ PARTICLE,
+ EXP_INFO,
+ PICKUP_INFO,
+ HIT_PLAYER_MONSTER,
+ HIT_MONSTER_PLAYER,
+ HIT_CRITICAL,
+ HIT_LOCAL_PLAYER_MONSTER,
+ HIT_LOCAL_PLAYER_CRITICAL,
+ HIT_LOCAL_PLAYER_MISS,
+ MISS,
+ USER_COLOR_LAST
+ };
+
+ /**
+ * Constructor
+ */
+ UserPalette();
+
+ /**
+ * Destructor
+ */
+ ~UserPalette();
+
+ /**
+ * Gets the committed color associated with the specified type.
+ *
+ * @param type the color type requested
+ *
+ * @return the requested committed color
+ */
+ inline const gcn::Color &getCommittedColor(int type)
+ {
+ return mColors[type].committedColor;
+ }
+
+ /**
+ * Gets the test color associated with the specified type.
+ *
+ * @param type the color type requested
+ *
+ * @return the requested test color
+ */
+ inline const gcn::Color &getTestColor(int type)
+ {
+ return mColors[type].testColor;
+ }
+
+ /**
+ * Sets the test color associated with the specified type.
+ *
+ * @param type the color type requested
+ * @param color the color that should be tested
+ */
+ inline void setTestColor(int type, gcn::Color color)
+ {
+ mColors[type].testColor = color;
+ }
+
+ /**
+ * Sets the color for the specified type.
+ *
+ * @param type color to be set
+ * @param r red component
+ * @param g green component
+ * @param b blue component
+ */
+ void setColor(int type, int r, int g, int b);
+
+ /**
+ * Sets the gradient type for the specified color.
+ *
+ * @param grad gradient type to set
+ */
+ void setGradient(int type, Palette::GradientType grad);
+
+ /**
+ * Sets the gradient delay for the specified color.
+ *
+ * @param grad gradient type to set
+ */
+ void setGradientDelay(int type, int delay)
+ { mColors[type].delay = delay; }
+
+ /**
+ * Returns the number of colors known.
+ *
+ * @return the number of colors known
+ */
+ inline int getNumberOfElements() { return mColors.size(); }
+
+ /**
+ * Returns the name of the ith color.
+ *
+ * @param i index of color interested in
+ *
+ * @return the name of the color
+ */
+ std::string getElementAt(int i);
+
+ /**
+ * Commit the colors
+ */
+ inline void commit()
+ {
+ commit(false);
+ }
+
+ /**
+ * Rollback the colors
+ */
+ void rollback();
+
+ /**
+ * Gets the ColorType used by the color for the element at index i in
+ * the current color model.
+ *
+ * @param i the index of the color
+ *
+ * @return the color type of the color with the given index
+ */
+ int getColorTypeAt(int i);
+
+ private:
+ /**
+ * Define a color replacement.
+ *
+ * @param i the index of the color to replace
+ * @param r red component
+ * @param g green component
+ * @param b blue component
+ */
+ void setColorAt(int i, int r, int g, int b);
+
+ /**
+ * Commit the colors. Commit the non-static color values, if
+ * commitNonStatic is true. Only needed in the constructor.
+ */
+ void commit(bool commitNonStatic);
+
+ /**
+ * Prefixes the given string with "Color", lowercases all letters but
+ * the first and all following a '_'. All '_'s will be removed.
+ *
+ * E.g.: HIT_PLAYER_MONSTER -> HitPlayerMonster
+ *
+ * @param typeName string to transform
+ *
+ * @return the transformed string
+ */
+ static std::string getConfigName(const std::string &typeName);
+
+ /**
+ * Initialise color
+ *
+ * @param c character that needs initialising
+ * @param rgb default color if not found in config
+ * @param text identifier of color
+ */
+ void addColor(int type, int rgb, GradientType grad,
+ const std::string &text, int delay = GRADIENT_DELAY);
+};
+
+extern UserPalette *userPalette;
+
+#endif // USER_PALETTE_H
diff --git a/src/resources/wallpaper.cpp b/src/resources/wallpaper.cpp
index c8857745..0fd1c291 100644
--- a/src/resources/wallpaper.cpp
+++ b/src/resources/wallpaper.cpp
@@ -53,21 +53,21 @@ static void initDefaultWallpaperPaths()
ResourceManager *resman = ResourceManager::getInstance();
// Init the path
- wallpaperPath = branding.getValue("wallpapersPath", "");
+ wallpaperPath = branding.getStringValue("wallpapersPath");
if (wallpaperPath.empty() || !resman->isDirectory(wallpaperPath))
- wallpaperPath = paths.getValue("wallpapers", "");
+ wallpaperPath = paths.getStringValue("wallpapers");
if (wallpaperPath.empty() || !resman->isDirectory(wallpaperPath))
wallpaperPath = "graphics/images/";
// Init the default file
- wallpaperFile = branding.getValue("wallpaperFile", "");
+ wallpaperFile = branding.getStringValue("wallpaperFile");
if (!wallpaperFile.empty() && !resman->isDirectory(wallpaperFile))
return;
else
- wallpaperFile = paths.getValue("wallpaperFile", "");
+ wallpaperFile = paths.getStringValue("wallpaperFile");
if (wallpaperFile.empty() || resman->isDirectory(wallpaperFile))
wallpaperFile = "login_wallpaper.png";
@@ -80,7 +80,7 @@ bool wallpaperCompare(WallpaperData a, WallpaperData b)
return (aa > ab || (aa == ab && a.width > b.width));
}
-
+#include <iostream>
void Wallpaper::loadWallpapers()
{
wallpaperData.clear();