Several new item abilities and one new monster ability

- Range augment ability for missiles
- Seeking ability for missiles (can strike an adjacent space if targeted space lacks a target, including chance of hitting invisible monsters)
- Weapon that calls node when attacking with it (works both in range and melee; the cases can be distinguished by the node with IF_CONTEXT)
- Armour that calls node when attacked while wearing (works both in range and melee; the cases can be distinguished by the node with IF_CONTEXT)
- Monster ability that calls node when monster attacked (works both in range and melee; the cases can be distinguished by the node with IF_CONTEXT)
- The above three only apply to non-magical attacks - some things don't trigger it, like spells, monster rays and breath weapons, possible a few others
- Armour that protects from all melee damage (including demon/undead, though less)
- Armour that decreases chance to be hit (both in range and melee, against non-magical attacks only)
- Armour that behaves like to the martyr's shield effect but also has a chance of adding some extra bonus damage
- Armour that's more encumbering than advertised (best for cursed items with concealed ability)
- Multiple items with the accuracy ability now stack
- Fix nephilim not getting their racial bonus to missile weapons
- In specials called as part of an attack, the reserved pointer -20 contains the target in a form ready to pass to a SELECT_TARGET node, while -21 and -22 contain the location of the target. This includes the new cases added in this commit plus the monster ability to call a special node on its turn. It does not include specials called in the spell targeting context (to do so would break legacy scenarios), nor specials called after a targeting node.
- If a PC is carrying more than their max weight, they gain encumbrance equal to one-tenth of the excess.
- Add some missing context cases to IF_CONTEXT node
This commit is contained in:
2015-01-20 21:10:21 -05:00
parent 7837459177
commit 74ed88f2f3
10 changed files with 216 additions and 22 deletions

View File

@@ -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<int> 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<eMonstAbil,uAbility> 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<eMonstAbil,uAbility> 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);

View File

@@ -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

View File

@@ -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)])

View File

@@ -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

View File

@@ -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)

View File

@@ -295,9 +295,12 @@ std::map<eMonstAbil,uAbility>::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};

View File

@@ -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

View File

@@ -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 {

View File

@@ -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) {