diff --git a/src/boe.actions.cpp b/src/boe.actions.cpp index 62e03b0a..3a899cf3 100644 --- a/src/boe.actions.cpp +++ b/src/boe.actions.cpp @@ -722,14 +722,15 @@ static void handle_item_shop_action(short item_hit) { play_sound(68); ASB("Your item is identified."); univ.party[stat_window].items[item_hit].ident = true; - combine_things(stat_window); + univ.party[stat_window].combine_things(); } break; case 3: case 4: case 5: // various selling play_sound(-39); univ.party.gold += store_selling_values[i]; ASB("You sell your item."); - take_item(stat_window,item_hit); + univ.party[stat_window].take_item(item_hit); + put_item_screen(stat_window,1); break; case 6: // enchant item if(!take_gold(store_selling_values[i],false)) @@ -2200,7 +2201,7 @@ void do_rest(long length, int hp_restore, int mp_restore) { for(int i = 0; i < 6; i++) univ.party[i].status.clear(); // Specials countdowns - if((length > 500 || age_before / 500 < univ.party.age / 500) && party_has_abil(eItemAbil::OCCASIONAL_STATUS)) { + if((length > 500 || age_before / 500 < univ.party.age / 500) && univ.party.has_abil(eItemAbil::OCCASIONAL_STATUS)) { // TODO: There used to be a "display strings" here; should we hook in a special node call? for(int i = 0; i < 6; i++) for(int j = 0; j < 24; j++) { @@ -2230,7 +2231,7 @@ void do_rest(long length, int hp_restore, int mp_restore) { if(univ.party[i].traits[eTrait::CHRONIC_DISEASE] && get_ran(1,0,110) == 1) { disease_pc(i,6); } - short item = pc_has_abil_equip(i,eItemAbil::REGENERATE); + short item = univ.party[i].has_abil_equip(eItemAbil::REGENERATE); if(item < 24 && univ.party[i].cur_health < univ.party[i].max_health && (overall_mode > MODE_OUTDOORS || get_ran(1,0,10) == 5)){ int j = get_ran(1,0,univ.party[i].items[item].abil_data[0] / 3); if(univ.party[i].items[item].abil_data[0] / 3 == 0) @@ -2311,7 +2312,7 @@ void increase_age() { } // Specials countdowns - if(univ.party.age % 500 == 0 && party_has_abil(eItemAbil::OCCASIONAL_STATUS)) { + if(univ.party.age % 500 == 0 && univ.party.has_abil(eItemAbil::OCCASIONAL_STATUS)) { // TODO: There used to be a "display strings" here; should we hook in a special node call? for(int i = 0; i < 6; i++) for(int j = 0; j < 24; j++) { @@ -2456,7 +2457,7 @@ void increase_age() { update_stat = true; move_to_zero(univ.party[i].status[eStatus::BLESS_CURSE]); move_to_zero(univ.party[i].status[eStatus::HASTE_SLOW]); - if((item = pc_has_abil_equip(i,eItemAbil::REGENERATE)) < 24 + if((item = univ.party[i].has_abil_equip(eItemAbil::REGENERATE)) < 24 && (univ.party[i].cur_health < univ.party[i].max_health) && ((overall_mode > MODE_OUTDOORS) || (get_ran(1,0,10) == 5))){ j = get_ran(1,0,univ.party[i].items[item].abil_data[0] / 3); @@ -2516,14 +2517,10 @@ void handle_hunting() { } void switch_pc(short which) { - cPlayer store_pc; - if(current_switch < 6) { if(current_switch != which) { add_string_to_buf("Switch: OK."); - store_pc = univ.party[which]; - univ.party[which] = univ.party[current_switch]; - univ.party[current_switch] = store_pc; + univ.party.swap_pcs(which, current_switch); if(current_pc == current_switch) current_pc = which; else if(current_pc == which) @@ -2549,7 +2546,7 @@ void drop_pc(short which) { add_string_to_buf("Delete PC: OK. "); kill_pc(which,eMainStatus::ABSENT); for(short i = which; i < 5; i++) - univ.party[i] = univ.party[i + 1]; + univ.party.swap_pcs(i, i + 1); univ.party[5].main_status = eMainStatus::ABSENT; set_stat_window(0); put_pc_screen(); diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index 08a50b77..86b6ff9e 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -547,11 +547,11 @@ void pc_attack(short who_att,short target) { } - if((skill_item = pc_has_abil_equip(who_att,eItemAbil::SKILL)) < 24) { + if((skill_item = univ.party[who_att].has_abil_equip(eItemAbil::SKILL)) < 24) { hit_adj += 5 * (univ.party[who_att].items[skill_item].item_level / 2 + 1); dam_adj += univ.party[who_att].items[skill_item].item_level / 2; } - if((skill_item = pc_has_abil_equip(who_att,eItemAbil::GIANT_STRENGTH)) < 24) { + if((skill_item = univ.party[who_att].has_abil_equip(eItemAbil::GIANT_STRENGTH)) < 24) { dam_adj += univ.party[who_att].items[skill_item].item_level; hit_adj += univ.party[who_att].items[skill_item].item_level * 2; } @@ -668,7 +668,7 @@ void pc_attack_weapon(short who_att,short target,short hit_adj,short dam_adj,cIt // poison if(univ.party[who_att].status[eStatus::POISONED_WEAPON] > 0) { short poison_amt = univ.party[who_att].status[eStatus::POISONED_WEAPON]; - if(pc_has_abil_equip(who_att,eItemAbil::POISON_AUGMENT) < 24) + if(univ.party[who_att].has_abil_equip(eItemAbil::POISON_AUGMENT) < 24) poison_amt += 2; poison_monst(which_m,poison_amt); move_to_zero(univ.party[who_att].status[eStatus::POISONED_WEAPON]); @@ -1209,11 +1209,13 @@ void do_combat_cast(location target) { if((cur_monst->mu == 0) && (cur_monst->cl == 0)) add_string_to_buf(" Can't duel: no magic."); else { - item = pc_has_abil(current_pc,eItemAbil::SMOKY_CRYSTAL); + item = univ.party[current_pc].has_abil(eItemAbil::SMOKY_CRYSTAL); if(item >= 24) add_string_to_buf(" You need a smoky crystal. "); else { - remove_charge(current_pc,item); + univ.party[current_pc].remove_charge(item); + if(stat_window == current_pc) + put_item_screen(stat_window,1); do_mindduel(current_pc,cur_monst); } } @@ -1475,7 +1477,7 @@ void fire_missile(location target) { hit_bonus = (overall_mode == MODE_FIRING) ? univ.party[missile_firer].items[missile_inv_slot].bonus : 0; hit_bonus += stat_adj(missile_firer,eSkill::DEXTERITY) - can_see_light(univ.party[missile_firer].combat_pos,target,sight_obscurity) + minmax(-8,8,univ.party[missile_firer].status[eStatus::BLESS_CURSE]); - if((skill_item = pc_has_abil_equip(missile_firer,eItemAbil::ACCURACY)) < 24) { + if((skill_item = univ.party[missile_firer].has_abil_equip(eItemAbil::ACCURACY)) < 24) { hit_bonus += univ.party[missile_firer].items[skill_item].abil_data[0] / 2; dam_bonus += univ.party[missile_firer].items[skill_item].abil_data[0] / 2; } @@ -1549,7 +1551,7 @@ void fire_missile(location target) { // poison if(univ.party[missile_firer].status[eStatus::POISONED_WEAPON] > 0 && univ.party[missile_firer].weap_poisoned == ammo_inv_slot) { poison_amt = univ.party[missile_firer].status[eStatus::POISONED_WEAPON]; - if(pc_has_abil_equip(missile_firer,eItemAbil::POISON_AUGMENT) < 24) + if(univ.party[missile_firer].has_abil_equip(eItemAbil::POISON_AUGMENT) < 24) poison_amt++; poison_monst(cur_monst,poison_amt); } @@ -1566,11 +1568,13 @@ void fire_missile(location target) { if(univ.party[missile_firer].items[ammo_inv_slot].ability != eItemAbil::MISSILE_RETURNING) univ.party[missile_firer].items[ammo_inv_slot].charges--; else univ.party[missile_firer].items[ammo_inv_slot].charges = 1; - if(pc_has_abil_equip(missile_firer,eItemAbil::DRAIN_MISSILES) < 24 + if(univ.party[missile_firer].has_abil_equip(eItemAbil::DRAIN_MISSILES) < 24 && univ.party[missile_firer].items[ammo_inv_slot].ability != eItemAbil::MISSILE_RETURNING) univ.party[missile_firer].items[ammo_inv_slot].charges--; if(univ.party[missile_firer].items[ammo_inv_slot].charges <= 0) - take_item(missile_firer,ammo_inv_slot); + univ.party[missile_firer].take_item(ammo_inv_slot); + if(missile_firer == stat_window) + put_item_screen(stat_window,1); } } @@ -1700,7 +1704,7 @@ void combat_run_monst() { move_to_zero(univ.party[i].status[eStatus::BLESS_CURSE]); move_to_zero(univ.party[i].status[eStatus::HASTE_SLOW]); move_to_zero(univ.party.status[ePartyStatus::STEALTH]); - if((item = pc_has_abil_equip(i,eItemAbil::REGENERATE)) < 24) { + if((item = univ.party[i].has_abil_equip(eItemAbil::REGENERATE)) < 24) { update_stat = true; heal_pc(i,get_ran(1,0,univ.party[i].items[item].item_level + 1)); } @@ -2752,7 +2756,7 @@ void monst_basic_abil(short m_num, std::pair abil, short ta handle_marked_damage(); break; case eMonstAbil::STUN: - if(target < 100 && pc_has_abil_equip(target, eItemAbil::LIFE_SAVING) < 24) + if(target < 100 && univ.party[target].has_abil_equip(eItemAbil::LIFE_SAVING) < 24) break; case eMonstAbil::STATUS: case eMonstAbil::STATUS2: switch(abil.second.gen.stat) { @@ -2831,7 +2835,7 @@ void monst_basic_abil(short m_num, std::pair abil, short ta break; case eMonstAbil::DRAIN_XP: if(target < 100) { - if(pc_has_abil_equip(target, eItemAbil::LIFE_SAVING) < 24) break; + if(univ.party[target].has_abil_equip(eItemAbil::LIFE_SAVING) < 24) break; drain_pc(target, univ.town.monst[m_num].level * abil.second.gen.strength / 100); } break; @@ -4152,7 +4156,7 @@ void handle_disease() { r1 = get_ran(1,0,7); if(univ.party[i].traits[eTrait::GOOD_CONST]) r1 -= 2; - if((get_ran(1,0,7) <= 0) || (pc_has_abil_equip(i,eItemAbil::STATUS_PROTECTION,int(eStatus::DISEASE)) < 24)) + if((get_ran(1,0,7) <= 0) || (univ.party[i].has_abil_equip(eItemAbil::STATUS_PROTECTION,int(eStatus::DISEASE)) < 24)) move_to_zero(univ.party[i].status[eStatus::DISEASE]); } put_pc_screen(); diff --git a/src/boe.dlgutil.cpp b/src/boe.dlgutil.cpp index c2df5232..6127d3e7 100644 --- a/src/boe.dlgutil.cpp +++ b/src/boe.dlgutil.cpp @@ -210,20 +210,21 @@ void handle_sale(cShopItem item, int i) { switch(item.type) { case eShopItemType::ITEM: - switch(pc_ok_to_buy(current_pc,cost,base_item)) { - case 1: + switch(univ.party[current_pc].ok_to_buy(cost,base_item)) { + case eBuyStatus::OK: play_sound(-38); - give_to_pc(current_pc,base_item,true); + take_gold(cost,true); + univ.party[current_pc].give_item(base_item,true); if(active_shop.getType() != eShopType::ITEMS) { // Magic shops have limited stock active_shop.clearItem(i); shop_sbar->setMaximum(shop_sbar->getMaximum() - 1); } break; - case 2: ASB("Can't carry any more items."); break; - case 3: ASB("Not enough cash."); break; - case 4: ASB("Item is too heavy."); break; - case 5: ASB("You own too many of this."); break; + case eBuyStatus::NO_SPACE: ASB("Can't carry any more items."); break; + case eBuyStatus::NEED_GOLD: ASB("Not enough cash."); break; + case eBuyStatus::TOO_HEAVY: ASB("Item is too heavy."); break; + case eBuyStatus::HAVE_LOTS: ASB("You own too many of this."); break; } break; case eShopItemType::ALCHEMY: diff --git a/src/boe.infodlg.cpp b/src/boe.infodlg.cpp index 58ca4b87..86cbfec1 100644 --- a/src/boe.infodlg.cpp +++ b/src/boe.infodlg.cpp @@ -552,8 +552,8 @@ static void display_pc_info(cDialog& me, const short pc) { short weap1 = 24,weap2 = 24,hit_adj = 0, dam_adj = 0,skill_item; - store = pc_carry_weight(pc); - i = amount_pc_can_carry(pc); + store = univ.party[pc].cur_weight(); + i = univ.party[pc].max_weight(); to_draw << univ.party[pc].name << " is carrying " << store << " stones out of " << i << '.'; me["weight"].setText(to_draw.str()); to_draw.str(""); @@ -594,11 +594,11 @@ static void display_pc_info(cDialog& me, const short pc) { hit_adj -= 25; dam_adj = stat_adj(pc,eSkill::STRENGTH) + minmax(-8,8,univ.party[pc].status[eStatus::BLESS_CURSE]); - if((skill_item = pc_has_abil_equip(pc,eItemAbil::SKILL)) < 24) { + if((skill_item = univ.party[pc].has_abil_equip(eItemAbil::SKILL)) < 24) { hit_adj += 5 * (univ.party[pc].items[skill_item].item_level / 2 + 1); dam_adj += univ.party[pc].items[skill_item].item_level / 2; } - if((skill_item = pc_has_abil_equip(pc,eItemAbil::GIANT_STRENGTH)) < 24) { + if((skill_item = univ.party[pc].has_abil_equip(eItemAbil::GIANT_STRENGTH)) < 24) { dam_adj += univ.party[pc].items[skill_item].item_level; hit_adj += univ.party[pc].items[skill_item].item_level * 2; } diff --git a/src/boe.items.cpp b/src/boe.items.cpp index 11f124f8..47312a3e 100644 --- a/src/boe.items.cpp +++ b/src/boe.items.cpp @@ -60,151 +60,21 @@ std::map excluding_types = { short selected,item_max = 0; -void sort_pc_items(short pc_num) { - cItem store_item; - using it = eItemType; - static std::map item_priority = { - {it::NO_ITEM, 20}, {it::ONE_HANDED, 8}, {it::TWO_HANDED, 8}, {it::GOLD, 20}, {it::BOW, 9}, - {it::ARROW, 9}, {it::THROWN_MISSILE, 3}, {it::POTION, 2}, {it::SCROLL, 1}, {it::WAND, 0}, - {it::TOOL, 7}, {it::FOOD, 20}, {it::SHIELD, 10}, {it::ARMOR, 10}, {it::HELM, 10}, - {it::GLOVES, 10}, {it::SHIELD_2, 10}, {it::BOOTS, 10}, {it::RING, 5}, {it::NECKLACE, 6}, - {it::WEAPON_POISON, 4}, {it::NON_USE_OBJECT, 11}, {it::PANTS, 12}, {it::CROSSBOW, 9}, {it::BOLTS, 9}, - {it::MISSILE_NO_AMMO, 9}, {it::UNUSED1, 20}, {it::UNUSED2, 20} - }; - bool no_swaps = false,store_equip; - short i; - - while(!no_swaps) { - no_swaps = true; - for(i = 0; i < 23; i++) - if(item_priority[univ.party[pc_num].items[i + 1].variety] < - item_priority[univ.party[pc_num].items[i].variety]) { - no_swaps = false; - store_item = univ.party[pc_num].items[i + 1]; - univ.party[pc_num].items[i + 1] = univ.party[pc_num].items[i]; - univ.party[pc_num].items[i] = store_item; - store_equip = univ.party[pc_num].equip[i + 1]; - univ.party[pc_num].equip[i + 1] = univ.party[pc_num].equip[i]; - univ.party[pc_num].equip[i] = store_equip; - if(univ.party[pc_num].weap_poisoned == i + 1) - univ.party[pc_num].weap_poisoned--; - else if(univ.party[pc_num].weap_poisoned == i) - univ.party[pc_num].weap_poisoned++; - } - } -} - -bool give_to_party(cItem item, short print_result) { - short i = 0; - - while(i < 6) { - if(give_to_pc(i,item,print_result)) - return true; - i++; - } - return false; -} - -bool give_to_pc(short pc_num,cItem item,short print_result,bool allow_overload) { - short free_space; - - if(item.variety == eItemType::NO_ITEM) - return true; - if(item.variety == eItemType::GOLD) { - univ.party.gold += item.item_level; - ASB("You get some gold."); - return true; - } - if(item.variety == eItemType::FOOD) { - univ.party.food += item.item_level; - ASB("You get some food."); - return true; - } - if(!allow_overload && item.item_weight() > - amount_pc_can_carry(pc_num) - pc_carry_weight(pc_num)) { - if(print_result) { - beep(); // TODO: This is a game event, so it should have a game sound, not a system alert. - ASB("Item too heavy to carry."); - } - return false; - } - free_space = pc_has_space(pc_num); - if(free_space == 24 || univ.party[pc_num].main_status != eMainStatus::ALIVE) - return false; - else { - item.property = false; - item.contained = false; - univ.party[pc_num].items[free_space] = item; - - if(print_result == 1) { - if(stat_window == pc_num) - put_item_screen(stat_window,0); - } - if(overall_mode != MODE_STARTUP && print_result) { - std::ostringstream announce; - announce << " " << univ.party[pc_num].name << " gets "; - if(!item.ident) - announce << item.name; - else announce << item.full_name; - announce << '.'; - add_string_to_buf(announce.str()); - } - - combine_things(pc_num); - sort_pc_items(pc_num); - return true; - } - return false; -} - -// TODO: Utilize the second parameter in special node processing -// if abil > 0, force abil, else ignore -bool forced_give(short item_num,eItemAbil abil,short dat) { - short i,j; - cItem item; - - if((item_num < 0) || (item_num > 399)) - return true; - item = get_stored_item(item_num); - if(abil > eItemAbil::NONE) { - item.ability = abil; - item.abil_data[0] = dat / 1000; - item.abil_data[1] = dat % 1000; - } - for(i = 0; i < 6; i++) - for(j = 0; j < 24; j++) - if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].items[j].variety == eItemType::NO_ITEM) { - univ.party[i].items[j] = item; - - std::ostringstream announce; - announce << " " << univ.party[i].name << " gets "; - if(!item.ident) - announce << item.name; - else announce << item.full_name; - announce << '.'; - add_string_to_buf(announce.str()); - combine_things(i); - sort_pc_items(i); - return true; - } - return false; -} - bool GTP(short item_num) { cItem item; item = get_stored_item(item_num); - return give_to_party(item,true); + return univ.party.give_item(item,true); } bool silent_GTP(short item_num) { cItem item; item = get_stored_item(item_num); - return give_to_party(item,false); + return univ.party.give_item(item,false); } void give_gold(short amount,bool print_result) { if(amount < 0) return; - univ.party.gold = univ.party.gold + amount; + univ.party.gold += amount; if(print_result) put_pc_screen(); } @@ -212,114 +82,12 @@ void give_gold(short amount,bool print_result) { bool take_gold(short amount,bool print_result) { if(univ.party.gold < amount) return false; - univ.party.gold = univ.party.gold - amount; + univ.party.gold += amount; if(print_result) put_pc_screen(); return true; } -// returnes equipped protection level of specified abil, or -1 if no such abil is equipped -short get_prot_level(short pc_num,eItemAbil abil, short dat) { - short i = 0; - - for(i = 0; i < 24; i++) - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && (univ.party[pc_num].items[i].ability == abil) - && univ.party[pc_num].equip[i] && (dat < 0 || dat == univ.party[pc_num].items[i].abil_data[1])) - return univ.party[pc_num].items[i].abil_data[0]; - return -1; - -} - -short pc_has_abil_equip(short pc_num,eItemAbil abil,short dat) { - for(short i = 0; i < 24; i++) { - if(univ.party[pc_num].items[i].variety == eItemType::NO_ITEM) continue; - if(univ.party[pc_num].items[i].ability != abil) continue; - if(!univ.party[pc_num].equip[i]) continue; - if(dat >= 0 && dat != univ.party[pc_num].items[i].abil_data[1]) continue; - return i; - } - return 24; -} - -short pc_has_abil(short pc_num,eItemAbil abil,short dat) { - for(short i = 0; i < 24; i++) { - if(univ.party[pc_num].items[i].variety == eItemType::NO_ITEM) continue; - if(univ.party[pc_num].items[i].ability != abil) continue; - if(dat >= 0 && dat != univ.party[pc_num].items[i].abil_data[1]) continue; - return i; - } - return 24; -} - -bool party_has_abil(eItemAbil abil,short dat) { - short i; - - for(i = 0; i < 6; i++) - if(univ.party[i].main_status == eMainStatus::ALIVE) - if(pc_has_abil(i,abil,dat) < 24) - return true; - return false; -} - -bool party_take_abil(eItemAbil abil,short dat) { - short i,item; - - for(i = 0; i < 6; i++) - if(univ.party[i].main_status == eMainStatus::ALIVE) - if((item = pc_has_abil(i,abil,dat)) < 24) { - if(univ.party[i].items[item].charges > 1) - univ.party[i].items[item].charges--; - else take_item(i,item); - return true; - } - return false; -} - -// returns true is party has item of given item class -// mode - 0 - take one of them, 1 - don't take -bool party_check_class(unsigned int item_class,short mode) { - short i,j; - - if(item_class == 0) - return false; - for(i = 0; i < 6; i++) - if(univ.party[i].main_status == eMainStatus::ALIVE) - for(j = 23; j >= 0; j--) - if(univ.party[i].items[j].variety != eItemType::NO_ITEM && (univ.party[i].items[j].special_class == item_class)) { - if(mode == 0) { - if(univ.party[i].items[j].charges > 1) - univ.party[i].items[j].charges--; - else take_item(i,j); - } - return true; - } - return false; -} -short amount_pc_can_carry(short pc_num) { - return 100 + (15 * min(univ.party[pc_num].skills[eSkill::STRENGTH],20)) + (univ.party[pc_num].traits[eTrait::STRENGTH] * 30) - + (univ.party[pc_num].traits[eTrait::BAD_BACK] * -50); -} -short pc_carry_weight(short pc_num) { - short i,storage = 0; - bool airy = false,heavy = false; - - for(i = 0; i < 24; i++) - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM) { - storage += univ.party[pc_num].items[i].item_weight(); - if(univ.party[pc_num].items[i].ability == eItemAbil::LIGHTER_OBJECT) - airy = true; - if(univ.party[pc_num].items[i].ability == eItemAbil::HEAVIER_OBJECT) - heavy = true; - } - if(airy) - storage -= 30; - if(heavy) - storage += 30; - if(storage < 0) - storage = 0; - return storage; -} - void give_food(short amount,bool print_result) { if(amount < 0) return; univ.party.food = univ.party.food + amount; @@ -344,83 +112,6 @@ short take_food(short amount,bool print_result) { return 0; } -short pc_has_space(short pc_num) { - short i = 0; - - while(i < 24) { - if(univ.party[pc_num].items[i].variety == eItemType::NO_ITEM) - return i; - i++; - } - return 24; -} - -// returns 1 if OK, 2 if no room, 3 if not enough cash, 4 if too heavy, 5 if too many of item -// Made specials cases for if item is gold or food -short pc_ok_to_buy(short pc_num,short cost,cItem item) { - short i; - - if(item.variety != eItemType::GOLD && item.variety != eItemType::FOOD) { - for(i = 0; i < 24; i++) - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && univ.party[pc_num].items[i].type_flag == item.type_flag - && (univ.party[pc_num].items[i].charges > 123)) - return 5; - - if(pc_has_space(pc_num) == 24) - return 2; - if(item.item_weight() > - amount_pc_can_carry(pc_num) - pc_carry_weight(pc_num)) { - return 4; - } - } - if(!take_gold(cost,false)) - return 3; - return 1; - - -} - -//short pc_num,which_item; // if which_item > 30, don't update stat win, item is which_item - 30 -void take_item(short pc_num,short which_item) { - short i; - bool do_print = true; - - if(which_item >= 30) { - do_print = false; - which_item -= 30; - } - - if(univ.party[pc_num].weap_poisoned == which_item && univ.party[pc_num].status[eStatus::POISONED_WEAPON] > 0) { - add_string_to_buf(" Poison lost. "); - univ.party[pc_num].status[eStatus::POISONED_WEAPON] = 0; - } - if(univ.party[pc_num].weap_poisoned > which_item && univ.party[pc_num].status[eStatus::POISONED_WEAPON] > 0) - univ.party[pc_num].weap_poisoned--; - - for(i = which_item; i < 23; i++) { - univ.party[pc_num].items[i] = univ.party[pc_num].items[i + 1]; - univ.party[pc_num].equip[i] = univ.party[pc_num].equip[i + 1]; - } - univ.party[pc_num].items[23] = cItem(); - univ.party[pc_num].equip[23] = false; - - if((stat_window == pc_num) && (do_print)) - put_item_screen(stat_window,1); -} - -void remove_charge(short pc_num,short which_item) { - if(univ.party[pc_num].items[which_item].charges > 0) { - univ.party[pc_num].items[which_item].charges--; - if(univ.party[pc_num].items[which_item].charges == 0) { - take_item(pc_num,which_item); - } - } - - if(stat_window == pc_num) - put_item_screen(stat_window,1); - -} - void enchant_weapon(short pc_num,short item_hit,short enchant_type,short new_val) { if(univ.party[pc_num].items[item_hit].magic || (univ.party[pc_num].items[item_hit].ability != eItemAbil::NONE)) @@ -567,10 +258,10 @@ void drop_item(short pc_num,short item_num,location where_drop) { 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) - take_item(pc_num,item_num); + univ.party[pc_num].take_item(item_num); else univ.party[pc_num].items[item_num].charges -= how_many; } - else take_item(pc_num,item_num); + else univ.party[pc_num].take_item(item_num); break; case MODE_DROP_TOWN: case MODE_DROP_COMBAT: @@ -595,7 +286,7 @@ void drop_item(short pc_num,short item_num,location where_drop) { else add_string_to_buf("Drop: OK"); univ.party[pc_num].items[item_num].charges -= how_many; if(take_given_item) - take_item(pc_num,item_num); + univ.party[pc_num].take_item(item_num); } break; default: //should never be reached @@ -686,12 +377,12 @@ void give_thing(short pc_num, short item_num) { univ.party[pc_num].items[item_num].charges -= how_many; item_store.charges = how_many; } - if(give_to_pc(who_to,item_store,0)) { + if(univ.party[who_to].give_item(item_store,0)) { if(take_given_item) - take_item(pc_num,item_num); + univ.party[pc_num].take_item(item_num); } else { - if(pc_has_space(who_to) == 24) + if(univ.party[who_to].has_space() == 24) ASB("Can't give: PC has max. # of items."); else ASB("Can't give: PC carrying too much."); if(how_many > 0) @@ -701,35 +392,6 @@ void give_thing(short pc_num, short item_num) { } } -void combine_things(short pc_num) { - short i,j,test; - - for(i = 0; i < 24; i++) { - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && - (univ.party[pc_num].items[i].type_flag > 0) && (univ.party[pc_num].items[i].ident)) { - for(j = i + 1; j < 24; j++) - if(univ.party[pc_num].items[j].variety != eItemType::NO_ITEM && - (univ.party[pc_num].items[j].type_flag == univ.party[pc_num].items[i].type_flag) - && (univ.party[pc_num].items[j].ident)) { - add_string_to_buf("(items combined)"); - test = (short) (univ.party[pc_num].items[i].charges) + (short) (univ.party[pc_num].items[j].charges); - if(test > 125) { - univ.party[pc_num].items[i].charges = 125; - ASB("(Can have at most 125 of any item."); - } - else univ.party[pc_num].items[i].charges += univ.party[pc_num].items[j].charges; - if(univ.party[pc_num].equip[j]) { - univ.party[pc_num].equip[i] = true; - univ.party[pc_num].equip[j] = false; - } - take_item(pc_num,30 + j); - } - } - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && univ.party[pc_num].items[i].charges < 0) - univ.party[pc_num].items[i].charges = 1; - } -} - // Procedure only ready for town and outdoor short dist_from_party(location where) { short store = 1000, i; @@ -864,7 +526,7 @@ static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& curr // 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 - || (pc_has_space(current_getting_pc) == 24))) { + || univ.party[current_getting_pc].has_space() == 24)) { current_getting_pc = 6; } @@ -873,7 +535,7 @@ static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& curr std::ostringstream sout; sout << "pc" << i + 1; std::string id = sout.str(); - if(univ.party[i].main_status == eMainStatus::ALIVE && pc_has_space(i) < 24 + if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].has_space() < 24 && ((!is_combat()) || (current_pc == i))) { if(current_getting_pc == 6) current_getting_pc = i; @@ -934,7 +596,7 @@ static void put_item_graphics(cDialog& me, size_t& first_item_shown, short& curr if(current_getting_pc < 6) { std::ostringstream sout; sout << univ.party[current_getting_pc].name << " is carrying "; - sout << pc_carry_weight(current_getting_pc) << " out of " << amount_pc_can_carry(current_getting_pc) << '.'; + sout << univ.party[current_getting_pc].cur_weight() << " out of " << univ.party[current_getting_pc].max_weight() << '.'; me["prompt"].setText(sout.str()); } @@ -993,7 +655,7 @@ static bool display_item_event_filter(cDialog& me, std::string id, size_t& first set_item_flag(item_array[item_hit]); play_sound(62); // formerly force_play_sound } else { - if(!allow_overload && item.item_weight() > amount_pc_can_carry(current_getting_pc) - pc_carry_weight(current_getting_pc)) { + if(!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); @@ -1002,7 +664,7 @@ 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 - give_to_pc(current_getting_pc, item, false, allow_overload); + univ.party[current_getting_pc].give_item(item, false, allow_overload); } *item_array[item_hit] = cItem(); item_array.erase(item_array.begin() + item_hit); @@ -1446,4 +1108,49 @@ short get_num_response(short min, short max, std::string prompt) { return numPanel.getResult(); } +static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod) { + me.toast(true); + if(item_hit != "cancel") { + short which_pc = item_hit[item_hit.length() - 1] - '1'; + me.setResult(which_pc); + } else me.setResult(6); + return true; +} +//active_only; // 0 - no 1 - yes 2 - disarm trap +short char_select_pc(short active_only,short free_inv_only,const char *title) { + short item_hit,i; + + make_cursor_sword(); + + cDialog selectPc("select-pc"); + selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6"}); + + selectPc["title"].setText(title); + + for(i = 0; i < 6; i++) { + std::string n = boost::lexical_cast(i + 1); + if(univ.party[i].main_status == eMainStatus::ABSENT || + (active_only && univ.party[i].main_status > eMainStatus::ALIVE) || + (free_inv_only == 1 && univ.party[i].has_space() == 24) || univ.party[i].main_status == eMainStatus::FLED) { + selectPc["pick" + n].hide(); + } + // TODO: Wouldn't this lead to blank name fields for non-active characters if those characters are allowed? + if(univ.party[i].main_status != eMainStatus::ABSENT) { + selectPc["pc" + n].setText(univ.party[i].name); + } + else selectPc["pc" + n].hide(); + } + + selectPc.run(); + item_hit = selectPc.getResult(); + + return item_hit; +} + +//active_only; // 0 - no 1 - yes 2 - disarm trap +short select_pc(short active_only,short free_inv_only) { + if(active_only == 2) + return char_select_pc(active_only,free_inv_only,"Trap! Who will disarm?"); + else return char_select_pc(active_only,free_inv_only,"Select a character:"); +} diff --git a/src/boe.items.h b/src/boe.items.h index 4631f381..5310fdb2 100644 --- a/src/boe.items.h +++ b/src/boe.items.h @@ -2,37 +2,21 @@ #include "dialog.hpp" #include "pict.hpp" -void sort_pc_items(short pc_num); -bool give_to_pc(short pc_num,cItem item,short print_result,bool allow_overload = false); -bool forced_give(short item_num,eItemAbil abil,short dat = -1); bool GTP(short item_num); bool silent_GTP(short item_num); void give_gold(short amount,bool print_result); bool take_gold(short amount,bool print_result); -short pc_has_abil_equip(short pc_num,eItemAbil abil,short dat = -1); -short pc_has_abil(short pc_num,eItemAbil abil,short dat = -1); -bool party_has_abil(eItemAbil abil,short dat = -1); -bool party_take_abil(eItemAbil abil,short dat = -1); -bool party_check_class(unsigned int item_class,short mode); -short pc_carry_weight(short pc_num); -short amount_pc_can_carry(short pc_num); void give_food(short amount,bool print_result); short take_food(short amount,bool print_result); -short pc_has_space(short pc_num); -short pc_ok_to_buy(short pc_num,short cost,cItem item); -void take_item(short pc_num,short which_item); -void remove_charge(short pc_num,short which_item); void enchant_weapon(short pc_num,short item_hit,short enchant_type,short new_val); void equip_item(short pc_num,short item_num); void drop_item(short pc_num,short item_num,location where_drop); bool place_item(cItem item,location where,bool forced,bool contained = false); void destroy_an_item(); void give_thing(short pc_num, short item_num); -void combine_things(short pc_num); short dist_from_party(location where); void set_item_flag(cItem *item); short get_item(location place,short pc_num,bool check_container); -short get_prot_level(short pc_num,eItemAbil abil,short dat = -1); void make_town_hostile(); void set_town_attitude(short lo,short hi,short att); @@ -51,13 +35,11 @@ void place_glands(location where,mon_num_t m_type); short party_total_level() ; void reset_item_max(); short item_val(cItem item); -bool give_to_party(cItem item, short print_result); void place_treasure(location where,short level,short loot,short mode); cItem return_treasure(short loot); void refresh_store_items(); std::string get_text_response(std::string prompt = "", pic_num_t pic = 16); short get_num_response(short min, short max, std::string prompt); -// These are defined in pc.editors.cpp since they are also used by the character editor short char_select_pc(short active_only,short free_inv_only,const char *title); short select_pc(short active_only,short free_inv_only); diff --git a/src/boe.main.cpp b/src/boe.main.cpp index f6404db5..6babd228 100644 --- a/src/boe.main.cpp +++ b/src/boe.main.cpp @@ -112,6 +112,7 @@ int main(int /*argc*/, char* argv[]) { init_directories(argv[0]); init_menubar(); // Do this first of all because otherwise a default File and Window menu will be seen sync_prefs(); + cUniverse::print_result = cParty::print_result = cPlayer::print_result = add_string_to_buf; init_graph_tool(); Initialize(); init_fileio(); diff --git a/src/boe.party.cpp b/src/boe.party.cpp index d25e6cfe..d9ce628c 100644 --- a/src/boe.party.cpp +++ b/src/boe.party.cpp @@ -236,7 +236,7 @@ void put_party_in_scen(std::string scen_name) { for(i = 23; i >= 0; i--) { univ.party[j].items[i].special_class = 0; if(univ.party[j].items[i].ability == eItemAbil::CALL_SPECIAL) { - take_item(j,i + 30); + univ.party[j].take_item(i); item_took = true; } } @@ -307,23 +307,15 @@ void put_party_in_scen(std::string scen_name) { bool create_pc(short spot,cDialog* parent) { bool still_ok = true; - if(spot == 6) { - for(spot = 0; spot < 6; spot++) - if(univ.party[spot].main_status == eMainStatus::ABSENT) - break; - } - if(spot == 6) - return false; - - univ.party[spot] = cPlayer(); + if(spot == 6) spot = univ.party.free_space(); + if(spot == 6) return false; + univ.party.new_pc(spot); pick_race_abil(&univ.party[spot],0); still_ok = spend_xp(spot,0,parent); if(!still_ok) return false; - univ.party[spot].cur_health = univ.party[spot].max_health; - univ.party[spot].cur_sp = univ.party[spot].max_sp; pick_pc_graphic(spot,0,parent); pick_pc_name(spot,parent); @@ -334,6 +326,8 @@ bool create_pc(short spot,cDialog* parent) { // TODO: Why only when not in MODE_STARTUP? univ.party[spot].finish_create(); } + univ.party[spot].cur_health = univ.party[spot].max_health; + univ.party[spot].cur_sp = univ.party[spot].max_sp; return true; } @@ -391,8 +385,8 @@ void curse_pc(short which_pc,short how_much) { if(univ.party[which_pc].main_status != eMainStatus::ALIVE) return; if(univ.party[which_pc].main_status == eMainStatus::ALIVE) { - if(how_much > 0 && (level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::BLESS_CURSE))) > 0) - how_much -= level / 2; + if(how_much > 0) + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(eStatus::BLESS_CURSE)) / 2; univ.party[which_pc].status[eStatus::BLESS_CURSE] = minmax(-8,8,univ.party[which_pc].status[eStatus::BLESS_CURSE] - how_much); if(how_much < 0) add_string_to_buf(" " + univ.party[which_pc].name + " blessed."); @@ -412,12 +406,11 @@ void dumbfound_pc(short which_pc,short how_much) { if(univ.party[which_pc].main_status != eMainStatus::ALIVE) return; r1 = get_ran(1,0,90); - if(pc_has_abil_equip(which_pc,eItemAbil::WILL) < 24) { + if(univ.party[which_pc].has_abil_equip(eItemAbil::WILL) < 24) { add_string_to_buf(" Ring of Will glows."); r1 -= 10; } - if((level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::DUMB))) > 0) - how_much -= level / 4; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(eStatus::DUMB)) / 4; if(r1 < univ.party[which_pc].level) how_much -= 2; if(how_much <= 0) { @@ -446,8 +439,7 @@ void disease_pc(short which_pc,short how_much) { add_string_to_buf(" " + univ.party[which_pc].name + " saved."); return; } - if((level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::DISEASE))) > 0) - how_much -= level / 2; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(eStatus::DISEASE)) / 2; if(univ.party[which_pc].traits[eTrait::FRAIL] && how_much > 1) how_much++; if(univ.party[which_pc].traits[eTrait::FRAIL] && how_much == 1 && get_ran(1,0,1) == 0) @@ -471,12 +463,10 @@ void sleep_pc(short which_pc,short how_much,eStatus what_type,short adjust) { // TODO: Uh, what if an invalid status is passed in? // --> Currently, you'd get that status effect, but with a "paralyzed" message, sound, and quick-help if(what_type == eStatus::ASLEEP || what_type == eStatus::PARALYZED) { //// - if((level = get_prot_level(which_pc,eItemAbil::WILL)) > 0) - how_much -= level / 2; - if((level = get_prot_level(which_pc,eItemAbil::FREE_ACTION)) > 0) - how_much -= (what_type == eStatus::ASLEEP) ? level : level * 300; - if((level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(what_type))) > 0) - how_much -= level / 4; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::WILL) / 2; + level = univ.party[which_pc].get_prot_level(eItemAbil::FREE_ACTION); + how_much -= (what_type == eStatus::ASLEEP) ? level : level * 300; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(what_type)) / 4; } r1 = get_ran(1,1,100) + adjust; @@ -511,8 +501,8 @@ void slow_pc(short which_pc,short how_much) { if(univ.party[which_pc].main_status != eMainStatus::ALIVE) return; if(univ.party[which_pc].main_status == eMainStatus::ALIVE) { - if(how_much > 0 && (level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::HASTE_SLOW))) > 0) - how_much -= level / 2; + if(how_much > 0) + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(eStatus::HASTE_SLOW)) / 2; univ.party[which_pc].status[eStatus::HASTE_SLOW] = minmax(-8,8,univ.party[which_pc].status[eStatus::HASTE_SLOW] - how_much); if(how_much < 0) add_string_to_buf(" " + univ.party[which_pc].name + " hasted."); @@ -528,8 +518,7 @@ void web_pc(short which_pc,short how_much) { if(univ.party[which_pc].main_status != eMainStatus::ALIVE) return; if(univ.party[which_pc].main_status == eMainStatus::ALIVE) { - if((level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::WEBS))) > 0) - how_much -= level / 2; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(eStatus::WEBS)) / 2; univ.party[which_pc].status[eStatus::WEBS] = min(univ.party[which_pc].status[eStatus::WEBS] + how_much,8); add_string_to_buf(" " + univ.party[which_pc].name + " webbed."); one_sound(17); @@ -541,7 +530,7 @@ void web_pc(short which_pc,short how_much) { void acid_pc(short which_pc,short how_much) { if(univ.party[which_pc].main_status != eMainStatus::ALIVE) return; - if(pc_has_abil_equip(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::ACID)) < 24) { + if(univ.party[which_pc].has_abil_equip(eItemAbil::STATUS_PROTECTION,int(eStatus::ACID)) < 24) { add_string_to_buf(" " + univ.party[which_pc].name + " resists acid."); return; } @@ -984,7 +973,7 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { break; case eSpell::MAGIC_MAP: - item = pc_has_abil(pc_num,eItemAbil::SAPPHIRE); + item = univ.party[pc_num].has_abil(eItemAbil::SAPPHIRE); if(item == 24 && !freebie) add_string_to_buf(" You need a sapphire. "); else if(univ.town->defy_scrying || univ.town->defy_mapping) @@ -992,7 +981,9 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { else { if(freebie) add_string_to_buf(" You have a vision."); else { - remove_charge(pc_num,item); + univ.party[pc_num].remove_charge(item); + if(stat_window == pc_num) + put_item_screen(stat_window,1); univ.party[pc_num].cur_sp -= (*spell_num).cost; add_string_to_buf(" As the sapphire dissolves, "); add_string_to_buf(" you have a vision. "); @@ -1350,11 +1341,11 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) { } else { if(!PSD[SDF_RESURRECT_NO_BALM]) { - if((item = pc_has_abil(pc_num,eItemAbil::RESURRECTION_BALM)) == 24) { + if((item = univ.party[pc_num].has_abil(eItemAbil::RESURRECTION_BALM)) == 24) { add_string_to_buf(" Need resurrection balm."); break; } - else take_item(pc_num,item); + else univ.party[pc_num].take_item(item); } if(spell_num == eSpell::RAISE_DEAD) { if(univ.party[target].main_status == eMainStatus::DEAD) @@ -1640,8 +1631,7 @@ void do_mindduel(short pc_num,cCreature *monst) { short i = 0,adjust,r1,r2,balance = 0; adjust = (univ.party[pc_num].level + univ.party[pc_num].skills[eSkill::INTELLIGENCE]) / 2 - monst->level * 2; - if((i = get_prot_level(pc_num,eItemAbil::WILL)) > 0) - adjust += i * 5; + adjust += univ.party[pc_num].get_prot_level(eItemAbil::WILL) * 5; if(monst->attitude % 2 != 1) make_town_hostile(); monst->attitude = 1; @@ -2298,7 +2288,7 @@ short stat_adj(short pc_num,eSkill which) { tr++; } // TODO: Use ability strength? - if(pc_has_abil_equip(pc_num,eItemAbil::BOOST_STAT,int(which)) < 24) + if(univ.party[pc_num].has_abil_equip(eItemAbil::BOOST_STAT,int(which)) < 24) tr++; return tr; } @@ -2359,20 +2349,28 @@ void do_alchemy() { which_p = alch_choice(pc_num); if(which_p < 20) { - if(pc_has_space(pc_num) == 24) { + if(univ.party[pc_num].has_space() == 24) { add_string_to_buf("Alchemy: Can't carry another item."); return; } - // TODO: Spread this out a bit, since there seems to be a chance of which_item2 not being initialized. - if(((which_item = pc_has_abil(pc_num,ingred1_needed[which_p])) == 24) || - (ingred2_needed[which_p] != eItemAbil::NONE && (which_item2 = pc_has_abil(pc_num,ingred2_needed[which_p])) == 24)) { + + if((which_item = univ.party[pc_num].has_abil(ingred1_needed[which_p])) == 24) { add_string_to_buf("Alchemy: Don't have ingredients."); return; } + + if(ingred2_needed[which_p] != eItemAbil::NONE) { + if(univ.party[pc_num].has_abil(ingred2_needed[which_p]) == 24) { + add_string_to_buf("Alchemy: Don't have ingredients."); + return; + } + univ.party[pc_num].remove_charge(which_item); + // We call this twice because remove_charge might move the item and change its index + which_item2 = univ.party[pc_num].has_abil(ingred2_needed[which_p]); + univ.party[pc_num].remove_charge(which_item2); + } else univ.party[pc_num].remove_charge(which_item); + play_sound(8); - remove_charge(pc_num,which_item); - if(ingred2_needed[which_p] != eItemAbil::NONE) - remove_charge(pc_num,which_item2); r1 = get_ran(1,1,100); if(r1 < fail_chance[univ.party[pc_num].skills[eSkill::ALCHEMY] - alch_difficulty[which_p]]) { @@ -2393,7 +2391,7 @@ void do_alchemy() { store_i.charges++; if(store_i.variety == eItemType::POTION) store_i.graphic_num += get_ran(1,0,2); - if(!give_to_pc(pc_num,store_i,0)) { + if(!univ.party[pc_num].give_item(store_i,false)) { ASB("No room in inventory."); ASB(" Potion placed on floor."); place_item(store_i,univ.town.p_loc,true); @@ -2549,10 +2547,8 @@ void poison_pc(short which_pc,short how_much) { short level; if(univ.party[which_pc].main_status == eMainStatus::ALIVE) { - if((level = get_prot_level(which_pc,eItemAbil::STATUS_PROTECTION,int(eStatus::POISON))) > 0) - how_much -= level / 2; - if((level = get_prot_level(which_pc,eItemAbil::FULL_PROTECTION)) > 0) - how_much -= level / 3; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::STATUS_PROTECTION,int(eStatus::POISON)) / 2; + how_much -= univ.party[which_pc].get_prot_level(eItemAbil::FULL_PROTECTION) / 3; if(univ.party[which_pc].traits[eTrait::FRAIL] && how_much > 1) how_much++; @@ -2670,12 +2666,12 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_ how_much -= 1; } - if((level = get_prot_level(which_pc,eItemAbil::DAMAGE_PROTECTION,int(damage_type))) > 0) { + if((level = univ.party[which_pc].get_prot_level(eItemAbil::DAMAGE_PROTECTION,int(damage_type))) > 0) { if(damage_type == eDamageType::WEAPON) how_much -= level; else how_much = how_much / ((level >= 7) ? 4 : 2); } // TODO: Do these perhaps depend on the ability strength less than they should? - if((level = get_prot_level(which_pc,eItemAbil::PROTECT_FROM_SPECIES,int(type_of_attacker))) > 0) + if((level = univ.party[which_pc].get_prot_level(eItemAbil::PROTECT_FROM_SPECIES,int(type_of_attacker))) > 0) how_much = how_much / ((level >= 7) ? 4 : 2); // invuln @@ -2690,7 +2686,7 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_ // major resistance if((damage_type == eDamageType::FIRE || damage_type == eDamageType::POISON || damage_type == eDamageType::MAGIC || damage_type == eDamageType::COLD) - && ((level = get_prot_level(which_pc,eItemAbil::FULL_PROTECTION)) > 0)) + && ((level = univ.party[which_pc].get_prot_level(eItemAbil::FULL_PROTECTION)) > 0)) how_much = how_much / ((level >= 7) ? 4 : 2); if(boom_anim_active) { @@ -2762,7 +2758,7 @@ void petrify_pc(short which_pc, short strength) { r1 += univ.party[which_pc].status[eStatus::BLESS_CURSE]; r1 -= strength; - if(pc_has_abil_equip(which_pc,eItemAbil::PROTECT_FROM_PETRIFY) < 24) + if(univ.party[which_pc].has_abil_equip(eItemAbil::PROTECT_FROM_PETRIFY) < 24) r1 = 20; if(r1 > 14) { @@ -2785,7 +2781,7 @@ void kill_pc(short which_pc,eMainStatus type) { } if(type != eMainStatus::STONE) - i = pc_has_abil_equip(which_pc,eItemAbil::LIFE_SAVING); + i = univ.party[which_pc].has_abil_equip(eItemAbil::LIFE_SAVING); if(!no_save && type != eMainStatus::ABSENT && univ.party[which_pc].skills[eSkill::LUCK] > 0 && get_ran(1,1,100) < hit_chance[univ.party[which_pc].skills[eSkill::LUCK]]) { @@ -2819,7 +2815,7 @@ void kill_pc(short which_pc,eMainStatus type) { } else { add_string_to_buf(" Life saved! "); - take_item(which_pc,i); + univ.party[which_pc].take_item(i); heal_pc(which_pc,200); } if(univ.party[current_pc].main_status != eMainStatus::ALIVE) @@ -2839,10 +2835,8 @@ void set_pc_moves() { r = get_encumberance(i); univ.party[i].ap = minmax(1,8,univ.party[i].ap - (r / 3)); - if((i_level = get_prot_level(i,eItemAbil::SPEED)) > 0) - univ.party[i].ap += i_level / 7 + 1; - if((i_level = get_prot_level(i,eItemAbil::SLOW_WEARER)) > 0) - univ.party[i].ap -= i_level / 5; + univ.party[i].ap += univ.party[i].get_prot_level(eItemAbil::SPEED) / 7 + 1; + univ.party[i].ap -= univ.party[i].get_prot_level(eItemAbil::SLOW_WEARER) / 5; if(univ.party[i].status[eStatus::HASTE_SLOW] < 0 && univ.party.age % 2 == 1) // slowed? univ.party[i].ap = 0; diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index a6a1b042..1fc8f9e9 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -1127,7 +1127,9 @@ void use_item(short pc,short item) { put_pc_screen(); if((take_charge) && (univ.party[pc].items[item].charges > 0)) - remove_charge(pc,item); + univ.party[pc].remove_charge(item); + if(stat_window == pc) + put_item_screen(stat_window,1); if(!take_charge) { draw_terrain(0); put_item_screen(stat_window,0); @@ -2176,12 +2178,12 @@ void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, break; case eSpecType::FORCED_GIVE: check_mess = true; - if(!forced_give(spec.ex1a,eItemAbil::NONE) && spec.ex1b >= 0) + if(!univ.party.forced_give(spec.ex1a,eItemAbil::NONE) && spec.ex1b >= 0) *next_spec = spec.ex1b; break; case eSpecType::BUY_ITEMS_OF_TYPE: for(i = 0; i < 144; i++) - if(party_check_class(spec.ex1a,0)) + if(univ.party.check_class(spec.ex1a,true)) store_val++; if(store_val == 0) { if( spec.ex1b >= 0) @@ -2401,7 +2403,7 @@ void oneshot_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, } switch(cur_node.type) { case eSpecType::ONCE_GIVE_ITEM: - if(!forced_give(spec.ex1a,eItemAbil::NONE)) { + if(!univ.party.forced_give(spec.ex1a,eItemAbil::NONE)) { set_sd = false; if( spec.ex2b >= 0) *next_spec = spec.ex2b; @@ -2477,7 +2479,7 @@ void oneshot_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, if(i == 1) {set_sd = false; *next_spec = -1;} else { store_i = get_stored_item(spec.ex1a); - if((spec.ex1a >= 0) && (!give_to_party(store_i,true))) { + if((spec.ex1a >= 0) && (!univ.party.give_item(store_i,true))) { set_sd = false; *next_spec = -1; } else { @@ -3024,7 +3026,7 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, } break; case eSpecType::IF_HAVE_ITEM_CLASS: - if(party_check_class(spec.ex1a,!spec.ex2a)) + if(univ.party.check_class(spec.ex1a,spec.ex2a)) *next_spec = spec.ex1b; break; case eSpecType::IF_EQUIP_ITEM_CLASS: @@ -3036,7 +3038,9 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, *next_spec = spec.ex1b; if(spec.ex2c) { *redraw = 1; - take_item(i,j); + univ.party[i].take_item(j); + if(i == stat_window) + put_item_screen(stat_window,1); } } break; diff --git a/src/boe.text.cpp b/src/boe.text.cpp index 7aed1f02..2156769a 100644 --- a/src/boe.text.cpp +++ b/src/boe.text.cpp @@ -1121,6 +1121,11 @@ short print_terrain(location space) { return (short) which_terrain; } +void add_string_to_buf(std::string str) { + // This is a separate overload instead of using a defaulted parameter so that + // it can be passed as an argument to various other functions + add_string_to_buf(str, 0); +} void add_string_to_buf(std::string str, unsigned short indent) { if(overall_mode == MODE_STARTUP) diff --git a/src/boe.text.h b/src/boe.text.h index 39f594f5..46270b14 100644 --- a/src/boe.text.h +++ b/src/boe.text.h @@ -30,7 +30,8 @@ void monst_damaged_mes(mon_num_t which_m,short how_much,short how_much_spec); void monst_killed_mes(mon_num_t which_m); void print_nums(short a,short b,short c); short print_terrain(location space); -void add_string_to_buf(std::string str, unsigned short indent = 0); // Set second paramater to nonzero to auto-split the line if it's too long +void add_string_to_buf(std::string str, unsigned short indent); // Set second paramater to nonzero to auto-split the line if it's too long +void add_string_to_buf(std::string str); void init_buf(); void print_buf () ; void restart_printing(); diff --git a/src/boe.town.cpp b/src/boe.town.cpp index d704a67b..cdf832ee 100644 --- a/src/boe.town.cpp +++ b/src/boe.town.cpp @@ -1061,7 +1061,7 @@ void pick_lock(location where,short pc_num) { short unlock_adjust; terrain = univ.town->terrain(where.x,where.y); - which_item = pc_has_abil_equip(pc_num,eItemAbil::LOCKPICKS); + which_item = univ.party[pc_num].has_abil_equip(eItemAbil::LOCKPICKS); if(which_item == 24) { add_string_to_buf(" Need lockpick equipped. "); return; @@ -1079,7 +1079,7 @@ void pick_lock(location where,short pc_num) { if(univ.party[pc_num].traits[eTrait::NIMBLE]) r1 -= 8; - if(pc_has_abil_equip(pc_num,eItemAbil::THIEVING) < 24) + if(univ.party[pc_num].has_abil_equip(eItemAbil::THIEVING) < 24) r1 = r1 - 12; if(univ.scenario.ter_types[terrain].special != eTerSpec::UNLOCKABLE) { @@ -1091,7 +1091,9 @@ void pick_lock(location where,short pc_num) { add_string_to_buf(" Didn't work. "); if(will_break) { add_string_to_buf(" Pick breaks. "); - remove_charge(pc_num,which_item); + univ.party[pc_num].remove_charge(which_item); + if(stat_window == pc_num) + put_item_screen(stat_window,1); } play_sound(41); } diff --git a/src/boe.townspec.cpp b/src/boe.townspec.cpp index a05a8134..7275cd13 100644 --- a/src/boe.townspec.cpp +++ b/src/boe.townspec.cpp @@ -39,7 +39,7 @@ void activate_monster_enc(short enc_num,std::string list,short str,short strsnd, // 7 - level drain 8 - alert 9 - big flames 10 - dumbfound 11 - disease 1 // 12 - disease all bool run_trap(short pc_num,eTrapType trap_type,short trap_level,short diff) { - short r1,skill,i,num_hits = 1,i_level; + short r1,skill,i,num_hits = 1; short trap_odds[30] = {5,30,35,42,48, 55,63,69,75,77, 78,80,82,84,86, 88,90,92,94,96,98,99,99,99,99,99,99,99,99,99}; @@ -65,8 +65,7 @@ bool run_trap(short pc_num,eTrapType trap_type,short trap_level,short diff) { if(pc_num < 6) { i = stat_adj(pc_num,eSkill::DEXTERITY); - if((i_level = get_prot_level(pc_num,eItemAbil::THIEVING)) > 0) - i = i + i_level / 2; + i += univ.party[pc_num].get_prot_level(eItemAbil::THIEVING) / 2; skill = minmax(0,20,univ.party[pc_num].skills[eSkill::DISARM_TRAPS] + + univ.party[pc_num].skills[eSkill::LUCK] / 2 + 1 - univ.town.difficulty + 2 * i); diff --git a/src/classes/party.cpp b/src/classes/party.cpp index 3a9959ae..d41d5abf 100644 --- a/src/classes/party.cpp +++ b/src/classes/party.cpp @@ -39,7 +39,12 @@ cParty::cParty(cUniverse& univ, long party_preset) : univ(univ) { std::fill(key_times, key_times + 20, std::numeric_limits::max()); for(int i = 0; i < 6; i++) - adven[i] = cPlayer(party_preset, i); + adven[i] = new cPlayer(*this, party_preset, i); +} + +cParty::~cParty() { + for(int i = 0; i < 6; i++) + delete adven[i]; } void cParty::append(legacy::party_record_type& old){ @@ -180,25 +185,31 @@ void cParty::cEncNote::append(int16_t(& old)[2], const cScenario& scenario) { } } -void cParty::add_pc(legacy::pc_record_type old){ - for(int i = 0; i < 6; i++) - if(adven[i].main_status == eMainStatus::ABSENT){ - adven[i].append(old); - break; - } +void cParty::append(legacy::pc_record_type(& old)[6]) { + for(int i = 0; i < 6; i++) { + delete adven[i]; + adven[i] = new cPlayer(*this); + adven[i]->append(old[i]); + } } -void cParty::void_pcs(){ - for(int i = 0; i < 6; i++) - adven[i].main_status = eMainStatus::ABSENT; +void cParty::new_pc(size_t spot) { + if(spot < 6){ + delete adven[spot]; + adven[spot] = new cPlayer(*this); + } } -void cParty::add_pc(cPlayer new_pc){ +size_t cParty::free_space() { for(int i = 0; i < 6; i++) - if(adven[i].main_status == eMainStatus::ABSENT){ - adven[i] = new_pc; - break; - } + if(adven[i]->main_status == eMainStatus::ABSENT) + return i; + return 6; +} + +void cParty::swap_pcs(size_t a, size_t b) { + if(a < 6 && b < 6) + std::swap(adven[a], adven[b]); } bool cParty::save_talk(const std::string& who, const std::string& where, const std::string& str1, const std::string& str2){ @@ -245,7 +256,87 @@ bool cParty::record(eEncNoteType type, const std::string& what, const std::strin void cParty::apply_status(eStatus which, int how_much) { for(int i = 0; i < 6; i++) - adven[i].apply_status(which, how_much); + adven[i]->apply_status(which, how_much); +} + +bool cParty::give_item(cItem item,bool do_print) { + for(int i = 0; i < 6; i++) { + if(adven[i]->give_item(item,do_print)) + return true; + } + return false; +} + +// TODO: Utilize the second parameter in special node processing +// if abil > 0, force abil, else ignore +bool cParty::forced_give(item_num_t item_num,eItemAbil abil,short dat) { + if(item_num < 0 || item_num >= univ.scenario.scen_items.size()) + return true; + cItem item = univ.scenario.scen_items[item_num]; + if(abil > eItemAbil::NONE) { + item.ability = abil; + item.abil_data[0] = dat / 1000; + item.abil_data[1] = dat % 1000; + } + for(int i = 0; i < 6; i++) + for(int j = 0; j < 24; j++) + if(adven[i]->main_status == eMainStatus::ALIVE && adven[i]->items[j].variety == eItemType::NO_ITEM) { + adven[i]->items[j] = item; + + if(print_result) { + std::ostringstream announce; + announce << " " << univ.party[i].name << " gets "; + if(!item.ident) + announce << item.name; + else announce << item.full_name; + announce << '.'; + print_result(announce.str()); + } + adven[i]->combine_things(); + adven[i]->sort_items(); + return true; + } + return false; +} + +bool cParty::has_abil(eItemAbil abil, short dat) { + for(int i = 0; i < 6; i++) + if(adven[i]->main_status == eMainStatus::ALIVE) + if(adven[i]->has_abil(abil,dat) < 24) + return true; + return false; +} + +bool cParty::take_abil(eItemAbil abil, short dat) { + short item; + for(int i = 0; i < 6; i++) + if(adven[i]->main_status == eMainStatus::ALIVE) + if((item = adven[i]->has_abil(abil,dat)) < 24) { + if(adven[i]->items[item].charges > 1) + adven[i]->items[item].charges--; + else adven[i]->take_item(item); + return true; + } + return false; +} + +bool cParty::check_class(unsigned int item_class,bool take) { + short i,j; + + if(item_class == 0) + return false; + for(i = 0; i < 6; i++) + if(adven[i]->main_status == eMainStatus::ALIVE) + for(j = 23; j >= 0; j--) + if(adven[i]->items[j].variety != eItemType::NO_ITEM && (adven[i]->items[j].special_class == item_class)) { + if(take) { + if(adven[i]->items[j].charges > 1) + adven[i]->items[j].charges--; + else adven[i]->take_item(j); + } + return true; + } + return false; } bool cParty::start_timer(short time, short node, short type){ @@ -652,15 +743,15 @@ 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."); 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]; + 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]; } const cPlayer& cParty::operator[](unsigned short n) const{ 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]; + 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]; } // Note that the pointer functions take the pointer with its negative sign stripped off! @@ -724,7 +815,7 @@ bool cParty::start_split(short x,short y,snd_num_t noise,short who) { univ.town.p_loc.y = y; for(i = 0; i < 6; i++) if(i != who) - adven[i].main_status += eMainStatus::SPLIT; + adven[i]->main_status += eMainStatus::SPLIT; play_sound(noise); return true; } @@ -832,4 +923,5 @@ std::ostream& operator<<(std::ostream& out, ePartyStatus type) { return out; } +void(* cParty::print_result)(std::string) = nullptr; diff --git a/src/classes/party.h b/src/classes/party.h index dfe74576..7d5f25fc 100644 --- a/src/classes/party.h +++ b/src/classes/party.h @@ -106,7 +106,7 @@ public: std::string scen_name; private: cUniverse& univ; - cPlayer adven[6]; + std::array adven; public: unsigned short setup[4][64][64]; // formerly setup_save_type std::array,3> stored_items; // formerly stored_items_list_type @@ -129,11 +129,12 @@ public: void append(legacy::big_tr_type& old); void append(legacy::stored_items_list_type& old,short which_list); void append(legacy::setup_save_type& old); + void append(legacy::pc_record_type(& old)[6]); void apply_status(eStatus which, int how_much); - void add_pc(legacy::pc_record_type old); - void add_pc(cPlayer new_pc); + void new_pc(size_t spot); + size_t free_space(); void void_pcs(); bool save_talk(const std::string& who, const std::string& where, const std::string& str1, const std::string& str2); bool add_to_journal(const std::string& event, short day); @@ -144,19 +145,26 @@ public: void writeTo(std::ostream& file) const; void readFrom(std::istream& file); + bool give_item(cItem item,bool do_print); + bool forced_give(item_num_t item_num,eItemAbil abil,short dat = -1); + bool has_abil(eItemAbil abil, short dat = -1); + bool take_abil(eItemAbil abil, short dat = -1); + bool check_class(unsigned int item_class,bool take); + bool start_split(short x, short y, snd_num_t noise, short who); bool end_split(snd_num_t noise); bool is_split() const; bool pc_present(short n) const; short pc_present() const; // If only one pc is present, returns the number of that pc. Otherwise returns 6. + void swap_pcs(size_t a, size_t b); typedef std::vector::iterator encIter; typedef std::vector::iterator journalIter; typedef std::vector::iterator talkIter; typedef std::vector::iterator timerIter; cParty(cUniverse& univ, long party_preset = 'dflt'); - // TODO: Remove this in favour of cParty constructor - friend void init_party(short); + ~cParty(); + static void(* print_result)(std::string); }; bool operator==(const cParty::cConvers& one, const cParty::cConvers& two); diff --git a/src/classes/pc.cpp b/src/classes/pc.cpp index f649d1a2..a6c14c95 100644 --- a/src/classes/pc.cpp +++ b/src/classes/pc.cpp @@ -108,6 +108,221 @@ void cPlayer::avatar() { status[eStatus::MARTYRS_SHIELD] = 8; } +void cPlayer::sort_items() { + using it = eItemType; + static std::map item_priority = { + {it::NO_ITEM, 20}, {it::ONE_HANDED, 8}, {it::TWO_HANDED, 8}, {it::GOLD, 20}, {it::BOW, 9}, + {it::ARROW, 9}, {it::THROWN_MISSILE, 3}, {it::POTION, 2}, {it::SCROLL, 1}, {it::WAND, 0}, + {it::TOOL, 7}, {it::FOOD, 20}, {it::SHIELD, 10}, {it::ARMOR, 10}, {it::HELM, 10}, + {it::GLOVES, 10}, {it::SHIELD_2, 10}, {it::BOOTS, 10}, {it::RING, 5}, {it::NECKLACE, 6}, + {it::WEAPON_POISON, 4}, {it::NON_USE_OBJECT, 11}, {it::PANTS, 12}, {it::CROSSBOW, 9}, {it::BOLTS, 9}, + {it::MISSILE_NO_AMMO, 9}, {it::UNUSED1, 20}, {it::UNUSED2, 20} + }; + bool no_swaps = false; + + while(!no_swaps) { + no_swaps = true; + for(int i = 0; i < 23; i++) + if(item_priority[items[i + 1].variety] < + item_priority[items[i].variety]) { + no_swaps = false; + std::swap(items[i + 1], items[i]); + std::swap(equip[i + 1], equip[i]); + if(weap_poisoned == i + 1) + weap_poisoned--; + else if(weap_poisoned == i) + weap_poisoned++; + } + } +} + +bool cPlayer::give_item(cItem item, bool do_print, bool allow_overload) { + short free_space; + + if(item.variety == eItemType::NO_ITEM) + return true; + if(item.variety == eItemType::GOLD) { + party.gold += item.item_level; + if(do_print && print_result) + print_result("You get some gold."); + return true; + } + if(item.variety == eItemType::FOOD) { + party.food += item.item_level; + if(do_print && print_result) + print_result("You get some food."); + return true; + } + if(!allow_overload && item.item_weight() > free_weight()) { + if(do_print && print_result) { + //beep(); // TODO: This is a game event, so it should have a game sound, not a system alert. + print_result("Item too heavy to carry."); + } + return false; + } + free_space = has_space(); + if(free_space == 24 || main_status != eMainStatus::ALIVE) + return false; + else { + item.property = false; + item.contained = false; + items[free_space] = item; + + if(do_print && print_result) { + std::ostringstream announce; + announce << " " << name << " gets "; + if(!item.ident) + announce << item.name; + else announce << item.full_name; + announce << '.'; + print_result(announce.str()); + } + + combine_things(); + sort_items(); + return true; + } + return false; +} + +short cPlayer::max_weight() { + return 100 + (15 * min(skills[eSkill::STRENGTH],20)) + (traits[eTrait::STRENGTH] * 30) + + (traits[eTrait::BAD_BACK] * -50); +} + +short cPlayer::cur_weight() { + short weight = 0; + bool airy = false,heavy = false; + + for(int i = 0; i < 24; i++) + if(items[i].variety != eItemType::NO_ITEM) { + weight += items[i].item_weight(); + if(items[i].ability == eItemAbil::LIGHTER_OBJECT) + airy = true; + if(items[i].ability == eItemAbil::HEAVIER_OBJECT) + heavy = true; + } + if(airy) + weight -= 30; + if(heavy) + weight += 30; + if(weight < 0) + weight = 0; + return weight; +} + +short cPlayer::free_weight() { + return max_weight() - cur_weight(); +} + +short cPlayer::has_space() { + for(int i = 0; i < 24; i++) { + if(items[i].variety == eItemType::NO_ITEM) + return i; + } + return 24; +} + +void cPlayer::combine_things() { + for(int i = 0; i < 24; i++) { + if(items[i].variety != eItemType::NO_ITEM && items[i].type_flag > 0 && items[i].ident) { + for(int j = i + 1; j < 24; j++) + if(items[j].variety != eItemType::NO_ITEM && items[j].type_flag == items[i].type_flag && items[j].ident) { + if(print_result) print_result("(items combined)"); + short test = items[i].charges + items[j].charges; + if(test > 125) { + items[i].charges = 125; + if(print_result) print_result("(Can have at most 125 of any item."); + } + else items[i].charges += items[j].charges; + if(equip[j]) { + equip[i] = true; + equip[j] = false; + } + take_item(j); + } + } + if(items[i].variety != eItemType::NO_ITEM && items[i].charges < 0) + items[i].charges = 1; + } +} + +short cPlayer::get_prot_level(eItemAbil abil, short dat) { + int sum = 0; + for(int i = 0; i < 24; i++) { + if(items[i].variety == eItemType::NO_ITEM) continue; + if(items[i].ability != abil) continue; + if(!equip[i]) continue; + if(dat >= 0 && dat != items[i].abil_data[1]) continue; + sum += items[i].abil_data[1]; + } + return sum; // TODO: Should we return -1 if the sum is 0? + +} + +short cPlayer::has_abil_equip(eItemAbil abil,short dat) { + for(short i = 0; i < 24; i++) { + if(items[i].variety == eItemType::NO_ITEM) continue; + if(items[i].ability != abil) continue; + if(!equip[i]) continue; + if(dat >= 0 && dat != items[i].abil_data[1]) continue; + return i; + } + return 24; +} + +short cPlayer::has_abil(eItemAbil abil,short dat) { + for(short i = 0; i < 24; i++) { + if(items[i].variety == eItemType::NO_ITEM) continue; + if(items[i].ability != abil) continue; + if(dat >= 0 && dat != items[i].abil_data[1]) continue; + return i; + } + return 24; +} + +eBuyStatus cPlayer::ok_to_buy(short cost,cItem item) { + if(item.variety != eItemType::GOLD && item.variety != eItemType::FOOD) { + for(int i = 0; i < 24; i++) + if(items[i].variety != eItemType::NO_ITEM && items[i].type_flag == item.type_flag && items[i].charges > 123) + return eBuyStatus::HAVE_LOTS; + + if(has_space() == 24) + return eBuyStatus::NO_SPACE; + if(item.item_weight() > free_weight()) { + return eBuyStatus::TOO_HEAVY; + } + } + if(cost > party.gold) + return eBuyStatus::NEED_GOLD; + return eBuyStatus::OK; +} + +void cPlayer::take_item(int which_item) { + if(weap_poisoned == which_item && status[eStatus::POISONED_WEAPON] > 0) { + if(print_result) print_result(" Poison lost."); + status[eStatus::POISONED_WEAPON] = 0; + } + if(weap_poisoned > which_item && status[eStatus::POISONED_WEAPON] > 0) + weap_poisoned--; + + for(int i = which_item; i < 23; i++) { + items[i] = items[i + 1]; + equip[i] = equip[i + 1]; + } + items[23] = cItem(); + equip[23] = false; +} + +void cPlayer::remove_charge(int which_item) { + if(items[which_item].charges > 0) { + items[which_item].charges--; + if(items[which_item].charges == 0) { + take_item(which_item); + } + } +} + void cPlayer::finish_create() { // Start items switch(race) { @@ -143,7 +358,7 @@ void cPlayer::finish_create() { cur_sp = max_sp; } -cPlayer::cPlayer(){ +cPlayer::cPlayer(cParty& party) : party(party) { short i; main_status = eMainStatus::ABSENT; name = "\n"; @@ -174,7 +389,7 @@ cPlayer::cPlayer(){ direction = 0; } -cPlayer::cPlayer(long key,short slot){ +cPlayer::cPlayer(cParty& party,long key,short slot) : party(party) { short i; main_status = eMainStatus::ALIVE; if(key == 'dbug'){ @@ -497,3 +712,5 @@ std::istream& operator >> (std::istream& in, eMainStatus& e){ else e = eMainStatus::ABSENT; return in; } + +void(* cPlayer::print_result)(std::string) = nullptr; diff --git a/src/classes/pc.h b/src/classes/pc.h index 0f1f4017..a62f354c 100644 --- a/src/classes/pc.h +++ b/src/classes/pc.h @@ -20,7 +20,12 @@ namespace legacy { struct pc_record_type; }; +enum class eBuyStatus {OK, NO_SPACE, NEED_GOLD, TOO_HEAVY, HAVE_LOTS}; + +class cParty; + class cPlayer { + cParty& party; public: eMainStatus main_status; std::string name; @@ -52,9 +57,24 @@ public: void apply_status(eStatus which, int how_much); void avatar(); + void combine_things(); + void sort_items(); + bool give_item(cItem item, bool do_print, bool allow_overload = false); + void take_item(int which_item); + void remove_charge(int which_item); + short has_space(); + short max_weight(); + short cur_weight(); + short free_weight(); + short get_prot_level(eItemAbil abil, short dat = -1); + short has_abil_equip(eItemAbil abil, short dat = -1); + short has_abil(eItemAbil abil, short dat = -1); + eBuyStatus ok_to_buy(short cost,cItem item); + void append(legacy::pc_record_type old); - cPlayer(); - cPlayer(long key,short slot); + cPlayer(cParty& party); + cPlayer(cParty& party,long key,short slot); + static void(* print_result)(std::string); short get_tnl(); void writeTo(std::ostream& file) const; void readFrom(std::istream& file); diff --git a/src/classes/universe.cpp b/src/classes/universe.cpp index 1c474c8c..a39db1b4 100644 --- a/src/classes/universe.cpp +++ b/src/classes/universe.cpp @@ -1079,3 +1079,5 @@ short cCurTown::countMonsters(){ to_ret++; return to_ret; } + +void(* cUniverse::print_result)(std::string) = nullptr; diff --git a/src/classes/universe.h b/src/classes/universe.h index 00fd97ba..87a7552f 100644 --- a/src/classes/universe.h +++ b/src/classes/universe.h @@ -178,6 +178,7 @@ public: void append(legacy::stored_outdoor_maps_type& old); short difficulty_adjust() const; explicit cUniverse(long party_type = 'dflt'); + static void(* print_result)(std::string); }; #endif diff --git a/src/pcedit/pc.action.cpp b/src/pcedit/pc.action.cpp index 5a095df1..5caf8370 100644 --- a/src/pcedit/pc.action.cpp +++ b/src/pcedit/pc.action.cpp @@ -76,7 +76,7 @@ bool handle_action(sf::Event event) { if((the_point.in(item_string_rects[i][1])) && // drop item univ.party[current_active_pc].items[i].variety != eItemType::NO_ITEM) { flash_rect(item_string_rects[i][1]); - take_item(current_active_pc,i); + univ.party[current_active_pc].take_item(i); } for(i = 0; i < 24; i++) if((the_point.in(item_string_rects[i][2])) && // identify item @@ -144,61 +144,6 @@ void edit_day() { univ.party.age = (long long) (3700) * (long long) (dialog_answer); } -void combine_things(short pc_num) { - short i,j,test; - - for(i = 0; i < 24; i++) { - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && - (univ.party[pc_num].items[i].type_flag > 0) && (univ.party[pc_num].items[i].ident)) { - for(j = i + 1; j < 24; j++) - if(univ.party[pc_num].items[j].variety != eItemType::NO_ITEM && - (univ.party[pc_num].items[j].type_flag == univ.party[pc_num].items[i].type_flag) - && (univ.party[pc_num].items[j].ident)) { - // add_string_to_buf("(items combined)"); - test = (short) (univ.party[pc_num].items[i].charges) + (short) (univ.party[pc_num].items[j].charges); - if(test > 125) { - univ.party[pc_num].items[i].charges = 125; - // ASB("Can have at most 125 of any item."); - } - else univ.party[pc_num].items[i].charges += univ.party[pc_num].items[j].charges; - if(univ.party[pc_num].equip[j]) { - univ.party[pc_num].equip[i] = true; - univ.party[pc_num].equip[j] = false; - } - take_item(pc_num,j); - } - } - if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && univ.party[pc_num].items[i].charges < 0) - univ.party[pc_num].items[i].charges = 1; - } -} - -bool give_to_pc(short pc_num,cItem item, short /*print_result*/) { - short free_space; - - if(item.variety == eItemType::NO_ITEM) - return true; - if((free_space = pc_has_space(pc_num)) == 24 || univ.party[pc_num].main_status != eMainStatus::ALIVE) - return false; - else { - univ.party[pc_num].items[free_space] = item; - combine_things(pc_num); - return true; - } - return false; -} - -bool give_to_party(cItem item,short print_result) { - short i = 0; - - while(i < 6) { - if(give_to_pc(i,item,print_result)) - return true; - i++; - } - return false; -} - void give_gold(short amount,bool /*print_result*/) { univ.party.gold = univ.party.gold + amount; } @@ -210,36 +155,6 @@ bool take_gold(short amount,bool /*print_result*/) { return true; } -short pc_has_space(short pc_num) { - short i = 0; - - while(i < 24) { - if(univ.party[pc_num].items[i].variety == eItemType::NO_ITEM) - return i; - i++; - } - return 24; -} - -//short pc_num,which_item; // if which_item > 20, don't update stat win, item is which_item - 20 -void take_item(short pc_num,short which_item) { - short i; - - if(univ.party[pc_num].weap_poisoned == which_item && univ.party[pc_num].status[eStatus::POISONED_WEAPON] > 0) { - univ.party[pc_num].status[eStatus::POISONED_WEAPON] = 0; - } - if(univ.party[pc_num].weap_poisoned > which_item && univ.party[pc_num].status[eStatus::POISONED_WEAPON] > 0) - univ.party[pc_num].weap_poisoned--; - - for(i = which_item; i < 23; i++) { - univ.party[pc_num].items[i] = univ.party[pc_num].items[i + 1]; - univ.party[pc_num].equip[i] = univ.party[pc_num].equip[i + 1]; - } - univ.party[pc_num].items[23].variety = eItemType::NO_ITEM; - univ.party[pc_num].equip[23] = false; - -} - void edit_xp(cPlayer *pc) { location view_loc; diff --git a/src/pcedit/pc.editors.cpp b/src/pcedit/pc.editors.cpp index f052ebd8..e87bafa3 100644 --- a/src/pcedit/pc.editors.cpp +++ b/src/pcedit/pc.editors.cpp @@ -89,53 +89,6 @@ short store_h,store_sp,i,store_skp; unsigned short store_g; short store_train_mode,store_train_pc; -static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod) { - me.toast(true); - if(item_hit != "cancel") { - short which_pc = item_hit[item_hit.length() - 1] - '1'; - me.setResult(which_pc); - } else me.setResult(6); - return true; -} - -//active_only; // 0 - no 1 - yes 2 - disarm trap -short char_select_pc(short active_only,short free_inv_only,const char *title) { - short item_hit,i; - - make_cursor_sword(); - - cDialog selectPc("select-pc"); - selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6"}); - - selectPc["title"].setText(title); - - for(i = 0; i < 6; i++) { - std::string n = boost::lexical_cast(i + 1); - if(univ.party[i].main_status == eMainStatus::ABSENT || - (active_only && univ.party[i].main_status > eMainStatus::ALIVE) || - (free_inv_only == 1 && pc_has_space(i) == 24) || univ.party[i].main_status == eMainStatus::FLED) { - selectPc["pick" + n].hide(); - } - // TODO: Wouldn't this lead to blank name fields for non-active characters if those characters are allowed? - if(univ.party[i].main_status != eMainStatus::ABSENT) { - selectPc["pc" + n].setText(univ.party[i].name); - } - else selectPc["pc" + n].hide(); - } - - selectPc.run(); - item_hit = selectPc.getResult(); - - return item_hit; -} - -//active_only; // 0 - no 1 - yes 2 - disarm trap -short select_pc(short active_only,short free_inv_only) { - if(active_only == 2) - return char_select_pc(active_only,free_inv_only,"Trap! Who will disarm?"); - else return char_select_pc(active_only,free_inv_only,"Select a character:"); -} - static void put_pc_spells(cDialog& me, const short store_trait_mode) { short i; diff --git a/src/pcedit/pc.editors.h b/src/pcedit/pc.editors.h index 9a4d457c..f1279327 100644 --- a/src/pcedit/pc.editors.h +++ b/src/pcedit/pc.editors.h @@ -1,14 +1,7 @@ -bool give_to_pc(short pc_num,cItem item, short print_result); -bool give_to_party(cItem item,short print_result); void give_gold(short amount,bool print_result); bool take_gold(short amount,bool print_result); -short pc_has_space(short pc_num); -void take_item(short pc_num,short which_item); -short char_select_pc(short active_only,short free_inv_only,const char *title); -short select_pc(short active_only,short free_inv_only); void give_spec_items(); void pick_race_abil(cPlayer *pc,short mode); void reset_boats(); -void combine_things(short pc_num); diff --git a/src/pcedit/pc.main.cpp b/src/pcedit/pc.main.cpp index 5f1140eb..8dc52b2f 100644 --- a/src/pcedit/pc.main.cpp +++ b/src/pcedit/pc.main.cpp @@ -325,7 +325,7 @@ void handle_item_menu(int item_hit) { cItem store_i; store_i = univ.scenario.scen_items[item_hit]; store_i.ident = true; - give_to_pc(current_active_pc,store_i,false); + univ.party[current_active_pc].give_item(store_i,false); } //short mode; // 0 - quit 1- restore diff --git a/src/tools/fileio.cpp b/src/tools/fileio.cpp index 5933c07a..20add5a7 100644 --- a/src/tools/fileio.cpp +++ b/src/tools/fileio.cpp @@ -772,9 +772,7 @@ bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bo univ.party.append(store_party); univ.party.append(store_setup); - univ.party.void_pcs(); - for(int i = 0; i < 6; i++) - univ.party.add_pc(store_pc[i]); + univ.party.append(store_pc); if(in_scen){ univ.out.append(store_out_info); if(town_restore){