From c5c78df94647a01a69f8c4297f64e0f576b2dd3f Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 12 Jul 2023 13:17:09 +0100 Subject: [PATCH 01/36] Linedef Action 460 (Award Rings) now supports negative ring counts UDMF now has a native ring drainer effect. Also fixes the WriteTextmap warning for the deprecated sector effect, which should have been encouraging you to use this action, not action 462!? --- src/p_setup.c | 2 +- src/p_spec.c | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 27b771df1..985414775 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2379,7 +2379,7 @@ static void P_WriteTextmap(void) { case 9: case 10: - CONS_Alert(CONS_WARNING, M_GetText("Sector %s has ring drainer effect, which is not supported in UDMF. Use action 462 instead.\n"), sizeu1(i)); + CONS_Alert(CONS_WARNING, M_GetText("Sector %s has ring drainer effect, which is not supported in UDMF. Use action 460 instead.\n"), sizeu1(i)); break; default: break; diff --git a/src/p_spec.c b/src/p_spec.c index d43c6e07b..d03505ea4 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4194,17 +4194,30 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha { INT16 rings = args[0]; INT32 delay = args[1]; - if (mo && mo->player) + if ( + mo && mo->player // Player + && rings != 0 // Any effect + && (delay <= 0 || !(leveltime % delay)) // Timing + ) { - // Don't award rings while SPB is targetting you - if (mo->player->pflags & PF_RINGLOCK) - return false; - - if (delay <= 0 || !(leveltime % delay)) + if (rings > 0) { + // Don't award rings while SPB is targetting you + if (mo->player->pflags & PF_RINGLOCK) + return false; + // args[2]: don't cap rings to 20 K_AwardPlayerRings(mo->player, rings, args[2]); } + else + { + // Don't push you below baseline + if (mo->player->rings < 0) + return false; + + mo->player->rings--; + S_StartSound(mo, sfx_antiri); + } } } break; From ba5047459c2f5bac33c3e3d296815a55082ad3f5 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 12 Jul 2023 16:06:50 +0100 Subject: [PATCH 02/36] Remove last remnants of Action 434 ("Custom Power") Had no behaviour, but was being processed --- src/p_setup.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 985414775..50eb148cd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1229,7 +1229,6 @@ static void P_LoadSidedefs(UINT8 *data) case 335: // Trigger linedef executor: Object dye - Each time case 336: // Trigger linedef executor: Object dye - Once case 425: // Calls P_SetMobjState on calling mobj - case 434: // Custom Power case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors case 443: // Calls a named Lua function case 459: // Control text prompt (named tag) @@ -5671,20 +5670,6 @@ static void P_ConvertBinaryLinedefTypes(void) case 433: //Enable/disable gravity flip lines[i].args[0] = !!(lines[i].flags & ML_NOCLIMB); break; - case 434: //Award power-up - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } - if (lines[i].sidenum[1] != 0xffff && lines[i].flags & ML_BLOCKPLAYERS) // read power from back sidedef - { - lines[i].stringargs[1] = Z_Malloc(strlen(sides[lines[i].sidenum[1]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[1], sides[lines[i].sidenum[1]].text, strlen(sides[lines[i].sidenum[1]].text) + 1); - } - else - P_WriteConstant((lines[i].flags & ML_NOCLIMB) ? -1 : (sides[lines[i].sidenum[0]].textureoffset >> FRACBITS), &lines[i].stringargs[1]); - break; case 435: //Change plane scroller direction lines[i].args[0] = tag; lines[i].args[1] = R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y) >> FRACBITS; From 36e0a23c09e58516b35c73ad73ab9467705638cb Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 12 Jul 2023 16:32:23 +0100 Subject: [PATCH 03/36] Remove console script support from maps The linedef's behaviour was broken horribly by long map names, and it's not worth the effort to fix it for the following reasons. - It was considered a security vulnerability to have free access to the console when it was written. - The game literally had a cvar to disable running console scripts. That's "I am willingly distributing ActiveX Word Documents in 2023" levels of foolhardery. - Anything GOOD it can do, both Lua and ACS can do better. --- src/d_netcmd.c | 3 --- src/d_netcmd.h | 1 - src/deh_soc.c | 7 ------- src/deh_tables.c | 1 - src/doomstat.h | 2 +- src/p_setup.c | 31 ------------------------------- src/p_spec.c | 12 ------------ 7 files changed, 1 insertion(+), 56 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 823099cf6..059a39b7a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -547,8 +547,6 @@ consvar_t cv_inttime = CVAR_INIT ("inttime", "10", CV_SAVE|CV_NETVAR, inttime_co static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Same"}, {1, "Next"}, {2, "Random"}, {3, "Vote"}, {0, NULL}}; consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Vote", CV_NETVAR, advancemap_cons_t, NULL); -consvar_t cv_runscripts = CVAR_INIT ("runscripts", "Yes", 0, CV_YesNo, NULL); - consvar_t cv_pause = CVAR_INIT ("pausepermission", "Server", CV_SAVE|CV_NETVAR, pause_cons_t, NULL); consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange); @@ -795,7 +793,6 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_startinglives); CV_RegisterVar(&cv_countdowntime); - CV_RegisterVar(&cv_runscripts); CV_RegisterVar(&cv_overtime); CV_RegisterVar(&cv_pause); CV_RegisterVar(&cv_mute); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 154c09717..eb1e5232b 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -70,7 +70,6 @@ extern consvar_t cv_scrambleonchange; extern consvar_t cv_netstat; extern consvar_t cv_countdowntime; -extern consvar_t cv_runscripts; extern consvar_t cv_mute; extern consvar_t cv_pause; diff --git a/src/deh_soc.c b/src/deh_soc.c index 48f4f6828..84b49aa76 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1296,13 +1296,6 @@ void readlevelheader(MYFILE *f, char * name) } } // Individual triggers for level flags, for ease of use (and 2.0 compatibility) - else if (fastcmp(word, "SCRIPTISFILE")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num]->levelflags |= LF_SCRIPTISFILE; - else - mapheaderinfo[num]->levelflags &= ~LF_SCRIPTISFILE; - } else if (fastcmp(word, "NOZONE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') diff --git a/src/deh_tables.c b/src/deh_tables.c index 59af77da5..3db11e57e 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6452,7 +6452,6 @@ struct int_const_s const INT_CONST[] = { {"RF_GHOSTLYMASK",RF_GHOSTLYMASK}, // Level flags - {"LF_SCRIPTISFILE",LF_SCRIPTISFILE}, {"LF_NOZONE",LF_NOZONE}, {"LF_SECTIONRACE",LF_SECTIONRACE}, {"LF_SUBTRACTNUM",LF_SUBTRACTNUM}, diff --git a/src/doomstat.h b/src/doomstat.h index 2fc4a3878..6c8bc76db 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -527,7 +527,7 @@ struct mapheader_t }; // level flags -#define LF_SCRIPTISFILE (1<<0) ///< True if the script is a file, not a lump. +//#define LF_(this slot is free) (1<<0) #define LF_NOZONE (1<<1) ///< Don't include "ZONE" on level title #define LF_SECTIONRACE (1<<2) ///< Section race level #define LF_SUBTRACTNUM (1<<3) ///< Use subtractive position number (for bright levels) diff --git a/src/p_setup.c b/src/p_setup.c index 50eb148cd..1506d18c0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7515,34 +7515,6 @@ void P_RespawnThings(void) } #endif -static void P_RunLevelScript(const char *scriptname) -{ - if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SCRIPTISFILE)) - { - lumpnum_t lumpnum; - char newname[9]; - - strncpy(newname, scriptname, 8); - - newname[8] = '\0'; - - lumpnum = W_CheckNumForName(newname); - - if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0) - { - CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname); - return; - } - - COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); - } - else - { - COM_BufAddText(va("exec %s\n", scriptname)); - } - COM_BufExecute(); // Run it! -} - static void P_ResetSpawnpoints(void) { UINT8 i; @@ -7994,9 +7966,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (mapheaderinfo[gamemap-1]->runsoc[0] != '#') P_RunSOC(mapheaderinfo[gamemap-1]->runsoc); - if (cv_runscripts.value && mapheaderinfo[gamemap-1]->scriptname[0] != '#') - P_RunLevelScript(mapheaderinfo[gamemap-1]->scriptname); - P_InitLevelSettings(); for (i = 0; i <= r_splitscreen; i++) diff --git a/src/p_spec.c b/src/p_spec.c index d03505ea4..aa1e88256 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2970,18 +2970,6 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha P_PlaySFX(stringargs[0] ? get_number(stringargs[0]) : sfx_None, mo, callsec, args[3], args[1], args[2]); break; - case 415: // Run a script - if (cv_runscripts.value) - { - lumpnum_t lumpnum = W_CheckNumForName(stringargs[0]); - - if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0) - CONS_Debug(DBG_SETUP, "Line type 415 Executor: script lump %s not found/not valid.\n", stringargs[0]); - else - COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); - } - break; - case 416: // Spawn adjustable fire flicker TAG_ITER_SECTORS(args[0], secnum) P_SpawnAdjustableFireFlicker(§ors[secnum], args[2], From b61701a1bca09d27d60057ac08e4572be443379e Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 12 Jul 2023 17:13:38 +0100 Subject: [PATCH 04/36] Tidier conversion of string properties to UDMF Previously, concatenated texture field strings were turned to text, then had that string run through get_number, then had it converted back into a string to become a stringarg. Now, the concatenated string is copied directly to the relevant stringarg. This fixes an issue where a map with object or state properties would have "context drift" - breaking when the object or state list changed, due to differently ordered freeslots, or new hardcoded objects. This is also made consistent and with less repeated code via the addition of multile P_Write functions to replace both P_WriteConstant and multiple copypasted blocks of zone allocation and memcpy. - P_WriteDuplicateText -- fresh zone memory copy with PU_LEVEL - P_WriteSkinColor -- converts ID to SKINCOLOR_ name, then runs it through P_WriteDuplicateText - P_WriteSfx -- converts ID to SFX_ name, then runs it through P_WriteDuplicateText In order to avoid making p_setup.c dependent on `deh_tables.h`, object type 1202 simply artifically produces the expected range of MT_ROCKCRUMBLE1 to MT_ROCKCRUMBLE16. It was technically possible to produce unexpected effects with extremely high offsets, but if your map was doing something like that, you're intelligent enough to go back and swap the MT_ constant out with your intended goal. --- src/p_setup.c | 225 +++++++++++++++++++++----------------------------- 1 file changed, 95 insertions(+), 130 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 1506d18c0..1517c4d9c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1108,6 +1108,52 @@ static void P_InitializeSidedef(side_t *sd) sd->colormap_data = NULL; } +/* -- Reference implementation +static void P_WriteConstant(INT32 constant, char **target) +{ + char buffer[12]; + size_t len; + + sprintf(buffer, "%d", constant); + len = strlen(buffer) + 1; + *target = Z_Malloc(len, PU_LEVEL, NULL); + M_Memcpy(*target, buffer, len); +} */ + +static void P_WriteDuplicateText(const char *text, char **target) +{ + if (text == NULL || text[0] == '\0') + return; + + size_t len = strlen(text) + 1; + *target = Z_Malloc(len, PU_LEVEL, NULL); + M_Memcpy(*target, text, len); +} + +static void P_WriteSkincolor(INT32 constant, char **target) +{ + if (constant <= SKINCOLOR_NONE + || constant >= (INT32)numskincolors) + return; + + P_WriteDuplicateText( + va("SKINCOLOR_%s", skincolors[constant].name), + target + ); +} + +static void P_WriteSfx(INT32 constant, char **target) +{ + if (constant <= sfx_None + || constant >= (INT32)sfxfree) + return; + + P_WriteDuplicateText( + va("sfx_%s", S_sfx[constant].name), + target + ); +} + static void P_LoadSidedefs(UINT8 *data) { mapsidedef_t *msd = (mapsidedef_t*)data; @@ -1197,8 +1243,8 @@ static void P_LoadSidedefs(UINT8 *data) char process[8 + 1]; M_Memcpy(process, msd->toptexture, 8); process[8] = '\0'; - sd->text = Z_Malloc(strlen(process) + 1, PU_LEVEL, NULL); - M_Memcpy(sd->text, process, strlen(process) + 1); + + P_WriteDuplicateText(process, &sd->text); } break; } @@ -1207,21 +1253,13 @@ static void P_LoadSidedefs(UINT8 *data) case 14: // Bustable block parameters case 15: // Fan particle spawner parameters { - char process[8*3+1]; - memset(process,0,8*3+1); - sd->toptexture = sd->midtexture = sd->bottomtexture = 0; - if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0') + if (msd->toptexture[7] == '\0' && strcasecmp(msd->toptexture, "MT_NULL") == 0) + { + // Don't bulk the conversion with irrelevant types break; - else - M_Memcpy(process,msd->toptexture,8); - if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0') - M_Memcpy(process+strlen(process), msd->midtexture, 8); - if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0') - M_Memcpy(process+strlen(process), msd->bottomtexture, 8); - sd->toptexture = get_number(process); - break; + } } - + // FALLTHRU case 331: // Trigger linedef executor: Skin - Continuous case 332: // Trigger linedef executor: Skin - Each time case 333: // Trigger linedef executor: Skin - Once @@ -1250,8 +1288,8 @@ static void P_LoadSidedefs(UINT8 *data) M_Memcpy(process+strlen(process), msd->midtexture, 8); if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0') M_Memcpy(process+strlen(process), msd->bottomtexture, 8); - sd->text = Z_Malloc(strlen(process)+1, PU_LEVEL, NULL); - M_Memcpy(sd->text, process, strlen(process)+1); + + P_WriteDuplicateText(process, &sd->text); break; } @@ -3175,13 +3213,17 @@ static void P_ProcessLinedefsAfterSidedefs(void) case 443: // Calls a named Lua function if (sides[ld->sidenum[0]].text) { - size_t len = strlen(sides[ld->sidenum[0]].text) + 1; + size_t len[2]; + len[0] = strlen(sides[ld->sidenum[0]].text) + 1; + len[1] = 0; + if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text) - len += strlen(sides[ld->sidenum[1]].text); - ld->text = Z_Malloc(len, PU_LEVEL, NULL); - M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text) + 1); - if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text) - M_Memcpy(ld->text + strlen(ld->text) + 1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text) + 1); + len[1] = strlen(sides[ld->sidenum[1]].text); + + ld->text = Z_Malloc(len[0] + len[1], PU_LEVEL, NULL); + M_Memcpy(ld->text, sides[ld->sidenum[0]].text, len[0]); + if (len[1]) + M_Memcpy(ld->text + len[0], sides[ld->sidenum[1]].text, len[1] + 1); } break; case 447: // Change colormap @@ -4337,14 +4379,6 @@ static void P_AddBinaryMapTags(void) } } -static void P_WriteConstant(INT32 constant, char **target) -{ - char buffer[12]; - sprintf(buffer, "%d", constant); - *target = Z_Malloc(strlen(buffer) + 1, PU_LEVEL, NULL); - M_Memcpy(*target, buffer, strlen(buffer) + 1); -} - static line_t *P_FindPointPushLine(taglist_t *list) { INT32 i, l; @@ -4584,7 +4618,7 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; lines[i].args[1] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS; lines[i].args[2] = !!(lines[i].flags & ML_SKEWTD); - P_WriteConstant(sides[lines[i].sidenum[0]].toptexture, &lines[i].stringargs[0]); + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 16: //Minecart parameters lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; @@ -5059,7 +5093,7 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[2] = 16; } if (lines[i].flags & ML_MIDSOLID) - P_WriteConstant(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS, &lines[i].stringargs[0]); + P_WriteSfx(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS, &lines[i].stringargs[0]); if (lines[i].flags & ML_SKEWTD) // Kart Z delay. Yes, it used the same field as the above. lines[i].args[3] = (unsigned)(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS); break; @@ -5328,11 +5362,7 @@ static void P_ConvertBinaryLinedefTypes(void) else lines[i].args[0] = TMT_CONTINUOUS; lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB); - if (lines[i].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1); - } + P_WriteDuplicateText(lines[i].text, &lines[i].stringargs[0]); lines[i].special = 331; break; case 334: // Object dye - continuous @@ -5345,11 +5375,7 @@ static void P_ConvertBinaryLinedefTypes(void) else lines[i].args[0] = TMT_CONTINUOUS; lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB); - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); lines[i].special = 334; break; case 337: //Emerald check - continuous @@ -5488,11 +5514,7 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[5] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0; lines[i].args[6] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1; lines[i].args[7] = sides[lines[i].sidenum[0]].bottomtexture; - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 414: //Play sound effect lines[i].args[3] = tag; @@ -5532,37 +5554,8 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[2] = TMSL_EVERYONE; } } - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; - case 415: //Run script - { - INT32 scrnum; - - lines[i].stringargs[0] = Z_Malloc(9, PU_LEVEL, NULL); - strcpy(lines[i].stringargs[0], G_BuildMapName(gamemap)); - lines[i].stringargs[0][0] = 'S'; - lines[i].stringargs[0][1] = 'C'; - lines[i].stringargs[0][2] = 'R'; - - scrnum = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; - if (scrnum < 0 || scrnum > 999) - { - scrnum = 0; - lines[i].stringargs[0][5] = lines[i].stringargs[0][6] = lines[i].stringargs[0][7] = '0'; - } - else - { - lines[i].stringargs[0][5] = (char)('0' + (char)((scrnum / 100))); - lines[i].stringargs[0][6] = (char)('0' + (char)((scrnum % 100) / 10)); - lines[i].stringargs[0][7] = (char)('0' + (char)(scrnum % 10)); - } - lines[i].stringargs[0][8] = '\0'; - break; - } case 416: //Start adjustable flickering light case 417: //Start adjustable pulsating light case 602: //Adjustable pulsating light @@ -5628,11 +5621,7 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB); break; case 425: //Change object state - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 426: //Stop object lines[i].args[0] = !!(lines[i].flags & ML_NOCLIMB); @@ -5696,31 +5685,17 @@ static void P_ConvertBinaryLinedefTypes(void) break; case 442: //Change object type state lines[i].args[2] = tag; - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); if (lines[i].sidenum[1] == 0xffff) lines[i].args[3] = 1; else { lines[i].args[3] = 0; - if (sides[lines[i].sidenum[1]].text) - { - lines[i].stringargs[1] = Z_Malloc(strlen(sides[lines[i].sidenum[1]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[1], sides[lines[i].sidenum[1]].text, strlen(sides[lines[i].sidenum[1]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[1]].text, &lines[i].stringargs[1]); } break; case 443: //Call Lua function - if (lines[i].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1); - } - else - CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in the front texture fields)\n", sizeu1(i)); + P_WriteDuplicateText(lines[i].text, &lines[i].stringargs[0]); break; case 444: //Earthquake lines[i].args[0] = P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS; @@ -5872,11 +5847,7 @@ static void P_ConvertBinaryLinedefTypes(void) if (lines[i].flags & ML_MIDSOLID) lines[i].args[3] |= TMP_FREEZETHINKERS;*/ lines[i].args[4] = (lines[i].sidenum[1] != 0xFFFF) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : tag; - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 460: //Award rings lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; @@ -5905,18 +5876,10 @@ static void P_ConvertBinaryLinedefTypes(void) } else lines[i].args[5] = 0; - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 463: //Dye object - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 464: //Trigger egg capsule lines[i].args[0] = tag; @@ -5935,11 +5898,7 @@ static void P_ConvertBinaryLinedefTypes(void) case 476: // ACS_ExecuteAlways case 477: // ACS_Suspend case 478: // ACS_Terminate - if (sides[lines[i].sidenum[0]].text) - { - lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1); - } + P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 480: //Polyobject - door slide lines[i].args[0] = tag; @@ -6731,7 +6690,9 @@ static void P_ConvertBinaryThingTypes(void) break; case 543: //Balloon if (mapthings[i].angle > 0) - P_WriteConstant(((mapthings[i].angle - 1) % (numskincolors - 1)) + 1, &mapthings[i].stringargs[0]); + { + P_WriteSkincolor(((mapthings[i].angle - 1) % (numskincolors - 1)) + 1, &mapthings[i].stringargs[0]); + } mapthings[i].args[0] = !!(mapthings[i].options & MTF_AMBUSH); break; case 555: //Diagonal yellow spring @@ -6756,22 +6717,22 @@ static void P_ConvertBinaryThingTypes(void) case 706: //Water ambience G case 707: //Water ambience H mapthings[i].args[0] = 35; - P_WriteConstant(sfx_amwtr1 + mapthings[i].type - 700, &mapthings[i].stringargs[0]); + P_WriteSfx(sfx_amwtr1 + mapthings[i].type - 700, &mapthings[i].stringargs[0]); mapthings[i].type = 700; break; case 708: //Disco ambience mapthings[i].args[0] = 512; - P_WriteConstant(sfx_ambint, &mapthings[i].stringargs[0]); + P_WriteSfx(sfx_ambint, &mapthings[i].stringargs[0]); mapthings[i].type = 700; break; case 709: //Volcano ambience mapthings[i].args[0] = 220; - P_WriteConstant(sfx_ambin2, &mapthings[i].stringargs[0]); + P_WriteSfx(sfx_ambin2, &mapthings[i].stringargs[0]); mapthings[i].type = 700; break; case 710: //Machine ambience mapthings[i].args[0] = 24; - P_WriteConstant(sfx_ambmac, &mapthings[i].stringargs[0]); + P_WriteSfx(sfx_ambmac, &mapthings[i].stringargs[0]); mapthings[i].type = 700; break; case 750: //Slope vertex @@ -6834,8 +6795,7 @@ static void P_ConvertBinaryThingTypes(void) mapthings[i].args[3] = sides[lines[j].sidenum[0]].rowoffset >> FRACBITS; mapthings[i].args[4] = lines[j].backsector ? sides[lines[j].sidenum[1]].textureoffset >> FRACBITS : 0; mapthings[i].args[6] = mapthings[i].angle; - if (sides[lines[j].sidenum[0]].toptexture) - P_WriteConstant(sides[lines[j].sidenum[0]].toptexture, &mapthings[i].stringargs[0]); + P_WriteDuplicateText(sides[lines[j].sidenum[0]].text, &mapthings[i].stringargs[0]); break; } case 762: //PolyObject spawn point (crush) @@ -6923,8 +6883,9 @@ static void P_ConvertBinaryThingTypes(void) mapthings[i].args[8] |= TMM_ALWAYSTHINK; if (mapthings[i].type == 1110) { - P_WriteConstant(sides[lines[j].sidenum[0]].toptexture, &mapthings[i].stringargs[0]); - P_WriteConstant(lines[j].backsector ? sides[lines[j].sidenum[1]].toptexture : MT_NULL, &mapthings[i].stringargs[1]); + P_WriteDuplicateText(sides[lines[j].sidenum[0]].text, &mapthings[i].stringargs[0]); + if (lines[j].backsector) + P_WriteDuplicateText(sides[lines[j].sidenum[1]].text, &mapthings[i].stringargs[1]); } break; } @@ -6965,7 +6926,11 @@ static void P_ConvertBinaryThingTypes(void) mapthings[i].args[0] = P_AproxDistance(lines[j].dx, lines[j].dy) >> FRACBITS; mapthings[i].args[1] = sides[lines[j].sidenum[0]].textureoffset >> FRACBITS; mapthings[i].args[2] = !!(lines[j].flags & ML_NOCLIMB); - P_WriteConstant(MT_ROCKCRUMBLE1 + (sides[lines[j].sidenum[0]].rowoffset >> FRACBITS), &mapthings[i].stringargs[0]); + INT32 id = (sides[lines[j].sidenum[0]].rowoffset >> FRACBITS); + // Rather than introduce deh_tables.h as a dependency for literally one + // conversion, we just... recreate the string expected to be produced. + if (id > 0 && id < 16) + P_WriteDuplicateText(va("MT_ROCKCRUMBLE%d", id+1), &mapthings[i].stringargs[0]); break; } case 1221: //Minecart saloon door From ed8007471fe84c7edbecd48a4daf008ad33d2ffd Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 12 Jul 2023 18:37:00 +0100 Subject: [PATCH 05/36] Remove binary-relevant text field from side_t (and line_t) Instead of storing this during binary-UDMF conversion and then copying it for certain argument fields, put it directly onto stringarg[0] (or [1] if it's the backside), then make relevant changes with those. Reduces the amount of Zone memory used for binary converted maps, and the amount of struct memory used for all types of maps. They have also been removed from the Lua API. Fret not, the stringargs on line_t have far more utility. --- src/lua_maplib.c | 11 ------ src/p_setup.c | 88 ++++++++++++++++++------------------------------ src/r_defs.h | 3 -- 3 files changed, 33 insertions(+), 69 deletions(-) diff --git a/src/lua_maplib.c b/src/lua_maplib.c index be2c3347b..c9ae8fca0 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -135,7 +135,6 @@ enum line_e { line_frontsector, line_backsector, line_polyobj, - line_text, line_callcount }; @@ -162,7 +161,6 @@ static const char *const line_opt[] = { "frontsector", "backsector", "polyobj", - "text", "callcount", NULL}; @@ -177,7 +175,6 @@ enum side_e { side_sector, side_special, side_repeatcnt, - side_text }; static const char *const side_opt[] = { @@ -191,7 +188,6 @@ static const char *const side_opt[] = { "sector", "special", "repeatcnt", - "text", NULL}; enum vertex_e { @@ -1088,9 +1084,6 @@ static int line_get(lua_State *L) case line_polyobj: LUA_PushUserdata(L, line->polyobj, META_POLYOBJ); return 1; - case line_text: - lua_pushstring(L, line->text); - return 1; case line_callcount: lua_pushinteger(L, line->callcount); return 1; @@ -1187,9 +1180,6 @@ static int side_get(lua_State *L) case side_repeatcnt: lua_pushinteger(L, side->repeatcnt); return 1; - case side_text: - lua_pushstring(L, side->text); - return 1; } return 0; } @@ -1214,7 +1204,6 @@ static int side_set(lua_State *L) case side_line: case side_sector: case side_special: - case side_text: default: return luaL_error(L, "side_t field " LUA_QS " cannot be set.", side_opt[field]); case side_textureoffset: diff --git a/src/p_setup.c b/src/p_setup.c index 1517c4d9c..16ed24b9e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -999,7 +999,6 @@ static void P_InitializeLinedef(line_t *ld) ld->tripwire = false; - ld->text = NULL; ld->callcount = 0; // cph 2006/09/30 - fix sidedef errors right away. @@ -1104,7 +1103,6 @@ static void P_InitializeSidedef(side_t *sd) sd->special = sd->line->special; } - sd->text = NULL; sd->colormap_data = NULL; } @@ -1199,6 +1197,9 @@ static void P_LoadSidedefs(UINT8 *data) case 413: // Change music { + if (!isfrontside) + break; + char process[8+1]; sd->toptexture = sd->midtexture = sd->bottomtexture = 0; @@ -1216,35 +1217,38 @@ static void P_LoadSidedefs(UINT8 *data) sd->midtexture = get_number(process); } - sd->text = Z_Malloc(7, PU_LEVEL, NULL); - if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')) + if (msd->toptexture[0] != '-' && msd->toptexture[1] != '\0') { + sd->line->stringargs[0] = Z_Malloc(7, PU_LEVEL, NULL); M_Memcpy(process,msd->toptexture,8); process[8] = '\0'; // If they type in O_ or D_ and their music name, just shrug, // then copy the rest instead. if ((process[0] == 'O' || process[0] == 'D') && process[7]) - M_Memcpy(sd->text, process+2, 6); + M_Memcpy(sd->line->stringargs[0], process+2, 6); else // Assume it's a proper music name. - M_Memcpy(sd->text, process, 6); - sd->text[6] = 0; + M_Memcpy(sd->line->stringargs[0], process, 6); + sd->line->stringargs[0][6] = '\0'; } - else - sd->text[0] = 0; + break; } case 414: // Play SFX { sd->toptexture = sd->midtexture = sd->bottomtexture = 0; + + if (!isfrontside) + break; + if (msd->toptexture[0] != '-' || msd->toptexture[1] != '\0') { char process[8 + 1]; M_Memcpy(process, msd->toptexture, 8); process[8] = '\0'; - P_WriteDuplicateText(process, &sd->text); + P_WriteDuplicateText(process, &sd->line->stringargs[0]); } break; } @@ -1289,7 +1293,10 @@ static void P_LoadSidedefs(UINT8 *data) if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0') M_Memcpy(process+strlen(process), msd->bottomtexture, 8); - P_WriteDuplicateText(process, &sd->text); + P_WriteDuplicateText( + process, + &sd->line->stringargs[(isfrontside) ? 0 : 1] + ); break; } @@ -3206,24 +3213,25 @@ static void P_ProcessLinedefsAfterSidedefs(void) switch (ld->special) { - // Compile linedef 'text' from both sidedefs 'text' for appropriate specials. + // Compile linedef text from both sidedefs for appropriate specials. case 331: // Trigger linedef executor: Skin - Continuous case 332: // Trigger linedef executor: Skin - Each time case 333: // Trigger linedef executor: Skin - Once case 443: // Calls a named Lua function - if (sides[ld->sidenum[0]].text) + if (ld->stringargs[0] && ld->stringargs[1]) { size_t len[2]; - len[0] = strlen(sides[ld->sidenum[0]].text) + 1; - len[1] = 0; + len[0] = strlen(ld->stringargs[0]); + len[1] = strlen(ld->stringargs[1]); - if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text) - len[1] = strlen(sides[ld->sidenum[1]].text); - - ld->text = Z_Malloc(len[0] + len[1], PU_LEVEL, NULL); - M_Memcpy(ld->text, sides[ld->sidenum[0]].text, len[0]); if (len[1]) - M_Memcpy(ld->text + len[0], sides[ld->sidenum[1]].text, len[1] + 1); + { + ld->stringargs[0] = Z_Realloc(ld->stringargs[0], len[0] + len[1] + 1, PU_LEVEL, NULL); + M_Memcpy(ld->stringargs[0] + len[0] + 1, ld->stringargs[1], len[1] + 1); + } + + Z_Free(ld->stringargs[1]); + ld->stringargs[1] = NULL; } break; case 447: // Change colormap @@ -4618,7 +4626,6 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; lines[i].args[1] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS; lines[i].args[2] = !!(lines[i].flags & ML_SKEWTD); - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 16: //Minecart parameters lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; @@ -5362,7 +5369,6 @@ static void P_ConvertBinaryLinedefTypes(void) else lines[i].args[0] = TMT_CONTINUOUS; lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB); - P_WriteDuplicateText(lines[i].text, &lines[i].stringargs[0]); lines[i].special = 331; break; case 334: // Object dye - continuous @@ -5375,7 +5381,6 @@ static void P_ConvertBinaryLinedefTypes(void) else lines[i].args[0] = TMT_CONTINUOUS; lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB); - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); lines[i].special = 334; break; case 337: //Emerald check - continuous @@ -5514,7 +5519,6 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[5] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0; lines[i].args[6] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1; lines[i].args[7] = sides[lines[i].sidenum[0]].bottomtexture; - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 414: //Play sound effect lines[i].args[3] = tag; @@ -5554,7 +5558,6 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[2] = TMSL_EVERYONE; } } - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 416: //Start adjustable flickering light case 417: //Start adjustable pulsating light @@ -5620,9 +5623,6 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB); break; - case 425: //Change object state - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); - break; case 426: //Stop object lines[i].args[0] = !!(lines[i].flags & ML_NOCLIMB); break; @@ -5685,17 +5685,7 @@ static void P_ConvertBinaryLinedefTypes(void) break; case 442: //Change object type state lines[i].args[2] = tag; - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); - if (lines[i].sidenum[1] == 0xffff) - lines[i].args[3] = 1; - else - { - lines[i].args[3] = 0; - P_WriteDuplicateText(sides[lines[i].sidenum[1]].text, &lines[i].stringargs[1]); - } - break; - case 443: //Call Lua function - P_WriteDuplicateText(lines[i].text, &lines[i].stringargs[0]); + lines[i].args[3] = (lines[i].sidenum[1] == 0xffff) ? 1 : 0; break; case 444: //Earthquake lines[i].args[0] = P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS; @@ -5847,7 +5837,6 @@ static void P_ConvertBinaryLinedefTypes(void) if (lines[i].flags & ML_MIDSOLID) lines[i].args[3] |= TMP_FREEZETHINKERS;*/ lines[i].args[4] = (lines[i].sidenum[1] != 0xFFFF) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : tag; - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 460: //Award rings lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; @@ -5876,10 +5865,6 @@ static void P_ConvertBinaryLinedefTypes(void) } else lines[i].args[5] = 0; - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); - break; - case 463: //Dye object - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); break; case 464: //Trigger egg capsule lines[i].args[0] = tag; @@ -5894,12 +5879,6 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[2] = TML_SECTOR; lines[i].args[3] = !!(lines[i].flags & ML_MIDPEG); break; - case 475: // ACS_Execute - case 476: // ACS_ExecuteAlways - case 477: // ACS_Suspend - case 478: // ACS_Terminate - P_WriteDuplicateText(sides[lines[i].sidenum[0]].text, &lines[i].stringargs[0]); - break; case 480: //Polyobject - door slide lines[i].args[0] = tag; lines[i].args[1] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; @@ -6795,7 +6774,7 @@ static void P_ConvertBinaryThingTypes(void) mapthings[i].args[3] = sides[lines[j].sidenum[0]].rowoffset >> FRACBITS; mapthings[i].args[4] = lines[j].backsector ? sides[lines[j].sidenum[1]].textureoffset >> FRACBITS : 0; mapthings[i].args[6] = mapthings[i].angle; - P_WriteDuplicateText(sides[lines[j].sidenum[0]].text, &mapthings[i].stringargs[0]); + P_WriteDuplicateText(lines[j].stringargs[0], &mapthings[i].stringargs[0]); break; } case 762: //PolyObject spawn point (crush) @@ -6883,9 +6862,8 @@ static void P_ConvertBinaryThingTypes(void) mapthings[i].args[8] |= TMM_ALWAYSTHINK; if (mapthings[i].type == 1110) { - P_WriteDuplicateText(sides[lines[j].sidenum[0]].text, &mapthings[i].stringargs[0]); - if (lines[j].backsector) - P_WriteDuplicateText(sides[lines[j].sidenum[1]].text, &mapthings[i].stringargs[1]); + P_WriteDuplicateText(lines[j].stringargs[0], &mapthings[i].stringargs[0]); + P_WriteDuplicateText(lines[j].stringargs[1], &mapthings[i].stringargs[1]); } break; } diff --git a/src/r_defs.h b/src/r_defs.h index 6f8049dc8..634928a59 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -612,7 +612,6 @@ struct line_t boolean tripwire; - char *text; // a concatenation of all front and back texture names, for linedef specials that require a string. INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0 // UDMF user-defined custom properties. @@ -640,8 +639,6 @@ struct side_t INT16 special; // the special of the linedef this side belongs to INT16 repeatcnt; // # of times to repeat midtexture - char *text; // a concatenation of all top, bottom, and mid texture names, for linedef specials that require a string. - extracolormap_t *colormap_data; // storage for colormaps; not applied to sectors. // UDMF user-defined custom properties. From 459e3684aba1616d87fb37f8fd1e88b3a8268fe8 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 15 Jul 2023 19:18:39 +0100 Subject: [PATCH 06/36] P_GetNodeType: Do not dereference invalid pointer for **nodedata parameter --- src/p_setup.c | 52 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 16ed24b9e..1317f6f0b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3572,10 +3572,17 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata) nodetype_t nodetype = NT_UNSUPPORTED; char signature[4 + 1]; + *nodedata = NULL; + if (udmf) { - *nodedata = vres_Find(virt, "ZNODES")->data; - supported[NT_XGLN] = supported[NT_XGL3] = true; + virtlump_t *virtznodes = vres_Find(virt, "ZNODES"); + + if (virtznodes && virtznodes->size) + { + *nodedata = virtznodes->data; + supported[NT_XGLN] = supported[NT_XGL3] = true; + } } else { @@ -3584,24 +3591,39 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata) if (virtsegs && virtsegs->size) { - *nodedata = vres_Find(virt, "NODES")->data; - return NT_DOOM; // Traditional map format BSP tree. - } - - virtssectors = vres_Find(virt, "SSECTORS"); - - if (virtssectors && virtssectors->size) - { // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature. - *nodedata = virtssectors->data; - supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true; + virtlump_t *virtnodes = vres_Find(virt, "NODES"); + if (virtnodes && virtnodes->size) + { + *nodedata = virtnodes->data; + return NT_DOOM; // Traditional map format BSP tree. + } } else - { // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature. - *nodedata = vres_Find(virt, "NODES")->data; - supported[NT_XNOD] = supported[NT_ZNOD] = true; + { + virtssectors = vres_Find(virt, "SSECTORS"); + + if (virtssectors && virtssectors->size) + { // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature. + *nodedata = virtssectors->data; + supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true; + } + else + { // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature. + virtlump_t *virtnodes = vres_Find(virt, "NODES"); + if (virtnodes && virtnodes->size) + { + *nodedata = virtnodes->data; + supported[NT_XNOD] = supported[NT_ZNOD] = true; + } + } } } + if (*nodedata == NULL) + { + I_Error("Level has no nodes (does your map have at least 2 sectors?)"); + } + M_Memcpy(signature, *nodedata, 4); signature[4] = '\0'; (*nodedata) += 4; From 25f48fd26fd55d0e924595b49591e16fb9ee979c Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 15 Jul 2023 20:08:53 +0100 Subject: [PATCH 07/36] Add ML_BLOCKMONSTERS to P_WriteTextmap --- src/p_setup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index 1317f6f0b..b92c18a4c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2557,6 +2557,8 @@ static void P_WriteTextmap(void) fprintf(f, "blocking = true;\n"); if (wlines[i].flags & ML_BLOCKPLAYERS) fprintf(f, "blockplayers = true;\n"); + if (wlines[i].flags & ML_BLOCKMONSTERS) + fprintf(f, "blockmonsters = true;\n"); if (wlines[i].flags & ML_TWOSIDED) fprintf(f, "twosided = true;\n"); if (wlines[i].flags & ML_DONTPEGTOP) From 034fa4b0a948e12034ca7b3128ae189eb11ce5f1 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 15 Jul 2023 22:50:53 +0100 Subject: [PATCH 08/36] Water Palace Turbine UDMF conversion Requires changes in the script to support which I am not awake enough to finalise or make compatible with other branches. This took far longer than appropriate. --- src/p_setup.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index b92c18a4c..395c34536 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7145,6 +7145,55 @@ static void P_ConvertBinaryThingTypes(void) case 3786: // MT_BATTLEUFO_SPAWNER mapthings[i].args[0] = mapthings[i].angle; break; + case 3400: // MT_WATERPALACETURBINE (TODO: not yet hardcoded) + { + mtag_t tag = (mtag_t)mapthings[i].angle; + INT32 j = Tag_FindLineSpecial(2009, tag); + + if (j == -1) + { + CONS_Debug(DBG_GAMELOGIC, "Water Palace Turbine setup: Unable to find parameter line 2009 (tag %d)!\n", tag); + break; + } + + if (!lines[j].backsector) + { + CONS_Debug(DBG_GAMELOGIC, "Water Palace Turbine setup: No backside for parameter line 2009 (tag %d)!\n", tag); + break; + } + + mapthings[i].angle = sides[lines[j].sidenum[0]].rowoffset >> FRACBITS; + + if (mapthings[i].options & MTF_EXTRA) + mapthings[i].args[0] = 1; + if (mapthings[i].options & MTF_OBJECTSPECIAL) + mapthings[i].args[1] = 1; + + mapthings[i].args[2] = lines[j].frontsector->floorheight >> FRACBITS; + mapthings[i].args[3] = lines[j].frontsector->ceilingheight >> FRACBITS; + + mapthings[i].args[4] = lines[j].backsector->floorheight >> FRACBITS; + + mapthings[i].args[5] = sides[lines[j].sidenum[0]].textureoffset >> FRACBITS; + if (mapthings[i].args[5] < 0) + mapthings[i].args[5] = -mapthings[i].args[5]; + + mapthings[i].args[6] = sides[lines[j].sidenum[1]].rowoffset >> FRACBITS; + if (mapthings[i].args[6] < 0) + mapthings[i].args[6] = -mapthings[i].args[6]; + + mapthings[i].args[7] = sides[lines[j].sidenum[1]].textureoffset >> FRACBITS; + if (mapthings[i].args[7] < 0) + mapthings[i].args[7] = -mapthings[i].args[7]; + + if (lines[j].flags & ML_SKEWTD) + mapthings[i].args[8] = R_PointToDist2(lines[j].v2->x, lines[j].v2->y, lines[j].v1->x, lines[j].v1->y) >> FRACBITS; + + if (lines[j].flags & ML_NOSKEW) + mapthings[i].args[9] = 1; + + break; + } case 3441: // MT_DASHRING (TODO: not yet hardcoded) case 3442: // MT_RAINBOWDASHRING (TODO: not yet hardcoded) mapthings[i].args[0] = mapthings[i].options & 13; From 599c87f62734da5e1db87e8ef1f1341218b21461 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sun, 16 Jul 2023 01:18:23 -0700 Subject: [PATCH 09/36] Don't rewrite the past if we might still need to send it --- src/d_clisrv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d5023265f..d03bffcad 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5173,7 +5173,8 @@ static void HandlePacketFromPlayer(SINT8 node) // If we already received a ticcmd for this tic, just submit it for the next one. tic_t faketic = maketic; - if (!!(netcmds[maketic % BACKUPTICS][netconsole].flags & TICCMD_RECEIVED)) + if ((!!(netcmds[maketic % BACKUPTICS][netconsole].flags & TICCMD_RECEIVED)) + && (maketic - firstticstosend < BACKUPTICS)) faketic++; // Copy ticcmd From c1810ac89178d1993c0f5bc08dce56611bb3b1c7 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 10:59:06 +0100 Subject: [PATCH 10/36] P_SetupSpawnedMapThing: Re-add missing MT_AMBIENT --- src/p_mobj.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index d927090fb..d692fb4fd 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -13131,6 +13131,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) mobj->flags2 |= MF2_AMBUSH; } break; + case MT_AMBIENT: + if (mthing->stringargs[0]) + mobj->threshold = get_number(mthing->stringargs[0]); + mobj->health = mthing->args[0] ? mthing->args[0] : TICRATE; + break; // SRB2Kart case MT_WAYPOINT: { From f9e11be98a79801e79e69a91bb403fcc24293f80 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 19 Jul 2023 21:27:58 +0100 Subject: [PATCH 11/36] UDMF upscaled springs: Treat as an increased hitbox only, not as a strength multiplier --- src/p_map.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 2d00b808e..16820869b 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -355,7 +355,6 @@ P_DoSpringEx // boolean P_DoSpring(mobj_t *spring, mobj_t *object) { - const fixed_t scaleVal = FixedSqrt(FixedMul(mapobjectscale, spring->scale)); fixed_t vertispeed = spring->info->mass; fixed_t horizspeed = spring->info->damage; UINT16 starcolor = (spring->info->painchance % numskincolors); @@ -428,7 +427,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) object->momx = savemomx; object->momy = savemomy; - P_DoSpringEx(object, scaleVal, vertispeed, horizspeed, + P_DoSpringEx(object, mapobjectscale, vertispeed, horizspeed, spring->angle, starcolor); // Re-solidify From c404e704c249a3af4c774d67e89de3be13be1b96 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 19 Jul 2023 22:54:05 +0100 Subject: [PATCH 12/36] Linedef type 460: ACTUALLY don't push your rings below baseline --- src/p_spec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 0a8f74b15..8c7ef332a 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4199,10 +4199,13 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha else { // Don't push you below baseline - if (mo->player->rings < 0) + if (mo->player->rings <= 0) return false; - mo->player->rings--; + if (rings > mo->player->rings) + rings = mo->player->rings; + + mo->player->rings -= rings; S_StartSound(mo, sfx_antiri); } } From 63450740ba73bfb273a465456ce7247306d86dfa Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 19 Jul 2023 22:54:20 +0100 Subject: [PATCH 13/36] Linedef type 460: Don't do anything in Spheres gametypes --- src/p_spec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index 8c7ef332a..81ec88f15 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4179,6 +4179,9 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha case 460: // Award rings { + if (gametyperules & GTR_SPHERES) + return false; + INT16 rings = args[0]; INT32 delay = args[1]; if ( From 5d144243e5f6b5513822ec6134938e5dc98c4fa6 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Fri, 21 Jul 2023 00:48:41 -0700 Subject: [PATCH 14/36] Play wavedash sounds at full volume --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 31ee0be8e..4b158debe 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9672,7 +9672,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (!keepsliptide && K_IsLosingSliptideZip(player) && player->sliptideZip > 0) { if (!S_SoundPlaying(player->mo, sfx_waved2)) - S_StartSoundAtVolume(player->mo, sfx_waved2, 255/2); // Losing combo time, going to boost + S_StartSoundAtVolume(player->mo, sfx_waved2, 255); // Losing combo time, going to boost S_StopSoundByID(player->mo, sfx_waved1); S_StopSoundByID(player->mo, sfx_waved4); player->sliptideZipDelay++; @@ -9703,7 +9703,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->sliptideZipBoost += yourBoost; K_SpawnDriftBoostExplosion(player, 0); - S_StartSoundAtVolume(player->mo, sfx_waved3, 2*255/3); // Boost + S_StartSoundAtVolume(player->mo, sfx_waved3, 255); // Boost } S_StopSoundByID(player->mo, sfx_waved1); S_StopSoundByID(player->mo, sfx_waved2); @@ -9717,7 +9717,7 @@ static void K_KartDrift(player_t *player, boolean onground) S_StopSoundByID(player->mo, sfx_waved1); S_StopSoundByID(player->mo, sfx_waved2); if (player->sliptideZip > 0 && !S_SoundPlaying(player->mo, sfx_waved4)) - S_StartSoundAtVolume(player->mo, sfx_waved4, 2*255/5); // Passive woosh + S_StartSoundAtVolume(player->mo, sfx_waved4, 255); // Passive woosh } player->aizdrifttilt -= player->aizdrifttilt / 4; @@ -9734,7 +9734,7 @@ static void K_KartDrift(player_t *player, boolean onground) S_StopSoundByID(player->mo, sfx_waved2); S_StopSoundByID(player->mo, sfx_waved4); if (!S_SoundPlaying(player->mo, sfx_waved1)) - S_StartSoundAtVolume(player->mo, sfx_waved1, 255/2); // Charging + S_StartSoundAtVolume(player->mo, sfx_waved1, 255); // Charging } if (player->drift From b4bd8f428d3cb484be6e50b59dc1279308e29a40 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 21 Jul 2023 16:20:25 +0100 Subject: [PATCH 15/36] Permit lowercase enum grabs for sounds in get_number outside of Lua A different solution to the issue reported on the SRB2 port of this branch. We don't care quite as much about exact game behaviour, so this permits lowercase sound effects (same as the source code) in maps' stringargs. --- src/deh_lua.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deh_lua.c b/src/deh_lua.c index 6309859bd..7b9555104 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -493,7 +493,7 @@ static inline int lib_getenum(lua_State *L) if (mathlib) return luaL_error(L, "player sprite '%s' could not be found.\n", word); return 0; } - else if (!mathlib && fastncmp("sfx_",word,4)) { + else if (fastncmp("sfx_",word,4)) { p = word+4; for (i = 0; i < NUMSFX; i++) if (S_sfx[i].name && fastcmp(p, S_sfx[i].name)) { From 5b4fc1826440acaca3fc777d295c20a481ca171a Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 18:06:57 +0100 Subject: [PATCH 16/36] P_PostLoadLevel: Move all events directly requiring finalised player lists into this function - Grand Prix (& backup) and Match Race bot initialisation - Fixes rival selection - P_InitPlayers - Has to be after bot init - Beginning of demo recording - Grand Prix and Match Race replays appear to both function far better, and equivalently to boot. - A desync in "RNG seed 24" is reported. This is PR_BOTS. This is the only notable desync I can see! - ACS_LoadLevelScripts - I'm not ironclad sold on this being the forever home, but it's also a not-from-netsave thing, and barring external input I could see some scripts being dependent on a finalised player list. --- src/d_netcmd.c | 6 ---- src/p_setup.c | 80 +++++++++++++++++++++++++++----------------------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 35c3fed88..30ac6377c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3230,12 +3230,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) if (demo.timing) G_DoneLevelLoad(); - if (metalrecording) - G_BeginMetal(); - if (demo.recording) // Okay, level loaded, character spawned and skinned, - G_BeginRecording(); // I AM NOW READY TO RECORD. - demo.deferstart = true; - #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); #endif diff --git a/src/p_setup.c b/src/p_setup.c index 27b771df1..4a6d41e73 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7789,33 +7789,6 @@ static void P_InitGametype(void) spectateGriefed = 0; K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped - if (grandprixinfo.gp == true) - { - if (savedata.lives > 0) - { - K_LoadGrandPrixSaveGame(); - savedata.lives = 0; - } - else if (grandprixinfo.initalize == true) - { - K_InitGrandPrixRank(&grandprixinfo.rank); - K_InitGrandPrixBots(); - grandprixinfo.initalize = false; - } - else if (grandprixinfo.wonround == true) - { - K_UpdateGrandPrixBots(); - grandprixinfo.wonround = false; - } - } - else - { - // We're in a Match Race, use simplistic randomized bots. - K_UpdateMatchRaceBots(); - } - - P_InitPlayers(); - if (modeattacking && !demo.playback) P_LoadRecordGhosts(); @@ -8344,12 +8317,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) K_InitDirector(); } - // Initialize ACS scripts - if (!fromnetsave) - { - ACS_LoadLevelScripts(gamemap-1); - } - // Remove the loading shit from the screen if (rendermode != render_none && !titlemapinaction && !reloadinggamestate) F_WipeColorFill(levelfadecol); @@ -8415,6 +8382,49 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) void P_PostLoadLevel(void) { + P_MapStart(); + + if (demo.playback) + ; + else if (grandprixinfo.gp == true) + { + if (savedata.lives > 0) + { + K_LoadGrandPrixSaveGame(); + savedata.lives = 0; + } + else if (grandprixinfo.initalize == true) + { + K_InitGrandPrixRank(&grandprixinfo.rank); + K_InitGrandPrixBots(); + grandprixinfo.initalize = false; + } + else if (grandprixinfo.wonround == true) + { + K_UpdateGrandPrixBots(); + grandprixinfo.wonround = false; + } + } + else + { + // We're in a Match Race, use simplistic randomized bots. + K_UpdateMatchRaceBots(); + } + + P_InitPlayers(); + + if (metalrecording) + G_BeginMetal(); + if (demo.recording) // Okay, level loaded, character spawned and skinned, + G_BeginRecording(); // I AM NOW READY TO RECORD. + demo.deferstart = true; + + // Initialize ACS scripts + //if (!fromnetsave) -- I don't know if it's appropriate to remove this entirely yet + { + ACS_LoadLevelScripts(gamemap-1); + } + K_TimerInit(); nextmapoverride = 0; @@ -8429,12 +8439,10 @@ void P_PostLoadLevel(void) marathonmode &= ~MA_INIT; } - P_MapStart(); // just in case MapLoad modifies tm.thing - ACS_RunLevelStartScripts(); LUA_HookInt(gamemap, HOOK(MapLoad)); - P_MapEnd(); // just in case MapLoad modifies tm.thing + P_MapEnd(); // We're now done loading the level. levelloading = false; From bf8d09980db86a940f15307b6fe38458aa6ee2bf Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 18:09:11 +0100 Subject: [PATCH 17/36] G_DoPlayDemo: Make sure buffer is NULL so you can watch replays immediately after recording them. This is yet another g_demo.c-specific hack, and has been marked as such. --- src/g_demo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_demo.c b/src/g_demo.c index 5b8a5854e..9b66237eb 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3060,6 +3060,10 @@ void G_DoPlayDemo(const char *defdemoname) } else { + // FIXME: this file doesn't manage its memory and actually free this when it's done using it + Z_Free(demobuf.buffer); + demobuf.buffer = NULL; + n = defdemoname+strlen(defdemoname); while (*n != '/' && *n != '\\' && n != defdemoname) n--; From 859b3fa350f74d87ca1add7f85b1559db63da691 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 18:20:22 +0100 Subject: [PATCH 18/36] On second thoughts, let's be EXTRA careful to avoid any potential double frees. --- src/g_demo.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 9b66237eb..d7f240f38 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3061,7 +3061,7 @@ void G_DoPlayDemo(const char *defdemoname) else { // FIXME: this file doesn't manage its memory and actually free this when it's done using it - Z_Free(demobuf.buffer); + //Z_Free(demobuf.buffer); demobuf.buffer = NULL; n = defdemoname+strlen(defdemoname); @@ -4183,13 +4183,19 @@ boolean G_CheckDemoStatus(void) return true; } - if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) + if (!demo.recording) + return false; + + if (modeattacking || demo.savemode != DSM_NOTSAVING) { G_SaveDemo(); return true; } + Z_Free(demobuf.buffer); + demo.recording = false; + return false; } From 4885611f47a3b936095594232a912a4330c2ce3a Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 10:20:42 +0100 Subject: [PATCH 19/36] Move ACS script loading back to P_DoLoadLevel, since on second thought it's just LOADING and not executing anything --- src/p_setup.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 4a6d41e73..758c4b425 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8304,8 +8304,13 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // a netgame save is being loaded, and could actively be harmful by messing with // the client's view of the data.) if (!fromnetsave) + { P_InitGametype(); + // Initialize ACS scripts + ACS_LoadLevelScripts(gamemap-1); + } + // Now safe to free. vres_Free(curmapvirt); curmapvirt = NULL; @@ -8419,12 +8424,6 @@ void P_PostLoadLevel(void) G_BeginRecording(); // I AM NOW READY TO RECORD. demo.deferstart = true; - // Initialize ACS scripts - //if (!fromnetsave) -- I don't know if it's appropriate to remove this entirely yet - { - ACS_LoadLevelScripts(gamemap-1); - } - K_TimerInit(); nextmapoverride = 0; From e0b47b16e7d129a8fa7df839e6038bc0fdd9e126 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 14:03:11 +0100 Subject: [PATCH 20/36] Improve Spectator entry handling - Re-entry SHOULDN'T get stuck nonzero on first join for a given node - Set to 0 for all players on level start - Set to 0 for all players on level end - DEFINITELY never intentionally set on join with teamchange finalisation - This basically just prevents people from spamming New Challenger after hopping out mid-session, since the previous version, in addition to being buggy, was a little annoying. - New Challenger no longer happens on tic 0 of mapload - Handles any late, pending PF_PF_WANTSTOJOINs without threat of reset in P_PostLoadLevel --- src/d_netcmd.c | 2 +- src/g_game.c | 4 +++- src/k_kart.c | 7 +++++-- src/k_kart.h | 2 +- src/p_mobj.c | 1 - src/p_setup.c | 5 +++++ 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 30ac6377c..f5e2f26e1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1617,7 +1617,7 @@ static void FinalisePlaystateChange(INT32 playernum) // To attempt to discourage rage-spectators, we delay any rejoining. // If you're engaging in a DUEL and quit early, in addition to the // indignity of losing your PWR, you get a special extra-long delay. - if (netgame) + if (netgame && players[playernum].jointime > 1) { UINT8 pcount = 0; diff --git a/src/g_game.c b/src/g_game.c index 708479fbc..4a995e55e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2374,7 +2374,7 @@ void G_Ticker(boolean run) && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING // definitely good || gamestate == GS_WAITINGPLAYERS)) // definitely a problem if we don't do it at all in this gamestate, but might need more protection? { - K_CheckSpectateStatus(); + K_CheckSpectateStatus(true); } if (pausedelay && pausedelay != INT32_MIN) @@ -2436,6 +2436,8 @@ static inline void G_PlayerFinishLevel(INT32 player) p->starpostnum = 0; memset(&p->respawn, 0, sizeof (p->respawn)); + + p->spectatorReentry = 0; // Clean up any pending re-entry forbiddings } // diff --git a/src/k_kart.c b/src/k_kart.c index 4f570f3b5..d5077bbc2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11740,7 +11740,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) Obj_RingShooterInput(player); } -void K_CheckSpectateStatus(void) +void K_CheckSpectateStatus(boolean considermapreset) { UINT8 respawnlist[MAXPLAYERS]; UINT8 i, j, numingame = 0, numjoiners = 0; @@ -11770,7 +11770,7 @@ void K_CheckSpectateStatus(void) players[i].spectatewait = 0; } - if (gamestate != GS_LEVEL) + if (gamestate != GS_LEVEL || considermapreset == false) { players[i].spectatorReentry = 0; } @@ -11881,6 +11881,9 @@ void K_CheckSpectateStatus(void) break; } + if (considermapreset == false) + return; + // Reset the match when 2P joins 1P, DUEL mode // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled if (i > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2)) diff --git a/src/k_kart.h b/src/k_kart.h index 809c8673c..2f8eca77a 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -189,7 +189,7 @@ boolean K_FastFallBounce(player_t *player); fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original); void K_AdjustPlayerFriction(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); -void K_CheckSpectateStatus(void); +void K_CheckSpectateStatus(boolean considermapreset); UINT8 K_GetInvincibilityItemFrame(void); UINT8 K_GetOrbinautItemFrame(UINT8 count); boolean K_IsSPBInGame(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index 103a1e452..e69faaefc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11830,7 +11830,6 @@ void P_SpawnPlayer(INT32 playernum) else if (netgame && p->jointime <= 1 && pcount) { p->spectator = true; - p->spectatorReentry = 0; } else if (multiplayer && !netgame) { diff --git a/src/p_setup.c b/src/p_setup.c index 758c4b425..0c7c6738d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8389,6 +8389,11 @@ void P_PostLoadLevel(void) { P_MapStart(); + if (G_GametypeHasSpectators()) + { + K_CheckSpectateStatus(false); + } + if (demo.playback) ; else if (grandprixinfo.gp == true) From 39d9dc99aa71ce13fe7757b8d5cd4cfaa3b29bb7 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 14:33:32 +0100 Subject: [PATCH 21/36] FinalisePlaystateChange: Don't reset spectatorReentry timer if it's already nonzero --- src/d_netcmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5e2f26e1..7f1042f8c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1617,7 +1617,11 @@ static void FinalisePlaystateChange(INT32 playernum) // To attempt to discourage rage-spectators, we delay any rejoining. // If you're engaging in a DUEL and quit early, in addition to the // indignity of losing your PWR, you get a special extra-long delay. - if (netgame && players[playernum].jointime > 1) + if ( + netgame + && players[playernum].jointime > 1 + && players[playernum].spectatorReentry == 0 + ) { UINT8 pcount = 0; From 3d383f209eff6e0525963c7fb4e740db9f5b467f Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 14:37:34 +0100 Subject: [PATCH 22/36] Got_TeamChange: Applying PF_WANTSTOJOIN is NOT a playstate change --- src/d_netcmd.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7f1042f8c..4a53e4159 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4076,6 +4076,8 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) //Now that we've done our error checking and killed the player //if necessary, put the player on the correct team/status. + boolean nochangeoccourred = false; + if (G_GametypeHasTeams()) { if (!NetPacket.packet.newteam) @@ -4087,6 +4089,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { players[playernum].ctfteam = NetPacket.packet.newteam; players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false; + nochangeoccourred = true; } } else if (G_GametypeHasSpectators()) @@ -4094,7 +4097,10 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (!NetPacket.packet.newteam) players[playernum].spectator = true; else + { players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false; + nochangeoccourred = true; + } } if (NetPacket.packet.autobalance) @@ -4135,7 +4141,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) } }*/ - if (gamestate != GS_LEVEL) + if (gamestate != GS_LEVEL || nochangeoccourred == true) return; FinalisePlaystateChange(playernum); From 67a1fda79b6ae0b7cbc198d596c6323b0835df54 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 16:55:30 +0100 Subject: [PATCH 23/36] Permit starting a netgame or match race from the console via the map command None of the following can be combined together, and Grand Prix is still the default with no parameter from the menus. - `-server` - Starts a server - `-match` - Starts a Match (Race) round - `-skill` - Already existed, but can now only be used for GP Also guarantees the main menu wil be closed, which wasn't previously guaranteed. --- src/d_netcmd.c | 135 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 46 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 35c3fed88..7d72b490e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2877,6 +2877,8 @@ static void Command_Map_f(void) size_t option_gametype; size_t option_encore; size_t option_skill; + size_t option_server; + size_t option_match; boolean newresetplayers; boolean newforcespecialstage; @@ -2900,7 +2902,9 @@ static void Command_Map_f(void) option_force = COM_CheckPartialParm("-f"); option_gametype = COM_CheckPartialParm("-g"); option_encore = COM_CheckPartialParm("-e"); - option_skill = COM_CheckPartialParm("-s"); + option_skill = COM_CheckParm("-skill"); + option_server = COM_CheckParm("-server"); + option_match = COM_CheckParm("-match"); newresetplayers = ! COM_CheckParm("-noresetplayers"); newforcespecialstage = COM_CheckParm("-forcespecialstage"); @@ -3011,71 +3015,110 @@ static void Command_Map_f(void) } } - if (!(netgame || multiplayer)) { - grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); - grandprixinfo.masterbots = false; - - if (option_skill) + if ((option_match && option_server) + || (option_match && option_skill) + || (option_server && option_skill)) { - const char *skillname = COM_Argv(option_skill + 1); - INT32 newskill = -1; - INT32 j; - - for (j = 0; gpdifficulty_cons_t[j].strvalue; j++) - { - if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname)) - { - newskill = (INT16)gpdifficulty_cons_t[j].value; - break; - } - } - - if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match - { - INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too - if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER) - newskill = (INT16)num; - } - - if (newskill != -1) - { - if (newskill == KARTGP_MASTER) - { - grandprixinfo.gamespeed = KARTSPEED_HARD; - grandprixinfo.masterbots = true; - } - else - { - grandprixinfo.gamespeed = newskill; - grandprixinfo.masterbots = false; - } - } + CONS_Alert(CONS_WARNING, M_GetText("These options can't be combined.\nSelect only one out of -server, -match, or -skill.\n")); + Z_Free(realmapname); + Z_Free(mapname); + return; } - grandprixinfo.gp = true; - grandprixinfo.wonround = false; - if (!Playing()) { UINT8 ssplayers = cv_splitplayers.value-1; - - grandprixinfo.cup = NULL; - grandprixinfo.initalize = true; + boolean newnetgame = (option_server != 0); multiplayer = true; - restoreMenu = NULL; + netgame = false; strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); if (cv_maxconnections.value < ssplayers+1) CV_SetValue(&cv_maxconnections, ssplayers+1); + SV_StartSinglePlayerServer(newgametype, newnetgame); + if (splitscreen != ssplayers) { splitscreen = ssplayers; SplitScreen_OnChange(); } + + if (!newnetgame && option_match == 0) + { + grandprixinfo.gp = true; + grandprixinfo.initalize = true; + grandprixinfo.cup = NULL; + + grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); + grandprixinfo.masterbots = false; + } + + if (newnetgame) + { + restoreMenu = &PLAY_MP_OptSelectDef; + } + else + { + restoreMenu = NULL; + } + + M_ClearMenus(true); + } + else if ( + ((grandprixinfo.gp == true ? option_match : option_skill) != 0) // Can't swap between. + || (!netgame && (option_server != 0)) // Can't promote to server. + ) + { + CONS_Alert(CONS_WARNING, M_GetText("You are already playing a game.\nReturn to the menu to use this option.\n")); + Z_Free(realmapname); + Z_Free(mapname); + return; + } + + if (grandprixinfo.gp) + { + grandprixinfo.wonround = false; + + if (option_skill) + { + const char *skillname = COM_Argv(option_skill + 1); + INT32 newskill = -1; + INT32 j; + + for (j = 0; gpdifficulty_cons_t[j].strvalue; j++) + { + if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname)) + { + newskill = (INT16)gpdifficulty_cons_t[j].value; + break; + } + } + + if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match + { + INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too + if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER) + newskill = (INT16)num; + } + + if (newskill != -1) + { + if (newskill == KARTGP_MASTER) + { + grandprixinfo.gamespeed = KARTSPEED_HARD; + grandprixinfo.masterbots = true; + } + else + { + grandprixinfo.gamespeed = newskill; + grandprixinfo.masterbots = false; + } + } + } } } From 7c952e3509877f774e0707233fe930546f68c47f Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 18:32:50 +0100 Subject: [PATCH 24/36] Tidy P_LoadLevel music starting a little --- src/p_setup.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 27b771df1..abf092639 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8119,13 +8119,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. - if (!(reloadinggamestate || gamestate != GS_LEVEL)) + if (gamestate == GS_LEVEL) S_FadeMusic(0, FixedMul( FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); - if (reloadinggamestate) - ; - else if (K_PodiumSequence()) + if (K_PodiumSequence()) { // mapmusrng is set by local player position in K_ResetCeremony S_InitLevelMusic(true); From 2fd7c7717c1b95ffc655ff9e4b8506a227184be3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 18:55:49 +0100 Subject: [PATCH 25/36] P_StartPositionMusic Consistently sets position or encore hum for multiple contexts Now can be used in P_RestoreMusic for restoration purposes --- src/p_local.h | 1 + src/p_tick.c | 17 +---------------- src/p_user.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index 98168afe2..3bc622743 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -184,6 +184,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, an void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_RestoreMusic(player_t *player); +void P_StartPositionMusic(boolean exact); void P_EndingMusic(void); mobj_t *P_SpawnGhostMobj(mobj_t *mobj); mobj_t *P_SpawnFakeShadow(mobj_t *mobj, UINT8 offset); diff --git a/src/p_tick.c b/src/p_tick.c index 1c756a8e5..7b7a64056 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -901,22 +901,7 @@ void P_Ticker(boolean run) } // POSITION!! music - if (encoremode) - { - // Encore humming starts immediately. - if (leveltime == 1) - S_ChangeMusicInternal("encore", true); - } - else - { - // Plays the POSITION music after the camera spin - if (leveltime == introtime) - S_ChangeMusicInternal( - (mapheaderinfo[gamemap-1]->positionmus[0] - ? mapheaderinfo[gamemap-1]->positionmus - : "postn" - ), true); - } + P_StartPositionMusic(true); // exact times only } } diff --git a/src/p_user.c b/src/p_user.c index 00d3420ab..dc8aee15c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -705,6 +705,37 @@ void P_PlayVictorySound(mobj_t *source) S_StartSound(source, sfx_kwin); } +// +// P_StartPositionMusic +// +// Consistently sets starting music! +// +void P_StartPositionMusic(boolean exact) +{ + if (encoremode) + { + if (exact + ? (leveltime != 1) + : (leveltime < 1)) + return; + + S_ChangeMusicInternal("encore", true); + } + else + { + if (exact + ? (leveltime != introtime) + : (leveltime < introtime)) + return; + + S_ChangeMusicInternal( + (mapheaderinfo[gamemap-1]->positionmus[0] + ? mapheaderinfo[gamemap-1]->positionmus + : "postn" + ), true); + } +} + // // P_EndingMusic // @@ -882,6 +913,7 @@ void P_RestoreMusic(player_t *player) if ((K_CheckBossIntro() == false) && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled { + P_StartPositionMusic(false); // inexact timing permitted return; } From 2dbb11af89ca93a12fac039178e0be8dfc430214 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 18:56:54 +0100 Subject: [PATCH 26/36] Use S_AttemptToRestoreMusic to handle music after server connection Resolves #473 --- src/d_clisrv.c | 3 +++ src/s_sound.c | 14 +++++--------- src/s_sound.h | 3 +++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 65cb6d067..f4102b48b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3960,6 +3960,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) playerconsole[newplayernum] = console; G_BuildLocalSplitscreenParty(newplayernum); + if (node == mynode && splitscreenplayer == 0) + S_AttemptToRestoreMusic(); // Earliest viable point + if (netgame) { char joinmsg[256]; diff --git a/src/s_sound.c b/src/s_sound.c index 7a1acce5a..ed0e29661 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1362,7 +1362,7 @@ static UINT32 queue_fadeinms; static tic_t pause_starttic; -static void S_AttemptToRestoreMusic(void) +void S_AttemptToRestoreMusic(void) { switch (gamestate) { @@ -1376,6 +1376,9 @@ static void S_AttemptToRestoreMusic(void) case GS_INTERMISSION: S_ChangeMusicInternal("racent", true); break; + case GS_CEREMONY: + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + break; case GS_TITLESCREEN: S_ChangeMusicInternal("_title", looptitle); break; @@ -2975,14 +2978,7 @@ void S_InitLevelMusic(boolean fromnetsave) mapmusresume = 0; } - S_StopMusic(); // Starting ambience should always be restarted, if playing. - - if (leveltime < (starttime + (TICRATE/2))) // SRB2Kart - { - ; - } - else - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + S_StopMusic(); S_ResetMusicStack(); music_stack_noposition = false; diff --git a/src/s_sound.h b/src/s_sound.h index 48a5c571c..182c98160 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -346,6 +346,9 @@ void S_ResumeAudio(void); void S_EnableSound(void); void S_DisableSound(void); +// Attempt to restore music based on gamestate. +void S_AttemptToRestoreMusic(void); + // // Music Fading // From b4197a226397ae8c7e9d214a349568073e45eed8 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 24 Jul 2023 23:40:30 +0100 Subject: [PATCH 27/36] Double-free emergency fix for virtual resources - vres_GetMap guarantees the same memory for the same lump id, provided it's active - vres_Free won't delete curmapvirt unless you do some silly temp pointer stuff --- src/p_setup.c | 11 +++++++++-- src/p_setup.h | 1 + src/w_wad.c | 12 ++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 6ee45f218..ea2fb595d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8280,8 +8280,15 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } // Now safe to free. - vres_Free(curmapvirt); - curmapvirt = NULL; + // We do the following silly + // construction because vres_Free + // no-sells deletions of pointers + // that are == curmapvirt. + { + virtres_t *temp = curmapvirt; + curmapvirt = NULL; + vres_Free(temp); + } if (!reloadinggamestate) { diff --git a/src/p_setup.h b/src/p_setup.h index fd343f927..e626363f4 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -35,6 +35,7 @@ extern boolean levelloading; extern UINT8 levelfadecol; extern lumpnum_t lastloadedmaplumpnum; // for comparative savegame +extern virtres_t *curmapvirt; /* for levelflat type */ enum diff --git a/src/w_wad.c b/src/w_wad.c index a2f249f3f..96fe606be 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -2354,6 +2354,12 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) virtlump_t* vlumps = NULL; size_t numlumps = 0; + if (lastloadedmaplumpnum == lumpnum && curmapvirt != NULL) + { + // Avoid duplicating all our hard work. + return curmapvirt; + } + if (W_IsLumpWad(lumpnum)) { UINT32 realentry; @@ -2430,6 +2436,12 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) */ void vres_Free(virtres_t* vres) { + if (vres == curmapvirt) + { + // No-sell multiple references. + return; + } + while (vres->numlumps--) { if (vres->vlumps[vres->numlumps].data) From f040523b19b7e20013a66ff8afacb48dfa14fd23 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 25 Jul 2023 18:01:07 +0100 Subject: [PATCH 28/36] Implement the Sunbeam Palm decoration - Randomised tilt - Randomised leaf angles - Technically specifiable number of leaves, but the default of 5 is sane enough --- src/deh_tables.c | 6 +++++ src/info.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ src/info.h | 9 ++++++++ src/p_mobj.c | 46 +++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+) diff --git a/src/deh_tables.c b/src/deh_tables.c index 8e15ef2f1..c7130ecac 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4449,6 +4449,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_LIZARDMAN", "S_LIONMAN", + "S_SUNBEAMPALM_STEM", + "S_SUNBEAMPALM_LEAF", + "S_KARMAFIREWORK1", "S_KARMAFIREWORK2", "S_KARMAFIREWORK3", @@ -5735,6 +5738,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_LIZARDMAN", "MT_LIONMAN", + "MT_SUNBEAMPALM_STEM", + "MT_SUNBEAMPALM_LEAF", + "MT_KARMAFIREWORK", "MT_RINGSPARKS", "MT_GAINAX", diff --git a/src/info.c b/src/info.c index 749b2a6af..99f88652f 100644 --- a/src/info.c +++ b/src/info.c @@ -818,6 +818,9 @@ char sprnames[NUMSPRITES + 1][5] = "WBLZ", "WBLN", + "TUST", + "TULE", + "FWRK", "MXCL", "RGSP", @@ -5155,6 +5158,9 @@ state_t states[NUMSTATES] = {SPR_WBLZ, 0, -1, {NULL}, 0, 0, S_NULL}, // S_LIZARDMAN {SPR_WBLN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_LIONMAN + {SPR_TUST, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SUNBEAMPALM_STEM + {SPR_TULE, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SUNBEAMPALM_LEAF + {SPR_FWRK, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK2}, // S_KARMAFIREWORK1 {SPR_FWRK, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK3}, // S_KARMAFIREWORK2 {SPR_FWRK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK4}, // S_KARMAFIREWORK3 @@ -29293,6 +29299,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SUNBEAMPALM_STEM + 2697, // doomednum + S_SUNBEAMPALM_STEM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 90<args[0])+1 % 6) + 4); + + const fixed_t pivot = P_RandomRange(PR_DECORATION, -40, 20) * FRACUNIT; + + mobj->rollangle = FixedAngle(pivot); + + const fixed_t temptop = FixedDiv(mobj->height, mobj->scale); + const fixed_t tempside = mobj->info->radius * 2; + + const fixed_t top = P_ReturnThrustX(mobj, mobj->rollangle, temptop) + P_ReturnThrustY(mobj, mobj->rollangle, tempside); + const fixed_t side = P_ReturnThrustX(mobj, mobj->rollangle, tempside) - P_ReturnThrustY(mobj, mobj->rollangle, temptop); + + const fixed_t basex = P_ReturnThrustX(mobj, mobj->angle, side); + const fixed_t basey = P_ReturnThrustY(mobj, mobj->angle, side); + + const angle_t divideangle = FixedAngle((360*FRACUNIT)/numleaves); + angle_t leafangle = P_RandomKey(PR_DECORATION, 360)*ANG1; + + const fixed_t dist = mobjinfo[MT_SUNBEAMPALM_LEAF].radius; + + // Spawn all of the papersprite leaves + for (i = 0; i < numleaves; i++, leafangle += divideangle) + { + mobj_t *leaf; + + leaf = P_SpawnMobjFromMobj( + mobj, + basex + P_ReturnThrustX(mobj, leafangle, dist), + basey + P_ReturnThrustY(mobj, leafangle, dist), + top, + MT_SUNBEAMPALM_LEAF + ); + + if (P_MobjWasRemoved(leaf)) + continue; + + leaf->angle = leafangle; + + leaf->frame |= P_RandomKey(PR_DECORATION, 5); + } + + break; + } case MT_BATTLECAPSULE: { sector_t *sec = R_PointInSubsector(mobj->x, mobj->y)->sector; From 03f9ff08473099b5a7f9c066bce019bd06167e6a Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Jul 2023 14:20:46 +0100 Subject: [PATCH 29/36] Character Select menu: Put B, D on the right side of the PLAYER/profile name text, to avoid overlapping the player's eyes --- src/k_menudraw.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8324cbada..d15e39d42 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1633,17 +1633,17 @@ static void M_DrawCharSelectPreview(UINT8 num) if (p->showextra == false) { - V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); - V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); + INT32 backx = x + ((num & 1) ? -1 : 11); + V_DrawScaledPatch(backx, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); + + V_DrawScaledPatch(x + ((num & 1) ? 44 : 0), y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); + + profile_t *pr = NULL; if (p->mdepth > CSSTEP_PROFILE) { - profile_t *pr = PR_GetProfile(p->profilen); - V_DrawCenteredFileString(x+16+18, y+2, 0, pr->profilename); - } - else - { - V_DrawFileString(x+16, y+2, 0, "PLAYER"); + pr = PR_GetProfile(p->profilen); } + V_DrawCenteredFileString(backx+26, y+2, 0, pr ? pr->profilename : "PLAYER"); } if (p->mdepth >= CSSTEP_FOLLOWER) From 23f479f365d24f2a6ec81a2808e9815170a2b2f4 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Jul 2023 14:31:50 +0100 Subject: [PATCH 30/36] Intermission - DUEL mode Show players' appearances on intermissions with less than or equal to 2 players, in port priority order, to mimic Sonic 3 Competition. Also shows player letter and profile name in Match Race, to really drive the reference home. --- src/k_menu.h | 1 + src/k_menudraw.c | 2 +- src/y_inter.c | 109 ++++++++++++++++++++++++++++++++++++++++++++--- src/y_inter.h | 1 + 4 files changed, 105 insertions(+), 8 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index ff7876ef8..ab3a26443 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1155,6 +1155,7 @@ void M_DrawMessageMenu(void); void M_DrawImageDef(void); void M_DrawCharacterSelect(void); +boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, UINT8 spr2, UINT8 rotation, UINT32 frame, INT32 addflags, UINT8 *colormap); void M_DrawCupSelect(void); void M_DrawLevelSelect(void); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d15e39d42..5821e474c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1474,7 +1474,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) } // returns false if the character couldn't be rendered -static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, UINT8 spr2, UINT8 rotation, UINT32 frame, INT32 addflags, UINT8 *colormap) +boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, UINT8 spr2, UINT8 rotation, UINT32 frame, INT32 addflags, UINT8 *colormap) { UINT8 spr; spritedef_t *sprdef; diff --git a/src/y_inter.c b/src/y_inter.c index 935691a17..524c84fd9 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -178,6 +178,8 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) data.numplayers = 0; data.roundnum = 0; + data.isduel = (numplayersingame <= 2); + for (j = 0; j < numplayersingame; j++) { for (i = 0; i < MAXPLAYERS; i++) @@ -441,7 +443,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) INT32 x, y; INT32 x2, returny, inwardshim = 0; - boolean verticalresults = (standings->numplayers < 4); + boolean verticalresults = (standings->numplayers < 4 && (standings->numplayers == 1 || standings->isduel == false)); boolean datarightofcolumn = false; boolean drawping = (netgame && gamestate == GS_LEVEL); @@ -488,7 +490,11 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) y = 106 - (heightcount * yspacing)/2; - if (y < 70) + if (standings->isduel) + { + y += 38; + } + else if (y < 70) { // One sanity check. y = 70; @@ -502,7 +508,20 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) : P_IsLocalPlayer ); - for (i = 0; i < standings->numplayers; i++) + boolean doreverse = ( + standings->isduel && standings->numplayers == 2 + && standings->num[0] > standings->num[1] + ); + + i = 0; + UINT8 halfway = (standings->numplayers-1)/2; + if (doreverse) + { + i = standings->numplayers-1; + halfway++; + } + + do // don't use "continue" in this loop just for sanity's sake { const UINT8 pnum = standings->num[i]; @@ -512,6 +531,67 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) standings->num[i] = MAXPLAYERS; // this should be the only field setting in this function else { + UINT8 *charcolormap = NULL; + if (standings->color[i] != SKINCOLOR_NONE) + { + charcolormap = R_GetTranslationColormap(standings->character[i], standings->color[i], GTC_CACHE); + } + + if (standings->isduel) + { + INT32 duelx = x + 25 - inwardshim/2, duely = y - 80; + if (datarightofcolumn) + duelx += inwardshim/2; + else + duelx -= inwardshim/2; + + V_DrawScaledPatch(duelx, duely, 0, W_CachePatchName("DUELGRPH", PU_CACHE)); + V_DrawScaledPatch(duelx + 8, duely + 9, V_TRANSLUCENT, W_CachePatchName("PREVBACK", PU_CACHE)); + + UINT8 spr2 = SPR2_STIN; + if (standings->pos[i] == 2) + { + spr2 = (datarightofcolumn ? SPR2_STGR : SPR2_STGL); + } + + M_DrawCharacterSprite( + duelx + 40, duely + 78, + standings->character[i], + spr2, + (datarightofcolumn ? 1 : 7), + 0, + 0, + charcolormap + ); + + if (!netgame) + { + UINT8 j, profilen = 0; + for (j = 0; j <= splitscreen; j++) + { + if (pnum == g_localplayers[j]) + break; + } + + if (j > splitscreen) + continue; + + profilen = cv_lastprofile[j].value; + + duelx += 8; + duely += 5; + + INT32 backx = duelx + (datarightofcolumn ? -1 : 11); + + V_DrawScaledPatch(backx, duely, 0, W_CachePatchName("FILEBACK", PU_CACHE)); + + V_DrawScaledPatch(duelx + (datarightofcolumn ? 44 : 0), duely, 0, W_CachePatchName(va("CHARSEL%c", 'A' + j), PU_CACHE)); + + profile_t *pr = PR_GetProfile(profilen); + V_DrawCenteredFileString(backx+26, duely, 0, pr ? pr->profilename : "PLAYER"); + } + } + // Apply the jitter offset (later reversed) if (standings->jitter[pnum] > 0) y--; @@ -522,12 +602,15 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) if (standings->color[i] != SKINCOLOR_NONE) { - UINT8 *charcolormap; if ((players[pnum].pflags & PF_NOCONTEST) && players[pnum].bot) { // RETIRED !! - charcolormap = R_GetTranslationColormap(TC_DEFAULT, standings->color[i], GTC_CACHE); - V_DrawMappedPatch(x+14, y-5, 0, W_CachePatchName("MINIDEAD", PU_CACHE), charcolormap); + V_DrawMappedPatch( + x+14, y-5, + 0, + W_CachePatchName("MINIDEAD", PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, standings->color[i], GTC_CACHE) + ); } else { @@ -645,7 +728,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) y += yspacing; - if (verticalresults == false && i == (standings->numplayers-1)/2) + if (verticalresults == false && i == halfway) { x = 169 + xoffset - inwardshim; y = returny; @@ -653,7 +736,19 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) datarightofcolumn = true; x2 = x + 118 + 5; } + + if (!doreverse) + { + if (++i < standings->numplayers) + continue; + break; + } + + if (i == 0) + break; + i--; } + while (true); } // diff --git a/src/y_inter.h b/src/y_inter.h index 27a85fa6d..6995023db 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -22,6 +22,7 @@ typedef struct boolean gotthrough; // show "got through" boolean showrank; // show rank-restricted queue entry at the end, if it exists boolean encore; // encore mode + boolean isduel; // duel mode UINT8 roundnum; // round number char headerstring[64]; // holds levelnames up to 64 characters From 8553581d806517f23535caaa100bd21290639f11 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Jul 2023 15:51:39 +0100 Subject: [PATCH 31/36] Improved behaviour for skipping second half of intermission in offline games Instead of tying it directly to the number of players, instead base it on whether the points are important to keep track of. - Match Race or Time Attack - No next map override, since points will persist to that one - No maps queued, so points aren't relevant - OR maps are queued but you haven't started them yet, so the points will be discarded --- src/y_inter.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/y_inter.c b/src/y_inter.c index 524c84fd9..64801bd1c 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1786,9 +1786,22 @@ void Y_StartIntermission(void) // Prevent a weird bug timer = 1; } - else if (nump < 2 && !netgame) + else if ( + ( // Match Race or Time Attack + netgame == false + && grandprixinfo.gp == false + ) + && ( + modeattacking != ATTACKING_NONE // Definitely never another map + || ( // Any level sequence? + roundqueue.size == 0 // No maps queued, points aren't relevant + || roundqueue.position == 0 // OR points from this round will be discarded + ) + ) + ) { // No PWR/global score, skip it + // (the above is influenced by G_GetNextMap) timer /= 2; } else From 2b4cd831e1097f1bc666e9a952f05e673fedcf91 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Jul 2023 16:51:05 +0100 Subject: [PATCH 32/36] Fix on-screen alignment of Duel Intermission boxes - Now more consistent between time/score and rankings mode - Symmetrical if you flipped the screen, no slight rightward bias in placement --- src/y_inter.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/y_inter.c b/src/y_inter.c index 64801bd1c..b074c9e4a 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -539,11 +539,8 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) if (standings->isduel) { - INT32 duelx = x + 25 - inwardshim/2, duely = y - 80; - if (datarightofcolumn) - duelx += inwardshim/2; - else - duelx -= inwardshim/2; + INT32 duelx = x + 22 + (datarightofcolumn ? inwardshim : -inwardshim); + INT32 duely = y - 80; V_DrawScaledPatch(duelx, duely, 0, W_CachePatchName("DUELGRPH", PU_CACHE)); V_DrawScaledPatch(duelx + 8, duely + 9, V_TRANSLUCENT, W_CachePatchName("PREVBACK", PU_CACHE)); From 092bfd475edf3fdd6fbdd6c7443ab6e1086e7247 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jul 2023 18:05:44 -0700 Subject: [PATCH 33/36] P_ProcessSpecial: fix ring drain sign --- src/p_spec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index 81ec88f15..96aae8f18 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4205,6 +4205,8 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha if (mo->player->rings <= 0) return false; + rings = -(rings); + if (rings > mo->player->rings) rings = mo->player->rings; From 3eb356f7b991b03a346ae034a50d63e33074ae6d Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 27 Jul 2023 18:19:14 -0700 Subject: [PATCH 34/36] Comptime.cmake: get rid of CMake < 3.5 compatibility warning --- cmake/Comptime.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Comptime.cmake b/cmake/Comptime.cmake index 8388aed9e..c609efee4 100644 --- a/cmake/Comptime.cmake +++ b/cmake/Comptime.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) set(CMAKE_BINARY_DIR "${BINARY_DIR}") set(CMAKE_CURRENT_BINARY_DIR "${BINARY_DIR}") From b7d4513fba3d97798428265c946613bdca80ab05 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 27 Jul 2023 18:32:11 -0700 Subject: [PATCH 35/36] CMakeLists.txt: add -fmax-errors=5 for GNU Stops compilation after 5 errors, such as in the case of cascading errors from a missing semicolon. --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4bfe87f14..25f966d11 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -513,6 +513,11 @@ target_compile_options(SRB2SDL2 PRIVATE $<$,$>: /Wv:19.20.27004.0 > + + # GNU + $<$: + -fmax-errors=5 + > ) if(SRB2_CONFIG_ERRORMODE) target_compile_options(SRB2SDL2 PRIVATE From 1b5d59746f49fd1c38b733004f579d73700aa2f6 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 28 Jul 2023 22:21:37 +0100 Subject: [PATCH 36/36] Adjustments/fixes to Duel Intermission view - Always show, even in online - Add Egga Channel/CPU icon if not a local player-controlled character - This fixes an infinite loop crash that could previously affect 1v1s VS bots --- src/y_inter.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/y_inter.c b/src/y_inter.c index b074c9e4a..bfaa71521 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -561,31 +561,38 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) charcolormap ); - if (!netgame) + duelx += 8; + duely += 5; + + UINT8 j; + for (j = 0; j <= splitscreen; j++) { - UINT8 j, profilen = 0; - for (j = 0; j <= splitscreen; j++) - { - if (pnum == g_localplayers[j]) - break; - } + if (pnum == g_localplayers[j]) + break; + } - if (j > splitscreen) - continue; + INT32 letterpos = duelx + (datarightofcolumn ? 44 : 0); - profilen = cv_lastprofile[j].value; + if (j > splitscreen) + { + V_DrawScaledPatch(letterpos, duely, 0, W_CachePatchName(va("CHAR%s", (players[pnum].bot ? "CPU" : "EGGA")), PU_CACHE)); + } + else + { + duelx += (datarightofcolumn ? -1 : 11); - duelx += 8; - duely += 5; + UINT8 profilen = cv_lastprofile[j].value; - INT32 backx = duelx + (datarightofcolumn ? -1 : 11); + V_DrawScaledPatch(duelx, duely, 0, W_CachePatchName("FILEBACK", PU_CACHE)); - V_DrawScaledPatch(backx, duely, 0, W_CachePatchName("FILEBACK", PU_CACHE)); + if (datarightofcolumn && j == 0) + letterpos++; // A is one pixel thinner - V_DrawScaledPatch(duelx + (datarightofcolumn ? 44 : 0), duely, 0, W_CachePatchName(va("CHARSEL%c", 'A' + j), PU_CACHE)); + V_DrawScaledPatch(letterpos, duely, 0, W_CachePatchName(va("CHARSEL%c", 'A' + j), PU_CACHE)); profile_t *pr = PR_GetProfile(profilen); - V_DrawCenteredFileString(backx+26, duely, 0, pr ? pr->profilename : "PLAYER"); + + V_DrawCenteredFileString(duelx+26, duely, 0, pr ? pr->profilename : "PLAYER"); } }