/** * Load a specific item sets file * * @param filename The (full) path and name of the file to load */ void ItemManager::loadSets(const std::string& filename) { FileParser infile; // @CLASS ItemManager: Sets|Definition of a item sets, items/sets.txt... if (!infile.open(filename, FileParser::MOD_FILE, FileParser::ERROR_NORMAL)) return; bool clear_bonus = true; int id = 0; bool id_line; while (infile.next()) { if (infile.key == "id") { // @ATTR id|int|A uniq id for the item set. id_line = true; id = Parse::toInt(infile.val); if (id > 0) { size_t new_size = id+1; if (item_sets.size() <= new_size) item_sets.resize(new_size); } clear_bonus = true; } else id_line = false; if (id < 1) { if (id_line) infile.error("ItemManager: Item set index out of bounds 1-%d, skipping set.", INT_MAX); continue; } if (id_line) continue; assert(item_sets.size() > std::size_t(id)); if (infile.key == "name") { // @ATTR name|string|Name of the item set. item_sets[id].name = msg->get(infile.val); } else if (infile.key == "items") { // @ATTR items|list(item_id)|List of item id's that is part of the set. item_sets[id].items.clear(); std::string item_id = Parse::popFirstString(infile.val); while (item_id != "") { int temp_id = Parse::toInt(item_id); if (temp_id > 0 && temp_id < static_cast<int>(items.size())) { items[temp_id].set = id; item_sets[id].items.push_back(temp_id); } else { const int maxsize = static_cast<int>(items.size()-1); infile.error("ItemManager: Item index out of bounds 1-%d, skipping item.", maxsize); } item_id = Parse::popFirstString(infile.val); } } else if (infile.key == "color") { // @ATTR color|color|A specific of color for the set. item_sets[id].color = Parse::toRGB(infile.val); } else if (infile.key == "bonus") { // @ATTR bonus|repeatable(int, string, int) : Required set item count, Stat name, Value|Bonus to append to items in the set. if (clear_bonus) { item_sets[id].bonus.clear(); clear_bonus = false; } SetBonusData bonus; bonus.requirement = Parse::popFirstInt(infile.val); parseBonus(bonus, infile); item_sets[id].bonus.push_back(bonus); } else if (infile.key == "bonus_power_level") { // @ATTR bonus_power_level|repeatable(int, power_id, int) : Required set item count, Base power, Bonus levels|Grants bonus levels to a given base power. SetBonusData bonus; bonus.requirement = Parse::popFirstInt(infile.val); bonus.power_id = Parse::popFirstInt(infile.val); bonus.value = Parse::popFirstInt(infile.val); item_sets[id].bonus.push_back(bonus); } else { infile.error("ItemManager: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); }
/** * Load a specific items file * * @param filename The (full) path and name of the file to load */ void ItemManager::loadItems(const std::string& filename, bool locateFileName) { FileParser infile; // @CLASS ItemManager: Items|Description about the class and it usage, items/items.txt... if (!infile.open(filename, locateFileName)) return; // used to clear vectors when overriding items bool clear_req_stat = true; bool clear_bonus = true; bool clear_loot_anim = true; bool clear_replace_power = true; int id = 0; bool id_line = false; while (infile.next()) { if (infile.key == "id") { // @ATTR id|integer|An uniq id of the item used as reference from other classes. id_line = true; id = toInt(infile.val); addUnknownItem(id); clear_req_stat = true; clear_bonus = true; clear_loot_anim = true; clear_replace_power = true; } else id_line = false; if (id < 1) { if (id_line) infile.error("ItemManager: Item index out of bounds 1-%d, skipping item.", INT_MAX); continue; } if (id_line) continue; assert(items.size() > std::size_t(id)); if (infile.key == "name") { // @ATTR name|string|Item name displayed on long and short tooltips. items[id].name = msg->get(infile.val); items[id].has_name = true; } else if (infile.key == "flavor") // @ATTR flavor|string|A description of the item. items[id].flavor = msg->get(infile.val); else if (infile.key == "level") // @ATTR level|integer|The item's level. Has no gameplay impact. (Deprecated?) items[id].level = toInt(infile.val); else if (infile.key == "icon") { // @ATTR icon|integer|An id for the icon to display for this item. items[id].icon = toInt(infile.nextValue()); } else if (infile.key == "book") { // @ATTR book|string|A book file to open when this item is activated. items[id].book = infile.val; } else if (infile.key == "quality") { // @ATTR quality|string|Item quality matching an id in items/qualities.txt items[id].quality = infile.val; } else if (infile.key == "item_type") { // @ATTR item_type|string|Equipment slot [artifact, head, chest, hands, legs, feets, main, off, ring] or base item type [gem, consumable] items[id].type = infile.val; } else if (infile.key == "equip_flags") { // @ATTR equip_flags|flag (string), ...|A comma separated list of flags to set when this item is equipped. See engine/equip_flags.txt. items[id].equip_flags.clear(); std::string flag = popFirstString(infile.val); while (flag != "") { items[id].equip_flags.push_back(flag); flag = popFirstString(infile.val); } } else if (infile.key == "dmg_melee") { // @ATTR dmg_melee|[min (integer), max (integer)]|Defines the item melee damage, if only min is specified the melee damage is fixed. items[id].dmg_melee_min = toInt(infile.nextValue()); if (infile.val.length() > 0) items[id].dmg_melee_max = toInt(infile.nextValue()); else items[id].dmg_melee_max = items[id].dmg_melee_min; } else if (infile.key == "dmg_ranged") { // @ATTR dmg_ranged|[min (integer), max (integer)]|Defines the item ranged damage, if only min is specified the ranged damage is fixed. items[id].dmg_ranged_min = toInt(infile.nextValue()); if (infile.val.length() > 0) items[id].dmg_ranged_max = toInt(infile.nextValue()); else items[id].dmg_ranged_max = items[id].dmg_ranged_min; } else if (infile.key == "dmg_ment") { // @ATTR dmg_ment|[min (integer), max (integer)]|Defines the item mental damage, if only min is specified the ranged damage is fixed. items[id].dmg_ment_min = toInt(infile.nextValue()); if (infile.val.length() > 0) items[id].dmg_ment_max = toInt(infile.nextValue()); else items[id].dmg_ment_max = items[id].dmg_ment_min; } else if (infile.key == "abs") { // @ATTR abs|[min (integer), max (integer)]|Defines the item absorb value, if only min is specified the absorb value is fixed. items[id].abs_min = toInt(infile.nextValue()); if (infile.val.length() > 0) items[id].abs_max = toInt(infile.nextValue()); else items[id].abs_max = items[id].abs_min; } else if (infile.key == "requires_level") { // @ATTR requires_level|integer|The hero's level must match or exceed this value in order to equip this item. items[id].requires_level = toInt(infile.val); } else if (infile.key == "requires_stat") { // @ATTR requires_stat|[ [physical:mental:offense:defense], amount (integer) ]|Make item require specific stat level ex. requires_stat=physical,6 will require hero to have level 6 in physical stats if (clear_req_stat) { items[id].req_stat.clear(); items[id].req_val.clear(); clear_req_stat = false; } std::string s = infile.nextValue(); if (s == "physical") items[id].req_stat.push_back(REQUIRES_PHYS); else if (s == "mental") items[id].req_stat.push_back(REQUIRES_MENT); else if (s == "offense") items[id].req_stat.push_back(REQUIRES_OFF); else if (s == "defense") items[id].req_stat.push_back(REQUIRES_DEF); else infile.error("%s unrecognized at; requires_stat must be one of [physical:mental:offense:defense]", s.c_str()); items[id].req_val.push_back(toInt(infile.nextValue())); } else if (infile.key == "requires_class") { // @ATTR requires_class|string|The hero's base class (engine/classes.txt) must match for this item to be equipped. items[id].requires_class = infile.val; } else if (infile.key == "bonus") { // @ATTR bonus|[stat_name (string), amount (integer)]|Adds a bonus to the item power_tag being a uniq tag of a power definition, e.: bonus=HP regen, 50 if (clear_bonus) { items[id].bonus.clear(); clear_bonus = false; } BonusData bdata; parseBonus(bdata, infile); items[id].bonus.push_back(bdata); } else if (infile.key == "soundfx") { // @ATTR soundfx|string|Sound effect filename to play for the specific item. items[id].sfx = infile.val; items[id].sfx_id = snd->load(items[id].sfx, "ItemManager"); } else if (infile.key == "gfx") // @ATTR gfx|string|Filename of an animation set to display when the item is equipped. items[id].gfx = infile.val; else if (infile.key == "loot_animation") { // @ATTR loot_animation|filename (string), min quantity (int), max quantity (int)|Specifies the loot animation file for the item. The max quantity, or both quantity values, may be omitted. if (clear_loot_anim) { items[id].loot_animation.clear(); clear_loot_anim = false; } LootAnimation la; la.name = popFirstString(infile.val); la.low = popFirstInt(infile.val); la.high = popFirstInt(infile.val); items[id].loot_animation.push_back(la); } else if (infile.key == "power") { // @ATTR power|power_id|Adds a specific power to the item which makes it usable as a power and can be placed in action bar. if (toInt(infile.val) > 0) items[id].power = toInt(infile.val); else infile.error("ItemManager: Power index out of bounds 1-%d, skipping power.", INT_MAX); } else if (infile.key == "replace_power") { // @ATTR replace_power|old (integer), new (integer)|Replaces the old power id with the new power id in the action bar when equipped. if (clear_replace_power) { items[id].replace_power.clear(); clear_replace_power = false; } Point power_ids = toPoint(infile.val); items[id].replace_power.push_back(power_ids); } else if (infile.key == "power_desc") // @ATTR power_desc|string|A string describing the additional power. items[id].power_desc = msg->get(infile.val); else if (infile.key == "price") // @ATTR price|integer|The amount of currency the item costs, if set to 0 the item cannot be sold. items[id].price = toInt(infile.val); else if (infile.key == "price_per_level") // @ATTR price_per_level|integer|Additional price for each player level above 1 items[id].price_per_level = toInt(infile.val); else if (infile.key == "price_sell") // @ATTR price_sell|integer|The amount of currency the item is sold for, if set to 0 the sell prices is prices*vendor_ratio. items[id].price_sell = toInt(infile.val); else if (infile.key == "max_quantity") // @ATTR max_quantity|integer|Max item count per stack. items[id].max_quantity = toInt(infile.val); else if (infile.key == "pickup_status") // @ATTR pickup_status|string|Set a campaign status when item is picked up, this is used for quest items. items[id].pickup_status = infile.val; else if (infile.key == "stepfx") // @ATTR stepfx|string|Sound effect when walking, this applies only to armors. items[id].stepfx = infile.val; else if (infile.key == "disable_slots") { // @ATTR disable_slots|type (string), ...|A comma separated list of equip slot types to disable when this item is equipped. items[id].disable_slots.clear(); std::string slot_type = popFirstString(infile.val); while (slot_type != "") { items[id].disable_slots.push_back(slot_type); slot_type = popFirstString(infile.val); } } else if (infile.key == "quest_item") { // @ATTR quest_item|bool|If true, this item is a quest item and can not be dropped, stashed, or sold. items[id].quest_item = toBool(infile.val); } else { infile.error("ItemManager: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); }
/** * Load a specific items file * * @param filename The (full) path and name of the file to load */ void ItemManager::loadItems(const std::string& filename) { FileParser infile; // @CLASS ItemManager: Items|Description about the class and it usage, items/items.txt... if (!infile.open(filename, FileParser::MOD_FILE, FileParser::ERROR_NORMAL)) return; // used to clear vectors when overriding items bool clear_req_stat = true; bool clear_bonus = true; bool clear_loot_anim = true; bool clear_replace_power = true; int id = 0; bool id_line = false; while (infile.next()) { if (infile.key == "id") { // @ATTR id|item_id|An uniq id of the item used as reference from other classes. id_line = true; id = Parse::toInt(infile.val); addUnknownItem(id); clear_req_stat = true; clear_bonus = true; clear_loot_anim = true; clear_replace_power = true; } else id_line = false; if (id < 1) { if (id_line) infile.error("ItemManager: Item index out of bounds 1-%d, skipping item.", INT_MAX); continue; } if (id_line) continue; assert(items.size() > std::size_t(id)); if (infile.key == "name") { // @ATTR name|string|Item name displayed on long and short tooltips. items[id].name = msg->get(infile.val); items[id].has_name = true; } else if (infile.key == "flavor") // @ATTR flavor|string|A description of the item. items[id].flavor = msg->get(infile.val); else if (infile.key == "level") // @ATTR level|int|The item's level. Has no gameplay impact. (Deprecated?) items[id].level = Parse::toInt(infile.val); else if (infile.key == "icon") { // @ATTR icon|icon_id|An id for the icon to display for this item. items[id].icon = Parse::toInt(infile.val); } else if (infile.key == "book") { // @ATTR book|filename|A book file to open when this item is activated. items[id].book = infile.val; } else if (infile.key == "quality") { // @ATTR quality|predefined_string|Item quality matching an id in items/qualities.txt items[id].quality = infile.val; } else if (infile.key == "item_type") { // @ATTR item_type|predefined_string|Equipment slot matching an id in items/types.txt items[id].type = infile.val; } else if (infile.key == "equip_flags") { // @ATTR equip_flags|list(predefined_string)|A comma separated list of flags to set when this item is equipped. See engine/equip_flags.txt. items[id].equip_flags.clear(); std::string flag = Parse::popFirstString(infile.val); while (flag != "") { items[id].equip_flags.push_back(flag); flag = Parse::popFirstString(infile.val); } } else if (infile.key == "dmg") { // @ATTR dmg|predefined_string, int, int : Damage type, Min, Max|Defines the item's base damage type and range. Max may be ommitted and will default to Min. std::string dmg_type_str = Parse::popFirstString(infile.val); size_t dmg_type = eset->damage_types.list.size(); for (size_t i = 0; i < eset->damage_types.list.size(); ++i) { if (dmg_type_str == eset->damage_types.list[i].id) { dmg_type = i; break; } } if (dmg_type == eset->damage_types.list.size()) { infile.error("ItemManager: '%s' is not a known damage type id.", dmg_type_str.c_str()); } else { items[id].dmg_min[dmg_type] = Parse::popFirstInt(infile.val); if (infile.val.length() > 0) items[id].dmg_max[dmg_type] = Parse::popFirstInt(infile.val); else items[id].dmg_max[dmg_type] = items[id].dmg_min[dmg_type]; } } else if (infile.key == "abs") { // @ATTR abs|int, int : Min, Max|Defines the item absorb value, if only min is specified the absorb value is fixed. items[id].abs_min = Parse::popFirstInt(infile.val); if (infile.val.length() > 0) items[id].abs_max = Parse::popFirstInt(infile.val); else items[id].abs_max = items[id].abs_min; } else if (infile.key == "requires_level") { // @ATTR requires_level|int|The hero's level must match or exceed this value in order to equip this item. items[id].requires_level = Parse::toInt(infile.val); } else if (infile.key == "requires_stat") { // @ATTR requires_stat|repeatable(predefined_string, int) : Primary stat name, Value|Make item require specific stat level ex. requires_stat=physical,6 will require hero to have level 6 in physical stats if (clear_req_stat) { items[id].req_stat.clear(); items[id].req_val.clear(); clear_req_stat = false; } std::string s = Parse::popFirstString(infile.val); size_t req_stat_index = eset->primary_stats.getIndexByID(s); if (req_stat_index != eset->primary_stats.list.size()) items[id].req_stat.push_back(req_stat_index); else infile.error("ItemManager: '%s' is not a valid primary stat.", s.c_str()); items[id].req_val.push_back(Parse::popFirstInt(infile.val)); } else if (infile.key == "requires_class") { // @ATTR requires_class|predefined_string|The hero's base class (engine/classes.txt) must match for this item to be equipped. items[id].requires_class = infile.val; } else if (infile.key == "bonus") { // @ATTR bonus|repeatable(predefined_string, int) : Stat name, Value|Adds a bonus to the item by stat name, example: bonus=hp, 50 if (clear_bonus) { items[id].bonus.clear(); clear_bonus = false; } BonusData bdata; parseBonus(bdata, infile); items[id].bonus.push_back(bdata); } else if (infile.key == "bonus_power_level") { // @ATTR bonus_power_level|repeatable(power_id, int) : Base power, Bonus levels|Grants bonus levels to a given base power. BonusData bdata; bdata.power_id = Parse::popFirstInt(infile.val); bdata.value = Parse::popFirstInt(infile.val); items[id].bonus.push_back(bdata); } else if (infile.key == "soundfx") { // @ATTR soundfx|filename|Sound effect filename to play for the specific item. items[id].sfx = infile.val; items[id].sfx_id = snd->load(items[id].sfx, "ItemManager"); } else if (infile.key == "gfx") // @ATTR gfx|filename|Filename of an animation set to display when the item is equipped. items[id].gfx = infile.val; else if (infile.key == "loot_animation") { // @ATTR loot_animation|repeatable(filename, int, int) : Loot image, Min quantity, Max quantity|Specifies the loot animation file for the item. The max quantity, or both quantity values, may be omitted. if (clear_loot_anim) { items[id].loot_animation.clear(); clear_loot_anim = false; } LootAnimation la; la.name = Parse::popFirstString(infile.val); la.low = Parse::popFirstInt(infile.val); la.high = Parse::popFirstInt(infile.val); items[id].loot_animation.push_back(la); } else if (infile.key == "power") { // @ATTR power|power_id|Adds a specific power to the item which makes it usable as a power and can be placed in action bar. if (Parse::toInt(infile.val) > 0) items[id].power = Parse::toInt(infile.val); else infile.error("ItemManager: Power index out of bounds 1-%d, skipping power.", INT_MAX); } else if (infile.key == "replace_power") { // @ATTR replace_power|repeatable(int, int) : Old power, New power|Replaces the old power id with the new power id in the action bar when equipped. if (clear_replace_power) { items[id].replace_power.clear(); clear_replace_power = false; } Point power_ids = Parse::toPoint(infile.val); items[id].replace_power.push_back(power_ids); } else if (infile.key == "power_desc") // @ATTR power_desc|string|A string describing the additional power. items[id].power_desc = msg->get(infile.val); else if (infile.key == "price") // @ATTR price|int|The amount of currency the item costs, if set to 0 the item cannot be sold. items[id].price = Parse::toInt(infile.val); else if (infile.key == "price_per_level") // @ATTR price_per_level|int|Additional price for each player level above 1 items[id].price_per_level = Parse::toInt(infile.val); else if (infile.key == "price_sell") // @ATTR price_sell|int|The amount of currency the item is sold for, if set to 0 the sell prices is prices*vendor_ratio. items[id].price_sell = Parse::toInt(infile.val); else if (infile.key == "max_quantity") // @ATTR max_quantity|int|Max item count per stack. items[id].max_quantity = Parse::toInt(infile.val); else if (infile.key == "pickup_status") // @ATTR pickup_status|string|Set a campaign status when item is picked up, this is used for quest items. items[id].pickup_status = infile.val; else if (infile.key == "stepfx") // @ATTR stepfx|predefined_string|Sound effect when walking, this applies only to armors. items[id].stepfx = infile.val; else if (infile.key == "disable_slots") { // @ATTR disable_slots|list(predefined_string)|A comma separated list of equip slot types to disable when this item is equipped. items[id].disable_slots.clear(); std::string slot_type = Parse::popFirstString(infile.val); while (slot_type != "") { items[id].disable_slots.push_back(slot_type); slot_type = Parse::popFirstString(infile.val); } } else if (infile.key == "quest_item") { // @ATTR quest_item|bool|If true, this item is a quest item and can not be dropped, stashed, or sold. items[id].quest_item = Parse::toBool(infile.val); } else { infile.error("ItemManager: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); }
/** * Load a specific item sets file * * @param filename The (full) path and name of the file to load */ void ItemManager::loadSets(const std::string& filename, bool locateFileName) { FileParser infile; // @CLASS ItemManager: Sets|Definition of a item sets, items/sets.txt... if (!infile.open(filename, locateFileName)) return; bool clear_bonus = true; int id = 0; bool id_line; while (infile.next()) { if (infile.key == "id") { // @ATTR id|integer|A uniq id for the item set. id_line = true; id = toInt(infile.val); if (id > 0) { size_t new_size = id+1; if (item_sets.size() <= new_size) item_sets.resize(new_size); } clear_bonus = true; } else id_line = false; if (id < 1) { if (id_line) infile.error("ItemManager: Item set index out of bounds 1-%d, skipping set.", INT_MAX); continue; } if (id_line) continue; assert(item_sets.size() > std::size_t(id)); if (infile.key == "name") { // @ATTR name|string|Name of the item set. item_sets[id].name = msg->get(infile.val); } else if (infile.key == "items") { // @ATTR items|[item_id,...]|List of item id's that is part of the set. item_sets[id].items.clear(); std::string item_id = infile.nextValue(); while (item_id != "") { int temp_id = toInt(item_id); if (temp_id > 0 && temp_id < static_cast<int>(items.size())) { items[temp_id].set = id; item_sets[id].items.push_back(temp_id); } else { const int maxsize = static_cast<int>(items.size()-1); infile.error("ItemManager: Item index out of bounds 1-%d, skipping item.", maxsize); } item_id = infile.nextValue(); } } else if (infile.key == "color") { // @ATTR color|color|A specific of color for the set. item_sets[id].color = toRGB(infile.val); } else if (infile.key == "bonus") { // @ATTR bonus|[requirements (integer), bonus stat (string), bonus (integer)]|Bonus to append to items in the set. if (clear_bonus) { item_sets[id].bonus.clear(); clear_bonus = false; } Set_bonus bonus; bonus.requirement = toInt(infile.nextValue()); parseBonus(bonus, infile); item_sets[id].bonus.push_back(bonus); } else { infile.error("ItemManager: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); }