# HG changeset patch # User Adam Kaminski # Date 1603901187 14400 # Wed Oct 28 12:06:27 2020 -0400 # Node ID 7fdf32e9c97e8d9f08d57fb497dc5ce4acb88c8f # Parent 8de243236c59b7589445753eaa15e9f013e06b53 Added ACS function SetPlayerClass() to allow changing of a player's class. diff -r 8de243236c59 -r 7fdf32e9c97e docs/zandronum-history.txt --- a/docs/zandronum-history.txt Mon Oct 26 21:02:38 2020 -0400 +++ b/docs/zandronum-history.txt Wed Oct 28 12:06:27 2020 -0400 @@ -22,6 +22,7 @@ + - 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] + - Added new console variable "sv_noobituaries" to prevent obituaries from being printed to the server console when a player dies. [Kaminsky] ++ - Added ACS function: SetPlayerClass(int player, str class, bool respawn) to allow changing of a player's class. [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 8de243236c59 -r 7fdf32e9c97e src/p_acs.cpp --- a/src/p_acs.cpp Mon Oct 26 21:02:38 2020 -0400 +++ b/src/p_acs.cpp Wed Oct 28 12:06:27 2020 -0400 @@ -5121,6 +5121,7 @@ ACSF_SetCurrentGamemode, ACSF_GetCurrentGamemode, ACSF_SetGamemodeLimit, + ACSF_SetPlayerClass, // ZDaemon ACSF_GetTeamScore = 19620, // (int team) @@ -7139,6 +7140,57 @@ break; } + case ACSF_SetPlayerClass: + { + player_t *player = &players[args[0]]; + const bool bRespawn = !!args[2]; + + // [AK] Don't allow the clients to change the player's class. + if ( NETWORK_InClientMode() ) + return 0; + + // [AK] Don't bother changing the player's class if they're actually spectating. + if ( PLAYER_IsTrueSpectator( player ) ) + return 0; + + const char *classname = FBehavior::StaticLookupString( args[1] ); + const PClass *playerclass = PClass::FindClass( classname ); + + // [AK] Stop if the class provided doesn't exist or isn't a descendant of PlayerPawn. + // Also check if the player isn't already playing as the same class. + if ( playerclass == NULL || !playerclass->IsDescendantOf( RUNTIME_CLASS( APlayerPawn )) || player->cls == playerclass ) + return 0; + + // [AK] Don't change the player's class if it's not allowed. + if ( !TEAM_IsActorAllowedForPlayer( GetDefaultByType( playerclass ), player ) ) + return 0; + + player->userinfo.PlayerClassChanged( playerclass->Meta.GetMetaString( APMETA_DisplayName )); + // [AK] In a singleplayer game, we must also change the class the player would start as. + if ( NETWORK_GetState() != NETSTATE_SERVER ) + SinglePlayerClass[args[0]] = player->userinfo.GetPlayerClassNum(); + + if ( bRespawn && PLAYER_IsValidPlayerWithMo( args[0] ) ) + { + APlayerPawn *pmo = player->mo; + player->playerstate = PST_REBORNNOINVENTORY; + + // [AK] Unmorph the player before respawning them with a new class. + if ( player->morphTics ) + P_UndoPlayerMorph( player, player ); + + // [AK] If we're the server, tell the clients to destroy the body. + if ( NETWORK_GetState() == NETSTATE_SERVER ) + SERVERCOMMANDS_DestroyThing( pmo ); + + pmo->Destroy(); + pmo = NULL; + GAMEMODE_SpawnPlayer( player - players ); + } + + return 1; + } + case ACSF_GetActorFloorTexture: { auto a = SingleActorFromTID(args[0], activator);