Fix D2 emulation of D1 boss teleport handling

Descent 1 bosses could always teleport, but were only placed in large
areas where free teleporting was always permitted.

Descent 2 boss 1 was placed in a confined segment and not permitted to
teleport out of it until it was opened.  This was implemented by a
two-part change relative to Descent 1 rules:

- Descent 1 bosses were always permitted to teleport to any teleport
  destination segment.  Descent 2 bosses were only permitted to teleport
  if the player was visible or the boss had recently been hit.
- When computing the permitted list of teleport destination segments,
  Descent 1 used directly connected accessible segments, then stopped.
  Descent 2 started with this rule, but if the list had at most 1 entry,
  then it would assume this is the confined boss and recompute the list
  with the first wall ignored.  This recomputed list allowed the boss to
  teleport to any segment in the larger arena outside its starting
  segment.

After D2X-Rebirth support for emulating Descent 1 bosses was enhanced in
28bd4c1650, Descent 1 bosses gained the ability to teleport out of a
confining cube early, but only in D2X-Rebirth when emulating Descent 1.
In D1X-Rebirth, when a boss is placed in a confining cube, it can always
teleport, but only to that confining cube.  In D2X-Rebirth, Descent 1
bosses retain the always-teleport rule of Descent 1, but gained the
Descent 2 rule for expanding its search to the surrounding arena.  It
should only use the expanded search when it also abides by the Descent 2
restriction not to teleport before the first wall is opened.  It did not
abide by that rule.  This commit adds that restriction for Descent 1
bosses.

Reported-by: ef314159 <https://github.com/dxx-rebirth/dxx-rebirth/issues/348>
Fixes: 28bd4c1650 ("Enable D1 boss behavior in d2x build. So we get correct boss behavior when emulating D1, and 3rd party mn2s can include D1 bosses.")
This commit is contained in:
Kp 2017-10-06 01:59:09 +00:00
parent 0909b126ac
commit 8291391b7f

View file

@ -2353,7 +2353,7 @@ namespace dsx {
// --------------------------------------------------------------------------------------------------------------------
// Do special stuff for a boss.
static void do_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp)
static void do_d1_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp, const int player_visibility)
{
#ifndef NDEBUG
if (objp->shields != Prev_boss_shields) {
@ -2361,6 +2361,9 @@ static void do_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp)
}
#endif
if (!player_visibility && !Boss_hit_this_frame)
return;
if (!Boss_dying) {
if (objp->ctype.ai_info.CLOAKED == 1) {
if (GameTime64 - Boss_cloak_start_time > Boss_cloak_duration / 3 &&
@ -2374,7 +2377,8 @@ static void do_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp)
Last_teleport_time -= Boss_teleport_interval/4;
}
if (GameTime64 > Boss_cloak_start_time + Boss_cloak_duration)
if (GameTime64 > (Boss_cloak_start_time + Boss_cloak_duration) ||
GameTime64 < Boss_cloak_start_time)
objp->ctype.ai_info.CLOAKED = 0;
} else {
if (Boss_hit_this_frame ||
@ -2403,7 +2407,7 @@ static void do_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp)
static void do_super_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp, fix dist_to_player, int player_visibility)
{
static int eclip_state = 0;
do_boss_stuff(vmsegptridx, objp);
do_d1_boss_stuff(vmsegptridx, objp, player_visibility);
// Only master player can cause gating to occur.
if ((Game_mode & GM_MULTI) && !multi_i_am_master())
@ -2443,7 +2447,7 @@ static void do_super_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t o
}
#if defined(DXX_BUILD_DESCENT_II)
static void do_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp, int player_visibility)
static void do_d2_boss_stuff(fvmsegptridx &vmsegptridx, const vmobjptridx_t objp, int player_visibility)
{
int boss_id, boss_index;
@ -3055,18 +3059,10 @@ _exit_cheat:
vm_vec_zero(gun_point);
}
switch (robptr.boss_flag) {
switch (const auto boss_flag = robptr.boss_flag) {
case 0:
break;
case BOSS_D1:
if (aip->GOAL_STATE == AIS_FLIN)
aip->GOAL_STATE = AIS_FIRE;
if (aip->CURRENT_STATE == AIS_FLIN)
aip->CURRENT_STATE = AIS_FIRE;
do_boss_stuff(vmsegptridx, obj);
break;
case BOSS_SUPER:
if (aip->GOAL_STATE == AIS_FLIN)
aip->GOAL_STATE = AIS_FIRE;
@ -3089,8 +3085,11 @@ _exit_cheat:
default:
#if defined(DXX_BUILD_DESCENT_I)
(void)boss_flag;
Int3(); // Bogus boss flag value.
#elif defined(DXX_BUILD_DESCENT_II)
break;
#endif
case BOSS_D1:
{
int pv;
@ -3108,10 +3107,16 @@ _exit_cheat:
pv = 0;
}
do_boss_stuff(vmsegptridx, obj, pv);
#if defined(DXX_BUILD_DESCENT_II)
if (boss_flag != BOSS_D1)
{
do_d2_boss_stuff(vmsegptridx, obj, pv);
break;
}
#endif
do_d1_boss_stuff(vmsegptridx, obj, pv);
}
break;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -4314,7 +4319,7 @@ void do_ai_frame_all(void)
}
}
// (Moved here from do_boss_stuff() because that only gets called if robot aware of player.)
// (Moved here from do_d2_boss_stuff() because that only gets called if robot aware of player.)
if (Boss_dying) {
range_for (const auto &&objp, vmobjptridx)
{