00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "resources/itemdb.h"
00023
00024 #include "resources/iteminfo.h"
00025 #include "resources/resourcemanager.h"
00026
00027 #include "log.h"
00028
00029 #include "utils/dtor.h"
00030 #include "utils/gettext.h"
00031 #include "utils/strprintf.h"
00032 #include "utils/stringutils.h"
00033 #include "utils/xml.h"
00034
00035 #include <libxml/tree.h>
00036
00037 #include <cassert>
00038
00039 namespace
00040 {
00041 ItemDB::ItemInfos mItemInfos;
00042 ItemDB::NamedItemInfos mNamedItemInfos;
00043 ItemInfo *mUnknown;
00044 bool mLoaded = false;
00045 }
00046
00047
00048 static void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node);
00049 static void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node);
00050
00051 static char const *const fields[][2] =
00052 {
00053 { "attack", N_("Attack %+d") },
00054 { "defense", N_("Defense %+d") },
00055 { "hp", N_("HP %+d") },
00056 { "mp", N_("MP %+d") }
00057 };
00058
00059 static std::list<ItemDB::Stat*> extraStats;
00060
00061 void ItemDB::setStatsList(std::list<ItemDB::Stat*> stats)
00062 {
00063 extraStats = stats;
00064 }
00065
00066 static ItemType itemTypeFromString(const std::string &name, int id = 0)
00067 {
00068 if (name=="generic") return ITEM_UNUSABLE;
00069 else if (name=="usable") return ITEM_USABLE;
00070 else if (name=="equip-1hand") return ITEM_EQUIPMENT_ONE_HAND_WEAPON;
00071 else if (name=="equip-2hand") return ITEM_EQUIPMENT_TWO_HANDS_WEAPON;
00072 else if (name=="equip-torso") return ITEM_EQUIPMENT_TORSO;
00073 else if (name=="equip-arms") return ITEM_EQUIPMENT_ARMS;
00074 else if (name=="equip-head") return ITEM_EQUIPMENT_HEAD;
00075 else if (name=="equip-legs") return ITEM_EQUIPMENT_LEGS;
00076 else if (name=="equip-shield") return ITEM_EQUIPMENT_SHIELD;
00077 else if (name=="equip-ring") return ITEM_EQUIPMENT_RING;
00078 else if (name=="equip-necklace") return ITEM_EQUIPMENT_NECKLACE;
00079 else if (name=="equip-feet") return ITEM_EQUIPMENT_FEET;
00080 else if (name=="equip-ammo") return ITEM_EQUIPMENT_AMMO;
00081 else return ITEM_UNUSABLE;
00082 }
00083
00084 static WeaponType weaponTypeFromString(const std::string &name, int id = 0)
00085 {
00086 if (name=="knife") return WPNTYPE_KNIFE;
00087 else if (name=="sword") return WPNTYPE_SWORD;
00088 else if (name=="polearm") return WPNTYPE_POLEARM;
00089 else if (name=="staff") return WPNTYPE_STAFF;
00090 else if (name=="whip") return WPNTYPE_WHIP;
00091 else if (name=="bow") return WPNTYPE_BOW;
00092 else if (name=="shooting") return WPNTYPE_SHOOTING;
00093 else if (name=="mace") return WPNTYPE_MACE;
00094 else if (name=="axe") return WPNTYPE_AXE;
00095 else if (name=="thrown") return WPNTYPE_THROWN;
00096
00097 else return WPNTYPE_NONE;
00098 }
00099
00100 void ItemDB::load()
00101 {
00102 if (mLoaded)
00103 return;
00104
00105 logger->log("Initializing item database...");
00106
00107 mUnknown = new ItemInfo;
00108 mUnknown->setName(_("Unknown item"));
00109 mUnknown->setImageName("");
00110 mUnknown->setSprite("error.xml", GENDER_MALE);
00111 mUnknown->setSprite("error.xml", GENDER_FEMALE);
00112
00113 XML::Document doc(_("items.xml"));
00114 xmlNodePtr rootNode = doc.rootNode();
00115
00116 if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items"))
00117 {
00118 logger->error("ItemDB: Error while loading items.xml!");
00119 }
00120
00121 for_each_xml_child_node(node, rootNode)
00122 {
00123 if (!xmlStrEqual(node->name, BAD_CAST "item"))
00124 continue;
00125
00126 int id = XML::getProperty(node, "id", 0);
00127
00128 if (id == 0)
00129 {
00130 logger->log("ItemDB: Invalid or missing item ID in items.xml!");
00131 continue;
00132 }
00133 else if (mItemInfos.find(id) != mItemInfos.end())
00134 {
00135 logger->log("ItemDB: Redefinition of item ID %d", id);
00136 }
00137
00138 std::string typeStr = XML::getProperty(node, "type", "other");
00139 int weight = XML::getProperty(node, "weight", 0);
00140 int view = XML::getProperty(node, "view", 0);
00141
00142 std::string name = XML::getProperty(node, "name", "");
00143 std::string image = XML::getProperty(node, "image", "");
00144 std::string description = XML::getProperty(node, "description", "");
00145 int weaponType = weaponTypeFromString(XML::getProperty(node, "weapon-type", ""));
00146 int attackRange = XML::getProperty(node, "attack-range", 0);
00147
00148 ItemInfo *itemInfo = new ItemInfo;
00149 itemInfo->setId(id);
00150 itemInfo->setImageName(image);
00151 itemInfo->setName(name.empty() ? _("Unnamed") : name);
00152 itemInfo->setDescription(description);
00153 itemInfo->setType(itemTypeFromString(typeStr));
00154 itemInfo->setView(view);
00155 itemInfo->setWeight(weight);
00156 itemInfo->setWeaponType(weaponType);
00157 itemInfo->setAttackRange(attackRange);
00158
00159 std::string effect;
00160 for (int i = 0; i < int(sizeof(fields) / sizeof(fields[0])); ++i)
00161 {
00162 int value = XML::getProperty(node, fields[i][0], 0);
00163 if (!value) continue;
00164 if (!effect.empty()) effect += " / ";
00165 effect += strprintf(gettext(fields[i][1]), value);
00166 }
00167 for (std::list<Stat*>::iterator it = extraStats.begin();
00168 it != extraStats.end(); it++)
00169 {
00170 int value = XML::getProperty(node, (*it)->tag.c_str(), 0);
00171 if (!value) continue;
00172 if (!effect.empty()) effect += " / ";
00173 effect += strprintf((*it)->format.c_str(), value);
00174 }
00175 std::string temp = XML::getProperty(node, "effect", "");
00176 if (!effect.empty() && !temp.empty())
00177 effect += " / ";
00178 effect += temp;
00179 itemInfo->setEffect(effect);
00180
00181 for_each_xml_child_node(itemChild, node)
00182 {
00183 if (xmlStrEqual(itemChild->name, BAD_CAST "sprite"))
00184 {
00185 std::string attackParticle = XML::getProperty(
00186 itemChild, "particle-effect", "");
00187 itemInfo->setParticleEffect(attackParticle);
00188
00189 loadSpriteRef(itemInfo, itemChild);
00190 }
00191 else if (xmlStrEqual(itemChild->name, BAD_CAST "sound"))
00192 {
00193 loadSoundRef(itemInfo, itemChild);
00194 }
00195 }
00196
00197 mItemInfos[id] = itemInfo;
00198 if (!name.empty())
00199 {
00200 std::string temp = name;
00201 toLower(trim(temp));
00202
00203 NamedItemInfos::const_iterator itr = mNamedItemInfos.find(temp);
00204 if (itr == mNamedItemInfos.end())
00205 {
00206 mNamedItemInfos[temp] = itemInfo;
00207 }
00208 else
00209 {
00210 logger->log("ItemDB: Duplicate name of item found item %d", id);
00211 }
00212 }
00213
00214 #define CHECK_PARAM(param, error_value) \
00215 if (param == error_value) \
00216 logger->log("ItemDB: Missing " #param " attribute for item %i!",id)
00217
00218 if (id >= 0)
00219 {
00220 CHECK_PARAM(name, "");
00221 CHECK_PARAM(description, "");
00222 }
00223 CHECK_PARAM(image, "");
00224
00225
00226
00227
00228
00229 #undef CHECK_PARAM
00230 }
00231
00232 mLoaded = true;
00233 }
00234
00235 void ItemDB::unload()
00236 {
00237 logger->log("Unloading item database...");
00238
00239 delete mUnknown;
00240 mUnknown = NULL;
00241
00242 delete_all(mItemInfos);
00243 mItemInfos.clear();
00244 mLoaded = false;
00245 }
00246
00247 const ItemInfo& ItemDB::get(int id)
00248 {
00249 assert(mLoaded);
00250
00251 ItemInfos::const_iterator i = mItemInfos.find(id);
00252
00253 if (i == mItemInfos.end())
00254 {
00255 logger->log("ItemDB: Error, unknown item ID# %d", id);
00256 return *mUnknown;
00257 }
00258 else
00259 {
00260 return *(i->second);
00261 }
00262 }
00263
00264 const ItemInfo& ItemDB::get(const std::string &name)
00265 {
00266 assert(mLoaded && !name.empty());
00267
00268 NamedItemInfos::const_iterator i = mNamedItemInfos.find(name);
00269
00270 if (i == mNamedItemInfos.end())
00271 {
00272 logger->log("ItemDB: Error, unknown item name %s", name.c_str());
00273 return *mUnknown;
00274 }
00275 else
00276 {
00277 return *(i->second);
00278 }
00279 }
00280
00281 void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node)
00282 {
00283 std::string gender = XML::getProperty(node, "gender", "unisex");
00284 std::string filename = (const char*) node->xmlChildrenNode->content;
00285
00286 if (gender == "male" || gender == "unisex")
00287 {
00288 itemInfo->setSprite(filename, GENDER_MALE);
00289 }
00290 if (gender == "female" || gender == "unisex")
00291 {
00292 itemInfo->setSprite(filename, GENDER_FEMALE);
00293 }
00294 }
00295
00296 void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node)
00297 {
00298 std::string event = XML::getProperty(node, "event", "");
00299 std::string filename = (const char*) node->xmlChildrenNode->content;
00300
00301 if (event == "hit")
00302 {
00303 itemInfo->addSound(EQUIP_EVENT_HIT, filename);
00304 }
00305 else if (event == "strike")
00306 {
00307 itemInfo->addSound(EQUIP_EVENT_STRIKE, filename);
00308 }
00309 else
00310 {
00311 logger->log("ItemDB: Ignoring unknown sound event '%s'",
00312 event.c_str());
00313 }
00314 }