Skip to content

Commit 97c9145

Browse files
Opportunity attacks
Instead of triggering attacks when monsters move next to the player, fire em when the player moves away from monsters. No energy cost for this one, except for stuff like juggernauts and ogres. Intended to reduce impact on combat generally.
1 parent 8cb1a8b commit 97c9145

File tree

3 files changed

+53
-36
lines changed

3 files changed

+53
-36
lines changed

crawl-ref/source/mon-act.cc

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3129,37 +3129,12 @@ static bool _monster_swaps_places(monster* mon, const coord_def& delta)
31293129
return false;
31303130
}
31313131

3132-
static void _mons_closing_attack(monster& mons, coord_def delta)
3132+
void mon_maybe_attack_you(monster& mons)
31333133
{
3134-
// XXX: should check reach instead of a fixed distance 1?
3135-
if (mons.confused() || mons_is_batty(mons) || mons.foe_distance() > 1)
3134+
monster *ru_target = nullptr;
3135+
if (_handle_ru_melee_redirection(mons, &ru_target))
31363136
return;
3137-
if (!one_chance_in(6))
3138-
return;
3139-
actor* foe = mons.get_foe();
3140-
ASSERT(foe); // else, how is foe distance <= 0?
3141-
if (!mons.can_see(*foe))
3142-
return;
3143-
coord_def old_pos = mons.pos() - delta;
3144-
if (adjacent(old_pos, foe->pos()))
3145-
return;
3146-
3147-
3148-
const string name = foe->name(DESC_THE);
3149-
const string msg = make_stringf(" attacks %s as %s move%s adjacent!",
3150-
name.c_str(),
3151-
mons.pronoun(PRONOUN_SUBJECTIVE).c_str(),
3152-
mons.pronoun_plurality() ? "" : "s");
3153-
simple_monster_message(mons, msg.c_str());
3154-
3155-
if (foe->is_player())
3156-
{
3157-
monster *ru_target = nullptr;
3158-
if (_handle_ru_melee_redirection(mons, &ru_target))
3159-
return;
3160-
_melee_attack_player(mons, ru_target);
3161-
} else
3162-
fight_melee(&mons, foe);
3137+
_melee_attack_player(mons, ru_target);
31633138
}
31643139

31653140
static bool _do_move_monster(monster& mons, const coord_def& delta)
@@ -3288,7 +3263,6 @@ static bool _do_move_monster(monster& mons, const coord_def& delta)
32883263
_handle_manticore_barbs(mons);
32893264

32903265
_swim_or_move_energy(mons);
3291-
_mons_closing_attack(mons, delta);
32923266

32933267
return true;
32943268
}

crawl-ref/source/mon-act.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ void handle_monster_move(monster* mon);
3636

3737
void queue_monster_for_action(monster* mons);
3838

39+
void mon_maybe_attack_you(monster &mon);
40+
3941
#define ENERGY_SUBMERGE(entry) (max(entry->energy_usage.swim / 2, 1))

crawl-ref/source/movement.cc

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,45 @@ static void _clear_constriction_data()
229229
you.stop_being_constricted();
230230
}
231231

232+
static void _trigger_opportunity_attacks(coord_def new_pos)
233+
{
234+
for (adjacent_iterator ai(you.pos()); ai; ++ai)
235+
{
236+
monster* mon = monster_at(*ai);
237+
// No, there is no logic to this ordering (pf):
238+
if (!mon
239+
|| mon->wont_attack()
240+
|| !mons_has_attacks(*mon)
241+
|| adjacent(new_pos, mon->pos())
242+
|| mon->confused()
243+
|| !mon->can_see(you)
244+
|| !one_chance_in(5))
245+
{
246+
continue;
247+
}
248+
actor* foe = mon->get_foe();
249+
if (!foe || !foe->is_player())
250+
continue;
251+
252+
simple_monster_message(*mon, " attacks as you move away!");
253+
const int old_energy = mon->speed_increment;
254+
mon_maybe_attack_you(*mon);
255+
// Refund up to 100 energy (1 turn) from the attack.
256+
// Thus, only slow attacking monsters use energy for these.
257+
mon->speed_increment = min(mon->speed_increment + 100, old_energy);
258+
259+
if (you.pending_revival)
260+
return;
261+
}
262+
}
263+
264+
static void _apply_pre_move_effects(coord_def new_pos)
265+
{
266+
remove_water_hold();
267+
_clear_constriction_data();
268+
_trigger_opportunity_attacks(new_pos);
269+
}
270+
232271
bool apply_cloud_trail(const coord_def old_pos)
233272
{
234273
if (you.duration[DUR_CLOUD_TRAIL])
@@ -687,10 +726,6 @@ static spret _rampage_forward(coord_def move)
687726
}
688727

689728
// We've passed the validity checks, go ahead and rampage.
690-
691-
// First, apply any necessary pre-move effects:
692-
remove_water_hold();
693-
_clear_constriction_data();
694729
const coord_def old_pos = you.pos();
695730

696731
clear_messages();
@@ -709,6 +744,11 @@ static spret _rampage_forward(coord_def move)
709744
valid_target->name(DESC_THE, true).c_str());
710745
}
711746

747+
// First, apply any necessary pre-move effects:
748+
_apply_pre_move_effects(beam.target);
749+
if (you.pending_revival) // died to opportunity attack
750+
return spret::success;
751+
712752
// stepped = true, we're flavouring this as movement, not a blink.
713753
move_player_to_grid(beam.target, true);
714754

@@ -1074,8 +1114,9 @@ void move_player_action(coord_def move)
10741114
// when confusion causes no move.
10751115
if (you.pos() != targ && targ_pass)
10761116
{
1077-
remove_water_hold();
1078-
_clear_constriction_data();
1117+
_apply_pre_move_effects(targ);
1118+
if (you.pending_revival) // died to opportunity attack
1119+
return;
10791120
move_player_to_grid(targ, true);
10801121
apply_barbs_damage();
10811122
remove_ice_movement();

0 commit comments

Comments
 (0)