diff --git a/rsrc/dialogs/get-items.xml b/rsrc/dialogs/get-items.xml
index 3eeccd36..4fdeacbc 100644
--- a/rsrc/dialogs/get-items.xml
+++ b/rsrc/dialogs/get-items.xml
@@ -9,6 +9,7 @@
+
diff --git a/rsrc/dialogs/select-pc.xml b/rsrc/dialogs/select-pc.xml
index 328e354b..202ebf45 100644
--- a/rsrc/dialogs/select-pc.xml
+++ b/rsrc/dialogs/select-pc.xml
@@ -1,7 +1,7 @@
diff --git a/src/game/boe.actions.cpp b/src/game/boe.actions.cpp
index 9410a05f..ab61765c 100644
--- a/src/game/boe.actions.cpp
+++ b/src/game/boe.actions.cpp
@@ -651,7 +651,7 @@ static void handle_drop_item(location destination, bool& need_redraw) {
add_string_to_buf("Drop: must be adjacent.");
else if(sight_obscurity(destination.x,destination.y) == 5)
ASB("Drop: Space is blocked.");
- else drop_item(univ.cur_pc,store_drop_item,destination);
+ else drop_item(stat_window == ITEM_WIN_JUNK ? 7 : univ.cur_pc,store_drop_item,destination);
overall_mode = MODE_TOWN;
}
need_redraw = true;
@@ -796,7 +796,7 @@ static void handle_give_item(short item_hit, bool& did_something, bool& need_red
add_string_to_buf("Give item: Finish what you're doing first.");
return;
}
- give_thing(stat_window, item_hit);
+ give_thing(stat_window!=ITEM_WIN_JUNK ? stat_window : 7, item_hit);
did_something = true;
need_redraw = true;
take_ap(1);
@@ -1367,7 +1367,7 @@ bool handle_action(const sf::Event& event) {
break;
}
}
- if(stat_window <= ITEM_WIN_QUESTS) {
+ if(stat_window <= ITEM_WIN_JUNK) {
for(int i = 0; i < 8; i++)
for(auto j : item_buttons[i].keys())
if(item_area_button_active[i][j] && point_in_area.in(item_buttons[i][j])) {
@@ -1399,7 +1399,10 @@ bool handle_action(const sf::Event& event) {
put_spec_item_info(spec_item_array[item_hit]);
else if(stat_window == ITEM_WIN_QUESTS)
put_quest_info(spec_item_array[item_hit]);
- else display_pc_item(stat_window, item_hit,univ.party[stat_window].items[item_hit],0);
+ else if (stat_window == ITEM_WIN_JUNK)
+ display_pc_item(stat_window, item_hit,univ.party.get_junk_item(item_hit),0);
+ else
+ display_pc_item(stat_window, item_hit,univ.party[stat_window].items[item_hit],0);
break;
case ITEMBTN_SPEC: // sell? That this code was reached indicates that the item was sellable
// (Based on item_area_button_active)
@@ -1768,6 +1771,11 @@ bool handle_keystroke(const sf::Event& event){
case '1': case '2': case '3': case '4': case '5': case '6':
handle_switch_pc(((short) chr) - 49, need_redraw, need_reprint);
break;
+
+ case '7': // Junk
+ if (univ.party.show_junk_bag)
+ set_stat_window(ITEM_WIN_JUNK);
+ break;
case '9': // Special items
set_stat_window(ITEM_WIN_SPECIAL);
@@ -2708,6 +2716,7 @@ void start_new_game(bool force) {
// use user's easy mode and less wandering mode
univ.party.easy_mode=get_bool_pref("EasyMode", false);
univ.party.less_wm=get_bool_pref("LessWanderingMonsters", false);
+ univ.party.show_junk_bag=get_bool_pref("ShowJunkBag", false);
if(force) return;
fs::path file = nav_put_party();
if(!file.empty()) save_party(file, univ);
diff --git a/src/game/boe.consts.hpp b/src/game/boe.consts.hpp
index 6aac9e90..6bb20a99 100644
--- a/src/game/boe.consts.hpp
+++ b/src/game/boe.consts.hpp
@@ -138,6 +138,7 @@ enum eItemWinMode {
ITEM_WIN_PC6 = 5,
ITEM_WIN_SPECIAL = 6,
ITEM_WIN_QUESTS = 7,
+ ITEM_WIN_JUNK = 8,
};
// Gobal window rects
diff --git a/src/game/boe.dlgutil.cpp b/src/game/boe.dlgutil.cpp
index 97ce5b75..ba66267d 100644
--- a/src/game/boe.dlgutil.cpp
+++ b/src/game/boe.dlgutil.cpp
@@ -1202,17 +1202,13 @@ static bool prefs_event_filter (cDialog& me, std::string id, eKeyMod) {
if (overall_mode != MODE_STARTUP && party_in_memory) {
univ.party.easy_mode = dynamic_cast(me["easier"]).getState() != led_off;
univ.party.less_wm = dynamic_cast(me["lesswm"]).getState() != led_off;
+ univ.party.show_junk_bag = dynamic_cast(me["junk"]).getState() != led_off;
}
set_pref("DrawTerrainAnimation", dynamic_cast(me["noanim"]).getState() == led_off);
set_pref("DrawTerrainShoreFrills", dynamic_cast(me["noshore"]).getState() == led_off);
set_pref("ShowStartupSplash", dynamic_cast(me["skipsplash"]).getState() == led_off);
+ set_pref("ShowJunkBag", dynamic_cast(me["junk"]).getState() != led_off);
std::string speed = dynamic_cast(me["speed"]).getSelected();
- /* TODO: Should I add these additional preferences from Windows?
- party.stuff_done[SDF_NO_TARGET_LINE] = cd_get_led(1099,50);
- party.stuff_done[SDF_LESS_SOUND] = cd_get_led(1099,52);
- party.stuff_done[SDF_FASTER_BOOM_SPACES] = cd_get_led(1099,56);
- party.stuff_done[SDF_ASK_ABOUT_TEXT_BOX] = cd_get_led(1099,60);
- */
if(speed == "fast")
set_pref("GameSpeed", 0);
else if(speed == "med")
@@ -1286,9 +1282,11 @@ void pick_preferences() {
if(overall_mode == MODE_STARTUP && !party_in_memory) {
dynamic_cast(prefsDlog["easier"]).setState(get_bool_pref("EasyMode") ? led_red : led_off);
dynamic_cast(prefsDlog["lesswm"]).setState(get_bool_pref("LessWanderingMonsters") ? led_red : led_off);
+ dynamic_cast(prefsDlog["junk"]).setState(get_bool_pref("ShowJunkBag", false) ? led_red : led_off);
} else {
dynamic_cast(prefsDlog["easier"]).setState(univ.party.easy_mode ? led_red : led_off);
dynamic_cast(prefsDlog["lesswm"]).setState(univ.party.less_wm ? led_red : led_off);
+ dynamic_cast(prefsDlog["junk"]).setState(univ.party.show_junk_bag ? led_red : led_off);
}
dynamic_cast(prefsDlog["noanim"]).setState(get_bool_pref("DrawTerrainAnimations", true) ? led_off : led_red);
dynamic_cast(prefsDlog["noshore"]).setState(get_bool_pref("DrawTerrainShoreFrills", true) ? led_off : led_red);
diff --git a/src/game/boe.fileio.cpp b/src/game/boe.fileio.cpp
index 497a3012..82c01de1 100644
--- a/src/game/boe.fileio.cpp
+++ b/src/game/boe.fileio.cpp
@@ -60,6 +60,7 @@ void finish_load_party(){
// use user's easy mode and less wandering mode
univ.party.easy_mode=get_bool_pref("EasyMode", false);
univ.party.less_wm=get_bool_pref("LessWanderingMonsters", false);
+ univ.party.show_junk_bag=get_bool_pref("ShowJunkBag", false);
// now if not in scen, this is it.
if(!in_scen) {
diff --git a/src/game/boe.infodlg.cpp b/src/game/boe.infodlg.cpp
index c0a939cc..21584d4a 100644
--- a/src/game/boe.infodlg.cpp
+++ b/src/game/boe.infodlg.cpp
@@ -202,10 +202,10 @@ static bool display_pc_item_event_filter(cDialog& me, std::string item_hit, cIte
return true;
}
-void display_pc_item(short pc_num,short item,cItem si,cDialog* parent) {
+void display_pc_item(short pc_num,short item,cItem const &si,cDialog* parent) {
using namespace std::placeholders;
cItem store_i;
- if(pc_num == 6)
+ if(pc_num == 6 || pc_num == ITEM_WIN_JUNK)
store_i = si;
else store_i = univ.party[pc_num].items[item];
set_cursor(sword_curs);
diff --git a/src/game/boe.infodlg.hpp b/src/game/boe.infodlg.hpp
index 933b80fe..d410ff37 100644
--- a/src/game/boe.infodlg.hpp
+++ b/src/game/boe.infodlg.hpp
@@ -4,13 +4,14 @@
#include "item.hpp"
#include "monster.hpp"
+#include "party.hpp"
#include "pc.hpp"
#include "creature.hpp"
class cDialog;
void display_spells(eSkill mode,short force_spell,cDialog* parent);
void display_skills(eSkill force_skill,cDialog* parent);
-void display_pc_item(short pc_num,short item,class cItem si,cDialog* parent);
+void display_pc_item(short pc_num,short item,class cItem const &si,cDialog* parent);
void display_monst(short array_pos,cCreature *which_m,short mode);
void display_alchemy();
void display_traits_graphics();
diff --git a/src/game/boe.items.cpp b/src/game/boe.items.cpp
index a8713940..f7709863 100644
--- a/src/game/boe.items.cpp
+++ b/src/game/boe.items.cpp
@@ -102,59 +102,59 @@ void equip_item(short pc_num,short item_num) {
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_given_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_data[0];
-
- 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_given_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_given_item)
- univ.party[pc_num].take_item(item_num);
- break;
- default: //should never be reached
- break;
+ if (pc_num>7) {
+ add_string_to_buf("Drop: Unexpected pc.");
+ return;
}
- if(spec >= 0)
- while(how_many--)
- run_special(eSpecCtx::DROP_ITEM, eSpecCtxType::SCEN, spec, where_drop, &s1, &s2, &need_redraw);
+
+ cItem &item_to_drop = pc_num<=6 ? univ.party[pc_num].items[item_num] : univ.party.get_junk_item(item_num);
+ if(pc_num<6 && univ.party[pc_num].equip[item_num] && item_to_drop.cursed) {
+ add_string_to_buf("Drop: Item is cursed.");
+ return;
+ }
+ if (overall_mode== MODE_OUTDOORS) {
+ std::string choice = cChoiceDlog("drop-item-confirm",{"okay","cancel"}).show();
+ if(choice == "cancel")
+ return;
+ add_string_to_buf("Drop: OK");
+ }
+
+ cItem item_store = item_to_drop;
+ int spec = -1;
+ if(item_to_drop.ability == eItemAbil::DROP_CALL_SPECIAL)
+ spec = item_to_drop.abil_data[0];
+
+ short how_many = 1;
+ bool take_given_item = true;
+ if(item_to_drop.type_flag > 0 && item_to_drop.charges > 1) {
+ how_many = get_num_of_items(item_to_drop.charges);
+ if(how_many <= 0)
+ return;
+ if(how_many < item_to_drop.charges)
+ take_given_item = false;
+ item_store.charges = how_many;
+ item_to_drop.charges -= how_many;
+ }
+ if (overall_mode==MODE_DROP_TOWN || overall_mode==MODE_DROP_COMBAT) {
+ if (place_item(item_store,where_drop,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");
+ }
+ if(take_given_item) {
+ if (pc_num<=6)
+ univ.party[pc_num].take_item(item_num);
+ else
+ univ.party.take_junk_item(item_num);
+ }
+ if(spec < 0) return;
+
+ bool need_redraw = false;
+ short s1, s2;
+ while(how_many--)
+ run_special(eSpecCtx::DROP_ITEM, eSpecCtxType::SCEN, spec, where_drop, &s1, &s2, &need_redraw);
if(need_redraw)
draw_terrain(0);
}
@@ -183,41 +183,56 @@ bool place_item(cItem item,location where,bool contained) {
}
void give_thing(short pc_num, short item_num) {
+ if (pc_num>7) {
+ add_string_to_buf("Give: Unexpected pc.");
+ return;
+ }
short who_to,how_many = 0;
cItem item_store;
bool take_given_item = true;
- if(univ.party[pc_num].equip[item_num] && univ.party[pc_num].items[item_num].cursed)
+ cItem &item_to_give=pc_num==7 ? univ.party.get_junk_item(item_num) : univ.party[pc_num].items[item_num];
+ if(pc_num<=6 && univ.party[pc_num].equip[item_num] && item_to_give.cursed)
add_string_to_buf("Give: Item is cursed.");
else {
- item_store = univ.party[pc_num].items[item_num];
- who_to = char_select_pc(3,"Give item to who?");
- if((overall_mode == MODE_COMBAT) && !adjacent(univ.party[pc_num].combat_pos,univ.party[who_to].combat_pos)) {
+ item_store = item_to_give;
+ if (univ.party.show_junk_bag && pc_num!=7)
+ who_to = char_select_pc(4, "Give item to who? (type '7' for Bag)");
+ else
+ who_to = char_select_pc(3, "Give item to who?");
+ if(overall_mode == MODE_COMBAT && who_to < 6 && !adjacent(univ.party[pc_num].combat_pos,univ.party[who_to].combat_pos)) {
add_string_to_buf("Give: Must be adjacent.");
who_to = 6;
}
-
- if((who_to < 6) && (who_to != pc_num)
- && ((overall_mode != MODE_COMBAT) || (adjacent(univ.party[pc_num].combat_pos,univ.party[who_to].combat_pos)))) {
- if((item_store.type_flag > 0) && (item_store.charges > 1)) {
- how_many = get_num_of_items(item_store.charges);
+ else if ((who_to < 6 || who_to == 7) && who_to != pc_num) {
+ if((item_to_give.type_flag > 0) && (item_to_give.charges > 1)) {
+ how_many = get_num_of_items(item_to_give.charges);
if(how_many == 0)
return;
- if(how_many < item_store.charges)
+ if(how_many < item_to_give.charges)
take_given_item = false;
- univ.party[pc_num].items[item_num].charges -= how_many;
+ item_to_give.charges -= how_many;
item_store.charges = how_many;
}
- if(univ.party[who_to].give_item(item_store,0)) {
- if(take_given_item)
- univ.party[pc_num].take_item(item_num);
- }
+ if ((who_to==7 && univ.party.give_junk_item(item_store, is_town() ? univ.party.town_num : 200)) ||
+ (who_to<6 && univ.party[who_to].give_item(item_store,0)))
+ ; // ok
else {
- if(!univ.party[who_to].has_space())
+ if(who_to<6 && !univ.party[who_to].has_space())
ASB("Can't give: PC has max. # of items.");
- else ASB("Can't give: PC carrying too much.");
+ else if(who_to<6)
+ ASB("Can't give: PC carrying too much.");
+ else
+ ASB("Can't give: unknown problem.");
if(how_many > 0)
- univ.party[pc_num].items[item_num].charges += how_many;
+ item_to_give.charges += how_many;
+ take_given_item = false;
+ }
+ if(take_given_item) {
+ if (pc_num<=6)
+ univ.party[pc_num].take_item(item_num);
+ else
+ univ.party.take_junk_item(item_num);
}
}
}
@@ -293,6 +308,24 @@ short get_item(location place,short pc_num,bool check_container) {
}
+bool is_town_hostile()
+{
+ if (!is_town())
+ return true;
+ if (univ.town.monst.hostile)
+ return true;
+ int numFriendly=0, numHostile=0;
+ for (auto const &monster : univ.town.monst) {
+ if (!monster.active || monster.summon_time>0)
+ continue;
+ if (monster.is_friendly())
+ ++numFriendly;
+ else
+ ++numHostile;
+ }
+ return numHostile>=numFriendly;
+}
+
void make_town_hostile() {
set_town_attitude(0, -1, eAttitude::HOSTILE_A);
return;
@@ -364,15 +397,16 @@ static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& curr
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;
-
}
+ else if (!univ.party.show_junk_bag && current_getting_pc==7)
+ 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))) {
+ && (!is_combat() || univ.cur_pc == i)) {
if(current_getting_pc == 6)
current_getting_pc = i;
me[id].show();
@@ -381,11 +415,10 @@ static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& curr
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);
+ me.addLabelFor(id, current_getting_pc == i ? "* " : " ", LABEL_LEFT, 7, true);
}
-
+ if (univ.party.show_junk_bag)
+ me.addLabelFor("junk", current_getting_pc == 7 ? "* " : " ", LABEL_LEFT, 7, true);
if (first_item_shown >= item_array.size())
first_item_shown=std::max(size_t(8),item_array.size())-8;
// darken arrows, as appropriate
@@ -446,9 +479,9 @@ static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& curr
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") {
+ if(id == "done")
me.toast(true);
- } else if(id == "up") {
+ else if(id == "up") {
if(first_item_shown > 0) {
first_item_shown -= 8;
put_item_graphics(me, first_item_shown, current_getting_pc, item_array);
@@ -461,7 +494,11 @@ static bool display_item_event_filter(cDialog& me, std::string id, size_t& first
} 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(id.substr(0,4) == "item" && id.length()==10 && id.substr(5,5)=="-info") {
+ } else if (id == "junk") {
+ current_getting_pc = 7;
+ put_item_graphics(me, first_item_shown, current_getting_pc, item_array);
+ }
+ else if(id.substr(0,4) == "item" && id.length()==10 && id.substr(5,5)=="-info") {
size_t item_hit;
item_hit = id[4] - '1';
item_hit += first_item_shown;
@@ -507,7 +544,7 @@ static bool display_item_event_filter(cDialog& me, std::string id, size_t& first
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()) {
+ if(current_getting_pc<6 && !allow_overload && item.item_weight() > univ.party[current_getting_pc].free_weight()) {
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
me["prompt"].setText("It's too heavy to carry.");
give_help(38,0,me);
@@ -516,10 +553,14 @@ static bool display_item_event_filter(cDialog& me, std::string id, size_t& first
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);
+ if (current_getting_pc<6) {
+ int flags = GIVE_DO_PRINT;
+ if(allow_overload)
+ flags |= GIVE_ALLOW_OVERLOAD;
+ univ.party[current_getting_pc].give_item(item, flags);
+ }
+ else
+ univ.party.give_junk_item(item, is_combat() ? -1 : is_town() ? univ.party.town_num : 200);
}
*item_array[item_hit] = cItem();
item_array.erase(item_array.begin() + item_hit);
@@ -557,7 +598,7 @@ bool display_item(location from_loc,short /*pc_num*/,short mode, bool check_cont
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);
-
+ if (stat_window==ITEM_WIN_JUNK) set_stat_window(stat_window); // FIXME MUST NO BE HERE
put_item_screen(stat_window);
put_pc_screen();
@@ -571,7 +612,7 @@ bool show_get_items(std::string titleText, std::vector& itemRefs, short
cDialog itemDialog("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.attachClickHandlers(handler, {"pc1", "pc2", "pc3", "pc4", "pc5", "pc6", "junk"});
itemDialog.setResult(false);
cTextMsg& title = dynamic_cast(itemDialog["title"]);
@@ -586,6 +627,10 @@ bool show_get_items(std::string titleText, std::vector& itemRefs, short
sout << "item" << i << "-info";
itemDialog[sout.str()].attachClickHandler(handler);
}
+ if (univ.party.show_junk_bag)
+ itemDialog["junk"].show();
+ else
+ itemDialog["junk"].hide();
put_item_graphics(itemDialog, first_item, pc_getting, itemRefs);
void (*give_help)(short,short,cDialog&) = ::give_help;
@@ -881,7 +926,9 @@ short get_num_response(short min, short max, std::string prompt) {
static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod) {
me.toast(true);
- if(item_hit != "cancel") {
+ if (item_hit == "junk")
+ 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);
@@ -889,14 +936,14 @@ static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod)
}
// mode determines which PCs can be picked
-// 0 - only living pcs, 1 - any pc, 2 - only dead pcs, 3 - only living pcs with inventory space
+// 0 - only living pcs, 1 - any pc, 2 - only dead pcs, 3 - only living pcs with inventory space, 4 - only living pcs with inventory space or junk bag
short char_select_pc(short mode,const char *title) {
short item_hit;
set_cursor(sword_curs);
cDialog selectPc("select-pc");
- selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6"});
+ selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6", "junk"});
selectPc["title"].setText(title);
@@ -907,6 +954,7 @@ short char_select_pc(short mode,const char *title) {
can_pick = false;
else switch(mode) {
case 3:
+ case 4:
if(!univ.party[i].has_space())
can_pick = false;
// Fallthrough intentional
@@ -926,7 +974,14 @@ short char_select_pc(short mode,const char *title) {
selectPc["pc" + n].setText(univ.party[i].name);
}
}
-
+ if (mode==4) {
+ selectPc["junk"].show();
+ selectPc["junk-text"].show();
+ }
+ else {
+ selectPc["junk"].hide();
+ selectPc["junk-text"].hide();
+ }
selectPc.run();
item_hit = selectPc.getResult();
diff --git a/src/game/boe.items.hpp b/src/game/boe.items.hpp
index 6ee0de5f..2e622dde 100644
--- a/src/game/boe.items.hpp
+++ b/src/game/boe.items.hpp
@@ -1,7 +1,11 @@
#include "dialog.hpp"
+#include "item.hpp"
+#include "monster.hpp"
#include "pict.hpp"
+enum class eSpecCtxType;
+
bool GTP(short item_num);
bool silent_GTP(short item_num);
void give_gold(short amount,bool print_result);
@@ -16,6 +20,7 @@ short dist_from_party(location where);
void set_item_flag(cItem *item);
short get_item(location place,short pc_num,bool check_container);
+bool is_town_hostile();
void make_town_hostile();
void set_town_attitude(short lo,short hi,eAttitude att);
bool show_get_items(std::string titleText, std::vector& itemRefs, short pc_getting, bool overload = false);
diff --git a/src/game/boe.locutils.cpp b/src/game/boe.locutils.cpp
index 1821b6f3..bcd5129f 100644
--- a/src/game/boe.locutils.cpp
+++ b/src/game/boe.locutils.cpp
@@ -47,36 +47,21 @@ void make_explored(short i,short j, short val) {
}
}
+static bool is_out(eGameMode overall_mode) {
+ return overall_mode == MODE_OUTDOORS || overall_mode == MODE_LOOK_OUTDOORS;
+}
bool is_out() {
- if((overall_mode == MODE_OUTDOORS) || (overall_mode == MODE_LOOK_OUTDOORS))
- return true;
- else if(overall_mode == MODE_SHOPPING) {
- std::swap(overall_mode, store_pre_shop_mode);
- bool ret = is_out();
- std::swap(overall_mode, store_pre_shop_mode);
- return ret;
- } else if(overall_mode == MODE_TALKING) {
- std::swap(overall_mode, store_pre_talk_mode);
- bool ret = is_out();
- std::swap(overall_mode, store_pre_talk_mode);
- return ret;
- } else return false;
+ return is_out(overall_mode) || (overall_mode == MODE_SHOPPING && is_out(store_pre_shop_mode)) ||
+ (overall_mode == MODE_TALKING && is_out(store_pre_talk_mode));
}
+static bool is_town(eGameMode overall_mode) {
+ return (overall_mode > MODE_OUTDOORS && overall_mode < MODE_COMBAT) || overall_mode == MODE_LOOK_TOWN;
+}
bool is_town() {
- if((overall_mode > MODE_OUTDOORS && overall_mode < MODE_COMBAT) || overall_mode == MODE_LOOK_TOWN || cartoon_happening)
- return true;
- else if(overall_mode == MODE_SHOPPING) {
- std::swap(overall_mode, store_pre_shop_mode);
- bool ret = is_town();
- std::swap(overall_mode, store_pre_shop_mode);
- return ret;
- } else if(overall_mode == MODE_TALKING) {
- std::swap(overall_mode, store_pre_talk_mode);
- bool ret = is_town();
- std::swap(overall_mode, store_pre_talk_mode);
- return ret;
- } else return false;
+ return is_town(overall_mode) || cartoon_happening ||
+ (overall_mode == MODE_SHOPPING && is_town(store_pre_shop_mode)) ||
+ (overall_mode == MODE_TALKING && is_town(store_pre_talk_mode));
}
bool is_combat() {
diff --git a/src/game/boe.party.cpp b/src/game/boe.party.cpp
index f815a7fe..58e551fc 100644
--- a/src/game/boe.party.cpp
+++ b/src/game/boe.party.cpp
@@ -142,6 +142,20 @@ void put_party_in_scen(std::string scen_name) {
else
thisItem.special_class = 0;
}
+ for (size_t i = univ.party.junk_items.size(); i>0; i--) {
+ cItem & thisItem = univ.party.junk_items[i-1].first;
+ univ.party.junk_items[i-1].second.clear();
+ if (thisItem.variety == eItemType::NO_ITEM)
+ continue;
+ if (is_item_specific_to_scenario(thisItem)) {
+ if (i!=univ.party.junk_items.size())
+ std::swap(univ.party.junk_items[i-1], univ.party.junk_items.back());
+ univ.party.junk_items.pop_back();
+ item_took = true;
+ }
+ else
+ thisItem.special_class = 0;
+ }
if(item_took)
cChoiceDlog("removed-special-items").show();
@@ -596,6 +610,30 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) {
for(cPlayer& pc : univ.party)
for(cItem& item : pc.items)
item.ident = true;
+ if (univ.party.show_junk_bag && is_town() && !is_combat() && !is_town_hostile()) {
+ // now try to identify items in the junk bag,
+ // the junk bag is clearly a magic item so
+ // it must interfer with this spell
+ short numDone=0, numFailed=0;
+ for (auto &itemSet : univ.party.junk_items) {
+ if (itemSet.first.ident) continue;
+ if (get_ran(1, 0, 1)) {
+ ++numDone;
+ itemSet.first.ident=true;
+ }
+ else
+ ++numFailed;
+ }
+ if (numDone || numFailed) {
+ ASB(std::string("Junk Bag: ")+std::to_string(numDone)+'/'+std::to_string(numDone+numFailed)
+ +" items identified.");
+ }
+ if (numDone) {
+ univ.party.combine_junk_items();
+ if (stat_window==ITEM_WIN_JUNK)
+ set_stat_window(ITEM_WIN_JUNK);
+ }
+ }
break;
case eSpell::TRUE_SIGHT:
diff --git a/src/game/boe.specials.cpp b/src/game/boe.specials.cpp
index f114ba4c..197def32 100644
--- a/src/game/boe.specials.cpp
+++ b/src/game/boe.specials.cpp
@@ -2223,9 +2223,19 @@ void general_spec(const runtime_state& ctx) {
ctx.next_spec = spec.ex1b;
break;
case eSpecType::BUY_ITEMS_OF_TYPE:
- for(short i = 0; i < 144; i++)
- if(univ.party.check_class(spec.ex1a,true))
+ for(short i = 0; i < 144; i++) {
+ if(!univ.party.check_class(spec.ex1a,true))
+ break;
+ store_val++;
+ }
+ if (univ.party.show_junk_bag && is_town() && !is_combat()) {
+ int check_town_id = is_town_hostile() ? univ.party.town_num : -1;
+ for(short i = 0; i < 144; i++) {
+ if(store_val>=144 || !univ.party.check_junk_class(spec.ex1a,true,check_town_id))
+ break;
store_val++;
+ }
+ }
if(store_val == 0) {
if(spec.ex1b >= 0)
ctx.next_spec = spec.ex1b;
@@ -3351,6 +3361,9 @@ void ifthen_spec(const runtime_state& ctx) {
case eSpecType::IF_HAVE_ITEM_CLASS:
if(univ.party.check_class(spec.ex1a,spec.ex2a > 0))
ctx.next_spec = spec.ex1b;
+ else if (univ.party.show_junk_bag && is_town() && !is_combat() &&
+ univ.party.check_junk_class(spec.ex1a,spec.ex2a > 0, is_town_hostile() ? univ.party.town_num : -1))
+ ctx.next_spec = spec.ex1b;
break;
case eSpecType::IF_EQUIP_ITEM_CLASS:
for(cPlayer& pc : univ.party)
diff --git a/src/game/boe.specials.hpp b/src/game/boe.specials.hpp
index a68a4347..64696aa3 100644
--- a/src/game/boe.specials.hpp
+++ b/src/game/boe.specials.hpp
@@ -1,6 +1,12 @@
+#ifndef BOE_GAME_SPECIALS_H
+#define BOE_GAME_SPECIALS_H
#include "creature.hpp"
+struct pending_special_type;
+enum class eSpecCtx;
+enum class eSpecCtxType;
+
bool handle_wandering_specials(short mode);
bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,bool *forced);
void check_fields(location where_check,eSpecCtx mode,cPlayer& which_pc);
@@ -25,3 +31,5 @@ void run_special(pending_special_type spec, short* a, short* b, bool* redraw);
void get_strs(std::string& str1, std::string& str2,eSpecCtxType cur_type,short which_str1,short which_str2);
void set_campaign_flag(short sdf_a, short sdf_b, short cpf_a, short cpf_b, short str, bool get_send);
+
+#endif
diff --git a/src/game/boe.text.cpp b/src/game/boe.text.cpp
index 7d6a59e2..9dcf1b8c 100644
--- a/src/game/boe.text.cpp
+++ b/src/game/boe.text.cpp
@@ -7,21 +7,23 @@ const int TEXT_BUF_LEN = 70;
#include "boe.global.hpp"
#include "boe.graphutil.hpp"
-#include "universe.hpp"
+#include "boe.items.hpp"
#include "boe.text.hpp"
#include "boe.locutils.hpp"
#include "boe.infodlg.hpp"
+
+#include "enum_map.hpp"
#include "mathutil.hpp"
#include "render_text.hpp"
#include "render_image.hpp"
#include "render_shapes.hpp"
-#include "tiling.hpp"
-#include "utility.hpp"
-#include "scrollbar.hpp"
-#include "res_image.hpp"
#include "res_font.hpp"
+#include "res_image.hpp"
+#include "scrollbar.hpp"
#include "spell.hpp"
-#include "enum_map.hpp"
+#include "tiling.hpp"
+#include "universe.hpp"
+#include "utility.hpp"
typedef struct {
char line[50];
@@ -243,7 +245,9 @@ void put_item_screen(eItemWinMode screen_num) {
case ITEM_WIN_QUESTS:
win_draw_string(item_stats_gworld,upper_frame_rect,"Quests/Jobs:",eTextMode::WRAP,style);
break;
-
+ case ITEM_WIN_JUNK:
+ win_draw_string(item_stats_gworld,upper_frame_rect,"Junk Bag:",eTextMode::WRAP,style);
+ break;
default: // on an items page
pc = screen_num;
sout.str("");;
@@ -291,7 +295,58 @@ void put_item_screen(eItemWinMode screen_num) {
}
}
break;
+ case ITEM_WIN_JUNK:
+ style.colour = Colours::BLACK;
+ for(short i = 0; i < 8; i++) {
+ i_num = i + item_offset;
+ sout.str("");
+ sout << i_num + 1 << '.';
+ win_draw_string(item_stats_gworld,item_buttons[i][ITEMBTN_NAME],sout.str(),eTextMode::WRAP,style);
+
+ dest_rect = item_buttons[i][ITEMBTN_NAME];
+ dest_rect.left += 36;
+ dest_rect.top -= 2;
+
+ const cItem& item = univ.party.get_junk_item(i_num);
+ if(item.variety != eItemType::NO_ITEM) {
+ style.font = FONT_PLAIN;
+ style.colour = Colours::BLACK;
+
+ sout.str("");
+ if(!item.ident)
+ sout << item.name << " ";
+ else { /// Don't place # of charges when Sell button up and space tight
+ sout << item.full_name << ' ';
+ if(item.charges > 0 && item.ability != eItemAbil::MESSAGE && (stat_screen_mode == MODE_INVEN || stat_screen_mode == MODE_SHOP))
+ sout << '(' << int(item.charges) << ')';
+ }
+ dest_rect.left -= 2;
+ win_draw_string(item_stats_gworld,dest_rect,sout.str(),eTextMode::WRAP,style);
+ style.italic = false;
+ style.colour = Colours::BLACK;
+
+ place_item_graphic(i,item.graphic_num);
+ item_area_button_active[i][ITEMBTN_NAME] = item_area_button_active[i][ITEMBTN_ICON] = false;
+ place_item_button(3,i,ITEMBTN_INFO); // info button
+
+ if(stat_screen_mode == MODE_INVEN && !is_combat()) {
+ // check if we need to add give and drop
+ int townId=is_town() ? univ.party.town_num : 200;
+ if (univ.party.is_junk_item_compatible_with_town(i_num, townId) ||
+ (is_town() && !is_town_hostile())) {
+ place_item_button(1,i,ITEMBTN_GIVE);
+ if (is_town()) place_item_button(2,i,ITEMBTN_DROP);
+ }
+ }
+#if 0
+ if(stat_screen_mode != MODE_INVEN && stat_screen_mode != MODE_SHOP) {
+ place_buy_button(i,pc,i_num);
+ }
+#endif
+ } // end of if item is there
+ } // end of for(short i = 0; i < 8; i++)
+ break;
default: // on an items page
style.colour = Colours::BLACK;
@@ -569,6 +624,9 @@ void set_stat_window(eItemWinMode new_stat) {
array_pos = max(0,array_pos - 8);
item_sbar->setMaximum(array_pos);
break;
+ case ITEM_WIN_JUNK:
+ item_sbar->setMaximum(max(8,univ.party.junk_items.size())-8);
+ break;
default:
item_sbar->setMaximum(16);
break;
diff --git a/src/game/boe.text.hpp b/src/game/boe.text.hpp
index 5f030ff3..1b7ce1c9 100644
--- a/src/game/boe.text.hpp
+++ b/src/game/boe.text.hpp
@@ -1,5 +1,7 @@
#include
+#include "outdoors.hpp"
+
class cVehicle;
struct Texture;
diff --git a/src/universe/party.cpp b/src/universe/party.cpp
index e374401c..91959dd4 100644
--- a/src/universe/party.cpp
+++ b/src/universe/party.cpp
@@ -63,6 +63,7 @@ cParty::cParty(const cParty& other)
, hostiles_present(other.hostiles_present)
, easy_mode(other.easy_mode)
, less_wm(other.less_wm)
+ , show_junk_bag(other.show_junk_bag)
, magic_ptrs(other.magic_ptrs)
, light_level(other.light_level)
, outdoor_corner(other.outdoor_corner)
@@ -106,6 +107,7 @@ cParty::cParty(const cParty& other)
, scen_won(other.scen_won)
, scen_played(other.scen_played)
, campaign_flags(other.campaign_flags)
+ , junk_items(other.junk_items)
, pointers(other.pointers)
{
memcpy(stuff_done, other.stuff_done, sizeof(stuff_done));
@@ -133,6 +135,7 @@ void cParty::swap(cParty& other) {
std::swap(hostiles_present, other.hostiles_present);
std::swap(easy_mode, other.easy_mode);
std::swap(less_wm, other.less_wm);
+ std::swap(show_junk_bag, other.show_junk_bag);
std::swap(magic_ptrs, other.magic_ptrs);
std::swap(light_level, other.light_level);
std::swap(outdoor_corner, other.outdoor_corner);
@@ -177,6 +180,7 @@ void cParty::swap(cParty& other) {
std::swap(scen_won, other.scen_won);
std::swap(scen_played, other.scen_played);
std::swap(campaign_flags, other.campaign_flags);
+ std::swap(junk_items, other.junk_items);
std::swap(pointers, other.pointers);
std::swap(stuff_done, other.stuff_done);
std::swap(setup, other.setup);
@@ -230,6 +234,104 @@ cMonster &cParty::get_summon(mon_num_t id) {
return bad_monster;
}
+cItem const &cParty::get_junk_item(item_num_t id) const
+{
+ if (id0;
+ return false;
+}
+
+static bool combine_items(cItem &item1, cItem const &item2)
+{
+ if (item1.variety != eItemType::NO_ITEM && item1.ident && item2.ident &&
+ item2.variety != eItemType::NO_ITEM && item1.type_flag == item2.type_flag &&
+ item1.name == item2.name && item1.special_class == item2.special_class) {
+ short test = item1.charges + item2.charges;
+ if(test > 125) {
+ item1.charges = 125;
+ if(cParty::print_result)
+ cParty::print_result("(Can have at most 125 of any item.");
+ }
+ else item1.charges += item2.charges;
+ return true;
+ }
+ return false;
+}
+
+bool cParty::give_junk_item(cItem const &item, int townId) {
+ if(item.variety == eItemType::NO_ITEM)
+ return true;
+ if(item.variety == eItemType::GOLD || item.variety == eItemType::FOOD || item.variety == eItemType::QUEST || item.variety == eItemType::SPECIAL) {
+ if(print_result)
+ print_result("Trying to add unexpected items in junk bag.");
+ return false;
+ }
+ cItem fItem(item);
+ fItem.property = false;
+ fItem.contained = false;
+ fItem.held = false;
+ if (fItem.charges<0) fItem.charges=1;
+ if(fItem.type_flag > 0 && fItem.ident) {
+ for(auto &jItemSet : junk_items) {
+ if(combine_items(jItemSet.first, fItem)) {
+ if (townId>=0) jItemSet.second.insert(townId);
+ return true;
+ }
+ }
+ }
+ std::set listTowns;
+ if (townId>=0) listTowns.insert(townId);
+ junk_items.push_back(std::make_pair(fItem,listTowns));
+ return true;
+}
+
+void cParty::combine_junk_items()
+{
+ // FIXME: probably better to first construct a multimap of name => id, items for identify id
+ // then for each item which shares the same names check if we combine them and keep a set
+ // of item id to remove, then we can remove the item
+ for (size_t i=junk_items.size(); i>0; --i) {
+ auto const &item=junk_items[i-1].first;
+ if (!item.ident) continue;
+ for (size_t j=i-1; j>0; --j) {
+ if (!combine_items(junk_items[j-1].first, item))
+ continue;
+ if (i!=j)
+ junk_items[j-1].second.insert(junk_items[i-1].second.begin(), junk_items[i-1].second.end());
+ std::swap(junk_items[i-1],junk_items.back());
+ junk_items.pop_back();
+ break;
+ }
+ }
+}
+
+void cParty::take_junk_item(item_num_t id)
+{
+ if (id>=junk_items.size()) {
+ if(print_result)
+ print_result("Trying to remove unexistant item in junk bag.");
+ return;
+ }
+ junk_items.erase(junk_items.begin()+id);
+}
+
void cParty::import_legacy(legacy::party_record_type const & old, cUniverse& univ){
scen_name = old.scen_name;
age = old.age;
@@ -774,6 +876,26 @@ bool cParty::check_class(unsigned int item_class,bool take) {
return false;
}
+bool cParty::check_junk_class(unsigned int item_class,bool take,int townId) {
+ if(!show_junk_bag || item_class == 0)
+ return false;
+ for (size_t i=0; i=0 && itemSet.second.count(townId)==0)
+ continue;
+ if (take) {
+ if(itemSet.first.charges > 1)
+ --itemSet.first.charges;
+ else
+ take_junk_item(i);
+ return true;
+ }
+ }
+ return false;
+}
+
bool cParty::start_timer(short time, spec_num_t node, eSpecCtxType type){
if(party_event_timers.size() == party_event_timers.max_size()) return false; // Shouldn't be reached
cTimer t;
@@ -793,6 +915,7 @@ void cParty::writeTo(std::ostream& file) const {
file << "HOSTILES " << int(hostiles_present) << '\n';
file << "EASY " << int(easy_mode) << '\n';
file << "LESSWM " << int(less_wm) << '\n';
+ file << "JUNKBAG" << int(show_junk_bag) << "\n";
for(int i = 0; i < 310; i++)
for(int j = 0; j < 50; j++)
if(stuff_done[i][j] > 0)
@@ -845,8 +968,8 @@ void cParty::writeTo(std::ostream& file) const {
file << "PLAYED " << scen_played << '\n';
for(auto p : active_quests)
file << "QUEST " << p.first << ' ' << p.second.status << ' ' << p.second.start << ' ' << p.second.source << '\n';
- for(auto p : store_limited_stock) {
- for(auto p2 : p.second) {
+ for(auto const &p : store_limited_stock) {
+ for(auto const &p2 : p.second) {
file << "SHOPSTOCK " << p.first << ' ' << p2.first << ' ' << p2.second << '\n';
}
}
@@ -876,6 +999,17 @@ void cParty::writeTo(std::ostream& file) const {
}
}
file << '\f';
+ for (size_t i=0; i> n;
less_wm = n;
+ } else if(cur == "JUNKBAG") {
+ int n;
+ sin >> n;
+ show_junk_bag = n;
} else if(cur == "CREATEVERSION") {
unsigned long long version;
sin >> std::hex >> version >> std::dec;
@@ -1118,6 +1256,17 @@ void cParty::readFrom(std::istream& file){
int i,j;
bin >> i >> j;
magic_store_items[i][j].readFrom(bin);
+ } else if (cur == "JUNKITEM") {
+ junk_items.emplace_back();
+ int n;
+ bin >> n;
+ for (int i=0; i> town;
+ if (town<0) break;
+ junk_items.back().second.insert(town);
+ }
+ junk_items.back().first.readFrom(bin);
} else if(cur == "ENCOUNTER") {
int i;
bin >> i;
@@ -1230,7 +1379,8 @@ void cParty::readFrom(std::istream& file){
}
cPlayer& cParty::operator[](unsigned short n){
- if(n > 6) throw std::out_of_range("Attempt to access a player that doesn't exist.");
+ if(n > 6)
+ throw std::out_of_range("Attempt to access a player that doesn't exist.");
else if(n == 6)
return *adven[0]; // TODO: PC #6 should never be accessed, but bounds checking is rarely done, so this is a quick fix.
return *adven[n];
diff --git a/src/universe/party.hpp b/src/universe/party.hpp
index 6d7d2d73..2a987908 100644
--- a/src/universe/party.hpp
+++ b/src/universe/party.hpp
@@ -9,11 +9,12 @@
#ifndef BOE_DATA_PARTY_H
#define BOE_DATA_PARTY_H
-#include
-#include
#include
#include