Merge branch 'sealed-acs-splitscreen' into 'master'

Sealed ACS Splitscreen (resolves #1209)

Closes #1209

See merge request KartKrew/Kart!2190
This commit is contained in:
Oni 2024-03-31 05:54:16 +00:00
commit 8089cbd3a7
14 changed files with 200 additions and 29 deletions

View file

@ -39,6 +39,7 @@
#include "../r_textures.h"
#include "../m_cond.h"
#include "../r_skins.h"
#include "../k_kart.h"
#include "../k_battle.h"
#include "../k_grandprix.h"
#include "../k_podium.h"
@ -1646,6 +1647,54 @@ bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
return false;
}
/*--------------------------------------------------
bool CallFunc_PlayerLosing(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Returns the activating player's losing status.
--------------------------------------------------*/
bool CallFunc_PlayerLosing(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
auto info = &static_cast<Thread *>(thread)->info;
(void)argV;
(void)argC;
if ((info != NULL)
&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
&& (info->mo->player != NULL))
{
thread->dataStk.push(K_IsPlayerLosing(info->mo->player));
return false;
}
thread->dataStk.push(false);
return false;
}
/*--------------------------------------------------
bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Returns the activating player's exiting status.
--------------------------------------------------*/
bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
auto info = &static_cast<Thread *>(thread)->info;
(void)argV;
(void)argC;
if ((info != NULL)
&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
&& (info->mo->player != NULL))
{
thread->dataStk.push((info->mo->player->exiting != 0));
return false;
}
thread->dataStk.push(false);
return false;
}
/*--------------------------------------------------
bool CallFunc_GetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)

View file

@ -76,6 +76,8 @@ bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *ar
bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerSkin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerLosing(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_GetObjectDye(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerEmeralds(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PlayerLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);

View file

@ -172,6 +172,8 @@ Environment::Environment()
addFuncDataACS0( 316, addCallFunc(CallFunc_PositionStart));
addFuncDataACS0( 317, addCallFunc(CallFunc_FreePlay));
addFuncDataACS0( 318, addCallFunc(CallFunc_CheckTutorialChallenge));
addFuncDataACS0( 319, addCallFunc(CallFunc_PlayerLosing));
addFuncDataACS0( 320, addCallFunc(CallFunc_PlayerExiting));
addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait));
addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition));

View file

@ -244,6 +244,29 @@ void ACS_RunPlayerEnterScript(player_t *player)
map->scriptStartTypeForced(ACS_ST_ENTER, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunPlayerFinishScript(player_t *player)
See header file for description.
--------------------------------------------------*/
void ACS_RunPlayerFinishScript(player_t *player)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
ACSVM::MapScope::ScriptStartInfo scriptInfo;
ThreadInfo info;
P_SetTarget(&info.mo, player->mo);
scriptInfo.info = &info;
map->scriptStartTypeForced(ACS_ST_FINISH, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line)

View file

@ -139,6 +139,22 @@ void ACS_RunPlayerDeathScript(player_t *player);
void ACS_RunPlayerEnterScript(player_t *player);
/*--------------------------------------------------
void ACS_RunPlayerFinishScript(player_t *player);
Runs the map's special script for a player
finishing (P_DoPlayerExit).
Input Arguments:-
player: The player to run the script for.
Return:-
None
--------------------------------------------------*/
void ACS_RunPlayerFinishScript(player_t *player);
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line);

View file

@ -43,6 +43,7 @@ enum acs_scriptType_e
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.
ACS_ST_FINISH = 11, // FINISH: Runs when a player finishes
};
//

View file

@ -2520,9 +2520,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
}
}
if (p->spectator == false)
if (p->spectator == false && !betweenmaps)
{
if (betweenmaps || enteredGame == true)
if (enteredGame == true)
{
ACS_RunPlayerEnterScript(p);
}
@ -2531,15 +2531,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
ACS_RunPlayerRespawnScript(p);
}
}
if (betweenmaps)
return;
if (leveltime < starttime)
return;
if (exiting)
return;
}
//

View file

@ -2749,13 +2749,6 @@ void K_PlayOvertakeSound(mobj_t *source)
boolean tasteful = (!source->player || !source->player->karthud[khud_voices]);
if (!(gametyperules & GTR_CIRCUIT)) // Only in race
return;
// 4 seconds from before race begins, 10 seconds afterwards
if (leveltime < starttime+(10*TICRATE))
return;
if (
cv_kartvoices.value
&& (tasteful || cv_kartvoices.value == 2)
@ -11100,7 +11093,7 @@ void K_KartUpdatePosition(player_t *player)
if (position != oldposition) // Changed places?
{
if (player->positiondelay <= 0 && position < oldposition && P_IsDisplayPlayer(player) == true)
if (!K_Cooperative() && player->positiondelay <= 0 && position < oldposition && P_IsDisplayPlayer(player) == true)
{
// Play sound when getting closer to 1st.
UINT32 soundpos = (max(0, position - 1) * MAXPLAYERS)/realplayers; // always 1-15 despite there being 16 players at max...
@ -11143,12 +11136,6 @@ void K_KartUpdatePosition(player_t *player)
player->topinfirst = 0;
}
// Special stages: fade out music near the finish line
if (P_IsPartyPlayer(player))
{
K_FadeOutSpecialMusic(player->distancetofinish);
}
player->position = position;
}
@ -12106,14 +12093,23 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
boolean HOLDING_ITEM = (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT));
boolean NO_HYUDORO = (player->stealingtimer == 0);
if (!player->exiting)
// Play overtake sounds, but only if
// - your place changed
// - not exiting
// - in relevant gametype
// - more than 10 seconds after initial scramble
if (player->oldposition != player->position
&& !player->exiting
&& (gametyperules & GTR_CIRCUIT)
&& !K_Cooperative()
&& leveltime >= starttime+(10*TICRATE))
{
if (player->oldposition < player->position) // But first, if you lost a place,
{
player->oldposition = player->position; // then the other player taunts.
K_RegularVoiceTimers(player); // and you can't for a bit
}
else if (player->oldposition > player->position) // Otherwise,
else // Otherwise,
{
K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!"
player->oldposition = player->position; // Restore the old position,

View file

@ -120,6 +120,19 @@ void K_TickSpecialStage(void)
return;
}
// Special stages: fade out music near the finish line
UINT8 i;
UINT32 lowestdistance = UINT32_MAX;
for (i = 0; i <= r_splitscreen; i++)
{
if (!playeringame[displayplayers[i]] || players[displayplayers[i]].spectator)
continue;
if (players[displayplayers[i]].distancetofinish >= lowestdistance)
continue;
lowestdistance = players[displayplayers[i]].distancetofinish;
}
K_FadeOutSpecialMusic(lowestdistance);
if (P_MobjWasRemoved(specialstageinfo.ufo))
{
P_SetTarget(&specialstageinfo.ufo, NULL);
@ -148,6 +161,53 @@ mobj_t *K_GetPossibleSpecialTarget(void)
return specialstageinfo.ufo;
}
/*--------------------------------------------------
boolean K_PlayerIsEmptyHandedInSpecial(void)
See header file for description.
--------------------------------------------------*/
boolean K_PlayerIsEmptyHandedInSpecial(player_t *player)
{
if (specialstageinfo.valid == false)
return false; // Not Sealed Star
if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo)))
return true; // UFO exists
thinker_t *think;
mobj_t *thing;
player_t *orbitplayer = NULL;
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
thing = (mobj_t *)think;
if (thing->type != MT_EMERALD)
continue;
// emerald_award(thing)
if (!thing->tracer || P_MobjWasRemoved(thing->tracer))
continue;
if (thing->tracer->type != MT_PLAYER || !thing->tracer->player)
continue;
orbitplayer = thing->tracer->player;
if (orbitplayer->exiting && !(orbitplayer->pflags & PF_NOCONTEST))
{
// Another player has successfully taken the emerald to the end
return false;
}
// The emerald is being carried, but not by you
return (orbitplayer != player);
}
// EMERALD DELETED!?
return true;
}
/*--------------------------------------------------
void K_FadeOutSpecialMusic(UINT32 distance)

View file

@ -68,6 +68,21 @@ void K_TickSpecialStage(void);
mobj_t *K_GetPossibleSpecialTarget(void);
/*--------------------------------------------------
boolean K_PlayerIsEmptyHandedInSpecial(player_t *player)
Gets whether the player has failed a Sealed
Star via finishing without an Emerald
Input Arguments:-
player - Player to check for
Return:-
Should player fail or not
--------------------------------------------------*/
boolean K_PlayerIsEmptyHandedInSpecial(player_t *player);
/*--------------------------------------------------
void K_FadeOutSpecialMusic(UINT32 distance)

View file

@ -149,6 +149,9 @@ static void Obj_EmeraldOrbitPlayer(mobj_t *emerald)
if (!(bestplayer->pflags & PF_NOCONTEST))
{
P_MoveOrigin(emerald, bestplayer->mo->x, bestplayer->mo->y, bestplayer->mo->z);
P_SetTarget(&emerald->tracer, NULL); // Ensures that tracer is correctly reset, allowing ACS to detect empty-handdeness.
// "Why not just check target, which has to be set for any part of the behavior to work at all?"
// Because Hyudoro is an emerald. I will not explain further. Program for a different game.
Obj_BeginEmeraldOrbit(emerald, bestplayer->mo, 100 * mapobjectscale, 64, 0);
return;
}
@ -316,7 +319,7 @@ void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT3
P_SetTarget(&emerald_orbit(emerald), target);
P_SetTarget(&emerald->punt_ref, target);
if (P_MobjWasRemoved(emerald_award(emerald)))
if (!emerald_award(emerald) || P_MobjWasRemoved(emerald_award(emerald)))
{
P_SetTarget(&emerald_award(emerald), target);
}

View file

@ -8892,6 +8892,16 @@ void P_PostLoadLevel(void)
ACS_RunLevelStartScripts();
LUA_HookInt(gamemap, HOOK(MapLoad));
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
ACS_RunPlayerEnterScript(&players[i]);
}
P_MapEnd();
// We're now done loading the level.

View file

@ -1997,7 +1997,7 @@ static void K_HandleLapIncrement(player_t *player)
if (specialstageinfo.valid == true)
{
// Don't permit a win just by sneaking ahead of the UFO/emerald.
if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo)))
if (K_PlayerIsEmptyHandedInSpecial(player))
{
applyflags |= PF_NOCONTEST;

View file

@ -71,6 +71,7 @@
#include "k_credits.h"
#include "k_hud.h" // K_AddMessage
#include "m_easing.h"
#include "acs/interface.h"
#ifdef HWRENDER
#include "hardware/hw_light.h"
@ -1342,6 +1343,8 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
demo.savebutton = leveltime;
}
}
ACS_RunPlayerFinishScript(player);
}
//