mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Implement Follower Audience object
- Replaces Chao audience entirely
- Convenient, because one of the two default follower types used in the audience is Chao
- Can provide one follower, or a list of followers, on the stringarg1 (seperated by spaces/commas) and it'll pick randomly between them
- If not provided, uses the mapheader follower list
- Can provide one skincolor, or a list of skincolors, on the stringarg2 (seperated by spaces/commas) and it'll pick randomly between them
- If not provided, uses the follower's default color
- If the follower's default color is Match/Opposite or the user provides "Random" in stringarg2, pick a random skincolor
- If arg3 is set, floats in the air
- MTF_OBJECTSPECIAL in binary format
- If arg4 is set, faces the closest player
- MTF_AMBUSH in binary format
- Uses some funny mathematical tricks to avoid checking on the same frame as every other audience member at once
This commit is contained in:
parent
5f15736626
commit
a74b7995c9
9 changed files with 295 additions and 94 deletions
|
|
@ -3860,14 +3860,6 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
"S_DEZLASER_TRAIL4",
|
||||
"S_DEZLASER_TRAIL5",
|
||||
|
||||
// Audience Members
|
||||
"S_RANDOMAUDIENCE",
|
||||
"S_AUDIENCE_CHAO_CHEER1",
|
||||
"S_AUDIENCE_CHAO_CHEER2",
|
||||
"S_AUDIENCE_CHAO_WIN1",
|
||||
"S_AUDIENCE_CHAO_WIN2",
|
||||
"S_AUDIENCE_CHAO_LOSE",
|
||||
|
||||
// 1.0 Kart Decoratives
|
||||
"S_FLAYM1",
|
||||
"S_FLAYM2",
|
||||
|
|
|
|||
12
src/info.c
12
src/info.c
|
|
@ -4483,16 +4483,6 @@ state_t states[NUMSTATES] =
|
|||
{SPR_DEZL, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL5}, // S_DEZLASER_TRAIL4
|
||||
{SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_NULL}, // S_DEZLASER_TRAIL5
|
||||
|
||||
{SPR_NULL, 0, 1, {A_RandomStateRange}, S_AUDIENCE_CHAO_CHEER1, S_AUDIENCE_CHAO_CHEER2, S_RANDOMAUDIENCE}, // S_RANDOMAUDIENCE
|
||||
|
||||
{SPR_AUDI, 0, 5, {NULL}, 0, 0, S_AUDIENCE_CHAO_CHEER2}, // S_AUDIENCE_CHAO_CHEER1
|
||||
{SPR_AUDI, 1, 20, {A_BunnyHop}, 7, 0, S_AUDIENCE_CHAO_CHEER1}, // S_AUDIENCE_CHAO_CHEER2
|
||||
|
||||
{SPR_AUDI, 2, 5, {NULL}, 0, 0, S_AUDIENCE_CHAO_WIN2}, // S_AUDIENCE_CHAO_WIN1
|
||||
{SPR_AUDI, 3, 25, {A_BunnyHop}, 10, 0, S_AUDIENCE_CHAO_WIN1}, // S_AUDIENCE_CHAO_WIN2
|
||||
|
||||
{SPR_AUDI, 4|FF_ANIMATE, -1, {NULL}, 1, 17, S_NULL}, // S_AUDIENCE_CHAO_LOSE
|
||||
|
||||
{SPR_FLAM, FF_FULLBRIGHT|FF_ADD|0, 3, {NULL}, 0, 0, S_FLAYM2}, // S_FLAYM1,
|
||||
{SPR_FLAM, FF_FULLBRIGHT|FF_ADD|1, 3, {NULL}, 0, 0, S_FLAYM3}, // S_FLAYM2,
|
||||
{SPR_FLAM, FF_FULLBRIGHT|FF_ADD|2, 3, {NULL}, 0, 0, S_FLAYM4}, // S_FLAYM3,
|
||||
|
|
@ -24880,7 +24870,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
|
||||
{ // MT_RANDOMAUDIENCE
|
||||
1488, // doomednum
|
||||
S_RANDOMAUDIENCE, // spawnstate
|
||||
S_UNKNOWN, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
|
|
|
|||
|
|
@ -4929,14 +4929,6 @@ typedef enum state
|
|||
S_DEZLASER_TRAIL4,
|
||||
S_DEZLASER_TRAIL5,
|
||||
|
||||
// Audience Members
|
||||
S_RANDOMAUDIENCE,
|
||||
S_AUDIENCE_CHAO_CHEER1,
|
||||
S_AUDIENCE_CHAO_CHEER2,
|
||||
S_AUDIENCE_CHAO_WIN1,
|
||||
S_AUDIENCE_CHAO_WIN2,
|
||||
S_AUDIENCE_CHAO_LOSE,
|
||||
|
||||
// 1.0 Kart Decoratives
|
||||
S_FLAYM1,
|
||||
S_FLAYM2,
|
||||
|
|
|
|||
|
|
@ -114,6 +114,10 @@ void Obj_PlayerUsedRingShooter(mobj_t *base, player_t *player);
|
|||
void Obj_RingShooterDelete(mobj_t *mo);
|
||||
void Obj_UpdateRingShooterFace(mobj_t *part);
|
||||
|
||||
/* Follower Audience */
|
||||
void Obj_RandomAudienceInit(mobj_t * mobj, mapthing_t *mthing);
|
||||
void Obj_RandomAudienceThink(mobj_t * mobj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ typedef enum
|
|||
PR_VOICES, // Player voice sounds
|
||||
PR_RANDOMSKIN, // Random skin select from Heavy Magician(?)
|
||||
|
||||
PR_RANDOMAUDIENCE, // Audience randomisation
|
||||
|
||||
PR_RULESCRAMBLE, // Rule scrambing events
|
||||
|
||||
PR_MUSICSELECT, // Randomized music selection
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
loops.c
|
||||
drop-target.c
|
||||
ring-shooter.c
|
||||
audience.c
|
||||
)
|
||||
|
|
|
|||
272
src/objects/audience.c
Normal file
272
src/objects/audience.c
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
#include "../doomdef.h"
|
||||
#include "../info.h"
|
||||
#include "../g_game.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../k_follower.h"
|
||||
#include "../m_random.h"
|
||||
#include "../p_local.h"
|
||||
#include "../p_setup.h"
|
||||
#include "../r_draw.h" // R_GetColorByName
|
||||
#include "../r_main.h" // R_PointToAngle2, R_PointToDist2
|
||||
#include "../z_zone.h" // Z_StrDup/Z_Free
|
||||
|
||||
#define audience_mainstate(o) ((o)->cvmem)
|
||||
|
||||
#define audience_bobamp(o) ((o)->cusval)
|
||||
#define audience_bobspeed(o) ((o)->reactiontime)
|
||||
|
||||
#define audience_animoffset(o) ((o)->threshold)
|
||||
|
||||
#define audience_focusplayer(o) ((o)->lastlook)
|
||||
#define audience_focusdelay(o) ((o)->movecount)
|
||||
|
||||
void
|
||||
Obj_RandomAudienceInit
|
||||
( mobj_t * mobj,
|
||||
mapthing_t *mthing)
|
||||
{
|
||||
UINT16 *reflist = NULL;
|
||||
UINT16 tempreflist[MAXHEADERFOLLOWERS];
|
||||
UINT8 numref = 0;
|
||||
INT32 followerpick = 0;
|
||||
|
||||
P_SetScale(mobj, (mobj->destscale <<= 1));
|
||||
|
||||
audience_mainstate(mobj) = S_NULL;
|
||||
|
||||
// Pick follower
|
||||
{
|
||||
if (mthing->stringargs[0] != NULL)
|
||||
{
|
||||
// From mapthing
|
||||
char *stringcopy = Z_StrDup(mthing->stringargs[0]);
|
||||
char *tok = strtok(stringcopy, " ,");
|
||||
|
||||
numref = 0;
|
||||
while (tok && numref < MAXHEADERFOLLOWERS)
|
||||
{
|
||||
tempreflist[numref++] = K_FollowerAvailable(tok);
|
||||
tok = strtok(NULL, " ,");
|
||||
}
|
||||
|
||||
Z_Free(stringcopy);
|
||||
|
||||
reflist = tempreflist;
|
||||
}
|
||||
else
|
||||
{
|
||||
// From mapheader
|
||||
|
||||
if (!mapheaderinfo[gamemap-1])
|
||||
{
|
||||
// No mapheader, no shoes, no service.
|
||||
return;
|
||||
}
|
||||
|
||||
numref = mapheaderinfo[gamemap-1]->numFollowers;
|
||||
reflist = mapheaderinfo[gamemap-1]->followers;
|
||||
}
|
||||
|
||||
if (!numref || !reflist)
|
||||
{
|
||||
// This is the one thing a user should definitely be told about.
|
||||
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings));
|
||||
return;
|
||||
}
|
||||
|
||||
followerpick = reflist[P_RandomKey(PR_RANDOMAUDIENCE, numref)];
|
||||
|
||||
if (followerpick < 0 || followerpick >= numfollowers)
|
||||
{
|
||||
// Is this user error or user choice..?
|
||||
P_RemoveMobj(mobj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle storing follower properties on the object
|
||||
{
|
||||
audience_mainstate(mobj) = followers[followerpick].followstate;
|
||||
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return;
|
||||
|
||||
if (mthing->args[2] != 0)
|
||||
{
|
||||
mobj->flags |= MF_NOGRAVITY;
|
||||
}
|
||||
|
||||
if (mthing->args[3] != 0)
|
||||
{
|
||||
mobj->flags2 |= MF2_AMBUSH;
|
||||
}
|
||||
|
||||
// The following is derived from the default bobamp
|
||||
if (!(mobj->flags & MF_NOGRAVITY) && followers[followerpick].bobamp < 4*FRACUNIT)
|
||||
{
|
||||
audience_bobamp(mobj) = 4*mobj->scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
audience_bobamp(mobj) = FixedMul(mobj->scale, followers[followerpick].bobamp);
|
||||
}
|
||||
|
||||
audience_bobspeed(mobj) = followers[followerpick].bobspeed;
|
||||
audience_focusplayer(mobj) = MAXPLAYERS;
|
||||
|
||||
if (P_RandomChance(PR_RANDOMAUDIENCE, FRACUNIT/2))
|
||||
{
|
||||
audience_animoffset(mobj) = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle colors
|
||||
{
|
||||
UINT16 colorpick = SKINCOLOR_NONE;
|
||||
|
||||
if (mthing->stringargs[1] != NULL)
|
||||
{
|
||||
if (!strcmp("Random", mthing->stringargs[1]))
|
||||
{
|
||||
colorpick = FOLLOWERCOLOR_MATCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *stringcopy = Z_StrDup(mthing->stringargs[1]);
|
||||
char *tok = strtok(stringcopy, " ");
|
||||
|
||||
numref = 0;
|
||||
while (tok && numref < MAXHEADERFOLLOWERS)
|
||||
{
|
||||
tempreflist[numref++] = R_GetColorByName(tok);
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
Z_Free(stringcopy);
|
||||
|
||||
if (numref)
|
||||
{
|
||||
colorpick = tempreflist[P_RandomKey(PR_RANDOMAUDIENCE, numref)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (colorpick == SKINCOLOR_NONE)
|
||||
{
|
||||
colorpick = followers[followerpick].defaultcolor;
|
||||
}
|
||||
|
||||
if (colorpick == FOLLOWERCOLOR_MATCH
|
||||
|| colorpick == FOLLOWERCOLOR_OPPOSITE)
|
||||
{
|
||||
colorpick = P_RandomKey(PR_RANDOMAUDIENCE, numskincolors-1)+1;
|
||||
}
|
||||
|
||||
mobj->color = colorpick;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Obj_RandomAudienceThink
|
||||
( mobj_t * mobj)
|
||||
{
|
||||
if (audience_mainstate(mobj) == S_NULL)
|
||||
{
|
||||
// Uninitialised, don't do anything funny.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mobj->flags2 & MF2_AMBUSH)
|
||||
{
|
||||
if (audience_focusdelay(mobj) == 0)
|
||||
{
|
||||
fixed_t bestdist = INT32_MAX, dist;
|
||||
UINT8 i;
|
||||
|
||||
audience_focusplayer(mobj) = MAXPLAYERS;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false
|
||||
|| players[i].spectator == true
|
||||
|| P_MobjWasRemoved(players[i].mo))
|
||||
continue;
|
||||
|
||||
dist = R_PointToDist2(
|
||||
mobj->x,
|
||||
mobj->y,
|
||||
players[i].mo->x,
|
||||
players[i].mo->y
|
||||
);
|
||||
|
||||
if (dist >= bestdist)
|
||||
continue;
|
||||
|
||||
dist = R_PointToDist2(
|
||||
mobj->z,
|
||||
0,
|
||||
players[i].mo->z,
|
||||
dist
|
||||
);
|
||||
|
||||
if (dist >= bestdist)
|
||||
continue;
|
||||
|
||||
bestdist = dist;
|
||||
audience_focusplayer(mobj) = i;
|
||||
}
|
||||
|
||||
// Try to add some spacing out so the object isn't constantly looking for players
|
||||
audience_focusdelay(mobj) = TICRATE + min((bestdist/FRACUNIT), (2*TICRATE)) + (bestdist % TICRATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
audience_focusdelay(mobj)--;
|
||||
}
|
||||
|
||||
if (audience_focusplayer(mobj) < MAXPLAYERS && audience_focusplayer(mobj) >= 0)
|
||||
{
|
||||
mobj->angle = R_PointToAngle2(
|
||||
mobj->x,
|
||||
mobj->y,
|
||||
players[audience_focusplayer(mobj)].mo->x,
|
||||
players[audience_focusplayer(mobj)].mo->y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (mobj->flags & MF_NOGRAVITY)
|
||||
{
|
||||
// This horrible calculation was inherited from k_follower.c, with only newlines (and a FRACUNIT offset) added
|
||||
mobj->sprzoff = FixedMul(audience_bobamp(mobj),
|
||||
FRACUNIT + FINESINE(((
|
||||
FixedMul(4 * M_TAU_FIXED, audience_bobspeed(mobj))
|
||||
* (leveltime + audience_animoffset(mobj))
|
||||
) >> ANGLETOFINESHIFT) & FINEMASK));
|
||||
|
||||
// Gravity
|
||||
if (mobj->flags2 & MF2_OBJECTFLIP)
|
||||
{
|
||||
mobj->sprzoff = -mobj->sprzoff;
|
||||
}
|
||||
}
|
||||
else if (audience_animoffset(mobj) > 0)
|
||||
{
|
||||
// Skipped frames at spawn for offset in jumping
|
||||
audience_animoffset(mobj)--;
|
||||
}
|
||||
else if (mobj->flags2 & MF2_OBJECTFLIP)
|
||||
{
|
||||
if (mobj->z + mobj->height >= mobj->ceilingz)
|
||||
{
|
||||
mobj->momz = -audience_bobamp(mobj);
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
}
|
||||
}
|
||||
else if (mobj->z <= mobj->floorz)
|
||||
{
|
||||
mobj->momz = audience_bobamp(mobj);
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
}
|
||||
}
|
||||
78
src/p_mobj.c
78
src/p_mobj.c
|
|
@ -10267,39 +10267,9 @@ void P_SceneryThinker(mobj_t *mobj)
|
|||
mobj->renderflags &= ~RF_DONTDRAW;
|
||||
}
|
||||
|
||||
if (mobj->type != MT_RANDOMAUDIENCE)
|
||||
return;
|
||||
|
||||
if (mobj->type == MT_RANDOMAUDIENCE)
|
||||
{
|
||||
if (!mobj->colorized) // a fan of someone?
|
||||
return;
|
||||
|
||||
if (mobj->threshold >= 0) // not already happy or sad?
|
||||
{
|
||||
if (!playeringame[mobj->threshold] || players[mobj->threshold].spectator) // focused on a valid player?
|
||||
return;
|
||||
|
||||
if (!(players[mobj->threshold].exiting) && !(players[mobj->threshold].pflags & PF_NOCONTEST)) // not finished yet?
|
||||
return;
|
||||
|
||||
if (K_IsPlayerLosing(&players[mobj->threshold]))
|
||||
mobj->threshold = -2;
|
||||
else
|
||||
{
|
||||
mobj->threshold = -1;
|
||||
S_StartSound(mobj, sfx_chaooo);
|
||||
}
|
||||
}
|
||||
|
||||
if (mobj->threshold == -1)
|
||||
mobj->angle += ANGLE_22h;
|
||||
|
||||
if (((statenum_t)(mobj->state-states) != S_AUDIENCE_CHAO_CHEER2) || (mobj->tics != states[S_AUDIENCE_CHAO_CHEER2].tics)) // not at the start of your cheer jump?
|
||||
return;
|
||||
|
||||
mobj->momz = 0;
|
||||
|
||||
P_SetMobjState(mobj, ((mobj->threshold == -1) ? S_AUDIENCE_CHAO_WIN2 : S_AUDIENCE_CHAO_LOSE));
|
||||
Obj_RandomAudienceThink(mobj);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10814,41 +10784,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
|
|||
case MT_CDUFO:
|
||||
P_SetScale(mobj, (mobj->destscale = 3*FRACUNIT/2));
|
||||
break;
|
||||
case MT_RANDOMAUDIENCE:
|
||||
{
|
||||
fixed_t randu = P_RandomFixed(PR_UNDEFINED);
|
||||
P_SetScale(mobj, (mobj->destscale <<= 1));
|
||||
if (randu < (FRACUNIT/9)) // a fan of someone?
|
||||
{
|
||||
UINT8 i, pcount = 0;
|
||||
UINT8 pnum[MAXPLAYERS];
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
pnum[pcount] = i;
|
||||
pcount++;
|
||||
}
|
||||
|
||||
if (pcount)
|
||||
{
|
||||
mobj->threshold = pnum[P_RandomKey(PR_UNDEFINED, pcount)];
|
||||
mobj->color = players[mobj->threshold].skincolor;
|
||||
mobj->colorized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (randu > (FRACUNIT/2))
|
||||
{
|
||||
mobj->color = P_RandomKey(PR_UNDEFINED, numskincolors-1)+1;
|
||||
break;
|
||||
}
|
||||
|
||||
mobj->color = SKINCOLOR_CYAN;
|
||||
break;
|
||||
}
|
||||
case MT_MARBLETORCH:
|
||||
P_SpawnMobj(mobj->x, mobj->y, mobj->z + (29*mobj->scale), MT_MARBLELIGHT);
|
||||
break;
|
||||
|
|
@ -13217,6 +13152,15 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MT_RANDOMAUDIENCE:
|
||||
{
|
||||
Obj_RandomAudienceInit(mobj, mthing);
|
||||
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
case MT_AAZTREE_HELPER:
|
||||
{
|
||||
fixed_t top = mobj->z;
|
||||
|
|
|
|||
|
|
@ -6739,6 +6739,10 @@ static void P_ConvertBinaryThingTypes(void)
|
|||
case 1305: //Rollout Rock
|
||||
mapthings[i].args[0] = !!(mapthings[i].options & MTF_AMBUSH);
|
||||
break;
|
||||
case 1488: // Follower Audience (unfortunately numbered)
|
||||
mapthings[i].args[2] = !!(mapthings[i].options & MTF_OBJECTSPECIAL);
|
||||
mapthings[i].args[3] = !!(mapthings[i].options & MTF_AMBUSH);
|
||||
break;
|
||||
case 1500: //Glaregoyle
|
||||
case 1501: //Glaregoyle (Up)
|
||||
case 1502: //Glaregoyle (Down)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue