diff --git a/rsrc/strings/monster-abilities.txt b/rsrc/strings/monster-abilities.txt index 41447bf4..603acac2 100644 --- a/rsrc/strings/monster-abilities.txt +++ b/rsrc/strings/monster-abilities.txt @@ -44,6 +44,7 @@ Summon (%5 chance) Summon (%20 chance) Summon (%50 chance) Run global special during turn +Run global special when hit Death triggers global special Radiate fire fields Radiate ice fields @@ -76,7 +77,6 @@ Custom long damage (advanced) - Missile Damaging Status effect @@ -178,7 +178,7 @@ Chance of using Special to Call - +Special to Call diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index bebc058c..1008df6b 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -788,10 +788,18 @@ void pc_attack_weapon(short who_att,short target,short hit_adj,short dam_adj,cIt } if((weap.ability == eItemAbil::STATUS_WEAPON) && (get_ran(1,0,1) == 1)) { apply_weapon_status(eStatus(weap.abil_data[1]), weap.abil_data[0], r2 + spec_dam, target + 100, "Blade"); - } - if((weap.ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) { + } else if(weap.ability == eItemAbil::SOULSUCKER && get_ran(1,0,1) == 1) { add_string_to_buf(" Blade drains life."); heal_pc(who_att,weap.abil_data[0] / 2); + } else if(weap.ability == eItemAbil::WEAPON_CALL_SPECIAL) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = which_m->cur_loc.x; + PSD[SDF_SPEC_TARGLOC_Y] = which_m->cur_loc.y; + PSD[SDF_SPEC_TARGET] = 100 + target; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKING_MELEE, 0, weap.abil_data[0],univ.party[who_att].combat_pos, &s1, &s2, &s3); } } else { @@ -1464,6 +1472,8 @@ void load_missile() { add_string_to_buf(" (Hit 's' to cancel.)"); overall_mode = MODE_THROWING; current_spell_range = 8; + if(univ.party[current_pc].items[thrown].ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += univ.party[current_pc].items[thrown].abil_data[0]; current_pat = single; } else if(((bolts < 24) && (bow < 24)) || ((arrow < 24) && (crossbow < 24))) { @@ -1482,6 +1492,8 @@ void load_missile() { add_string_to_buf("Fire: Select a target. "); add_string_to_buf(" (Hit 's' to cancel.)"); current_spell_range = 12; + if(univ.party[current_pc].items[arrow].ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += univ.party[current_pc].items[arrow].abil_data[0]; if(univ.party[current_pc].items[arrow].ability == eItemAbil::EXPLODING_WEAPON) current_pat = radius2; else @@ -1494,6 +1506,8 @@ void load_missile() { add_string_to_buf("Fire: Select a target. "); add_string_to_buf(" (Hit 's' to cancel.)"); current_spell_range = 12; + if(univ.party[current_pc].items[bolts].ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += univ.party[current_pc].items[bolts].abil_data[0]; current_pat = single; } else if(no_ammo < 24) { @@ -1503,6 +1517,8 @@ void load_missile() { add_string_to_buf("Fire: Select a target. "); add_string_to_buf(" (Hit 's' to cancel.)"); current_spell_range = 12; + if(univ.party[current_pc].items[no_ammo].ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += univ.party[current_pc].items[no_ammo].abil_data[0]; current_pat = single; } else add_string_to_buf("Fire: Equip a missile. "); @@ -1523,14 +1539,42 @@ void fire_missile(location target) { hit_bonus = univ.party[missile_firer].items[missile_inv_slot].bonus; 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 = 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; + skill_item = univ.party[missile_firer].get_prot_level(eItemAbil::ACCURACY) / 2; + hit_bonus += skill_item; + dam_bonus += skill_item; + if(univ.party[missile_firer].items[ammo_inv_slot].ability == eItemAbil::SEEKING_MISSILE) { + targ_monst = monst_there(target); + std::set targets; + if(targ_monst >= univ.town->max_monst() || univ.town.monst[targ_monst].attitude % 2 == 0) { + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + if(i == 0 && j == 0) continue; + targ_monst = monst_there(loc(target.x + i, target.y + j)); + if(targ_monst < univ.town->max_monst()) { + bool invisible = univ.town.monst[targ_monst].invisible; + bool friendly = univ.town.monst[targ_monst].attitude % 2 == 0; + int seek_chance = 10; + if(invisible && friendly) + seek_chance -= 8; + else if(invisible) + seek_chance -= 5; + else if(friendly) + seek_chance -= 9; + if(get_ran(1,1,10) <= seek_chance) + targets.insert(targ_monst); + } + } + } + if(!targets.empty()) { + auto iter = targets.begin(); + std::advance(iter, get_ran(1,1,targets.size()) - 1); + target = univ.town.monst[*iter].cur_loc; + } + } else hit_bonus += 10; } // race adj. - // TODO: Should this apply to sliths as well? The bladbase suggests otherwise, but it has been changed from the original; maybe the sliths were originally considered to be reptiles. - if(univ.party[missile_firer].race == eRace::REPTILE) + if(univ.party[missile_firer].race == eRace::NEPHIL) hit_bonus += 2; if(univ.party[missile_firer].items[ammo_inv_slot].ability == eItemAbil::EXPLODING_WEAPON) @@ -1600,11 +1644,20 @@ void fire_missile(location target) { } if((missile.ability == eItemAbil::STATUS_WEAPON) && (get_ran(1,0,1) == 1)) { apply_weapon_status(eStatus(missile.abil_data[1]), missile.abil_data[0], r2 + spec_dam, targ_monst + 100, "Missile"); - } - if((missile.ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) { + } else if(missile.ability == eItemAbil::SOULSUCKER && get_ran(1,0,1) == 1) { add_string_to_buf(" Missile drains life."); heal_pc(missile_firer,missile.abil_data[0] / 2); } + if(cur_monst->abil[eMonstAbil::HIT_TRIGGER].active) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = cur_monst->cur_loc.x; + PSD[SDF_SPEC_TARGLOC_Y] = cur_monst->cur_loc.y; + PSD[SDF_SPEC_TARGET] = 100 + targ_monst; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKED_RANGE, 0, cur_monst->abil[eMonstAbil::HIT_TRIGGER].special.extra1, univ.party[missile_firer].combat_pos, &s1, &s2, &s3); + } } else if((targ_monst = pc_there(target)) < 6) { eDamageType dmg_tp = eDamageType::UNBLOCKABLE; cItem& missile = univ.party[missile_firer].items[ammo_inv_slot]; @@ -1625,10 +1678,29 @@ void fire_missile(location target) { } if((missile.ability == eItemAbil::STATUS_WEAPON) && (get_ran(1,0,1) == 1)) { apply_weapon_status(eStatus(missile.abil_data[1]), missile.abil_data[0], r2 + spec_dam, targ_monst, "Missile"); - } - if((missile.ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) { + } else if(missile.ability == eItemAbil::SOULSUCKER && get_ran(1,0,1) == 1) { add_string_to_buf(" Missile drains life."); heal_pc(missile_firer,missile.abil_data[0] / 2); + } else if(missile.ability == eItemAbil::WEAPON_CALL_SPECIAL) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = univ.party[targ_monst].combat_pos.x; + PSD[SDF_SPEC_TARGLOC_Y] = univ.party[targ_monst].combat_pos.y; + PSD[SDF_SPEC_TARGET] = 11 + targ_monst; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKING_RANGE, 0, missile.abil_data[0], univ.party[missile_firer].combat_pos, &s1, &s2, &s3); + } + int spec_item = univ.party[targ_monst].has_abil_equip(eItemAbil::HIT_CALL_SPECIAL); + if(spec_item < 24) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = univ.party[targ_monst].combat_pos.x; + PSD[SDF_SPEC_TARGLOC_Y] = univ.party[targ_monst].combat_pos.y; + PSD[SDF_SPEC_TARGET] = 11 + targ_monst; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKED_RANGE, 0, univ.party[targ_monst].items[spec_item].abil_data[0], univ.party[missile_firer].combat_pos, &s1, &s2, &s3); } } } @@ -2171,7 +2243,14 @@ void do_monster_turn() { uAbility abil = cur_monst->abil[eMonstAbil::SPECIAL]; short s1, s2, s3; special_called = true; - // TODO: Is it good to allow only one monster to use a special ability per combat round? + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = targ_space.x; + PSD[SDF_SPEC_TARGLOC_Y] = targ_space.y; + if(target < 6) + PSD[SDF_SPEC_TARGET] = 11 + target; // ready to be passed to SELECT_TARGET node + else PSD[SDF_SPEC_TARGET] = target; // ready to be passed to SELECT_TARGET node run_special(eSpecCtx::MONST_SPEC_ABIL,0,abil.special.extra1,cur_monst->cur_loc,&s1,&s2,&s3); take_m_ap(abil.special.extra2,cur_monst); } @@ -2433,6 +2512,7 @@ void monster_attack_pc(short who_att,short target) { r1 = get_ran(1,1,100) - 5 * min(8,attacker->status[eStatus::BLESS_CURSE]) + 5 * univ.party[target].status[eStatus::BLESS_CURSE] + 5 * stat_adj(target,eSkill::DEXTERITY) - 15; r1 += 5 * (attacker->status[eStatus::WEBS] / 3); + r1 += univ.party[target].get_prot_level(eItemAbil::EVASION); if(univ.party[target].parry < 100) r1 += 5 * univ.party[target].parry; @@ -2465,9 +2545,13 @@ void monster_attack_pc(short who_att,short target) { damaged_message(store_hp - univ.party[target].cur_health, attacker->a[i].type); - if(univ.party[target].status[eStatus::MARTYRS_SHIELD] > 0) { + int martyr1 = univ.party[target].status[eStatus::MARTYRS_SHIELD]; + int martyr2 = univ.party[target].get_prot_level(eItemAbil::MARTYRS_SHIELD); + if(martyr1 + martyr2 > 0) { + int dmg = store_hp - univ.party[target].cur_health; + if(get_ran(1,1,20) < martyr2) dmg += max(1, martyr2 / 5); add_string_to_buf(" Shares damage! "); - damage_monst(who_att, 6, store_hp - univ.party[target].cur_health, 0, eDamageType::MAGIC,0); + damage_monst(who_att, 6, dmg, 0, eDamageType::MAGIC,0); } for(auto& abil : attacker->abil) { @@ -2521,6 +2605,18 @@ void monster_attack_pc(short who_att,short target) { if(snd > 0) play_sound(snd); monst_basic_abil(who_att, abil, target); } + + int spec_item = univ.party[target].has_abil_equip(eItemAbil::HIT_CALL_SPECIAL); + if(spec_item < 24) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = univ.party[target].combat_pos.x; + PSD[SDF_SPEC_TARGLOC_Y] = univ.party[target].combat_pos.y; + PSD[SDF_SPEC_TARGET] = 11 + who_att; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKED_MELEE, 0, univ.party[target].items[spec_item].abil_data[0], attacker->cur_loc, &s1, &s2, &s3); + } } } else { @@ -2644,6 +2740,17 @@ void monster_attack_monster(short who_att,short attackee) { monst_basic_abil(who_att, abil, attackee + 100); put_pc_screen(); } + + if(target->abil[eMonstAbil::HIT_TRIGGER].active) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = target->cur_loc.x; + PSD[SDF_SPEC_TARGLOC_Y] = target->cur_loc.y; + PSD[SDF_SPEC_TARGET] = 100 + attackee; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKED_MELEE, 0, target->abil[eMonstAbil::HIT_TRIGGER].special.extra1, attacker->cur_loc, &s1, &s2, &s3); + } } } else { @@ -2739,6 +2846,7 @@ void monst_fire_missile(short m_num,short bless,std::pair a int target_bless = target < 100 ? univ.party[target].status[eStatus::BLESS_CURSE] : m_target->status[eStatus::BLESS_CURSE]; location target_pos = target < 100 ? univ.party[target].combat_pos : m_target->cur_loc; int r1 = get_ran(1,1,100) - 5 * min(10,bless) + 5 * target_bless - 5 * can_see_light(source, target_pos, sight_obscurity); + r1 += univ.party[target].get_prot_level(eItemAbil::EVASION); if(univ.party[target].parry < 100) r1 += 5 * univ.party[target].parry; @@ -2756,6 +2864,29 @@ void monst_fire_missile(short m_num,short bless,std::pair a if(target < 100) add_string_to_buf(" Misses " + univ.party[target].name + '.'); else monst_spell_note(m_target->number,18); } + if(target < 100) { + int spec_item = univ.party[target].has_abil_equip(eItemAbil::HIT_CALL_SPECIAL); + if(spec_item < 24) { + short s1,s2,s3; + // TODO: This force_ptr...run_special code is almost duplicated in several places; maybe make a call_attack_spec subroutine? + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = univ.party[target].combat_pos.x; + PSD[SDF_SPEC_TARGLOC_Y] = univ.party[target].combat_pos.y; + PSD[SDF_SPEC_TARGET] = 11 + target; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKED_RANGE, 0, univ.party[target].items[spec_item].abil_data[0], univ.town.monst[m_num].cur_loc, &s1, &s2, &s3); + } + } else if(m_target->abil[eMonstAbil::HIT_TRIGGER].active) { + short s1,s2,s3; + univ.party.force_ptr(21, 301, 5); + univ.party.force_ptr(22, 301, 6); + univ.party.force_ptr(20, 301, 7); + PSD[SDF_SPEC_TARGLOC_X] = m_target->cur_loc.x; + PSD[SDF_SPEC_TARGLOC_Y] = m_target->cur_loc.y; + PSD[SDF_SPEC_TARGET] = target; // ready to be passed to SELECT_TARGET node + run_special(eSpecCtx::ATTACKED_RANGE, 0, m_target->abil[eMonstAbil::HIT_TRIGGER].special.extra1, univ.town.monst[m_num].cur_loc, &s1, &s2, &s3); + } } else if(abil.first == eMonstAbil::MISSILE_WEB) { if(target < 100) add_string_to_buf(" Throws web at " + univ.party[target].name + '.'); else monst_spell_note(m_target->number, 58); diff --git a/src/boe.consts.h b/src/boe.consts.h index e8877511..39727a9c 100644 --- a/src/boe.consts.h +++ b/src/boe.consts.h @@ -28,6 +28,9 @@ #define SDF_SPEC_TER 301][2 #define SDF_SPEC_STRBUF 301][3 #define SDF_SPEC_TRAPLVL 301][4 +#define SDF_SPEC_TARGLOC_X 301][5 +#define SDF_SPEC_TARGLOC_Y 301][6 +#define SDF_SPEC_TARGET 301][7 #define SDF_SKIP_STARTUP 305][4 // preferably deprecated #define SDF_LESS_SOUND 305][5 #define SDF_NO_TARGET_LINE 305][6 diff --git a/src/boe.monster.cpp b/src/boe.monster.cpp index d5ec8e0e..6830eefe 100644 --- a/src/boe.monster.cpp +++ b/src/boe.monster.cpp @@ -1375,9 +1375,14 @@ void activate_monsters(short code,short /*attitude*/) { short get_encumberance(short pc_num) { short store = 0,i,what_val; + what_val = univ.party[pc_num].free_weight(); + if(what_val < 0) store += what_val / -10; + for(i = 0; i < 24; i++) if(univ.party[pc_num].equip[i]) { what_val = univ.party[pc_num].items[i].awkward; + if(univ.party[pc_num].items[i].ability == eItemAbil::ENCUMBERING) + what_val += univ.party[pc_num].items[i].abil_data[0]; if(what_val == 1 && get_ran(1,0,130) < hit_chance[univ.party[pc_num].skill(eSkill::DEFENSE)]) what_val--; if(what_val > 1 && get_ran(1,0,70) < hit_chance[univ.party[pc_num].skill(eSkill::DEFENSE)]) diff --git a/src/boe.party.cpp b/src/boe.party.cpp index de811f9d..0e341012 100644 --- a/src/boe.party.cpp +++ b/src/boe.party.cpp @@ -2610,7 +2610,7 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_ // armor if(damage_type == eDamageType::WEAPON || damage_type == eDamageType::UNDEAD || damage_type == eDamageType::DEMON) { how_much -= minmax(-5,5,univ.party[which_pc].status[eStatus::BLESS_CURSE]); - for(i = 0; i < 24; i++) + for(i = 0; i < 24; i++) { if((univ.party[which_pc].items[i].variety != eItemType::NO_ITEM) && (univ.party[which_pc].equip[i])) { if(isArmourType(univ.party[which_pc].items[i].variety)) { r1 = get_ran(1,1,univ.party[which_pc].items[i].item_level); @@ -2638,6 +2638,15 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_ how_much += r1; } } + if(univ.party[which_pc].items[i].ability == eItemAbil::MELEE_PROTECTION) { + r1 = get_ran(1,1,univ.party[which_pc].items[i].abil_data[0]); + if(damage_type == eDamageType::DEMON) + how_much -= max(1,r1 / 5); + else if(damage_type == eDamageType::UNDEAD) + how_much -= max(1,r1 / 4); + else how_much -= max(1,r1 / 2); + } + } } // parry diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index 0ec6f2af..ccac7b86 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -1594,7 +1594,6 @@ void kill_monst(cCreature *which_m,short who_killed,eMainStatus type) { play_sound(29 + i); } else switch(which_m->m_type) { case eRace::GIANT: play_sound(29); break; - // TODO: Should sliths be considered reptiles too? Check original bladbase. // TODO: Should birds be considered beasts? If there are any birds in the bladbase, probably; otherwise, better to have new sound case eRace::REPTILE: case eRace::BEAST: case eRace::DEMON: case eRace::UNDEAD: case eRace::STONE: i = get_ran(1,0,1); play_sound(31 + i); break; @@ -1932,8 +1931,14 @@ void run_special(eSpecCtx which_mode,short which_type,short start_spec,location } break; case eSpecCtx::KILL_MONST: case eSpecCtx::SEE_MONST: case eSpecCtx::MONST_SPEC_ABIL: - // The monster is the target + case eSpecCtx::ATTACKED_MELEE: case eSpecCtx::ATTACKING_MELEE: + case eSpecCtx::ATTACKED_RANGE: case eSpecCtx::ATTACKING_RANGE: + // The monster/PC on the trigger space is the target current_pc_picked_in_spec_enc = 100 + monst_there(spec_loc); + if(current_pc_picked_in_spec_enc > univ.town->max_monst()) + current_pc_picked_in_spec_enc = pc_there(spec_loc); + if(current_pc_picked_in_spec_enc == 6) + current_pc_picked_in_spec_enc = -1; break; case eSpecCtx::TARGET: case eSpecCtx::USE_SPACE: // If there's a monster on the space, select that as the target @@ -3613,6 +3618,30 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, if(which_mode == eSpecCtx::SEE_MONST) *next_spec = spec.ex1c; break; + case 19: // Monster using special ability + if(which_mode == eSpecCtx::MONST_SPEC_ABIL) + *next_spec = spec.ex1c; + break; + case 20: // Town goes hostile + if(which_mode == eSpecCtx::TOWN_HOSTILE) + *next_spec = spec.ex1c; + break; + case 21: // Item ability activated on attacking + if(which_mode == eSpecCtx::ATTACKING_MELEE) + *next_spec = spec.ex1c; + break; + case 22: // Item ability activated on attacking + if(which_mode == eSpecCtx::ATTACKING_RANGE) + *next_spec = spec.ex1c; + break; + case 23: // Item or monster ability activated on being hit + if(which_mode == eSpecCtx::ATTACKED_MELEE) + *next_spec = spec.ex1c; + break; + case 24: // Item or monster ability activated on being hit + if(which_mode == eSpecCtx::ATTACKED_RANGE) + *next_spec = spec.ex1c; + break; // Past here are special values that don't have an equivalent in eSpecCtx. case 100: // Look (town or out) if(which_mode == eSpecCtx::OUT_LOOK || which_mode == eSpecCtx::TOWN_LOOK) diff --git a/src/classes/monster.cpp b/src/classes/monster.cpp index df6a9997..d1e8d5a4 100644 --- a/src/classes/monster.cpp +++ b/src/classes/monster.cpp @@ -295,9 +295,12 @@ std::map::iterator cMonster::addAbil(eMonstAbilTemplate wha case eMonstAbilTemplate::SPECIAL: abil[eMonstAbil::SPECIAL].special = {true, param, 1}; return abil.find(eMonstAbil::SPECIAL); + case eMonstAbilTemplate::HIT_TRIGGERS: + abil[eMonstAbil::HIT_TRIGGER].special = {true, param, 0}; + return abil.find(eMonstAbil::HIT_TRIGGER); case eMonstAbilTemplate::DEATH_TRIGGERS: abil[eMonstAbil::DEATH_TRIGGER].special = {true, param, 0}; - return abil.find(eMonstAbil::SPECIAL); + return abil.find(eMonstAbil::DEATH_TRIGGER); // Radiate abilities case eMonstAbilTemplate::RADIATE_FIRE: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FIRE, param}; diff --git a/src/classes/monster.h b/src/classes/monster.h index a94a9246..f55b3df4 100644 --- a/src/classes/monster.h +++ b/src/classes/monster.h @@ -38,7 +38,7 @@ enum class eMonstAbilTemplate { TOUCH_POISON, TOUCH_ACID, TOUCH_DISEASE, TOUCH_WEB, TOUCH_SLEEP, TOUCH_DUMB, TOUCH_PARALYSIS, TOUCH_PETRIFY, TOUCH_DEATH, TOUCH_XP_DRAIN, TOUCH_ICY, TOUCH_ICY_DRAINING, TOUCH_STUN, TOUCH_STEAL_FOOD, TOUCH_STEAL_GOLD, // Misc abilities - SPLITS, MARTYRS_SHIELD, ABSORB_SPELLS, SUMMON_5, SUMMON_20, SUMMON_50, SPECIAL, DEATH_TRIGGERS, + SPLITS, MARTYRS_SHIELD, ABSORB_SPELLS, SUMMON_5, SUMMON_20, SUMMON_50, SPECIAL, HIT_TRIGGERS, DEATH_TRIGGERS, // Radiate abilities RADIATE_FIRE, RADIATE_ICE, RADIATE_SHOCK, RADIATE_ANTIMAGIC, RADIATE_SLEEP, RADIATE_STINK, RADIATE_BLADE, RADIATE_WEB, // Advanced abilities diff --git a/src/classes/simpletypes.h b/src/classes/simpletypes.h index 80152857..13cdc550 100644 --- a/src/classes/simpletypes.h +++ b/src/classes/simpletypes.h @@ -162,6 +162,7 @@ enum class eMonstAbil { MISSILE_WEB, RAY_HEAT, SPECIAL, + HIT_TRIGGER, DEATH_TRIGGER, RADIATE, @@ -323,14 +324,21 @@ enum class eItemAbil { HEALING_WEAPON = 3, EXPLODING_WEAPON = 4, RETURNING_MISSILE = 5, + DISTANCE_MISSILE = 6, + SEEKING_MISSILE = 7, STATUS_WEAPON = 9, SOULSUCKER = 10, DRAIN_MISSILES = 11, WEAK_WEAPON = 12, CAUSES_FEAR = 13, + WEAPON_CALL_SPECIAL = 14, // General abilities DAMAGE_PROTECTION = 30, FULL_PROTECTION = 31, + MELEE_PROTECTION = 32, + EVASION = 33, + MARTYRS_SHIELD = 34, + ENCUMBERING = 35, STATUS_PROTECTION = 36, SKILL = 37, BOOST_STAT = 38, @@ -340,6 +348,7 @@ enum class eItemAbil { LIGHTER_OBJECT = 44, HEAVIER_OBJECT = 45, OCCASIONAL_STATUS = 46, + HIT_CALL_SPECIAL = 47, LIFE_SAVING = 48, PROTECT_FROM_PETRIFY = 49, REGENERATE = 50, @@ -487,6 +496,10 @@ enum class eSpecCtx { SEE_MONST = 18, MONST_SPEC_ABIL = 19, TOWN_HOSTILE = 20, + ATTACKING_MELEE = 21, + ATTACKING_RANGE = 22, + ATTACKED_MELEE = 23, + ATTACKED_RANGE = 24, }; enum class eSpecType { diff --git a/src/scenedit/scen.core.cpp b/src/scenedit/scen.core.cpp index bcd7eeaa..283de6ff 100644 --- a/src/scenedit/scen.core.cpp +++ b/src/scenedit/scen.core.cpp @@ -898,6 +898,7 @@ static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst param = choose_text(STRT_MONST, 0, &me, "Summon which monster?"); break; case eMonstAbilTemplate::SPECIAL: + case eMonstAbilTemplate::HIT_TRIGGERS: case eMonstAbilTemplate::DEATH_TRIGGERS: param = get_fresh_spec(0); if(param < 0) { @@ -1052,7 +1053,7 @@ static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst return true; }); } else if(cat == eMonstAbilCat::SPECIAL) { - if(abil == eMonstAbil::SPECIAL || abil == eMonstAbil::DEATH_TRIGGER) + if(abil == eMonstAbil::SPECIAL || abil == eMonstAbil::HIT_TRIGGER || abil == eMonstAbil::DEATH_TRIGGER) abil_dlg["pick-extra1"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool { short spec = me["extra1"].getTextAsNum(); if(spec < 0 || spec > 255) {