From 25ef2ed7138d38c9ec00c483b43af7c26245905e Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Mon, 12 Jan 2015 21:17:44 -0500 Subject: [PATCH] Initial reformation of item abilities - Merge most of the spell usable abilities into a single "cast spell" ability which can take any spell as an additional parameter - Merge the affect status usable abilities into a single "affect status" ability which can take any status as an additional parameter - Merge acidic and poisoned weapon into a single "status weapon" ability which can take any status as an additional parameter (though the handling for other statuses isn't yet implemented) - Fix mass charm spell getting worse as you intelligence bonus increases - Mass charm item spell is now easier to resist (an unintended side-effect) - PC versions of Wrack and Unholy Ravaging implemented (for use as item spells) --- src/boe.actions.cpp | 8 +- src/boe.combat.cpp | 181 +++++++++------ src/boe.combat.h | 2 + src/boe.items.cpp | 14 +- src/boe.party.cpp | 121 ++++++---- src/boe.party.h | 6 +- src/boe.specials.cpp | 266 ++++++++++------------ src/boe.town.cpp | 4 +- src/classes/item.cpp | 436 ++++++++++++++++++++++++++++++++++++- src/classes/item.h | 16 +- src/classes/simpletypes.h | 44 +--- src/classes/spell.cpp | 35 +-- src/classes/spell.hpp | 1 + src/classes/universe.cpp | 8 +- src/scenedit/scen.core.cpp | 4 +- 15 files changed, 778 insertions(+), 368 deletions(-) diff --git a/src/boe.actions.cpp b/src/boe.actions.cpp index 1a856192..0416531b 100644 --- a/src/boe.actions.cpp +++ b/src/boe.actions.cpp @@ -2216,8 +2216,8 @@ void do_rest(long length, int hp_restore, int mp_restore) { } short item = pc_has_abil_equip(i,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].ability_strength / 3); - if(univ.party[i].items[item].ability_strength / 3 == 0) + 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) j = get_ran(1,0,1); if(is_out()) j = j * 4; heal_pc(i,j); @@ -2434,8 +2434,8 @@ void increase_age() { if((item = pc_has_abil_equip(i,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].ability_strength / 3); - if(univ.party[i].items[item].ability_strength / 3 == 0) + 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) j = get_ran(1,0,1); if(is_out()) j = j * 4; heal_pc(i,j); diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index bb1541d7..b67d2dd5 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -608,11 +608,11 @@ void pc_attack(short who_att,short target) { r2 = get_ran(1,1,univ.party[who_att].items[weap1].item_level) + dam_adj + 2 + univ.party[who_att].items[weap1].bonus; if(univ.party[who_att].items[weap1].ability == eItemAbil::WEAK_WEAPON) - r2 = (r2 * (10 - univ.party[who_att].items[weap1].ability_strength)) / 10; + r2 = (r2 * (10 - univ.party[who_att].items[weap1].abil_data[0])) / 10; if(r1 <= hit_chance[univ.party[who_att].skills[what_skill1]]) { spec_dam = calc_spec_dam(univ.party[who_att].items[weap1].ability, - univ.party[who_att].items[weap1].ability_strength,which_m); + univ.party[who_att].items[weap1].abil_data[0],which_m); // assassinate r1 = get_ran(1,1,100); @@ -648,17 +648,22 @@ void pc_attack(short who_att,short target) { poison_monst(which_m,poison_amt); move_to_zero(univ.party[who_att].status[eStatus::POISONED_WEAPON]); } - if((univ.party[who_att].items[weap1].ability == eItemAbil::POISONED_WEAPON) && (get_ran(1,0,1) == 1)) { + if((univ.party[who_att].items[weap1].ability == eItemAbil::STATUS_WEAPON) && (get_ran(1,0,1) == 1)) { + switch(eStatus(univ.party[who_att].items[weap1].abil_data[1])) { + // TODO: Handle other status types + case eStatus::POISON: add_string_to_buf(" Blade drips venom. "); - poison_monst(which_m,univ.party[who_att].items[weap1].ability_strength / 2); - } - if((univ.party[who_att].items[weap1].ability == eItemAbil::ACIDIC_WEAPON) && (get_ran(1,0,1) == 1)) { + poison_monst(which_m,univ.party[who_att].items[weap1].abil_data[0] / 2); + break; + case eStatus::ACID: add_string_to_buf(" Blade drips acid. "); - acid_monst(which_m,univ.party[who_att].items[weap1].ability_strength / 2); + acid_monst(which_m,univ.party[who_att].items[weap1].abil_data[0] / 2); + break; + } } if((univ.party[who_att].items[weap1].ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) { add_string_to_buf(" Blade drains life. "); - heal_pc(who_att,univ.party[who_att].items[weap1].ability_strength / 2); + heal_pc(who_att,univ.party[who_att].items[weap1].abil_data[0] / 2); } } else { @@ -688,11 +693,11 @@ void pc_attack(short who_att,short target) { r1 += 5 * (univ.party[current_pc].status[eStatus::WEBS] / 3); r2 = get_ran(1,1,univ.party[who_att].items[weap2].item_level) + dam_adj - 1 + univ.party[who_att].items[weap2].bonus; if(univ.party[who_att].items[weap2].ability == eItemAbil::WEAK_WEAPON) - r2 = (r2 * (10 - univ.party[who_att].items[weap2].ability_strength)) / 10; + r2 = (r2 * (10 - univ.party[who_att].items[weap2].abil_data[0])) / 10; if(r1 <= hit_chance[univ.party[who_att].skills[what_skill2]]) { spec_dam = calc_spec_dam(univ.party[who_att].items[weap2].ability, - univ.party[who_att].items[weap2].ability_strength,which_m); + univ.party[who_att].items[weap2].abil_data[0],which_m); switch(what_skill2) { case eSkill::EDGED_WEAPONS: if(univ.party[who_att].items[weap1].item_level < 8) @@ -710,17 +715,21 @@ void pc_attack(short who_att,short target) { break; } - if((univ.party[who_att].items[weap2].ability == eItemAbil::POISONED_WEAPON) && (get_ran(1,0,1) == 1)) { + if((univ.party[who_att].items[weap2].ability == eItemAbil::STATUS_WEAPON) && (get_ran(1,0,1) == 1)) { + switch(eStatus(univ.party[who_att].items[weap2].abil_data[1])) { + case eStatus::POISON: add_string_to_buf(" Blade drips venom. "); - poison_monst(which_m,univ.party[who_att].items[weap2].ability_strength / 2); - } - if((univ.party[who_att].items[weap2].ability == eItemAbil::ACIDIC_WEAPON) && (get_ran(1,0,1) == 1)) { + poison_monst(which_m,univ.party[who_att].items[weap2].abil_data[0] / 2); + break; + case eStatus::ACID: add_string_to_buf(" Blade drips acid. "); - acid_monst(which_m,univ.party[who_att].items[weap2].ability_strength / 2); + acid_monst(which_m,univ.party[who_att].items[weap2].abil_data[0] / 2); + break; + } } if((univ.party[who_att].items[weap2].ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) { add_string_to_buf(" Blade drains life. "); - heal_pc(who_att,univ.party[who_att].items[weap2].ability_strength / 2); + heal_pc(who_att,univ.party[who_att].items[weap2].abil_data[0] / 2); } } @@ -1027,7 +1036,10 @@ void do_combat_cast(location target) { //hit_space(target,r1,5,1,0); break; case eSpell::WOUND: - r1 = get_ran(min(7,2 + bonus + level / 2),1,4); + case eSpell::WRACK: + if(spell_being_cast == eSpell::WRACK) + r1 = get_ran(2 + bonus / 2,1,4); + else r1 = get_ran(min(7,2 + bonus + level / 2),1,4); add_missile(target,14,1,0,0); do_missile_anim(100,univ.party[current_pc].combat_pos,24); hit_space(target,r1,DAMAGE_UNBLOCKABLE,1,0); @@ -1163,6 +1175,15 @@ void do_combat_cast(location target) { charm_monst(cur_monst,0,eStatus::PARALYZED,500); store_sound = 24; break; + case eSpell::UNHOLY_RAVAGING: + store_m_type = 14; + store_sound = 53; + r1 = get_ran(4,1,8); + r2 = get_ran(1,0,2); + damage_monst(targ_num, 7, r1, 0, DAMAGE_MAGIC,0); + slow_monst(cur_monst, 4 + r2); + poison_monst(cur_monst, 5 + r2); + break; case eSpell::SCRY_MONSTER: store_m_type = -1; @@ -1450,8 +1471,8 @@ void fire_missile(location target) { 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) { - hit_bonus += univ.party[missile_firer].items[skill_item].ability_strength / 2; - dam_bonus += univ.party[missile_firer].items[skill_item].ability_strength / 2; + 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; } // race adj. @@ -1479,7 +1500,7 @@ void fire_missile(location target) { pause(dist(univ.party[current_pc].combat_pos,target)*5); run_a_missile(univ.party[missile_firer].combat_pos,target,2,1,5,0,0,100); start_missile_anim(); - place_spell_pattern(radius2,target, DAMAGE_FIRE,univ.party[missile_firer].items[ammo_inv_slot].ability_strength * 2,missile_firer); + place_spell_pattern(radius2,target, DAMAGE_FIRE,univ.party[missile_firer].items[ammo_inv_slot].abil_data[0] * 2,missile_firer); do_explosion_anim(5,0); end_missile_anim(); handle_marked_damage(); @@ -1525,7 +1546,7 @@ void fire_missile(location target) { else if((targ_monst = monst_there(target)) < univ.town->max_monst()) { cur_monst = &univ.town.monst[targ_monst]; spec_dam = calc_spec_dam(univ.party[missile_firer].items[ammo_inv_slot].ability, - univ.party[missile_firer].items[ammo_inv_slot].ability_strength,cur_monst); + univ.party[missile_firer].items[ammo_inv_slot].abil_data[0],cur_monst); if(univ.party[missile_firer].items[ammo_inv_slot].ability == eItemAbil::MISSILE_HEAL_TARGET) { ASB(" There is a flash of light."); cur_monst->health += r2; @@ -4211,9 +4232,8 @@ void end_combat() { bool combat_cast_mage_spell() { - short target,i,store_sp,bonus = 1,r1,store_sound = 0,store_m_type = 0,num_opp = 0; + short store_sp; eSpell spell_num; - cCreature *which_m; cMonster get_monst; if(univ.town.is_antimagic(univ.party[current_pc].combat_pos.x,univ.party[current_pc].combat_pos.y)) { @@ -4254,7 +4274,6 @@ bool combat_cast_mage_spell() { store_sum_monst_cost = get_monst.level; } - bonus = stat_adj(current_pc,eSkill::INTELLIGENCE); combat_posing_monster = current_working_monster = current_pc; if(spell_num == eSpell::NONE) return false; print_spell_cast(spell_num,eSkill::MAGE_SPELLS); @@ -4271,13 +4290,29 @@ bool combat_cast_mage_spell() { start_fancy_spell_targeting(spell_num); } else { - start_missile_anim(); take_ap(6); draw_terrain(2); + combat_immed_mage_cast(current_pc,spell_num); + } + put_pc_screen(); + } + combat_posing_monster = current_working_monster = -1; + // Did anything actually get cast? + if(store_sp == univ.party[current_pc].cur_sp) + return false; + else return true; +} + +void combat_immed_mage_cast(short current_pc, eSpell spell_num, bool freebie) { + short target, store_m_type = 0, i, store_sound = 0, num_opp = 0, r1; + short bonus = freebie ? 1 : stat_adj(current_pc,eSkill::INTELLIGENCE); + cCreature* which_m; + start_missile_anim(); switch(spell_num) { case eSpell::SHOCKWAVE: - univ.party[current_pc].cur_sp -= (*spell_num).cost; - add_string_to_buf(" The ground shakes. "); + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; + add_string_to_buf(" The ground shakes!"); do_shockwave(univ.party[current_pc].combat_pos); break; @@ -4285,7 +4320,8 @@ bool combat_cast_mage_spell() { // target = select_pc(11,0); target = store_spell_target; if(target < 6) { - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; play_sound(4); std::string c_line = " " + univ.party[target].name; switch(spell_num) { @@ -4320,7 +4356,8 @@ bool combat_cast_mage_spell() { case eSpell::HASTE_MAJOR: case eSpell::BLESS_MAJOR: store_sound = 25; - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; for(i = 0; i < 6; i++) @@ -4344,7 +4381,8 @@ bool combat_cast_mage_spell() { case eSpell::SLOW_GROUP: case eSpell::FEAR_GROUP: case eSpell::PARALYSIS_MASS: // affect monsters in area spells - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; store_sound = 25; if(spell_num == eSpell::FEAR_GROUP) store_sound = 54; @@ -4388,36 +4426,15 @@ bool combat_cast_mage_spell() { place_spell_pattern(radius2,univ.party[current_pc].combat_pos,WALL_BLADES,6); break; } - - } if(num_opp < 10) do_missile_anim((num_opp < 5) ? 50 : 25,univ.party[current_pc].combat_pos,store_sound); else play_sound(store_sound); end_missile_anim(); - put_pc_screen(); - } - combat_posing_monster = current_working_monster = -1; - // Did anything actually get cast? - if(store_sp == univ.party[current_pc].cur_sp) - return false; - else return true; } - bool combat_cast_priest_spell() { - short target,i,store_sp,bonus,store_sound = 0,store_m_type = 0,num_opp = 0; + short store_sp; eSpell spell_num; - cCreature *which_m; - effect_pat_type protect_pat = {{ - {0,1,1,1,1,1,1,1,0}, - {1,5,5,5,5,5,5,5,1}, - {1,5,6,6,6,6,6,5,1}, - {1,5,6,3,3,3,6,5,1}, - {1,5,6,3,3,3,6,5,1}, - {1,5,6,3,3,3,6,5,1}, - {1,5,6,6,6,6,6,5,1}, - {1,5,5,5,5,5,5,5,1}, - {0,1,1,1,1,1,1,1,0}}}; if(univ.town.is_antimagic(univ.party[current_pc].combat_pos.x,univ.party[current_pc].combat_pos.y)) { add_string_to_buf(" Not in antimagic field."); @@ -4441,7 +4458,6 @@ bool combat_cast_priest_spell() { } if(spell_num == eSpell::NONE) return false; - bonus = stat_adj(current_pc,eSkill::INTELLIGENCE); combat_posing_monster = current_working_monster = current_pc; @@ -4461,23 +4477,52 @@ bool combat_cast_priest_spell() { start_fancy_spell_targeting(spell_num); } else { - start_missile_anim(); take_ap(5); draw_terrain(2); + combat_immed_priest_cast(current_pc, spell_num); + } + put_pc_screen(); + } + + combat_posing_monster = current_working_monster = -1; + // Did anything actually get cast? + if(store_sp == univ.party[current_pc].cur_sp) + return false; + else return true; +} + +void combat_immed_priest_cast(short current_pc, eSpell spell_num, bool freebie) { + short target,i,store_sound = 0,store_m_type = 0,num_opp = 0; + short bonus = freebie ? 1 : stat_adj(current_pc,eSkill::INTELLIGENCE); + cCreature *which_m; + effect_pat_type protect_pat = {{ + {0,1,1,1,1,1,1,1,0}, + {1,5,5,5,5,5,5,5,1}, + {1,5,6,6,6,6,6,5,1}, + {1,5,6,3,3,3,6,5,1}, + {1,5,6,3,3,3,6,5,1}, + {1,5,6,3,3,3,6,5,1}, + {1,5,6,6,6,6,6,5,1}, + {1,5,5,5,5,5,5,5,1}, + {0,1,1,1,1,1,1,1,0} + }}; + start_missile_anim(); switch(spell_num) { case eSpell::BLESS_MINOR: case eSpell::BLESS: // target = select_pc(11,0); target = store_spell_target; if(target < 6) { store_sound = 4; - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; curse_pc(target,-(spell_num==eSpell::BLESS_MINOR ? 2 : max(2,(univ.party[current_pc].level * 3) / 4 + 1 + bonus))); add_missile(univ.party[target].combat_pos,8,0,0,0); } break; case eSpell::BLESS_PARTY: - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; for(i = 0; i < 6; i++) if(univ.party[i].main_status == eMainStatus::ALIVE) { curse_pc(i, -(univ.party[current_pc].level / 3)); @@ -4487,7 +4532,8 @@ bool combat_cast_priest_spell() { break; case eSpell::AVATAR: - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; add_string_to_buf(" " + univ.party[current_pc].name + " is an avatar!"); heal_pc(current_pc,200); cure_pc(current_pc,8); @@ -4502,11 +4548,15 @@ bool combat_cast_priest_spell() { break; case eSpell::CURSE_ALL: case eSpell::CHARM_MASS: case eSpell::PESTILENCE: - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; store_sound = 24; for(i = 0; i < univ.town->max_monst(); i++) { if((univ.town.monst[i].active != 0) &&(univ.town.monst[i].attitude % 2 == 1) && (dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= (*spell_num).range)) { + // TODO: Should this ^ also check that you can see each target? ie can_see_light(...) < 5 + // --> can_see_light(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc,sight_obscurity) + // (The item version of the spell used to check for this, but no longer does since it now defers to here.) which_m = &univ.town.monst[i]; switch(spell_num) { case eSpell::CURSE_ALL: @@ -4514,6 +4564,9 @@ bool combat_cast_priest_spell() { store_m_type = 8; break; case eSpell::CHARM_MASS: + // TODO: As an item spell, the penalty was 0, though perhaps it was intended to be 8 + // (since 8 was passed as the final argument). Now the penalty has increased to 27. + // It should probably be put back somehow. charm_monst(which_m,28 - bonus,eStatus::CHARM,0); store_m_type = 14; break; @@ -4531,25 +4584,17 @@ bool combat_cast_priest_spell() { break; case eSpell::PROTECTIVE_CIRCLE: - univ.party[current_pc].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[current_pc].cur_sp -= (*spell_num).cost; play_sound(24); add_string_to_buf(" Protective field created."); place_spell_pattern(protect_pat,univ.party[current_pc].combat_pos,6); break; } - } if(num_opp < 10) do_missile_anim((num_opp < 5) ? 50 : 25,univ.party[current_pc].combat_pos,store_sound); else play_sound(store_sound); end_missile_anim(); - put_pc_screen(); - } - - combat_posing_monster = current_working_monster = -1; - // Did anything actually get cast? - if(store_sp == univ.party[current_pc].cur_sp) - return false; - else return true; } void start_spell_targeting(eSpell num, bool freebie) { diff --git a/src/boe.combat.h b/src/boe.combat.h index a3381f49..7f0a0381 100644 --- a/src/boe.combat.h +++ b/src/boe.combat.h @@ -52,6 +52,8 @@ bool out_monst_all_dead(); void end_combat(); bool combat_cast_mage_spell(); bool combat_cast_priest_spell(); +void combat_immed_mage_cast(short current_pc, eSpell spell_num, bool freebie = false); +void combat_immed_priest_cast(short current_pc, eSpell spell_num, bool freebie = false); void start_spell_targeting(eSpell num, bool freebie = false); void start_fancy_spell_targeting(eSpell num, bool freebie = false); void spell_cast_hit_return(); diff --git a/src/boe.items.cpp b/src/boe.items.cpp index 6fa7c662..580957b1 100644 --- a/src/boe.items.cpp +++ b/src/boe.items.cpp @@ -222,7 +222,7 @@ short get_prot_level(short pc_num,eItemAbil abil) { 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])) - return univ.party[pc_num].items[i].ability_strength; + return univ.party[pc_num].items[i].abil_data[0]; return -1; } @@ -441,15 +441,16 @@ void enchant_weapon(short pc_num,short item_hit,short enchant_type,short new_val break; case 3: store_name += " (F)"; - univ.party[pc_num].items[item_hit].ability = eItemAbil::FLAME; - univ.party[pc_num].items[item_hit].ability_strength = 5; + univ.party[pc_num].items[item_hit].ability = eItemAbil::CAST_SPELL; + univ.party[pc_num].items[item_hit].abil_data[0] = 5; + univ.party[pc_num].items[item_hit].abil_data[1] = int(eSpell::FLAME); univ.party[pc_num].items[item_hit].charges = 8; break; case 4: store_name += " (F!)"; univ.party[pc_num].items[item_hit].value = new_val; univ.party[pc_num].items[item_hit].ability = eItemAbil::FLAMING_WEAPON; - univ.party[pc_num].items[item_hit].ability_strength = 5; + univ.party[pc_num].items[item_hit].abil_data[0] = 5; break; case 5: store_name += " (+5)"; @@ -459,8 +460,9 @@ void enchant_weapon(short pc_num,short item_hit,short enchant_type,short new_val case 6: store_name += " (B)"; univ.party[pc_num].items[item_hit].bonus++; - univ.party[pc_num].items[item_hit].ability = eItemAbil::BLESS_CURSE; - univ.party[pc_num].items[item_hit].ability_strength = 5; + univ.party[pc_num].items[item_hit].ability = eItemAbil::AFFECT_STATUS; + univ.party[pc_num].items[item_hit].abil_data[0] = 5; + univ.party[pc_num].items[item_hit].abil_data[1] = int(eStatus::BLESS_CURSE); univ.party[pc_num].items[item_hit].magic_use_type = 0; univ.party[pc_num].items[item_hit].charges = 8; break; diff --git a/src/boe.party.cpp b/src/boe.party.cpp index a20c5416..f1e3d06b 100644 --- a/src/boe.party.cpp +++ b/src/boe.party.cpp @@ -862,7 +862,7 @@ void give_party_spell(short which) { } } -void do_mage_spell(short pc_num,eSpell spell_num) { +void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { short i,j,item,target,r1,adj,store; location where; @@ -875,12 +875,14 @@ void do_mage_spell(short pc_num,eSpell spell_num) { switch(spell_num) { case eSpell::LIGHT: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; increase_light(50); break; case eSpell::IDENTIFY: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; ASB("All of your items are identified."); for(i = 0; i < 6; i++) for(j = 0; j < 24; j++) @@ -888,7 +890,8 @@ void do_mage_spell(short pc_num,eSpell spell_num) { break; case eSpell::TRUE_SIGHT: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; for(where.x = 0; where.x < 64; where.x++) for(where.y = 0; where.y < 64; where.y++) if(dist(where,univ.town.p_loc) <= 2) @@ -899,7 +902,8 @@ void do_mage_spell(short pc_num,eSpell spell_num) { case eSpell::SUMMON_BEAST: r1 = get_summon_monster(1); if(r1 < 0) break; - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(3,1,4) + adj; if(!summon_monster(r1,where,store,2)) add_string_to_buf(" Summon failed."); @@ -909,7 +913,8 @@ void do_mage_spell(short pc_num,eSpell spell_num) { j = minmax(1,7,store); r1 = get_summon_monster(1); //// if(r1 < 0) break; - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(4,1,4) + adj; for(i = 0; i < j; i++) if(!summon_monster(r1,where,store,2)) @@ -920,7 +925,8 @@ void do_mage_spell(short pc_num,eSpell spell_num) { j = minmax(1,6,store); r1 = get_summon_monster(2); //// if(r1 < 0) break; - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(5,1,4) + adj; for(i = 0; i < j; i++) if(!summon_monster(r1,where,store,2)) @@ -931,7 +937,8 @@ void do_mage_spell(short pc_num,eSpell spell_num) { j = minmax(1,5,store); r1 = get_summon_monster(3); //// if(r1 < 0) break; - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(7,1,4) + stat_adj(who_cast,eSkill::INTELLIGENCE); for(i = 0; i < j; i++) if(!summon_monster(r1,where,store,2)) @@ -941,31 +948,35 @@ void do_mage_spell(short pc_num,eSpell spell_num) { store = get_ran(5,1,4) + 2 * stat_adj(who_cast,eSkill::INTELLIGENCE); if(!summon_monster(85,where,store,2)) add_string_to_buf(" Summon failed."); - else univ.party[pc_num].cur_sp -= (*spell_num).cost; + else if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; break; case eSpell::DISPEL_SQUARE: - add_string_to_buf(" Target spell. "); current_pat = square; - start_town_targeting(spell_num,pc_num); + start_town_targeting(spell_num,pc_num,freebie); break; case eSpell::LIGHT_LONG: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; increase_light(200); break; case eSpell::MAGIC_MAP: item = pc_has_abil(pc_num,eItemAbil::SAPPHIRE); - if(item == 24) + if(item == 24 && !freebie) add_string_to_buf(" You need a sapphire. "); else if(univ.town->defy_scrying || univ.town->defy_mapping) add_string_to_buf(" The spell fails. "); else { - remove_charge(pc_num,item); - 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. "); + if(freebie) add_string_to_buf(" You have a vision."); + else { + remove_charge(pc_num,item); + 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. "); + } for(i = 0; i < 64; i++) for(j = 0; j < 64; j++) make_explored(i,j); @@ -975,22 +986,21 @@ void do_mage_spell(short pc_num,eSpell spell_num) { case eSpell::STEALTH: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; PSD[SDF_PARTY_STEALTHY] += max(6,univ.party[pc_num].level * 2); break; case eSpell::SCRY_MONSTER: case eSpell::UNLOCK: case eSpell::CAPTURE_SOUL: case eSpell::DISPEL_BARRIER: case eSpell::BARRIER_FIRE: case eSpell::BARRIER_FORCE: case eSpell::QUICKFIRE: - add_string_to_buf(" Target spell. "); current_pat = single; - start_town_targeting(spell_num,pc_num); + start_town_targeting(spell_num,pc_num,freebie); break; case eSpell::ANTIMAGIC: - add_string_to_buf(" Target spell. "); current_pat = radius2; - start_town_targeting(spell_num,pc_num); + start_town_targeting(spell_num,pc_num,freebie); break; case eSpell::FLIGHT: @@ -1003,7 +1013,8 @@ void do_mage_spell(short pc_num,eSpell spell_num) { else if(univ.party.in_horse >= 0) //// add_string_to_buf(" Leave horse first. "); else { - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; add_string_to_buf(" You start flying! "); PSD[SDF_PARTY_FLIGHT] = 3; } @@ -1011,7 +1022,7 @@ void do_mage_spell(short pc_num,eSpell spell_num) { case eSpell::RESIST_MAGIC: case eSpell::PROTECTION: target = store_spell_target; - if(target < 6) + if(target < 6 && !freebie) univ.party[pc_num].cur_sp -= (*spell_num).cost; if(spell_num == eSpell::PROTECTION && target < 6) { univ.party[target].status[eStatus::INVULNERABLE] += 2 + stat_adj(pc_num,eSkill::INTELLIGENCE) + get_ran(2,1,2); @@ -1029,7 +1040,7 @@ void do_mage_spell(short pc_num,eSpell spell_num) { } } -void do_priest_spell(short pc_num,eSpell spell_num) { +void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) { short r1,r2, target, i,item,store,adj,x,y; location loc; location where; @@ -1047,7 +1058,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { switch(spell_num) { case eSpell::LOCATION: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; if(is_town()) { loc = (overall_mode == MODE_OUTDOORS) ? univ.party.p_loc : univ.town.p_loc; @@ -1064,7 +1076,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { break; case eSpell::MANNA_MINOR: case eSpell::MANNA: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; store = univ.party[pc_num].level / 3 + 2 * stat_adj(who_cast,eSkill::INTELLIGENCE) + get_ran(2,1,4); r1 = max(0,store); if(spell_num == eSpell::MANNA_MINOR) @@ -1076,11 +1089,12 @@ void do_priest_spell(short pc_num,eSpell spell_num) { case eSpell::RITUAL_SANCTIFY: add_string_to_buf(" Sanctify which space? "); current_pat = single; - start_town_targeting(spell_num,pc_num); + start_town_targeting(spell_num,pc_num,freebie); break; case eSpell::LIGHT_DIVINE: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; // TODO: Should this call increase_light_level? univ.party.light_level += 210; break; @@ -1089,10 +1103,12 @@ void do_priest_spell(short pc_num,eSpell spell_num) { store = stat_adj(who_cast,eSkill::INTELLIGENCE); if(!summon_monster(125,where,get_ran(2,1,4) + store,2)) add_string_to_buf(" Summon failed."); - else univ.party[pc_num].cur_sp -= (*spell_num).cost; + else if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; break; case eSpell::STICKS_TO_SNAKES: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; r1 = univ.party[who_cast].level / 6 + stat_adj(who_cast,eSkill::INTELLIGENCE) / 3 + get_ran(1,0,1); for(i = 0; i < r1; i++) { r2 = get_ran(1,0,7); @@ -1102,7 +1118,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { } break; case eSpell::SUMMON_HOST: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(2,1,4) + stat_adj(who_cast,eSkill::INTELLIGENCE); if(!summon_monster(126,where,store,2)) add_string_to_buf(" Summon failed."); @@ -1116,36 +1133,39 @@ void do_priest_spell(short pc_num,eSpell spell_num) { store = get_ran(6,1,4) + stat_adj(who_cast,eSkill::INTELLIGENCE); if(!summon_monster(122,where,store,2)) add_string_to_buf(" Summon failed."); - else univ.party[pc_num].cur_sp -= (*spell_num).cost; + else if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; break; case eSpell::MOVE_MOUNTAINS: case eSpell::MOVE_MOUNTAINS_MASS: add_string_to_buf(" Destroy what? "); current_pat = (spell_num == eSpell::MOVE_MOUNTAINS) ? single : square; - start_town_targeting(spell_num,pc_num); + start_town_targeting(spell_num,pc_num,freebie); break; case eSpell::DISPEL_SPHERE: case eSpell::DISPEL_FIELD: - add_string_to_buf(" Target spell. "); current_pat = (spell_num == eSpell::DISPEL_SPHERE) ? radius2 : single; - start_town_targeting(spell_num,pc_num); + start_town_targeting(spell_num,pc_num,freebie); break; case eSpell::DETECT_LIFE: add_string_to_buf(" Monsters now on map. "); PSD[SDF_PARTY_DETECT_LIFE] += 6 + get_ran(1,0,6); clear_map(); - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; break; case eSpell::FIREWALK: add_string_to_buf(" You are now firewalking. "); PSD[SDF_PARTY_FIREWALK] += univ.party[pc_num].level / 12 + 2; - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; break; case eSpell::SHATTER: add_string_to_buf(" You send out a burst of energy. "); - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; for(loc.x = where.x - 1;loc.x < where.x + 2; loc.x++) for(loc.y = where.y - 1;loc.y < where.y + 2; loc.y++) crumble_wall(loc); @@ -1165,7 +1185,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { add_string_to_buf(" Not while on horseback. "); return; } - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; add_string_to_buf(" You are moved... "); force_town_enter(univ.scenario.which_town_start,univ.scenario.where_start); start_town_mode(univ.scenario.which_town_start,9); @@ -1184,7 +1205,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { // target = select_pc(11,0); target = store_spell_target; if(target < 6) { - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; std::ostringstream sout; sout << " " << univ.party[target].name; switch(spell_num) { @@ -1253,7 +1275,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { return; } - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; std::ostringstream sout; sout << " " << univ.party[target].name; if(spell_num == eSpell::MARTYRS_SHIELD) { @@ -1307,7 +1330,7 @@ void do_priest_spell(short pc_num,eSpell spell_num) { } else { if(!PSD[SDF_RESURRECT_NO_BALM]) { - if((item = pc_has_abil(pc_num,eItemAbil::RESSURECTION_BALM)) == 24) { + if((item = pc_has_abil(pc_num,eItemAbil::RESURRECTION_BALM)) == 24) { add_string_to_buf(" Need resurrection balm."); break; } @@ -1355,7 +1378,8 @@ void do_priest_spell(short pc_num,eSpell spell_num) { break; case eSpell::HEAL_ALL_LIGHT: case eSpell::HEAL_ALL: case eSpell::REVIVE_ALL: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; if(spell_num != eSpell::REVIVE_ALL) { r1 = get_ran((spell_num == eSpell::HEAL_ALL ? 6 : 3) + adj, 1, 4); add_string_to_buf(" Party healed " + std::to_string(r1) + "."); @@ -1373,13 +1397,15 @@ void do_priest_spell(short pc_num,eSpell spell_num) { break; case eSpell::POISON_CURE_ALL: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; add_string_to_buf(" Party cured."); cure_party(3 + stat_adj(pc_num,eSkill::INTELLIGENCE)); break; case eSpell::SANCTUARY_MASS: case eSpell::CLEANSE_MAJOR: case eSpell::HYPERACTIVITY: - univ.party[pc_num].cur_sp -= (*spell_num).cost; + if(!freebie) + univ.party[pc_num].cur_sp -= (*spell_num).cost; switch(spell_num) { case eSpell::SANCTUARY_MASS: add_string_to_buf(" Party hidden.");break; case eSpell::CLEANSE_MAJOR: add_string_to_buf(" Party cleansed.");break; @@ -2262,6 +2288,7 @@ short stat_adj(short pc_num,eSkill which) { } void start_town_targeting(eSpell s_num,short who_c,bool freebie) { + add_string_to_buf(" Target spell."); overall_mode = MODE_TOWN_TARGET; town_spell = s_num; who_cast = who_c; @@ -2339,7 +2366,7 @@ void do_alchemy() { } else { store_i.value = potion_val[which_p]; - store_i.ability_strength = potion_strs[which_p]; + store_i.abil_data[0] = potion_strs[which_p]; store_i.ability = (eItemAbil) potion_abils[which_p]; if(which_p == 8) store_i.magic_use_type = 2; diff --git a/src/boe.party.h b/src/boe.party.h index b51a8db4..89f79c37 100644 --- a/src/boe.party.h +++ b/src/boe.party.h @@ -28,8 +28,8 @@ bool is_weapon(short pc_num,short item); void cast_spell(eSkill type); bool repeat_cast_ok(eSkill type); void give_party_spell(short which); -void do_mage_spell(short pc_num,eSpell spell_num); -void do_priest_spell(short pc_num,eSpell spell_num); +void do_mage_spell(short pc_num,eSpell spell_num, bool freebie = false); +void do_priest_spell(short pc_num,eSpell spell_num, bool freebie = false); void cast_town_spell(location where); bool cast_spell_on_space(location where, eSpell spell); void crumble_wall(location where); @@ -39,7 +39,7 @@ bool pc_can_cast_spell(short pc_num,eSpell spell_num); bool pc_can_cast_spell(short pc_num,eSkill spell_num); eSpell pick_spell(short pc_num,eSkill type); short stat_adj(short pc_num,eSkill which); -void start_town_targeting(eSpell s_num,short who_c,bool freebie = false); +void start_town_targeting(eSpell s_num,short who_c,bool freebie); void do_alchemy(); short alch_choice(short pc_num); bool pick_pc_graphic(short pc_num,short mode,cDialog* parent_num); diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index 1a1a1184..0a02bfe5 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -28,6 +28,7 @@ #include "dlogutil.hpp" #include "fileio.hpp" #include +#include "spell.hpp" extern sf::RenderWindow mainPtr; extern eGameMode overall_mode; @@ -58,18 +59,10 @@ bool special_in_progress = false; // 0 - can't use 1 - combat only 2 - town only 3 - town & combat only 4 - everywhere 5 - outdoor // + 10 - mag. inept can use std::map abil_chart = { - {eItemAbil::POISON_WEAPON,13}, {eItemAbil::BLESS_CURSE,4}, {eItemAbil::AFFECT_POISON,4}, {eItemAbil::HASTE_SLOW,4}, - {eItemAbil::AFFECT_INVULN,3}, {eItemAbil::AFFECT_MAGIC_RES,3}, {eItemAbil::AFFECT_WEB,3}, {eItemAbil::AFFECT_DISEASE,4}, - {eItemAbil::AFFECT_SANCTUARY,3}, {eItemAbil::AFFECT_DUMBFOUND,3}, {eItemAbil::AFFECT_MARTYRS_SHIELD,3}, {eItemAbil::AFFECT_SLEEP,3}, - {eItemAbil::AFFECT_PARALYSIS,3}, {eItemAbil::AFFECT_ACID,3}, {eItemAbil::BLISS,3}, {eItemAbil::AFFECT_EXPERIENCE,4}, + {eItemAbil::POISON_WEAPON,13}, {eItemAbil::AFFECT_STATUS,3}, {eItemAbil::BLISS,3}, {eItemAbil::AFFECT_EXPERIENCE,4}, {eItemAbil::AFFECT_SKILL_POINTS,4}, {eItemAbil::AFFECT_HEALTH,4}, {eItemAbil::AFFECT_SPELL_POINTS,4}, {eItemAbil::DOOM,3}, {eItemAbil::LIGHT,13}, {eItemAbil::STEALTH,3}, {eItemAbil::FIREWALK,3}, {eItemAbil::FLYING,5}, {eItemAbil::MAJOR_HEALING,4}, - {eItemAbil::CALL_SPECIAL,4}, {eItemAbil::FLAME,1}, {eItemAbil::FIREBALL,1}, {eItemAbil::FIRESTORM,1}, {eItemAbil::KILL,1}, - {eItemAbil::ICE_BOLT,1}, {eItemAbil::SLOW,1}, {eItemAbil::SHOCKWAVE,1}, {eItemAbil::DISPEL_UNDEAD,1}, {eItemAbil::DISPEL_SPIRIT,1}, - {eItemAbil::SUMMONING,3}, {eItemAbil::MASS_SUMMONING,3}, {eItemAbil::ACID_SPRAY,1}, {eItemAbil::STINKING_CLOUD,1}, - {eItemAbil::SLEEP_FIELD,1}, {eItemAbil::VENOM,1}, {eItemAbil::SHOCKSTORM,1}, {eItemAbil::PARALYSIS,1}, {eItemAbil::WEB,1}, - {eItemAbil::STRENGTHEN_TARGET,1}, {eItemAbil::QUICKFIRE,3}, {eItemAbil::MASS_CHARM,1}, {eItemAbil::MAGIC_MAP,2}, - {eItemAbil::DISPEL_BARRIER,2}, {eItemAbil::ICE_WALL,1}, {eItemAbil::CHARM_SPELL,1}, {eItemAbil::ANTIMAGIC_CLOUD,1}, + {eItemAbil::CALL_SPECIAL,4}, {eItemAbil::CAST_SPELL,4}, }; // TODO: I bet this is completely unused; it looks like it does nothing. @@ -592,18 +585,42 @@ void use_item(short pc,short item) { bool take_charge = true,inept_ok = false; short level,i,j,item_use_code,str,type,r1; short sp[3] = {}; // Dummy values to pass to run_special; not actually used - eStatus which_stat; + eStatus status; + eSpell spell; location user_loc; cCreature *which_m; extern effect_pat_type single; eItemAbil abil = univ.party[pc].items[item].ability; level = univ.party[pc].items[item].item_level; + // TODO: Replace abil_chart with a cItem member function item_use_code = abil_chart[abil]; if(item_use_code >= 10) { item_use_code -= 10; inept_ok = true; } + if(abil == eItemAbil::AFFECT_STATUS) { + status = eStatus(univ.party[pc].items[item].abil_data[1]); + if(status == eStatus::POISON || status == eStatus::DISEASE || status == eStatus::HASTE_SLOW || status == eStatus:: BLESS_CURSE) + item_use_code = 4; + } else if(abil == eItemAbil::CAST_SPELL) { + spell = eSpell(univ.party[pc].items[item].abil_data[1]); + int when = (*spell).when_cast; + // Rather than trying to translate the spell's "when cast" value to the less expressive item_use_code, + // simply check if it's useable in the current context. + if(is_out() && when & WHEN_OUTDOORS) + item_use_code = 5; + else if(is_town() && when & WHEN_TOWN) + item_use_code = 2; + else if(is_combat() && when & WHEN_COMBAT) + item_use_code = 1; + else { + // It's not useable in the current context, so translate to the item_use_code that gives the best error message + if(is_town() || is_out()) + item_use_code = 1; + else item_use_code = 2; + } + } if(is_out()) user_loc = univ.party.p_loc; @@ -651,63 +668,60 @@ void use_item(short pc,short item) { if(univ.party[pc].items[item].variety == eItemType::POTION) play_sound(56); - str = univ.party[pc].items[item].ability_strength; - store_item_spell_level = str * 2 + 1; + str = univ.party[pc].items[item].abil_data[0]; + store_item_spell_level = str; type = univ.party[pc].items[item].magic_use_type; switch(abil) { case eItemAbil::POISON_WEAPON: // poison weapon take_charge = poison_weapon(pc,str,0); break; - case eItemAbil::BLESS_CURSE: + case eItemAbil::AFFECT_STATUS: + switch(status) { + case eStatus::BLESS_CURSE: play_sound(4); - which_stat = eStatus::BLESS_CURSE; if(type % 2 == 1) { ASB(" You feel awkward."); str = str * -1; }else ASB(" You feel blessed."); if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::HASTE_SLOW: + case eStatus::HASTE_SLOW: // TODO: Is this the right sound? play_sound(75); - which_stat = eStatus::HASTE_SLOW; if(type % 2 == 1) { ASB(" You feel sluggish."); str = str * -1; }else ASB(" You feel speedy."); if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::AFFECT_INVULN: + case eStatus::INVULNERABLE: // TODO: Is this the right sound? play_sound(68); - which_stat = eStatus::INVULNERABLE; if(type % 2 == 1) { ASB(" You feel odd."); str = str * -1; }else ASB(" You feel protected."); if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::AFFECT_MAGIC_RES: + case eStatus::MAGIC_RESISTANCE: // TODO: Is this the right sound? play_sound(51); - which_stat = eStatus::MAGIC_RESISTANCE; if(type % 2 == 1) { ASB(" You feel odd."); str = str * -1; }else ASB(" You feel protected."); if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::AFFECT_WEB: - which_stat = eStatus::WEBS; + case eStatus::WEBS: if(type % 2 == 1) ASB(" You feel sticky."); else { @@ -715,34 +729,32 @@ void use_item(short pc,short item) { str = str * -1; } if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::AFFECT_SANCTUARY: + case eStatus::INVISIBLE: // TODO: Is this the right sound? play_sound(43); - which_stat = eStatus::INVISIBLE; if(type % 2 == 1) { ASB(" You feel exposed."); str = str * -1; }else ASB(" You feel obscure."); if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::AFFECT_MARTYRS_SHIELD: + case eStatus::MARTYRS_SHIELD: // TODO: Is this the right sound? play_sound(43); - which_stat = eStatus::MARTYRS_SHIELD; if(type % 2 == 1) { ASB(" You feel dull."); str = str * -1; }else ASB(" You start to glow slightly."); if(type > 1) - affect_party(which_stat,str); - else affect_pc(pc,which_stat,str); + affect_party(status,str); + else affect_pc(pc,status,str); break; - case eItemAbil::AFFECT_POISON: + case eStatus::POISON: switch(type) { case 0: ASB(" You feel better."); @@ -762,7 +774,7 @@ void use_item(short pc,short item) { break; } break; - case eItemAbil::AFFECT_DISEASE: + case eStatus::DISEASE: switch(type) { case 0: ASB(" You feel healthy."); @@ -783,7 +795,7 @@ void use_item(short pc,short item) { break; } break; - case eItemAbil::AFFECT_DUMBFOUND: + case eStatus::DUMB: switch(type) { case 0: ASB(" You feel clear headed."); @@ -804,7 +816,7 @@ void use_item(short pc,short item) { break; } break; - case eItemAbil::AFFECT_SLEEP: + case eStatus::ASLEEP: switch(type) { case 0: ASB(" You feel alert."); @@ -825,7 +837,7 @@ void use_item(short pc,short item) { break; } break; - case eItemAbil::AFFECT_PARALYSIS: + case eStatus::PARALYZED: switch(type) { case 0: ASB(" You find it easier to move."); @@ -846,7 +858,7 @@ void use_item(short pc,short item) { break; } break; - case eItemAbil::AFFECT_ACID: + case eStatus::ACID: switch(type) { case 0: ASB(" Your skin tingles pleasantly."); @@ -867,6 +879,7 @@ void use_item(short pc,short item) { break; } break; + } case eItemAbil::BLISS: switch(type) { case 0: case 1: @@ -1037,43 +1050,56 @@ void use_item(short pc,short item) { run_special(eSpecCtx::USE_SPEC_ITEM,0,str,user_loc,&sp[0],&sp[1],&sp[2]); break; - - // spell effects - case eItemAbil::FLAME: - add_string_to_buf(" It fires a bolt of flame."); - start_spell_targeting(eSpell::FLAME, true); - break; - case eItemAbil::FIREBALL: - add_string_to_buf(" It shoots a fireball. "); - start_spell_targeting(eSpell::FIREBALL, true); - break; - case eItemAbil::FIRESTORM: - add_string_to_buf(" It shoots a huge fireball. "); - start_spell_targeting(eSpell::FIRESTORM, true); - break; - case eItemAbil::KILL: - add_string_to_buf(" It shoots a black ray. "); - start_spell_targeting(eSpell::KILL, true); - break; - case eItemAbil::ICE_BOLT: - add_string_to_buf(" It fires a ball of ice. "); - start_spell_targeting(eSpell::ICE_BOLT, true); - break; - case eItemAbil::SLOW: - add_string_to_buf(" It fires a purple ray. "); - start_spell_targeting(eSpell::SLOW, true); - break; - case eItemAbil::SHOCKWAVE: - add_string_to_buf(" The ground shakes! "); - do_shockwave(univ.party[current_pc].combat_pos); - break; - case eItemAbil::DISPEL_UNDEAD: - add_string_to_buf(" It shoots a white ray. "); - start_spell_targeting(eSpell::DISPEL_UNDEAD, true); - break; - case eItemAbil::DISPEL_SPIRIT: - add_string_to_buf(" It shoots a golden ray. "); - start_spell_targeting(eSpell::RAVAGE_SPIRIT, true); + case eItemAbil::CAST_SPELL: + if(univ.town.is_antimagic(user_loc.x, user_loc.y)) { + add_string_to_buf(" Not in antimagic field."); + take_charge = false; + break; + } + switch(spell) { + case eSpell::FLAME: add_string_to_buf(" It fires a bolt of flame."); break; + case eSpell::FIREBALL: add_string_to_buf(" It shoots a fireball."); break; + case eSpell::FIRESTORM: add_string_to_buf(" It shoots a huge fireball. "); break; + case eSpell::KILL: add_string_to_buf(" It shoots a black ray."); break; + case eSpell::ICE_BOLT: add_string_to_buf(" It fires a ball of ice."); break; + case eSpell::SLOW: add_string_to_buf(" It fires a purple ray."); break; + case eSpell::DISPEL_UNDEAD: add_string_to_buf(" It shoots a white ray."); break; + case eSpell::RAVAGE_SPIRIT: add_string_to_buf(" It shoots a golden ray."); break; + case eSpell::ACID_SPRAY: add_string_to_buf(" Acid sprays from the tip!"); break; + case eSpell::FOUL_VAPOR: add_string_to_buf(" It creates a cloud of gas."); break; + case eSpell::CLOUD_SLEEP: add_string_to_buf(" It creates a shimmering cloud."); break; + case eSpell::POISON: add_string_to_buf(" A green ray emerges."); break; + case eSpell::SHOCKSTORM: add_string_to_buf(" Sparks fly."); break; + case eSpell::PARALYZE_BEAM: add_string_to_buf(" It shoots a silvery beam."); break; + case eSpell::GOO_BOMB: add_string_to_buf(" It explodes!"); break; + case eSpell::STRENGTHEN_TARGET: add_string_to_buf(" It shoots a fiery red ray."); break; + case eSpell::CHARM_MASS: ASB("It throbs, and emits odd rays."); break; + case eSpell::DISPEL_BARRIER: add_string_to_buf(" It fires a blinding ray."); break; + case eSpell::WALL_ICE_BALL: add_string_to_buf(" It shoots a blue sphere."); break; + case eSpell::CHARM_FOE: add_string_to_buf(" It fires a lovely, sparkling beam."); break; + case eSpell::ANTIMAGIC: add_string_to_buf(" Your hair stands on end."); break; + } + if(overall_mode == MODE_COMBAT) { + bool priest = (*spell).is_priest(); + switch((*spell).refer) { + case REFER_YES: + if(priest) do_priest_spell(current_pc, spell, true); + else do_mage_spell(current_pc, spell, true); + break; + case REFER_TARGET: + start_spell_targeting(spell, true); + break; + case REFER_FANCY: + start_fancy_spell_targeting(spell, true); + break; + case REFER_IMMED: + if(priest) combat_immed_priest_cast(current_pc, spell, true); + else combat_immed_mage_cast(current_pc, spell, true); + break; + } + } else if((*spell).is_priest()) + do_priest_spell(current_pc, spell, true); + else do_mage_spell(current_pc, spell, true); break; case eItemAbil::SUMMONING: if(!summon_monster(str,user_loc,50,2)) @@ -1085,82 +1111,10 @@ void use_item(short pc,short item) { if(!summon_monster(str,user_loc,r1,2)) add_string_to_buf(" Summon failed."); break; - case eItemAbil::ACID_SPRAY: - add_string_to_buf(" Acid sprays from the tip! "); - start_spell_targeting(eSpell::ACID_SPRAY, true); - break; - case eItemAbil::STINKING_CLOUD: - add_string_to_buf(" It creates a cloud of gas. "); - start_spell_targeting(eSpell::FOUL_VAPOR, true); - break; - case eItemAbil::SLEEP_FIELD: - add_string_to_buf(" It creates a shimmering cloud. "); - start_spell_targeting(eSpell::CLOUD_SLEEP, true); - break; - case eItemAbil::VENOM: - add_string_to_buf(" A green ray emerges. "); - start_spell_targeting(eSpell::POISON, true); - break; - case eItemAbil::SHOCKSTORM: - add_string_to_buf(" Sparks fly."); - start_spell_targeting(eSpell::SHOCKSTORM, true); - break; - case eItemAbil::PARALYSIS: - add_string_to_buf(" It shoots a silvery beam. "); - start_spell_targeting(eSpell::PARALYZE_BEAM, true); - break; - case eItemAbil::WEB: - add_string_to_buf(" It explodes!"); - start_spell_targeting(eSpell::GOO_BOMB, true); - break; - case eItemAbil::STRENGTHEN_TARGET: - add_string_to_buf(" It shoots a fiery red ray. "); - start_spell_targeting(eSpell::STRENGTHEN_TARGET, true); - break; case eItemAbil::QUICKFIRE: add_string_to_buf("Fire pours out!"); univ.town.set_quickfire(user_loc.x,user_loc.y,true); break; - case eItemAbil::MASS_CHARM: - ASB("It throbs, and emits odd rays."); - for(i = 0; i < univ.town->max_monst(); i++) { - if((univ.town.monst[i].active != 0) && (univ.town.monst[i].attitude % 2 == 1) - && (dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= 8) - && (can_see_light(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) { - which_m = &univ.town.monst[i]; - charm_monst(which_m,0,eStatus::CHARM,8); - } - } - break; - case eItemAbil::MAGIC_MAP: - if(univ.town->defy_scrying || univ.town->defy_mapping) { - add_string_to_buf(" It doesn't work."); - break; - } - add_string_to_buf(" You have a vision. "); - for(i = 0; i < univ.town->max_dim(); i++) - for(j = 0; j < univ.town->max_dim(); j++) - make_explored(i,j); - clear_map(); - break; - case eItemAbil::DISPEL_BARRIER: - add_string_to_buf(" It fires a blinding ray."); - add_string_to_buf(" Target spell. "); - current_pat = single; - start_town_targeting(eSpell::DISPEL_BARRIER,current_pc, true); - break; - case eItemAbil::ICE_WALL: - add_string_to_buf(" It shoots a blue sphere. "); - start_spell_targeting(eSpell::WALL_ICE_BALL, true); - break; - case eItemAbil::CHARM_SPELL: - add_string_to_buf(" It fires a lovely, sparkling beam."); - start_spell_targeting(eSpell::CHARM_FOE, true); - break; - case eItemAbil::ANTIMAGIC_CLOUD: - add_string_to_buf(" Your hair stands on end. "); - start_spell_targeting(eSpell::ANTIMAGIC, true); - break; } } diff --git a/src/boe.town.cpp b/src/boe.town.cpp index 30e30c15..5caf58a4 100644 --- a/src/boe.town.cpp +++ b/src/boe.town.cpp @@ -1064,13 +1064,13 @@ void pick_lock(location where,short pc_num) { return; } - r1 = get_ran(1,1,100) + univ.party[pc_num].items[which_item].ability_strength * 7; + r1 = get_ran(1,1,100) + univ.party[pc_num].items[which_item].abil_data[0] * 7; if(r1 < 75) will_break = true; r1 = get_ran(1,1,100) - 5 * stat_adj(pc_num,eSkill::DEXTERITY) + univ.town.difficulty * 7 - - 5 * univ.party[pc_num].skills[eSkill::LOCKPICKING] - univ.party[pc_num].items[which_item].ability_strength * 7; + - 5 * univ.party[pc_num].skills[eSkill::LOCKPICKING] - univ.party[pc_num].items[which_item].abil_data[0] * 7; // Nimble? if(univ.party[pc_num].traits[eTrait::NIMBLE]) diff --git a/src/classes/item.cpp b/src/classes/item.cpp index be4279ab..b82ab164 100644 --- a/src/classes/item.cpp +++ b/src/classes/item.cpp @@ -47,7 +47,7 @@ cItem::cItem(){ magic_use_type = 0; graphic_num = 0; ability = eItemAbil::NONE; - ability_strength = 0; + abil_data[0] = 0; type_flag = 0; is_special = 0; value = 0; @@ -66,7 +66,7 @@ cItem::cItem(){ cItem::cItem(long preset){ ability = eItemAbil::NONE; - ability_strength = 0; + abil_data[0] = 0; type_flag = 0; is_special = 0; special_class = 0; @@ -260,8 +260,432 @@ void cItem::append(legacy::item_record_type& old){ graphic_num = 74; else if(graphic_num >= 45) // small graphics were moved up to make a bit more room for new large graphics graphic_num += 10; - ability = (eItemAbil) old.ability; - ability_strength = old.ability_strength; + abil_data[0] = old.ability_strength; + switch(old.ability) { + // Weapon abilities + case 0: + ability = eItemAbil::NONE; + break; + case 1: + ability = eItemAbil::FLAMING_WEAPON; + break; + case 2: + ability = eItemAbil::DEMON_SLAYER; + break; + case 3: + ability = eItemAbil::UNDEAD_SLAYER; + break; + case 4: + ability = eItemAbil::LIZARD_SLAYER; + break; + case 5: + ability = eItemAbil::GIANT_SLAYER; + break; + case 6: + ability = eItemAbil::MAGE_SLAYER; + break; + case 7: + ability = eItemAbil::PRIEST_SLAYER; + break; + case 8: + ability = eItemAbil::BUG_SLAYER; + break; + case 9: + ability = eItemAbil::STATUS_WEAPON; + abil_data[1] = int(eStatus::ACID); + break; + case 10: + ability = eItemAbil::SOULSUCKER; + break; + case 11: + ability = eItemAbil::DRAIN_MISSILES; + break; + case 12: + ability = eItemAbil::WEAK_WEAPON; + break; + case 13: + ability = eItemAbil::CAUSES_FEAR; + break; + case 14: + ability = eItemAbil::STATUS_WEAPON; + abil_data[1] = int(eStatus::POISON); + break; + // General abilities + case 30: + ability = eItemAbil::PROTECTION; + break; + case 31: + ability = eItemAbil::FULL_PROTECTION; + break; + case 32: + ability = eItemAbil::FIRE_PROTECTION; + break; + case 33: + ability = eItemAbil::COLD_PROTECTION; + break; + case 34: + ability = eItemAbil::POISON_PROTECTION; + break; + case 35: + ability = eItemAbil::MAGIC_PROTECTION; + break; + case 36: + ability = eItemAbil::ACID_PROTECTION; + break; + case 37: + ability = eItemAbil::SKILL; + break; + case 38: + ability = eItemAbil::STRENGTH; + break; + case 39: + ability = eItemAbil::DEXTERITY; + break; + case 40: + ability = eItemAbil::INTELLIGENCE; + break; + case 41: + ability = eItemAbil::ACCURACY; + break; + case 42: + ability = eItemAbil::THIEVING; + break; + case 43: + ability = eItemAbil::GIANT_STRENGTH; + break; + case 44: + ability = eItemAbil::LIGHTER_OBJECT; + break; + case 45: + ability = eItemAbil::HEAVIER_OBJECT; + break; + case 46: + ability = eItemAbil::OCCASIONAL_BLESS; + break; + case 47: + ability = eItemAbil::OCCASIONAL_HASTE; + break; + case 48: + ability = eItemAbil::LIFE_SAVING; + break; + case 49: + ability = eItemAbil::PROTECT_FROM_PETRIFY; + break; + case 50: + ability = eItemAbil::REGENERATE; + break; + case 51: + ability = eItemAbil::POISON_AUGMENT; + break; + case 52: + ability = eItemAbil::DISEASE_PARTY; + break; + case 53: + ability = eItemAbil::WILL; + break; + case 54: + ability = eItemAbil::FREE_ACTION; + break; + case 55: + ability = eItemAbil::SPEED; + break; + case 56: + ability = eItemAbil::SLOW_WEARER; + break; + case 57: + ability = eItemAbil::PROTECT_FROM_UNDEAD; + break; + case 58: + ability = eItemAbil::PROTECT_FROM_DEMONS; + break; + case 59: + ability = eItemAbil::PROTECT_FROM_HUMANOIDS; + break; + case 60: + ability = eItemAbil::PROTECT_FROM_REPTILES; + break; + case 61: + ability = eItemAbil::PROTECT_FROM_GIANTS; + break; + case 62: + ability = eItemAbil::PROTECT_FROM_DISEASE; + break; + // Usable abilities + case 70: + ability = eItemAbil::POISON_WEAPON; + break; + case 71: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::BLESS_CURSE); + break; + case 72: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::POISON); + break; + case 73: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::HASTE_SLOW); + break; + case 74: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::INVULNERABLE); + break; + case 75: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::MAGIC_RESISTANCE); + break; + case 76: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::WEBS); + break; + case 77: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::DISEASE); + break; + case 78: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::INVISIBLE); + break; + case 79: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::DUMB); + break; + case 80: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::MARTYRS_SHIELD); + break; + case 81: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::ASLEEP); + break; + case 82: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::PARALYZED); + break; + case 83: + ability = eItemAbil::AFFECT_STATUS; + abil_data[1] = int(eStatus::ACID); + break; + case 84: + ability = eItemAbil::BLISS; + break; + case 85: + ability = eItemAbil::AFFECT_EXPERIENCE; + break; + case 86: + ability = eItemAbil::AFFECT_SKILL_POINTS; + break; + case 87: + ability = eItemAbil::AFFECT_HEALTH; + break; + case 88: + ability = eItemAbil::AFFECT_SPELL_POINTS; + break; + case 89: + ability = eItemAbil::DOOM; + break; + case 90: + ability = eItemAbil::LIGHT; + break; + case 91: + ability = eItemAbil::STEALTH; + break; + case 92: + ability = eItemAbil::FIREWALK; + break; + case 93: + ability = eItemAbil::FLYING; + break; + case 94: + ability = eItemAbil::MAJOR_HEALING; + break; + case 95: + ability = eItemAbil::CALL_SPECIAL; + break; + // Spells + case 110: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::FLAME); + break; + case 111: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::FIREBALL); + break; + case 112: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::FIRESTORM); + break; + case 113: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::KILL); + break; + case 114: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::ICE_BOLT); + break; + case 115: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::SLOW); + break; + case 116: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::SHOCKWAVE); + break; + case 117: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::DISPEL_UNDEAD); + break; + case 118: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::RAVAGE_SPIRIT); + break; + case 119: + ability = eItemAbil::SUMMONING; + break; + case 120: + ability = eItemAbil::MASS_SUMMONING; + break; + case 121: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::ACID_SPRAY); + break; + case 122: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::FOUL_VAPOR); + break; + case 123: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::CLOUD_SLEEP); + break; + case 124: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::POISON); + break; + case 125: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::SHOCKSTORM); + break; + case 126: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::PARALYZE_BEAM); + break; + case 127: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::GOO_BOMB); + break; + case 128: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::STRENGTHEN_TARGET); + break; + case 129: + ability = eItemAbil::QUICKFIRE; + break; + case 130: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::CHARM_MASS); + break; + case 131: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::MAGIC_MAP); + break; + case 132: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::DISPEL_BARRIER); + break; + case 133: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::WALL_ICE_BALL); + break; + case 134: + ability = eItemAbil::CAST_SPELL; + abil_data[1] = int(eSpell::CHARM_FOE); + break; + case 135: + ability = eItemAbil::CAST_SPELL; + abil_data[0] = abil_data[0] * 2 + 1; + abil_data[1] = int(eSpell::ANTIMAGIC); + break; + // Reagents + case 150: + ability = eItemAbil::HOLLY; + break; + case 151: + ability = eItemAbil::COMFREY; + break; + case 152: + ability = eItemAbil::NETTLE; + break; + case 153: + ability = eItemAbil::WORMGRASS; + break; + case 154: + ability = eItemAbil::ASPTONGUE; + break; + case 155: + ability = eItemAbil::EMBERF; + break; + case 156: + ability = eItemAbil::GRAYMOLD; + break; + case 157: + ability = eItemAbil::MANDRAKE; + break; + case 158: + ability = eItemAbil::SAPPHIRE; + break; + case 159: + ability = eItemAbil::SMOKY_CRYSTAL; + break; + case 160: + ability = eItemAbil::RESURRECTION_BALM; + break; + case 161: + ability = eItemAbil::LOCKPICKS; + break; + // Missile abilities + case 170: + ability = eItemAbil::MISSILE_RETURNING; + break; + case 171: + ability = eItemAbil::MISSILE_LIGHTNING; + break; + case 172: + ability = eItemAbil::MISSILE_EXPLODING; + break; + case 173: + ability = eItemAbil::MISSILE_ACID; + break; + case 174: + ability = eItemAbil::MISSILE_SLAY_UNDEAD; + break; + case 175: + ability = eItemAbil::MISSILE_SLAY_DEMON; + break; + case 176: + ability = eItemAbil::MISSILE_HEAL_TARGET; + break; + } type_flag = old.type_flag; is_special = old.is_special; value = old.value; @@ -295,7 +719,7 @@ void cItem::writeTo(std::ostream& file, std::string prefix) const { file << prefix << "USE " << magic_use_type << '\n'; file << prefix << "ICON " << graphic_num << '\n'; file << prefix << "ABILITY " << ability << '\n'; - file << prefix << "ABILSTR " << ability_strength << '\n'; + file << prefix << "ABILSTR " << abil_data[0] << '\t' << abil_data[1] << '\n'; file << prefix << "TYPE " << type_flag << '\n'; file << prefix << "ISSPEC " << is_special << '\n'; file << prefix << "VALUE " << value << '\n'; @@ -331,7 +755,7 @@ void cItem::readFrom(std::istream& sin){ else if(cur == "USE") sin >> magic_use_type; else if(cur == "ICON") sin >> graphic_num; else if(cur == "ABILITY") sin >> ability; - else if(cur == "ABILSTR") sin >> ability_strength; + else if(cur == "ABILSTR") sin >> abil_data[0] >> abil_data[1]; else if(cur == "TYPE") sin >> type_flag; else if(cur == "ISSPEC") sin >> is_special; else if(cur == "VALUE") sin >> value; diff --git a/src/classes/item.h b/src/classes/item.h index 35a1d682..eccfb148 100644 --- a/src/classes/item.h +++ b/src/classes/item.h @@ -29,7 +29,7 @@ public: int magic_use_type; unsigned short graphic_num; eItemAbil ability; - unsigned int ability_strength; + unsigned int abil_data[2]; unsigned short type_flag; unsigned int is_special; short value; @@ -77,18 +77,4 @@ public: std::string descr; }; -/* - typedef struct { - short variety, item_level; - char awkward, bonus, protection, charges, type; - unsigned char graphic_num,ability, type_flag, is_special; - short value; - bool identified, magic; - unsigned char weight, description_flag; - char full_name[25], name[15]; - unsigned char reserved1,reserved2; - unsigned char magic_use_type, ability_strength, treas_class, real_abil; - } short_item_record_type; - */ - #endif diff --git a/src/classes/simpletypes.h b/src/classes/simpletypes.h index 0fb5ba40..147e9984 100644 --- a/src/classes/simpletypes.h +++ b/src/classes/simpletypes.h @@ -300,12 +300,11 @@ enum class eItemAbil { MAGE_SLAYER = 6, PRIEST_SLAYER = 7, BUG_SLAYER = 8, - ACIDIC_WEAPON = 9, + STATUS_WEAPON = 9, SOULSUCKER = 10, DRAIN_MISSILES = 11, WEAK_WEAPON = 12, CAUSES_FEAR = 13, - POISONED_WEAPON = 14, // General abilities PROTECTION = 30, FULL_PROTECTION = 31, @@ -342,19 +341,8 @@ enum class eItemAbil { PROTECT_FROM_DISEASE = 62, // Nonspell Usable POISON_WEAPON = 70, //put poison on weapon - BLESS_CURSE = 71, - AFFECT_POISON = 72, - HASTE_SLOW = 73, - AFFECT_INVULN = 74, - AFFECT_MAGIC_RES = 75, - AFFECT_WEB = 76, - AFFECT_DISEASE = 77, - AFFECT_SANCTUARY = 78, - AFFECT_DUMBFOUND = 79, - AFFECT_MARTYRS_SHIELD = 80, - AFFECT_SLEEP = 81, - AFFECT_PARALYSIS = 82, - AFFECT_ACID = 83, + AFFECT_STATUS = 71, + CAST_SPELL = 72, BLISS = 84, AFFECT_EXPERIENCE = 85, AFFECT_SKILL_POINTS = 86, @@ -367,33 +355,9 @@ enum class eItemAbil { FLYING = 93, MAJOR_HEALING = 94, CALL_SPECIAL = 95, - // Spell Usable - FLAME = 110, - FIREBALL = 111, - FIRESTORM = 112, - KILL = 113, - ICE_BOLT = 114, - SLOW = 115, - SHOCKWAVE = 116, - DISPEL_UNDEAD = 117, - DISPEL_SPIRIT = 118, SUMMONING = 119, MASS_SUMMONING = 120, - ACID_SPRAY = 121, - STINKING_CLOUD = 122, - SLEEP_FIELD = 123, - VENOM = 124, - SHOCKSTORM = 125, - PARALYSIS = 126, - WEB = 127, - STRENGTHEN_TARGET = 128, //wand of carrunos effect QUICKFIRE = 129, - MASS_CHARM = 130, - MAGIC_MAP = 131, - DISPEL_BARRIER = 132, - ICE_WALL = 133, - CHARM_SPELL = 134, - ANTIMAGIC_CLOUD = 135, // Reagents HOLLY = 150, // Holly/Toadstool COMFREY = 151, // Comfreey Root @@ -405,7 +369,7 @@ enum class eItemAbil { MANDRAKE = 157, SAPPHIRE = 158, SMOKY_CRYSTAL = 159, - RESSURECTION_BALM = 160, + RESURRECTION_BALM = 160, LOCKPICKS = 161, // Missile Abilities MISSILE_RETURNING = 170, diff --git a/src/classes/spell.cpp b/src/classes/spell.cpp index de218797..2ab0e67f 100644 --- a/src/classes/spell.cpp +++ b/src/classes/spell.cpp @@ -57,7 +57,12 @@ const cSpell& cSpell::finish() { std::string cSpell::name() const { return get_str("magic-names", int(num) + 1); -}; +} + +bool cSpell::is_priest() const { + // This is used to determine where the spell is implemented, not which skill is required. + return int(num) >= 100; +} const cSpell& operator*(eSpell spell_num) { return cSpell::dictionary[spell_num]; @@ -343,32 +348,32 @@ cSpell P_CLEANSE_MAJOR = cSpell(eSpell::CLEANSE_MAJOR).asType(eSkill::PRIEST_SPE // Special spells cSpell S_STRENGTHEN_TARGET = cSpell(eSpell::STRENGTHEN_TARGET) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_SUMMON_RAT = cSpell(eSpell::SUMMON_RAT) - .withRange(8).finish(); + .withRange(8).withRefer(REFER_TARGET).when(WHEN_COMBAT).when(WHEN_TOWN).finish(); cSpell S_WALL_ICE_BALL = cSpell(eSpell::WALL_ICE_BALL) - .withRange(8).finish(); + .withRange(8).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_GOO_BOMB = cSpell(eSpell::GOO_BOMB) - .withRange(12).finish(); + .withRange(12).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_FOUL_VAPOR = cSpell(eSpell::FOUL_VAPOR) - .withRange(8).finish(); + .withRange(8).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_CLOUD_SLEEP_LARGE = cSpell(eSpell::CLOUD_SLEEP_LARGE) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_ACID_SPRAY = cSpell(eSpell::ACID_SPRAY) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_PARALYZE_BEAM = cSpell(eSpell::PARALYZE_BEAM) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_SLEEP_MASS = cSpell(eSpell::SLEEP_MASS) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_RAVAGE_ENEMIES = cSpell(eSpell::RAVAGE_ENEMIES) - .withRange(12).finish(); + .withRange(12).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_DISPEL_FIELD = cSpell(eSpell::DISPEL_FIELD) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).when(WHEN_TOWN).finish(); cSpell S_MOVE_MOUNTAINS_MASS = cSpell(eSpell::MOVE_MOUNTAINS_MASS) - .withRange(8).finish(); + .withRange(8).withRefer(REFER_TARGET).when(WHEN_TOWN).finish(); // TODO: These two have a range of 10 only because all monster spells do (monster spells ignore official spell range) // They should perhaps have better ranges assigned at some point. cSpell S_WRACK = cSpell(eSpell::WRACK).asLevel(1) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell S_UNHOLY_RAVAGING = cSpell(eSpell::UNHOLY_RAVAGING).asLevel(6) - .withRange(10).finish(); + .withRange(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); diff --git a/src/classes/spell.hpp b/src/classes/spell.hpp index e888f3b2..e3ef3204 100644 --- a/src/classes/spell.hpp +++ b/src/classes/spell.hpp @@ -38,6 +38,7 @@ public: eSkill type; int when_cast; std::string name() const; + bool is_priest() const; static eSpell fromNum(eSkill type, int num); }; diff --git a/src/classes/universe.cpp b/src/classes/universe.cpp index 4d621723..10114ef5 100644 --- a/src/classes/universe.cpp +++ b/src/classes/universe.cpp @@ -876,7 +876,7 @@ void cUniverse::check_item(cItem& item) { else if(item.graphic_num >= 1000) update_items[item.graphic_num - 1000].insert(&item); if(item.ability == eItemAbil::SUMMONING || item.ability == eItemAbil::MASS_SUMMONING) { - m_num_t monst = item.ability_strength; + m_num_t monst = item.abil_data[0]; if(monst >= 10000) check_monst(party.summons[monst - 10000]); else check_monst(scenario.scen_monsters[monst]); @@ -986,7 +986,7 @@ void cUniverse::exportSummons() { for(size_t j = 0; j < party[i].items.size(); j++) { if(party[i].items[j].variety == eItemType::NO_ITEM) continue; if(party[i].items[j].ability == eItemAbil::SUMMONING || party[i].items[j].ability == eItemAbil::MASS_SUMMONING) { - m_num_t monst = party[i].items[j].ability_strength; + m_num_t monst = party[i].items[j].abil_data[0]; if(monst >= 10000) used_monsters.insert(monst - 10000); else { @@ -1000,7 +1000,7 @@ void cUniverse::exportSummons() { for(size_t j = 0; j < party.stored_items[i].size(); j++) { if(party.stored_items[i][j].variety == eItemType::NO_ITEM) continue; if(party.stored_items[i][j].ability == eItemAbil::SUMMONING||party.stored_items[i][j].ability == eItemAbil::MASS_SUMMONING) { - m_num_t monst = party.stored_items[i][j].ability_strength; + m_num_t monst = party.stored_items[i][j].abil_data[0]; if(monst >= 10000) used_monsters.insert(monst - 10000); else { @@ -1031,7 +1031,7 @@ void cUniverse::exportSummons() { party.summons[dest] = scenario.scen_monsters[monst]; else party.summons.push_back(scenario.scen_monsters[monst]); for(auto& item : update_items[monst]) - item->ability_strength = 10000 + dest; + item->abil_data[0] = 10000 + dest; for(int i = 0; i < 4; i++) if(party.imprisoned_monst[i] == monst) party.imprisoned_monst[i] = dest + 10000; diff --git a/src/scenedit/scen.core.cpp b/src/scenedit/scen.core.cpp index 257bc8c1..43e07fd8 100644 --- a/src/scenedit/scen.core.cpp +++ b/src/scenedit/scen.core.cpp @@ -958,7 +958,7 @@ static void put_item_abils_in_dlog(cDialog& me, cItem& store_item, short which_i dynamic_cast(me["use-type"]).setSelected("use" + std::to_string(store_item.magic_use_type)); dynamic_cast(me["treasure"]).setSelected("type" + std::to_string(store_item.treas_class)); - me["str"].setTextToNum(store_item.ability_strength); + me["str"].setTextToNum(store_item.abil_data[0]); dynamic_cast(me["always-id"]).setState(store_item.ident ? led_red : led_off); dynamic_cast(me["magic"]).setState(store_item.magic ? led_red : led_off); @@ -969,7 +969,7 @@ static void put_item_abils_in_dlog(cDialog& me, cItem& store_item, short which_i static bool save_item_abils(cDialog& me, cItem& store_item) { store_item.magic_use_type = boost::lexical_cast(dynamic_cast(me["use-type"]).getSelected().substr(3)); store_item.treas_class = boost::lexical_cast(dynamic_cast(me["treasure"]).getSelected().substr(4)); - store_item.ability_strength = me["str"].getTextAsNum(); + store_item.abil_data[0] = me["str"].getTextAsNum(); store_item.property = store_item.enchanted = store_item.contained = false; store_item.ident = dynamic_cast(me["always-id"]).getState() != led_off;