Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MBF21 Seeker missile codepointers (Take 2) #45

Merged
merged 12 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion prboom2/doc/mbf21.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ MBF21 defaults:
- For future-proofing, if more nonzero args are defined on a state than its action pointer expects (e.g. defining Args3 on a state that uses A_WeaponSound), an error will be thrown on startup.

#### New DEHACKED Codepointers
- [PR](https://github.com/kraflab/dsda-doom/pull/20), [PR](https://github.com/kraflab/dsda-doom/pull/38), [PR](https://github.com/kraflab/dsda-doom/pull/40), [PR](https://github.com/kraflab/dsda-doom/pull/41)
- [PR](https://github.com/kraflab/dsda-doom/pull/20), [PR](https://github.com/kraflab/dsda-doom/pull/38), [PR](https://github.com/kraflab/dsda-doom/pull/40), [PR](https://github.com/kraflab/dsda-doom/pull/41), [PR](https://github.com/kraflab/dsda-doom/pull/45)
- All new MBF21 pointers use the new "Args" fields for params, rather than misc1/misc2 fields
- Arg fields are listed in order in the docs below, e.g. for `A_SpawnObject`, `type` is Args1, `angle` is Args2, etc.
- Although all args are integers internally, there are effectively the following types of args:
Expand Down Expand Up @@ -285,6 +285,39 @@ MBF21 defaults:
- `state (uint)`: State to jump to on the calling actor when resurrecting a corpse
- `sound (uint)`: Sound to play when resurrecting a corpse

- **A_SeekTracer(threshold, maxturnangle)**
- Generic seeker missile function.
- Args:
- `threshold (fixed)`: If angle to target is lower than this, missile will 'snap' directly to face the target
- `maxturnangle (fixed)`: Maximum angle a missile will turn towards the target if angle is above the threshold
- Notes:
- When using this function, keep in mind that seek 'strength' also depends on how often this function is called -- e.g. calling `A_SeekTracer` every tic will result in a much more aggressive seek than calling it every 4 tics, even if the args are the same
- This function uses Heretic's seeker missile logic (P_SeekerMissile), rather than Doom's, with two notable changes:
- The actor's `tracer` pointer is used as the seek target, rather than Heretic's `special1` field
- On the z-axis, the missile will seek towards the vertical centerpoint of the seek target, rather than the bottom (resulting in much friendly behavior when seeking toward enemies on high ledges). Refer to the implementation for details.

- **A_FindTracer(fov, distance)**
- Searches for a valid tracer (seek target), if the calling actor doesn't already have one. Particularly useful for player missiles.
- Args:
- `fov (fixed)`: Field-of-view, relative to calling actor's angle, to search for targets in. If zero, the search will occur in all directions.
- `distance (int)`: Distance to search, in map blocks (128 units); if not set, defaults to 10. Setting this value too high may hurt performance, so don't get overzealous.
- Notes:
- This function is intended for use on player-fired missiles; it may not produce particularly useful results if used on a monster projectile.
- This function will no-op if the calling actor already has a tracer. To forcibly re-acquire a new tracer, call A_ClearTracer first.
- This function uses a variant of Hexen's blockmap search algorithm (P_RoughMonsterSearch); refer to the implementation for specifics, but it's identical to Hexen's except for the rules for picking a valid target (in order of evaluation):
- Actors without the SHOOTABLE flag are skipped
- The projectile's owner (target) is skipped
- Actors on the same "team" are skipped (e.g. MF_FRIEND actors will not pick players or fellow MF_FRIENDs), with a few exceptions:
- If actors are infighting (i.e. candidate actor is projectile owner's target), actor will not be skipped
- Players will not be skipped in deathmatch
- This rule is to work around a weird quirk in MBF (namely, that players have MF_FRIEND set even in DM); ports with a robust "team" implementation may be able to do a smarter check here in general.
- If the `fov` arg is nonzero, actors outside of an `fov`-degree cone, relative to the missile's angle, are skipped
- Actors not in line-of-sight of the missile are skipped

- **A_ClearTracer**
- Clears the calling actor's tracer (seek target) field.
- No Args.

- **A_JumpIfHealthBelow(state, health)**
- Jumps to a state if caller's health is below the specified threshold.
- Args:
Expand All @@ -309,6 +342,22 @@ MBF21 defaults:
- This function uses the same approximate distance check as other functions in the game (P_AproxDistance).
- The jump will only occur if distance is BELOW the given value -- e.g. `A_JumpIfTargetCloser(420, 69)` will jump to state 420 if distance is 68 or lower.

- **A_JumpIfTracerInSight(state)**
- Jumps to a state if caller's tracer (seek target) is in line-of-sight.
- Args:
- `state (uint)`: State to jump to
- Notes:
- This function only considers line-of-sight, i.e. independent of calling actor's facing direction.

- **A_JumpIfTracerCloser(state, distance)**
- Jumps to a state if caller's tracer (seek target) is closer than the specified distance.
- Args:
- `state (uint)`: State to jump to
- `distance (fixed)`: Distance threshold, in map units
- Notes:
- This function uses the same approximate distance check as other functions in the game (P_AproxDistance).
- The jump will only occur if distance is BELOW the given value -- e.g. `A_JumpIfTracerCloser(420, 69)` will jump to state 420 if distance is 68 or lower.

- **A_JumpIfFlagsSet(state, flags, flags2)**
- Jumps to a state if caller has the specified thing flags set.
- Args:
Expand Down Expand Up @@ -343,6 +392,7 @@ MBF21 defaults:
- Notes:
- Unlike native Doom attack codepointers, this function will not consume ammo, trigger the Flash state, or play a sound.
- The `pitch` arg uses the same approximated pitch calculation that Doom's monster aim / autoaim uses. Refer to the implementation for specifics.
- The spawned projectile's `tracer` pointer is set to the player's autoaim target, if available.

- **A_WeaponBulletAttack(hspread, vspread, numbullets, damagebase, damagedice)**
- Generic weapon bullet attack.
Expand Down
5 changes: 5 additions & 0 deletions prboom2/src/d_deh.c
Original file line number Diff line number Diff line change
Expand Up @@ -1493,9 +1493,14 @@ static const deh_bexptr deh_bexptrs[] = // CPhipps - static const
{A_RadiusDamage, "A_RadiusDamage", 2},
{A_NoiseAlert, "A_NoiseAlert", 0},
{A_HealChase, "A_HealChase", 2},
{A_SeekTracer, "A_SeekTracer", 2},
{A_FindTracer, "A_FindTracer", 2, {0, 10}},
{A_ClearTracer, "A_ClearTracer", 0},
{A_JumpIfHealthBelow, "A_JumpIfHealthBelow", 2},
{A_JumpIfTargetInSight, "A_JumpIfTargetInSight", 1},
{A_JumpIfTargetCloser, "A_JumpIfTargetCloser", 2},
{A_JumpIfTracerInSight, "A_JumpIfTracerInSight", 1},
{A_JumpIfTracerCloser, "A_JumpIfTracerCloser", 2},
{A_JumpIfFlagsSet, "A_JumpIfFlagsSet", 3},
{A_AddFlags, "A_AddFlags", 2},
{A_RemoveFlags, "A_RemoveFlags", 2},
Expand Down
94 changes: 92 additions & 2 deletions prboom2/src/p_enemy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3242,6 +3242,57 @@ void A_HealChase(mobj_t* actor)
A_Chase(actor);
}

//
// A_SeekTracer
// A parameterized seeker missile function.
// args[0]: direct-homing threshold angle (degrees, in fixed point)
// args[1]: maximum turn angle (degrees, in fixed point)
//
void A_SeekTracer(mobj_t *actor)
{
angle_t threshold, maxturnangle;

if (!mbf21 || !actor)
return;

threshold = FixedToAngle(actor->state->args[0]);
maxturnangle = FixedToAngle(actor->state->args[1]);

P_SeekerMissile(actor, &actor->tracer, threshold, maxturnangle, true);
}

//
// A_FindTracer
// Search for a valid tracer (seek target), if the calling actor doesn't already have one.
// args[0]: field-of-view to search in (degrees, in fixed point); if zero, will search in all directions
// args[1]: distance to search (map blocks, i.e. 128 units)
//
void A_FindTracer(mobj_t *actor)
{
angle_t fov;
int dist;

if (!mbf21 || !actor || actor->tracer)
return;

fov = FixedToAngle(actor->state->args[0]);
dist = (actor->state->args[1]);

actor->tracer = P_RoughTargetSearch(actor, fov, dist);
}

//
// A_ClearTracer
// Clear current tracer (seek target).
//
void A_ClearTracer(mobj_t *actor)
{
if (!mbf21 || !actor)
return;

actor->tracer = NULL;
}

//
// A_JumpIfHealthBelow
// Jumps to a state if caller's health is below the specified threshold.
Expand Down Expand Up @@ -3301,6 +3352,45 @@ void A_JumpIfTargetCloser(mobj_t* actor)
P_SetMobjState(actor, state);
}

//
// A_JumpIfTracerInSight
// Jumps to a state if caller's tracer (seek target) is in line-of-sight.
// args[0]: State to jump to
//
void A_JumpIfTracerInSight(mobj_t* actor)
{
int state;

if (!mbf21 || !actor || !actor->tracer)
return;

state = actor->state->args[0];

if (P_CheckSight(actor, actor->tracer))
P_SetMobjState(actor, state);
}

//
// A_JumpIfTracerCloser
// Jumps to a state if caller's tracer (seek target) is closer than the specified distance.
// args[0]: State to jump to
// args[1]: Distance threshold (fixed point)
//
void A_JumpIfTracerCloser(mobj_t* actor)
{
int state, distance;

if (!mbf21 || !actor || !actor->tracer)
return;

state = actor->state->args[0];
distance = actor->state->args[1];

if (distance > P_AproxDistance(actor->x - actor->tracer->x,
actor->y - actor->tracer->y))
P_SetMobjState(actor, state);
}

//
// A_JumpIfFlagsSet
// Jumps to a state if caller has the specified thing flags set.
Expand Down Expand Up @@ -3703,7 +3793,7 @@ void A_MummyAttack2(mobj_t * actor)

void A_MummyFX1Seek(mobj_t * actor)
{
P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 20);
P_SeekerMissile(actor, &actor->special1.m, ANG1_X * 10, ANG1_X * 20, false);
}

void A_MummySoul(mobj_t * mummy)
Expand Down Expand Up @@ -4157,7 +4247,7 @@ void A_WhirlwindSeek(mobj_t * actor)
{
return;
}
P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 30);
P_SeekerMissile(actor, &actor->special1.m, ANG1_X * 10, ANG1_X * 30, false);
}

void A_HeadIceImpact(mobj_t * ice)
Expand Down
5 changes: 5 additions & 0 deletions prboom2/src/p_enemy.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,14 @@ void A_MonsterMeleeAttack(mobj_t *);
void A_RadiusDamage(mobj_t *);
void A_NoiseAlert(mobj_t *);
void A_HealChase(mobj_t *);
void A_SeekTracer(mobj_t *);
void A_FindTracer(mobj_t *);
void A_ClearTracer(mobj_t *);
void A_JumpIfHealthBelow(mobj_t *);
void A_JumpIfTargetInSight(mobj_t *);
void A_JumpIfTargetCloser(mobj_t *);
void A_JumpIfTracerInSight(mobj_t *);
void A_JumpIfTracerCloser(mobj_t *);
void A_JumpIfFlagsSet(mobj_t *);
void A_AddFlags(mobj_t *);
void A_RemoveFlags(mobj_t *);
Expand Down
Loading