Merge branch 'follower-audience' into 'master'

Follower Audience

Closes #498 and #325

See merge request KartKrew/Kart!1185
This commit is contained in:
toaster 2023-04-24 21:49:34 +00:00
commit 3d0abdd74a
17 changed files with 496 additions and 215 deletions

View file

@ -1196,8 +1196,8 @@ static void IdentifyVersion(void)
D_AddFile(startupiwads, va(pandf,srb2waddir,"textures_segazones.pk3"));
D_AddFile(startupiwads, va(pandf,srb2waddir,"textures_originalzones.pk3"));
D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3"));
D_AddFile(startupiwads, va(pandf,srb2waddir,"maps.pk3"));
D_AddFile(startupiwads, va(pandf,srb2waddir,"followers.pk3"));
D_AddFile(startupiwads, va(pandf,srb2waddir,"maps.pk3"));
#define UNLOCKTESTING
#if defined(DEVELOP) && defined(UNLOCKTESTING)
D_AddFile(startupiwads, va(pandf,srb2waddir,"unlocks.pk3"));
@ -1531,8 +1531,8 @@ void D_SRB2Main(void)
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_SEGA_PK3); // textures_segazones.pk3
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_ORIGINAL_PK3); // textures_originalzones.pk3
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_CHARS_PK3); // chars.pk3
mainwads++; W_VerifyFileMd5(mainwads, ASSET_HASH_FOLLOWERS_PK3); // followers.pk3
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_PK3); // maps.pk3
mainwads++; W_VerifyFileMd5(mainwads, ASSET_HASH_FOLLOWERS_PK3); // followers.pk3
#else
#ifdef USE_PATCH_FILE
mainwads++; // scripts.pk3
@ -1542,8 +1542,8 @@ void D_SRB2Main(void)
mainwads++; // textures_segazones.pk3
mainwads++; // textures_originalzones.pk3
mainwads++; // chars.pk3
mainwads++; // maps.pk3
mainwads++; // followers.pk3
mainwads++; // maps.pk3
#ifdef UNLOCKTESTING
mainwads++; // unlocks.pk3
#endif

View file

@ -143,6 +143,9 @@ void clear_emblems(void)
Z_Free(emblemlocations[i].stringVar);
emblemlocations[i].stringVar = NULL;
Z_Free(emblemlocations[i].stringVar2);
emblemlocations[i].stringVar2 = NULL;
}
memset(&emblemlocations, 0, sizeof(emblemlocations));
@ -187,7 +190,7 @@ void clear_levels(void)
// (no need to set num to 0, we're freeing the entire header shortly)
Z_Free(mapheaderinfo[nummapheaders]->customopts);
P_DeleteFlickies(nummapheaders);
P_DeleteHeaderFollowers(nummapheaders);
Z_Free(mapheaderinfo[nummapheaders]->mainrecord);
@ -1042,77 +1045,47 @@ void readlevelheader(MYFILE *f, char * name)
// Now go to uppercase
strupr(word2);
// List of flickies that are be freed in this map
if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST"))
// List of followers that are be freed in this map
if (fastcmp(word, "FOLLOWERS"))
{
if (fastcmp(word2, "NONE"))
P_DeleteFlickies(num);
else if (fastcmp(word2, "DEMO"))
P_SetDemoFlickies(num);
else if (fastcmp(word2, "ALL"))
{
mobjtype_t tmpflickies[MAXFLICKIES];
for (mapheaderinfo[num]->numFlickies = 0;
((mapheaderinfo[num]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num]->numFlickies].type);
mapheaderinfo[num]->numFlickies++)
tmpflickies[mapheaderinfo[num]->numFlickies] = FLICKYTYPES[mapheaderinfo[num]->numFlickies].type;
if (mapheaderinfo[num]->numFlickies) // just in case...
{
size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num]->numFlickies;
mapheaderinfo[num]->flickies = Z_Realloc(mapheaderinfo[num]->flickies, newsize, PU_STATIC, NULL);
M_Memcpy(mapheaderinfo[num]->flickies, tmpflickies, newsize);
}
}
P_DeleteHeaderFollowers(num);
else if (fastcmp(word2, "DEFAULT"))
P_SetDefaultHeaderFollowers(num);
else
{
mobjtype_t tmpflickies[MAXFLICKIES];
mapheaderinfo[num]->numFlickies = 0;
INT16 tmpfollowers[MAXHEADERFOLLOWERS];
mapheaderinfo[num]->numFollowers = 0;
tmp = strtok(word2,",");
// get up to the first MAXFLICKIES flickies
// get up to the first MAXHEADERFOLLOWERS followers
do {
if (mapheaderinfo[num]->numFlickies == MAXFLICKIES) // never going to get above that number
if (mapheaderinfo[num]->numFollowers == MAXHEADERFOLLOWERS) // never going to get above that number
{
deh_warning("Level header %d: too many flickies\n", num);
deh_warning("Level header %d: too many followers\n", num);
break;
}
if (fastncmp(tmp, "MT_", 3)) // support for specified mobjtypes...
{
i = get_mobjtype(tmp);
if (!i)
i = K_FollowerAvailable(tmp);
if (i == -1)
{
//deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too
deh_warning("Level header %d: unknown follower selection %s\n", num, tmp);
continue;
}
tmpflickies[mapheaderinfo[num]->numFlickies] = i;
tmpfollowers[mapheaderinfo[num]->numFollowers] = i;
mapheaderinfo[num]->numFollowers++;
}
else // ...or a quick, limited selection of default flickies!
{
for (i = 0; FLICKYTYPES[i].name; i++)
if (fastcmp(tmp, FLICKYTYPES[i].name))
break;
if (!FLICKYTYPES[i].name)
{
deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp);
continue;
}
tmpflickies[mapheaderinfo[num]->numFlickies] = FLICKYTYPES[i].type;
}
mapheaderinfo[num]->numFlickies++;
} while ((tmp = strtok(NULL,",")) != NULL);
if (mapheaderinfo[num]->numFlickies)
if (mapheaderinfo[num]->numFollowers)
{
size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num]->numFlickies;
mapheaderinfo[num]->flickies = Z_Realloc(mapheaderinfo[num]->flickies, newsize, PU_STATIC, NULL);
size_t newsize = sizeof(INT16) * mapheaderinfo[num]->numFollowers;
mapheaderinfo[num]->followers = Z_Realloc(mapheaderinfo[num]->followers, newsize, PU_STATIC, NULL);
// now we add them to the list!
M_Memcpy(mapheaderinfo[num]->flickies, tmpflickies, newsize);
M_Memcpy(mapheaderinfo[num]->followers, tmpfollowers, newsize);
}
else
deh_warning("Level header %d: no valid flicky types found\n", num);
deh_warning("Level header %d: no valid follower types found\n", num);
}
}
@ -2231,6 +2204,13 @@ void reademblemdata(MYFILE *f, INT32 num)
emblemlocations[num-1].var = get_number(word2);
}
else if (fastcmp(word, "VAR2"))
{
Z_Free(emblemlocations[num-1].stringVar2);
emblemlocations[num-1].stringVar2 = Z_StrDup(word2);
emblemlocations[num-1].var2 = get_number(word2);
}
else if (fastcmp(word, "FLAGS"))
emblemlocations[num-1].flags = get_number(word2);
else

View file

@ -31,31 +31,6 @@ char *FREE_MOBJS[NUMMOBJFREESLOTS];
char *FREE_SKINCOLORS[NUMCOLORFREESLOTS];
UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway.
struct flickytypes_s FLICKYTYPES[] = {
{"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky)
{"RABBIT", MT_FLICKY_02}, // Pocky (1)
{"CHICKEN", MT_FLICKY_03}, // Cucky (1)
{"SEAL", MT_FLICKY_04}, // Rocky (1)
{"PIG", MT_FLICKY_05}, // Picky (1)
{"CHIPMUNK", MT_FLICKY_06}, // Ricky (1)
{"PENGUIN", MT_FLICKY_07}, // Pecky (1)
{"FISH", MT_FLICKY_08}, // Nicky (CD)
{"RAM", MT_FLICKY_09}, // Flocky (CD)
{"PUFFIN", MT_FLICKY_10}, // Wicky (CD)
{"COW", MT_FLICKY_11}, // Macky (SRB2)
{"RAT", MT_FLICKY_12}, // Micky (2)
{"BEAR", MT_FLICKY_13}, // Becky (2)
{"DOVE", MT_FLICKY_14}, // Docky (CD)
{"CAT", MT_FLICKY_15}, // Nyannyan (Flicky)
{"CANARY", MT_FLICKY_16}, // Lucky (CD)
{"a", 0}, // End of normal flickies - a lower case character so will never fastcmp valid with uppercase tmp
//{"FLICKER", MT_FLICKER}, // Flacky (SRB2)
{"SPIDER", MT_SECRETFLICKY_01}, // Sticky (SRB2)
{"BAT", MT_SECRETFLICKY_02}, // Backy (SRB2)
{"SEED", MT_SEED}, // Seed (CD)
{NULL, 0}
};
// IMPORTANT!
// DO NOT FORGET TO SYNC THIS LIST WITH THE ACTIONNUM ENUM IN INFO.H
actionpointer_t actionpointers[] =
@ -3885,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",
@ -6428,6 +6395,7 @@ struct int_const_s const INT_CONST[] = {
// Global emblem var flags
{"GE_NOTMEDAL", GE_NOTMEDAL},
{"GE_TIMED", GE_TIMED},
{"GE_FOLLOWER", GE_FOLLOWER},
// Map emblem var flags
{"ME_ENCORE",ME_ENCORE},

View file

@ -41,8 +41,6 @@ struct flickytypes_s {
const mobjtype_t type;
};
#define MAXFLICKIES 64
/** Action pointer for reading actions from Dehacked lumps.
*/
struct actionpointer_t

View file

@ -386,6 +386,7 @@ struct staffbrief_t
};
#define MAXMUSNAMES 3 // maximum definable music tracks per level
#define MAXHEADERFOLLOWERS 32
/** Map header information.
*/
@ -458,9 +459,9 @@ struct mapheader_t
boolean use_light_angle; ///< When false, wall lighting is evenly distributed. When true, wall lighting is directional.
angle_t light_angle; ///< Angle of directional wall lighting.
// Freed animal information
UINT8 numFlickies; ///< Internal. For freed flicky support.
mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
// Audience information
UINT8 numFollowers; ///< Internal. For audience support.
INT16 *followers; ///< List of audience followers in this level. Allocated dynamically for space reasons. Be careful.
// Script information
char runsoc[33]; ///< SOC to execute at start of level (32 character limit instead of 63)

View file

@ -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

View file

@ -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,

View file

@ -369,7 +369,10 @@ void K_HandleFollower(player_t *player)
{
// finally, add a cool floating effect to the z height.
// not stolen from k_kart I swear!!
fixed_t sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK));
fixed_t sine = FixedMul(fl.bobamp,
FINESINE(((
FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * leveltime
) >> ANGLETOFINESHIFT) & FINEMASK));
sz += FixedMul(player->mo->scale, sine) * P_MobjFlip(player->mo);
break;
}

View file

@ -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_AudienceInit(mobj_t * mobj, mapthing_t *mthing, INT32 followerpick);
void Obj_AudienceThink(mobj_t * mobj, boolean focusonplayer);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -132,6 +132,7 @@ struct conditionset_t
// Global emblem flags
#define GE_NOTMEDAL 1 // Doesn't count towards number of medals
#define GE_TIMED 2 // Disappears after var time
#define GE_FOLLOWER 4 // Takes on the appearance of a Follower in (string)var2
// Map emblem flags
#define ME_ENCORE 1 // Achieve in Encore
@ -147,7 +148,9 @@ struct emblem_t
UINT16 color; ///< skincolor to use
INT32 flags; ///< GE or ME constants
INT32 var; ///< If needed, specifies extra information
INT32 var2; ///< Ditto
char *stringVar; ///< String version
char *stringVar2; ///< Ditto
};
// Unlockable information

View file

@ -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

View file

@ -15,4 +15,5 @@ target_sources(SRB2SDL2 PRIVATE
loops.c
drop-target.c
ring-shooter.c
audience.c
)

308
src/objects/audience.c Normal file
View file

@ -0,0 +1,308 @@
#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
// The following cannot be used due to conflicts with MT_EMBLEM.
//#define audience_emblem_reserved_1(o) ((o)->reactiontime)
#define audience_mainstate(o) ((o)->cvmem)
#define audience_bobamp(o) ((o)->movefactor)
#define audience_bobspeed(o) ((o)->cusval)
#define audience_animoffset(o) ((o)->threshold)
#define audience_focusplayer(o) ((o)->lastlook)
#define audience_focusdelay(o) ((o)->movecount)
void
Obj_AudienceInit
( mobj_t * mobj,
mapthing_t *mthing,
INT32 followerpick)
{
INT16 *reflist = NULL;
INT16 tempreflist[MAXHEADERFOLLOWERS];
UINT8 numref = 0;
audience_mainstate(mobj) = S_NULL;
// Pick follower
if (mthing != NULL)
{
if (mthing->stringargs[0] != NULL)
{
// From mapthing
char *stringcopy = Z_StrDup(mthing->stringargs[0]);
char *tok = strtok(stringcopy, " ,");
char *c; // for erasing underscores
numref = 0;
while (tok && numref < MAXHEADERFOLLOWERS)
{
// Match follower name conversion
for (c = tok; *c; c++)
{
if (*c != '_')
continue;
*c = ' ';
}
if ((tempreflist[numref++] = K_FollowerAvailable(tok)) == -1)
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower \"%s\" is invalid!\n", sizeu1(mthing-mapthings), 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
{
mobj->destscale = FixedMul(3*mobj->destscale, followers[followerpick].scale);
P_SetScale(mobj, mobj->destscale);
audience_mainstate(mobj) = followers[followerpick].followstate;
P_SetMobjState(mobj, audience_mainstate(mobj));
if (P_MobjWasRemoved(mobj))
return;
// The following is derived from the default bobamp
if (mobj->type != MT_EMBLEM && !(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
if (mthing != NULL)
{
UINT16 colorpick = SKINCOLOR_NONE;
if (mthing->stringargs[1] != NULL)
{
if (!stricmp("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)
{
if ((tempreflist[numref++] = R_GetColorByName(tok)) == SKINCOLOR_NONE)
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower color \"%s\" is invalid!\n", sizeu1(mthing-mapthings), tok);
tok = strtok(NULL, " ");
}
Z_Free(stringcopy);
if (numref)
{
colorpick = tempreflist[P_RandomKey(PR_RANDOMAUDIENCE, numref)];
}
}
}
if (colorpick == SKINCOLOR_NONE
|| (colorpick >= numskincolors
&& colorpick != FOLLOWERCOLOR_MATCH
&& colorpick != FOLLOWERCOLOR_OPPOSITE))
{
colorpick = followers[followerpick].defaultcolor;
}
if (colorpick >= numskincolors)
{
colorpick = P_RandomKey(PR_RANDOMAUDIENCE, numskincolors-1)+1;
}
mobj->color = colorpick;
}
}
void
Obj_AudienceThink
( mobj_t * mobj,
boolean focusonplayer)
{
if (audience_mainstate(mobj) == S_NULL)
{
// Uninitialised, don't do anything funny.
return;
}
if (focusonplayer == true)
{
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)
{
angle_t diff = R_PointToAngle2(
mobj->x,
mobj->y,
players[audience_focusplayer(mobj)].mo->x,
players[audience_focusplayer(mobj)].mo->y
) - mobj->angle;
boolean reverse = (diff >= ANGLE_180);
if (reverse)
diff = InvAngle(diff);
if (diff > (ANG1*5))
diff /= 5;
if (reverse)
diff = InvAngle(diff);
mobj->angle += diff;
}
}
if (mobj->flags & MF_NOGRAVITY)
{
// This horrible calculation was inherited from k_follower.c
mobj->sprzoff = FixedMul(audience_bobamp(mobj),
FINESINE(((
FixedMul(4 * M_TAU_FIXED, audience_bobspeed(mobj))
* (leveltime + audience_animoffset(mobj))
) >> ANGLETOFINESHIFT) & FINEMASK));
// Offset to not go through floor...
if (mobj->type == MT_EMBLEM)
{
; // ...unless it's important to keep a centered hitbox
}
else if (mobj->flags2 & MF2_OBJECTFLIP)
{
mobj->sprzoff -= audience_bobamp(mobj);
}
else
{
mobj->sprzoff += audience_bobamp(mobj);
}
}
else if (audience_animoffset(mobj) > 0)
{
// Skipped frames at spawn for offset in jumping
audience_animoffset(mobj)--;
}
else if (audience_bobamp(mobj) == 0)
{
// Just sit there
;
}
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));
}
}

View file

@ -10234,13 +10234,8 @@ mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz
if (!flickytype)
{
if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
return NULL;
else
{
INT32 prandom = P_RandomKey(PR_UNDEFINED, mapheaderinfo[gamemap-1]->numFlickies);
flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
}
// The flicky list system has been removed, so no backups are possible.
return NULL;
}
if (moveforward)

View file

@ -40,6 +40,7 @@
#include "k_kart.h"
#include "k_battle.h"
#include "k_color.h"
#include "k_follower.h"
#include "k_respawn.h"
#include "k_bot.h"
#include "k_terrain.h"
@ -7102,6 +7103,13 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
{
INT32 trans = 0;
if (mobj->flags2 & MF2_STRONGBOX)
{
Obj_AudienceThink(mobj, true);
if (P_MobjWasRemoved(mobj))
return false;
}
mobj->frame &= ~FF_TRANSMASK;
mobj->renderflags &= ~RF_TRANSMASK;
@ -10267,39 +10275,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_AudienceThink(mobj, !!(mobj->flags2 & MF2_AMBUSH));
}
}
@ -10814,41 +10792,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;
@ -12309,10 +12252,11 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
INT32 j;
emblem_t* emblem = M_GetLevelEmblems(gamemap);
skincolornum_t emcolor;
INT16 tagnum = Tag_FGet(&mthing->tags);
while (emblem)
{
if (emblem->type == ET_GLOBAL && emblem->tag == Tag_FGet(&mthing->tags))
if (emblem->type == ET_GLOBAL && emblem->tag == tagnum)
break;
emblem = M_GetLevelEmblems(-1);
@ -12320,7 +12264,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
if (!emblem)
{
CONS_Alert(CONS_WARNING, "P_SetupEmblem: No map emblem for map %d with tag %d found!\n", gamemap, Tag_FGet(&mthing->tags));
CONS_Alert(CONS_WARNING, "P_SetupEmblem: No map emblem for map %d with tag %d found!\n", gamemap, tagnum);
return false;
}
@ -12340,6 +12284,44 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
mobj->reactiontime = emblemlocations[j].var;
}
if (emblemlocations[j].flags & GE_FOLLOWER)
{
INT32 followerpick;
char testname[SKINNAMESIZE+1];
size_t i;
// match deh_soc readfollower()
for (i = 0; emblemlocations[j].stringVar2[i]; i++)
{
testname[i] = emblemlocations[j].stringVar2[i];
if (emblemlocations[j].stringVar2[i] == '_')
testname[i] = ' ';
}
testname[i] = '\0';
followerpick = K_FollowerAvailable(testname);
if (followerpick == -1)
{
CONS_Alert(CONS_WARNING, "P_SetupEmblem: Follower \"%s\" on emblem for map %d with tag %d not found!\n", emblemlocations[j].stringVar2, gamemap, tagnum);
return false;
}
// Signal that you are to behave like a follower
mobj->flags2 |= MF2_STRONGBOX;
if (followers[followerpick].mode == FOLLOWERMODE_GROUND)
{
mobj->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT);
}
// Set up data
Obj_AudienceInit(mobj, NULL, followerpick);
if (P_MobjWasRemoved(mobj))
{
CONS_Alert(CONS_WARNING, "P_SetupEmblem: Follower \"%s\" causes emblem for map %d with tag %d to be removed immediately!\n", emblemlocations[j].stringVar2, gamemap, tagnum);
return false;
}
}
return true;
}
@ -13217,6 +13199,25 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
}
break;
}
case MT_RANDOMAUDIENCE:
{
if (mthing->args[2] != 0)
{
mobj->flags |= MF_NOGRAVITY;
}
if (mthing->args[3] != 0)
{
mobj->flags2 |= MF2_AMBUSH;
}
Obj_AudienceInit(mobj, mthing, -1);
if (P_MobjWasRemoved(mobj))
return false;
break;
}
case MT_AAZTREE_HELPER:
{
fixed_t top = mobj->z;
@ -13420,6 +13421,9 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
break;
}
if (P_MobjWasRemoved(mobj))
return false;
if (mobj->flags & MF_BOSS)
{
if (mthing->args[1]) // No egg trap for this boss
@ -13441,7 +13445,12 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
mobj->destscale = FixedMul(mobj->destscale, mthing->scale);
if (!P_SetupSpawnedMapThing(mthing, mobj, &doangle))
{
if (P_MobjWasRemoved(mobj))
return NULL;
return mobj;
}
if (doangle)
{
@ -13669,7 +13678,8 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi
y + FixedMul(length, FINESINE(fineangle)),
z, MT_LOOPCENTERPOINT);
Obj_LinkLoopAnchor(loopanchor, loopcenter, mthing->args[0]);
if (!P_MobjWasRemoved(loopanchor))
Obj_LinkLoopAnchor(loopanchor, loopcenter, mthing->args[0]);
}
for (r = 0; r < numitems; r++)

View file

@ -347,31 +347,51 @@ FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg)
I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details.");
}
/** Sets a header's flickies to be equivalent to the original Freed Animals
/** Sets a header's followers to the default list
*
* \param i The header to set flickies for
* \param i The header to set followers for
*/
void P_SetDemoFlickies(INT16 i)
void P_SetDefaultHeaderFollowers(UINT16 i)
{
mapheaderinfo[i]->numFlickies = 5;
mapheaderinfo[i]->flickies = Z_Realloc(mapheaderinfo[i]->flickies, 5*sizeof(mobjtype_t), PU_STATIC, NULL);
mapheaderinfo[i]->flickies[0] = MT_FLICKY_02/*MT_BUNNY*/;
mapheaderinfo[i]->flickies[1] = MT_FLICKY_01/*MT_BIRD*/;
mapheaderinfo[i]->flickies[2] = MT_FLICKY_12/*MT_MOUSE*/;
mapheaderinfo[i]->flickies[3] = MT_FLICKY_11/*MT_COW*/;
mapheaderinfo[i]->flickies[4] = MT_FLICKY_03/*MT_CHICKEN*/;
static INT16 defaultfollowers[MAXHEADERFOLLOWERS];
static UINT8 validdefaultfollowers = 0;
if (validdefaultfollowers == 0)
{
const char *defaultfollowernames[] =
{
"Flicky",
"Chao",
"Motobuddy",
NULL
};
for (validdefaultfollowers = 0; defaultfollowernames[validdefaultfollowers]; validdefaultfollowers++)
{
defaultfollowers[validdefaultfollowers] = K_FollowerAvailable(defaultfollowernames[validdefaultfollowers]);
}
I_Assert(validdefaultfollowers != 0);
}
mapheaderinfo[i]->followers = Z_Realloc(mapheaderinfo[i]->followers, sizeof(INT16) * validdefaultfollowers, PU_STATIC, NULL);
for (mapheaderinfo[i]->numFollowers = 0; mapheaderinfo[i]->numFollowers < validdefaultfollowers; mapheaderinfo[i]->numFollowers++)
{
mapheaderinfo[i]->followers[mapheaderinfo[i]->numFollowers] = defaultfollowers[mapheaderinfo[i]->numFollowers];
}
}
/** Clears a header's flickies
/** Clears a header's followers
*
* \param i The header to clear flickies for
* \param i The header to clear followers for
*/
void P_DeleteFlickies(INT16 i)
void P_DeleteHeaderFollowers(UINT16 i)
{
if (mapheaderinfo[i]->flickies)
Z_Free(mapheaderinfo[i]->flickies);
mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->numFlickies = 0;
if (mapheaderinfo[i]->followers)
Z_Free(mapheaderinfo[i]->followers);
mapheaderinfo[i]->followers = NULL;
mapheaderinfo[i]->numFollowers = 0;
}
#define NUMLAPS_DEFAULT 3
@ -419,10 +439,10 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
mapheaderinfo[num]->light_contrast = 16;
mapheaderinfo[num]->use_light_angle = false;
mapheaderinfo[num]->light_angle = 0;
#if 1 // equivalent to "FlickyList = DEMO"
P_SetDemoFlickies(num);
#else // equivalent to "FlickyList = NONE"
P_DeleteFlickies(num);
#if 1 // equivalent to "Followers = DEFAULT"
P_SetDefaultHeaderFollowers(num);
#else
P_DeleteHeaderFollowers(num);
#endif
mapheaderinfo[num]->mapvisited = 0;
@ -486,7 +506,7 @@ void P_AllocMapHeader(INT16 i)
mapheaderinfo[i]->ghostCount = 0;
mapheaderinfo[i]->cup = NULL;
mapheaderinfo[i]->mainrecord = NULL;
mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->followers = NULL;
nummapheaders++;
}
P_ClearSingleMapHeaderInfo(i);
@ -1807,8 +1827,10 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *
size_t argnum = atol(param + 9);
if (argnum >= NUMMAPTHINGSTRINGARGS)
return;
mapthings[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL);
M_Memcpy(mapthings[i].stringargs[argnum], val, strlen(val) + 1);
size_t len = strlen(val);
mapthings[i].stringargs[argnum] = Z_Malloc(len + 1, PU_LEVEL, NULL);
M_Memcpy(mapthings[i].stringargs[argnum], val, len);
mapthings[i].stringargs[argnum][len] = '\0';
}
else if (fastncmp(param, "arg", 3) && strlen(param) > 3)
{
@ -6718,6 +6740,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)

View file

@ -144,8 +144,8 @@ boolean P_ApplyLightOffsetFine(UINT8 baselightlevel);
size_t P_PrecacheLevelFlats(void);
void P_AllocMapHeader(INT16 i);
void P_SetDemoFlickies(INT16 i);
void P_DeleteFlickies(INT16 i);
void P_SetDefaultHeaderFollowers(UINT16 i);
void P_DeleteHeaderFollowers(UINT16 i);
// Needed for NiGHTS
void P_ReloadRings(void);