void GameSwitcher::loadFPS() { // Load FPS rendering settings FileParser infile; // @CLASS GameSwitcher: FPS counter|Description of menus/fps.txt if (infile.open("menus/fps.txt")) { while(infile.next()) { // @ATTR position|x (integer), y (integer), align (alignment)|Position of the fps counter. if(infile.key == "position") { fps_position.x = popFirstInt(infile.val); fps_position.y = popFirstInt(infile.val); fps_corner = parse_alignment(popFirstString(infile.val)); } // @ATTR color|r (integer), g (integer), b (integer)|Color of the fps counter text. else if(infile.key == "color") { fps_color = toRGB(infile.val); } else { infile.error("GameSwitcher: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } // this is a dummy string used to approximate the fps position when aligned to the right font->setFont("font_regular"); fps_position.w = font->calc_width("00 fps"); fps_position.h = font->getLineHeight(); // Delete the label object if it exists (we'll recreate this with showFPS()) if (label_fps) { delete label_fps; label_fps = NULL; } }
void Map::loadNPC(FileParser &infile) { std::string s; if (infile.key == "type") { // @ATTR npc.type|string|(IGNORED BY ENGINE) The "type" field, as used by Tiled and other mapping tools. npcs.back().type = infile.val; } else if (infile.key == "filename") { // @ATTR npc.filename|string|Filename of an NPC definition. npcs.back().id = infile.val; } else if (infile.key == "requires_status") { // @ATTR npc.requires_status|list(string)|Status required for NPC load. There can be multiple states, separated by comma while ( (s = popFirstString(infile.val)) != "") npcs.back().requires_status.push_back(s); } else if (infile.key == "requires_not_status") { // @ATTR npc.requires_not_status|list(string)|Status required to be missing for NPC load. There can be multiple states, separated by comma while ( (s = popFirstString(infile.val)) != "") npcs.back().requires_not_status.push_back(s); } else if (infile.key == "location") { // @ATTR npc.location|point|Location of NPC npcs.back().pos.x = static_cast<float>(popFirstInt(infile.val)) + 0.5f; npcs.back().pos.y = static_cast<float>(popFirstInt(infile.val)) + 0.5f; } else { infile.error("Map: '%s' is not a valid key.", infile.key.c_str()); } }
void GameStateConfigBase::readConfig() { //Load the menu configuration from file FileParser infile; if (infile.open("menus/config.txt")) { while (infile.next()) { if (parseKeyButtons(infile)) continue; int x1 = popFirstInt(infile.val); int y1 = popFirstInt(infile.val); int x2 = popFirstInt(infile.val); int y2 = popFirstInt(infile.val); if (parseKey(infile, x1, y1, x2, y2)) continue; else if (parseStub(infile)) continue; else { infile.error("GameStateConfigBase: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } }
void LootManager::loadLootTables() { std::vector<std::string> filenames = mods->list("loot", false); for (unsigned i=0; i<filenames.size(); i++) { FileParser infile; if (!infile.open(filenames[i])) continue; std::vector<Event_Component> *ec_list = &loot_tables[filenames[i]]; Event_Component *ec = NULL; bool skip_to_next = false; while (infile.next()) { if (infile.section == "") { if (infile.key == "loot") { ec_list->push_back(Event_Component()); ec = &ec_list->back(); parseLoot(infile.val, ec, ec_list); } } else if (infile.section == "loot") { if (infile.new_section) { ec_list->push_back(Event_Component()); ec = &ec_list->back(); ec->type = EC_LOOT; skip_to_next = false; } if (skip_to_next || ec == NULL) continue; if (infile.key == "id") { ec->s = infile.val; if (ec->s == "currency") ec->c = CURRENCY_ID; else if (toInt(ec->s, -1) != -1) ec->c = toInt(ec->s); else { skip_to_next = true; infile.error("LootManager: Invalid item id for loot."); } } else if (infile.key == "chance") { if (infile.val == "fixed") ec->z = 0; else ec->z = toInt(infile.val); } else if (infile.key == "quantity") { ec->a = std::max(popFirstInt(infile.val), 1); ec->b = std::max(popFirstInt(infile.val), ec->a); } } } infile.close(); } }
void Map::loadHeader(FileParser &infile) { if (infile.key == "title") { // @ATTR title|string|Title of map this->title = msg->get(infile.val); } else if (infile.key == "width") { // @ATTR width|int|Width of map this->w = static_cast<unsigned short>(std::max(toInt(infile.val), 1)); } else if (infile.key == "height") { // @ATTR height|int|Height of map this->h = static_cast<unsigned short>(std::max(toInt(infile.val), 1)); } else if (infile.key == "tileset") { // @ATTR tileset|filename|Filename of a tileset definition to use for map this->tileset = infile.val; } else if (infile.key == "music") { // @ATTR music|filename|Filename of background music to use for map music_filename = infile.val; } else if (infile.key == "hero_pos") { // @ATTR hero_pos|point|The player will spawn in this location if no point was previously given. hero_pos.x = static_cast<float>(popFirstInt(infile.val)) + 0.5f; hero_pos.y = static_cast<float>(popFirstInt(infile.val)) + 0.5f; hero_pos_enabled = true; } else if (infile.key == "parallax_layers") { // @ATTR parallax_layers|filename|Filename of a parallax layers definition. parallax_filename = infile.val; } else if (infile.key == "background_color") { // @ATTR background_color|color, int : Color, alpha|Background color for the map. background_color = toRGBA(infile.val); } else if (infile.key == "tilewidth") { // @ATTR tilewidth|int|Inherited from Tiled map file. Unused by engine. } else if (infile.key == "tileheight") { // @ATTR tileheight|int|Inherited from Tiled map file. Unused by engine. } else if (infile.key == "orientation") { // this is only used by Tiled when importing Flare maps } else { infile.error("Map: '%s' is not a valid key.", infile.key.c_str()); } }
/** * This is used in menus (e.g. MenuInventory) when parsing their config files */ LabelInfo eatLabelInfo(std::string val) { LabelInfo info; std::string justify,valign,style; std::string tmp = popFirstString(val); if (tmp == "hidden") { info.hidden = true; } else { info.hidden = false; info.x = toInt(tmp); info.y = popFirstInt(val); justify = popFirstString(val); valign = popFirstString(val); style = popFirstString(val); if (justify == "left") info.justify = JUSTIFY_LEFT; else if (justify == "center") info.justify = JUSTIFY_CENTER; else if (justify == "right") info.justify = JUSTIFY_RIGHT; if (valign == "top") info.valign = VALIGN_TOP; else if (valign == "center") info.valign = VALIGN_CENTER; else if (valign == "bottom") info.valign = VALIGN_BOTTOM; if (style != "") info.font_style = style; } return info; }
SDLFontEngine::SDLFontEngine() : FontEngine(), active_font(NULL) { // Initiate SDL_ttf if(!TTF_WasInit() && TTF_Init()==-1) { logError("SDLFontEngine: TTF_Init: %s", TTF_GetError()); Exit(2); } // load the fonts // @CLASS SDLFontEngine: Font settings|Description of engine/font_settings.txt FileParser infile; if (infile.open("engine/font_settings.txt")) { while (infile.next()) { if (infile.new_section) { SDLFontStyle f; f.name = infile.section; font_styles.push_back(f); } if (font_styles.empty()) continue; SDLFontStyle *style = &(font_styles.back()); if ((infile.key == "default" && style->path == "") || infile.key == LANGUAGE) { // @ATTR $STYLE.default, $STYLE.$LANGUAGE|filename (string), point size (integer), blending (boolean)|Filename, point size, and blend mode of the font to use for this language. $STYLE can be something like "font_normal" or "font_bold". $LANGUAGE can be a 2-letter region code. style->path = popFirstString(infile.val); style->ptsize = popFirstInt(infile.val); style->blend = toBool(popFirstString(infile.val)); style->ttfont = TTF_OpenFont(mods->locate("fonts/" + style->path).c_str(), style->ptsize); if(style->ttfont == NULL) { logError("FontEngine: TTF_OpenFont: %s", TTF_GetError()); } else { int lineskip = TTF_FontLineSkip(style->ttfont); style->line_height = lineskip; style->font_height = lineskip; } } } infile.close(); } // set the font colors Color color; if (infile.open("engine/font_colors.txt")) { while (infile.next()) { // @ATTR menu_normal, menu_bonus, menu_penalty, widget_normal, widget_disabled|r (integer), g (integer), b (integer)|Colors for menus and widgets // @ATTR combat_givedmg, combat_takedmg, combat_crit, combat_buff, combat_miss|r (integer), g (integer), b (integer)|Colors for combat text // @ATTR requirements_not_met, item_bonus, item_penalty, item_flavor|r (integer), g (integer), b (integer)|Colors for tooltips // @ATTR item_$QUALITY|r (integer), g (integer), b (integer)|Colors for item quality. $QUALITY should match qualities used in items/items.txt color_map[infile.key] = toRGB(infile.val); } infile.close(); } // Attempt to set the default active font setFont("font_regular"); if (!active_font) { logError("FontEngine: Unable to determine default font!"); Exit(1); } }
Event_Component EventManager::getRandomMapFromFile(const std::string& fname) { // map pool is the same, so pick the next one in the "playlist" if (fname == mapr->intermap_random_filename && !mapr->intermap_random_queue.empty()) { Event_Component ec = mapr->intermap_random_queue.front(); mapr->intermap_random_queue.pop(); return ec; } // starting a new map pool, so clear the queue while (!mapr->intermap_random_queue.empty()) { mapr->intermap_random_queue.pop(); } FileParser infile; std::vector<Event_Component> ec_list; // @CLASS EventManager: Random Map List|Description of maps/random/lists/ if (infile.open(fname)) { while (infile.next()) { // @ATTR map|filename, int, int : Map file, X, Y|Adds a map and optional spawn position to the random list of maps to teleport to. if (infile.key == "map") { Event_Component ec; ec.s = popFirstString(infile.val); if (ec_list.empty() || ec.s != mapr->getFilename()) { ec.x = -1; ec.y = -1; std::string test_x = popFirstString(infile.val); if (!test_x.empty()) { ec.x = toInt(test_x); ec.y = popFirstInt(infile.val); } ec_list.push_back(ec); } } } infile.close(); } if (ec_list.empty()) { mapr->intermap_random_filename = ""; return Event_Component(); } else { mapr->intermap_random_filename = fname; while (!ec_list.empty()) { size_t index = rand() % ec_list.size(); mapr->intermap_random_queue.push(ec_list[index]); ec_list.erase(ec_list.begin() + index); } Event_Component ec = mapr->intermap_random_queue.front(); mapr->intermap_random_queue.pop(); return ec; } }
/** * Take the savefile CSV list of items quantities and convert to storage array */ void ItemStorage::setQuantities(std::string s) { s = s + ','; for (int i=0; i<slot_number; i++) { storage[i].quantity = popFirstInt(s, ','); if (storage[i].quantity < 0) { logError("ItemStorage: Items quantity on position %d is negative, setting to zero", i); storage[i].quantity = 0; } } }
void GameStateConfigDesktop::readConfig() { FileParser infile; if (infile.open("menus/config.txt")) { while (infile.next()) { int x1 = popFirstInt(infile.val); int y1 = popFirstInt(infile.val); int x2 = popFirstInt(infile.val); int y2 = popFirstInt(infile.val); if (parseKeyDesktop(infile, x1, y1, x2, y2)) continue; else if (parseKey(infile, x1, y1, x2, y2)) continue; else { infile.error("GameStateConfigDesktop: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } }
void MenuBook::loadText(FileParser &infile) { // @ATTR text.text_pos|int, int, int, ["left", "center", "right"] : X, Y, Width, Text justify|Position of the text. if (infile.key == "text_pos") { size.back().x = popFirstInt(infile.val); size.back().y = popFirstInt(infile.val); size.back().w = popFirstInt(infile.val); std::string _justify = popFirstString(infile.val); if (_justify == "left") justify.back() = JUSTIFY_LEFT; else if (_justify == "center") justify.back() = JUSTIFY_CENTER; else if (_justify == "right") justify.back() = JUSTIFY_RIGHT; } // @ATTR text.text_font|color, string : Font color, Font style|Font color and style. else if (infile.key == "text_font") { Color color; color.r = static_cast<Uint8>(popFirstInt(infile.val)); color.g = static_cast<Uint8>(popFirstInt(infile.val)); color.b = static_cast<Uint8>(popFirstInt(infile.val)); textColor.back() = color; textFont.back() = popFirstString(infile.val); } // @ATTR text.text|string|The text to be displayed. else if (infile.key == "text") { // we use substr here to remove the comma from the end textData.back() = msg->get(infile.val.substr(0, infile.val.length() - 1)); } else { infile.error("MenuBook: '%s' is not a valid key.", infile.key.c_str()); } }
bool GameStateConfigBase::parseKeyButtons(FileParser &infile) { // @CLASS GameStateConfigBase|Description of menus/config.txt if (infile.key == "button_ok") { // @ATTR button_ok|int, int, alignment : X, Y, Alignment|Position of the "OK" button. int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); ok_button->setBasePos(x, y, a); } else if (infile.key == "button_defaults") { // @ATTR button_defaults|int, int, alignment : X, Y, Alignment|Position of the "Defaults" button. int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); defaults_button->setBasePos(x, y, a); } else if (infile.key == "button_cancel") { // @ATTR button_cancel|int, int, alignment : X, Y, Alignment|Position of the "Cancel" button. int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); cancel_button->setBasePos(x, y, a); } else { return false; } return true; }
void MenuBook::loadText(FileParser &infile) { // @ATTR text.text_pos|x (integer), y (integer), w (integer), [left:center:right]|Position of the text. if (infile.key == "text_pos") { size.back().x = popFirstInt(infile.val); size.back().y = popFirstInt(infile.val); size.back().w = popFirstInt(infile.val); std::string _justify = popFirstString(infile.val); if (_justify == "left") justify.back() = JUSTIFY_LEFT; else if (_justify == "center") justify.back() = JUSTIFY_CENTER; else if (_justify == "right") justify.back() = JUSTIFY_RIGHT; } // @ATTR text.text_font|r (integer), g (integer), b (integer), style (string)|Font color and style. else if (infile.key == "text_font") { Color color; color.r = popFirstInt(infile.val); color.g = popFirstInt(infile.val); color.b = popFirstInt(infile.val); textColor.back() = color; textFont.back() = popFirstString(infile.val); } // @ATTR text.text|string|The text to be displayed. else if (infile.key == "text") { textData.back() = infile.val; // remove comma from the end textData.back() = textData.back().substr(0, textData.back().length() - 1); } else { infile.error("MenuBook: '%s' is not a valid key.", infile.key.c_str()); } }
void MenuPowers::loadUpgrade(FileParser &infile) { // @ATTR upgrade.id|integer|A power id from powers/powers.txt for this upgrade. if (infile.key == "id") { int id = popFirstInt(infile.val); if (id > 0) { skip_section = false; power_cell_upgrade.back().id = static_cast<short>(id); } else { skip_section = true; power_cell_upgrade.pop_back(); infile.error("MenuPowers: Power index out of bounds 1-%d, skipping power.", INT_MAX); } return; } if (skip_section) return; // @ATTR upgrade.requires_physoff|integer|Upgrade requires Physical and Offense stat of this value. if (infile.key == "requires_physoff") power_cell_upgrade.back().requires_physoff = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_physdef|integer|Upgrade requires Physical and Defense stat of this value. else if (infile.key == "requires_physdef") power_cell_upgrade.back().requires_physdef = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_mentoff|integer|Upgrade requires Mental and Offense stat of this value. else if (infile.key == "requires_mentoff") power_cell_upgrade.back().requires_mentoff = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_mentdef|integer|Upgrade requires Mental and Defense stat of this value. else if (infile.key == "requires_mentdef") power_cell_upgrade.back().requires_mentdef = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_defense|integer|Upgrade requires Defense stat of this value. else if (infile.key == "requires_defense") power_cell_upgrade.back().requires_defense = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_offense|integer|Upgrade requires Offense stat of this value. else if (infile.key == "requires_offense") power_cell_upgrade.back().requires_offense = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_physical|integer|Upgrade requires Physical stat of this value. else if (infile.key == "requires_physical") power_cell_upgrade.back().requires_physical = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_mental|integer|Upgrade requires Mental stat of this value. else if (infile.key == "requires_mental") power_cell_upgrade.back().requires_mental = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_point|boolean|Upgrade requires a power point to unlock. else if (infile.key == "requires_point") power_cell_upgrade.back().requires_point = toBool(infile.val); // @ATTR upgrade.requires_level|integer|Upgrade requires at least this level for the hero. else if (infile.key == "requires_level") power_cell_upgrade.back().requires_level = static_cast<short>(toInt(infile.val)); // @ATTR upgrade.requires_power|integer|Upgrade requires another power id. else if (infile.key == "requires_power") power_cell_upgrade.back().requires_power.push_back(static_cast<short>(toInt(infile.val))); // @ATTR upgrade.visible_requires_status|string|Hide the upgrade if we don't have this campaign status. else if (infile.key == "visible_requires_status") power_cell_upgrade.back().visible_requires_status.push_back(infile.val); // @ATTR upgrade.visible_requires_not_status|string|Hide the upgrade if we have this campaign status. else if (infile.key == "visible_requires_not_status") power_cell_upgrade.back().visible_requires_not.push_back(infile.val); else infile.error("MenuPowers: '%s' is not a valid key.", infile.key.c_str()); }
void Map::loadLayer(FileParser &infile) { if (infile.key == "type") { // @ATTR layer.type|string|Map layer type. layers.resize(layers.size()+1); layers.back().resize(w); for (size_t i=0; i<layers.back().size(); ++i) { layers.back()[i].resize(h); } layernames.push_back(infile.val); if (infile.val == "collision") collision_layer = static_cast<int>(layernames.size())-1; } else if (infile.key == "format") { // @ATTR layer.format|string|Format for map layer, must be 'dec' if (infile.val != "dec") { infile.error("Map: The format of a layer must be 'dec'!"); logErrorDialog("Map: The format of a layer must be 'dec'!"); mods->resetModConfig(); Exit(1); } } else if (infile.key == "data") { // @ATTR layer.data|raw|Raw map layer data // layer map data handled as a special case // The next h lines must contain layer data. for (int j=0; j<h; j++) { std::string val = infile.getRawLine(); infile.incrementLineNum(); if (!val.empty() && val[val.length()-1] != ',') { val += ','; } // verify the width of this row int comma_count = 0; for (unsigned i=0; i<val.length(); ++i) { if (val[i] == ',') comma_count++; } if (comma_count != w) { infile.error("Map: A row of layer data has a width not equal to %d.", w); mods->resetModConfig(); Exit(1); } for (int i=0; i<w; i++) layers.back()[i][j] = static_cast<unsigned short>(popFirstInt(val)); } } else { infile.error("Map: '%s' is not a valid key.", infile.key.c_str()); } }
/** * Take the savefile CSV list of items id and convert to storage array */ void ItemStorage::setItems(std::string s) { s = s + ','; for (int i=0; i<slot_number; i++) { storage[i].item = popFirstInt(s, ','); // check if such item exists to avoid crash if savegame was modified manually if (storage[i].item < 0) { logError("ItemStorage: Item on position %d has negative id, skipping", i); storage[i].clear(); } else if (items->items.empty() || static_cast<unsigned>(storage[i].item) > items->items.size()-1) { logError("ItemStorage: Item id (%d) out of bounds 1-%d, marking as unknown", storage[i].item, static_cast<int>(items->items.size())); items->addUnknownItem(storage[i].item); } } }
void MenuPowers::loadUpgrade(FileParser &infile) { // @ATTR upgrade.id|int|A power id from powers/powers.txt for this upgrade. if (infile.key == "id") { int id = popFirstInt(infile.val); if (id > 0) { skip_section = false; power_cell_upgrade.back().id = (id); } else { skip_section = true; power_cell_upgrade.pop_back(); infile.error("MenuPowers: Power index out of bounds 1-%d, skipping power.", INT_MAX); } return; } if (skip_section) return; // @ATTR upgrade.requires_primary|predefined_string, int : Primary stat name, Required value|Upgrade requires this primary stat to be at least the specificed value. if (infile.key == "requires_primary") { std::string prim_stat = popFirstString(infile.val); size_t prim_stat_index = getPrimaryStatIndex(prim_stat); if (prim_stat_index != PRIMARY_STATS.size()) { power_cell_upgrade.back().requires_primary[prim_stat_index] = toInt(infile.val); } else { infile.error("MenuPowers: '%s' is not a valid primary stat.", prim_stat.c_str()); } } // @ATTR upgrade.requires_point|bool|Upgrade requires a power point to unlock. else if (infile.key == "requires_point") power_cell_upgrade.back().requires_point = toBool(infile.val); // @ATTR upgrade.requires_level|int|Upgrade requires at least this level for the hero. else if (infile.key == "requires_level") power_cell_upgrade.back().requires_level = toInt(infile.val); // @ATTR upgrade.requires_power|int|Upgrade requires another power id. else if (infile.key == "requires_power") power_cell_upgrade.back().requires_power.push_back(toInt(infile.val)); // @ATTR upgrade.visible_requires_status|repeatable(string)|Hide the upgrade if we don't have this campaign status. else if (infile.key == "visible_requires_status") power_cell_upgrade.back().visible_requires_status.push_back(infile.val); // @ATTR upgrade.visible_requires_not_status|repeatable(string)|Hide the upgrade if we have this campaign status. else if (infile.key == "visible_requires_not_status") power_cell_upgrade.back().visible_requires_not.push_back(infile.val); else infile.error("MenuPowers: '%s' is not a valid key.", infile.key.c_str()); }
/** * Load avatar sprite layer definitions into vector. */ void Avatar::loadLayerDefinitions() { layer_def = std::vector<std::vector<unsigned> >(8, std::vector<unsigned>()); layer_reference_order = std::vector<std::string>(); FileParser infile; // @CLASS Avatar: Hero layers|Description of engine/hero_layers.txt if (infile.open("engine/hero_layers.txt")) { while(infile.next()) { if (infile.key == "layer") { // @ATTR layer|direction (integer), string, ...]|Defines the hero avatar sprite layer unsigned dir = popFirstInt(infile.val); if (dir>7) { infile.error("Avatar: Hero layer direction must be in range [0,7]"); Exit(1); } std::string layer = popFirstString(infile.val); while (layer != "") { // check if already in layer_reference: unsigned ref_pos; for (ref_pos = 0; ref_pos < layer_reference_order.size(); ++ref_pos) if (layer == layer_reference_order[ref_pos]) break; if (ref_pos == layer_reference_order.size()) layer_reference_order.push_back(layer); layer_def[dir].push_back(ref_pos); layer = popFirstString(infile.val); } } else { infile.error("Avatar: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } // There are the positions of the items relative to layer_reference_order // so if layer_reference_order=main,body,head,off // and we got a layer=3,off,body,head,main // then the layer_def[3] looks like (3,1,2,0) }
/** * Load a class definition, index */ void GameStatePlay::loadClass(int index) { if (index < 0 || static_cast<unsigned>(index) >= HERO_CLASSES.size()) { logError("SaveLoad: Class index out of bounds."); return; } // game slots are currently 1-4 if (game_slot == 0) return; pc->stats.character_class = HERO_CLASSES[index].name; pc->stats.physical_character += HERO_CLASSES[index].physical; pc->stats.mental_character += HERO_CLASSES[index].mental; pc->stats.offense_character += HERO_CLASSES[index].offense; pc->stats.defense_character += HERO_CLASSES[index].defense; menu->inv->addCurrency(HERO_CLASSES[index].currency); menu->inv->inventory[EQUIPMENT].setItems(HERO_CLASSES[index].equipment); for (unsigned i=0; i<HERO_CLASSES[index].powers.size(); i++) { pc->stats.powers_list.push_back(HERO_CLASSES[index].powers[i]); } for (unsigned i=0; i<HERO_CLASSES[index].statuses.size(); i++) { camp->setStatus(HERO_CLASSES[index].statuses[i]); } menu->act->set(HERO_CLASSES[index].hotkeys); // Add carried items std::string carried = HERO_CLASSES[index].carried; ItemStack stack; stack.quantity = 1; while (carried != "") { stack.item = popFirstInt(carried); menu->inv->add(stack, CARRIED, -1, false); } // apply stats, inventory, and powers applyPlayerData(); // reset character menu menu->chr->refreshStats(); }
void GameStateLoad::readGameSlots() { FileParser infile; std::stringstream filename; std::vector<std::string> save_dirs; getDirList(PATH_USER + "saves/" + SAVE_PREFIX, save_dirs); std::sort(save_dirs.begin(), save_dirs.end(), compareSaveDirs); game_slots.resize(save_dirs.size(), NULL); visible_slots = (game_slot_max > static_cast<int>(game_slots.size()) ? static_cast<int>(game_slots.size()) : game_slot_max); for (size_t i=0; i<save_dirs.size(); ++i){ // save data is stored in slot#/avatar.txt filename.str(""); filename << PATH_USER << "saves/" << SAVE_PREFIX << "/" << save_dirs[i] << "/avatar.txt"; if (!infile.open(filename.str(),false)) continue; game_slots[i] = new GameSlot(); game_slots[i]->id = toInt(save_dirs[i]); while (infile.next()) { // load (key=value) pairs if (infile.key == "name") game_slots[i]->stats.name = infile.val; else if (infile.key == "class") { game_slots[i]->stats.character_class = popFirstString(infile.val); game_slots[i]->stats.character_subclass = popFirstString(infile.val); } else if (infile.key == "xp") game_slots[i]->stats.xp = toInt(infile.val); else if (infile.key == "build") { for (size_t j = 0; j < PRIMARY_STATS.size(); ++j) { game_slots[i]->stats.primary[j] = popFirstInt(infile.val); } } else if (infile.key == "equipped") { std::string repeat_val = popFirstString(infile.val); while (repeat_val != "") { game_slots[i]->equipped.push_back(toInt(repeat_val)); repeat_val = popFirstString(infile.val); } } else if (infile.key == "option") { game_slots[i]->stats.gfx_base = popFirstString(infile.val); game_slots[i]->stats.gfx_head = popFirstString(infile.val); game_slots[i]->stats.gfx_portrait = popFirstString(infile.val); } else if (infile.key == "spawn") { game_slots[i]->current_map = getMapName(popFirstString(infile.val)); } else if (infile.key == "permadeath") { game_slots[i]->stats.permadeath = toBool(infile.val); } } infile.close(); game_slots[i]->stats.recalc(); game_slots[i]->stats.direction = 6; game_slots[i]->preview.setStatBlock(&(game_slots[i]->stats)); loadPreview(game_slots[i]); } }
GameStateLoad::GameStateLoad() : GameState() , background(NULL) , selection(NULL) , portrait_border(NULL) , portrait(NULL) , loading_requested(false) , loading(false) , loaded(false) , delete_items(true) , selected_slot(-1) , visible_slots(0) , scroll_offset(0) , has_scroll_bar(false) , game_slot_max(4) , text_trim_boundary(0) { if (items == NULL) items = new ItemManager(); label_loading = new WidgetLabel(); // Confirmation box to confirm deleting confirm = new MenuConfirm(msg->get("Delete Save"), msg->get("Delete this save?")); button_exit = new WidgetButton(); button_exit->label = msg->get("Exit to Title"); button_new = new WidgetButton(); button_new->label = msg->get("New Game"); button_new->enabled = true; button_load = new WidgetButton(); button_load->label = msg->get("Choose a Slot"); button_load->enabled = false; button_delete = new WidgetButton(); button_delete->label = msg->get("Delete Save"); button_delete->enabled = false; scrollbar = new WidgetScrollBar(); // Set up tab list tablist = TabList(HORIZONTAL); tablist.add(button_exit); tablist.add(button_new); // Read positions from config file FileParser infile; // @CLASS GameStateLoad|Description of menus/gameload.txt if (infile.open("menus/gameload.txt")) { while (infile.next()) { // @ATTR button_new|int, int, alignment : X, Y, Alignment|Position of the "New Game" button. if (infile.key == "button_new") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); button_new->setBasePos(x, y, a); } // @ATTR button_load|int, int, alignment : X, Y, Alignment|Position of the "Load Game" button. else if (infile.key == "button_load") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); button_load->setBasePos(x, y, a); } // @ATTR button_delete|int, int, alignment : X, Y, Alignment|Position of the "Delete Save" button. else if (infile.key == "button_delete") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); button_delete->setBasePos(x, y, a); } // @ATTR button_exit|int, int, alignment : X, Y, Alignment|Position of the "Exit to Title" button. else if (infile.key == "button_exit") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); ALIGNMENT a = parse_alignment(popFirstString(infile.val)); button_exit->setBasePos(x, y, a); } // @ATTR portrait|rectangle|Position and dimensions of the portrait image. else if (infile.key == "portrait") { portrait_dest = toRect(infile.val); } // @ATTR gameslot|rectangle|Position and dimensions of the first game slot. else if (infile.key == "gameslot") { gameslot_pos = toRect(infile.val); } // @ATTR name|label|The label for the hero's name. Position is relative to game slot position. else if (infile.key == "name") { name_pos = eatLabelInfo(infile.val); } // @ATTR level|label|The label for the hero's level. Position is relative to game slot position. else if (infile.key == "level") { level_pos = eatLabelInfo(infile.val); } // @ATTR class|label|The label for the hero's class. Position is relative to game slot position. else if (infile.key == "class") { class_pos = eatLabelInfo(infile.val); } // @ATTR map|label|The label for the hero's current location. Position is relative to game slot position. else if (infile.key == "map") { map_pos = eatLabelInfo(infile.val); } // @ATTR slot_number|label|The label for the save slot index. Position is relative to game slot position. else if (infile.key == "slot_number") { slot_number_pos = eatLabelInfo(infile.val); } // @ATTR loading_label|label|The label for the "Entering game world..."/"Loading saved game..." text. else if (infile.key == "loading_label") { loading_pos = eatLabelInfo(infile.val); } // @ATTR sprite|point|Position for the avatar preview image in each slot else if (infile.key == "sprite") { sprites_pos = toPoint(infile.val); } // @ATTR visible_slots|int|The maximum numbers of visible save slots. else if (infile.key == "visible_slots") { game_slot_max = toInt(infile.val); // can't have less than 1 game slot visible game_slot_max = std::max(game_slot_max, 1); } // @ATTR text_trim_boundary|int|The position of the right-side boundary where text will be shortened with an ellipsis. Position is relative to game slot position. else if (infile.key == "text_trim_boundary") { text_trim_boundary = toInt(infile.val); } else { infile.error("GameStateLoad: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } // prevent text from overflowing on the right edge of game slots if (text_trim_boundary == 0 || text_trim_boundary > gameslot_pos.w) text_trim_boundary = gameslot_pos.w; button_new->refresh(); button_load->refresh(); button_delete->refresh(); loadGraphics(); readGameSlots(); color_normal = font->getColor("menu_normal"); refreshWidgets(); updateButtons(); // if we specified a slot to load at launch, load it now if (!LOAD_SLOT.empty()) { size_t load_slot_id = toInt(LOAD_SLOT) - 1; LOAD_SLOT.clear(); if (load_slot_id < game_slots.size()) { setSelectedSlot(static_cast<int>(load_slot_id)); loading_requested = true; } } render_device->setBackgroundColor(Color(0,0,0,0)); }
MenuVendor::MenuVendor(StatBlock *_stats) : Menu() , stats(_stats) , closeButton(new WidgetButton("images/menus/buttons/button_x.png")) , tabControl(new WidgetTabControl()) , slots_cols(1) , slots_rows(1) , activetab(VENDOR_BUY) , color_normal(font->getColor("menu_normal")) , npc(NULL) , buyback_stock() { setBackground("images/menus/vendor.png"); tabControl->setTabTitle(VENDOR_BUY, msg->get("Inventory")); tabControl->setTabTitle(VENDOR_SELL, msg->get("Buyback")); // Load config settings FileParser infile; // @CLASS MenuVendor|Description of menus/vendor.txt if(infile.open("menus/vendor.txt")) { while(infile.next()) { if (parseMenuKey(infile.key, infile.val)) continue; // @ATTR close|x (integer), y (integer)|Position of the close button. if(infile.key == "close") { Point pos = toPoint(infile.val); closeButton->setBasePos(pos.x, pos.y); } // @ATTR slots_area|x (integer), y (integer)|Position of the top-left slot. else if(infile.key == "slots_area") { slots_area.x = popFirstInt(infile.val); slots_area.y = popFirstInt(infile.val); } // @ATTR vendor_cols|integer|The number of columns in the grid of slots. else if (infile.key == "vendor_cols") { slots_cols = std::max(1, toInt(infile.val)); } // @ATTR vendor_rows|integer|The number of rows in the grid of slots. else if (infile.key == "vendor_rows") { slots_rows = std::max(1, toInt(infile.val)); } // @ATTR label_title|label|The position of the text that displays the NPC's name. else if (infile.key == "label_title") { title = eatLabelInfo(infile.val); } else { infile.error("MenuVendor: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } VENDOR_SLOTS = slots_cols * slots_rows; slots_area.w = slots_cols*ICON_SIZE; slots_area.h = slots_rows*ICON_SIZE; stock[VENDOR_BUY].init(VENDOR_SLOTS, slots_area, ICON_SIZE, slots_cols); stock[VENDOR_SELL].init(VENDOR_SLOTS, slots_area, ICON_SIZE, slots_cols); buyback_stock.init(NPC_VENDOR_MAX_STOCK); for (unsigned i = 0; i < VENDOR_SLOTS; i++) { tablist.add(stock[VENDOR_BUY].slots[i]); } for (unsigned i = 0; i < VENDOR_SLOTS; i++) { tablist.add(stock[VENDOR_SELL].slots[i]); } align(); }
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; }
bool StatBlock::loadCoreStat(FileParser *infile) { // @CLASS StatBlock: Core stats|Description of engine/stats.txt and enemies in enemies/ if (infile->key == "speed") { // @ATTR speed|float|Movement speed float fvalue = toFloat(infile->val, 0); speed = speed_default = fvalue / MAX_FRAMES_PER_SEC; return true; } else if (infile->key == "cooldown") { // @ATTR cooldown|int|Cooldown between attacks in 'ms' or 's'. cooldown = parse_duration(infile->val); return true; } else if (infile->key == "cooldown_hit") { // @ATTR cooldown_hit|duration|Duration of cooldown after being hit in 'ms' or 's'. cooldown_hit = parse_duration(infile->val); return true; } else if (infile->key == "stat") { // @ATTR stat|string, int : Stat name, Value|The starting value for this stat. std::string stat = popFirstString(infile->val); int value = popFirstInt(infile->val); for (size_t i=0; i<STAT_COUNT; ++i) { if (STAT_KEY[i] == stat) { starting[i] = value; return true; } } for (size_t i = 0; i < DAMAGE_TYPES.size(); ++i) { if (DAMAGE_TYPES[i].min == stat) { starting[STAT_COUNT + (i*2)] = value; return true; } else if (DAMAGE_TYPES[i].max == stat) { starting[STAT_COUNT + (i*2) + 1] = value; return true; } } } else if (infile->key == "stat_per_level") { // @ATTR stat_per_level|predefined_string, int : Stat name, Value|The value for this stat added per level. std::string stat = popFirstString(infile->val); int value = popFirstInt(infile->val); for (unsigned i=0; i<STAT_COUNT; i++) { if (STAT_KEY[i] == stat) { per_level[i] = value; return true; } } for (size_t i = 0; i < DAMAGE_TYPES.size(); ++i) { if (DAMAGE_TYPES[i].min == stat) { per_level[STAT_COUNT + (i*2)] = value; return true; } else if (DAMAGE_TYPES[i].max == stat) { per_level[STAT_COUNT + (i*2) + 1] = value; return true; } } } else if (infile->key == "stat_per_primary") { // @ATTR stat_per_primary|predefined_string, predefined_string, int : Primary Stat, Stat name, Value|The value for this stat added for every point allocated to this primary stat. std::string prim_stat = popFirstString(infile->val); size_t prim_stat_index = getPrimaryStatIndex(prim_stat); std::string stat = popFirstString(infile->val); int value = popFirstInt(infile->val); for (unsigned i=0; i<STAT_COUNT; i++) { if (STAT_KEY[i] == stat) { per_primary[prim_stat_index][i] = value; return true; } } for (size_t i = 0; i < DAMAGE_TYPES.size(); ++i) { if (DAMAGE_TYPES[i].min == stat) { per_primary[prim_stat_index][STAT_COUNT + (i*2)] = value; return true; } else if (DAMAGE_TYPES[i].max == stat) { per_primary[prim_stat_index][STAT_COUNT + (i*2) + 1] = value; return true; } } } else if (infile->key == "vulnerable") { // @ATTR vulnerable|predefined_string, int : Element, Value|Percentage weakness to this element. std::string element = popFirstString(infile->val); int value = popFirstInt(infile->val); for (unsigned int i=0; i<ELEMENTS.size(); i++) { if (element == ELEMENTS[i].id) { vulnerable[i] = vulnerable_base[i] = value; return true; } } } else if (infile->key == "power_filter") { // @ATTR power_filter|list(power_id)|Only these powers are allowed to hit this entity. std::string power_id = popFirstString(infile->val); while (!power_id.empty()) { power_filter.push_back(toInt(power_id)); power_id = popFirstString(infile->val); } return true; } return false; }
/** * load a statblock, typically for an enemy definition */ void StatBlock::load(const std::string& filename) { // @CLASS StatBlock: Enemies|Description of enemies in enemies/ FileParser infile; if (!infile.open(filename)) return; bool clear_loot = true; bool flee_range_defined = false; while (infile.next()) { if (infile.new_section) { // APPENDed file clear_loot = true; } int num = toInt(infile.val); float fnum = toFloat(infile.val); bool valid = loadCoreStat(&infile) || loadSfxStat(&infile); // @ATTR name|string|Name if (infile.key == "name") name = msg->get(infile.val); // @ATTR humanoid|bool|This creature gives human traits when transformed into, such as the ability to talk with NPCs. else if (infile.key == "humanoid") humanoid = toBool(infile.val); // @ATTR level|int|Level else if (infile.key == "level") level = num; // enemy death rewards and events // @ATTR xp|int|XP awarded upon death. else if (infile.key == "xp") xp = num; else if (infile.key == "loot") { // @ATTR loot|repeatable(loot)|Possible loot that can be dropped on death. // loot entries format: // loot=[id],[percent_chance] // optionally allow range: // loot=[id],[percent_chance],[count_min],[count_max] if (clear_loot) { loot_table.clear(); clear_loot = false; } loot_table.push_back(Event_Component()); loot->parseLoot(infile.val, &loot_table.back(), &loot_table); } else if (infile.key == "loot_count") { // @ATTR loot_count|int, int : Min, Max|Sets the minimum (and optionally, the maximum) amount of loot this creature can drop. Overrides the global drop_max setting. loot_count.x = popFirstInt(infile.val); loot_count.y = popFirstInt(infile.val); if (loot_count.x != 0 || loot_count.y != 0) { loot_count.x = std::max(loot_count.x, 1); loot_count.y = std::max(loot_count.y, loot_count.x); } } // @ATTR defeat_status|string|Campaign status to set upon death. else if (infile.key == "defeat_status") defeat_status = infile.val; // @ATTR convert_status|string|Campaign status to set upon being converted to a player ally. else if (infile.key == "convert_status") convert_status = infile.val; // @ATTR first_defeat_loot|item_id|Drops this item upon first death. else if (infile.key == "first_defeat_loot") first_defeat_loot = num; // @ATTR quest_loot|string, string, item_id : Required status, Required not status, Item|Drops this item when campaign status is met. else if (infile.key == "quest_loot") { quest_loot_requires_status = popFirstString(infile.val); quest_loot_requires_not_status = popFirstString(infile.val); quest_loot_id = popFirstInt(infile.val); } // behavior stats // @ATTR flying|bool|Creature can move over gaps/water. else if (infile.key == "flying") flying = toBool(infile.val); // @ATTR intangible|bool|Creature can move through walls. else if (infile.key == "intangible") intangible = toBool(infile.val); // @ATTR facing|bool|Creature can turn to face their target. else if (infile.key == "facing") facing = toBool(infile.val); // @ATTR waypoint_pause|duration|Duration to wait at each waypoint in 'ms' or 's'. else if (infile.key == "waypoint_pause") waypoint_pause = parse_duration(infile.val); // @ATTR turn_delay|duration|Duration it takes for this creature to turn and face their target in 'ms' or 's'. else if (infile.key == "turn_delay") turn_delay = parse_duration(infile.val); // @ATTR chance_pursue|int|Percentage change that the creature will chase their target. else if (infile.key == "chance_pursue") chance_pursue = num; // @ATTR chance_flee|int|Percentage chance that the creature will run away from their target. else if (infile.key == "chance_flee") chance_flee = num; else if (infile.key == "power") { // @ATTR power|["melee", "ranged", "beacon", "on_hit", "on_death", "on_half_dead", "on_join_combat", "on_debuff"], power_id, int : State, Power, Chance|A power that has a chance of being triggered in a certain state. AIPower ai_power; std::string ai_type = popFirstString(infile.val); ai_power.id = powers->verifyID(popFirstInt(infile.val), &infile, false); if (ai_power.id == 0) continue; // verifyID() will print our error message ai_power.chance = popFirstInt(infile.val); if (ai_type == "melee") ai_power.type = AI_POWER_MELEE; else if (ai_type == "ranged") ai_power.type = AI_POWER_RANGED; else if (ai_type == "beacon") ai_power.type = AI_POWER_BEACON; else if (ai_type == "on_hit") ai_power.type = AI_POWER_HIT; else if (ai_type == "on_death") ai_power.type = AI_POWER_DEATH; else if (ai_type == "on_half_dead") ai_power.type = AI_POWER_HALF_DEAD; else if (ai_type == "on_join_combat") ai_power.type = AI_POWER_JOIN_COMBAT; else if (ai_type == "on_debuff") ai_power.type = AI_POWER_DEBUFF; else { infile.error("StatBlock: '%s' is not a valid enemy power type.", ai_type.c_str()); continue; } if (ai_power.type == AI_POWER_HALF_DEAD) half_dead_power = true; powers_ai.push_back(ai_power); } else if (infile.key == "passive_powers") { // @ATTR passive_powers|list(power_id)|A list of passive powers this creature has. powers_passive.clear(); std::string p = popFirstString(infile.val); while (p != "") { powers_passive.push_back(toInt(p)); p = popFirstString(infile.val); } } // @ATTR melee_range|float|Minimum distance from target required to use melee powers. else if (infile.key == "melee_range") melee_range = fnum; // @ATTR threat_range|float, float: Engage distance, Stop distance|The first value is the radius of the area this creature will be able to start chasing the hero. The second, optional, value is the radius at which this creature will stop pursuing their target and defaults to double the first value. else if (infile.key == "threat_range") { threat_range = toFloat(popFirstString(infile.val)); std::string tr_far = popFirstString(infile.val); if (!tr_far.empty()) threat_range_far = toFloat(tr_far); else threat_range_far = threat_range * 2; } // @ATTR flee_range|float|The radius at which this creature will start moving to a safe distance. Defaults to half of the threat_range. else if (infile.key == "flee_range") { flee_range = fnum; flee_range_defined = true; } // @ATTR combat_style|["default", "aggressive", "passive"]|How the creature will enter combat. Default is within range of the hero; Aggressive is always in combat; Passive must be attacked to enter combat. else if (infile.key == "combat_style") { if (infile.val == "default") combat_style = COMBAT_DEFAULT; else if (infile.val == "aggressive") combat_style = COMBAT_AGGRESSIVE; else if (infile.val == "passive") combat_style = COMBAT_PASSIVE; else infile.error("StatBlock: Unknown combat style '%s'", infile.val.c_str()); } // @ATTR animations|filename|Filename of an animation definition. else if (infile.key == "animations") animations = infile.val; // @ATTR suppress_hp|bool|Hides the enemy HP bar for this creature. else if (infile.key == "suppress_hp") suppress_hp = toBool(infile.val); else if (infile.key == "categories") { // @ATTR categories|list(string)|Categories that this enemy belongs to. categories.clear(); std::string cat; while ((cat = popFirstString(infile.val)) != "") { categories.push_back(cat); } } // @ATTR flee_duration|duration|The minimum amount of time that this creature will flee. They may flee longer than the specified time. else if (infile.key == "flee_duration") flee_duration = parse_duration(infile.val); // @ATTR flee_cooldown|duration|The amount of time this creature must wait before they can start fleeing again. else if (infile.key == "flee_cooldown") flee_cooldown = parse_duration(infile.val); // this is only used for EnemyGroupManager // we check for them here so that we don't get an error saying they are invalid else if (infile.key == "rarity") ; // but do nothing else if (!valid) { infile.error("StatBlock: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); hp = starting[STAT_HP_MAX]; mp = starting[STAT_MP_MAX]; if (!flee_range_defined) flee_range = threat_range / 2; applyEffects(); }
MenuActionBar::MenuActionBar() : sprite_emptyslot(NULL) , sprite_disabled(NULL) , sprite_attention(NULL) , slots_count(0) , drag_prev_slot(-1) , updated(false) , twostep_slot(-1) { src.w = ICON_SIZE; src.h = ICON_SIZE; menu_labels.resize(4); tablist = TabList(HORIZONTAL, ACTIONBAR_BACK, ACTIONBAR_FORWARD, ACTIONBAR); for (unsigned int i=0; i<4; i++) { menus[i] = new WidgetSlot(-1, ACTIONBAR); } // Read data from config file FileParser infile; // @CLASS MenuActionBar|Description of menus/actionbar.txt if (infile.open("menus/actionbar.txt")) { while (infile.next()) { if (parseMenuKey(infile.key, infile.val)) continue; // @ATTR slot|repeatable(int, int, int) : Index, X, Y|Index (max 10) and position for power slot. if (infile.key == "slot") { unsigned index = popFirstInt(infile.val); if (index == 0 || index > 10) { infile.error("MenuActionBar: Slot index must be in range 1-10."); } else { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); addSlot(index-1, x, y); } } // @ATTR slot_M1|point|Position for the primary action slot. else if (infile.key == "slot_M1") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); addSlot(10, x, y); } // @ATTR slot_M2|point|Position for the secondary action slot. else if (infile.key == "slot_M2") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); addSlot(11, x, y); } // @ATTR char_menu|point|Position for the Character menu button. else if (infile.key == "char_menu") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); menus[MENU_CHARACTER]->setBasePos(x, y); menus[MENU_CHARACTER]->pos.w = menus[MENU_CHARACTER]->pos.h = ICON_SIZE; } // @ATTR inv_menu|point|Position for the Inventory menu button. else if (infile.key == "inv_menu") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); menus[MENU_INVENTORY]->setBasePos(x, y); menus[MENU_INVENTORY]->pos.w = menus[MENU_INVENTORY]->pos.h = ICON_SIZE; } // @ATTR powers_menu|point|Position for the Powers menu button. else if (infile.key == "powers_menu") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); menus[MENU_POWERS]->setBasePos(x, y); menus[MENU_POWERS]->pos.w = menus[MENU_POWERS]->pos.h = ICON_SIZE; } // @ATTR log_menu|point|Position for the Log menu button. else if (infile.key == "log_menu") { int x = popFirstInt(infile.val); int y = popFirstInt(infile.val); menus[MENU_LOG]->setBasePos(x, y); menus[MENU_LOG]->pos.w = menus[MENU_LOG]->pos.h = ICON_SIZE; } else infile.error("MenuActionBar: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } for (unsigned int i=0; i<4; i++) { tablist.add(menus[i]); } slots_count = static_cast<unsigned>(slots.size()); hotkeys.resize(slots_count); hotkeys_temp.resize(slots_count); hotkeys_mod.resize(slots_count); locked.resize(slots_count); slot_item_count.resize(slots_count); slot_enabled.resize(slots_count); slot_activated.resize(slots_count); slot_cooldown_size.resize(slots_count); clear(); loadGraphics(); align(); menu_act = this; }
void StatBlock::loadHeroStats() { // set the default global cooldown cooldown = parse_duration("66ms"); // Redefine numbers from config file if present FileParser infile; // @CLASS StatBlock: Hero stats|Description of engine/stats.txt if (infile.open("engine/stats.txt")) { while (infile.next()) { int value = toInt(infile.val); bool valid = loadCoreStat(&infile); if (infile.key == "max_points_per_stat") { // @ATTR max_points_per_stat|int|Maximum points for each primary stat. max_points_per_stat = value; } else if (infile.key == "sfx_step") { // @ATTR sfx_step|string|An id for a set of step sound effects. See items/step_sounds.txt. sfx_step = infile.val; } else if (infile.key == "stat_points_per_level") { // @ATTR stat_points_per_level|int|The amount of stat points awarded each level. stat_points_per_level = value; } else if (infile.key == "power_points_per_level") { // @ATTR power_points_per_level|int|The amount of power points awarded each level. power_points_per_level = value; } else if (!valid) { infile.error("StatBlock: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } if (max_points_per_stat == 0) max_points_per_stat = max_spendable_stat_points / 4 + 1; statsLoaded = true; // load the XP table // @CLASS StatBlock: XP table|Description of engine/xp_table.txt if (infile.open("engine/xp_table.txt")) { while(infile.next()) { if (infile.key == "level") { // @ATTR level|int, int : Level, XP|The amount of XP required for this level. unsigned lvl_id = popFirstInt(infile.val); unsigned long lvl_xp = toUnsignedLong(popFirstString(infile.val)); if (lvl_id > xp_table.size()) xp_table.resize(lvl_id); xp_table[lvl_id - 1] = lvl_xp; } } infile.close(); } if (xp_table.empty()) { logError("StatBlock: No XP table defined."); xp_table.push_back(0); } max_spendable_stat_points = static_cast<int>(xp_table.size()) * stat_points_per_level; }
MenuInventory::MenuInventory(StatBlock *_stats) { stats = _stats; MAX_EQUIPPED = 4; MAX_CARRIED = 64; visible = false; setBackground("images/menus/inventory.png"); currency = 0; carried_cols = 4; // default to 4 if menus/inventory.txt::carried_cols not set carried_rows = 4; // default to 4 if menus/inventory.txt::carried_rows not set drag_prev_src = -1; changed_equipment = true; log_msg = ""; show_book = ""; closeButton = new WidgetButton("images/menus/buttons/button_x.png"); // Load config settings FileParser infile; // @CLASS MenuInventory|Description of menus/inventory.txt if (infile.open("menus/inventory.txt")) { while(infile.next()) { if (parseMenuKey(infile.key, infile.val)) continue; // @ATTR close|x (integer), y (integer)|Position of the close button. if(infile.key == "close") { Point pos = toPoint(infile.val); closeButton->setBasePos(pos.x, pos.y); } // @ATTR equipment_slot|x (integer), y (integer), size (integer), slot_type (string)|Position and item type of an equipment slot. else if(infile.key == "equipment_slot") { Rect area; Point pos; pos.x = area.x = popFirstInt(infile.val); pos.y = area.y = popFirstInt(infile.val); area.w = area.h = popFirstInt(infile.val); equipped_area.push_back(area); equipped_pos.push_back(pos); slot_type.push_back(popFirstString(infile.val)); } // @ATTR slot_name|string|The displayed name of the last defined equipment slot. else if(infile.key == "slot_name") slot_desc.push_back(infile.val); // @ATTR carried_area|x (integer), y (integer)|Position of the first normal inventory slot. else if(infile.key == "carried_area") { Point pos; carried_pos.x = carried_area.x = popFirstInt(infile.val); carried_pos.y = carried_area.y = popFirstInt(infile.val); } // @ATTR carried_cols|integer|The number of columns for the normal inventory. else if (infile.key == "carried_cols") carried_cols = std::max(1, toInt(infile.val)); // @ATTR carried_rows|integer|The number of rows for the normal inventory. else if (infile.key == "carried_rows") carried_rows = std::max(1, toInt(infile.val)); // @ATTR label_title|label|Position of the "Inventory" label. else if (infile.key == "label_title") title = eatLabelInfo(infile.val); // @ATTR currency|label|Position of the label that displays the total currency being carried. else if (infile.key == "currency") currency_lbl = eatLabelInfo(infile.val); // @ATTR help|x (integer), y (integer), w (integer), h (integer)|A mouse-over area that displays some help text for inventory shortcuts. else if (infile.key == "help") help_pos = toRect(infile.val); else infile.error("MenuInventory: '%s' is not a valid key.", infile.key.c_str()); } infile.close(); } MAX_EQUIPPED = equipped_area.size(); MAX_CARRIED = carried_cols * carried_rows; carried_area.w = carried_cols*ICON_SIZE; carried_area.h = carried_rows*ICON_SIZE; color_normal = font->getColor("menu_normal"); color_high = font->getColor("menu_bonus"); inventory[EQUIPMENT].init(MAX_EQUIPPED, equipped_area, slot_type); inventory[CARRIED].init(MAX_CARRIED, carried_area, ICON_SIZE, carried_cols); for (int i = 0; i < MAX_EQUIPPED; i++) { tablist.add(inventory[EQUIPMENT].slots[i]); } for (int i = 0; i < MAX_CARRIED; i++) { tablist.add(inventory[CARRIED].slots[i]); } align(); }
MenuStash::MenuStash(StatBlock *_stats) : Menu() , stats(_stats) , closeButton(new WidgetButton("images/menus/buttons/button_x.png")) , color_normal(font->getColor("menu_normal")) , stock() , updated(false) { setBackground("images/menus/stash.png"); slots_cols = 8; // default if menus/stash.txt::stash_cols not set slots_rows = 8; // default if menus/stash.txt::slots_rows not set // Load config settings FileParser infile; // @CLASS MenuStash|Description of menus/stash.txt if (infile.open("menus/stash.txt")) { while(infile.next()) { if (parseMenuKey(infile.key, infile.val)) continue; // @ATTR close|x (integer), y (integer)|Position of the close button. if (infile.key == "close") { Point pos = toPoint(infile.val); closeButton->setBasePos(pos.x, pos.y); } // @ATTR slots_area|x (integer), y (integer)|Position of the top-left slot. else if (infile.key == "slots_area") { slots_area.x = popFirstInt(infile.val); slots_area.y = popFirstInt(infile.val); } // @ATTR stash_cols|integer|The number of columns for the grid of slots. else if (infile.key == "stash_cols") { slots_cols = std::max(1, toInt(infile.val)); } // @ATTR stash_rows|integer|The number of rows for the grid of slots. else if (infile.key == "stash_rows") { slots_rows = std::max(1, toInt(infile.val)); } // @ATTR label_title|label|Position of the "Stash" label. else if (infile.key == "label_title") { title = eatLabelInfo(infile.val); } // @ATTR currency|label|Position of the label displaying the amount of currency stored in the stash. else if (infile.key == "currency") { currency = eatLabelInfo(infile.val); } else { infile.error("MenuStash: '%s' is not a valid key.", infile.key.c_str()); } } infile.close(); } STASH_SLOTS = slots_cols * slots_rows; slots_area.w = slots_cols*ICON_SIZE; slots_area.h = slots_rows*ICON_SIZE; stock.init( STASH_SLOTS, slots_area, ICON_SIZE, slots_cols); for (int i = 0; i < STASH_SLOTS; i++) { tablist.add(stock.slots[i]); } align(); }
SDLFontEngine::SDLFontEngine() : FontEngine(), active_font(NULL) { // Initiate SDL_ttf if(!TTF_WasInit() && TTF_Init()==-1) { logError("SDLFontEngine: TTF_Init: %s", TTF_GetError()); Exit(2); } // load the fonts // @CLASS SDLFontEngine: Font settings|Description of engine/font_settings.txt FileParser infile; if (infile.open("engine/font_settings.txt")) { while (infile.next()) { if (infile.new_section && infile.section == "font") { SDLFontStyle f; f.name = infile.section; font_styles.push_back(SDLFontStyle()); } if (font_styles.empty()) continue; SDLFontStyle *style = &(font_styles.back()); if (infile.key == "id") { // @ATTR font.id|string|An identifier used to reference this font. style->name = infile.val; } else if (infile.key == "style") { // @ATTR font.style|repeatable(["default", predefined_string], filename, int, bool) : Language, Font file, Point size, Blending|Filename, point size, and blend mode of the font to use for this language. Language can be "default" or a 2-letter region code. std::string lang = popFirstString(infile.val); if ((lang == "default" && style->path == "") || lang == LANGUAGE) { style->path = popFirstString(infile.val); style->ptsize = popFirstInt(infile.val); style->blend = toBool(popFirstString(infile.val)); style->ttfont = TTF_OpenFont(mods->locate("fonts/" + style->path).c_str(), style->ptsize); if(style->ttfont == NULL) { logError("FontEngine: TTF_OpenFont: %s", TTF_GetError()); } else { int lineskip = TTF_FontLineSkip(style->ttfont); style->line_height = lineskip; style->font_height = lineskip; } } } } infile.close(); } // set the font colors Color color; if (infile.open("engine/font_colors.txt")) { while (infile.next()) { // @ATTR menu_normal|color|Basic menu text color. Recommended: white. // @ATTR menu_bonus|color|Positive menu text color. Recommended: green. // @ATTR menu_penalty|color|Negative menu text color. Recommended: red. // @ATTR widget_normal|color|Basic widget text color. Recommended: white. // @ATTR widget_disabled|color|Disabled widget text color. Recommended: grey. // @ATTR combat_givedmg|color|Enemy damage text color. Recommended: white. // @ATTR combat_takedmg|color|Player damage text color. Recommended: red. // @ATTR combat_crit|color|Enemy critical damage text color. Recommended: yellow. // @ATTR requirements_no_met|color|Unmet requirements text color. Recommended: red. // @ATTR item_bonus|color|Item bonus text color. Recommended: green. // @ATTR item_penalty|color|Item penalty text color. Recommended: red. // @ATTR item_flavor|color|Item flavor text color. Recommended: grey. color_map[infile.key] = toRGB(infile.val); } infile.close(); } // Attempt to set the default active font setFont("font_regular"); if (!active_font) { logError("FontEngine: Unable to determine default font!"); Exit(1); } }