- redirect most references to the global players array through FLevelLocals.

The Map loader may not access any global state at all - everything it can touch must be exchangable.
Furthermore, if we want to sandbox each level, there may be no direct access to any kind of global state whatsoever from the play code.
This commit is contained in:
Christoph Oelckers 2019-01-30 01:15:32 +01:00
parent c15212ca82
commit 8bbdee5c28
19 changed files with 259 additions and 189 deletions

View file

@ -35,6 +35,7 @@
#include "actor.h"
#include "d_player.h"
#include "p_local.h"
#include "g_levellocals.h"
//==========================================================================
//
@ -61,14 +62,18 @@
Only one selector of each type can be used.
*/
#define AAPTR_RESOLVE_PLAYERNUM(playernum) (playeringame[playernum] ? players[playernum].mo : NULL)
AActor *COPY_AAPTR(AActor *origin, int selector)
{
if (selector == AAPTR_DEFAULT) return origin;
FTranslatedLineTarget t;
auto Level = origin->Level;
auto AAPTR_RESOLVE_PLAYERNUM = [=](int playernum) -> AActor*
{
return (Level->PlayerInGame(playernum) ? Level->Players[playernum]->mo : nullptr);
};
if (origin)
{
if (origin->player)

View file

@ -154,12 +154,12 @@ void FS_EmulateCmd(FLevelLocals *Level, char * string)
{
sc.MustGetFloat();
double playerviewheight = sc.Float;
for(int i=0;i<MAXPLAYERS;i++)
for (auto p : Level->Players)
{
// No, this is not correct. But this is the way Legacy WADs expect it to be handled!
if (players[i].mo != NULL) players[i].mo->FloatVar(NAME_ViewHeight) = playerviewheight;
players[i].viewheight = playerviewheight;
players[i].Uncrouch();
if (p->mo != nullptr) p->mo->FloatVar(NAME_ViewHeight) = playerviewheight;
p->viewheight = playerviewheight;
p->Uncrouch();
}
while (sc.GetString())
{

View file

@ -278,7 +278,7 @@ int FParser::T_GetPlayerNum(const svalue_t &arg)
{
return -1;
}
if(!playeringame[playernum]) // no error, just return -1
if(!Level->PlayerInGame(playernum)) // no error, just return -1
{
return -1;
}
@ -288,7 +288,7 @@ int FParser::T_GetPlayerNum(const svalue_t &arg)
AActor *FParser::T_GetPlayerActor(const svalue_t &arg)
{
int num = T_GetPlayerNum(arg);
return num == -1 ? nullptr : players[num].mo;
return num == -1 ? nullptr : Level->Players[num]->mo;
}
PClassActor *T_ClassType(const svalue_t &arg)
@ -659,7 +659,7 @@ void FParser::SF_PlayerTip(void)
if (CheckArgs(1))
{
int plnum = T_GetPlayerNum(t_argv[0]);
if (plnum!=-1 && players[plnum].mo->CheckLocalView(consoleplayer))
if (plnum!=-1 && Level->Players[plnum]->mo->CheckLocalView(consoleplayer))
{
C_MidPrint(SmallFont, GetFormatString(1).GetChars());
}
@ -692,7 +692,7 @@ void FParser::SF_PlayerMsg(void)
if (CheckArgs(1))
{
int plnum = T_GetPlayerNum(t_argv[0]);
if (plnum!=-1 && players[plnum].mo->CheckLocalView(consoleplayer))
if (plnum!=-1 && Level->Players[plnum]->mo->CheckLocalView(consoleplayer))
{
Printf(PRINT_HIGH, "%s\n", GetFormatString(1).GetChars());
}
@ -714,7 +714,7 @@ void FParser::SF_PlayerInGame(void)
if (plnum!=-1)
{
t_return.type = svt_int;
t_return.value.i = playeringame[plnum];
t_return.value.i = Level->PlayerInGame(plnum);
}
}
}
@ -742,7 +742,7 @@ void FParser::SF_PlayerName(void)
if(plnum !=-1)
{
t_return.type = svt_string;
t_return.string = players[plnum].userinfo.GetName();
t_return.string = Level->Players[plnum]->userinfo.GetName();
}
else
{
@ -773,7 +773,7 @@ void FParser::SF_PlayerObj(void)
if(plnum !=-1)
{
t_return.type = svt_mobj;
t_return.value.mobj = players[plnum].mo;
t_return.value.mobj = Level->Players[plnum]->mo;
}
else
{
@ -1403,7 +1403,7 @@ void FParser::SF_SetCamera(void)
if (CheckArgs(1))
{
player = Script->trigger->player;
if (!player) player=&players[0];
if (!player) player = Level->Players[0];
newcamera = actorvalue(t_argv[0]);
if(!newcamera)
@ -1436,7 +1436,7 @@ void FParser::SF_ClearCamera(void)
{
player_t * player;
player = Script->trigger->player;
if (!player) player=&players[0];
if (!player) player = Level->Players[0];
AActor * cam=player->camera;
if (cam)
@ -2419,13 +2419,13 @@ void FParser::SF_PlayerKeys(void)
if(t_argc == 2)
{
t_return.type = svt_int;
t_return.value.i = CheckInventory(players[playernum].mo, keyname);
t_return.value.i = CheckInventory(Level->Players[playernum]->mo, keyname);
return;
}
else
{
givetake = intvalue(t_argv[2]);
ScriptUtil::Exec(givetake?NAME_GiveInventory : NAME_TakeInventory, ScriptUtil::Pointer, players[playernum].mo, ScriptUtil::Int, keyname.GetIndex(), ScriptUtil::Int, 1, ScriptUtil::End);
ScriptUtil::Exec(givetake?NAME_GiveInventory : NAME_TakeInventory, ScriptUtil::Pointer, Level->Players[playernum]->mo, ScriptUtil::Int, keyname.GetIndex(), ScriptUtil::Int, 1, ScriptUtil::End);
t_return.type = svt_int;
t_return.value.i = 0;
}
@ -2504,14 +2504,14 @@ void FParser::SF_PlayerWeapon()
if (t_argc == 2)
{
AActor * wp = players[playernum].mo->FindInventory(ti);
AActor * wp = Level->Players[playernum]->mo->FindInventory(ti);
t_return.type = svt_int;
t_return.value.i = wp!=NULL;
return;
}
else
{
AActor * wp = players[playernum].mo->FindInventory(ti);
AActor * wp = Level->Players[playernum]->mo->FindInventory(ti);
newweapon = !!intvalue(t_argv[2]);
if (!newweapon)
@ -2520,14 +2520,14 @@ void FParser::SF_PlayerWeapon()
{
wp->Destroy();
// If the weapon is active pick a replacement. Legacy didn't do this!
if (players[playernum].PendingWeapon==wp) players[playernum].PendingWeapon=WP_NOCHANGE;
if (players[playernum].ReadyWeapon==wp)
if (Level->Players[playernum]->PendingWeapon==wp) Level->Players[playernum]->PendingWeapon=WP_NOCHANGE;
if (Level->Players[playernum]->ReadyWeapon==wp)
{
players[playernum].ReadyWeapon=nullptr;
Level->Players[playernum]->ReadyWeapon=nullptr;
IFVM(PlayerPawn, PickNewWeapon)
{
VMValue param[] = { players[playernum].mo, (void*)nullptr };
VMValue param[] = { Level->Players[playernum]->mo, (void*)nullptr };
VMCall(func, param, 2, nullptr, 0);
}
}
@ -2537,9 +2537,9 @@ void FParser::SF_PlayerWeapon()
{
if (!wp)
{
auto pw=players[playernum].PendingWeapon;
players[playernum].mo->GiveInventoryType(ti);
players[playernum].PendingWeapon=pw;
auto pw=Level->Players[playernum]->PendingWeapon;
Level->Players[playernum]->mo->GiveInventoryType(ti);
Level->Players[playernum]->PendingWeapon=pw;
}
}
@ -2588,13 +2588,13 @@ void FParser::SF_PlayerSelectedWeapon()
return;
}
players[playernum].PendingWeapon = players[playernum].mo->FindInventory(ti);
Level->Players[playernum]->PendingWeapon = Level->Players[playernum]->mo->FindInventory(ti);
}
t_return.type = svt_int;
for(int i=0;i<9;i++)
{
if (players[playernum].ReadyWeapon->GetClass ()->TypeName == FName(WeaponNames[i]))
if (Level->Players[playernum]->ReadyWeapon->GetClass ()->TypeName == FName(WeaponNames[i]))
{
t_return.value.i=i;
break;
@ -2620,7 +2620,7 @@ void FParser::SF_GiveInventory(void)
if(t_argc == 2) count=1;
else count=intvalue(t_argv[2]);
ScriptUtil::Exec(NAME_GiveInventory, ScriptUtil::Pointer, players[playernum].mo, ScriptUtil::Int, FName(stringvalue(t_argv[1])).GetIndex(), ScriptUtil::Int, count, ScriptUtil::End);
ScriptUtil::Exec(NAME_GiveInventory, ScriptUtil::Pointer, Level->Players[playernum]->mo, ScriptUtil::Int, FName(stringvalue(t_argv[1])).GetIndex(), ScriptUtil::Int, count, ScriptUtil::End);
t_return.type = svt_int;
t_return.value.i = 0;
}
@ -2643,7 +2643,7 @@ void FParser::SF_TakeInventory(void)
if(t_argc == 2) count=32767;
else count=intvalue(t_argv[2]);
ScriptUtil::Exec(NAME_TakeInventory, ScriptUtil::Pointer, players[playernum].mo, ScriptUtil::Int, FName(stringvalue(t_argv[1])).GetIndex(), ScriptUtil::Int, count, ScriptUtil::End);
ScriptUtil::Exec(NAME_TakeInventory, ScriptUtil::Pointer, Level->Players[playernum]->mo, ScriptUtil::Int, FName(stringvalue(t_argv[1])).GetIndex(), ScriptUtil::Int, count, ScriptUtil::End);
t_return.type = svt_int;
t_return.value.i = 0;
}
@ -2668,7 +2668,7 @@ void FParser::SF_CheckInventory(void)
return;
}
t_return.type = svt_int;
t_return.value.i = CheckInventory(players[playernum].mo, stringvalue(t_argv[1]));
t_return.value.i = CheckInventory(Level->Players[playernum]->mo, stringvalue(t_argv[1]));
}
}
@ -3221,10 +3221,10 @@ void FParser::SF_PlayerAddFrag()
{
playernum1 = T_GetPlayerNum(t_argv[0]);
players[playernum1].fragcount++;
Level->Players[playernum1]->fragcount++;
t_return.type = svt_int;
t_return.value.f = players[playernum1].fragcount;
t_return.value.f = Level->Players[playernum1]->fragcount;
}
else
@ -3232,10 +3232,10 @@ void FParser::SF_PlayerAddFrag()
playernum1 = T_GetPlayerNum(t_argv[0]);
playernum2 = T_GetPlayerNum(t_argv[1]);
players[playernum1].frags[playernum2]++;
Level->Players[playernum1]->frags[playernum2]++;
t_return.type = svt_int;
t_return.value.f = players[playernum1].frags[playernum2];
t_return.value.f = Level->Players[playernum1]->frags[playernum2];
}
}
}

View file

@ -616,7 +616,7 @@ void T_PreprocessScripts(FLevelLocals *Level)
// get the other scripts
// levelscript started by player 0 'superplayer'
th->LevelScript->trigger = players[0].mo;
th->LevelScript->trigger = Level->Players[0]->mo;
th->LevelScript->Preprocess(Level);
th->LevelScript->ParseScript(nullptr, th);

View file

@ -494,7 +494,7 @@ int P_CheckKeys (AActor *owner, int keynum, bool remote, bool quiet)
// If we get here, that means the actor isn't holding an appropriate key.
if (owner == players[consoleplayer].camera)
if (owner->CheckLocalView(consoleplayer))
{
PrintMessage(failtext);

View file

@ -835,12 +835,12 @@ bool FLevelLocals::DoCompleted (FString nextlevel, wbstartstruct_t &wminfo)
for (i=0 ; i<MAXPLAYERS ; i++)
{
wminfo.plyr[i].skills = players[i].killcount;
wminfo.plyr[i].sitems = players[i].itemcount;
wminfo.plyr[i].ssecret = players[i].secretcount;
wminfo.plyr[i].skills = Players[i]->killcount;
wminfo.plyr[i].sitems = Players[i]->itemcount;
wminfo.plyr[i].ssecret = Players[i]->secretcount;
wminfo.plyr[i].stime = time;
memcpy (wminfo.plyr[i].frags, players[i].frags, sizeof(wminfo.plyr[i].frags));
wminfo.plyr[i].fragcount = players[i].fragcount;
memcpy (wminfo.plyr[i].frags, Players[i]->frags, sizeof(wminfo.plyr[i].frags));
wminfo.plyr[i].fragcount = Players[i]->fragcount;
}
// [RH] If we're in a hub and staying within that hub, take a snapshot.
@ -1035,11 +1035,11 @@ void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool au
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && (deathmatch || players[i].playerstate == PST_DEAD))
players[i].playerstate = PST_ENTER; // [BC]
memset (players[i].frags,0,sizeof(players[i].frags));
if (PlayerInGame(i) && (deathmatch || Players[i]->playerstate == PST_DEAD))
Players[i]->playerstate = PST_ENTER; // [BC]
memset (Players[i]->frags,0,sizeof(Players[i]->frags));
if (!(dmflags2 & DF2_YES_KEEPFRAGS) && (alwaysapplydmflags || deathmatch))
players[i].fragcount = 0;
Players[i]->fragcount = 0;
}
if (changeflags & CHANGELEVEL_NOMONSTERS)
@ -1093,19 +1093,19 @@ void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool au
{
for (int i = 0; i<MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo != NULL)
P_PlayerStartStomp(players[i].mo);
if (PlayerInGame(i) && Players[i]->mo != nullptr)
P_PlayerStartStomp(Players[i]->mo);
}
}
// For each player, if they are viewing through a player, make sure it is themselves.
for (int ii = 0; ii < MAXPLAYERS; ++ii)
{
if (playeringame[ii])
if (PlayerInGame(ii))
{
if (players[ii].camera == NULL || players[ii].camera->player != NULL)
if (Players[ii]->camera == nullptr || Players[ii]->camera->player != nullptr)
{
players[ii].camera = players[ii].mo;
Players[ii]->camera = Players[ii]->mo;
}
if (savegamerestore)
@ -1119,7 +1119,7 @@ void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool au
if (fromSnapshot)
{
// ENTER scripts are being handled when the player gets spawned, this cannot be changed due to its effect on voodoo dolls.
Behaviors.StartTypedScripts(SCRIPT_Return, players[ii].mo, true);
Behaviors.StartTypedScripts(SCRIPT_Return, Players[ii]->mo, true);
}
}
}
@ -1181,8 +1181,8 @@ void FLevelLocals::WorldDone (void)
// Strife needs a special case here to choose between good and sad ending. Bad is handled elsewhere.
if (endsequence == NAME_Inter_Strife)
{
if (players[0].mo->FindInventory (NAME_QuestItem25) ||
players[0].mo->FindInventory (NAME_QuestItem28))
if (Players[0]->mo->FindInventory (NAME_QuestItem25) ||
Players[0]->mo->FindInventory (NAME_QuestItem28))
{
endsequence = NAME_Inter_Strife_Good;
}
@ -1317,12 +1317,12 @@ void FLevelLocals::StartTravel ()
{
if (playeringame[i])
{
AActor *pawn = players[i].mo;
AActor *pawn = Players[i]->mo;
AActor *inv;
players[i].camera = nullptr;
Players[i]->camera = nullptr;
// Only living players travel. Dead ones get a new body on the new level.
if (players[i].health > 0)
if (Players[i]->health > 0)
{
pawn->UnlinkFromWorld (nullptr);
int tid = pawn->tid; // Save TID
@ -1721,14 +1721,14 @@ void FLevelLocals::UnSnapshotLevel (bool hubLoad)
while ((pawn = next) != 0)
{
next = it.Next();
if (pawn->player == NULL || pawn->player->mo == NULL || !playeringame[pawn->player - players])
if (pawn->player == nullptr || pawn->player->mo == nullptr || !PlayerInGame(pawn->player))
{
int i;
// If this isn't the unmorphed original copy of a player, destroy it, because it's extra.
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].morphTics && players[i].mo->alternative == pawn)
if (PlayerInGame(i) && Players[i]->morphTics && Players[i]->mo->alternative == pawn)
{
break;
}

View file

@ -48,6 +48,7 @@
#include "p_spec.h"
#include "actor.h"
#include "p_effect.h"
#include "d_player.h"
#include "p_destructible.h"
#include "r_data/r_sections.h"
#include "r_data/r_canvastexture.h"
@ -102,7 +103,15 @@ struct FLevelLocals
{
void *level;
void *Level; // bug catchers.
FLevelLocals() : Behaviors(this), tagManager(this) {}
FLevelLocals() : Behaviors(this), tagManager(this)
{
// Make sure that these point to the right data all the time.
// This will be needed for as long as it takes to completely separate global UI state from per-level play state.
for (int i = 0; i < MAXPLAYERS; i++)
{
Players[i] = &players[i];
}
}
friend class MapLoader;
@ -494,6 +503,42 @@ public:
TObjPtr<DAutomapBase*> automap = nullptr;
int bodyqueslot;
// For now this merely points to the global player array, but with this in place, access to this array can be moved over to the level.
// As things progress each level needs to be able to point to different players,
// but even then the level will not own the player - the player merely links to the level.
// This should also be made a real object eventually.
player_t *Players[MAXPLAYERS];
// This is to allow refactoring without refactoring the data right away.
bool PlayerInGame(int pnum)
{
return playeringame[pnum];
}
// This needs to be done better, but for now it should be good enough.
bool PlayerInGame(player_t *player)
{
for (int i = 0; i < MAXPLAYERS; i++)
{
if (player == Players[i]) return PlayerInGame(i);
}
return false;
}
int PlayerNum(player_t *player)
{
for (int i = 0; i < MAXPLAYERS; i++)
{
if (player == Players[i]) return i;
}
return -1;
}
bool isPrimaryLevel() const
{
return true;
}
int NumMapSections;
uint32_t flags;

View file

@ -122,9 +122,9 @@ void DEarthquake::Tick ()
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !(players[i].cheats & CF_NOCLIP))
if (Level->PlayerInGame(i) && !(Level->Players[i]->cheats & CF_NOCLIP))
{
AActor *victim = players[i].mo;
AActor *victim = Level->Players[i]->mo;
double dist;
dist = m_Spot->Distance2D(victim, true);

View file

@ -3247,8 +3247,8 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position)
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo != nullptr)
players[i].health = players[i].mo->health;
if (Level->PlayerInGame(i) && Level->Players[i]->mo != nullptr)
Level->Players[i]->health = Level->Players[i]->mo->health;
}
if (!map->HasBehavior && !map->isText)
TranslateTeleportThings(); // [RH] Assign teleport destination TIDs

View file

@ -1725,7 +1725,7 @@ static bool DoUseInv (AActor *actor, PClassActor *info)
//
//============================================================================
static int UseInventory (AActor *activator, const char *type)
static int UseInventory (FLevelLocals *Level, AActor *activator, const char *type)
{
PClassActor *info;
int ret = 0;
@ -1743,8 +1743,8 @@ static int UseInventory (AActor *activator, const char *type)
{
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
ret += DoUseInv (players[i].mo, info);
if (Level->PlayerInGame(i))
ret += DoUseInv (Level->Players[i]->mo, info);
}
}
else
@ -1759,6 +1759,7 @@ static int UseInventory (AActor *activator, const char *type)
// CheckInventory
//
// Returns how much of a particular item an actor has.
// This also gets called from FraggleScript.
//
//============================================================================
@ -3672,7 +3673,7 @@ int DLevelScript::CountPlayers ()
int count = 0, i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
if (Level->PlayerInGame(i))
count++;
return count;
@ -3850,9 +3851,9 @@ void DLevelScript::DoFadeRange (int r1, int g1, int b1, int a1,
{
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
viewer = &players[i];
viewer = Level->Players[i];
showme:
if (ftime <= 0.f)
{
@ -3965,7 +3966,7 @@ int DoGetMasterTID (AActor *self)
if (self->master) return self->master->tid;
else if (self->FriendPlayer)
{
player_t *player = &players[(self->FriendPlayer)-1];
player_t *player = self->Level->Players[(self->FriendPlayer)-1];
return player->mo->tid;
}
else return 0;
@ -4504,13 +4505,13 @@ int DLevelScript::GetPlayerInput(int playernum, int inputnum)
}
p = activator->player;
}
else if (playernum >= MAXPLAYERS || !playeringame[playernum])
else if (playernum >= MAXPLAYERS || !Level->PlayerInGame(playernum))
{
return 0;
}
else
{
p = &players[playernum];
p = Level->Players[playernum];
}
if (p == NULL)
{
@ -4961,11 +4962,11 @@ static int DoGetCVar(FBaseCVar *cvar, bool is_string)
int DLevelScript::SetUserCVar(int playernum, const char *cvarname, int value, bool is_string)
{
if ((unsigned)playernum >= MAXPLAYERS || !playeringame[playernum])
if ((unsigned)playernum >= MAXPLAYERS || !Level->PlayerInGame(playernum))
{
return 0;
}
FBaseCVar **cvar_p = players[playernum].userinfo.CheckKey(FName(cvarname, true));
FBaseCVar **cvar_p = Level->Players[playernum]->userinfo.CheckKey(FName(cvarname, true));
FBaseCVar *cvar;
// Only mod-created cvars may be set.
if (cvar_p == NULL || (cvar = *cvar_p) == NULL || (cvar->GetFlags() & CVAR_IGNORE) || !(cvar->GetFlags() & CVAR_MOD))
@ -4975,7 +4976,7 @@ int DLevelScript::SetUserCVar(int playernum, const char *cvarname, int value, bo
DoSetCVar(cvar, value, is_string);
// If we are this player, then also reflect this change in the local version of this cvar.
if (playernum == consoleplayer)
if (playernum == consoleplayer && Level->isPrimaryLevel())
{
FBaseCVar *cvar = FindCVar(cvarname, NULL);
// If we can find it in the userinfo, then we should also be able to find it in the normal cvar list,
@ -5004,7 +5005,9 @@ int DLevelScript::SetCVar(AActor *activator, const char *cvarname, int value, bo
{
return 0;
}
return SetUserCVar(int(activator->player - players), cvarname, value, is_string);
auto pnum = Level->PlayerNum(activator->player);
if (pnum < 0) return 0;
return SetUserCVar(pnum, cvarname, value, is_string);
}
DoSetCVar(cvar, value, is_string);
return 1;
@ -5388,25 +5391,25 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
case ACSF_GetAirSupply:
{
if (args[0] < 0 || args[0] >= MAXPLAYERS || !playeringame[args[0]])
if (args[0] < 0 || args[0] >= MAXPLAYERS || !Level->PlayerInGame(args[0]))
{
return 0;
}
else
{
return players[args[0]].air_finished - Level->time;
return Level->Players[args[0]]->air_finished - Level->time;
}
}
case ACSF_SetAirSupply:
{
if (args[0] < 0 || args[0] >= MAXPLAYERS || !playeringame[args[0]])
if (args[0] < 0 || args[0] >= MAXPLAYERS || !Level->PlayerInGame(args[0]))
{
return 0;
}
else
{
players[args[0]].air_finished = args[1] + Level->time;
Level->Players[args[0]]->air_finished = args[1] + Level->time;
return 1;
}
}
@ -5420,14 +5423,14 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
case ACSF_GetArmorType:
{
if (args[1] < 0 || args[1] >= MAXPLAYERS || !playeringame[args[1]])
if (args[1] < 0 || args[1] >= MAXPLAYERS || !Level->PlayerInGame(args[1]))
{
return 0;
}
else
{
FName p(Level->Behaviors.LookupString(args[0]));
auto armor = players[args[1]].mo->FindInventory(NAME_BasicArmor);
auto armor = Level->Players[args[1]]->mo->FindInventory(NAME_BasicArmor);
if (armor && armor->NameVar(NAME_ArmorType) == p) return armor->IntVar(NAME_Amount);
}
return 0;
@ -8454,9 +8457,9 @@ scriptwait:
player = activator->player;
}
}
else if (playeringame[STACK(1)-1])
else if (Level->PlayerInGame(STACK(1)-1))
{
player = &players[STACK(1)-1];
player = Level->Players[STACK(1)-1];
}
else
{
@ -8610,6 +8613,7 @@ scriptwait:
{
optstart = sp;
}
if (Level->isPrimaryLevel())
{
AActor *screen = activator;
if (screen != NULL &&
@ -9198,7 +9202,7 @@ scriptwait:
break;
case PCD_USEINVENTORY:
STACK(1) = UseInventory (activator, Level->Behaviors.LookupString (STACK(1)));
STACK(1) = UseInventory (Level, activator, Level->Behaviors.LookupString (STACK(1)));
break;
case PCD_USEACTORINVENTORY:
@ -9207,7 +9211,7 @@ scriptwait:
const char *type = Level->Behaviors.LookupString(STACK(1));
if (STACK(2) == 0)
{
ret = UseInventory(NULL, type);
ret = UseInventory(Level, NULL, type);
}
else
{
@ -9215,7 +9219,7 @@ scriptwait:
AActor *actor;
for (actor = it.Next(); actor != NULL; actor = it.Next())
{
ret += UseInventory(actor, type);
ret += UseInventory(Level, actor, type);
}
}
STACK(2) = ret;
@ -9655,18 +9659,18 @@ scriptwait:
}
else
{
STACK(1) = playeringame[STACK(1)];
STACK(1) = Level->PlayerInGame(STACK(1));
}
break;
case PCD_PLAYERISBOT:
if (STACK(1) < 0 || STACK(1) >= MAXPLAYERS || !playeringame[STACK(1)])
if (STACK(1) < 0 || STACK(1) >= MAXPLAYERS || !Level->PlayerInGame(STACK(1)))
{
STACK(1) = false;
}
else
{
STACK(1) = (players[STACK(1)].Bot != NULL);
STACK(1) = (Level->Players[STACK(1)]->Bot != nullptr);
}
break;
@ -9858,24 +9862,24 @@ scriptwait:
break;
case PCD_PLAYERCLASS: // [GRB]
if (STACK(1) < 0 || STACK(1) >= MAXPLAYERS || !playeringame[STACK(1)])
if (STACK(1) < 0 || STACK(1) >= MAXPLAYERS || !Level->PlayerInGame(STACK(1)))
{
STACK(1) = -1;
}
else
{
STACK(1) = players[STACK(1)].CurrentPlayerClass;
STACK(1) = Level->Players[STACK(1)]->CurrentPlayerClass;
}
break;
case PCD_GETPLAYERINFO: // [GRB]
if (STACK(2) < 0 || STACK(2) >= MAXPLAYERS || !playeringame[STACK(2)])
if (STACK(2) < 0 || STACK(2) >= MAXPLAYERS || !Level->PlayerInGame(STACK(2)))
{
STACK(2) = -1;
}
else
{
player_t *pl = &players[STACK(2)];
player_t *pl = Level->Players[STACK(2)];
userinfo_t *userinfo = &pl->userinfo;
switch (STACK(1))
{
@ -9973,13 +9977,14 @@ scriptwait:
{
int playernum = STACK(1);
if (playernum < 0 || playernum >= MAXPLAYERS || !playeringame[playernum] || players[playernum].camera == NULL || players[playernum].camera->player != NULL)
if (playernum < 0 || playernum >= MAXPLAYERS || !Level->PlayerInGame(playernum) ||
Level->Players[playernum]->camera == nullptr || Level->Players[playernum]->camera->player != nullptr)
{
STACK(1) = -1;
}
else
{
STACK(1) = players[playernum].camera->tid;
STACK(1) = Level->Players[playernum]->camera->tid;
}
}
break;
@ -10301,8 +10306,8 @@ void FLevelLocals::DoDeferedScripts ()
if (scriptdata)
{
P_GetScriptGoing (this, (unsigned)def->playernum < MAXPLAYERS &&
playeringame[def->playernum] ? players[def->playernum].mo : NULL,
NULL, def->script,
PlayerInGame(def->playernum) ? Players[def->playernum]->mo : nullptr,
nullptr, def->script,
scriptdata, module,
def->args, 3,
def->type == acsdefered_t::defexealways ? ACS_ALWAYS : 0);

View file

@ -1684,18 +1684,20 @@ DEFINE_ACTION_FUNCTION(AActor, CheckIfSeen)
{
PARAM_SELF_PROLOGUE(AActor);
auto Level = self->Level;
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
auto p = Level->Players[i];
// Always check sight from each player.
if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY))
if (P_CheckSight(p->mo, self, SF_IGNOREVISIBILITY))
{
ACTION_RETURN_BOOL(false);
}
// If a player is viewing from a non-player, then check that too.
if (players[i].camera != NULL && players[i].camera->player == NULL &&
P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY))
if (p->camera != nullptr && p->camera->player == NULL &&
P_CheckSight(p->camera, self, SF_IGNOREVISIBILITY))
{
ACTION_RETURN_BOOL(false);
}
@ -1755,18 +1757,20 @@ DEFINE_ACTION_FUNCTION(AActor, CheckSightOrRange)
PARAM_BOOL(twodi);
range *= range;
for (int i = 0; i < MAXPLAYERS; ++i)
auto Level = self->Level;
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
auto p = Level->Players[i];
// Always check from each player.
if (DoCheckSightOrRange(self, players[i].mo, range, twodi, true))
if (DoCheckSightOrRange(self, p->mo, range, twodi, true))
{
ACTION_RETURN_BOOL(false);
}
// If a player is viewing from a non-player, check that too.
if (players[i].camera != NULL && players[i].camera->player == NULL &&
DoCheckSightOrRange(self, players[i].camera, range, twodi, true))
if (p->camera != nullptr && p->camera->player == nullptr &&
DoCheckSightOrRange(self, p->camera, range, twodi, true))
{
ACTION_RETURN_BOOL(false);
}
@ -1783,18 +1787,20 @@ DEFINE_ACTION_FUNCTION(AActor, CheckRange)
PARAM_BOOL(twodi);
range *= range;
for (int i = 0; i < MAXPLAYERS; ++i)
auto Level = self->Level;
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
auto p = Level->Players[i];
// Always check from each player.
if (DoCheckSightOrRange(self, players[i].mo, range, twodi, false))
if (DoCheckSightOrRange(self, p->mo, range, twodi, false))
{
ACTION_RETURN_BOOL(false);
}
// If a player is viewing from a non-player, check that too.
if (players[i].camera != NULL && players[i].camera->player == NULL &&
DoCheckSightOrRange(self, players[i].camera, range, twodi, false))
if (p->camera != nullptr && p->camera->player == nullptr &&
DoCheckSightOrRange(self, p->camera, range, twodi, false))
{
ACTION_RETURN_BOOL(false);
}

View file

@ -738,7 +738,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
int i;
// Make sure this is actually a player.
if (pc == nullptr || pc->player == nullptr || npc == nullptr) return;
if (pc == nullptr || pc->player == nullptr || npc == nullptr || pc->Level != currentUILevel) return;
auto Level = pc->Level;
// [CW] If an NPC is talking to a PC already, then don't let

View file

@ -324,10 +324,6 @@ void P_SpawnParticle(FLevelLocals *Level, const DVector3 &pos, const DVector3 &v
//
void P_RunEffects (FLevelLocals *Level)
{
if (players[consoleplayer].camera == NULL) return;
int pnum = players[consoleplayer].camera->Sector->Index() * Level->sectors.Size();
AActor *actor;
auto iterator = Level->GetThinkerIterator<AActor>();

View file

@ -1005,12 +1005,12 @@ void P_RandomChaseDir (AActor *actor)
{
i = 0;
}
else for (i = pr_newchasedir() & (MAXPLAYERS-1); !playeringame[i]; i = (i+1) & (MAXPLAYERS-1))
else for (i = pr_newchasedir() & (MAXPLAYERS-1); !actor->Level->PlayerInGame(i); i = (i+1) & (MAXPLAYERS-1))
{
}
player = players[i].mo;
player = actor->Level->Players[i]->mo;
}
if (player != NULL && playeringame[i])
if (player != NULL && actor->Level->PlayerInGame(i))
{
if (pr_newchasedir() & 1 || !P_CheckSight (actor, player))
{
@ -1203,7 +1203,7 @@ int P_LookForMonsters (AActor *actor)
AActor *mo;
auto iterator = actor->Level->GetThinkerIterator<AActor>();
if (!P_CheckSight (players[0].mo, actor, SF_SEEPASTBLOCKEVERYTHING))
if (!P_CheckSight (actor->Level->Players[0]->mo, actor, SF_SEEPASTBLOCKEVERYTHING))
{ // Player can't see monster
return false;
}
@ -1592,11 +1592,13 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
// Go back to a player, no matter whether it's visible or not
for (int anyone=0; anyone<=1; anyone++)
{
auto Level = actor->Level;
for (int c=0; c<MAXPLAYERS; c++)
{
if (playeringame[c] && players[c].playerstate==PST_LIVE &&
actor->IsFriend(players[c].mo) &&
(anyone || P_IsVisible(actor, players[c].mo, allaround)))
auto p = Level->Players[i];
if (Level->PlayerInGame(c) && p->playerstate==PST_LIVE &&
actor->IsFriend(p->mo) &&
(anyone || P_IsVisible(actor, p->mo, allaround)))
{
actor->target = players[c].mo;
@ -1623,8 +1625,9 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
} // [SP] if false, and in deathmatch, intentional fall-through
if (!(gameinfo.gametype & (GAME_DoomStrifeChex)) &&
actor->Level->isPrimaryLevel() &&
!multiplayer &&
players[0].health <= 0 &&
actor->Level->Players[0]->health <= 0 &&
actor->goal == NULL &&
gamestate != GS_TITLELEVEL
)
@ -1648,7 +1651,7 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
if (c++ < MAXPLAYERS)
{
pnum = (pnum + 1) & (MAXPLAYERS - 1);
if (!playeringame[pnum])
if (!actor->Level->PlayerInGame(pnum))
continue;
if (actor->TIDtoHate == 0)
@ -1686,12 +1689,12 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
return actor->target == actor->goal && actor->goal != NULL;
}
player = &players[pnum];
player = actor->Level->Players[pnum];
if (!(player->mo->flags & MF_SHOOTABLE))
continue; // not shootable (observer or dead)
if (!((actor->flags ^ player->mo->flags) & MF_FRIENDLY))
if (actor->IsFriend(player->mo))
continue; // same +MF_FRIENDLY, ignore
if (player->cheats & CF_NOTARGET)
@ -2284,7 +2287,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
if (actor->FriendPlayer != 0)
{
player = &players[actor->FriendPlayer - 1];
player = actor->Level->Players[actor->FriendPlayer - 1];
}
else
{
@ -2293,10 +2296,10 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
{
i = 0;
}
else for (i = pr_newchasedir() & (MAXPLAYERS-1); !playeringame[i]; i = (i+1) & (MAXPLAYERS-1))
else for (i = pr_newchasedir() & (MAXPLAYERS-1); !actor->Level->PlayerInGame(i); i = (i+1) & (MAXPLAYERS-1))
{
}
player = &players[i];
player = actor->Level->Players[i];
}
if (player->attacker && player->attacker->health > 0 && player->attacker->flags & MF_SHOOTABLE && pr_newchasedir() < 80)
{
@ -3037,7 +3040,7 @@ int CheckBossDeath (AActor *actor)
// make sure there is a player alive for victory
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].health > 0)
if (actor->Level->PlayerInGame(i) && actor->Level->Players[i]->health > 0)
break;
if (i == MAXPLAYERS)

View file

@ -547,11 +547,11 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
}
}
}
else if (!multiplayer && CountsAsKill())
else if (!multiplayer && CountsAsKill() && Level->isPrimaryLevel())
{
// count all monster deaths,
// even those caused by other monsters
players[0].killcount++;
Level->Players[0]->killcount++;
}
if (player)
@ -577,11 +577,12 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
//Added by MC: Discard enemies.
for (int i = 0; i < MAXPLAYERS; i++)
{
if (players[i].Bot != NULL && this == players[i].Bot->enemy)
DBot *Bot = Level->Players[i]->Bot;
if (Bot != nullptr && this == Bot->enemy)
{
if (players[i].Bot->dest == players[i].Bot->enemy)
players[i].Bot->dest = nullptr;
players[i].Bot->enemy = nullptr;
if (Bot->dest == Bot->enemy)
Bot->dest = nullptr;
Bot->enemy = nullptr;
}
}

View file

@ -2835,24 +2835,25 @@ FUNC(LS_ChangeCamera)
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
if (!Level->PlayerInGame(i))
continue;
AActor *oldcamera = players[i].camera;
auto p = Level->Players[i];
AActor *oldcamera = p->camera;
if (camera)
{
players[i].camera = camera;
p->camera = camera;
if (arg2)
players[i].cheats |= CF_REVERTPLEASE;
p->cheats |= CF_REVERTPLEASE;
}
else
{
players[i].camera = players[i].mo;
players[i].cheats &= ~CF_REVERTPLEASE;
p->camera = p->mo;
p->cheats &= ~CF_REVERTPLEASE;
}
if (oldcamera != players[i].camera)
if (oldcamera != p->camera)
{
R_ClearPastViewer (players[i].camera);
R_ClearPastViewer (p->camera);
}
}
}
@ -2977,14 +2978,15 @@ FUNC(LS_SetPlayerProperty)
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].mo == NULL)
auto p = Level->Players[i];
if (!Level->PlayerInGame(i) || p->mo == nullptr)
continue;
if (arg1)
{ // Give power
if (power != 4)
{
auto item = players[i].mo->GiveInventoryType ((PClass::FindActor(powers[power])));
auto item = p->mo->GiveInventoryType ((PClass::FindActor(powers[power])));
if (item != NULL && power == 0 && arg1 == 1)
{
item->ColorVar(NAME_BlendColor) = MakeSpecialColormap(INVERSECOLORMAP);
@ -2999,7 +3001,7 @@ FUNC(LS_SetPlayerProperty)
{ // Take power
if (power != 4)
{
auto item = players[i].mo->FindInventory (PClass::FindActor(powers[power]));
auto item = p->mo->FindInventory (PClass::FindActor(powers[power]));
if (item != NULL)
{
item->Destroy ();
@ -3072,25 +3074,26 @@ FUNC(LS_SetPlayerProperty)
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
if (!Level->PlayerInGame(i))
continue;
auto p = Level->Players[i];
if (arg1)
{
players[i].cheats |= mask;
p->cheats |= mask;
if (arg2 == PROP_FLY)
{
players[i].mo->flags2 |= MF2_FLY;
players[i].mo->flags |= MF_NOGRAVITY;
p->mo->flags2 |= MF2_FLY;
p->mo->flags |= MF_NOGRAVITY;
}
}
else
{
players[i].cheats &= ~mask;
p->cheats &= ~mask;
if (arg2 == PROP_FLY)
{
players[i].mo->flags2 &= ~MF2_FLY;
players[i].mo->flags &= ~MF_NOGRAVITY;
p->mo->flags2 &= ~MF2_FLY;
p->mo->flags &= ~MF_NOGRAVITY;
}
}
}
@ -3310,9 +3313,9 @@ FUNC(LS_GlassBreak)
{
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
it = players[i].mo;
it = Level->Players[i]->mo;
break;
}
}

View file

@ -945,16 +945,17 @@ DEFINE_ACTION_FUNCTION(AActor, GiveBody)
bool AActor::CheckLocalView (int playernum) const
{
if (players[playernum].camera == this)
auto p = &players[playernum];
if (p->camera == this)
{
return true;
}
if (players[playernum].mo != this || players[playernum].camera == NULL)
if (p->mo != this || p->camera == nullptr)
{
return false;
}
if (players[playernum].camera->player == NULL &&
!(players[playernum].camera->flags3 & MF3_ISMONSTER))
if (p->camera->player == NULL &&
!(p->camera->flags3 & MF3_ISMONSTER))
{
return true;
}
@ -985,6 +986,10 @@ bool AActor::IsInsideVisibleAngles() const
if (players[consoleplayer].camera == nullptr)
return true;
// Not detectable.
if (!Level->isPrimaryLevel())
return true;
DAngle anglestart = VisibleStartAngle;
DAngle angleend = VisibleEndAngle;
DAngle pitchstart = VisibleStartPitch;
@ -3264,7 +3269,7 @@ bool AActor::IsOkayToAttack (AActor *link)
AActor * Friend;
if (flags5 & MF5_SUMMONEDMONSTER) Friend = tracer;
else if (flags2 & MF2_SEEKERMISSILE) Friend = target;
else if ((flags & MF_FRIENDLY) && FriendPlayer) Friend = players[FriendPlayer-1].mo;
else if ((flags & MF_FRIENDLY) && FriendPlayer) Friend = Level->Players[FriendPlayer-1]->mo;
else Friend = this;
// Friend checks
@ -5052,9 +5057,9 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag
for (int ii = 0; ii < MAXPLAYERS; ++ii)
{
if (playeringame[ii] && players[ii].camera == oldactor)
if (PlayerInGame(ii) && Players[ii]->camera == oldactor)
{
players[ii].camera = mobj;
Players[ii]->camera = mobj;
}
}
@ -6825,13 +6830,13 @@ bool AActor::IsFriend (AActor *other)
if (deathmatch && teamplay)
return IsTeammate(other) ||
(FriendPlayer != 0 && other->FriendPlayer != 0 &&
players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo));
Level->Players[FriendPlayer-1]->mo->IsTeammate(Level->Players[other->FriendPlayer-1]->mo));
return !deathmatch ||
FriendPlayer == other->FriendPlayer ||
FriendPlayer == 0 ||
other->FriendPlayer == 0 ||
players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo);
Level->Players[FriendPlayer-1]->mo->IsTeammate(Level->Players[other->FriendPlayer-1]->mo);
}
// [SP] If friendly flags match, then they are on the same team.
/*if (!((flags ^ other->flags) & MF_FRIENDLY))
@ -6858,13 +6863,13 @@ bool AActor::IsHostile (AActor *other)
if (deathmatch && teamplay)
return !IsTeammate(other) &&
!(FriendPlayer != 0 && other->FriendPlayer != 0 &&
players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo));
Level->Players[FriendPlayer-1]->mo->IsTeammate(Level->Players[other->FriendPlayer-1]->mo));
return deathmatch &&
FriendPlayer != other->FriendPlayer &&
FriendPlayer !=0 &&
other->FriendPlayer != 0 &&
!players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo);
!Level->Players[FriendPlayer-1]->mo->IsTeammate(Level->Players[other->FriendPlayer-1]->mo);
}
return true;
}

View file

@ -573,7 +573,7 @@ void P_SerializePlayers(FLevelLocals *Level, FSerializer &arc, bool skipload)
// Count the number of players present right now.
for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
++numPlayersNow;
}
@ -879,16 +879,16 @@ void FLevelLocals::SpawnExtraPlayers()
// be sure to spawn the extra players.
int i;
if (deathmatch)
if (deathmatch || !isPrimaryLevel())
{
return;
}
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo == NULL)
if (PlayerInGame(i) && Players[i]->mo == NULL)
{
players[i].playerstate = PST_ENTER;
Players[i]->playerstate = PST_ENTER;
SpawnPlayer(&playerstarts[i], i, (flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
}
@ -1019,9 +1019,9 @@ void FLevelLocals::Serialize(FSerializer &arc, bool hubload)
}
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo != nullptr)
if (PlayerInGame(i) && Players[i]->mo != nullptr)
{
FWeaponSlots::SetupWeaponSlots(players[i].mo);
FWeaponSlots::SetupWeaponSlots(Players[i]->mo);
}
}
RecreateAllAttachedLights();

View file

@ -378,13 +378,13 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
Level->SetMusicVolume(Level->MusicVolume);
for (i = 0; i < MAXPLAYERS; ++i)
{
players[i].killcount = players[i].secretcount
= players[i].itemcount = 0;
Level->Players[i]->killcount = Level->Players[i]->secretcount
= Level->Players[i]->itemcount = 0;
}
}
for (i = 0; i < MAXPLAYERS; ++i)
{
players[i].mo = nullptr;
Level->Players[i]->mo = nullptr;
}
// [RH] Clear any scripted translation colors the previous level may have set.
for (i = 0; i < int(translationtables[TRANSLATION_LevelScripted].Size()); ++i)
@ -439,9 +439,9 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
players[i].mo = nullptr;
Level->Players[i]->mo = nullptr;
Level->DeathMatchSpawnPlayer(i);
}
}
@ -451,9 +451,9 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
{
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
if (Level->PlayerInGame(i))
{
players[i].mo = nullptr;
Level->Players[i]->mo = nullptr;
FPlayerStart *mthing = Level->PickPlayerStart(i);
Level->SpawnPlayer(mthing, i, (Level->flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
@ -466,11 +466,12 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
{
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo != nullptr)
auto p = Level->Players[i];
if (Level->PlayerInGame(i) && p->mo != nullptr)
{
if (!(players[i].mo->flags & MF_FRIENDLY))
if (!(p->mo->flags & MF_FRIENDLY))
{
AActor * oldSpawn = players[i].mo;
AActor * oldSpawn = p->mo;
Level->DeathMatchSpawnPlayer(i);
oldSpawn->Destroy();
}