void Map::loadNPC(FileParser &infile) { std::string s; if (infile.key == "type") { // @ATTR npc.type|string|Type of NPC npcs.back().id = infile.val; } if (infile.key == "requires_status") { // @ATTR npc.requires_status|string|Status required for NPC load. There can be multiple states, separated by comma while ( (s = infile.nextValue()) != "") npcs.back().requires_status.push_back(s); } if (infile.key == "requires_not_status") { // @ATTR npc.requires_not|string|Status required to be missing for NPC load. There can be multiple states, separated by comma while ( (s = infile.nextValue()) != "") npcs.back().requires_not_status.push_back(s); } else if (infile.key == "location") { // @ATTR npc.location|[x(integer), y(integer)]|Location of NPC npcs.back().pos.x = toInt(infile.nextValue()) + 0.5f; npcs.back().pos.y = toInt(infile.nextValue()) + 0.5f; } }
void Map::loadNPC(FileParser &infile) { std::string s; if (infile.key == "type") { // @ATTR npc.type|string|Filename of an NPC definition. npcs.back().id = infile.val; } else if (infile.key == "requires_status") { // @ATTR npc.requires_status|string|Status required for NPC load. There can be multiple states, separated by comma while ( (s = infile.nextValue()) != "") npcs.back().requires_status.push_back(s); } else if (infile.key == "requires_not_status") { // @ATTR npc.requires_not_status|string|Status required to be missing for NPC load. There can be multiple states, separated by comma while ( (s = infile.nextValue()) != "") npcs.back().requires_not_status.push_back(s); } else if (infile.key == "location") { // @ATTR npc.location|[x(integer), y(integer)]|Location of NPC npcs.back().pos.x = static_cast<float>(toInt(infile.nextValue())) + 0.5f; npcs.back().pos.y = static_cast<float>(toInt(infile.nextValue())) + 0.5f; // make sure this NPC has a collision tile // otherwise, it becomes possible for the player to stand "inside" the npc, which will trigger their event infinitely if (collision_layer != -1) { unsigned tile_x = static_cast<unsigned>(npcs.back().pos.x); unsigned tile_y = static_cast<unsigned>(npcs.back().pos.y); if (tile_x < static_cast<unsigned>(w) && tile_y < static_cast<unsigned>(h)) { short unsigned int& tile = layers[collision_layer][tile_x][tile_y]; if (tile == BLOCKS_NONE) { logError("Map: NPC at (%d, %d) does not have a collision tile. Creating one now.", tile_x, tile_y); tile = BLOCKS_MOVEMENT_HIDDEN; } } } } else { infile.error("Map: '%s' is not a valid key.", infile.key.c_str()); } }
void Map::loadHeader(FileParser &infile) { if (infile.key == "title") { this->title = msg->get(infile.val); } else if (infile.key == "width") { this->w = toInt(infile.val); } else if (infile.key == "height") { this->h = toInt(infile.val); } else if (infile.key == "tileset") { this->tileset = infile.val; } else if (infile.key == "music") { music_filename = infile.val; } else if (infile.key == "location") { spawn.x = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2; spawn.y = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2; spawn_dir = toInt(infile.nextValue()); } }
void ItemManager::loadSets(const string& filename) { FileParser infile; if (!infile.open(filename)) { fprintf(stderr, "Unable to open %s!\n", filename.c_str()); return; } int id = 0; bool id_line; while (infile.next()) { if (infile.key == "id") { id_line = true; id = toInt(infile.val); if (id > 0 && id >= (int)item_sets.size()) { // *2 to amortize the resizing to O(log(n)). item_sets.resize((2*id) + 1); } } else id_line = false; if (id < 1) { if (id_line) fprintf(stderr, "Item set index out of bounds 1-%d, skipping\n", INT_MAX); continue; } if (id_line) continue; if (infile.key == "name") { item_sets[id].name = msg->get(infile.val); } else if (infile.key == "items") { string item_id = infile.nextValue(); while (item_id != "") { if (toInt(item_id) > 0) { items[toInt(item_id)].set = id; item_sets[id].items.push_back(toInt(item_id)); item_id = infile.nextValue(); } else fprintf(stderr, "Item index inside item set %s definition out of bounds 1-%d, skipping item\n", item_sets[id].name.c_str(), INT_MAX); } } else if (infile.key == "color") { item_sets[id].color.r = toInt(infile.nextValue()); item_sets[id].color.g = toInt(infile.nextValue()); item_sets[id].color.b = toInt(infile.nextValue()); } else if (infile.key == "bonus") { Set_bonus bonus; bonus.requirement = toInt(infile.nextValue()); bonus.bonus_stat = infile.nextValue(); bonus.bonus_val = toInt(infile.nextValue()); item_sets[id].bonus.push_back(bonus); } } infile.close(); }
void Map::loadEnemyGroup(FileParser &infile, Map_Group *group) { if (infile.key == "type") { group->category = infile.val; } else if (infile.key == "level") { group->levelmin = toInt(infile.nextValue()); group->levelmax = toInt(infile.nextValue()); } else if (infile.key == "location") { group->pos.x = toInt(infile.nextValue()); group->pos.y = toInt(infile.nextValue()); group->area.x = toInt(infile.nextValue()); group->area.y = toInt(infile.nextValue()); } else if (infile.key == "number") { group->numbermin = toInt(infile.nextValue()); group->numbermax = toInt(infile.nextValue()); } else if (infile.key == "chance") { float n = toInt(infile.nextValue()) / 100.0f; group->chance = std::min(1.0f, std::max(0.0f, n)); } }
bool GameStateConfig::getLanguagesList() { FileParser infile; if (infile.open("engine/languages.txt")) { unsigned int i=0; while (infile.next()) { language_ISO[i] = infile.key; language_full[i] = infile.nextValue(); i += 1; } infile.close(); } return true; }
bool GameStateConfig::getLanguagesList() { FileParser infile; if (infile.open(mods->locate("engine/languages.txt"))) { unsigned int i=0; while (infile.next()) { language_ISO[i] = infile.key; language_full[i] = infile.nextValue(); i += 1; } } else fprintf(stderr, "Unable to open languages.txt!\n"); infile.close(); return true; }
void GameStateLoad::readGameSlot(int slot) { stringstream filename; FileParser infile; // abort if not a valid slot number if (slot < 0 || slot >= GAME_SLOT_MAX) return; // save slots are named save#.txt filename << "saves/save" << (slot+1) << ".txt"; if (!infile.open(filename.str())) return; while (infile.next()) { // load (key=value) pairs if (infile.key == "name") stats[slot].name = infile.val; else if (infile.key == "xp") stats[slot].xp = atoi(infile.val.c_str()); else if (infile.key == "build") { stats[slot].physical_character = atoi(infile.nextValue().c_str()); stats[slot].mental_character = atoi(infile.nextValue().c_str()); stats[slot].offense_character = atoi(infile.nextValue().c_str()); stats[slot].defense_character = atoi(infile.nextValue().c_str()); } else if (infile.key == "equipped") { equipped[slot][0] = atoi(infile.nextValue().c_str()); equipped[slot][1] = atoi(infile.nextValue().c_str()); equipped[slot][2] = atoi(infile.nextValue().c_str()); } else if (infile.key == "option") { stats[slot].base = infile.nextValue(); stats[slot].head = infile.nextValue(); stats[slot].portrait = infile.nextValue(); } else if (infile.key == "spawn") { current_map[slot] = getMapName(infile.nextValue()); } } infile.close(); stats[slot].recalc(); loadPreview(slot); }
void Map::loadEnemy(FileParser &infile) { if (infile.key == "type") { // @ATTR enemy.type|string|Enemy type enemies.back().type = infile.val; } else if (infile.key == "location") { // @ATTR enemy.location|[x(integer), y(integer)]|Location of enemy enemies.back().pos.x = toInt(infile.nextValue()) + 0.5f; enemies.back().pos.y = toInt(infile.nextValue()) + 0.5f; } else if (infile.key == "direction") { // @ATTR enemy.direction|integer|Direction of enemy enemies.back().direction = toInt(infile.val); } else if (infile.key == "waypoints") { // @ATTR enemy.waypoint|[x(integer), y(integer)]|Enemy waypoint std::string none = ""; std::string a = infile.nextValue(); std::string b = infile.nextValue(); while (a != none) { FPoint p; p.x = toInt(a) + 0.5f; p.y = toInt(b) + 0.5f; enemies.back().waypoints.push(p); a = infile.nextValue(); b = infile.nextValue(); } } else if (infile.key == "wander_area") { // @ATTR enemy.wander_area|[x(integer),y(integer),w(integer),h(integer)]|Wander area for the enemy. enemies.back().wander = true; enemies.back().wander_area.x = toInt(infile.nextValue()); enemies.back().wander_area.y = toInt(infile.nextValue()); enemies.back().wander_area.w = toInt(infile.nextValue()); enemies.back().wander_area.h = toInt(infile.nextValue()); } // @ATTR enemy.requires_status|string|Status required for enemy load else if (infile.key == "requires_status") enemies.back().requires_status.push_back(infile.nextValue()); // @ATTR enemy.requires_not_status|string|Status required to be missing for enemy load else if (infile.key == "requires_not_status") enemies.back().requires_not_status.push_back(infile.nextValue()); }
void loadTilesetSettings() { FileParser infile; // load tileset settings from engine config if (infile.open(mods->locate("engine/tileset_config.txt").c_str())) { while (infile.next()) { if (infile.key == "units_per_tile") { UNITS_PER_TILE = atoi(infile.val.c_str()); } else if (infile.key == "tile_size") { TILE_W = atoi(infile.nextValue().c_str()); TILE_H = atoi(infile.nextValue().c_str()); TILE_W_HALF = TILE_W /2; TILE_H_HALF = TILE_H /2; } else if (infile.key == "orientation") { if (infile.val == "isometric") TILESET_ORIENTATION = TILESET_ISOMETRIC; else if (infile.val == "orthogonal") TILESET_ORIENTATION = TILESET_ORTHOGONAL; } } infile.close(); } else { fprintf(stderr, "No tileset config found! Defaulting to 64x32 isometric tiles.\n"); } // Init automatically calculated parameters TILE_SHIFT = log2(UNITS_PER_TILE); VIEW_W_HALF = VIEW_W / 2; VIEW_H_HALF = VIEW_H / 2; if (TILESET_ORIENTATION == TILESET_ISOMETRIC) { UNITS_PER_PIXEL_X = UNITS_PER_TILE / TILE_W * 2; UNITS_PER_PIXEL_Y = UNITS_PER_TILE / TILE_H * 2; } else { // TILESET_ORTHOGONAL UNITS_PER_PIXEL_X = UNITS_PER_TILE / TILE_W; UNITS_PER_PIXEL_Y = UNITS_PER_TILE / TILE_H; } }
void EnemyGroupManager::parseEnemyFileAndStore(const string& filename) { FileParser infile; if (infile.open(mods->locate("enemies/" + filename))) { Enemy_Level new_enemy; new_enemy.type = filename.substr(0, filename.length()-4); //removes the ".txt" from the filename while (infile.next()) { if (infile.key == "level") { new_enemy.level = atoi(infile.val.c_str()); } else if (infile.key == "rarity") { new_enemy.rarity = infile.val.c_str(); } else if (infile.key == "categories") { string cat; while ( (cat = infile.nextValue()) != "") { _categories[cat].push_back(new_enemy); } } } infile.close(); } }
void loadTilesetSettings() { FileParser infile; // load tileset settings from engine config if (infile.open("engine/tileset_config.txt", true, true, "Unable to open engine/tileset_config.txt! Defaulting to 64x32 isometric tiles.\n")) { while (infile.next()) { if (infile.key == "tile_size") { TILE_W = toInt(infile.nextValue()); TILE_H = toInt(infile.nextValue()); TILE_W_HALF = TILE_W /2; TILE_H_HALF = TILE_H /2; } else if (infile.key == "orientation") { if (infile.val == "isometric") TILESET_ORIENTATION = TILESET_ISOMETRIC; else if (infile.val == "orthogonal") TILESET_ORIENTATION = TILESET_ORTHOGONAL; } } infile.close(); } // Init automatically calculated parameters VIEW_W_HALF = VIEW_W / 2; VIEW_H_HALF = VIEW_H / 2; if (TILESET_ORIENTATION == TILESET_ISOMETRIC) { UNITS_PER_PIXEL_X = 2.0f / TILE_W; UNITS_PER_PIXEL_Y = 2.0f / TILE_H; } else { // TILESET_ORTHOGONAL UNITS_PER_PIXEL_X = 1.0f / TILE_W; UNITS_PER_PIXEL_Y = 1.0f / TILE_H; } if (UNITS_PER_PIXEL_X == 0 || UNITS_PER_PIXEL_Y == 0) { fprintf(stderr, "One of UNITS_PER_PIXEL values is zero! %dx%d\n", (int)UNITS_PER_PIXEL_X, (int)UNITS_PER_PIXEL_Y); SDL_Quit(); exit(1); } }
void EnemyGroupManager::parseEnemyFilesAndStore() { std::vector<std::string> enemy_paths = mods->list("enemies", false); for (unsigned i=0; i<enemy_paths.size(); ++i) { FileParser infile; // @CLASS EnemyGroupManager|Description of enemies in enemies/ if (!infile.open(enemy_paths[i])) return; Enemy_Level new_enemy; infile.new_section = true; bool first = true; while (infile.next()) { if (infile.new_section || first) { new_enemy.type = enemy_paths[i]; first = false; } if (infile.key == "level") { // @ATTR level|integer|Level of the enemy new_enemy.level = toInt(infile.val); } else if (infile.key == "rarity") { // @ATTR rarity|[common,uncommon,rare]|Enemy rarity new_enemy.rarity = infile.val; } else if (infile.key == "categories") { // @ATTR categories|string,...|Comma separated list of enemy categories string cat; while ( (cat = infile.nextValue()) != "") { _categories[cat].push_back(new_enemy); } } } infile.close(); } }
void EnemyGroupManager::parseEnemyFilesAndStore() { FileParser infile; // @CLASS enemies|Describing enemies files in enemies/ if (!infile.open("enemies", true, false)) return; Enemy_Level new_enemy; infile.new_section = true; bool first = true; while (infile.next()) { if (infile.new_section || first) { const string fname = infile.getFileName(); const int firstpos = fname.rfind("/") + 1; const int len = fname.length() - firstpos - 4; //removes the ".txt" from the filename new_enemy.type = fname.substr(firstpos, len); first = false; } if (infile.key == "level") { // @ATTR level|integer|Level of the enemy new_enemy.level = toInt(infile.val); } else if (infile.key == "rarity") { // @ATTR rarity|[common,uncommon,rare]|Enemy rarity new_enemy.rarity = infile.val; } else if (infile.key == "categories") { // @ATTR categories|string,...|Comma separated list of enemy categories string cat; while ( (cat = infile.nextValue()) != "") { _categories[cat].push_back(new_enemy); } } } infile.close(); }
/** * load a statblock, typically for an enemy definition */ void StatBlock::load(const string& filename) { FileParser infile; int num = 0; if (infile.open(mods->locate(filename))) { while (infile.next()) { if (isInt(infile.val)) num = toInt(infile.val); if (infile.key == "name") name = msg->get(infile.val); else if (infile.key == "humanoid") { if (infile.val == "true") humanoid = true; } else if (infile.key == "sfx_prefix") sfx_prefix = infile.val; else if (infile.key == "gfx_prefix") gfx_prefix = infile.val; else if (infile.key == "level") level = num; // enemy death rewards and events else if (infile.key == "xp") xp = num; else if (infile.key == "loot_chance") loot_chance = num; else if (infile.key == "item_class") { string str; while ((str = infile.nextValue()) != "") { if (!isInt(str)) { item_classes.push_back(str); item_class_prob.push_back(1); item_class_prob_sum++; } else { num = toInt(str); item_class_prob[item_classes.size()-1] = num; item_class_prob_sum += num - 1; // one was already added, so add one less } } } else if (infile.key == "defeat_status") defeat_status = infile.val; else if (infile.key == "first_defeat_loot") first_defeat_loot = num; else if (infile.key == "quest_loot") { quest_loot_requires = infile.nextValue(); quest_loot_not = infile.nextValue(); quest_loot_id = toInt(infile.nextValue()); } // combat stats else if (infile.key == "hp") { hp = num; maxhp = num; } else if (infile.key == "mp") { mp = num; maxmp = num; } else if (infile.key == "cooldown") cooldown = num; else if (infile.key == "accuracy") accuracy = num; else if (infile.key == "avoidance") avoidance = num; else if (infile.key == "dmg_melee_min") dmg_melee_min = num; else if (infile.key == "dmg_melee_max") dmg_melee_max = num; else if (infile.key == "dmg_ment_min") dmg_ment_min = num; else if (infile.key == "dmg_ment_max") dmg_ment_max = num; else if (infile.key == "dmg_ranged_min") dmg_ranged_min = num; else if (infile.key == "dmg_ranged_max") dmg_ranged_max = num; else if (infile.key == "absorb_min") absorb_min = num; else if (infile.key == "absorb_max") absorb_max = num; // behavior stats else if (infile.key == "flying") { if (num == 1) flying = true; } else if (infile.key == "intangible") { if (num == 1) intangible = true; } else if (infile.key == "facing") { if (num == 0) facing = false; } else if (infile.key == "waypoint_pause") waypoint_pause = num; else if (infile.key == "speed") speed = num; else if (infile.key == "dspeed") dspeed = num; else if (infile.key == "turn_delay") turn_delay = num; else if (infile.key == "chance_pursue") chance_pursue = num; else if (infile.key == "chance_flee") chance_flee = num; else if (infile.key == "chance_melee_phys") power_chance[MELEE_PHYS] = num; else if (infile.key == "chance_melee_ment") power_chance[MELEE_MENT] = num; else if (infile.key == "chance_ranged_phys") power_chance[RANGED_PHYS] = num; else if (infile.key == "chance_ranged_ment") power_chance[RANGED_MENT] = num; else if (infile.key == "power_melee_phys") power_index[MELEE_PHYS] = num; else if (infile.key == "power_melee_ment") power_index[MELEE_MENT] = num; else if (infile.key == "power_ranged_phys") power_index[RANGED_PHYS] = num; else if (infile.key == "power_ranged_ment") power_index[RANGED_MENT] = num; else if (infile.key == "power_beacon") power_index[BEACON] = num; else if (infile.key == "cooldown_melee_phys") power_cooldown[MELEE_PHYS] = num; else if (infile.key == "cooldown_melee_ment") power_cooldown[MELEE_MENT] = num; else if (infile.key == "cooldown_ranged_phys") power_cooldown[RANGED_PHYS] = num; else if (infile.key == "cooldown_ranged_ment") power_cooldown[RANGED_MENT] = num; else if (infile.key == "power_on_hit") power_index[ON_HIT] = num; else if (infile.key == "power_on_death") power_index[ON_DEATH] = num; else if (infile.key == "power_on_half_dead") power_index[ON_HALF_DEAD] = num; else if (infile.key == "power_on_debuff") power_index[ON_DEBUFF] = num; else if (infile.key == "power_on_join_combat") power_index[ON_JOIN_COMBAT] = num; else if (infile.key == "chance_on_hit") power_chance[ON_HIT] = num; else if (infile.key == "chance_on_death") power_chance[ON_DEATH] = num; else if (infile.key == "chance_on_half_dead") power_chance[ON_HALF_DEAD] = num; else if (infile.key == "chance_on_debuff") power_chance[ON_DEBUFF] = num; else if (infile.key == "chance_on_join_combat") power_chance[ON_JOIN_COMBAT] = num; else if (infile.key == "melee_range") melee_range = num; else if (infile.key == "threat_range") threat_range = num; // animation stats else if (infile.key == "melee_weapon_power") melee_weapon_power = num; else if (infile.key == "mental_weapon_power") mental_weapon_power = num; else if (infile.key == "ranged_weapon_power") ranged_weapon_power = num; else if (infile.key == "animations") animations = infile.val; else if (infile.key == "animation_speed") animationSpeed = num; // hp countdown else if (infile.key == "hp_countdown_ticks") hp_countdown_ticks = num; // hide enemy HP bar else if (infile.key == "suppress_hp") suppress_hp = num; for (unsigned int i=0; i<ELEMENTS.size(); i++) { if (infile.key == "vulnerable_" + ELEMENTS[i].name) vulnerable[i] = num; } } infile.close(); } else fprintf(stderr, "Unable to open %s!\n", filename.c_str()); }
/** * NPCs are stored in simple config files * * @param npc_id Config file for npc */ void NPC::load(const std::string& npc_id) { FileParser infile; ItemStack stack; std::string filename_portrait = ""; bool clear_random_table = true; // @CLASS NPC|Description of NPCs in npcs/ if (infile.open(npc_id)) { while (infile.next()) { if (infile.section == "dialog") { if (infile.new_section) { dialog.push_back(std::vector<Event_Component>()); } Event_Component e; e.type = EC_NONE; if (infile.key == "him" || infile.key == "her") { // @ATTR dialog.him, dialog.her|string|A line of dialog from the NPC. e.type = EC_NPC_DIALOG_THEM; e.s = msg->get(infile.val); } else if (infile.key == "you") { // @ATTR dialog.you|string|A line of dialog from the player. e.type = EC_NPC_DIALOG_YOU; e.s = msg->get(infile.val); } else if (infile.key == "voice") { // @ATTR dialog.voice|string|Filename of a voice sound file to play. e.type = EC_NPC_VOICE; e.x = loadSound(infile.val, NPC_VOX_QUEST); } else if (infile.key == "topic") { // @ATTR dialog.topic|string|The name of this dialog topic. Displayed when picking a dialog tree. e.type = EC_NPC_DIALOG_TOPIC; e.s = msg->get(infile.val); } else if (infile.key == "group") { // @ATTR dialog.group|string|Dialog group. e.type = EC_NPC_DIALOG_GROUP; e.s = infile.val; } else if (infile.key == "allow_movement") { // @ATTR dialog.allow_movement|boolean|Restrict the player's mvoement during dialog. e.type = EC_NPC_ALLOW_MOVEMENT; e.s = infile.val; } else { Event ev; EventManager::loadEventComponent(infile, &ev, NULL); for (size_t i=0; i<ev.components.size(); ++i) { if (ev.components[i].type != EC_NONE) { dialog.back().push_back(ev.components[i]); } } } if (e.type != EC_NONE) { dialog.back().push_back(e); } } else { filename = npc_id; if (infile.new_section) { // APPENDed file clear_random_table = true; } if (infile.key == "name") { // @ATTR name|string|NPC's name. name = msg->get(infile.val); } else if (infile.key == "gfx") { // @ATTR gfx|string|Filename of an animation definition. gfx = infile.val; } // handle talkers else if (infile.key == "talker") { // @ATTR talker|boolean|Allows this NPC to be talked to. talker = toBool(infile.val); } else if (infile.key == "portrait") { // @ATTR portrait|string|Filename of a portrait image. filename_portrait = infile.val; } // handle vendors else if (infile.key == "vendor") { // @ATTR vendor|string|Allows this NPC to buy/sell items. vendor = toBool(infile.val); } else if (infile.key == "constant_stock") { // @ATTR constant_stock|item (integer), ...|A list of items this vendor has for sale. stack.quantity = 1; while (infile.val != "") { stack.item = toInt(infile.nextValue()); stock.add(stack); } } else if (infile.key == "status_stock") { // @ATTR status_stock|status (string), item (integer), ...|A list of items this vendor will have for sale if the required status is met. if (camp->checkStatus(infile.nextValue())) { stack.quantity = 1; while (infile.val != "") { stack.item = toInt(infile.nextValue()); stock.add(stack); } } } else if (infile.key == "random_stock") { // @ATTR random_stock|[string,drop_chance([fixed:chance(integer)]),quantity_min(integer),quantity_max(integer)],...|Use a loot table to add random items to the stock; either a filename or an inline definition. if (clear_random_table) { random_table.clear(); clear_random_table = false; } random_table.push_back(Event_Component()); loot->parseLoot(infile, &random_table.back(), &random_table); } else if (infile.key == "random_stock_count") { // @ATTR random_stock_count|min (integer), max (integer)|Sets the minimum (and optionally, the maximum) amount of random items this npc can have. random_table_count.x = toInt(infile.nextValue()); random_table_count.y = toInt(infile.nextValue()); if (random_table_count.x != 0 || random_table_count.y != 0) { clampFloor(random_table_count.x, 1); clampFloor(random_table_count.y, random_table_count.x); } } // handle vocals else if (infile.key == "vox_intro") { // @ATTR vox_intro|string|Filename of a sound file to play when initially interacting with the NPC. loadSound(infile.val, NPC_VOX_INTRO); } else { infile.error("NPC: '%s' is not a valid key.", infile.key.c_str()); } } } infile.close(); } loadGraphics(filename_portrait); // fill inventory with items from random stock table unsigned rand_count = randBetween(random_table_count.x, random_table_count.y); std::vector<ItemStack> rand_itemstacks; for (unsigned i=0; i<rand_count; ++i) { loot->checkLoot(random_table, NULL, &rand_itemstacks); } std::sort(rand_itemstacks.begin(), rand_itemstacks.end(), compareItemStack); for (size_t i=0; i<rand_itemstacks.size(); ++i) { stock.add(rand_itemstacks[i]); } }
void AnimationSet::load() { assert(!loaded); loaded = true; FileParser parser; const string filename = mods->locate(name); if (!parser.open(filename, "Error loading animation definition: " + name)) return; string _name = ""; int position = 0; int frames = 0; int duration = 0; Point render_size; Point render_offset; string type = ""; string starting_animation = ""; bool first_section=true; bool compressed_loading=false; // is reset every section to false, set by frame keyword Animation *newanim = NULL; vector<short> active_frames; // Parse the file and on each new section create an animation object from the data parsed previously parser.next(); do { // create the animation if finished parsing a section if (parser.new_section) { if (!first_section && !compressed_loading) { Animation *a = new Animation(_name, type, sprite); a->setupUncompressed(render_size, render_offset, position, frames, duration); if (!active_frames.empty()) a->setActiveFrames(active_frames); active_frames.clear(); animations.push_back(a); } first_section = false; compressed_loading = false; } if (parser.key == "image") { if (sprite) { printf("multiple images specified in %s, dragons be here!\n", name.c_str()); SDL_Quit(); exit(128); } imagefile = parser.val; imag->increaseCount(imagefile); sprite = imag->getSurface(imagefile); } else if (parser.key == "position") { position = toInt(parser.val); } else if (parser.key == "frames") { frames = toInt(parser.val); } else if (parser.key == "duration") { duration = toInt(parser.val); // TEMP: if an animation is too fast, display one frame per fps anyway if (duration < 1) duration=1; } else if (parser.key == "type") type = parser.val; else if (parser.key == "render_size") { render_size.x = toInt(parser.nextValue()); render_size.y = toInt(parser.nextValue()); } else if (parser.key == "render_offset") { render_offset.x = toInt(parser.nextValue()); render_offset.y = toInt(parser.nextValue()); } else if (parser.key == "active_frame") { active_frames.clear(); string nv = parser.nextValue(); if (nv == "all") { active_frames.push_back(-1); } else { while (nv != "") { active_frames.push_back(toInt(nv)); nv = parser.nextValue(); } sort(active_frames.begin(), active_frames.end()); active_frames.erase(unique(active_frames.begin(), active_frames.end()), active_frames.end()); } } else if (parser.key == "frame") { if (compressed_loading == false) { // first frame statement in section newanim = new Animation(_name, type, sprite); newanim->setup(frames, duration); if (!active_frames.empty()) newanim->setActiveFrames(active_frames); active_frames.clear(); animations.push_back(newanim); compressed_loading = true; } // frame = index, direction, x, y, w, h, offsetx, offsety SDL_Rect r; Point offset; const int index = toInt(parser.nextValue()); const int direction = toInt(parser.nextValue()); r.x = toInt(parser.nextValue()); r.y = toInt(parser.nextValue()); r.w = toInt(parser.nextValue()); r.h = toInt(parser.nextValue()); offset.x = toInt(parser.nextValue()); offset.y = toInt(parser.nextValue()); newanim->addFrame(index, direction, r, offset); } else { fprintf(stderr, "animations definitions (%s): Key %s not supported!\n", filename.c_str(), parser.key.c_str()); } if (_name == "") { // This is the first animation starting_animation = parser.section; } _name = parser.section; } while (parser.next()); if (!compressed_loading) { // add final animation Animation *a = new Animation(_name, type, sprite); a->setupUncompressed(render_size, render_offset, position, frames, duration); if (!active_frames.empty()) a->setActiveFrames(active_frames); active_frames.clear(); animations.push_back(a); } if (starting_animation != "") { Animation *a = getAnimation(starting_animation); delete defaultAnimation; defaultAnimation = a; } }
/** * Load a specific items file * * @param filename The full path and name of the file to load */ void ItemManager::load(const string& filename) { FileParser infile; if (!infile.open(filename)) { fprintf(stderr, "Unable to open %s!\n", filename.c_str()); return; } int id = 0; bool id_line = false; while (infile.next()) { if (infile.key == "id") { id_line = true; id = toInt(infile.val); ensureFitsId(items, id+1); } else id_line = false; if (id < 1) { if (id_line) fprintf(stderr, "Item index out of bounds 1-%d, skipping\n", INT_MAX); continue; } if (id_line) continue; assert(items.size() > std::size_t(id)); if (infile.key == "name") items[id].name = msg->get(infile.val); else if (infile.key == "flavor") items[id].flavor = msg->get(infile.val); else if (infile.key == "level") items[id].level = toInt(infile.val); else if (infile.key == "icon") { items[id].icon = toInt(infile.nextValue()); } else if (infile.key == "quality") { if (infile.val == "low") items[id].quality = ITEM_QUALITY_LOW; else if (infile.val == "high") items[id].quality = ITEM_QUALITY_HIGH; else if (infile.val == "epic") items[id].quality = ITEM_QUALITY_EPIC; } else if (infile.key == "item_type") { items[id].type = infile.val; } else if (infile.key == "dmg_melee") { 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") { 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") { 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") { 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 == "req") { string s = infile.nextValue(); if (s == "p") items[id].req_stat = REQUIRES_PHYS; else if (s == "m") items[id].req_stat = REQUIRES_MENT; else if (s == "o") items[id].req_stat = REQUIRES_OFF; else if (s == "d") items[id].req_stat = REQUIRES_DEF; items[id].req_val = toInt(infile.nextValue()); } else if (infile.key == "bonus") { items[id].bonus_stat.push_back(infile.nextValue()); items[id].bonus_val.push_back(toInt(infile.nextValue())); } else if (infile.key == "soundfx") { items[id].sfx = snd->load(infile.val, "ItemManager"); } else if (infile.key == "gfx") items[id].gfx = infile.val; else if (infile.key == "loot_animation") items[id].loot_animation = infile.val; else if (infile.key == "power") { if (toInt(infile.val) > 0) { items[id].power = toInt(infile.val); } else fprintf(stderr, "Power index inside item %d definition out of bounds 1-%d, skipping item\n", id, INT_MAX); } else if (infile.key == "power_mod") items[id].power_mod = toInt(infile.val); else if (infile.key == "power_desc") items[id].power_desc = msg->get(infile.val); else if (infile.key == "price") items[id].price = toInt(infile.val); else if (infile.key == "price_sell") items[id].price_sell = toInt(infile.val); else if (infile.key == "max_quantity") items[id].max_quantity = toInt(infile.val); else if (infile.key == "rand_loot") items[id].rand_loot = toInt(infile.val); else if (infile.key == "rand_vendor") items[id].rand_vendor = toInt(infile.val); else if (infile.key == "pickup_status") items[id].pickup_status = infile.val; else if (infile.key == "stepfx") items[id].stepfx = infile.val; else if (infile.key == "class") { string classname = infile.nextValue(); while (classname != "") { unsigned pos; // find the position where this classname is stored: for (pos = 0; pos < item_class_names.size(); pos++) { if (item_class_names[pos] == classname) break; } // if it was not found, add it to the end. // pos is already the correct index. if (pos == item_class_names.size()) { item_class_names.push_back(classname); item_class_items.push_back(vector<unsigned int>()); } // add item id to the item list of that class: item_class_items[pos].push_back(id); classname = infile.nextValue(); } } } infile.close(); }
/** * Powers are defined in [mod]/powers/powers.txt * * @param filename The full path and filename to this powers.txt file */ void PowerManager::loadPowers(const std::string& filename) { FileParser infile; if (!infile.open(filename)) { fprintf(stderr, "Unable to open %s!\n", filename.c_str()); return; } int input_id = 0; bool skippingEntry = false; while (infile.next()) { // id needs to be the first component of each power. That is how we write // data to the correct power. if (infile.key == "id") { input_id = toInt(infile.val); skippingEntry = input_id < 1; if (skippingEntry) fprintf(stderr, "Power index out of bounds 1-%d, skipping\n", INT_MAX); if (static_cast<int>(powers.size()) < input_id + 1) powers.resize(input_id + 1); continue; } if (skippingEntry) continue; if (infile.key == "type") { if (infile.val == "fixed") powers[input_id].type = POWTYPE_FIXED; else if (infile.val == "missile") powers[input_id].type = POWTYPE_MISSILE; else if (infile.val == "repeater") powers[input_id].type = POWTYPE_REPEATER; else if (infile.val == "spawn") powers[input_id].type = POWTYPE_SPAWN; else if (infile.val == "transform") powers[input_id].type = POWTYPE_TRANSFORM; else if (infile.val == "effect") powers[input_id].type = POWTYPE_EFFECT; else fprintf(stderr, "unknown type %s\n", infile.val.c_str()); } else if (infile.key == "name") powers[input_id].name = msg->get(infile.val); else if (infile.key == "description") powers[input_id].description = msg->get(infile.val); else if (infile.key == "tag") powers[input_id].tag = infile.val; else if (infile.key == "icon") powers[input_id].icon = toInt(infile.val); else if (infile.key == "new_state") { if (infile.val == "swing") powers[input_id].new_state = POWSTATE_SWING; else if (infile.val == "shoot") powers[input_id].new_state = POWSTATE_SHOOT; else if (infile.val == "cast") powers[input_id].new_state = POWSTATE_CAST; else if (infile.val == "block") powers[input_id].new_state = POWSTATE_BLOCK; else if (infile.val == "instant") powers[input_id].new_state = POWSTATE_INSTANT; else fprintf(stderr, "unknown new_state %s\n", infile.val.c_str()); } else if (infile.key == "face") powers[input_id].face = toBool(infile.val); else if (infile.key == "source_type") { if (infile.val == "hero") powers[input_id].source_type = SOURCE_TYPE_HERO; else if (infile.val == "neutral") powers[input_id].source_type = SOURCE_TYPE_NEUTRAL; else if (infile.val == "enemy") powers[input_id].source_type = SOURCE_TYPE_ENEMY; else fprintf(stderr, "unknown source_type %s\n", infile.val.c_str()); } else if (infile.key == "beacon") powers[input_id].beacon = toBool(infile.val); else if (infile.key == "count") powers[input_id].count = toInt(infile.val); else if (infile.key == "passive") powers[input_id].passive = toBool(infile.val); else if (infile.key == "passive_trigger") { if (infile.val == "on_block") powers[input_id].passive_trigger = TRIGGER_BLOCK; else if (infile.val == "on_hit") powers[input_id].passive_trigger = TRIGGER_HIT; else if (infile.val == "on_halfdeath") powers[input_id].passive_trigger = TRIGGER_HALFDEATH; else if (infile.val == "on_joincombat") powers[input_id].passive_trigger = TRIGGER_JOINCOMBAT; else if (infile.val == "on_death") powers[input_id].passive_trigger = TRIGGER_DEATH; else fprintf(stderr, "unknown passive trigger %s\n", infile.val.c_str()); } // power requirements else if (infile.key == "requires_physical_weapon") powers[input_id].requires_physical_weapon = toBool(infile.val); else if (infile.key == "requires_mental_weapon") powers[input_id].requires_mental_weapon = toBool(infile.val); else if (infile.key == "requires_offense_weapon") powers[input_id].requires_offense_weapon = toBool(infile.val); else if (infile.key == "requires_mp") powers[input_id].requires_mp = toInt(infile.val); else if (infile.key == "requires_hp") powers[input_id].requires_hp = toInt(infile.val); else if (infile.key == "sacrifice") powers[input_id].sacrifice = toBool(infile.val); else if (infile.key == "requires_los") powers[input_id].requires_los = toBool(infile.val); else if (infile.key == "requires_empty_target") powers[input_id].requires_empty_target = toBool(infile.val); else if (infile.key == "requires_item") powers[input_id].requires_item = toInt(infile.val); else if (infile.key == "requires_equipped_item") powers[input_id].requires_equipped_item = toInt(infile.val); else if (infile.key == "requires_targeting") powers[input_id].requires_targeting = toBool(infile.val); else if (infile.key == "cooldown") powers[input_id].cooldown = toInt(infile.val); // animation info else if (infile.key == "animation") powers[input_id].animation_name = "animations/powers/" + infile.val; else if (infile.key == "sfx") powers[input_id].sfx_index = loadSFX(infile.val); else if (infile.key == "directional") powers[input_id].directional = toBool(infile.val); else if (infile.key == "visual_random") powers[input_id].visual_random = toInt(infile.val); else if (infile.key == "visual_option") powers[input_id].visual_option = toInt(infile.val); else if (infile.key == "aim_assist") powers[input_id].aim_assist = toBool(infile.val); else if (infile.key == "speed") powers[input_id].speed = toInt(infile.val); else if (infile.key == "lifespan") powers[input_id].lifespan = toInt(infile.val); else if (infile.key == "floor") powers[input_id].floor = toBool(infile.val); else if (infile.key == "complete_animation") powers[input_id].complete_animation = toBool(infile.val); // hazard traits else if (infile.key == "use_hazard") powers[input_id].use_hazard = toBool(infile.val); else if (infile.key == "no_attack") powers[input_id].no_attack = toBool(infile.val); else if (infile.key == "radius") powers[input_id].radius = toInt(infile.val); else if (infile.key == "base_damage") { if (infile.val == "none") powers[input_id].base_damage = BASE_DAMAGE_NONE; else if (infile.val == "melee") powers[input_id].base_damage = BASE_DAMAGE_MELEE; else if (infile.val == "ranged") powers[input_id].base_damage = BASE_DAMAGE_RANGED; else if (infile.val == "ment") powers[input_id].base_damage = BASE_DAMAGE_MENT; else fprintf(stderr, "unknown base_damage %s\n", infile.val.c_str()); } else if (infile.key == "damage_multiplier") powers[input_id].damage_multiplier = toInt(infile.val); else if (infile.key == "starting_pos") { if (infile.val == "source") powers[input_id].starting_pos = STARTING_POS_SOURCE; else if (infile.val == "target") powers[input_id].starting_pos = STARTING_POS_TARGET; else if (infile.val == "melee") powers[input_id].starting_pos = STARTING_POS_MELEE; else fprintf(stderr, "unknown starting_pos %s\n", infile.val.c_str()); } else if (infile.key == "multitarget") powers[input_id].multitarget = toBool(infile.val); else if (infile.key == "trait_armor_penetration") powers[input_id].trait_armor_penetration = toBool(infile.val); else if (infile.key == "trait_crits_impaired") powers[input_id].trait_crits_impaired = toInt(infile.val); else if (infile.key == "trait_elemental") { for (unsigned int i=0; i<ELEMENTS.size(); i++) { if (infile.val == ELEMENTS[i].name) powers[input_id].trait_elemental = i; } } else if (infile.key == "range") powers[input_id].range = toInt(infile.nextValue()); //steal effects else if (infile.key == "hp_steal") powers[input_id].hp_steal = toInt(infile.val); else if (infile.key == "mp_steal") powers[input_id].mp_steal = toInt(infile.val); //missile modifiers else if (infile.key == "missile_angle") powers[input_id].missile_angle = toInt(infile.val); else if (infile.key == "angle_variance") powers[input_id].angle_variance = toInt(infile.val); else if (infile.key == "speed_variance") powers[input_id].speed_variance = toInt(infile.val); //repeater modifiers else if (infile.key == "delay") powers[input_id].delay = toInt(infile.val); // buff/debuff durations else if (infile.key == "transform_duration") powers[input_id].transform_duration = toInt(infile.val); else if (infile.key == "manual_untransform") powers[input_id].manual_untransform = toBool(infile.val); // buffs else if (infile.key == "buff") powers[input_id].buff= toBool(infile.val); else if (infile.key == "buff_teleport") powers[input_id].buff_teleport = toBool(infile.val); else if (infile.key == "post_effect") { infile.val = infile.val + ','; PostEffect pe; pe.id = eatFirstInt(infile.val, ','); pe.magnitude = eatFirstInt(infile.val, ','); pe.duration = eatFirstInt(infile.val, ','); powers[input_id].post_effects.push_back(pe); } else if (infile.key == "effect_type") powers[input_id].effect_type = infile.val; else if (infile.key == "effect_additive") powers[input_id].effect_additive = toBool(infile.val); else if (infile.key == "effect_render_above") powers[input_id].effect_render_above = toBool(infile.val); // pre and post power effects else if (infile.key == "post_power") powers[input_id].post_power = toInt(infile.val); else if (infile.key == "wall_power") powers[input_id].wall_power = toInt(infile.val); else if (infile.key == "allow_power_mod") powers[input_id].allow_power_mod = toBool(infile.val); // spawn info else if (infile.key == "spawn_type") powers[input_id].spawn_type = infile.val; else if (infile.key == "target_neighbor") powers[input_id].target_neighbor = toInt(infile.val); else fprintf(stderr, "ignoring unknown key %s set to %s\n", infile.key.c_str(), infile.val.c_str()); } infile.close(); }
void Map::loadEnemyGroup(FileParser &infile, Map_Group *group) { if (infile.key == "type") { // @ATTR enemygroup.type|string|The category of enemies that will spawn in this group. group->category = infile.val; } else if (infile.key == "level") { // @ATTR enemygroup.level|[min(integer), max(integer)]|Defines the level range of enemies in group. If only one number is given, it's the exact level. group->levelmin = std::max(0, toInt(infile.nextValue())); group->levelmax = std::max(0, toInt(infile.nextValue(), group->levelmin)); } else if (infile.key == "location") { // @ATTR enemygroup.location|[x(integer), y(integer), x2(integer), y2(integer)]|Location area for enemygroup group->pos.x = toInt(infile.nextValue()); group->pos.y = toInt(infile.nextValue()); group->area.x = toInt(infile.nextValue()); group->area.y = toInt(infile.nextValue()); } else if (infile.key == "number") { // @ATTR enemygroup.number|[min(integer), max(integer]|Defines the range of enemies in group. If only one number is given, it's the exact amount. group->numbermin = std::max(0, toInt(infile.nextValue())); group->numbermax = std::max(0, toInt(infile.nextValue(), group->numbermin)); } else if (infile.key == "chance") { // @ATTR enemygroup.chance|integer|Percentage of chance float n = static_cast<float>(std::max(0, toInt(infile.nextValue()))) / 100.0f; group->chance = std::min(1.0f, std::max(0.0f, n)); } else if (infile.key == "direction") { // @ATTR enemygroup.direction|direction|Direction that enemies will initially face. group->direction = parse_direction(infile.val); } else if (infile.key == "waypoints") { // @ATTR enemygroup.waypoints|[x(integer), y(integer)]|Enemy waypoints; single enemy only; negates wander_radius std::string none = ""; std::string a = infile.nextValue(); std::string b = infile.nextValue(); while (a != none) { FPoint p; p.x = static_cast<float>(toInt(a)) + 0.5f; p.y = static_cast<float>(toInt(b)) + 0.5f; group->waypoints.push(p); a = infile.nextValue(); b = infile.nextValue(); } // disable wander radius, since we can't have waypoints and wandering at the same time group->wander_radius = 0; } else if (infile.key == "wander_radius") { // @ATTR enemygroup.wander_radius|integer|The radius (in tiles) that an enemy will wander around randomly; negates waypoints group->wander_radius = std::max(0, toInt(infile.nextValue())); // clear waypoints, since wandering will use the waypoint queue while (!group->waypoints.empty()) { group->waypoints.pop(); } } else if (infile.key == "requires_status") { // @ATTR enemygroup.requires_status|string|Status required for loading enemies group->requires_status.push_back(infile.nextValue()); } else if (infile.key == "requires_not_status") { // @ATTR enemygroup.requires_not_status|string|Status required to be missing for loading enemies group->requires_not_status.push_back(infile.nextValue()); } else { infile.error("Map: '%s' is not a valid key.", infile.key.c_str()); } }
/** * load */ int MapRenderer::load(string filename) { FileParser infile; string val; string cur_layer; string data_format; clearEvents(); bool collider_set = false; if (infile.open(mods->locate("maps/" + filename))) { while (infile.next()) { if (infile.new_section) { data_format = "dec"; // default if (enemy_awaiting_queue) { enemies.push(new_enemy); enemy_awaiting_queue = false; } if (npc_awaiting_queue) { npcs.push(new_npc); npc_awaiting_queue = false; } if (group_awaiting_queue) { push_enemy_group(new_group); group_awaiting_queue = false; } // for sections that are stored in collections, add a new object here if (infile.section == "enemy") { new_enemy.clear(); enemy_awaiting_queue = true; } else if (infile.section == "enemygroup") { new_group.clear(); group_awaiting_queue = true; } else if (infile.section == "npc") { new_npc.clear(); npc_awaiting_queue = true; } else if (infile.section == "event") { events.push_back(Map_Event()); } } if (infile.section == "header") { if (infile.key == "title") { this->title = msg->get(infile.val); } else if (infile.key == "width") { this->w = atoi(infile.val.c_str()); } else if (infile.key == "height") { this->h = atoi(infile.val.c_str()); } else if (infile.key == "tileset") { this->tileset = infile.val; } else if (infile.key == "music") { if (this->music_filename == infile.val) { this->new_music = false; } else { this->music_filename = infile.val; this->new_music = true; } } else if (infile.key == "location") { spawn.x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; spawn.y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; spawn_dir = atoi(infile.nextValue().c_str()); } } else if (infile.section == "layer") { if (infile.key == "type") { cur_layer = infile.val; } else if (infile.key == "format") { data_format = infile.val; } else if (infile.key == "data") { // layer map data handled as a special case // The next h lines must contain layer data. TODO: err if (data_format == "hex") { for (int j=0; j<h; j++) { val = infile.getRawLine() + ','; for (int i=0; i<w; i++) { if (cur_layer == "background") background[i][j] = eatFirstHex(val, ','); else if (cur_layer == "object") object[i][j] = eatFirstHex(val, ','); else if (cur_layer == "collision") collision[i][j] = eatFirstHex(val, ','); } } } else if (data_format == "dec") { for (int j=0; j<h; j++) { val = infile.getRawLine() + ','; for (int i=0; i<w; i++) { if (cur_layer == "background") background[i][j] = eatFirstInt(val, ','); else if (cur_layer == "object") object[i][j] = eatFirstInt(val, ','); else if (cur_layer == "collision") collision[i][j] = eatFirstInt(val, ','); } } } if ((cur_layer == "collision") && !collider_set) { collider.setmap(collision); collider.map_size.x = w; collider.map_size.y = h; } } } else if (infile.section == "enemy") { if (infile.key == "type") { new_enemy.type = infile.val; } else if (infile.key == "location") { new_enemy.pos.x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; new_enemy.pos.y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; } else if (infile.key == "direction") { new_enemy.direction = atoi(infile.val.c_str()); } else if (infile.key == "waypoints") { string none = ""; string a = infile.nextValue(); string b = infile.nextValue(); while (a != none) { Point p; p.x = atoi(a.c_str()) * UNITS_PER_TILE + UNITS_PER_TILE / 2; p.y = atoi(b.c_str()) * UNITS_PER_TILE + UNITS_PER_TILE / 2; new_enemy.waypoints.push(p); a = infile.nextValue(); b = infile.nextValue(); } } else if (infile.key == "wander_area") { new_enemy.wander = true; new_enemy.wander_area.x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE / 2; new_enemy.wander_area.y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE / 2; new_enemy.wander_area.w = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE / 2; new_enemy.wander_area.h = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE / 2; } } else if (infile.section == "enemygroup") { if (infile.key == "type") { new_group.category = infile.val; } else if (infile.key == "level") { new_group.levelmin = atoi(infile.nextValue().c_str()); new_group.levelmax = atoi(infile.nextValue().c_str()); } else if (infile.key == "location") { new_group.pos.x = atoi(infile.nextValue().c_str()); new_group.pos.y = atoi(infile.nextValue().c_str()); new_group.area.x = atoi(infile.nextValue().c_str()); new_group.area.y = atoi(infile.nextValue().c_str()); } else if (infile.key == "number") { new_group.numbermin = atoi(infile.nextValue().c_str()); new_group.numbermax = atoi(infile.nextValue().c_str()); } else if (infile.key == "chance") { new_group.chance = atoi(infile.nextValue().c_str()) / 100.0f; if (new_group.chance > 1.0f) { new_group.chance = 1.0f; } if (new_group.chance < 0.0f) { new_group.chance = 0.0f; } } } else if (infile.section == "npc") { if (infile.key == "type") { new_npc.id = infile.val; } else if (infile.key == "location") { new_npc.pos.x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; new_npc.pos.y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; } } else if (infile.section == "event") { if (infile.key == "type") { events.back().type = infile.val; } else if (infile.key == "location") { events.back().location.x = atoi(infile.nextValue().c_str()); events.back().location.y = atoi(infile.nextValue().c_str()); events.back().location.w = atoi(infile.nextValue().c_str()); events.back().location.h = atoi(infile.nextValue().c_str()); } else if (infile.key == "hotspot") { events.back().hotspot.x = atoi(infile.nextValue().c_str()); events.back().hotspot.y = atoi(infile.nextValue().c_str()); events.back().hotspot.w = atoi(infile.nextValue().c_str()); events.back().hotspot.h = atoi(infile.nextValue().c_str()); } else if (infile.key == "tooltip") { events.back().tooltip = msg->get(infile.val); } else if (infile.key == "power_path") { events.back().power_src.x = atoi(infile.nextValue().c_str()); events.back().power_src.y = atoi(infile.nextValue().c_str()); string dest = infile.nextValue(); if (dest == "hero") { events.back().targetHero = true; } else { events.back().power_dest.x = atoi(dest.c_str()); events.back().power_dest.y = atoi(infile.nextValue().c_str()); } } else if (infile.key == "power_damage") { events.back().damagemin = atoi(infile.nextValue().c_str()); events.back().damagemax = atoi(infile.nextValue().c_str()); } else if (infile.key == "cooldown") { events.back().cooldown = atoi(infile.val.c_str()); } else { // new event component Event_Component *e = &(events.back()).components[events.back().comp_num]; e->type = infile.key; if (infile.key == "intermap") { e->s = infile.nextValue(); e->x = atoi(infile.nextValue().c_str()); e->y = atoi(infile.nextValue().c_str()); } else if (infile.key == "intramap") { e->x = atoi(infile.nextValue().c_str()); e->y = atoi(infile.nextValue().c_str()); } else if (infile.key == "mapmod") { e->s = infile.nextValue(); e->x = atoi(infile.nextValue().c_str()); e->y = atoi(infile.nextValue().c_str()); e->z = atoi(infile.nextValue().c_str()); // add repeating mapmods string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; e->x = atoi(infile.nextValue().c_str()); e->y = atoi(infile.nextValue().c_str()); e->z = atoi(infile.nextValue().c_str()); repeat_val = infile.nextValue(); } } else if (infile.key == "soundfx") { e->s = infile.val; } else if (infile.key == "loot") { e->s = infile.nextValue(); e->x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; e->y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; e->z = atoi(infile.nextValue().c_str()); // add repeating loot string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; e->x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; e->y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; e->z = atoi(infile.nextValue().c_str()); repeat_val = infile.nextValue(); } } else if (infile.key == "msg") { e->s = msg->get(infile.val); } else if (infile.key == "shakycam") { e->x = atoi(infile.val.c_str()); } else if (infile.key == "requires_status") { e->s = infile.nextValue(); // add repeating requires_status string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; repeat_val = infile.nextValue(); } } else if (infile.key == "requires_not") { e->s = infile.nextValue(); // add repeating requires_not string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; repeat_val = infile.nextValue(); } } else if (infile.key == "requires_item") { e->x = atoi(infile.nextValue().c_str()); // add repeating requires_item string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->x = atoi(repeat_val.c_str()); repeat_val = infile.nextValue(); } } else if (infile.key == "set_status") { e->s = infile.nextValue(); // add repeating set_status string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; repeat_val = infile.nextValue(); } } else if (infile.key == "unset_status") { e->s = infile.nextValue(); // add repeating unset_status string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; repeat_val = infile.nextValue(); } } else if (infile.key == "remove_item") { e->x = atoi(infile.nextValue().c_str()); // add repeating remove_item string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->x = atoi(repeat_val.c_str()); repeat_val = infile.nextValue(); } } else if (infile.key == "reward_xp") { e->x = atoi(infile.val.c_str()); } else if (infile.key == "power") { e->x = atoi(infile.val.c_str()); } else if (infile.key == "spawn") { e->s = infile.nextValue(); e->x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; e->y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; // add repeating spawn string repeat_val = infile.nextValue(); while (repeat_val != "") { events.back().comp_num++; e = &events.back().components[events.back().comp_num]; e->type = infile.key; e->s = repeat_val; e->x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; e->y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; repeat_val = infile.nextValue(); } } events.back().comp_num++; } } } infile.close(); // reached end of file. Handle any final sections. if (enemy_awaiting_queue) { enemies.push(new_enemy); enemy_awaiting_queue = false; } if (npc_awaiting_queue) { npcs.push(new_npc); npc_awaiting_queue = false; } if (group_awaiting_queue){ push_enemy_group(new_group); group_awaiting_queue = false; } } if (this->new_music) { loadMusic(); this->new_music = false; } tset.load(this->tileset); return 0; }
/** * Load a specific items file * * @param filename The full path and name of the file to load */ void ItemManager::loadItems() { FileParser infile; // @CLASS Item|Description about the class and it usage, items/items.txt... if (!infile.open("items/items.txt", true, false)) return; 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); ensureFitsId(items, id+1); } else id_line = false; if (id < 1) { if (id_line) fprintf(stderr, "Item index out of bounds 1-%d, skipping\n", 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); else if (infile.key == "flavor") // @ATTR flavor|string| items[id].flavor = msg->get(infile.val); else if (infile.key == "level") // @ATTR level|integer| items[id].level = toInt(infile.val); else if (infile.key == "icon") { // @ATTR icon|integer| items[id].icon = toInt(infile.nextValue()); } else if (infile.key == "quality") { // @ATTR quality|[low:high:epic]|Item quality, corresponds to item color. if (infile.val == "low") items[id].quality = ITEM_QUALITY_LOW; else if (infile.val == "high") items[id].quality = ITEM_QUALITY_HIGH; else if (infile.val == "epic") items[id].quality = ITEM_QUALITY_EPIC; } 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") { infile.val = infile.val + ','; std::string flag = eatFirstString(infile.val,','); while (flag != "") { items[id].equip_flags.push_back(flag); flag = eatFirstString(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 == "req") { // @ATTR req|[ [p:m:o:d], amount (integer) ]|Make item require specific stat level ex. req=p,6 will require hero to have level 6 in physical stats string s = infile.nextValue(); if (s == "p") items[id].req_stat = REQUIRES_PHYS; else if (s == "m") items[id].req_stat = REQUIRES_MENT; else if (s == "o") items[id].req_stat = REQUIRES_OFF; else if (s == "d") items[id].req_stat = REQUIRES_DEF; items[id].req_val = toInt(infile.nextValue()); } else if (infile.key == "bonus") { // @ATTR bonus|[power_tag (string), amount (integer)]|Adds a bonus to the item power_tag being a uniq tag of a power definition, e.: bonus=HP regen, 50 items[id].bonus_stat.push_back(infile.nextValue()); items[id].bonus_val.push_back(toInt(infile.nextValue())); } else if (infile.key == "soundfx") { // @ATTR soundfx|string|Sound effect for the specific item. items[id].sfx = snd->load(infile.val, "ItemManager"); } else if (infile.key == "gfx") // @ATTR gfx|string|Graphics for the specific item. items[id].gfx = infile.val; else if (infile.key == "loot_animation") { // @ATTR loot_animation|string|Specifies the loot animation for the item. infile.val = infile.val + ','; LootAnimation la; la.name = eatFirstString(infile.val, ','); la.low = eatFirstInt(infile.val, ','); la.high = eatFirstInt(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 fprintf(stderr, "Power index inside item %d definition out of bounds 1-%d, skipping item\n", id, INT_MAX); } else if (infile.key == "power_mod") // @ATTR power_mod|integer|Power modifier of item. items[id].power_mod = toInt(infile.val); 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_sell") // @ATTR sell_price|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 == "rand_loot") // @ATTR rand_loot|integer|Max amount appearing in loot stack. items[id].rand_loot = toInt(infile.val); else if (infile.key == "rand_vendor") // @ATTR rand_vendor|integer|Max amount appearing in vendor stack. items[id].rand_vendor = 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 == "class") { // @ATTR class|[classname (string), ...]|A comma separated list of classes the item belongs too. string classname = infile.nextValue(); while (classname != "") { unsigned pos; // find the position where this classname is stored: for (pos = 0; pos < item_class_names.size(); pos++) { if (item_class_names[pos] == classname) break; } // if it was not found, add it to the end. // pos is already the correct index. if (pos == item_class_names.size()) { item_class_names.push_back(classname); item_class_items.push_back(vector<unsigned int>()); } // add item id to the item list of that class: item_class_items[pos].push_back(id); classname = infile.nextValue(); } } } infile.close(); }
/** * Load a specific items file * * @param filename The full path and name of the file to load */ void ItemManager::load(const string& filename) { FileParser infile; if (!infile.open(filename)) { fprintf(stderr, "Unable to open %s!\n", filename.c_str()); return; } int id = 0; bool id_line = false; while (infile.next()) { if (infile.key == "id") { id_line = true; id = toInt(infile.val); if (id > 0 && id >= (int)items.size()) { // *2 to amortize the resizing to O(log(n)). items.resize((2*id) + 1); } } else id_line = false; if (id < 1) { if (id_line) fprintf(stderr, "Item index out of bounds 1-%d, skipping\n", INT_MAX); continue; } if (id_line) continue; if (infile.key == "name") items[id].name = msg->get(infile.val); else if (infile.key == "level") items[id].level = toInt(infile.val); else if (infile.key == "icon") { items[id].icon = toInt(infile.nextValue()); } else if (infile.key == "quality") { if (infile.val == "low") items[id].quality = ITEM_QUALITY_LOW; else if (infile.val == "high") items[id].quality = ITEM_QUALITY_HIGH; else if (infile.val == "epic") items[id].quality = ITEM_QUALITY_EPIC; } else if (infile.key == "item_type") { items[id].type = infile.val; } else if (infile.key == "dmg_melee") { 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") { 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") { 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") { 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 == "req") { string s = infile.nextValue(); if (s == "p") items[id].req_stat = REQUIRES_PHYS; else if (s == "m") items[id].req_stat = REQUIRES_MENT; else if (s == "o") items[id].req_stat = REQUIRES_OFF; else if (s == "d") items[id].req_stat = REQUIRES_DEF; items[id].req_val = toInt(infile.nextValue()); } else if (infile.key == "bonus") { items[id].bonus_stat.push_back(infile.nextValue()); items[id].bonus_val.push_back(toInt(infile.nextValue())); } else if (infile.key == "sfx") { if (infile.val == "book") items[id].sfx = SFX_BOOK; else if (infile.val == "cloth") items[id].sfx = SFX_CLOTH; else if (infile.val == "coins") items[id].sfx = SFX_COINS; else if (infile.val == "gem") items[id].sfx = SFX_GEM; else if (infile.val == "leather") items[id].sfx = SFX_LEATHER; else if (infile.val == "metal") items[id].sfx = SFX_METAL; else if (infile.val == "page") items[id].sfx = SFX_PAGE; else if (infile.val == "maille") items[id].sfx = SFX_MAILLE; else if (infile.val == "object") items[id].sfx = SFX_OBJECT; else if (infile.val == "heavy") items[id].sfx = SFX_HEAVY; else if (infile.val == "wood") items[id].sfx = SFX_WOOD; else if (infile.val == "potion") items[id].sfx = SFX_POTION; } else if (infile.key == "gfx") items[id].gfx = infile.val; else if (infile.key == "loot_animation") items[id].loot_animation = infile.val; else if (infile.key == "power") { if (toInt(infile.val) > 0) { items[id].power = toInt(infile.val); } else fprintf(stderr, "Power index inside item %d definition out of bounds 1-%d, skipping item\n", id, INT_MAX); } else if (infile.key == "power_mod") items[id].power_mod = toInt(infile.val); else if (infile.key == "power_desc") items[id].power_desc = msg->get(infile.val); else if (infile.key == "price") items[id].price = toInt(infile.val); else if (infile.key == "price_sell") items[id].price_sell = toInt(infile.val); else if (infile.key == "max_quantity") items[id].max_quantity = toInt(infile.val); else if (infile.key == "rand_loot") items[id].rand_loot = toInt(infile.val); else if (infile.key == "rand_vendor") items[id].rand_vendor = toInt(infile.val); else if (infile.key == "pickup_status") items[id].pickup_status = infile.val; else if (infile.key == "stepfx") items[id].stepfx = infile.val; else if (infile.key == "class") { string classname = infile.nextValue(); while (classname != "") { unsigned pos; // find the position where this classname is stored: for (pos = 0; pos < item_class_names.size(); pos++) { if (item_class_names[pos] == classname) break; } // if it was not found, add it to the end. // pos is already the correct index. if (pos == item_class_names.size()) { item_class_names.push_back(classname); item_class_items.push_back(vector<unsigned int>()); } // add item id to the item list of that class: item_class_items[pos].push_back(id); classname = infile.nextValue(); } } } infile.close(); }
void loadTilesetSettings() { // reset defaults UNITS_PER_PIXEL_X = 2; UNITS_PER_PIXEL_Y = 4; TILE_W = 64; TILE_H = 32; TILE_W_HALF = TILE_W/2; TILE_H_HALF = TILE_H/2; TILESET_ISOMETRIC = 0; TILESET_ORTHOGONAL = 1; TILESET_ORIENTATION = TILESET_ISOMETRIC; FileParser infile; // load tileset settings from engine config // @CLASS Settings: Tileset config|Description of engine/tileset_config.txt if (infile.open("engine/tileset_config.txt", true, "Unable to open engine/tileset_config.txt! Defaulting to 64x32 isometric tiles.")) { while (infile.next()) { if (infile.key == "tile_size") { // @ATTR tile_size|w (integet), h (integer)|The width and height of a tile. TILE_W = static_cast<unsigned short>(toInt(infile.nextValue())); TILE_H = static_cast<unsigned short>(toInt(infile.nextValue())); TILE_W_HALF = TILE_W /2; TILE_H_HALF = TILE_H /2; } else if (infile.key == "orientation") { // @ATTR orientation|[isometric, orthogonal]|The perspective of tiles; isometric or orthogonal. if (infile.val == "isometric") TILESET_ORIENTATION = TILESET_ISOMETRIC; else if (infile.val == "orthogonal") TILESET_ORIENTATION = TILESET_ORTHOGONAL; } else { infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } // Init automatically calculated parameters if (TILESET_ORIENTATION == TILESET_ISOMETRIC) { if (TILE_W > 0 && TILE_H > 0) { UNITS_PER_PIXEL_X = 2.0f / TILE_W; UNITS_PER_PIXEL_Y = 2.0f / TILE_H; } else { logError("Settings: Tile dimensions must be greater than 0. Resetting to the default size of 64x32."); TILE_W = 64; TILE_H = 32; } } else { // TILESET_ORTHOGONAL if (TILE_W > 0 && TILE_H > 0) { UNITS_PER_PIXEL_X = 1.0f / TILE_W; UNITS_PER_PIXEL_Y = 1.0f / TILE_H; } else { logError("Settings: Tile dimensions must be greater than 0. Resetting to the default size of 64x32."); TILE_W = 64; TILE_H = 32; } } if (UNITS_PER_PIXEL_X == 0 || UNITS_PER_PIXEL_Y == 0) { logError("Settings: One of UNITS_PER_PIXEL values is zero! %dx%d", (int)UNITS_PER_PIXEL_X, (int)UNITS_PER_PIXEL_Y); Exit(1); } }
void loadMiscSettings() { // reset to defaults ELEMENTS.clear(); EQUIP_FLAGS.clear(); HERO_CLASSES.clear(); FRAME_W = 0; FRAME_H = 0; IGNORE_TEXTURE_FILTER = false; ICON_SIZE = 0; AUTOPICKUP_CURRENCY = false; MAX_ABSORB = 90; MAX_RESIST = 90; MAX_BLOCK = 100; MAX_AVOIDANCE = 99; MIN_ABSORB = 0; MIN_RESIST = 0; MIN_BLOCK = 0; MIN_AVOIDANCE = 0; CURRENCY = "Gold"; VENDOR_RATIO = 0.25; DEATH_PENALTY = true; DEATH_PENALTY_PERMADEATH = false; DEATH_PENALTY_CURRENCY = 50; DEATH_PENALTY_XP = 0; DEATH_PENALTY_XP_CURRENT = 0; DEATH_PENALTY_ITEM = false; MENUS_PAUSE = false; SAVE_HPMP = false; ENABLE_PLAYGAME = false; CORPSE_TIMEOUT = 60*MAX_FRAMES_PER_SEC; SELL_WITHOUT_VENDOR = true; AIM_ASSIST = 0; SAVE_PREFIX = ""; WINDOW_TITLE = "Flare"; SOUND_FALLOFF = 15; PARTY_EXP_PERCENTAGE = 100; ENABLE_ALLY_COLLISION_AI = true; ENABLE_ALLY_COLLISION = true; CURRENCY_ID = 1; INTERACT_RANGE = 3; SAVE_ONLOAD = true; SAVE_ONEXIT = true; TOOLTIP_OFFSET = 0; TOOLTIP_WIDTH = 1; TOOLTIP_MARGIN = 0; TOOLTIP_MARGIN_NPC = 0; FileParser infile; // @CLASS Settings: Misc|Description of engine/misc.txt if (infile.open("engine/misc.txt")) { while (infile.next()) { // @ATTR save_hpmp|boolean|When saving the game, keep the hero's current HP and MP. if (infile.key == "save_hpmp") SAVE_HPMP = toBool(infile.val); // @ATTR corpse_timeout|duration|Duration that a corpse can exist on the map in 'ms' or 's'. else if (infile.key == "corpse_timeout") CORPSE_TIMEOUT = parse_duration(infile.val); // @ATTR sell_without_vendor|boolean|Allows selling items when not at a vendor via CTRL-Click. else if (infile.key == "sell_without_vendor") SELL_WITHOUT_VENDOR = toBool(infile.val); // @ATTR aim_assist|integer|The pixel offset for powers that use aim_assist. else if (infile.key == "aim_assist") AIM_ASSIST = toInt(infile.val); // @ATTR window_title|string|Sets the text in the window's titlebar. else if (infile.key == "window_title") WINDOW_TITLE = infile.val; // @ATTR save_prefix|string|A string that's prepended to save filenames to prevent conflicts between mods. else if (infile.key == "save_prefix") SAVE_PREFIX = infile.val; // @ATTR sound_falloff|integer|The maximum radius in tiles that any single sound is audible. else if (infile.key == "sound_falloff") SOUND_FALLOFF = toInt(infile.val); // @ATTR party_exp_percentage|integer|The percentage of XP given to allies. else if (infile.key == "party_exp_percentage") PARTY_EXP_PERCENTAGE = toInt(infile.val); // @ATTR enable_ally_collision|boolean|Allows allies to block the player's path. else if (infile.key == "enable_ally_collision") ENABLE_ALLY_COLLISION = toBool(infile.val); // @ATTR enable_ally_collision_ai|boolean|Allows allies to block the path of other AI creatures. else if (infile.key == "enable_ally_collision_ai") ENABLE_ALLY_COLLISION_AI = toBool(infile.val); else if (infile.key == "currency_id") { // @ATTR currency_id|integer|An item id that will be used as currency. CURRENCY_ID = toInt(infile.val); if (CURRENCY_ID < 1) { CURRENCY_ID = 1; logError("Settings: Currency ID below the minimum allowed value. Resetting it to %d", CURRENCY_ID); } } // @ATTR interact_range|float|Distance where the player can interact with objects and NPCs. else if (infile.key == "interact_range") INTERACT_RANGE = toFloat(infile.val); // @ATTR menus_pause|boolean|Opening any menu will pause the game. else if (infile.key == "menus_pause") MENUS_PAUSE = toBool(infile.val); // @ATTR save_onload|boolean|Save the game upon changing maps. else if (infile.key == "save_onload") SAVE_ONLOAD = toBool(infile.val); // @ATTR save_onexit|boolean|Save the game upon quitting to the title screen or desktop. else if (infile.key == "save_onexit") SAVE_ONEXIT = toBool(infile.val); else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } if (SAVE_PREFIX == "") { logError("Settings: save_prefix not found in engine/misc.txt, setting to 'default'. This may cause save file conflicts between games that have no save_prefix."); SAVE_PREFIX = "default"; } // @CLASS Settings: Resolution|Description of engine/resolutions.txt if (infile.open("engine/resolutions.txt")) { while (infile.next()) { // @ATTR menu_frame_width|integer|Width of frame for New Game, Configuration, etc. menus. if (infile.key == "menu_frame_width") FRAME_W = static_cast<unsigned short>(toInt(infile.val)); // @ATTR menu_frame_height|integer|Height of frame for New Game, Configuration, etc. menus. else if (infile.key == "menu_frame_height") FRAME_H = static_cast<unsigned short>(toInt(infile.val)); // @ATTR icon_size|integer|Size of icons. else if (infile.key == "icon_size") ICON_SIZE = static_cast<unsigned short>(toInt(infile.val)); // @ATTR required_width|integer|Minimum window/screen resolution width. else if (infile.key == "required_width") { MIN_SCREEN_W = static_cast<unsigned short>(toInt(infile.val)); } // @ATTR required_height|integer|Minimum window/screen resolution height. else if (infile.key == "required_height") { MIN_SCREEN_H = static_cast<unsigned short>(toInt(infile.val)); } // @ATTR virtual_height|integer|The height (in pixels) of the game's actual rendering area. The width will be resized to match the window's aspect ration, and everything will be scaled up to fill the window. else if (infile.key == "virtual_height") { VIEW_H = static_cast<unsigned short>(toInt(infile.val)); VIEW_H_HALF = VIEW_H / 2; } // @ATTR ignore_texture_filter|boolean|If true, this ignores the "Texture Filtering" video setting and uses only nearest-neighbor scaling. This is good for games that use pixel art assets. else if (infile.key == "ignore_texture_filter") { IGNORE_TEXTURE_FILTER = toBool(infile.val); } else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } // prevent the window from being too small if (SCREEN_W < MIN_SCREEN_W) SCREEN_W = MIN_SCREEN_W; if (SCREEN_H < MIN_SCREEN_H) SCREEN_H = MIN_SCREEN_H; // set the default virtual height if it's not defined if (VIEW_H == 0) { logError("Settings: virtual_height is undefined. Setting it to %d.", MIN_SCREEN_H); VIEW_H = MIN_SCREEN_H; VIEW_H_HALF = VIEW_H / 2; } // @CLASS Settings: Gameplay|Description of engine/gameplay.txt if (infile.open("engine/gameplay.txt")) { while (infile.next()) { if (infile.key == "enable_playgame") { // @ATTR enable_playgame|boolean|Enables the "Play Game" button on the main menu. ENABLE_PLAYGAME = toBool(infile.val); } else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } // @CLASS Settings: Combat|Description of engine/combat.txt if (infile.open("engine/combat.txt")) { while (infile.next()) { // @ATTR max_absorb_percent|integer|Maximum percentage of damage that can be absorbed. if (infile.key == "max_absorb_percent") MAX_ABSORB = static_cast<short>(toInt(infile.val)); // @ATTR max_resist_percent|integer|Maximum percentage of elemental damage that can be resisted. else if (infile.key == "max_resist_percent") MAX_RESIST = static_cast<short>(toInt(infile.val)); // @ATTR max_block_percent|integer|Maximum percentage of damage that can be blocked. else if (infile.key == "max_block_percent") MAX_BLOCK = static_cast<short>(toInt(infile.val)); // @ATTR max_avoidance_percent|integer|Maximum percentage chance that hazards can be avoided. else if (infile.key == "max_avoidance_percent") MAX_AVOIDANCE = static_cast<short>(toInt(infile.val)); // @ATTR min_absorb_percent|integer|Minimum percentage of damage that can be absorbed. else if (infile.key == "min_absorb_percent") MIN_ABSORB = static_cast<short>(toInt(infile.val)); // @ATTR min_resist_percent|integer|Minimum percentage of elemental damage that can be resisted. else if (infile.key == "min_resist_percent") MIN_RESIST = static_cast<short>(toInt(infile.val)); // @ATTR min_block_percent|integer|Minimum percentage of damage that can be blocked. else if (infile.key == "min_block_percent") MIN_BLOCK = static_cast<short>(toInt(infile.val)); // @ATTR min_avoidance_percent|integer|Minimum percentage chance that hazards can be avoided. else if (infile.key == "min_avoidance_percent") MIN_AVOIDANCE = static_cast<short>(toInt(infile.val)); else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } // @CLASS Settings: Elements|Description of engine/elements.txt if (infile.open("engine/elements.txt")) { while (infile.next()) { if (infile.new_section) { if (infile.section == "element") { // check if the previous element and remove it if there is no identifier if (!ELEMENTS.empty() && ELEMENTS.back().id == "") { ELEMENTS.pop_back(); } ELEMENTS.resize(ELEMENTS.size()+1); } } if (ELEMENTS.empty() || infile.section != "element") continue; // @ATTR element.id|string|An identifier for this element. if (infile.key == "id") ELEMENTS.back().id = infile.val; // @ATTR element.name|string|The displayed name of this element. else if (infile.key == "name") ELEMENTS.back().name = infile.val; else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); // check if the last element and remove it if there is no identifier if (!ELEMENTS.empty() && ELEMENTS.back().id == "") { ELEMENTS.pop_back(); } } // @CLASS Settings: Equip flags|Description of engine/equip_flags.txt if (infile.open("engine/equip_flags.txt")) { while (infile.next()) { if (infile.new_section) { if (infile.section == "flag") { // check if the previous flag and remove it if there is no identifier if (!EQUIP_FLAGS.empty() && EQUIP_FLAGS.back().id == "") { EQUIP_FLAGS.pop_back(); } EQUIP_FLAGS.resize(EQUIP_FLAGS.size()+1); } } if (EQUIP_FLAGS.empty() || infile.section != "flag") continue; // @ATTR flag.id|string|An identifier for this equip flag. if (infile.key == "id") EQUIP_FLAGS.back().id = infile.val; // @ATTR flag.name|string|The displayed name of this equip flag. else if (infile.key == "name") EQUIP_FLAGS.back().name = infile.val; else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); // check if the last flag and remove it if there is no identifier if (!EQUIP_FLAGS.empty() && EQUIP_FLAGS.back().id == "") { EQUIP_FLAGS.pop_back(); } } // @CLASS Settings: Classes|Description of engine/classes.txt if (infile.open("engine/classes.txt")) { while (infile.next()) { if (infile.new_section) { if (infile.section == "class") { // check if the previous class and remove it if there is no name if (!HERO_CLASSES.empty() && HERO_CLASSES.back().name == "") { HERO_CLASSES.pop_back(); } HERO_CLASSES.resize(HERO_CLASSES.size()+1); } } if (HERO_CLASSES.empty() || infile.section != "class") continue; if (!HERO_CLASSES.empty()) { // @ATTR name|string|The displayed name of this class. if (infile.key == "name") HERO_CLASSES.back().name = infile.val; // @ATTR description|string|A description of this class. else if (infile.key == "description") HERO_CLASSES.back().description = infile.val; // @ATTR currency|integer|The amount of currency this class will start with. else if (infile.key == "currency") HERO_CLASSES.back().currency = toInt(infile.val); // @ATTR equipment|item (integer), ...|A list of items that are equipped when starting with this class. else if (infile.key == "equipment") HERO_CLASSES.back().equipment = infile.val; // @ATTR physical|integer|Class starts with this physical stat. else if (infile.key == "physical") HERO_CLASSES.back().physical = toInt(infile.val); // @ATTR mental|integer|Class starts with this mental stat. else if (infile.key == "mental") HERO_CLASSES.back().mental = toInt(infile.val); // @ATTR offense|integer|Class starts with this offense stat. else if (infile.key == "offense") HERO_CLASSES.back().offense = toInt(infile.val); // @ATTR defense|integer|Class starts with this defense stat. else if (infile.key == "defense") HERO_CLASSES.back().defense = toInt(infile.val); else if (infile.key == "actionbar") { // @ATTR actionbar|power (integer), ...|A list of powers to place in the action bar for the class. for (int i=0; i<12; i++) { HERO_CLASSES.back().hotkeys[i] = toInt(infile.nextValue()); } } else if (infile.key == "powers") { // @ATTR powers|power (integer), ...|A list of powers that are unlocked when starting this class. std::string power; while ( (power = infile.nextValue()) != "") { HERO_CLASSES.back().powers.push_back(toInt(power)); } } else if (infile.key == "campaign") { // @ATTR campaign|status (string), ...|A list of campaign statuses that are set when starting this class. std::string status; while ( (status = infile.nextValue()) != "") { HERO_CLASSES.back().statuses.push_back(status); } } // @ATTR power_tree|string|Power tree that will be loaded by MenuPowers else if (infile.key == "power_tree") HERO_CLASSES.back().power_tree = infile.val; else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); // check if the last class and remove it if there is no name if (!HERO_CLASSES.empty() && HERO_CLASSES.back().name == "") { HERO_CLASSES.pop_back(); } } // Make a default hero class if none were found if (HERO_CLASSES.empty()) { HeroClass c; c.name = "Adventurer"; msg->get("Adventurer"); // this is needed for translation HERO_CLASSES.push_back(c); } // @CLASS Settings: Death penalty|Description of engine/death_penalty.txt if (infile.open("engine/death_penalty.txt")) { while (infile.next()) { // @ATTR enable|boolean|Enable the death penalty. if (infile.key == "enable") DEATH_PENALTY = toBool(infile.val); // @ATTR permadeath|boolean|Force permadeath for all new saves. else if (infile.key == "permadeath") DEATH_PENALTY_PERMADEATH = toBool(infile.val); // @ATTR currency|integer|Remove this percentage of currency. else if (infile.key == "currency") DEATH_PENALTY_CURRENCY = toInt(infile.val); // @ATTR xp_total|integer|Remove this percentage of total XP. else if (infile.key == "xp_total") DEATH_PENALTY_XP = toInt(infile.val); // @ATTR xp_current_level|integer|Remove this percentage of the XP gained since the last level. else if (infile.key == "xp_current_level") DEATH_PENALTY_XP_CURRENT = toInt(infile.val); // @ATTR random_item|integer|Removes a random item from the player's inventory. else if (infile.key == "random_item") DEATH_PENALTY_ITEM = toBool(infile.val); else infile.error("Settings: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } // @CLASS Settings: Tooltips|Description of engine/tooltips.txt if (infile.open("engine/tooltips.txt")) { while (infile.next()) { // @ATTR tooltip_offset|integer|Offset in pixels from the origin point (usually mouse cursor). if (infile.key == "tooltip_offset") TOOLTIP_OFFSET = toInt(infile.val); // @ATTR tooltip_width|integer|Maximum width of tooltip in pixels. else if (infile.key == "tooltip_width") TOOLTIP_WIDTH = toInt(infile.val); // @ATTR tooltip_margin|integer|Padding between the text and the tooltip borders. else if (infile.key == "tooltip_margin") TOOLTIP_MARGIN = toInt(infile.val); // @ATTR npc_tooltip_margin|integer|Vertical offset for NPC labels. else if (infile.key == "npc_tooltip_margin") TOOLTIP_MARGIN_NPC = toInt(infile.val); } infile.close(); } // @CLASS Settings: Loot|Description of engine/loot.txt if (infile.open("engine/loot.txt")) { while (infile.next()) { if (infile.key == "currency_name") { // @ATTR currency_name|string|Define the name of currency in game CURRENCY = msg->get(infile.val); } } infile.close(); } }
bool GameStateCutscene::load(std::string filename) { FileParser infile; // @CLASS Cutscene|Description of cutscenes in cutscenes/ if (!infile.open(filename)) return false; // parse the cutscene file while (infile.next()) { if (infile.new_section) { if (infile.section == "scene") scenes.push(Scene()); } if (infile.section.empty()) { // allow having an empty section (globals such as scale_gfx might be set here } else if (infile.section == "scene") { SceneComponent sc = SceneComponent(); if (infile.key == "caption") { // @ATTR scene.caption|string|A caption that will be shown. sc.type = infile.key; sc.s = msg->get(infile.val); } else if (infile.key == "image") { // @ATTR scene.image|string|An image that will be shown. sc.type = infile.key; sc.s = infile.val; } else if (infile.key == "pause") { // @ATTR scene.pause|duration|Pause before next component sc.type = infile.key; sc.x = parse_duration(infile.val); } else if (infile.key == "soundfx") { // @ATTR scene.soundfx|string|A sound that will be played sc.type = infile.key; sc.s = infile.val; } if (sc.type != "") scenes.back().components.push(sc); } else { fprintf(stderr, "unknown section %s in file %s\n", infile.section.c_str(), infile.getFileName().c_str()); } if (infile.key == "scale_gfx") { // @ATTR scale_gfx|bool|The graphics will be scaled to fit screen width scale_graphics = toBool(infile.val); } else if (infile.key == "caption_margins") { // @ATTR caption_margins|[x,y]|Percentage-based margins for the caption text based on screen size caption_margins.x = toFloat(infile.nextValue())/100.0f; caption_margins.y = toFloat(infile.val)/100.0f; } } if (scenes.empty()) { fprintf(stderr, "No scenes defined in cutscene file %s\n", filename.c_str()); return false; } return true; }
/** * NPCs are stored in simple config files * * @param npc_id Config file loaded at npcs/[npc_id].txt */ void NPC::load(const string& npc_id, int hero_level) { FileParser infile; ItemStack stack; string filename_portrait = ""; if (infile.open("npcs/" + npc_id + ".txt")) { while (infile.next()) { if (infile.section == "dialog") { if (infile.new_section) { dialog.push_back(vector<Event_Component>()); } Event_Component e; e.type = infile.key; if (infile.key == "requires_status") e.s = infile.val; else if (infile.key == "requires_not") e.s = infile.val; else if (infile.key == "requires_level") e.x = toInt(infile.val); else if (infile.key == "requires_not_level") e.x = toInt(infile.val); else if (infile.key == "requires_item") e.x = toInt(infile.val); else if (infile.key == "him" || infile.key == "her") e.s = msg->get(infile.val); else if (infile.key == "you") e.s = msg->get(infile.val); else if (infile.key == "reward_item") { // id,count e.x = toInt(infile.nextValue()); e.y = toInt(infile.val); } else if (infile.key == "reward_xp") e.x = toInt(infile.val); else if (infile.key == "restore") e.s = infile.val; else if (infile.key == "reward_currency") e.x = toInt(infile.val); else if (infile.key == "remove_item") e.x = toInt(infile.val); else if (infile.key == "set_status") e.s = infile.val; else if (infile.key == "unset_status") e.s = infile.val; else if (infile.key == "voice") { e.x = loadSound(infile.val, NPC_VOX_QUEST); } else if (infile.key == "topic") { e.s = msg->get(infile.val); } else if (infile.key == "group") { e.s = infile.val; } dialog.back().push_back(e); } else { filename = npc_id; if (infile.key == "name") { name = msg->get(infile.val); } else if (infile.key == "level") { if (infile.val == "hero") level = hero_level; else level = toInt(infile.val); } else if (infile.key == "gfx") { gfx = infile.val; } // handle talkers else if (infile.key == "talker") { if (infile.val == "true") talker = true; } else if (infile.key == "portrait") { filename_portrait = infile.val; } // handle vendors else if (infile.key == "vendor") { if (infile.val == "true") vendor = true; } else if (infile.key == "constant_stock") { stack.quantity = 1; while (infile.val != "") { stack.item = toInt(infile.nextValue()); stock.add(stack); } } else if (infile.key == "status_stock") { if (camp->checkStatus(infile.nextValue())) { stack.quantity = 1; while (infile.val != "") { stack.item = toInt(infile.nextValue()); stock.add(stack); } } } // handle vocals else if (infile.key == "vox_intro") { loadSound(infile.val, NPC_VOX_INTRO); } } } infile.close(); } loadGraphics(filename_portrait); }
/** * When loading the game, load from file if possible */ void GameStatePlay::loadGame() { // game slots are currently 1-4 if (game_slot == 0) return; FileParser infile; int hotkeys[12]; for (int i=0; i<12; i++) { hotkeys[i] = -1; } stringstream ss; ss.str(""); ss << PATH_USER << "save" << game_slot << ".txt"; if (infile.open(ss.str())) { while (infile.next()) { if (infile.key == "name") pc->stats.name = infile.val; else if (infile.key == "permadeath") { pc->stats.permadeath = atoi(infile.val.c_str()); } else if (infile.key == "option") { pc->stats.base = infile.nextValue(); pc->stats.head = infile.nextValue(); pc->stats.portrait = infile.nextValue(); } else if (infile.key == "xp") pc->stats.xp = atoi(infile.val.c_str()); else if (infile.key == "build") { pc->stats.physical_character = atoi(infile.nextValue().c_str()); pc->stats.mental_character = atoi(infile.nextValue().c_str()); pc->stats.offense_character = atoi(infile.nextValue().c_str()); pc->stats.defense_character = atoi(infile.nextValue().c_str()); } else if (infile.key == "gold") { menu->inv->gold = atoi(infile.val.c_str()); } else if (infile.key == "equipped") { menu->inv->inventory[EQUIPMENT].setItems(infile.val); } else if (infile.key == "equipped_quantity") { menu->inv->inventory[EQUIPMENT].setQuantities(infile.val); } else if (infile.key == "carried") { menu->inv->inventory[CARRIED].setItems(infile.val); } else if (infile.key == "carried_quantity") { menu->inv->inventory[CARRIED].setQuantities(infile.val); } else if (infile.key == "spawn") { map->teleport_mapname = infile.nextValue(); if (fileExists(mods->locate("maps/" + map->teleport_mapname))) { map->teleport_destination.x = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; map->teleport_destination.y = atoi(infile.nextValue().c_str()) * UNITS_PER_TILE + UNITS_PER_TILE/2; map->teleportation = true; // prevent spawn.txt from putting us on the starting map map->clearEvents(); } else { map->teleport_mapname = "spawn.txt"; map->teleport_destination.x = 1; map->teleport_destination.y = 1; map->teleportation = true; } } else if (infile.key == "actionbar") { for (int i=0; i<12; i++) hotkeys[i] = atoi(infile.nextValue().c_str()); menu->act->set(hotkeys); } else if (infile.key == "transformed") { pc->stats.transform_type = infile.nextValue().c_str(); if (pc->stats.transform_type != "") pc->stats.transform_duration = -1; } else if (infile.key == "campaign") camp->setAll(infile.val); } infile.close(); } // initialize vars pc->stats.recalc(); menu->inv->applyEquipment(menu->inv->inventory[EQUIPMENT].storage); pc->stats.hp = pc->stats.maxhp; pc->stats.mp = pc->stats.maxmp; // reset character menu menu->chr->refreshStats(); // just for aesthetics, turn the hero to face the camera pc->stats.direction = 6; // set up MenuTalker for this hero menu->talker->setHero(pc->stats.name, pc->stats.portrait); // load sounds (gender specific) pc->loadSounds(); }
void ItemManager::loadSets() { FileParser infile; // @CLASS Item Set|Definition of a item sets, items/sets.txt... if (!infile.open("items/sets.txt", true, false)) return; 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); ensureFitsId(item_sets, id+1); } else id_line = false; if (id < 1) { if (id_line) fprintf(stderr, "Item set index out of bounds 1-%d, skipping\n", 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 name|[item_id,...]|List of item id's that is part of the set. 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); const char* cname = item_sets[id].name.c_str(); fprintf(stderr, "Item index inside item set %s definition out of bounds 1-%d, skipping item\n", cname, maxsize); } item_id = infile.nextValue(); } } else if (infile.key == "color") { // @ATTR color|color|A specific of color for the set. item_sets[id].color.r = toInt(infile.nextValue()); item_sets[id].color.g = toInt(infile.nextValue()); item_sets[id].color.b = toInt(infile.nextValue()); } else if (infile.key == "bonus") { // @ATTR bonus|[requirements (integer), bonus stat (string), bonus (integer)]|Bonus to append to items in the set. Set_bonus bonus; bonus.requirement = toInt(infile.nextValue()); bonus.bonus_stat = infile.nextValue(); bonus.bonus_val = toInt(infile.nextValue()); item_sets[id].bonus.push_back(bonus); } } infile.close(); }
void TileSet::load(const std::string& filename) { if (current_map == filename) return; reset(); FileParser infile; // @CLASS TileSet|Description of tilesets in tilesets/ if (infile.open(filename)) { while (infile.next()) { if (infile.key == "img") { // @ATTR img|string|Filename of a tile sheet image. loadGraphics(infile.val); } else if (infile.key == "tile") { // @ATTR tile|index (integer), x (integer), y (integer), w (integer), h (integer), x offset (integer), y offset (integer)|A single tile definition. // Verify that we have graphics for tiles if (!sprites) { std::cerr << "No graphics for tileset definition '" << filename << "', aborting." << std::endl; Exit(0); } unsigned index = popFirstInt(infile.val); if (index >= tiles.size()) tiles.resize(index + 1); tiles[index].tile = sprites->getGraphics()->createSprite(); tiles[index].tile->setClipX(popFirstInt(infile.val)); tiles[index].tile->setClipY(popFirstInt(infile.val)); tiles[index].tile->setClipW(popFirstInt(infile.val)); tiles[index].tile->setClipH(popFirstInt(infile.val)); tiles[index].offset.x = popFirstInt(infile.val); tiles[index].offset.y = popFirstInt(infile.val); max_size_x = std::max(max_size_x, (tiles[index].tile->getClip().w / TILE_W) + 1); max_size_y = std::max(max_size_y, (tiles[index].tile->getClip().h / TILE_H) + 1); } else if (infile.key == "transparency") { // @ATTR transparency|r (integer), g (integer), b (integer)|An RGB color to key out and treat as transparent. alpha_background = false; trans_r = (Uint8)popFirstInt(infile.val); trans_g = (Uint8)popFirstInt(infile.val); trans_b = (Uint8)popFirstInt(infile.val); } else if (infile.key == "animation") { // @ATTR animation|tile index (integer), x (integer), y (integer), duration (duration), ...|An animation for a tile. Durations are in 'ms' or 's'. int frame = 0; unsigned TILE_ID = toInt(infile.nextValue()); if (TILE_ID >= anim.size()) anim.resize(TILE_ID + 1); std::string repeat_val = infile.nextValue(); while (repeat_val != "") { anim[TILE_ID].frames++; anim[TILE_ID].pos.resize(frame + 1); anim[TILE_ID].frame_duration.resize(frame + 1); anim[TILE_ID].pos[frame].x = toInt(repeat_val); anim[TILE_ID].pos[frame].y = toInt(infile.nextValue()); anim[TILE_ID].frame_duration[frame] = parse_duration(infile.nextValue()); frame++; repeat_val = infile.nextValue(); } } else { infile.error("TileSet: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } current_map = filename; }