Expand three special node types
Can't Enter node renamed to Prevent Action, as it's a more accurate description of what it does. It has been expanded to cover the following cases, all of which are documented: - When the special node was called during an attack action (which involves a weapon whose ability is to call a special node when attacking, or an item or monster ability that calls a special node when hit), then Prevent Action reverses the action point cost. - When called as the result of a purchase (a shop item that calls a special), then Prevent Action prevents gold from being deducted (which is also new in this commit, as before it never deducted gold) - When called as the result of using a normal item (not a special item), then Prevent Action prevents a charge from being deducted - When called as the result of a monster using its ability, then Prevent Action prevents the action points from being deducted - Cases it already covered (cancelling initiation of talk mode, searching of containers, outdoor wandering encounters) have been documented - The fact that it will break things during talk mode is also documented now Start Spell Targeting node has been tweaked and gained some new options: - You can allow the player to target opaque or antimagic spaces. In town mode, you can prohibit them from targeting antimagic spaces. - You can specify a special node to be called if targeting fails because they selected an invalid space, or because an special node keyed to spellcasting context cancelled it - You no longer get a "Hit 'p' to cancel" message. Even better, hitting 'p' does not cancel it. (Well, more precisely, it triggers the failure node, with the party's or pc's location as the target space.) Misc: - Fix crash outdoors due to trying to check for force barriers
This commit is contained in:
@@ -278,6 +278,7 @@ bool prime_time() {
|
||||
|
||||
static void handle_spellcast(eSkill which_type, bool& did_something, bool& need_redraw, bool& need_reprint) {
|
||||
short store_sp[6];
|
||||
extern short spec_target_type, spec_target_fail;
|
||||
if(!someone_awake()) {
|
||||
ASB("Everyone's asleep/paralyzed.");
|
||||
need_reprint = true;
|
||||
@@ -302,6 +303,9 @@ static void handle_spellcast(eSkill which_type, bool& did_something, bool& need_
|
||||
overall_mode = MODE_TOWN;
|
||||
need_redraw = true;
|
||||
need_reprint = true;
|
||||
extern eSpell town_spell;
|
||||
if(town_spell == eSpell::NONE)
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, univ.town.p_loc);
|
||||
} else if(overall_mode == MODE_COMBAT) {
|
||||
if(which_type == eSkill::MAGE_SPELLS) {
|
||||
did_something = combat_cast_mage_spell();
|
||||
@@ -319,6 +323,9 @@ static void handle_spellcast(eSkill which_type, bool& did_something, bool& need_
|
||||
center = univ.party[current_pc].combat_pos;
|
||||
pause(10);
|
||||
need_redraw = true;
|
||||
extern eSpell spell_being_cast;
|
||||
if(spell_being_cast == eSpell::NONE)
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, univ.party[current_pc].combat_pos);
|
||||
}
|
||||
put_pc_screen();
|
||||
put_item_screen(stat_window,0);
|
||||
|
@@ -43,10 +43,11 @@ extern bool in_scen_debug;
|
||||
extern short fast_bang;
|
||||
extern short store_current_pc;
|
||||
extern short combat_posing_monster , current_working_monster ; // 0-5 PC 100 + x - monster x
|
||||
extern short spell_caster, missile_firer,current_monst_tactic;
|
||||
extern short missile_firer,current_monst_tactic;
|
||||
eSpell spell_being_cast;
|
||||
bool spell_freebie;
|
||||
short missile_inv_slot, ammo_inv_slot;
|
||||
short spell_caster, spec_target_type, spec_target_fail, spec_target_options;
|
||||
short force_wall_position = 10; // 10 -> no force wall
|
||||
bool processing_fields = true;
|
||||
short futzing;
|
||||
@@ -860,6 +861,8 @@ void pc_attack_weapon(short who_att,iLiving& target,short hit_adj,short dam_adj,
|
||||
univ.party.force_ptr(22, target.get_loc().y);
|
||||
univ.party.force_ptr(20, i_monst);
|
||||
run_special(eSpecCtx::ATTACKING_MELEE, 0, weap.abil_data[0],univ.party[who_att].combat_pos, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
univ.party[who_att].ap += 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -946,6 +949,11 @@ short calc_spec_dam(eItemAbil abil,short abil_str,short abil_dat,iLiving& monst,
|
||||
|
||||
void place_target(location target) {
|
||||
short i;
|
||||
bool allow_obstructed = false, allow_antimagic = false;
|
||||
if(spell_being_cast == eSpell::DISPEL_BARRIER || (spell_being_cast == eSpell::NONE && spec_target_options % 10 == 1))
|
||||
allow_obstructed = true;
|
||||
if(spell_being_cast == eSpell::NONE && spec_target_options / 10 == 2)
|
||||
allow_antimagic = true;
|
||||
|
||||
if(num_targets_left > 0) {
|
||||
if(loc_off_act_area(target)) {
|
||||
@@ -960,11 +968,11 @@ void place_target(location target) {
|
||||
add_string_to_buf(" Target out of range.");
|
||||
return;
|
||||
}
|
||||
if(sight_obscurity(target.x,target.y) == 5 && spell_being_cast != eSpell::DISPEL_BARRIER) {
|
||||
if(!allow_obstructed && sight_obscurity(target.x,target.y) == 5) {
|
||||
add_string_to_buf(" Target space obstructed.");
|
||||
return;
|
||||
}
|
||||
if(univ.town.is_antimagic(target.x,target.y)) {
|
||||
if(univ.town.is_antimagic(target.x,target.y) && !allow_antimagic) {
|
||||
add_string_to_buf(" Target in antimagic field.");
|
||||
return;
|
||||
}
|
||||
@@ -1010,6 +1018,11 @@ void do_combat_cast(location target) {
|
||||
mon_num_t summon;
|
||||
iLiving* victim;
|
||||
cPlayer& caster = univ.party[current_pc];
|
||||
bool allow_obstructed = false, allow_antimagic = false;
|
||||
if(spell_being_cast == eSpell::DISPEL_BARRIER || (spell_being_cast == eSpell::NONE && spec_target_options % 10 == 1))
|
||||
allow_obstructed = true;
|
||||
if(spell_being_cast == eSpell::NONE && spec_target_options / 10 == 2)
|
||||
allow_antimagic = false;
|
||||
|
||||
location ashes_loc;
|
||||
|
||||
@@ -1071,8 +1084,12 @@ void do_combat_cast(location target) {
|
||||
adjust = can_see_light(caster.combat_pos, target, sight_obscurity);
|
||||
// TODO: Should we do this here? Or in the handling of targeting modes?
|
||||
// (It really depends whether we want to be able to trigger it for targeting something other than a spell.)
|
||||
if(adjust <= 4 && !cast_spell_on_space(target, spell_being_cast))
|
||||
if(adjust <= 4 && !cast_spell_on_space(target, spell_being_cast)) {
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, target);
|
||||
continue; // The special node intercepted and cancelled regular spell behaviour.
|
||||
}
|
||||
|
||||
bool failed = spell_being_cast == eSpell::NONE;
|
||||
|
||||
if(adjust > 4) {
|
||||
add_string_to_buf(" Can't see target.");
|
||||
@@ -1080,13 +1097,14 @@ void do_combat_cast(location target) {
|
||||
else if(loc_off_act_area(target)) {
|
||||
add_string_to_buf(" Space not in town.");
|
||||
}
|
||||
else if(dist(caster.combat_pos,target) > (*spell_being_cast).range)
|
||||
else if(dist(caster.combat_pos,target) > current_spell_range)
|
||||
add_string_to_buf(" Target out of range.");
|
||||
else if(sight_obscurity(target.x,target.y) == 5 && spell_being_cast != eSpell::DISPEL_BARRIER)
|
||||
else if(sight_obscurity(target.x,target.y) == 5 && !allow_obstructed)
|
||||
add_string_to_buf(" Target space obstructed.");
|
||||
else if(univ.town.is_antimagic(target.x,target.y))
|
||||
else if(univ.town.is_antimagic(target.x,target.y) && !allow_antimagic)
|
||||
add_string_to_buf(" Target in antimagic field.");
|
||||
else {
|
||||
failed = false;
|
||||
if(!ap_taken) {
|
||||
if(!freebie)
|
||||
take_ap(5);
|
||||
@@ -1096,7 +1114,9 @@ void do_combat_cast(location target) {
|
||||
boom_targ[i] = target;
|
||||
switch(spell_being_cast) {
|
||||
case eSpell::NONE: // Not a spell but a special node targeting
|
||||
run_special(eSpecCtx::TARGET, spell_caster / 1000, spell_caster % 1000, target, &r1, &r2, &item);
|
||||
r1 = item = 0;
|
||||
run_special(eSpecCtx::TARGET, spec_target_type, spell_caster, target, &r1, &r2, &item);
|
||||
failed = r1;
|
||||
if(item > 0) redraw_screen(REFRESH_ALL);
|
||||
break;
|
||||
case eSpell::GOO: case eSpell::WEB: case eSpell::GOO_BOMB:
|
||||
@@ -1552,6 +1572,8 @@ void do_combat_cast(location target) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(failed)
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, target);
|
||||
}
|
||||
|
||||
do_missile_anim((num_targets > 1) ? 35 : 60,caster.combat_pos,store_sound);
|
||||
@@ -1840,6 +1862,8 @@ void fire_missile(location target) {
|
||||
univ.party.force_ptr(22, victim->get_loc().y);
|
||||
univ.party.force_ptr(20, univ.get_target_i(*victim));
|
||||
run_special(eSpecCtx::ATTACKING_RANGE, 0, missile.abil_data[0], missile_firer.combat_pos, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
missile_firer.ap += (overall_mode == MODE_FIRING) ? 3 : 2;
|
||||
}
|
||||
cCreature* monst; cPlayer* pc; int spec_item;
|
||||
if((monst = dynamic_cast<cCreature*>(victim)) && monst->abil[eMonstAbil::HIT_TRIGGER].active) {
|
||||
@@ -1848,12 +1872,16 @@ void fire_missile(location target) {
|
||||
univ.party.force_ptr(22, monst->cur_loc.y);
|
||||
univ.party.force_ptr(20, univ.get_target_i(*monst));
|
||||
run_special(eSpecCtx::ATTACKED_RANGE, 0, monst->abil[eMonstAbil::HIT_TRIGGER].special.extra1, missile_firer.combat_pos, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
missile_firer.ap += (overall_mode == MODE_FIRING) ? 3 : 2;
|
||||
} else if((pc = dynamic_cast<cPlayer*>(victim)) && (spec_item = pc->has_abil_equip(eItemAbil::HIT_CALL_SPECIAL)) < 24) {
|
||||
short s1,s2,s3;
|
||||
univ.party.force_ptr(21, pc->combat_pos.x);
|
||||
univ.party.force_ptr(22, pc->combat_pos.y);
|
||||
univ.party.force_ptr(20, univ.get_target_i(*pc));
|
||||
run_special(eSpecCtx::ATTACKED_RANGE, 0, pc->items[spec_item].abil_data[0], missile_firer.combat_pos, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
missile_firer.ap += (overall_mode == MODE_FIRING) ? 3 : 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2464,16 +2492,7 @@ void do_monster_turn() {
|
||||
print_monst_name(cur_monst->number);
|
||||
monst_fire_missile(i/*,cur_monst->skill*/,cur_monst->status[eStatus::BLESS_CURSE],
|
||||
pick_abil,cur_monst->cur_loc,&univ.get_target(target));
|
||||
if(pick_abil.first == eMonstAbil::MISSILE) {
|
||||
if(pick_abil.second.missile.type == eMonstMissile::ARROW || pick_abil.second.missile.type == eMonstMissile::BOLT
|
||||
|| pick_abil.second.missile.type == eMonstMissile::SPINE || pick_abil.second.missile.type == eMonstMissile::BOULDER)
|
||||
take_m_ap(3,cur_monst);
|
||||
else take_m_ap(2,cur_monst);
|
||||
} else if(pick_abil.first == eMonstAbil::RAY_HEAT)
|
||||
take_m_ap(1,cur_monst);
|
||||
else if(pick_abil.first == eMonstAbil::DAMAGE2)
|
||||
take_m_ap(4,cur_monst);
|
||||
else take_m_ap(3,cur_monst);
|
||||
take_m_ap(pick_abil.second.get_ap_cost(pick_abil.first), cur_monst);
|
||||
had_monst = true;
|
||||
acted_yet = true;
|
||||
}
|
||||
@@ -2489,7 +2508,8 @@ void do_monster_turn() {
|
||||
univ.party.force_ptr(20, 11 + target); // ready to be passed to SELECT_TARGET node
|
||||
else univ.party.force_ptr(20, 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);
|
||||
if(s1 <= 0)
|
||||
take_m_ap(abil.special.extra2,cur_monst);
|
||||
}
|
||||
} // Special attacks
|
||||
|
||||
@@ -2943,12 +2963,16 @@ void monster_attack(short who_att,iLiving* target) {
|
||||
univ.party.force_ptr(22, target->get_loc().y);
|
||||
univ.party.force_ptr(20, i_monst);
|
||||
run_special(eSpecCtx::ATTACKED_MELEE, 0, pc_target->items[spec_item].abil_data[0], attacker->cur_loc, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
attacker->ap += 4;
|
||||
} else if(m_target != nullptr && m_target->abil[eMonstAbil::HIT_TRIGGER].active) {
|
||||
short s1,s2,s3;
|
||||
univ.party.force_ptr(21, target->get_loc().x);
|
||||
univ.party.force_ptr(22, target->get_loc().y);
|
||||
univ.party.force_ptr(20, i_monst);
|
||||
run_special(eSpecCtx::ATTACKED_MELEE, 0, m_target->abil[eMonstAbil::HIT_TRIGGER].special.extra1, attacker->cur_loc, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
attacker->ap += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3081,6 +3105,8 @@ void monst_fire_missile(short m_num,short bless,std::pair<eMonstAbil,uAbility> a
|
||||
univ.party.force_ptr(22, target->get_loc().y);
|
||||
univ.party.force_ptr(20, i_monst);
|
||||
run_special(eSpecCtx::ATTACKED_RANGE, 0, pc_target->items[spec_item].abil_data[0], univ.town.monst[m_num].cur_loc, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
univ.town.monst[m_num].ap += abil.second.get_ap_cost(abil.first);
|
||||
}
|
||||
} else if(m_target != nullptr && m_target->abil[eMonstAbil::HIT_TRIGGER].active) {
|
||||
short s1,s2,s3;
|
||||
@@ -3088,6 +3114,8 @@ void monst_fire_missile(short m_num,short bless,std::pair<eMonstAbil,uAbility> a
|
||||
univ.party.force_ptr(22, m_target->cur_loc.y);
|
||||
univ.party.force_ptr(20, i_monst);
|
||||
run_special(eSpecCtx::ATTACKED_RANGE, 0, m_target->abil[eMonstAbil::HIT_TRIGGER].special.extra1, univ.town.monst[m_num].cur_loc, &s1, &s2, &s3);
|
||||
if(s1 > 0)
|
||||
univ.town.monst[m_num].ap += abil.second.get_ap_cost(abil.first);
|
||||
}
|
||||
} else if(abil.first == eMonstAbil::MISSILE_WEB) {
|
||||
if(pc_target != nullptr)
|
||||
@@ -5100,7 +5128,8 @@ void start_spell_targeting(eSpell num, bool freebie, int spell_range, eSpellPat
|
||||
spell_freebie = freebie;
|
||||
|
||||
add_string_to_buf(" Target spell.");
|
||||
if(isMage(num))
|
||||
if(num == eSpell::NONE);
|
||||
else if(isMage(num))
|
||||
add_string_to_buf(" (Hit 'm' to cancel.)");
|
||||
else add_string_to_buf(" (Hit 'p' to cancel.)");
|
||||
overall_mode = MODE_SPELL_TARGET;
|
||||
@@ -5163,7 +5192,8 @@ void start_fancy_spell_targeting(eSpell num, bool freebie, int spell_range, eSpe
|
||||
for(i = 0; i < 8; i++)
|
||||
spell_targets[i] = null_loc;
|
||||
add_string_to_buf(" Target spell.");
|
||||
if(isMage(num))
|
||||
if(num == eSpell::NONE);
|
||||
else if(isMage(num))
|
||||
add_string_to_buf(" (Hit 'm' to cancel.)");
|
||||
else add_string_to_buf(" (Hit 'p' to cancel.)");
|
||||
add_string_to_buf(" (Hit space to cast.)");
|
||||
|
@@ -248,7 +248,6 @@ void handle_shop_event(location p) {
|
||||
}
|
||||
|
||||
void handle_sale(cShopItem item, int i) {
|
||||
short s1, s2, s3; // Dummy variables to pass to run_special
|
||||
cItem base_item = item.item;
|
||||
short cost = item.getCost(active_shop.getCostAdjust());
|
||||
rectangle dummy_rect = {0,0,0,0};
|
||||
@@ -368,7 +367,13 @@ void handle_sale(cShopItem item, int i) {
|
||||
}
|
||||
break;
|
||||
case eShopItemType::CALL_SPECIAL:
|
||||
run_special(eSpecCtx::SHOPPING, 0, base_item.item_level, {0,0}, &s1, &s2, &s3);
|
||||
if(univ.party.gold < cost)
|
||||
ASB("Not enough gold.");
|
||||
else {
|
||||
short s1, s2, s3;
|
||||
run_special(eSpecCtx::SHOPPING, 0, base_item.item_level, {0,0}, &s1, &s2, &s3);
|
||||
if(s1 <= 0) take_gold(cost,false);
|
||||
}
|
||||
break;
|
||||
case eShopItemType::SKILL:
|
||||
if(base_item.item_level < 0 || base_item.item_level > 18) {
|
||||
|
@@ -98,7 +98,7 @@ location center;
|
||||
short current_pc;
|
||||
short combat_active_pc;
|
||||
effect_pat_type current_pat;
|
||||
short spell_caster, missile_firer,current_monst_tactic;
|
||||
short missile_firer,current_monst_tactic;
|
||||
short store_current_pc = 0;
|
||||
|
||||
sf::Clock animTimer;
|
||||
|
@@ -50,6 +50,7 @@ short combat_percent[20] = {
|
||||
short who_cast,which_pc_displayed;
|
||||
eSpell town_spell;
|
||||
extern bool spell_freebie;
|
||||
extern short spec_target_type, spec_target_fail, spec_target_options;
|
||||
bool spell_button_active[90];
|
||||
|
||||
extern short fast_bang;
|
||||
@@ -1304,6 +1305,8 @@ void cast_town_spell(location where) {
|
||||
(where.y <= univ.town->in_town_rect.top) ||
|
||||
(where.y >= univ.town->in_town_rect.bottom)) {
|
||||
add_string_to_buf(" Can't target outside town.");
|
||||
if(town_spell == eSpell::NONE)
|
||||
run_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, where, &r1, &store, &adjust);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1320,14 +1323,25 @@ void cast_town_spell(location where) {
|
||||
|
||||
// TODO: Should we do this here? Or in the handling of targeting modes?
|
||||
// (It really depends whether we want to be able to trigger it for targeting something other than a spell.)
|
||||
if(adjust <= 4 && !cast_spell_on_space(where, town_spell))
|
||||
return; // The special node intercepted and cancelled regular spell behaviour.
|
||||
if(adjust <= 4 && !cast_spell_on_space(where, town_spell)) {
|
||||
// The special node intercepted and cancelled regular spell behaviour.
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, where);
|
||||
return;
|
||||
}
|
||||
if(spec_target_options / 10 == 1 && univ.town.is_antimagic(where.x,where.y)) {
|
||||
add_string_to_buf(" Target in antimagic field.");
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, where);
|
||||
return;
|
||||
}
|
||||
|
||||
bool failed = town_spell == eSpell::NONE && adjust > 4;
|
||||
|
||||
if(adjust > 4)
|
||||
add_string_to_buf(" Can't see target.");
|
||||
else switch(town_spell) {
|
||||
case eSpell::NONE: // Not a spell but a special node targeting
|
||||
run_special(eSpecCtx::TARGET, spell_caster / 1000, spell_caster % 1000, where, &r1, &adjust, &store);
|
||||
r1 = store = 0;
|
||||
run_special(eSpecCtx::TARGET, spec_target_type, spell_caster, where, &r1, &adjust, &store);
|
||||
if(store > 0) redraw_screen(REFRESH_ALL);
|
||||
break;
|
||||
case eSpell::SCRY_MONSTER: case eSpell::CAPTURE_SOUL:
|
||||
@@ -1446,6 +1460,8 @@ void cast_town_spell(location where) {
|
||||
break;
|
||||
|
||||
}
|
||||
if(failed)
|
||||
queue_special(eSpecCtx::TARGET, spec_target_type, spec_target_fail, where);
|
||||
}
|
||||
|
||||
// TODO: Currently, the node is called before any spell-specific behaviour (eg missiles) occurs.
|
||||
|
@@ -41,7 +41,7 @@ extern effect_pat_type single,t,square,radius2,radius3,small_square,open_square,
|
||||
extern effect_pat_type current_pat;
|
||||
extern cOutdoors::cWandering store_wandering_special;
|
||||
extern eSpell spell_being_cast, town_spell;
|
||||
extern short spell_caster;
|
||||
extern short spell_caster, spec_target_fail, spec_target_type, spec_target_options;
|
||||
extern sf::RenderWindow mini_map;
|
||||
extern short fast_bang;
|
||||
extern bool end_scenario;
|
||||
@@ -192,11 +192,11 @@ bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,
|
||||
return false; // TODO: Maybe replace eTrimType::CITY check with a blockage == clear/special && is_special() check?
|
||||
}
|
||||
|
||||
if(univ.town.is_force_barr(where_check.x,where_check.y)) {
|
||||
if(mode != eSpecCtx::OUT_MOVE && univ.town.is_force_barr(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Magic barrier!");
|
||||
can_enter = false;
|
||||
}
|
||||
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
||||
if(mode != eSpecCtx::OUT_MOVE && univ.town.is_force_cage(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Force cage!");
|
||||
can_enter = false;
|
||||
}
|
||||
@@ -1076,6 +1076,8 @@ void use_item(short pc,short item) {
|
||||
case eItemAbil::CALL_SPECIAL:
|
||||
// TODO: Should this have its own separate eSpecCtx?
|
||||
run_special(eSpecCtx::USE_SPEC_ITEM,0,str,user_loc,&sp[0],&sp[1],&sp[2]);
|
||||
if(sp[0] > 0)
|
||||
take_charge = false;
|
||||
break;
|
||||
|
||||
case eItemAbil::CAST_SPELL:
|
||||
@@ -4159,7 +4161,16 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
else if(spec.ex1c > 1)
|
||||
start_fancy_spell_targeting(eSpell::NONE, true, spec.ex1b, eSpellPat(spec.ex1a), spec.ex1c);
|
||||
else start_spell_targeting(eSpell::NONE, true, spec.ex1b, eSpellPat(spec.ex1a));
|
||||
spell_caster = spec.jumpto + cur_spec_type * 1000;
|
||||
spell_caster = spec.jumpto;
|
||||
spec_target_type = cur_spec_type;
|
||||
spec_target_fail = spec.ex2a;
|
||||
spec_target_options = 0;
|
||||
if(spec.ex2b > 0)
|
||||
spec_target_options += 1;
|
||||
if(spec.ex2c > 0)
|
||||
spec_target_options += 20;
|
||||
else if(spec.ex2c == 0)
|
||||
spec_target_options += 10;
|
||||
break;
|
||||
case eSpecType::TOWN_SPELL_PAT_FIELD:
|
||||
if(spec.ex1c < -1 || spec.ex1c > 14) {
|
||||
|
@@ -989,6 +989,41 @@ std::string uAbility::to_string(eMonstAbil key) const {
|
||||
return sout.str();
|
||||
}
|
||||
|
||||
// Returns the action point cost, or one of the following magic values:
|
||||
// 0 - passive ability
|
||||
// -1 - part of normal attack
|
||||
// -256 - unknown (error)
|
||||
int uAbility::get_ap_cost(eMonstAbil key) const {
|
||||
switch(key) {
|
||||
case eMonstAbil::MISSILE:
|
||||
switch(missile.type) {
|
||||
case eMonstMissile::ARROW: case eMonstMissile::BOLT: case eMonstMissile::SPINE: case eMonstMissile::BOULDER:
|
||||
return 3;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
case eMonstAbil::RAY_HEAT:
|
||||
return 1;
|
||||
case eMonstAbil::DAMAGE2:
|
||||
return 4;
|
||||
case eMonstAbil::DAMAGE: case eMonstAbil::STATUS: case eMonstAbil::STATUS2: case eMonstAbil::STUN:
|
||||
case eMonstAbil::FIELD: case eMonstAbil::PETRIFY: case eMonstAbil::DRAIN_SP: case eMonstAbil::DRAIN_XP:
|
||||
case eMonstAbil::KILL: case eMonstAbil::STEAL_FOOD: case eMonstAbil::STEAL_GOLD:
|
||||
return gen.type == eMonstGen::TOUCH ? -1 : 3;
|
||||
case eMonstAbil::MISSILE_WEB:
|
||||
return 3;
|
||||
case eMonstAbil::SPECIAL:
|
||||
return special.extra2;
|
||||
case eMonstAbil::ABSORB_SPELLS: case eMonstAbil::DEATH_TRIGGER: case eMonstAbil::HIT_TRIGGER:
|
||||
case eMonstAbil::MARTYRS_SHIELD: case eMonstAbil::RADIATE: case eMonstAbil::SPLITS:
|
||||
case eMonstAbil::SUMMON:
|
||||
return 0;
|
||||
case eMonstAbil::NO_ABIL:
|
||||
return -256;
|
||||
}
|
||||
// return -256;
|
||||
}
|
||||
|
||||
void cMonster::writeTo(std::ostream& file) const {
|
||||
file << "MONSTER " << maybe_quote_string(m_name) << '\n';
|
||||
file << "LEVEL " << int(level) << '\n';
|
||||
|
@@ -98,6 +98,7 @@ union uAbility {
|
||||
int extra1, extra2, extra3;
|
||||
} special;
|
||||
std::string to_string(eMonstAbil myKey) const;
|
||||
int get_ap_cost(eMonstAbil key) const;
|
||||
};
|
||||
|
||||
class cMonster {
|
||||
|
@@ -983,24 +983,14 @@ static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil
|
||||
// These names start at line 80 in the strings file, but the first valid ability is ID 1, so add 79.
|
||||
me["type"].setText(get_str("monster-abilities", 79 + int(abil)));
|
||||
// Action points
|
||||
if(cat == eMonstAbilCat::MISSILE) {
|
||||
if(detail.missile.type == eMonstMissile::ARROW || detail.missile.type == eMonstMissile::BOLT || detail.missile.type == eMonstMissile::SPINE || detail.missile.type == eMonstMissile::BOULDER)
|
||||
me["ap"].setTextToNum(3);
|
||||
else me["ap"].setTextToNum(2);
|
||||
} else if(cat == eMonstAbilCat::GENERAL) {
|
||||
if(detail.gen.type == eMonstGen::TOUCH)
|
||||
if(abil != eMonstAbil::RADIATE && abil != eMonstAbil::RADIATE) {
|
||||
int ap = detail.get_ap_cost(abil);
|
||||
if(ap == 0)
|
||||
me["ap"].setText("0 (passive ability)");
|
||||
else if(ap == -1)
|
||||
me["ap"].setText("0 (part of standard attack)");
|
||||
else if(abil == eMonstAbil::DAMAGE2)
|
||||
me["ap"].setTextToNum(4);
|
||||
else me["ap"].setTextToNum(3);
|
||||
} else if(abil == eMonstAbil::RAY_HEAT)
|
||||
me["ap"].setTextToNum(1);
|
||||
else if(abil == eMonstAbil::MISSILE_WEB)
|
||||
me["ap"].setTextToNum(3);
|
||||
else if(abil == eMonstAbil::SPECIAL)
|
||||
me["ap"].setTextToNum(detail.special.extra2);
|
||||
else if(cat == eMonstAbilCat::SPECIAL)
|
||||
me["ap"].setText("0 (passive ability)");
|
||||
else me["ap"].setTextToNum(ap);
|
||||
}
|
||||
// Subtype
|
||||
if(cat == eMonstAbilCat::MISSILE)
|
||||
me["subtype"].setText(get_str("monster-abilities", 110 + int(detail.missile.type)));
|
||||
|
Reference in New Issue
Block a user