- 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 "actor.h"
#include "d_player.h" #include "d_player.h"
#include "p_local.h" #include "p_local.h"
#include "g_levellocals.h"
//========================================================================== //==========================================================================
// //
@ -61,14 +62,18 @@
Only one selector of each type can be used. 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) AActor *COPY_AAPTR(AActor *origin, int selector)
{ {
if (selector == AAPTR_DEFAULT) return origin; if (selector == AAPTR_DEFAULT) return origin;
FTranslatedLineTarget t; 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)
{ {
if (origin->player) if (origin->player)
@ -97,7 +102,7 @@ AActor *COPY_AAPTR(AActor *origin, int selector)
return t.linetarget; return t.linetarget;
} }
} }
switch (selector & AAPTR_STATIC_SELECTORS) switch (selector & AAPTR_STATIC_SELECTORS)
{ {
case AAPTR_PLAYER1: return AAPTR_RESOLVE_PLAYERNUM(0); case AAPTR_PLAYER1: return AAPTR_RESOLVE_PLAYERNUM(0);

View File

@ -154,12 +154,12 @@ void FS_EmulateCmd(FLevelLocals *Level, char * string)
{ {
sc.MustGetFloat(); sc.MustGetFloat();
double playerviewheight = sc.Float; 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! // 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; if (p->mo != nullptr) p->mo->FloatVar(NAME_ViewHeight) = playerviewheight;
players[i].viewheight = playerviewheight; p->viewheight = playerviewheight;
players[i].Uncrouch(); p->Uncrouch();
} }
while (sc.GetString()) while (sc.GetString())
{ {

View File

@ -278,7 +278,7 @@ int FParser::T_GetPlayerNum(const svalue_t &arg)
{ {
return -1; return -1;
} }
if(!playeringame[playernum]) // no error, just return -1 if(!Level->PlayerInGame(playernum)) // no error, just return -1
{ {
return -1; return -1;
} }
@ -288,7 +288,7 @@ int FParser::T_GetPlayerNum(const svalue_t &arg)
AActor *FParser::T_GetPlayerActor(const svalue_t &arg) AActor *FParser::T_GetPlayerActor(const svalue_t &arg)
{ {
int num = T_GetPlayerNum(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) PClassActor *T_ClassType(const svalue_t &arg)
@ -659,7 +659,7 @@ void FParser::SF_PlayerTip(void)
if (CheckArgs(1)) if (CheckArgs(1))
{ {
int plnum = T_GetPlayerNum(t_argv[0]); 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()); C_MidPrint(SmallFont, GetFormatString(1).GetChars());
} }
@ -692,7 +692,7 @@ void FParser::SF_PlayerMsg(void)
if (CheckArgs(1)) if (CheckArgs(1))
{ {
int plnum = T_GetPlayerNum(t_argv[0]); 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()); Printf(PRINT_HIGH, "%s\n", GetFormatString(1).GetChars());
} }
@ -714,7 +714,7 @@ void FParser::SF_PlayerInGame(void)
if (plnum!=-1) if (plnum!=-1)
{ {
t_return.type = svt_int; 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) if(plnum !=-1)
{ {
t_return.type = svt_string; t_return.type = svt_string;
t_return.string = players[plnum].userinfo.GetName(); t_return.string = Level->Players[plnum]->userinfo.GetName();
} }
else else
{ {
@ -773,7 +773,7 @@ void FParser::SF_PlayerObj(void)
if(plnum !=-1) if(plnum !=-1)
{ {
t_return.type = svt_mobj; t_return.type = svt_mobj;
t_return.value.mobj = players[plnum].mo; t_return.value.mobj = Level->Players[plnum]->mo;
} }
else else
{ {
@ -1402,8 +1402,8 @@ void FParser::SF_SetCamera(void)
if (CheckArgs(1)) if (CheckArgs(1))
{ {
player=Script->trigger->player; player = Script->trigger->player;
if (!player) player=&players[0]; if (!player) player = Level->Players[0];
newcamera = actorvalue(t_argv[0]); newcamera = actorvalue(t_argv[0]);
if(!newcamera) if(!newcamera)
@ -1435,8 +1435,8 @@ void FParser::SF_SetCamera(void)
void FParser::SF_ClearCamera(void) void FParser::SF_ClearCamera(void)
{ {
player_t * player; player_t * player;
player=Script->trigger->player; player = Script->trigger->player;
if (!player) player=&players[0]; if (!player) player = Level->Players[0];
AActor * cam=player->camera; AActor * cam=player->camera;
if (cam) if (cam)
@ -2419,13 +2419,13 @@ void FParser::SF_PlayerKeys(void)
if(t_argc == 2) if(t_argc == 2)
{ {
t_return.type = svt_int; 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; return;
} }
else else
{ {
givetake = intvalue(t_argv[2]); 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.type = svt_int;
t_return.value.i = 0; t_return.value.i = 0;
} }
@ -2504,14 +2504,14 @@ void FParser::SF_PlayerWeapon()
if (t_argc == 2) 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.type = svt_int;
t_return.value.i = wp!=NULL; t_return.value.i = wp!=NULL;
return; return;
} }
else else
{ {
AActor * wp = players[playernum].mo->FindInventory(ti); AActor * wp = Level->Players[playernum]->mo->FindInventory(ti);
newweapon = !!intvalue(t_argv[2]); newweapon = !!intvalue(t_argv[2]);
if (!newweapon) if (!newweapon)
@ -2520,14 +2520,14 @@ void FParser::SF_PlayerWeapon()
{ {
wp->Destroy(); wp->Destroy();
// If the weapon is active pick a replacement. Legacy didn't do this! // If the weapon is active pick a replacement. Legacy didn't do this!
if (players[playernum].PendingWeapon==wp) players[playernum].PendingWeapon=WP_NOCHANGE; if (Level->Players[playernum]->PendingWeapon==wp) Level->Players[playernum]->PendingWeapon=WP_NOCHANGE;
if (players[playernum].ReadyWeapon==wp) if (Level->Players[playernum]->ReadyWeapon==wp)
{ {
players[playernum].ReadyWeapon=nullptr; Level->Players[playernum]->ReadyWeapon=nullptr;
IFVM(PlayerPawn, PickNewWeapon) IFVM(PlayerPawn, PickNewWeapon)
{ {
VMValue param[] = { players[playernum].mo, (void*)nullptr }; VMValue param[] = { Level->Players[playernum]->mo, (void*)nullptr };
VMCall(func, param, 2, nullptr, 0); VMCall(func, param, 2, nullptr, 0);
} }
} }
@ -2537,9 +2537,9 @@ void FParser::SF_PlayerWeapon()
{ {
if (!wp) if (!wp)
{ {
auto pw=players[playernum].PendingWeapon; auto pw=Level->Players[playernum]->PendingWeapon;
players[playernum].mo->GiveInventoryType(ti); Level->Players[playernum]->mo->GiveInventoryType(ti);
players[playernum].PendingWeapon=pw; Level->Players[playernum]->PendingWeapon=pw;
} }
} }
@ -2588,13 +2588,13 @@ void FParser::SF_PlayerSelectedWeapon()
return; return;
} }
players[playernum].PendingWeapon = players[playernum].mo->FindInventory(ti); Level->Players[playernum]->PendingWeapon = Level->Players[playernum]->mo->FindInventory(ti);
} }
t_return.type = svt_int; t_return.type = svt_int;
for(int i=0;i<9;i++) 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; t_return.value.i=i;
break; break;
@ -2620,7 +2620,7 @@ void FParser::SF_GiveInventory(void)
if(t_argc == 2) count=1; if(t_argc == 2) count=1;
else count=intvalue(t_argv[2]); 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.type = svt_int;
t_return.value.i = 0; t_return.value.i = 0;
} }
@ -2643,7 +2643,7 @@ void FParser::SF_TakeInventory(void)
if(t_argc == 2) count=32767; if(t_argc == 2) count=32767;
else count=intvalue(t_argv[2]); 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.type = svt_int;
t_return.value.i = 0; t_return.value.i = 0;
} }
@ -2668,7 +2668,7 @@ void FParser::SF_CheckInventory(void)
return; return;
} }
t_return.type = svt_int; 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]); playernum1 = T_GetPlayerNum(t_argv[0]);
players[playernum1].fragcount++; Level->Players[playernum1]->fragcount++;
t_return.type = svt_int; t_return.type = svt_int;
t_return.value.f = players[playernum1].fragcount; t_return.value.f = Level->Players[playernum1]->fragcount;
} }
else else
@ -3232,10 +3232,10 @@ void FParser::SF_PlayerAddFrag()
playernum1 = T_GetPlayerNum(t_argv[0]); playernum1 = T_GetPlayerNum(t_argv[0]);
playernum2 = T_GetPlayerNum(t_argv[1]); playernum2 = T_GetPlayerNum(t_argv[1]);
players[playernum1].frags[playernum2]++; Level->Players[playernum1]->frags[playernum2]++;
t_return.type = svt_int; 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 // get the other scripts
// levelscript started by player 0 'superplayer' // 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->Preprocess(Level);
th->LevelScript->ParseScript(nullptr, th); 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 we get here, that means the actor isn't holding an appropriate key.
if (owner == players[consoleplayer].camera) if (owner->CheckLocalView(consoleplayer))
{ {
PrintMessage(failtext); PrintMessage(failtext);

View File

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

View File

@ -48,6 +48,7 @@
#include "p_spec.h" #include "p_spec.h"
#include "actor.h" #include "actor.h"
#include "p_effect.h" #include "p_effect.h"
#include "d_player.h"
#include "p_destructible.h" #include "p_destructible.h"
#include "r_data/r_sections.h" #include "r_data/r_sections.h"
#include "r_data/r_canvastexture.h" #include "r_data/r_canvastexture.h"
@ -102,7 +103,15 @@ struct FLevelLocals
{ {
void *level; void *level;
void *Level; // bug catchers. 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; friend class MapLoader;
@ -493,6 +502,42 @@ public:
TObjPtr<AActor*> bodyque[BODYQUESIZE]; TObjPtr<AActor*> bodyque[BODYQUESIZE];
TObjPtr<DAutomapBase*> automap = nullptr; TObjPtr<DAutomapBase*> automap = nullptr;
int bodyqueslot; 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; int NumMapSections;

View File

@ -122,9 +122,9 @@ void DEarthquake::Tick ()
{ {
for (i = 0; i < MAXPLAYERS; i++) 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; double dist;
dist = m_Spot->Distance2D(victim, true); 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) for (int i = 0; i < MAXPLAYERS; ++i)
{ {
if (playeringame[i] && players[i].mo != nullptr) if (Level->PlayerInGame(i) && Level->Players[i]->mo != nullptr)
players[i].health = players[i].mo->health; Level->Players[i]->health = Level->Players[i]->mo->health;
} }
if (!map->HasBehavior && !map->isText) if (!map->HasBehavior && !map->isText)
TranslateTeleportThings(); // [RH] Assign teleport destination TIDs 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; PClassActor *info;
int ret = 0; int ret = 0;
@ -1743,8 +1743,8 @@ static int UseInventory (AActor *activator, const char *type)
{ {
for (int i = 0; i < MAXPLAYERS; ++i) for (int i = 0; i < MAXPLAYERS; ++i)
{ {
if (playeringame[i]) if (Level->PlayerInGame(i))
ret += DoUseInv (players[i].mo, info); ret += DoUseInv (Level->Players[i]->mo, info);
} }
} }
else else
@ -1759,6 +1759,7 @@ static int UseInventory (AActor *activator, const char *type)
// CheckInventory // CheckInventory
// //
// Returns how much of a particular item an actor has. // 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; int count = 0, i;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (Level->PlayerInGame(i))
count++; count++;
return count; return count;
@ -3850,9 +3851,9 @@ void DLevelScript::DoFadeRange (int r1, int g1, int b1, int a1,
{ {
for (i = 0; i < MAXPLAYERS; ++i) for (i = 0; i < MAXPLAYERS; ++i)
{ {
if (playeringame[i]) if (Level->PlayerInGame(i))
{ {
viewer = &players[i]; viewer = Level->Players[i];
showme: showme:
if (ftime <= 0.f) if (ftime <= 0.f)
{ {
@ -3965,7 +3966,7 @@ int DoGetMasterTID (AActor *self)
if (self->master) return self->master->tid; if (self->master) return self->master->tid;
else if (self->FriendPlayer) else if (self->FriendPlayer)
{ {
player_t *player = &players[(self->FriendPlayer)-1]; player_t *player = self->Level->Players[(self->FriendPlayer)-1];
return player->mo->tid; return player->mo->tid;
} }
else return 0; else return 0;
@ -4504,13 +4505,13 @@ int DLevelScript::GetPlayerInput(int playernum, int inputnum)
} }
p = activator->player; p = activator->player;
} }
else if (playernum >= MAXPLAYERS || !playeringame[playernum]) else if (playernum >= MAXPLAYERS || !Level->PlayerInGame(playernum))
{ {
return 0; return 0;
} }
else else
{ {
p = &players[playernum]; p = Level->Players[playernum];
} }
if (p == NULL) 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) 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; 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; FBaseCVar *cvar;
// Only mod-created cvars may be set. // Only mod-created cvars may be set.
if (cvar_p == NULL || (cvar = *cvar_p) == NULL || (cvar->GetFlags() & CVAR_IGNORE) || !(cvar->GetFlags() & CVAR_MOD)) 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); DoSetCVar(cvar, value, is_string);
// If we are this player, then also reflect this change in the local version of this cvar. // 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); 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, // 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 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); DoSetCVar(cvar, value, is_string);
return 1; return 1;
@ -5388,25 +5391,25 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
case ACSF_GetAirSupply: 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; return 0;
} }
else else
{ {
return players[args[0]].air_finished - Level->time; return Level->Players[args[0]]->air_finished - Level->time;
} }
} }
case ACSF_SetAirSupply: 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; return 0;
} }
else else
{ {
players[args[0]].air_finished = args[1] + Level->time; Level->Players[args[0]]->air_finished = args[1] + Level->time;
return 1; return 1;
} }
} }
@ -5420,14 +5423,14 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
case ACSF_GetArmorType: 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; return 0;
} }
else else
{ {
FName p(Level->Behaviors.LookupString(args[0])); 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); if (armor && armor->NameVar(NAME_ArmorType) == p) return armor->IntVar(NAME_Amount);
} }
return 0; return 0;
@ -8454,9 +8457,9 @@ scriptwait:
player = activator->player; 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 else
{ {
@ -8610,6 +8613,7 @@ scriptwait:
{ {
optstart = sp; optstart = sp;
} }
if (Level->isPrimaryLevel())
{ {
AActor *screen = activator; AActor *screen = activator;
if (screen != NULL && if (screen != NULL &&
@ -9198,7 +9202,7 @@ scriptwait:
break; break;
case PCD_USEINVENTORY: case PCD_USEINVENTORY:
STACK(1) = UseInventory (activator, Level->Behaviors.LookupString (STACK(1))); STACK(1) = UseInventory (Level, activator, Level->Behaviors.LookupString (STACK(1)));
break; break;
case PCD_USEACTORINVENTORY: case PCD_USEACTORINVENTORY:
@ -9207,7 +9211,7 @@ scriptwait:
const char *type = Level->Behaviors.LookupString(STACK(1)); const char *type = Level->Behaviors.LookupString(STACK(1));
if (STACK(2) == 0) if (STACK(2) == 0)
{ {
ret = UseInventory(NULL, type); ret = UseInventory(Level, NULL, type);
} }
else else
{ {
@ -9215,7 +9219,7 @@ scriptwait:
AActor *actor; AActor *actor;
for (actor = it.Next(); actor != NULL; actor = it.Next()) for (actor = it.Next(); actor != NULL; actor = it.Next())
{ {
ret += UseInventory(actor, type); ret += UseInventory(Level, actor, type);
} }
} }
STACK(2) = ret; STACK(2) = ret;
@ -9655,18 +9659,18 @@ scriptwait:
} }
else else
{ {
STACK(1) = playeringame[STACK(1)]; STACK(1) = Level->PlayerInGame(STACK(1));
} }
break; break;
case PCD_PLAYERISBOT: 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; STACK(1) = false;
} }
else else
{ {
STACK(1) = (players[STACK(1)].Bot != NULL); STACK(1) = (Level->Players[STACK(1)]->Bot != nullptr);
} }
break; break;
@ -9858,24 +9862,24 @@ scriptwait:
break; break;
case PCD_PLAYERCLASS: // [GRB] 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; STACK(1) = -1;
} }
else else
{ {
STACK(1) = players[STACK(1)].CurrentPlayerClass; STACK(1) = Level->Players[STACK(1)]->CurrentPlayerClass;
} }
break; break;
case PCD_GETPLAYERINFO: // [GRB] 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; STACK(2) = -1;
} }
else else
{ {
player_t *pl = &players[STACK(2)]; player_t *pl = Level->Players[STACK(2)];
userinfo_t *userinfo = &pl->userinfo; userinfo_t *userinfo = &pl->userinfo;
switch (STACK(1)) switch (STACK(1))
{ {
@ -9973,13 +9977,14 @@ scriptwait:
{ {
int playernum = STACK(1); 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; STACK(1) = -1;
} }
else else
{ {
STACK(1) = players[playernum].camera->tid; STACK(1) = Level->Players[playernum]->camera->tid;
} }
} }
break; break;
@ -10301,8 +10306,8 @@ void FLevelLocals::DoDeferedScripts ()
if (scriptdata) if (scriptdata)
{ {
P_GetScriptGoing (this, (unsigned)def->playernum < MAXPLAYERS && P_GetScriptGoing (this, (unsigned)def->playernum < MAXPLAYERS &&
playeringame[def->playernum] ? players[def->playernum].mo : NULL, PlayerInGame(def->playernum) ? Players[def->playernum]->mo : nullptr,
NULL, def->script, nullptr, def->script,
scriptdata, module, scriptdata, module,
def->args, 3, def->args, 3,
def->type == acsdefered_t::defexealways ? ACS_ALWAYS : 0); def->type == acsdefered_t::defexealways ? ACS_ALWAYS : 0);

View File

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

View File

@ -738,7 +738,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
int i; int i;
// Make sure this is actually a player. // 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; auto Level = pc->Level;
// [CW] If an NPC is talking to a PC already, then don't let // [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) void P_RunEffects (FLevelLocals *Level)
{ {
if (players[consoleplayer].camera == NULL) return;
int pnum = players[consoleplayer].camera->Sector->Index() * Level->sectors.Size();
AActor *actor; AActor *actor;
auto iterator = Level->GetThinkerIterator<AActor>(); auto iterator = Level->GetThinkerIterator<AActor>();

View File

@ -1005,12 +1005,12 @@ void P_RandomChaseDir (AActor *actor)
{ {
i = 0; 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)) if (pr_newchasedir() & 1 || !P_CheckSight (actor, player))
{ {
@ -1203,7 +1203,7 @@ int P_LookForMonsters (AActor *actor)
AActor *mo; AActor *mo;
auto iterator = actor->Level->GetThinkerIterator<AActor>(); 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 { // Player can't see monster
return false; 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 // Go back to a player, no matter whether it's visible or not
for (int anyone=0; anyone<=1; anyone++) for (int anyone=0; anyone<=1; anyone++)
{ {
auto Level = actor->Level;
for (int c=0; c<MAXPLAYERS; c++) for (int c=0; c<MAXPLAYERS; c++)
{ {
if (playeringame[c] && players[c].playerstate==PST_LIVE && auto p = Level->Players[i];
actor->IsFriend(players[c].mo) && if (Level->PlayerInGame(c) && p->playerstate==PST_LIVE &&
(anyone || P_IsVisible(actor, players[c].mo, allaround))) actor->IsFriend(p->mo) &&
(anyone || P_IsVisible(actor, p->mo, allaround)))
{ {
actor->target = players[c].mo; 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 } // [SP] if false, and in deathmatch, intentional fall-through
if (!(gameinfo.gametype & (GAME_DoomStrifeChex)) && if (!(gameinfo.gametype & (GAME_DoomStrifeChex)) &&
actor->Level->isPrimaryLevel() &&
!multiplayer && !multiplayer &&
players[0].health <= 0 && actor->Level->Players[0]->health <= 0 &&
actor->goal == NULL && actor->goal == NULL &&
gamestate != GS_TITLELEVEL gamestate != GS_TITLELEVEL
) )
@ -1648,7 +1651,7 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
if (c++ < MAXPLAYERS) if (c++ < MAXPLAYERS)
{ {
pnum = (pnum + 1) & (MAXPLAYERS - 1); pnum = (pnum + 1) & (MAXPLAYERS - 1);
if (!playeringame[pnum]) if (!actor->Level->PlayerInGame(pnum))
continue; continue;
if (actor->TIDtoHate == 0) 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; return actor->target == actor->goal && actor->goal != NULL;
} }
player = &players[pnum]; player = actor->Level->Players[pnum];
if (!(player->mo->flags & MF_SHOOTABLE)) if (!(player->mo->flags & MF_SHOOTABLE))
continue; // not shootable (observer or dead) continue; // not shootable (observer or dead)
if (!((actor->flags ^ player->mo->flags) & MF_FRIENDLY)) if (actor->IsFriend(player->mo))
continue; // same +MF_FRIENDLY, ignore continue; // same +MF_FRIENDLY, ignore
if (player->cheats & CF_NOTARGET) if (player->cheats & CF_NOTARGET)
@ -2284,7 +2287,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
if (actor->FriendPlayer != 0) if (actor->FriendPlayer != 0)
{ {
player = &players[actor->FriendPlayer - 1]; player = actor->Level->Players[actor->FriendPlayer - 1];
} }
else else
{ {
@ -2293,10 +2296,10 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
{ {
i = 0; 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) 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 // make sure there is a player alive for victory
for (i = 0; i < MAXPLAYERS; i++) 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; break;
if (i == MAXPLAYERS) 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, // count all monster deaths,
// even those caused by other monsters // even those caused by other monsters
players[0].killcount++; Level->Players[0]->killcount++;
} }
if (player) if (player)
@ -577,11 +577,12 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
//Added by MC: Discard enemies. //Added by MC: Discard enemies.
for (int i = 0; i < MAXPLAYERS; i++) 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) if (Bot->dest == Bot->enemy)
players[i].Bot->dest = nullptr; Bot->dest = nullptr;
players[i].Bot->enemy = nullptr; Bot->enemy = nullptr;
} }
} }

View File

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

View File

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

View File

@ -378,13 +378,13 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
Level->SetMusicVolume(Level->MusicVolume); Level->SetMusicVolume(Level->MusicVolume);
for (i = 0; i < MAXPLAYERS; ++i) for (i = 0; i < MAXPLAYERS; ++i)
{ {
players[i].killcount = players[i].secretcount Level->Players[i]->killcount = Level->Players[i]->secretcount
= players[i].itemcount = 0; = Level->Players[i]->itemcount = 0;
} }
} }
for (i = 0; i < MAXPLAYERS; ++i) 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. // [RH] Clear any scripted translation colors the previous level may have set.
for (i = 0; i < int(translationtables[TRANSLATION_LevelScripted].Size()); ++i) 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++) 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); Level->DeathMatchSpawnPlayer(i);
} }
} }
@ -451,9 +451,9 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
{ {
for (i = 0; i < MAXPLAYERS; ++i) 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); FPlayerStart *mthing = Level->PickPlayerStart(i);
Level->SpawnPlayer(mthing, i, (Level->flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0); 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) 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); Level->DeathMatchSpawnPlayer(i);
oldSpawn->Destroy(); oldSpawn->Destroy();
} }