#include #include #include "boe.global.hpp" #include "universe/universe.hpp" #include "boe.graphics.hpp" #include "boe.text.hpp" #include "boe.items.hpp" #include "boe.specials.hpp" #include "boe.party.hpp" #include "boe.locutils.hpp" #include "boe.newgraph.hpp" #include "boe.infodlg.hpp" #include "sounds.hpp" #include "boe.monster.hpp" #include "boe.main.hpp" #include "mathutil.hpp" #include "utility.hpp" #include "dialogxml/dialogs/strdlog.hpp" #include "dialogxml/dialogs/strchoice.hpp" #include "dialogxml/dialogs/3choice.hpp" #include "dialogxml/widgets/message.hpp" #include #include #include "tools/prefs.hpp" #include "tools/winutil.hpp" #include "tools/cursors.hpp" #include "fileio/resmgr/res_dialog.hpp" #include "gfx/render_shapes.hpp" extern short which_combat_type; extern eGameMode overall_mode; extern eItemWinMode stat_window; extern bool boom_anim_active; extern rectangle d_rects[80]; extern short d_rect_index[80]; extern bool map_visible; extern cUniverse univ; extern void draw_map(bool need_refresh); short selected; const int ITEMS_IN_WINDOW = 8; bool GTP(short item_num) { cItem item = univ.scenario.get_stored_item(item_num); return univ.party.give_item(item,true); } bool silent_GTP(short item_num) { cItem item = univ.scenario.get_stored_item(item_num); return univ.party.give_item(item,false); } void give_gold(short amount,bool print_result) { if(amount < 0) return; univ.party.gold += amount; if(print_result) put_pc_screen(); } bool take_gold(short amount,bool print_result) { if(univ.party.gold < amount) return false; univ.party.gold -= amount; if(print_result) put_pc_screen(); return true; } void give_food(short amount,bool print_result) { if(amount < 0) return; univ.party.food = univ.party.food + amount; if(print_result) put_pc_screen(); } short take_food(short amount,bool print_result) { short shortfall = amount - univ.party.food; if(shortfall > 0) { univ.party.food = 0; }else{ univ.party.food -= amount; shortfall = 0; } if(print_result) put_pc_screen(); return shortfall; } void equip_item(short pc_num,short item_num) { if(overall_mode == MODE_COMBAT && univ.party[pc_num].items[item_num].variety == eItemType::FOOD) add_string_to_buf("Equip: Not in combat"); else { if(univ.party[pc_num].equip[item_num]) univ.party[pc_num].unequip_item(item_num, true); else if(is_combat() && univ.party[pc_num].items[item_num].variety == eItemType::ARMOR) add_string_to_buf("Equip: Not armor in combat"); else univ.party[pc_num].equip_item(item_num, true); } if(stat_window == pc_num) put_item_screen(stat_window); } void drop_item(short pc_num,short item_num,location where_drop) { short s1, s2; int spec = -1; std::string choice; short how_many = 1; cItem item_store; bool take_dropped_item = true, need_redraw = false; location loc; item_store = univ.party[pc_num].items[item_num]; if(item_store.ability == eItemAbil::DROP_CALL_SPECIAL) spec = item_store.abil_strength; if(univ.party[pc_num].equip[item_num] && univ.party[pc_num].items[item_num].cursed) add_string_to_buf("Drop: Item is cursed."); else switch(overall_mode) { case MODE_OUTDOORS: choice = cChoiceDlog("drop-item-confirm",{"okay","cancel"}).show(); if(choice == "cancel") return; add_string_to_buf("Drop: OK"); if((item_store.type_flag > 0) && (item_store.charges > 1)) { how_many = get_num_of_items(item_store.charges); if(how_many == item_store.charges) univ.party[pc_num].take_item(item_num); else univ.party[pc_num].items[item_num].charges -= how_many; } else univ.party[pc_num].take_item(item_num); break; case MODE_DROP_TOWN: case MODE_DROP_COMBAT: loc = where_drop; if((item_store.type_flag > 0) && (item_store.charges > 1)) { how_many = get_num_of_items(item_store.charges); if(how_many <= 0) return; if(how_many < item_store.charges) take_dropped_item = false; item_store.charges = how_many; } if(place_item(item_store,loc,true)) { add_string_to_buf("Drop: Item put away"); spec = -1; // Don't call drop specials if it was put away } else add_string_to_buf("Drop: OK"); univ.party[pc_num].items[item_num].charges -= how_many; if(take_dropped_item) univ.party[pc_num].take_item(item_num); break; default: //should never be reached break; } if(spec >= 0) while(how_many--) run_special(eSpecCtx::DROP_ITEM, eSpecCtxType::SCEN, spec, where_drop, &s1, &s2, &need_redraw); if(need_redraw) draw_terrain(0); } bool place_item(cItem item,location where,bool contained) { if(contained && !is_container(where)) contained = false; bool is_pushable_ctr = contained && (univ.town.is_barrel(where.x,where.y) || univ.town.is_crate(where.x,where.y)); for(short i = 0; i < univ.town.items.size(); i++) if(univ.town.items[i].variety == eItemType::NO_ITEM) { univ.town.items[i] = item; univ.town.items[i].item_loc = where; univ.town.items[i].contained = contained; univ.town.items[i].held = is_pushable_ctr; reset_item_max(); return univ.town.items[i].contained; } univ.town.items.push_back(item); univ.town.items.back().item_loc = where; univ.town.items.back().contained = contained; univ.town.items.back().held = is_pushable_ctr; reset_item_max(); return univ.town.items.back().contained; } cItem item_store; void give_thing(short pc_num, short item_num) { short who_to,how_many = 0; bool take_given_item = true; if(univ.party[pc_num].equip[item_num] && univ.party[pc_num].items[item_num].cursed) add_string_to_buf("Give: Item is cursed."); else { item_store = univ.party[pc_num].items[item_num]; who_to = select_pc(eSelectPC::ONLY_CAN_GIVE_FROM_ACTIVE,"Give item to who?"); // No party members can receive the item: if(who_to == 8){ if(overall_mode == MODE_COMBAT){ ASB("Can't give: must be adjacent with enough carrying capacity.", 2); }else{ ASB("Can't give: no one has the carrying capacity.", 2); } } if((who_to < 6) && (who_to != pc_num)) { if((item_store.type_flag > 0) && (item_store.charges > 1)) { how_many = get_num_of_items(item_store.charges); if(how_many == 0) return; if(how_many < item_store.charges) take_given_item = false; univ.party[pc_num].items[item_num].charges -= how_many; item_store.charges = how_many; } eBuyStatus give_status = univ.party[who_to].give_item(item_store,0); if(give_status != eBuyStatus::OK){ // This should be impossible, because select_pc() already checked that the options // were viable. showFatalError("Unexpectedly failed to give item!"); } if(take_given_item){ univ.party[pc_num].take_item(item_num); } } } } // Procedure only ready for town and outdoor short dist_from_party(location where) { short store = 1000; if(is_combat()) { for(short i = 0; i < 6; i++) if(univ.party[i].main_status == eMainStatus::ALIVE) store = min(store,dist(univ.party[i].combat_pos,where)); } else store = dist(univ.party.town_loc,where); return store; } // This marks the item as taken so that it won't show up again when the party returns to this town void set_item_flag(cItem* item) { if((item->is_special > 0) && (item->is_special < 65)) { item->is_special--; univ.town->set_item_taken(item->is_special); item->is_special = 0; } } //short pc_num; // if 6, any short get_item(location place,short pc_num,bool check_container) { short taken = 0; bool item_near = false; short mass_get = 1; for(short i = 0; i < univ.town.monst.size(); i++) if(univ.town.monst[i].is_alive() && !univ.town.monst[i].is_friendly() && (can_see_light(place,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) mass_get = 0; for(short i = 0; i < univ.town.items.size(); i++) if(univ.town.items[i].variety != eItemType::NO_ITEM) if(((adjacent(place,univ.town.items[i].item_loc)) || (mass_get == 1 && !check_container && ((dist(place,univ.town.items[i].item_loc) <= 4) || ((is_combat()) && (which_combat_type == 0))) && (can_see_light(place,univ.town.items[i].item_loc,sight_obscurity) < 5))) && ((!univ.town.items[i].contained) || (check_container))) { taken = 1; if(univ.town.items[i].value < 2) univ.town.items[i].ident = true; item_near = true; } if(item_near) if(display_item(place,pc_num,mass_get,check_container)) { // if true, there was a theft for(short i = 0; i < univ.town.monst.size(); i++) if(univ.town.monst[i].is_alive() && univ.town.monst[i].is_friendly() && (can_see_light(place,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) { make_town_hostile(); add_string_to_buf("Your crime was seen!"); break; } } if(pc_num != 10) { if(taken == 0) add_string_to_buf("Get: nothing here"); else add_string_to_buf("Get: OK"); } reset_item_max(); return taken; } void make_town_hostile() { set_town_attitude(0, -1, eAttitude::HOSTILE_A); return; } // Set Attitude node adapted from *i, meant to replace make_town_hostile node void set_town_attitude(short lo,short hi,eAttitude att) { short num; if(which_combat_type == 0) return; give_help(53,0); univ.town.monst.hostile = true; long long num_monst = univ.town.monst.size(); // Nice smart indexing, like Python :D if(lo <= -num_monst) lo = 0; if(lo < 0) lo = num_monst + lo; if(hi <= -num_monst) hi = 0; if(hi < 0) hi = num_monst + hi; if(hi < lo) std::swap(lo, hi); for(short i = lo; i <= hi; i++) { if(univ.town.monst[i].is_alive() && univ.town.monst[i].summon_time == 0){ univ.town.monst[i].attitude = att; num = univ.town.monst[i].number; // If made hostile, make mobile if(!univ.town.monst[i].is_friendly()) { univ.town.monst.hostile = true; univ.town.monst[i].mobility = 1; // If a "guard", give a power boost if(univ.scenario.scen_monsters[num].guard) { univ.town.monst[i].active = eCreatureStatus::ALERTED; univ.town.monst[i].health *= 3; univ.town.monst[i].status[eStatus::HASTE_SLOW] = 8; univ.town.monst[i].status[eStatus::BLESS_CURSE] = 8; } } } } // In some towns, doin' this'll getcha' killed. // (Or something else! Killing the party would be the responsibility of whatever special node is called.) if(univ.town.monst.hostile && univ.town->spec_on_hostile >= 0) run_special(eSpecCtx::TOWN_HOSTILE, eSpecCtxType::TOWN, univ.town->spec_on_hostile, univ.party.town_loc); } // TODO: Set town status to "dead"? Could reuse above with magic att (eg -1), or write new function. static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& current_getting_pc, const std::vector& item_array) { cItem item; char key_stash[2] = "a"; // First make sure all arrays for who can get stuff are in order. if(current_getting_pc < 6 && (univ.party[current_getting_pc].main_status != eMainStatus::ALIVE || !univ.party[current_getting_pc].has_space())) { current_getting_pc = 6; } for(short i = 0; i < 6; i++) { std::ostringstream sout; sout << "pc" << i + 1; std::string id = sout.str(); if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].has_space() && ((!is_combat()) || (univ.cur_pc == i))) { if(current_getting_pc == 6) current_getting_pc = i; me[id].show(); } else { me[id].hide(); sout << "-g"; me[sout.str()].hide(); } if(current_getting_pc == i) me.addLabelFor(id, "* ", LABEL_LEFT, 7, true); else me.addLabelFor(id," ", LABEL_LEFT, 7, true); } // darken arrows, as appropriate if(first_item_shown == 0) me["up"].hide(); else me["up"].show(); if(item_array.size() <= ITEMS_IN_WINDOW || first_item_shown > item_array.size() - (ITEMS_IN_WINDOW-1)) me["down"].hide(); else me["down"].show(); for(short i = 0; i < ITEMS_IN_WINDOW; i++) { std::ostringstream sout; sout << "item" << i + 1; std::string pict = sout.str() + "-g", name = sout.str() + "-name"; std::string detail = sout.str() + "-detail", weight = sout.str() + "-weight"; std::string key = sout.str() + "-key"; key_stash[0] = 'a' + i; // TODO: Rework this so that no exceptions are required if(i + first_item_shown < item_array.size() && item_array[i + first_item_shown]->variety != eItemType::NO_ITEM) { // display an item in window me[pict].show(); item = *item_array[i + first_item_shown]; me[name].setText(item.ident ? item.full_name : item.name); // TODO: Party sheet items cPict& pic = dynamic_cast(me[pict]); if(item.graphic_num >= 1000) pic.setPict(item.graphic_num - 1000, PIC_CUSTOM_ITEM); else pic.setPict(item.graphic_num, PIC_ITEM); me[detail].setText(item.interesting_string()); me[weight].setText("Weight: " + std::to_string(item.item_weight())); me[key].setText(key_stash); } else { // erase the spot me[pict].hide(); me[name].setText(""); me[detail].setText(""); me[weight].setText(""); me[key].setText(""); } } if(current_getting_pc < 6) { std::ostringstream sout; sout << univ.party[current_getting_pc].name << " is carrying "; sout << univ.party[current_getting_pc].cur_weight() << " out of " << univ.party[current_getting_pc].max_weight() << '.'; me["prompt"].setText(sout.str()); } for(short i = 0; i < 6; i++) if(univ.party[i].main_status == eMainStatus::ALIVE) { std::ostringstream sout; sout << "pc" << i + 1 << "-g"; pic_num_t pic = univ.party[i].which_graphic; if(pic >= 100 && pic < 1000) dynamic_cast(me[sout.str()]).setPict(pic - 100,PIC_MONST); else dynamic_cast(me[sout.str()]).setPict(pic,PIC_PC); } } static bool display_item_event_filter(cDialog& me, std::string id, size_t& first_item_shown, short& current_getting_pc, std::vector& item_array, bool allow_overload) { cItem item; if(id == "done") { me.toast(true); } else if(id == "up") { if(first_item_shown > 0) { first_item_shown -= ITEMS_IN_WINDOW; put_item_graphics(me, first_item_shown, current_getting_pc, item_array); } } else if(id == "down") { if(first_item_shown + ITEMS_IN_WINDOW < item_array.size()) { first_item_shown += ITEMS_IN_WINDOW; put_item_graphics(me, first_item_shown, current_getting_pc, item_array); } } else if(id.substr(0,2) == "pc") { current_getting_pc = id[2] - '1'; put_item_graphics(me, first_item_shown, current_getting_pc, item_array); } else { if(current_getting_pc == 6) return true; size_t item_hit; item_hit = id[4] - '1'; item_hit += first_item_shown; if(item_hit >= item_array.size()) return true; item = *item_array[item_hit]; if(item.property) { if(!me.getResult()) { std::string choice = cChoiceDlog("steal-item",{"steal","leave"}, &me).show(); if(choice == "leave") return true; me.setResult(true); } item.property = false; } if(item.variety == eItemType::GOLD) { if(item.item_level > 3000) item.item_level = 3000; set_item_flag(&item); give_gold(item.item_level,false); play_sound(39); // formerly force_play_sound } else if(item.variety == eItemType::FOOD) { give_food(item.item_level,false); set_item_flag(&item); play_sound(62); // formerly force_play_sound } else if(item.variety == eItemType::SPECIAL) { univ.party.spec_items.insert(item.item_level); set_item_flag(&item); } else if(item.variety == eItemType::QUEST) { univ.party.active_quests[item.item_level].status = eQuestStatus::STARTED; univ.party.active_quests[item.item_level].start = univ.party.calc_day(); univ.party.active_quests[item.item_level].source = -1; set_item_flag(&item); } else { if(!allow_overload && item.item_weight() > univ.party[current_getting_pc].free_weight()) { play_sound(41); me["prompt"].setText("It's too heavy to carry."); give_help(38,0,me); return true; } set_item_flag(&item); play_sound(0); // formerly force_play_sound int flags = GIVE_DO_PRINT; if(allow_overload) flags |= GIVE_ALLOW_OVERLOAD; univ.party[current_getting_pc].give_item(item, flags); } *item_array[item_hit] = cItem(); item_array.erase(item_array.begin() + item_hit); put_item_graphics(me, first_item_shown, current_getting_pc, item_array); } return true; } // Returns true if a theft committed //pc_num; // < 6 - this pc only 6 - any pc //short mode; // 0 - adjacent 1 - all in sight bool display_item(location from_loc,short /*pc_num*/,short mode, bool check_container) { // short item_array[130]; std::vector item_array; set_cursor(sword_curs); short current_getting_pc = univ.cur_pc; for(short i = 0; i < univ.town.items.size(); i++) if(univ.town.items[i].variety != eItemType::NO_ITEM) { if(((adjacent(from_loc,univ.town.items[i].item_loc)) || (mode == 1 && !check_container && ((dist(from_loc,univ.town.items[i].item_loc) <= 4) || ((is_combat()) && (which_combat_type == 0))) && (can_see_light(from_loc,univ.town.items[i].item_loc,sight_obscurity) < 5))) && (univ.town.items[i].contained == check_container) && ((!check_container) || (univ.town.items[i].item_loc == from_loc))) { item_array.push_back(&univ.town.items[i]); } } bool stole_something = false; if(check_container) stole_something = show_get_items("Looking in container:", item_array, current_getting_pc); else if(mode == 0) stole_something = show_get_items("Getting all adjacent items:", item_array, current_getting_pc); else stole_something = show_get_items("Getting all nearby items:", item_array, current_getting_pc); put_item_screen(stat_window); put_pc_screen(); return stole_something; } bool show_get_items(std::string titleText, std::vector& itemRefs, short pc_getting, bool overload) { using namespace std::placeholders; size_t first_item = 0; cDialog itemDialog(*ResMgr::dialogs.get("get-items")); auto handler = std::bind(display_item_event_filter, _1, _2, std::ref(first_item), std::ref(pc_getting), std::ref(itemRefs), overload); itemDialog.attachClickHandlers(handler, {"done", "up", "down"}); itemDialog.attachClickHandlers(handler, {"pc1", "pc2", "pc3", "pc4", "pc5", "pc6"}); itemDialog.setResult(false); cTextMsg& title = dynamic_cast(itemDialog["title"]); title.setText(titleText); for(int i = 1; i <= ITEMS_IN_WINDOW; i++) { std::ostringstream sout; sout << "item" << i << "-key"; itemDialog[sout.str()].attachKey({false, static_cast('`' + i), mod_none}); itemDialog[sout.str()].attachClickHandler(handler); } put_item_graphics(itemDialog, first_item, pc_getting, itemRefs); itemDialog.setResult(false); // The result is set to true if the player steals something itemDialog.runWithHelp(36, 37); return itemDialog.getResult(); } void custom_pic_dialog(std::string title, pic_num_t bigpic) { cDialog pic_dlg(*ResMgr::dialogs.get("show-map")); cControl& okay = pic_dlg["okay"]; cControl& text = pic_dlg["title"]; okay.attachClickHandler(std::bind(&cDialog::toast, &pic_dlg, false)); text.setText(title); cPict& map = dynamic_cast(pic_dlg["map"]); // We don't provide a way to use non-custom full sheets - why would you want to show standard help graphics? map.setPict(bigpic, PIC_CUSTOM_FULL); // Now we need to adjust the size to ensure that everything fits correctly. map.recalcRect(); rectangle mapBounds = map.getBounds(); rectangle btnBounds = okay.getBounds(); rectangle txtBounds = text.getBounds(); btnBounds.offset(-btnBounds.left, -btnBounds.top); btnBounds.offset(mapBounds.right - btnBounds.width(), mapBounds.bottom + 10); okay.setBounds(btnBounds); txtBounds.right = mapBounds.right; text.setBounds(txtBounds); pic_dlg.recalcRect(); pic_dlg.run(); } void story_dialog(std::string title, str_num_t first, str_num_t last, eSpecCtxType which_str_type, pic_num_t pic, ePicType pt, short anim_loops, int anim_fps) { cDialog story_dlg(*ResMgr::dialogs.get("many-str")); dynamic_cast(story_dlg["pict"]).setPict(pic, pt); setup_dialog_pict_anim(story_dlg, "pict", anim_loops, anim_fps); str_num_t cur = first; story_dlg.attachClickHandlers([&cur,first,last,which_str_type](cDialog& me, std::string clicked, eKeyMod) -> bool { if(clicked == "left") { if(cur > first) cur--; } else if(clicked == "done" || cur == last) { me.toast(false); return true; } else if(clicked == "right") { cur++; } std::string text; univ.get_str(text, which_str_type, cur); me["str"].setText(text); return true; }, {"left", "right", "done"}); story_dlg["left"].triggerClickHandler(story_dlg, "left", eKeyMod()); story_dlg["title"].setText(title); story_dlg.run(); } static bool get_num_of_items_event_filter(cDialog& me, std::string, eKeyMod) { if(me.toast(true)) me.setResult(me["number"].getTextAsNum()); return true; } //town_num; // Will be 0 - 200 for town, 200 - 290 for outdoors //short sign_type; // terrain type short get_num_of_items(short max_num) { set_cursor(sword_curs); cDialog numPanel(*ResMgr::dialogs.get("get-num")); numPanel["extra-led"].hide(); numPanel.attachClickHandlers(get_num_of_items_event_filter, {"okay"}); numPanel["prompt"].setText("How many? (0-" + std::to_string(max_num) + ") "); numPanel["number"].setTextToNum(max_num); numPanel.run(); return minmax(0,max_num,numPanel.getResult()); } void init_mini_map() { double map_scale = get_ui_scale_map(); if (map_scale < 0.1) map_scale = 1.0; if (mini_map().isOpen()) mini_map().close(); mini_map().create(sf::VideoMode(map_scale*296,map_scale*277), "Map", sf::Style::Titlebar | sf::Style::Close); // TODO why is 52,62 the default position, anyway? int map_x = get_int_pref("MapWindowX", 52); int map_y = get_int_pref("MapWindowY", 62); mini_map().setPosition(sf::Vector2i(map_x,map_y)); sf::View view; view.reset(sf::FloatRect(0, 0, map_scale*296,map_scale*277)); view.setViewport(sf::FloatRect(0, 0, map_scale, map_scale)); mini_map().setView(view); mini_map().setVisible(false); map_visible=false; setWindowFloating(mini_map(), true); makeFrontWindow(mainPtr(), mini_map()); // Create and initialize map gworld if(!map_gworld().create(384, 384)) { play_sound(2); throw std::string("Failed to initialized automap!"); } else { draw_map(true); } } void place_glands(location where,mon_num_t m_type) { cItem store_i; cMonster monst; if(m_type >= 10000) monst = univ.party.summons[m_type - 10000]; else monst = univ.scenario.scen_monsters[m_type]; if((monst.corpse_item >= 0) && (monst.corpse_item < 400) && (get_ran(1,1,100) < monst.corpse_item_chance)) { store_i = univ.scenario.get_stored_item(monst.corpse_item); place_item(store_i,where); } } void reset_item_max() { while(!univ.town.items.empty() && univ.town.items.back().variety == eItemType::NO_ITEM) univ.town.items.pop_back(); } short item_val(cItem item) { if(item.charges == 0) return item.value; return item.charges * item.value; } //// rewrite //short mode; // 0 - normal, 1 - force void place_treasure(location where,short level,short loot,short mode) { cItem new_item; short amt,r1; // Make these static const because they are never changed. // Saves them being initialized every time the function is called. static const short treas_chart[5][6] = { {0,-1,-1,-1,-1,-1}, {1,-1,-1,-1,-1,-1}, {2,1,1,-1,-1,-1}, {3,2,1,1,-1,-1}, {4,3,2,2,1,1} }; static const short treas_odds[5][6] = { {10,0,0,0,0,0}, {50,0,0,0,0,0}, {60,50,40,0,0,0}, {100,90,80,70,0,0}, {100,80,80,75,75,75} }; static const short id_odds[21] = { 0,10,15,20,25,30,35, 39,43,47,51,55,59,63, 67,71,73,75,77,79,81 }; static const short max_mult[5][10] = { {0,0,0,0,0,0,0,0,0,1}, {0,0,1,1,1,1,2,3,5,20}, {0,0,1,1,2,2,4,6,10,25}, {5,10,10,10,15,20,40,80,100,100}, {25,25,50,50,50,100,100,100,100,100} }; static const short min_chart[5][10] = { {0,0,0,0,0,0,0,0,0,1}, {0,0,0,0,0,0,0,0,5,20}, {0,0,0,0,1,1,5,10,15,40}, {10,10,15,20,20,30,40,50,75,100}, {50,100,100,100,100,200,200,200,200,200} }; short max,min; if(loot == 1) amt = get_ran(2,1,7) + 1; else amt = loot * (get_ran(1,0,10 + (loot * 6) + (level * 2)) + 5); if(univ.party.get_level() <= 12) amt += 1; if(univ.party.get_level() <= 60 && amt > 2) amt += 2; if(amt > 3) { new_item = univ.scenario.get_stored_item(0); new_item.item_level = amt; r1 = get_ran(1,1,9); if(((loot > 1) && (r1 < 7)) || ((loot == 1) && (r1 < 5)) || (mode == 1) || ((r1 < 6) && (univ.party.get_level() < 30)) || (loot > 2) ) place_item(new_item,where); } for(short j = 0; j < 5; j++) { r1 = get_ran(1,1,100); if((treas_chart[loot][j] >= 0) && (r1 <= treas_odds[loot][j] + check_party_stat(eSkill::LUCK, 0))) { r1 = get_ran(1,0,9); min = min_chart[treas_chart[loot][j]][r1]; r1 = get_ran(1,0,9); max = (min + level + (2 * (loot - 1)) + (check_party_stat(eSkill::LUCK, 0) / 3)) * max_mult[treas_chart[loot][j]][r1]; if(get_ran(1,0,1000) == 500) { max = 10000; min = 100; } // reality check if((loot == 1) && (max > 100) && (get_ran(1,0,8) < 7)) max = 100; if((loot == 2) && (max > 200) && (get_ran(1,0,8) < 6)) max = 200; new_item = univ.scenario.return_treasure(treas_chart[loot][j]); if((item_val(new_item) < min) || (item_val(new_item) > max)) { new_item = univ.scenario.return_treasure(treas_chart[loot][j]); if((item_val(new_item) < min) || (item_val(new_item) > max)) { new_item = univ.scenario.return_treasure(treas_chart[loot][j]); if(item_val(new_item) > max) new_item.variety = eItemType::NO_ITEM; } } // not many magic items if(mode == 0) { if(new_item.magic && (level < 2) && (get_ran(1,0,5) < 3)) new_item.variety = eItemType::NO_ITEM; if(new_item.magic && (level == 2) && (get_ran(1,0,5) < 2)) new_item.variety = eItemType::NO_ITEM; if(new_item.cursed && (get_ran(1,0,5) < 3)) new_item.variety = eItemType::NO_ITEM; } // if forced, keep dipping until a treasure comes up if((mode == 1) && (max >= 20)) { do new_item = univ.scenario.return_treasure(treas_chart[loot][j]); while(new_item.variety == eItemType::NO_ITEM || item_val(new_item) > max); } // Not many cursed items if(new_item.cursed && (get_ran(1,0,2) == 1)) new_item.variety = eItemType::NO_ITEM; if(new_item.variety != eItemType::NO_ITEM) { for(short i = 0; i < 6; i++) if((univ.party[i].main_status == eMainStatus::ALIVE) && get_ran(1,1,100) < id_odds[univ.party[i].skill(eSkill::ITEM_LORE)]) new_item.ident = true; place_item(new_item,where); } } } } static bool get_text_response_event_filter(cDialog& me, std::string, eKeyMod) { me.toast(true); me.setResult(me["response"].getText()); return true; } std::string get_text_response(std::string prompt, pic_num_t pic) { set_cursor(sword_curs); cDialog strPanel(*ResMgr::dialogs.get("get-response")); strPanel.attachClickHandlers(get_text_response_event_filter, {"okay"}); if(!prompt.empty()) { dynamic_cast(strPanel["pic"]).setPict(pic); strPanel["prompt"].setText(prompt); } strPanel.run(); // Note: Originally it only changed the first 15 characters. std::string result = strPanel.getResult(); std::transform(result.begin(), result.end(), result.begin(), tolower); return result; } short get_num_response(short min, short max, std::string prompt, std::vector choice_names, boost::optional cancel_value, short initial_value, std::string extra_led, bool* led_output) { std::ostringstream sout; sout << prompt; set_cursor(sword_curs); cDialog numPanel(*ResMgr::dialogs.get("get-num")); numPanel.attachClickHandlers(get_num_of_items_event_filter, {"okay"}); if(extra_led.empty()){ numPanel["extra-led"].hide(); }else{ numPanel["extra-led"].setText(extra_led); numPanel["extra-led"].recalcRect(); if(led_output != nullptr) dynamic_cast(numPanel["extra-led"]).setState(*led_output ? led_red : led_off); } sout << " (" << min << '-' << max << ')'; numPanel["prompt"].setText(sout.str()); numPanel["number"].setTextToNum(initial_value); if(!choice_names.empty()){ numPanel["choose"].attachClickHandler([&choice_names, &prompt](cDialog& me,std::string,eKeyMod) -> bool { cStringChoice choose_dlg(choice_names, prompt, &me); me["number"].setTextToNum(choose_dlg.show(me["number"].getTextAsNum())); return true; }); }else{ numPanel["choose"].hide(); } if(min < max) numPanel["number"].attachFocusHandler([min,max](cDialog& me,std::string,bool losing) -> bool { if(!losing) return true; int val = me["number"].getTextAsNum(); if(val < min || val > max) { showError("Number out of range!"); return false; } return true; }); bool cancel_clicked = false; if(cancel_value){ numPanel["cancel"].attachClickHandler([cancel_value, &cancel_clicked](cDialog& me,std::string,eKeyMod) -> bool { cancel_clicked = true; me.setResult(*cancel_value); me.toast(false); return true; }); }else{ numPanel["cancel"].hide(); } numPanel.run(); if(!cancel_clicked && led_output != nullptr){ *led_output = dynamic_cast(numPanel["extra-led"]).getState() == led_red; } return numPanel.getResult(); } static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod) { me.toast(true); if(item_hit == "pick-all"){ me.setResult(7); }else if(item_hit != "cancel"){ short which_pc = item_hit[item_hit.length() - 1] - '1'; me.setResult(which_pc); }else me.setResult(6); return true; } short select_pc(eSelectPC mode, std::string title, eSkill highlight_highest, bool allow_choose_all) { short item_hit; set_cursor(sword_curs); cDialog selectPc(*ResMgr::dialogs.get("select-pc")); selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6", "pick-all"}); // The default title is defined in select-pc.xml if(!title.empty()) selectPc["title"].setText(title); bool any_options = false; std::array pc_skills = {0, 0, 0, 0, 0, 0}; short highest_skill = 0; short last_skill = 0; bool all_pcs_equal = true; for(short i = 0; i < 6; i++) { std::string n = boost::lexical_cast(i + 1); bool can_pick = true; std::string extra_info = ""; if(univ.party[i].main_status == eMainStatus::ABSENT || univ.party[i].main_status == eMainStatus::FLED) can_pick = false; else switch(mode) { case eSelectPC::ONLY_CAN_GIVE_FROM_ACTIVE: if(i == univ.cur_pc){ can_pick = false; break; } if((overall_mode == MODE_COMBAT) && !adjacent(univ.party[univ.cur_pc].combat_pos,univ.party[i].combat_pos)) { can_pick = false; extra_info = "too far away"; break; } BOOST_FALLTHROUGH; case eSelectPC::ONLY_CAN_GIVE: switch(univ.party[i].can_give_item(item_store)){ case eBuyStatus::TOO_HEAVY: extra_info = "item too heavy"; if(false) // skip first line of fallthrough case eBuyStatus::NO_SPACE: extra_info = "no item slot"; can_pick = false; break; case eBuyStatus::DEAD: // Extra info not really needed, and kind of silly to print can_pick = false; break; default: break; } break; case eSelectPC::ONLY_LIVING_WITH_ITEM_SLOT: if(!univ.party[i].has_space()){ can_pick = false; extra_info = "no item slot"; } BOOST_FALLTHROUGH; case eSelectPC::ONLY_LIVING: if(univ.party[i].main_status != eMainStatus::ALIVE) can_pick = false; break; case eSelectPC::ONLY_DEAD: if(univ.party[i].main_status == eMainStatus::ALIVE) can_pick = false; break; case eSelectPC::ONLY_CAN_LOCKPICK:{ if(univ.party[i].main_status != eMainStatus::ALIVE){ can_pick = false; break; } if(!univ.party[i].has_abil(eItemAbil::LOCKPICKS)){ can_pick = false; extra_info = "no picks"; break; } const cInvenSlot& pick_slot = univ.party[i].has_abil_equip(eItemAbil::LOCKPICKS); if(!pick_slot){ can_pick = false; extra_info = "picks not equipped"; break; }else{ const cItem& picks = *pick_slot; std::string pick_name = picks.name; if(picks.ident) pick_name = picks.full_name; extra_info = pick_name + " x " + std::to_string(picks.charges); } }break; // Suppress a compiler warning: default: break; } selectPc["pc" + n].setText(univ.party[i].name); if(highlight_highest != eSkill::INVALID){ selectPc["pc" + n].appendText(" ({{skill}})"); } if(!extra_info.empty()){ selectPc["pc" + n].appendText(": " + extra_info); } if(highlight_highest != eSkill::INVALID){ short skill = univ.party[i].skills[highlight_highest]; pc_skills[i] = skill; if(univ.party[i].is_alive()){ if(skill > highest_skill) highest_skill = skill; if(skill != last_skill) all_pcs_equal = false; last_skill = skill; } } if(!can_pick) { selectPc["pick" + n].hide(); if(extra_info.empty()) selectPc["pc" + n].hide(); } else { any_options = true; } } if(!any_options){ if(mode == eSelectPC::ONLY_CAN_LOCKPICK){ ASB(" No one has lockpicks equipped."); print_buf(); } return 8; } if(highlight_highest != eSkill::INVALID){ selectPc["hint"].replaceText("{{skill}}", get_str("skills", (int)highlight_highest * 2 + 1)); for(int i = 0; i < 6; i++){ std::string n = boost::lexical_cast(i + 1); selectPc["pc" + n].replaceText("{{skill}}", std::to_string(pc_skills[i])); if(pc_skills[i] == highest_skill && !all_pcs_equal){ selectPc["pc" + n].setColour(Colours::LIGHT_GREEN); } } }else{ selectPc["hint"].hide(); } if(!allow_choose_all){ selectPc["pick-all"].hide(); selectPc["all"].hide(); } selectPc.run(); item_hit = selectPc.getResult(); return item_hit; }