Merge branch 'acs-game-over' into 'master'

ACS script type for game over (lose GP round with no extra lives), improve MapWarp and add a handful of functions

Closes #591 and #572

See merge request KartKrew/Kart!1403
This commit is contained in:
Oni 2023-08-21 16:50:18 +00:00
commit 852e2c1035
18 changed files with 261 additions and 240 deletions

View file

@ -43,6 +43,7 @@
#include "../k_podium.h"
#include "../k_bot.h"
#include "../z_zone.h"
#include "../music.h"
#include "call-funcs.hpp"
@ -1802,6 +1803,7 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor
if (argV[1] == 0)
skipstats = 1;
G_BeginLevelExit();
exitcountdown = 1;
if (server)
@ -1856,6 +1858,91 @@ bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word
return false;
}
/*--------------------------------------------------
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Halts the level exit if it's happening.
--------------------------------------------------*/
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
(void)argV;
(void)argC;
exitcountdown = 0;
return false;
}
/*--------------------------------------------------
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Exits the level.
--------------------------------------------------*/
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
(void)argV;
(void)argC;
if (exitcountdown == 1)
{
// An exit is already in progress.
return false;
}
if (argC >= 1)
{
skipstats = (argV[0] == 0);
}
G_BeginLevelExit();
exitcountdown = 1;
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return false;
}
/*--------------------------------------------------
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Play a tune. If it's already playing, restart from the
beginning.
--------------------------------------------------*/
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
ACSVM::MapScope *map = thread->scopeMap;
Music_Play(map->getString(argV[0])->str);
return false;
}
/*--------------------------------------------------
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Stop every tune that is currently playing.
--------------------------------------------------*/
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
Music_StopAll();
return false;
}
/*--------------------------------------------------
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Change the actual song lump that a tune will play.
--------------------------------------------------*/
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
ACSVM::MapScope *map = thread->scopeMap;
Music_Remap(map->getString(argV[0])->str, map->getString(argV[1])->str);
return false;
}
/*--------------------------------------------------
bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)

View file

@ -87,6 +87,11 @@ bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV,
bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);

View file

@ -171,6 +171,11 @@ Environment::Environment()
addFuncDataACS0( 503, addCallFunc(CallFunc_SetLineRenderStyle));
addFuncDataACS0( 504, addCallFunc(CallFunc_MapWarp));
addFuncDataACS0( 505, addCallFunc(CallFunc_AddBot));
addFuncDataACS0( 506, addCallFunc(CallFunc_StopLevelExit));
addFuncDataACS0( 507, addCallFunc(CallFunc_ExitLevel));
addFuncDataACS0( 508, addCallFunc(CallFunc_MusicPlay));
addFuncDataACS0( 509, addCallFunc(CallFunc_MusicStopAll));
addFuncDataACS0( 510, addCallFunc(CallFunc_MusicRemap));
}
ACSVM::Thread *Environment::allocThread()

View file

@ -315,6 +315,22 @@ void ACS_RunEmeraldScript(mobj_t *mo)
map->scriptStartType(ACS_ST_EMERALD, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunGameOverScript(void)
See header file for description.
--------------------------------------------------*/
void ACS_RunGameOverScript(void)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
map->scriptStartType(ACS_ST_GAMEOVER, {});
}
/*--------------------------------------------------
void ACS_Tick(void)

View file

@ -179,6 +179,17 @@ void ACS_RunCatcherScript(mobj_t *mo);
void ACS_RunEmeraldScript(mobj_t *mo);
/*--------------------------------------------------
void ACS_RunGameOverScript(void);
Runs the map's special scripts for exiting
the level, due to a losing condition and
without any extra lives to retry.
--------------------------------------------------*/
void ACS_RunGameOverScript(void);
/*--------------------------------------------------
void ACS_Tick(void);

View file

@ -42,6 +42,7 @@ enum acs_scriptType_e
ACS_ST_OVERTIME = 7, // OVERTIME: Runs when Overtime starts in timed game modes.
ACS_ST_UFO = 8, // UFO: Runs when the UFO Catcher is destroyed in a Special Stage.
ACS_ST_EMERALD = 9, // EMERALD: Runs when the Chaos Emerald is collected in a Special Stage.
ACS_ST_GAMEOVER = 10, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life.
};
//

View file

@ -5776,7 +5776,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
if (G_GamestateUsesExitLevel() == false)
return;
G_ExitLevel();
G_FinishExitLevel();
}
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)

View file

@ -762,8 +762,15 @@ extern UINT8 maxXtraLife; // Max extra lives from rings
extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
struct exitcondition_t
{
boolean losing;
boolean retry;
};
// For racing
extern tic_t racecountdown, exitcountdown, musiccountdown;
extern exitcondition_t g_exit;
#define DEFAULT_GRAVITY (4*FRACUNIT/5)
extern fixed_t gravity;

View file

@ -4161,7 +4161,7 @@ boolean G_CheckDemoStatus(void)
I_Quit();
if (multiplayer && !demo.title)
G_ExitLevel();
G_FinishExitLevel();
else
{
G_StopDemo();

View file

@ -274,6 +274,7 @@ mobj_t *hunt2;
mobj_t *hunt3;
tic_t racecountdown, exitcountdown, musiccountdown; // for racing
exitcondition_t g_exit;
fixed_t gravity;
fixed_t mapobjectscale;
@ -805,9 +806,7 @@ const char *G_BuildMapName(INT32 map)
*/
INT32 G_MapNumber(const char * name)
{
#ifdef NEXTMAPINSOC
if (strncasecmp("NEXTMAP_", name, 8) != 0)
#endif
{
INT32 map;
UINT32 hash = quickncasehash(name, MAXMAPLUMPNAME);
@ -826,7 +825,6 @@ INT32 G_MapNumber(const char * name)
return NEXTMAP_INVALID;
}
#ifdef NEXTMAPINSOC
name += 8;
if (strcasecmp("EVALUATION", name) == 0)
@ -835,9 +833,10 @@ INT32 G_MapNumber(const char * name)
return NEXTMAP_CREDITS;
if (strcasecmp("CEREMONY", name) == 0)
return NEXTMAP_CEREMONY;
//if (strcasecmp("TITLE", name) == 0)
if (strcasecmp("TITLE", name) == 0)
return NEXTMAP_TITLE;
#endif
return NEXTMAP_INVALID;
}
/** Clips the console player's mouse aiming to the current view.
@ -2928,95 +2927,116 @@ void G_AddPlayer(INT32 playernum)
demo_extradata[playernum] |= DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
}
void G_ExitLevel(void)
void G_BeginLevelExit(void)
{
g_exit.losing = true;
g_exit.retry = false;
if (grandprixinfo.gp == true)
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
K_PlayerFinishGrandPrix(&players[i]);
}
}
}
if (!G_GametypeUsesLives() || skipstats != 0)
{
g_exit.losing = false; // never force a retry
}
else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS))
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (!K_IsPlayerLosing(&players[i]))
{
g_exit.losing = false;
break;
}
}
}
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{
g_exit.losing = (grandprixinfo.wonround != true);
}
if (g_exit.losing)
{
// You didn't win...
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].lives > 0)
{
g_exit.retry = true;
break;
}
}
}
}
if (g_exit.losing && specialstageinfo.valid)
{
exitcountdown = TICRATE;
}
else
{
exitcountdown = raceexittime+1;
}
if (g_exit.losing)
{
if (!g_exit.retry)
{
ACS_RunGameOverScript();
}
}
}
void G_FinishExitLevel(void)
{
G_ResetAllDeviceRumbles();
if (gamestate == GS_LEVEL)
{
UINT8 i;
boolean doretry = false;
if (grandprixinfo.gp == true)
if (g_exit.retry)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
K_PlayerFinishGrandPrix(&players[i]);
}
}
}
if (!G_GametypeUsesLives() || skipstats != 0)
; // never force a retry
else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS))
{
doretry = true;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (!K_IsPlayerLosing(&players[i]))
{
doretry = false;
break;
}
}
}
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{
doretry = (grandprixinfo.wonround != true);
}
if (doretry)
{
// You didn't win...
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].lives > 0)
{
break;
}
}
}
if (i == MAXPLAYERS)
{
// GAME OVER, try again from the start!
if (grandprixinfo.gp == true
&& grandprixinfo.eventmode == GPEVENT_SPECIAL)
{
// We were in a Special Stage.
// We can still progress to the podium when we game over here.
doretry = false;
}
else if (netgame)
{
; // Restart cup here whenever we do Online GP
}
else
{
// Back to the menu with you.
G_HandleSaveLevel(true);
D_QuitNetGame();
CL_Reset();
D_ClearState();
M_StartControlPanel();
}
}
else
// Restart cup here whenever we do Online GP
if (!netgame)
{
// We have lives, just redo this one course.
G_SetRetryFlag();
return;
}
}
else if (g_exit.losing)
{
// We were in a Special Stage.
// We can still progress to the podium when we game over here.
const boolean special = grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_SPECIAL;
if (doretry == true)
if (!netgame && !special)
{
// Back to the menu with you.
G_HandleSaveLevel(true);
D_QuitNetGame();
CL_Reset();
D_ClearState();
M_StartControlPanel();
return;
}
}

View file

@ -206,7 +206,8 @@ boolean G_GametypeUsesLives(void);
boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void);
INT16 G_SometimesGetDifferentEncore(void);
void G_ExitLevel(void);
void G_BeginLevelExit(void);
void G_FinishExitLevel(void);
void G_NextLevel(void);
void G_GetNextMap(void);
void G_Continue(void);

View file

@ -844,6 +844,12 @@ boolean K_CanChangeRules(boolean allowdemos)
--------------------------------------------------*/
void K_PlayerFinishGrandPrix(player_t *player)
{
if (grandprixinfo.wonround == true)
{
// This was already completed.
return;
}
if (player->exiting == false)
{
// You did not finish

View file

@ -2869,7 +2869,8 @@ static int lib_gExitLevel(lua_State *L)
// Moved this bit to G_SetCustomExitVars
if (n >= 1) // Don't run the reset to defaults option
lib_gSetCustomExitVars(L);
G_ExitLevel();
G_BeginLevelExit();
G_FinishExitLevel();
return 0;
}

View file

@ -5731,6 +5731,10 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEUINT32(save->p, racecountdown);
WRITEUINT32(save->p, exitcountdown);
// exitcondition_t
WRITEUINT8(save->p, g_exit.losing);
WRITEUINT8(save->p, g_exit.retry);
WRITEFIXED(save->p, gravity);
WRITEFIXED(save->p, mapobjectscale);
@ -5906,6 +5910,10 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
racecountdown = READUINT32(save->p);
exitcountdown = READUINT32(save->p);
// exitcondition_t
g_exit.losing = READUINT8(save->p);
g_exit.retry = READUINT8(save->p);
gravity = READFIXED(save->p);
mapobjectscale = READFIXED(save->p);

View file

@ -1203,46 +1203,6 @@ static void P_LoadSidedefs(UINT8 *data)
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
break;
case 413: // Change music
{
if (!isfrontside)
break;
char process[8+1];
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
{
M_Memcpy(process,msd->bottomtexture,8);
process[8] = '\0';
sd->bottomtexture = get_number(process);
}
if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0')
{
M_Memcpy(process,msd->midtexture,8);
process[8] = '\0';
sd->midtexture = get_number(process);
}
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->line->stringargs[0], process+2, 6);
else // Assume it's a proper music name.
M_Memcpy(sd->line->stringargs[0], process, 6);
sd->line->stringargs[0][6] = '\0';
}
break;
}
case 414: // Play SFX
{
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
@ -5569,28 +5529,6 @@ static void P_ConvertBinaryLinedefTypes(void)
lines[i].args[3] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
lines[i].args[4] = lines[i].frontsector->ceilingheight >> FRACBITS;
break;
case 413: //Change music
if (lines[i].flags & ML_NOCLIMB)
lines[i].args[1] |= TMM_ALLPLAYERS;
if (lines[i].flags & ML_SKEWTD)
lines[i].args[1] |= TMM_OFFSET;
if (lines[i].flags & ML_NOSKEW)
lines[i].args[1] |= TMM_FADE;
if (lines[i].flags & ML_BLOCKPLAYERS)
lines[i].args[1] |= TMM_NORELOAD;
if (lines[i].flags & ML_NOTBOUNCY)
lines[i].args[1] |= TMM_FORCERESET;
if (lines[i].flags & ML_MIDSOLID)
lines[i].args[1] |= TMM_NOLOOP;
if (lines[i].flags & ML_MIDPEG)
lines[i].args[1] |= TMM_NOCREDIT;
lines[i].args[2] = sides[lines[i].sidenum[0]].midtexture;
lines[i].args[3] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
lines[i].args[4] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
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;
break;
case 414: //Play sound effect
lines[i].args[3] = tag;
if (tag != 0)
@ -7505,6 +7443,9 @@ static void P_InitLevelSettings(void)
racecountdown = exitcountdown = musiccountdown = exitfadestarted = 0;
curlap = bestlap = 0; // SRB2Kart
g_exit.losing = false;
g_exit.retry = false;
// Gamespeed and frantic items
gamespeed = KARTSPEED_EASY;
franticitems = false;

View file

@ -2886,88 +2886,6 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
}
break;
case 413: // Change music
// FIXME: port to new music system
#if 0
// console player only unless TMM_ALLPLAYERS is set
if ((args[1] & TMM_ALLPLAYERS) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction)
{
boolean musicsame = (!stringargs[0] || !stringargs[0][0] || !strnicmp(stringargs[0], S_MusicName(), 7));
UINT16 tracknum = (UINT16)max(args[7], 0);
INT32 position = (INT32)max(args[2], 0);
UINT32 prefadems = (UINT32)max(args[3], 0);
UINT32 postfadems = (UINT32)max(args[4], 0);
UINT8 fadetarget = (UINT8)max(args[5], 0);
INT16 fadesource = (INT16)max(args[6], -1);
// Seek offset from current song position
if (args[1] & TMM_OFFSET)
{
// adjust for loop point if subtracting
if (position < 0 && S_GetMusicLength() &&
S_GetMusicPosition() > S_GetMusicLoopPoint() &&
S_GetMusicPosition() + position < S_GetMusicLoopPoint())
position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0);
else
position = max(S_GetMusicPosition() + position, 0);
}
// Fade current music to target volume (if music won't be changed)
if ((args[1] & TMM_FADE) && fadetarget && musicsame)
{
// 0 fadesource means fade from current volume.
// meaning that we can't specify volume 0 as the source volume -- this starts at 1.
if (!fadesource)
fadesource = -1;
if (!postfadems)
S_SetInternalMusicVolume(fadetarget);
else
S_FadeMusicFromVolume(fadetarget, fadesource, postfadems);
if (position)
S_SetMusicPosition(position);
}
// Change the music and apply position/fade operations
else
{
if (!stringargs[0])
break;
strncpy(mapmusname, stringargs[0], 7);
mapmusname[6] = 0;
mapmusflags = tracknum & MUSIC_TRACKMASK;
if (!(args[1] & TMM_NORELOAD))
mapmusflags |= MUSIC_RELOADRESET;
if (args[1] & TMM_FORCERESET)
mapmusflags |= MUSIC_FORCERESET;
mapmusposition = position;
mapmusresume = 0;
S_ChangeMusicEx(mapmusname, mapmusflags, !(args[1] & TMM_NOLOOP), position,
!(args[1] & TMM_FADE) ? prefadems : 0,
!(args[1] & TMM_FADE) ? postfadems : 0);
if (!(args[1] & TMM_NOCREDIT))
S_ShowMusicCredit();
if ((args[1] & TMM_FADE) && fadetarget)
{
if (!postfadems)
S_SetInternalMusicVolume(fadetarget);
else
S_FadeMusicFromVolume(fadetarget, fadesource, postfadems);
}
}
// Except, you can use the TMM_NORELOAD flag to change this behavior.
// if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn.
}
#endif
break;
case 414: // Play SFX
P_PlaySFX(stringargs[0] ? get_number(stringargs[0]) : sfx_None, mo, callsec, args[3], args[1], args[2]);
break;

View file

@ -1318,19 +1318,12 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
if (P_CheckRacers() && !exitcountdown)
{
if (specialout == true)
{
exitcountdown = TICRATE;
}
else
{
exitcountdown = raceexittime+1;
}
G_BeginLevelExit();
}
}
else if (!exitcountdown) // All other gametypes
{
exitcountdown = raceexittime+1;
G_BeginLevelExit();
}
if (grandprixinfo.gp == true && player->bot == false && losing == false)
@ -3777,7 +3770,7 @@ void P_DoTimeOver(player_t *player)
if (!exitcountdown)
{
exitcountdown = raceexittime;
G_BeginLevelExit();
}
}

View file

@ -134,6 +134,7 @@ TYPEDEF (unloaded_mapheader_t);
TYPEDEF (tolinfo_t);
TYPEDEF (cupheader_t);
TYPEDEF (unloaded_cupheader_t);
TYPEDEF (exitcondition_t);
// font.h
TYPEDEF (font_t);