# HG changeset patch # User Adam Kaminski # Date 1603760406 14400 # Mon Oct 26 21:00:06 2020 -0400 # Node ID 0b05b9b82618b6ba52352e42cc8334ba43b95bb7 # Parent 63e9776991cadc372259522683d6216529fa72e6 Added new ACS functions: SetGamemodeLimit() to change gamemode limits, SetCurrentGamemode() to allow switching of gamemodes during a game, and GetCurrentGamemode() to get the current gamemode. diff -r 63e9776991ca -r 0b05b9b82618 docs/zandronum-history.txt --- a/docs/zandronum-history.txt Sun Oct 25 17:58:32 2020 -0400 +++ b/docs/zandronum-history.txt Mon Oct 26 21:00:06 2020 -0400 @@ -20,6 +20,7 @@ + - The server can now broadcast the MD5 hashes of loaded PWADs to launchers. [Sean] + - Added new console commands "demo_ticsplayed" to show the current position in demo playback and "demo_skipto" to skip to such a position. + - Added new console variable "sv_nodoorclose" to prevent manual door closing, in order to prevent large numbers of players from blocking a door by continuously opening and closing it. [DoomJoshuaBoy] ++ - Added new ACS functions: SetGamemodeLimits(int limit, int value) to change gamemode limits, SetCurrentGamemode(str gamemode, bool reset) to switch gamemodes during a game, and GetCurrentGamemode() to get the gamemode being played. [Kaminsky] - - Fixed: Bots tries to jump to reach item when sv_nojump is true. [sleep] - - Fixed: ACS function SetSkyScrollSpeed didn't work online. [Edward-san] - - Fixed: color codes in callvote reasons weren't terminated properly. [Dusk] diff -r 63e9776991ca -r 0b05b9b82618 src/gamemode.cpp --- a/src/gamemode.cpp Sun Oct 25 17:58:32 2020 -0400 +++ b/src/gamemode.cpp Mon Oct 26 21:00:06 2020 -0400 @@ -861,6 +861,62 @@ //***************************************************************************** // +void GAMEMODE_SetState( GAMESTATE_e GameState ) +{ + if( GameState == GAMESTATE_WAITFORPLAYERS ) + { + if ( survival ) + SURVIVAL_SetState( SURVS_WAITINGFORPLAYERS ); + else if ( invasion ) + INVASION_SetState( IS_WAITINGFORPLAYERS ); + else if ( duel ) + DUEL_SetState( DS_WAITINGFORPLAYERS ); + else if ( teamlms || lastmanstanding ) + LASTMANSTANDING_SetState( LMSS_WAITINGFORPLAYERS ); + else if ( possession || teampossession ) + POSSESSION_SetState( PSNS_WAITINGFORPLAYERS ); + } + else if( GameState == GAMESTATE_COUNTDOWN ) + { + if ( survival ) + SURVIVAL_SetState( SURVS_COUNTDOWN ); + else if ( invasion ) + INVASION_SetState( IS_FIRSTCOUNTDOWN ); + else if ( duel ) + DUEL_SetState( DS_COUNTDOWN ); + else if ( teamlms || lastmanstanding ) + LASTMANSTANDING_SetState( LMSS_COUNTDOWN ); + else if ( possession || teampossession ) + POSSESSION_SetState( PSNS_COUNTDOWN ); + } + else if( GameState == GAMESTATE_INPROGRESS ) + { + if ( survival ) + SURVIVAL_SetState( SURVS_INPROGRESS ); + else if ( invasion ) + INVASION_SetState( IS_INPROGRESS ); + else if ( duel ) + DUEL_SetState( DS_INDUEL ); + else if ( teamlms || lastmanstanding ) + LASTMANSTANDING_SetState( LMSS_INPROGRESS ); + else if ( possession || teampossession ) + POSSESSION_SetState( PSNS_INPROGRESS ); + } + else if( GameState == GAMESTATE_INRESULTSEQUENCE ) + { + if ( survival ) + SURVIVAL_SetState( SURVS_MISSIONFAILED ); + else if ( invasion ) + INVASION_SetState( IS_MISSIONFAILED ); + else if ( duel ) + DUEL_SetState( DS_WINSEQUENCE ); + else if ( teamlms || lastmanstanding ) + LASTMANSTANDING_SetState( LMSS_WINSEQUENCE ); + } +} + +//***************************************************************************** +// void GAMEMODE_HandleEvent ( const GAMEEVENT_e Event, AActor *pActivator, const int DataOne, const int DataTwo ) { // [BB] Clients don't start scripts. @@ -1082,3 +1138,69 @@ break; } } + +//***************************************************************************** +// +ULONG GAMEMODE_GetCountdownTicks( void ) +{ + if ( survival ) + return ( SURVIVAL_GetCountdownTicks() ); + else if ( invasion ) + return ( INVASION_GetCountdownTicks() ); + else if ( duel ) + return ( DUEL_GetCountdownTicks() ); + else if ( teamlms || lastmanstanding ) + return ( LASTMANSTANDING_GetCountdownTicks() ); + else if ( possession || teampossession ) + return ( POSSESSION_GetCountdownTicks() ); + + // [AK] The other gamemodes don't have a countdown, so just return zero. + return 0; +} + +//***************************************************************************** +// +void GAMEMODE_SetCountdownTicks( const ULONG Ticks ) +{ + if ( survival ) + SURVIVAL_SetCountdownTicks( Ticks ); + else if ( invasion ) + INVASION_SetCountdownTicks( Ticks ); + else if ( duel ) + DUEL_SetCountdownTicks( Ticks ); + else if ( teamlms || lastmanstanding ) + LASTMANSTANDING_SetCountdownTicks( Ticks ); + else if ( possession || teampossession ) + POSSESSION_SetCountdownTicks( Ticks ); +} + +//***************************************************************************** +// +void GAMEMODE_SetLimit( GAMELIMIT_e GameLimit, int value ) +{ + UCVarValue Val; + Val.Int = value; + + switch ( GameLimit ) + { + case GAMELIMIT_FRAGS: + fraglimit.ForceSet( Val, CVAR_Int ); + break; + + case GAMELIMIT_POINTS: + pointlimit.ForceSet( Val, CVAR_Int ); + break; + + case GAMELIMIT_DUELS: + duellimit.ForceSet( Val, CVAR_Int ); + break; + + case GAMELIMIT_WINS: + winlimit.ForceSet( Val, CVAR_Int ); + break; + + case GAMELIMIT_WAVES: + wavelimit.ForceSet( Val, CVAR_Int ); + break; + } +} \ No newline at end of file diff -r 63e9776991ca -r 0b05b9b82618 src/gamemode.h --- a/src/gamemode.h Sun Oct 25 17:58:32 2020 -0400 +++ b/src/gamemode.h Mon Oct 26 21:00:06 2020 -0400 @@ -108,6 +108,17 @@ } GAMEEVENT_e; //***************************************************************************** +typedef enum +{ + GAMELIMIT_FRAGS = 0, + GAMELIMIT_TIME, + GAMELIMIT_POINTS, + GAMELIMIT_DUELS, + GAMELIMIT_WINS, + GAMELIMIT_WAVES, +} GAMELIMIT_e; + +//***************************************************************************** // STRUCTURES typedef struct @@ -165,6 +176,7 @@ bool GAMEMODE_IsSpectatorAllowedSpecial ( const int Special ); bool GAMEMODE_IsHandledSpecial ( AActor *Activator, int Special ); GAMESTATE_e GAMEMODE_GetState ( void ); +void GAMEMODE_SetState ( GAMESTATE_e GameState ); void GAMEMODE_HandleEvent ( const GAMEEVENT_e Event, AActor *pActivator = NULL, const int DataOne = 0, const int DataTwo = 0 ); // [BB] This function doesn't really belong here. Find a better place for it. @@ -177,4 +189,8 @@ MODIFIER_e GAMEMODE_GetModifier( void ); void GAMEMODE_SetModifier( MODIFIER_e Modifier ); +ULONG GAMEMODE_GetCountdownTicks( void ); +void GAMEMODE_SetCountdownTicks( const ULONG Ticks ); +void GAMEMODE_SetLimit( GAMELIMIT_e GameLimit, int value ); + #endif // __GAMEMODE_H__ diff -r 63e9776991ca -r 0b05b9b82618 src/p_acs.cpp --- a/src/p_acs.cpp Sun Oct 25 17:58:32 2020 -0400 +++ b/src/p_acs.cpp Mon Oct 26 21:00:06 2020 -0400 @@ -5118,6 +5118,9 @@ ACSF_Strftime, ACSF_SetDeadSpectator, ACSF_SetActivatorToPlayer, + ACSF_SetCurrentGamemode, + ACSF_GetCurrentGamemode, + ACSF_SetGamemodeLimit, // ZDaemon ACSF_GetTeamScore = 19620, // (int team) @@ -7007,6 +7010,135 @@ } break; + case ACSF_SetCurrentGamemode: + { + const char *name = FBehavior::StaticLookupString( args[0] ); + const bool bResetLevel = !!args[1]; + const GAMEMODE_e oldmode = GAMEMODE_GetCurrentMode(); + const GAMESTATE_e state = GAMEMODE_GetState(); + GAMEMODE_e newmode; + ULONG ulCountdownTicks; + + // [AK] Only the server should change the gamemode, but not during the result sequence. + if ( NETWORK_InClientMode() || state == GAMESTATE_INRESULTSEQUENCE ) + return 0; + + // [AK] No need to change the gamemode if we're already playing it. + if ( stricmp( name, GAMEMODE_GetName( oldmode )) == 0 ) + return 0; + + for ( int i = 0; i < NUM_GAMEMODES; i++ ) + { + newmode = static_cast ( i ); + if ( stricmp( name, GAMEMODE_GetName( newmode )) != 0 ) + continue; + + // [AK] Don't change to any team game if there's no team starts on the map! + if (( GAMEMODE_GetFlags( newmode ) & GMF_TEAMGAME ) && TEAM_GetNumTeamsWithStarts() < 1 ) + return 0; + // [AK] Don't change to deathmatch if there's no deathmatch starts on the map! + if ( GAMEMODE_GetFlags( newmode ) & GMF_DEATHMATCH ) + { + if ( deathmatchstarts.Size() < 1 ) + return 0; + + // [AK] If we're changing to duel, make sure there's not too many players either. + if ( newmode == GAMEMODE_DUEL ) + { + ULONG ulNumPlayers = 0; + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playeringame[ulIdx] && players[ulIdx].bSpectating == false ) + ulNumPlayers++; + } + + if ( ulNumPlayers > 2 ) + return 0; + } + } + // [AK] Don't change to cooperative if there's no cooperative starts on the map! + else if ( GAMEMODE_GetFlags( newmode ) & GMF_COOPERATIVE ) + { + ULONG ulNumSpawns = 0; + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playerstarts[ulIdx].type != 0 ) + { + ulNumSpawns++; + break; + } + } + + if ( ulNumSpawns < 1 ) + return 0; + } + + // [AK] Get the ticks left in the countdown and reset the gamemode, if necessary. + ulCountdownTicks = GAMEMODE_GetCountdownTicks(); + GAMEMODE_SetState( GAMESTATE_WAITFORPLAYERS ); + + // [AK] If everything's okay now, change the gamemode. + GAMEMODE_ResetSpecalGamemodeStates(); + GAMEMODE_SetCurrentMode( newmode ); + + // [AK] If we're the server, tell the clients to change the gamemode too. + if ( NETWORK_GetState() == NETSTATE_SERVER ) + SERVERCOMMANDS_SetGameMode(); + + // [AK] Check if we want to reset the current map. + if ( bResetLevel ) + G_ChangeLevel( level.mapname, 0, 0 ); + else + { + // [AK] Remove players from any teams if the new gamemode doesn't support them. + if (( GAMEMODE_GetFlags( oldmode ) & GMF_PLAYERSONTEAMS ) && ( GAMEMODE_GetCurrentFlags() & GMF_PLAYERSONTEAMS ) == false ) + { + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + PLAYER_SetTeam( &players[ulIdx], teams.Size(), true ); + } + // [AK] If we need to move players into teams instead, assign them automatically. + else if (( GAMEMODE_GetFlags( oldmode ) & GMF_PLAYERSONTEAMS ) == false && ( GAMEMODE_GetCurrentFlags() & GMF_PLAYERSONTEAMS )) + { + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playeringame[ulIdx] && players[ulIdx].bSpectating == false && players[ulIdx].bOnTeam == false ) + PLAYER_SetTeam( &players[ulIdx], TEAM_ChooseBestTeamForPlayer(), true ); + } + } + + // [AK] If necessary, transfer the countdown time and state to the new gamemode. + if ( state > GAMESTATE_WAITFORPLAYERS ) + { + GAMEMODE_SetCountdownTicks( ulCountdownTicks ); + GAMEMODE_SetState( state ); + } + + GAMEMODE_SpawnSpecialGamemodeThings(); + } + return 1; + } + return 0; + } + + case ACSF_GetCurrentGamemode: + { + return GlobalACSStrings.AddString( GAMEMODE_GetName( GAMEMODE_GetCurrentMode() )); + } + + case ACSF_SetGamemodeLimit: + { + GAMELIMIT_e limit = static_cast ( args[0] ); + if ( limit == GAMELIMIT_TIME ) + { + UCVarValue Val; + Val.Float = FIXED2FLOAT( args[1] ); + timelimit.ForceSet( Val, CVAR_Float ); + } + else + GAMEMODE_SetLimit( limit, args[1] ); + break; + } + case ACSF_GetActorFloorTexture: { auto a = SingleActorFromTID(args[0], activator);