From e1d6455446944c54299ab47d1766b47f0472de30 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Apr 2023 17:48:19 +0100 Subject: [PATCH 01/19] Instantiate Followers earlier in load process Surprise tool that will help us later --- src/d_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index e2e342d8d..1d07c9298 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -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 From ea8573a70a91507a8aa33000f1a36d03dc7d5bc3 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 22:57:03 +0100 Subject: [PATCH 02/19] ParseTextmapThingParameter: Tidier, more explicit guarantees for null-terminated stringarg handling --- src/p_setup.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 35010f715..9a9e4bc6a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1807,8 +1807,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) { From 3612c1afa853286377b11474b16bd4493f904ec4 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 22:57:41 +0100 Subject: [PATCH 03/19] K_HandleFollower: Add newlines to improve readability of sine bobbing --- src/k_follower.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_follower.c b/src/k_follower.c index f0a1d7748..daa7b9d0d 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -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; } From 9eacdb4be4f7b107afca9976dd151cafe041a77a Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 22:58:50 +0100 Subject: [PATCH 04/19] P_SetupSpawnedMapThing: Catch more P_MobjWasRemoved --- src/p_mobj.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index af2394298..78b6c592d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -13420,6 +13420,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 +13444,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 +13677,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++) From 5f15736626f920d5e650a887160071ff7a02661a Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 23:01:13 +0100 Subject: [PATCH 05/19] Mapheader Followers system Replaces Flicky List system from 2.2. Specify a list of comma-seperated Followers. Not used by any systems in this commit, but important to store the data for later. --- src/deh_soc.c | 72 ++++++++++++++---------------------------------- src/deh_tables.c | 25 ----------------- src/deh_tables.h | 2 -- src/doomstat.h | 7 +++-- src/p_enemy.c | 9 ++---- src/p_setup.c | 63 +++++++++++++++++++++++++++--------------- src/p_setup.h | 4 +-- 7 files changed, 70 insertions(+), 112 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index fea7df30e..38b2fae56 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -187,7 +187,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 +1042,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(UINT16) * 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); } } diff --git a/src/deh_tables.c b/src/deh_tables.c index b5746272d..d5fb08579 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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[] = diff --git a/src/deh_tables.h b/src/deh_tables.h index 816f09c8f..79e0fd8f6 100644 --- a/src/deh_tables.h +++ b/src/deh_tables.h @@ -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 diff --git a/src/doomstat.h b/src/doomstat.h index 72a3a8c71..71c46a09f 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -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. + UINT16 *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) diff --git a/src/p_enemy.c b/src/p_enemy.c index 59979ac9a..152ced8f3 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -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) diff --git a/src/p_setup.c b/src/p_setup.c index 9a9e4bc6a..ecce8d2fd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -347,31 +347,50 @@ 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", + 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(UINT16) * 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 +438,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 +505,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); diff --git a/src/p_setup.h b/src/p_setup.h index c743460ce..fd343f927 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -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); From a74b7995c9e7a5eeeb25b86c70709364330f31a5 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 23:10:59 +0100 Subject: [PATCH 06/19] 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 --- src/deh_tables.c | 8 -- src/info.c | 12 +- src/info.h | 8 -- src/k_objects.h | 4 + src/m_random.h | 2 + src/objects/CMakeLists.txt | 1 + src/objects/audience.c | 272 +++++++++++++++++++++++++++++++++++++ src/p_mobj.c | 78 ++--------- src/p_setup.c | 4 + 9 files changed, 295 insertions(+), 94 deletions(-) create mode 100644 src/objects/audience.c diff --git a/src/deh_tables.c b/src/deh_tables.c index d5fb08579..17f7cc819 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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", diff --git a/src/info.c b/src/info.c index cfa23907a..505ad5373 100644 --- a/src/info.c +++ b/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 diff --git a/src/info.h b/src/info.h index d80c9798a..3a73a5280 100644 --- a/src/info.h +++ b/src/info.h @@ -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, diff --git a/src/k_objects.h b/src/k_objects.h index 875f177a8..7c5436f83 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -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 diff --git a/src/m_random.h b/src/m_random.h index 786f5d053..febfd4689 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -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 diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 609f18de4..6442860cc 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -15,4 +15,5 @@ target_sources(SRB2SDL2 PRIVATE loops.c drop-target.c ring-shooter.c + audience.c ) diff --git a/src/objects/audience.c b/src/objects/audience.c new file mode 100644 index 000000000..6e8d734d1 --- /dev/null +++ b/src/objects/audience.c @@ -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)); + } +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 78b6c592d..09d5f9c24 100644 --- a/src/p_mobj.c +++ b/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; diff --git a/src/p_setup.c b/src/p_setup.c index ecce8d2fd..ec327a0c1 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -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) From 178fe3052de2b06392bda35047b5176c7c8d010b Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 23:22:03 +0100 Subject: [PATCH 07/19] Obj_RandomAudienceInit: Increase the scale to 3x instead of 2x --- src/objects/audience.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/audience.c b/src/objects/audience.c index 6e8d734d1..a3e1f6fd7 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -30,7 +30,7 @@ Obj_RandomAudienceInit UINT8 numref = 0; INT32 followerpick = 0; - P_SetScale(mobj, (mobj->destscale <<= 1)); + P_SetScale(mobj, (mobj->destscale *= 3)); audience_mainstate(mobj) = S_NULL; From d7547edf0559c4eed86ff5a6233925556dffb2d5 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Apr 2023 23:22:35 +0100 Subject: [PATCH 08/19] P_SetDefaultHeaderFollowers: Add Motobuddy to the list as Eggman deserves his homies --- src/p_setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_setup.c b/src/p_setup.c index ec327a0c1..5a2ca6bfd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -362,6 +362,7 @@ void P_SetDefaultHeaderFollowers(UINT16 i) { "Flicky", "Chao", + "Motobuddy", NULL }; From 1e5fadc9010ac50884e23cc7ac25ed81ca5a364e Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 15:09:07 +0100 Subject: [PATCH 09/19] Audience-related functions: Rename + adjust - Since they're not necessarily going to be randomised, make the prefix Obj_Audience instead of Obj_RandomAudience. - Externalise some properties that we may not want affecting all hypothetical uses. - Flag auto-application should be done externally, since this won't be general. - Focusing on player should be controlled by the function call, not an ambiguous flag. --- src/k_objects.h | 4 ++-- src/objects/audience.c | 25 +++++++++---------------- src/p_mobj.c | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/k_objects.h b/src/k_objects.h index 7c5436f83..0852100cc 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -115,8 +115,8 @@ 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); +void Obj_AudienceInit(mobj_t * mobj, mapthing_t *mthing, INT32 followerpick); +void Obj_AudienceThink(mobj_t * mobj, boolean focusonplayer); #ifdef __cplusplus } // extern "C" diff --git a/src/objects/audience.c b/src/objects/audience.c index a3e1f6fd7..8498426be 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -21,20 +21,21 @@ #define audience_focusdelay(o) ((o)->movecount) void -Obj_RandomAudienceInit +Obj_AudienceInit ( mobj_t * mobj, - mapthing_t *mthing) + mapthing_t *mthing, + INT32 followerpick) { UINT16 *reflist = NULL; UINT16 tempreflist[MAXHEADERFOLLOWERS]; UINT8 numref = 0; - INT32 followerpick = 0; P_SetScale(mobj, (mobj->destscale *= 3)); audience_mainstate(mobj) = S_NULL; // Pick follower + if (mthing != NULL) { if (mthing->stringargs[0] != NULL) { @@ -92,16 +93,6 @@ Obj_RandomAudienceInit 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) { @@ -122,6 +113,7 @@ Obj_RandomAudienceInit } // Handle colors + if (mthing != NULL) { UINT16 colorpick = SKINCOLOR_NONE; @@ -168,8 +160,9 @@ Obj_RandomAudienceInit } void -Obj_RandomAudienceThink -( mobj_t * mobj) +Obj_AudienceThink +( mobj_t * mobj, + boolean focusonplayer) { if (audience_mainstate(mobj) == S_NULL) { @@ -177,7 +170,7 @@ Obj_RandomAudienceThink return; } - if (mobj->flags2 & MF2_AMBUSH) + if (focusonplayer == true) { if (audience_focusdelay(mobj) == 0) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 09d5f9c24..584cda68e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10269,7 +10269,7 @@ void P_SceneryThinker(mobj_t *mobj) if (mobj->type == MT_RANDOMAUDIENCE) { - Obj_RandomAudienceThink(mobj); + Obj_AudienceThink(mobj, !!(mobj->flags2 & MF2_AMBUSH)); } } @@ -13154,7 +13154,17 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean } case MT_RANDOMAUDIENCE: { - Obj_RandomAudienceInit(mobj, mthing); + 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; From 66d8d8757e9e7eeafc3ee841f3eaab3fdbca0008 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 15:09:49 +0100 Subject: [PATCH 10/19] emblem_t: Add var2/stringVar2 --- src/deh_soc.c | 10 ++++++++++ src/m_cond.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/deh_soc.c b/src/deh_soc.c index 38b2fae56..54830f217 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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)); @@ -2201,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 diff --git a/src/m_cond.h b/src/m_cond.h index a53e66ad7..a34825bc2 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -147,7 +147,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 From 8878bf84651ab73586e5c4df374b9c0ce0bb276b Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 15:12:06 +0100 Subject: [PATCH 11/19] P_SetupEmblem: Only call Tag_FGet once, and store the result for loop iteration/error messages --- src/p_mobj.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 584cda68e..3b60dc6bb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12244,10 +12244,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); @@ -12255,7 +12256,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; } From 945a00df6e3ac42041d2f69a0e2ef6009c3b13a8 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 15:17:43 +0100 Subject: [PATCH 12/19] Permit emblem pickups that take on the visage of a Follower - If Global-type map emblem has GE_FOLLOWER, attempt to take on the appearance of the follower specified by var2. - Unlike MT_RANDOMAUDIENCE, this picks whether the object should be floating or hopping based on the specified mode of the source Follower. - Always chooses to face the nearest player. - Rearrange some properties affected by Obj_Audience/audience.c to not conflict with MT_EMBLEM --- src/deh_tables.c | 1 + src/m_cond.h | 1 + src/objects/audience.c | 25 ++++++++++++++++------- src/p_mobj.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 17f7cc819..5b2fa3f24 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6395,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}, diff --git a/src/m_cond.h b/src/m_cond.h index a34825bc2..9fdb5bd2e 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -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 diff --git a/src/objects/audience.c b/src/objects/audience.c index 8498426be..aba436013 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -10,10 +10,13 @@ #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)->cusval) -#define audience_bobspeed(o) ((o)->reactiontime) +#define audience_bobamp(o) ((o)->movefactor) +#define audience_bobspeed(o) ((o)->cusval) #define audience_animoffset(o) ((o)->threshold) @@ -231,17 +234,25 @@ Obj_AudienceThink if (mobj->flags & MF_NOGRAVITY) { - // This horrible calculation was inherited from k_follower.c, with only newlines (and a FRACUNIT offset) added + // This horrible calculation was inherited from k_follower.c mobj->sprzoff = FixedMul(audience_bobamp(mobj), - FRACUNIT + FINESINE((( + FINESINE((( FixedMul(4 * M_TAU_FIXED, audience_bobspeed(mobj)) * (leveltime + audience_animoffset(mobj)) ) >> ANGLETOFINESHIFT) & FINEMASK)); - // Gravity - if (mobj->flags2 & MF2_OBJECTFLIP) + // Offset to not go through floor... + if (mobj->type == MT_EMBLEM) { - mobj->sprzoff = -mobj->sprzoff; + ; // ...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) diff --git a/src/p_mobj.c b/src/p_mobj.c index 3b60dc6bb..49dc1e992 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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; @@ -12276,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; + } + + // 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; + } + + // 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); + } + } + return true; } From af288571bd82af7534d96397d7d5072d75f13d19 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 17:37:26 +0100 Subject: [PATCH 13/19] P_SetupEmblem: Correct flags *before* Obj_AudienceInit --- src/p_mobj.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 49dc1e992..1d7869812 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12306,6 +12306,13 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) 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)) @@ -12313,13 +12320,6 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *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; } - - // 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); - } } return true; From 91f4169212299e554ecd073d37e7db5ddf06fa2b Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 17:42:11 +0100 Subject: [PATCH 14/19] Follower-mimicing emblems: Permit them to just sit there if their defined bobamp is 0 This is unlike audience members, which have a minimum jump height --- src/objects/audience.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/objects/audience.c b/src/objects/audience.c index aba436013..2218e744a 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -97,7 +97,7 @@ Obj_AudienceInit return; // The following is derived from the default bobamp - if (!(mobj->flags & MF_NOGRAVITY) && followers[followerpick].bobamp < 4*FRACUNIT) + if (mobj->type != MT_EMBLEM && !(mobj->flags & MF_NOGRAVITY) && followers[followerpick].bobamp < 4*FRACUNIT) { audience_bobamp(mobj) = 4*mobj->scale; } @@ -260,6 +260,11 @@ Obj_AudienceThink // 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) From a4a5752df07615c48ef7ef0d74c93b3693e29355 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 18:24:51 +0100 Subject: [PATCH 15/19] Obj_AudienceThink: Ease the angle change --- src/objects/audience.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/objects/audience.c b/src/objects/audience.c index 2218e744a..e0f58dc5c 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -223,12 +223,25 @@ Obj_AudienceThink if (audience_focusplayer(mobj) < MAXPLAYERS && audience_focusplayer(mobj) >= 0) { - mobj->angle = R_PointToAngle2( + 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; } } From 98288fd289a4fc3b18cd969a259d1fce62f044d7 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 20:20:51 +0100 Subject: [PATCH 16/19] Obj_AudienceInit: Incorporate follower scale into multipliciation --- src/objects/audience.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/objects/audience.c b/src/objects/audience.c index e0f58dc5c..4e74aaaab 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -33,8 +33,6 @@ Obj_AudienceInit UINT16 tempreflist[MAXHEADERFOLLOWERS]; UINT8 numref = 0; - P_SetScale(mobj, (mobj->destscale *= 3)); - audience_mainstate(mobj) = S_NULL; // Pick follower @@ -90,6 +88,9 @@ Obj_AudienceInit // 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)); From 2f8b8cf532ccfb46c488691ed66d0d547eaa203d Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 20:52:13 +0100 Subject: [PATCH 17/19] Follower lists: Fix typing Should consistently be signed INT16, not unsigned UINT16 --- src/deh_soc.c | 2 +- src/doomstat.h | 2 +- src/objects/audience.c | 4 ++-- src/p_setup.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 54830f217..751c3c07a 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1079,7 +1079,7 @@ void readlevelheader(MYFILE *f, char * name) if (mapheaderinfo[num]->numFollowers) { - size_t newsize = sizeof(UINT16) * mapheaderinfo[num]->numFollowers; + 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]->followers, tmpfollowers, newsize); diff --git a/src/doomstat.h b/src/doomstat.h index 71c46a09f..f7e13c93b 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -461,7 +461,7 @@ struct mapheader_t // Audience information UINT8 numFollowers; ///< Internal. For audience support. - UINT16 *followers; ///< List of audience followers in this level. Allocated dynamically for space reasons. Be careful. + 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) diff --git a/src/objects/audience.c b/src/objects/audience.c index 4e74aaaab..466dd01b2 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -29,8 +29,8 @@ Obj_AudienceInit mapthing_t *mthing, INT32 followerpick) { - UINT16 *reflist = NULL; - UINT16 tempreflist[MAXHEADERFOLLOWERS]; + INT16 *reflist = NULL; + INT16 tempreflist[MAXHEADERFOLLOWERS]; UINT8 numref = 0; audience_mainstate(mobj) = S_NULL; diff --git a/src/p_setup.c b/src/p_setup.c index 5a2ca6bfd..7efda662b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -374,7 +374,7 @@ void P_SetDefaultHeaderFollowers(UINT16 i) I_Assert(validdefaultfollowers != 0); } - mapheaderinfo[i]->followers = Z_Realloc(mapheaderinfo[i]->followers, sizeof(UINT16) * validdefaultfollowers, PU_STATIC, NULL); + 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++) { From c7cc730c351416f1df97e2052ba95cc34f8aaefd Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 21:06:21 +0100 Subject: [PATCH 18/19] Obj_AudienceInit: Improve color handling - Catch all invalid colors with default randomisation - Catch invalid colors more thoroughly - Report typos to the user --- src/objects/audience.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/objects/audience.c b/src/objects/audience.c index 466dd01b2..f924878a4 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -123,7 +123,7 @@ Obj_AudienceInit if (mthing->stringargs[1] != NULL) { - if (!strcmp("Random", mthing->stringargs[1])) + if (!stricmp("Random", mthing->stringargs[1])) { colorpick = FOLLOWERCOLOR_MATCH; } @@ -135,7 +135,8 @@ Obj_AudienceInit numref = 0; while (tok && numref < MAXHEADERFOLLOWERS) { - tempreflist[numref++] = R_GetColorByName(tok); + 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, " "); } @@ -148,13 +149,15 @@ Obj_AudienceInit } } - if (colorpick == SKINCOLOR_NONE) + if (colorpick == SKINCOLOR_NONE + || (colorpick >= numskincolors + && colorpick != FOLLOWERCOLOR_MATCH + && colorpick != FOLLOWERCOLOR_OPPOSITE)) { colorpick = followers[followerpick].defaultcolor; } - if (colorpick == FOLLOWERCOLOR_MATCH - || colorpick == FOLLOWERCOLOR_OPPOSITE) + if (colorpick >= numskincolors) { colorpick = P_RandomKey(PR_RANDOMAUDIENCE, numskincolors-1)+1; } From 3dd44961a0e685a4170a4af777b039aa9a46bad4 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Apr 2023 21:07:14 +0100 Subject: [PATCH 19/19] Obj_AudienceInit: Improve follower name processing - Convert underscores to spaces for string comparison - Report typoes to the user --- src/objects/audience.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/objects/audience.c b/src/objects/audience.c index f924878a4..631429d05 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -43,11 +43,21 @@ Obj_AudienceInit // From mapthing char *stringcopy = Z_StrDup(mthing->stringargs[0]); char *tok = strtok(stringcopy, " ,"); + char *c; // for erasing underscores numref = 0; while (tok && numref < MAXHEADERFOLLOWERS) { - tempreflist[numref++] = K_FollowerAvailable(tok); + // 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, " ,"); }