- removed level references from p_mobj.cpp.

This time there was one important exported script function: Actor.Spawn.
Since this will require a level pointer in the new scheme of things the old version had to be deprecated, because it is static with no argument that allows retrieving the level. However, since this is probably one of the most widely used functions I added a workaround to let it continue to work if used from inside an actor class, which should constitute >95% of all uses. This required a little bit of hackery in the compiler backend to swap out the function if appropriate.
Aside from that there were 5 places in the internal ZScript that needed handling, which mostly consisted of making a formerly static internal function non-static.
This commit is contained in:
Christoph Oelckers 2019-01-07 00:51:18 +01:00
parent 7d060dc696
commit 480dd347c9
33 changed files with 221 additions and 175 deletions

View file

@ -651,7 +651,7 @@ public:
virtual void PostBeginPlay() override; // Called immediately before the actor's first tick
virtual void Tick() override;
static AActor *StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false);
static AActor *StaticSpawn (FLevelLocals *Level, PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false);
inline AActor *GetDefault () const
{
@ -1569,34 +1569,34 @@ int P_FindUniqueTID(int start_tid, int limit);
PClassActor *ClassForSpawn(FName classname);
inline AActor *Spawn(PClassActor *type)
inline AActor *Spawn(FLevelLocals *l, PClassActor *type)
{
return AActor::StaticSpawn(type, DVector3(0, 0, 0), NO_REPLACE);
return AActor::StaticSpawn(l, type, DVector3(0, 0, 0), NO_REPLACE);
}
inline AActor *Spawn(PClassActor *type, const DVector3 &pos, replace_t allowreplacement)
inline AActor *Spawn(FLevelLocals *l, PClassActor *type, const DVector3 &pos, replace_t allowreplacement)
{
return AActor::StaticSpawn(type, pos, allowreplacement);
return AActor::StaticSpawn(l, type, pos, allowreplacement);
}
inline AActor *Spawn(FName type)
inline AActor *Spawn(FLevelLocals *l, FName type)
{
return AActor::StaticSpawn(ClassForSpawn(type), DVector3(0, 0, 0), NO_REPLACE);
return AActor::StaticSpawn(l, ClassForSpawn(type), DVector3(0, 0, 0), NO_REPLACE);
}
inline AActor *Spawn(FName type, const DVector3 &pos, replace_t allowreplacement)
inline AActor *Spawn(FLevelLocals *l, FName type, const DVector3 &pos, replace_t allowreplacement)
{
return AActor::StaticSpawn(ClassForSpawn(type), pos, allowreplacement);
return AActor::StaticSpawn(l, ClassForSpawn(type), pos, allowreplacement);
}
template<class T> inline T *Spawn(const DVector3 &pos, replace_t allowreplacement)
template<class T> inline T *Spawn(FLevelLocals *l, const DVector3 &pos, replace_t allowreplacement)
{
return static_cast<T *>(AActor::StaticSpawn(RUNTIME_CLASS(T), pos, allowreplacement));
return static_cast<T *>(AActor::StaticSpawn(l, RUNTIME_CLASS(T), pos, allowreplacement));
}
template<class T> inline T *Spawn() // for inventory items we do not need coordinates and replacement info.
template<class T> inline T *Spawn(FLevelLocals *l) // for inventory items we do not need coordinates and replacement info.
{
return static_cast<T *>(AActor::StaticSpawn(RUNTIME_CLASS(T), DVector3(0, 0, 0), NO_REPLACE));
return static_cast<T *>(AActor::StaticSpawn(l, RUNTIME_CLASS(T), DVector3(0, 0, 0), NO_REPLACE));
}
inline PClassActor *PClass::FindActor(FName name)

View file

@ -129,7 +129,7 @@ public:
void StartTravel ();
void FinishTravel ();
bool IsLeader (player_t *player);
void SetBodyAt (const DVector3 &pos, int hostnum);
void SetBodyAt (FLevelLocals *l, const DVector3 &pos, int hostnum);
double FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd);
bool SafeCheckPosition (AActor *actor, double x, double y, FCheckPosition &tm);
void BotTick(AActor *mo);

View file

@ -235,7 +235,7 @@ void DBot::Dofire (ticcmd_t *cmd)
// prediction aiming
Dist = player->mo->Distance2D(enemy);
fm = Dist / GetDefaultByType (GetBotInfo(player->ReadyWeapon).projectileType)->Speed;
bglobal.SetBodyAt(enemy->Pos() + enemy->Vel.XY() * fm * 2, 1);
bglobal.SetBodyAt(enemy->__GetLevel(), enemy->Pos() + enemy->Vel.XY() * fm * 2, 1);
Angle = player->mo->AngleTo(bglobal.body1);
if (Check_LOS (enemy, SHOOTFOV))
no_fire = false;
@ -479,7 +479,7 @@ AActor *DBot::Find_enemy ()
//Creates a temporary mobj (invisible) at the given location.
void FCajunMaster::SetBodyAt (const DVector3 &pos, int hostnum)
void FCajunMaster::SetBodyAt (FLevelLocals *l, const DVector3 &pos, int hostnum)
{
if (hostnum == 1)
{
@ -489,7 +489,7 @@ void FCajunMaster::SetBodyAt (const DVector3 &pos, int hostnum)
}
else
{
body1 = Spawn ("CajunBodyNode", pos, NO_REPLACE);
body1 = Spawn (l, "CajunBodyNode", pos, NO_REPLACE);
}
}
else if (hostnum == 2)
@ -500,7 +500,7 @@ void FCajunMaster::SetBodyAt (const DVector3 &pos, int hostnum)
}
else
{
body2 = Spawn ("CajunBodyNode", pos, NO_REPLACE);
body2 = Spawn (l, "CajunBodyNode", pos, NO_REPLACE);
}
}
}
@ -517,7 +517,7 @@ void FCajunMaster::SetBodyAt (const DVector3 &pos, int hostnum)
//Emulates missile travel. Returns distance travelled.
double FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd)
{
AActor *th = Spawn ("CajunTrace", source->PosPlusZ(4*8.), NO_REPLACE);
AActor *th = Spawn (source->__GetLevel(), "CajunTrace", source->PosPlusZ(4*8.), NO_REPLACE);
th->target = source; // where it came from
@ -543,7 +543,7 @@ DAngle DBot::FireRox (AActor *enemy, ticcmd_t *cmd)
AActor *actor;
double m;
bglobal.SetBodyAt(player->mo->PosPlusZ(player->mo->Height / 2) + player->mo->Vel.XY() * 5, 2);
bglobal.SetBodyAt(player->mo->__GetLevel(), player->mo->PosPlusZ(player->mo->Height / 2) + player->mo->Vel.XY() * 5, 2);
actor = bglobal.body2;
@ -553,7 +553,7 @@ DAngle DBot::FireRox (AActor *enemy, ticcmd_t *cmd)
//Predict.
m = ((dist+1) / GetDefaultByName("Rocket")->Speed);
bglobal.SetBodyAt(DVector3((enemy->Pos() + enemy->Vel * (m + 2)), ONFLOORZ), 1);
bglobal.SetBodyAt(player->mo->__GetLevel(), DVector3((enemy->Pos() + enemy->Vel * (m + 2)), ONFLOORZ), 1);
//try the predicted location
if (P_CheckSight (actor, bglobal.body1, SF_IGNOREVISIBILITY)) //See the predicted location, so give a test missile

View file

@ -2334,7 +2334,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
const AActor *def = GetDefaultByType (typeinfo);
DVector3 spawnpos = source->Vec3Angle(def->radius * 2 + source->radius, source->Angles.Yaw, 8.);
AActor *spawned = Spawn (typeinfo, spawnpos, ALLOW_REPLACE);
AActor *spawned = Spawn (source->__GetLevel(), typeinfo, spawnpos, ALLOW_REPLACE);
if (spawned != NULL)
{
if (type == DEM_SUMMONFRIEND || type == DEM_SUMMONFRIEND2 || type == DEM_SUMMONMBF)

View file

@ -881,7 +881,7 @@ void FParser::SF_Spawn(void)
}
t_return.type = svt_mobj;
t_return.value.mobj = Spawn(pclass, pos, ALLOW_REPLACE);
t_return.value.mobj = Spawn(Level, pclass, pos, ALLOW_REPLACE);
if (t_return.value.mobj)
{
@ -2930,7 +2930,7 @@ void FParser::SF_SpawnExplosion()
else
pos.Z = P_PointInSector(pos)->floorplane.ZatPoint(pos);
spawn = Spawn (pclass, pos, ALLOW_REPLACE);
spawn = Spawn (Level, pclass, pos, ALLOW_REPLACE);
t_return.type = svt_int;
t_return.value.i=0;
if (spawn)
@ -3761,7 +3761,7 @@ void FParser::SF_SpawnShot2(void)
t_return.type = svt_mobj;
AActor *mo = Spawn(pclass, source->PosPlusZ(z), ALLOW_REPLACE);
AActor *mo = Spawn(Level, pclass, source->PosPlusZ(z), ALLOW_REPLACE);
if (mo)
{
S_Sound(mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM);

View file

@ -1493,7 +1493,7 @@ void G_DeathMatchSpawnPlayer (int playernum)
}
}
}
AActor *mo = P_SpawnPlayer(spot, playernum);
AActor *mo = P_SpawnPlayer(&level, spot, playernum);
if (mo != NULL) P_PlayerStartStomp(mo);
}
@ -1638,13 +1638,13 @@ void G_DoReborn (int playernum, bool freshbot)
level.playerstarts[playernum].type != 0 &&
G_CheckSpot (playernum, &level.playerstarts[playernum]))
{
AActor *mo = P_SpawnPlayer(&level.playerstarts[playernum], playernum);
AActor *mo = P_SpawnPlayer(&level, &level.playerstarts[playernum], playernum);
if (mo != NULL) P_PlayerStartStomp(mo, true);
}
else
{ // try to spawn at any random player's spot
FPlayerStart *start = G_PickPlayerStart(playernum, PPS_FORCERANDOM);
AActor *mo = P_SpawnPlayer(start, playernum);
AActor *mo = P_SpawnPlayer(&level, start, playernum);
if (mo != NULL) P_PlayerStartStomp(mo, true);
}
}

View file

@ -1366,7 +1366,7 @@ int G_FinishTravel ()
// The player being spawned here is a short lived dummy and
// must not start any ENTER script or big problems will happen.
pawndup = P_SpawnPlayer(start, pnum, SPF_TEMPPLAYER);
pawndup = P_SpawnPlayer(pawn->__GetLevel(), start, pnum, SPF_TEMPPLAYER);
if (pawndup != NULL)
{
if (!(changeflags & CHANGELEVEL_KEEPFACING))

View file

@ -462,7 +462,7 @@ void cht_DoCheat (player_t *player, int cheat)
if (player->mo != NULL && player->health >= 0)
{
static VMFunction *gsp = nullptr;
if (gsp == nullptr) PClass::FindFunction(&gsp, NAME_Sigil, NAME_GiveSigilPiece);
if (gsp == nullptr) PClass::FindFunction(&gsp, NAME_Actor, NAME_GiveSigilPiece);
if (gsp)
{
VMValue params[1] = { player->mo };

View file

@ -111,7 +111,7 @@ void BloodCrypt (void *data, int key, int len);
void P_ClearUDMFKeys();
void InitRenderInfo();
extern AActor *SpawnMapThing (int index, FMapThing *mthing, int position);
extern AActor *SpawnMapThing (FLevelLocals *Level, int index, FMapThing *mthing, int position);
EXTERN_CVAR(Bool, am_textured)
@ -1380,7 +1380,7 @@ void MapLoader::SpawnThings (int position)
for (int i=0; i < numthings; i++)
{
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
AActor *actor = SpawnMapThing (Level, i, &MapThingsConverted[i], position);
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
if (udi != nullptr)
{

View file

@ -1070,4 +1070,5 @@ xx(SpotOuterAngle)
xx(lightflags)
xx(lighttype)
xx(InternalDynamicLight)
xx(_a_chase_default)
xx(_a_chase_default)
xx(Spawn2)

View file

@ -3737,7 +3737,7 @@ int DLevelScript::DoSpawn (int type, const DVector3 &pos, int tid, DAngle angle,
return 0;
}
actor = Spawn (info, pos, ALLOW_REPLACE);
actor = Spawn (&level, info, pos, ALLOW_REPLACE);
if (actor != NULL)
{
ActorFlags2 oldFlags2 = actor->flags2;

View file

@ -871,7 +871,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RadiusDamageSelf)
// the player to indicate bad things happened.
AActor *flash = NULL;
if(flashtype != NULL)
flash = Spawn(flashtype, self->target->PosPlusZ(self->target->Height / 4), ALLOW_REPLACE);
flash = Spawn(self->__GetLevel(), flashtype, self->target->PosPlusZ(self->target->Height / 4), ALLOW_REPLACE);
int dmgFlags = 0;
FName dmgType = NAME_BFGSplash;
@ -1575,7 +1575,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpawnDebris)
double xo = (pr_spawndebris() - 128) / 16.;
double yo = (pr_spawndebris() - 128) / 16.;
double zo = pr_spawndebris()*self->Height / 256 + self->GetBobOffset();
mo = Spawn(debris, self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
mo = Spawn(self->__GetLevel(), debris, self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
if (transfer_translation)
@ -1911,7 +1911,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Burst)
double xo = (pr_burst() - 128) * self->radius / 128;
double yo = (pr_burst() - 128) * self->radius / 128;
double zo = (pr_burst() * self->Height / 255);
mo = Spawn(chunk, self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
mo = Spawn(self->__GetLevel(), chunk, self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
@ -3102,7 +3102,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Teleport)
P_SpawnTeleportFog(ref, prev, true, true);
else
{
fog1 = Spawn(fog_type, prev, ALLOW_REPLACE);
fog1 = Spawn(self->__GetLevel(), fog_type, prev, ALLOW_REPLACE);
if (fog1 != NULL)
fog1->target = ref;
}
@ -3113,7 +3113,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Teleport)
P_SpawnTeleportFog(ref, ref->Pos(), false, true);
else
{
fog2 = Spawn(fog_type, ref->Pos(), ALLOW_REPLACE);
fog2 = Spawn(self->__GetLevel(), fog_type, ref->Pos(), ALLOW_REPLACE);
if (fog2 != NULL)
fog2->target = ref;
}
@ -3641,7 +3641,7 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo
if ((flags & RGF_NOSIGHT) || P_CheckSight(thing, self, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
{ // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight.
auto gift = Spawn(item);
auto gift = Spawn(thing->__GetLevel(), item);
if (gift->IsKindOf(NAME_Health))
{
gift->IntVar(NAME_Amount) *= amount;

View file

@ -961,7 +961,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
if (takestuff)
{
auto item = Spawn(reply->GiveType);
auto item = Spawn(player->mo->__GetLevel(), reply->GiveType);
// Items given here should not count as items!
item->ClearCounters();
if (item->GetClass()->TypeName == NAME_FlameThrower)

View file

@ -879,7 +879,7 @@ void P_DrawRailTrail(AActor *source, TArray<SPortalHit> &portalhits, int color1,
if (rnd & 4)
diff.Z = clamp<double>(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff);
}
AActor *thing = Spawn (spawnclass, pos + diff, ALLOW_REPLACE);
AActor *thing = Spawn (source->__GetLevel(), spawnclass, pos + diff, ALLOW_REPLACE);
if (thing)
{
if (source) thing->target = source;

View file

@ -3285,13 +3285,13 @@ FUNC(LS_GlassBreak)
{
if (type != nullptr)
{
glass = Spawn(*type, DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
glass = Spawn(Level, *type, DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
glass->AddZ(24.);
}
}
else
{
glass = Spawn("GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
glass = Spawn(Level, "GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
glass->AddZ(24.);
glass->SetState(glass->SpawnState + (pr_glass() % glass->health));
}

View file

@ -50,6 +50,7 @@ struct secplane_t;
struct FCheckPosition;
struct FTranslatedLineTarget;
struct FLinePortal;
struct FLevelLocals;
#include <stdlib.h>
@ -97,7 +98,7 @@ void P_PredictionLerpReset();
#define SPF_TEMPPLAYER 1 // spawning a short-lived dummy player
#define SPF_WEAPONFULLYUP 2 // spawn with weapon already raised
AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags=0);
AActor *P_SpawnPlayer (FLevelLocals *Level, FPlayerStart *mthing, int playernum, int flags=0);
int P_FaceMobj (AActor *source, AActor *target, DAngle *delta);
bool P_SeekerMissile (AActor *actor, double thresh, double turnMax, bool precise = false, bool usecurspeed=false);

View file

@ -4442,7 +4442,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
AActor *tempuff = NULL;
if (pufftype != NULL)
tempuff = Spawn(pufftype, t1->Pos(), ALLOW_REPLACE);
tempuff = Spawn(t1->__GetLevel(), pufftype, t1->Pos(), ALLOW_REPLACE);
if (tempuff != NULL)
{
TData.PuffSpecies = tempuff->GetSpecies();
@ -5187,7 +5187,7 @@ void P_RailAttack(FRailParams *p)
// used as damage inflictor
AActor *thepuff = NULL;
if (puffclass != NULL) thepuff = Spawn(puffclass, source->Pos(), ALLOW_REPLACE);
if (puffclass != NULL) thepuff = Spawn(source->__GetLevel(), puffclass, source->Pos(), ALLOW_REPLACE);
rail_data.PuffSpecies = (thepuff != NULL) ? thepuff->GetSpecies() : NAME_None;
Trace(start, source->Sector, vec, p->distance, MF_SHOOTABLE, ML_BLOCKEVERYTHING, source, trace, flags, ProcessRailHit, &rail_data);
@ -6183,7 +6183,7 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos)
{
AActor *mo;
mo = Spawn(bloodcls, thing->PosPlusZ(thing->Height / 2), ALLOW_REPLACE);
mo = Spawn(thing->__GetLevel(), bloodcls, thing->PosPlusZ(thing->Height / 2), ALLOW_REPLACE);
mo->Vel.X = pr_crunch.Random2() / 16.;
mo->Vel.Y = pr_crunch.Random2() / 16.;

View file

@ -739,7 +739,7 @@ AActor *AActor::GiveInventoryType (PClassActor *type)
{
if (type != nullptr)
{
auto item = Spawn (type);
auto item = Spawn (__GetLevel(), type);
if (!CallTryPickup (item, this))
{
item->Destroy ();
@ -784,7 +784,8 @@ void AActor::ClearInventory()
void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth)
{
level.total_monsters -= CountsAsKill();
auto Level = __GetLevel();
Level->total_monsters -= CountsAsKill();
TIDtoHate = other->TIDtoHate;
LastLookActor = other->LastLookActor;
LastLookPlayerNumber = other->LastLookPlayerNumber;
@ -799,7 +800,7 @@ void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealt
LastHeard = target = other->target;
}
if (resetHealth) health = SpawnHealth();
level.total_monsters += CountsAsKill();
Level->total_monsters += CountsAsKill();
}
DEFINE_ACTION_FUNCTION(AActor, CopyFriendliness)
@ -1226,7 +1227,7 @@ bool AActor::Grind(bool items)
return false;
}
AActor *gib = Spawn (i, Pos(), ALLOW_REPLACE);
AActor *gib = Spawn (__GetLevel(), i, Pos(), ALLOW_REPLACE);
if (gib != NULL)
{
gib->RenderStyle = RenderStyle;
@ -1793,6 +1794,7 @@ double P_XYMovement (AActor *mo, DVector2 scroll)
DVector2 start;
double Oldfloorz = mo->floorz;
double oldz = mo->Z();
auto Level = mo->__GetLevel();
double maxmove = (mo->waterlevel < 1) || (mo->flags & MF_MISSILE) ||
(mo->player && mo->player->crouchoffset<-10) ? MAXMOVE : MAXMOVE/4;
@ -2225,15 +2227,15 @@ explode:
(!(mo->flags2 & MF2_FLY) || !(mo->flags & MF_NOGRAVITY)) &&
!mo->waterlevel)
{ // [RH] Friction when falling is available for larger aircontrols
if (player != NULL && level.airfriction != 1.)
if (player != NULL && Level->airfriction != 1.)
{
mo->Vel.X *= level.airfriction;
mo->Vel.Y *= level.airfriction;
mo->Vel.X *= Level->airfriction;
mo->Vel.Y *= Level->airfriction;
if (player->mo == mo) // Not voodoo dolls
{
player->Vel.X *= level.airfriction;
player->Vel.Y *= level.airfriction;
player->Vel.X *= Level->airfriction;
player->Vel.Y *= Level->airfriction;
}
}
return Oldfloorz;
@ -2333,10 +2335,11 @@ explode:
// Move this to p_inter ***
void P_MonsterFallingDamage (AActor *mo)
{
auto Level = mo->__GetLevel();
int damage;
double vel;
if (!(level.flags2 & LEVEL2_MONSTERFALLINGDAMAGE))
if (!(Level->flags2 & LEVEL2_MONSTERFALLINGDAMAGE))
return;
if (mo->floorsector->Flags & SECF_NOFALLINGDAMAGE)
return;
@ -2464,6 +2467,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
mo->SetZ(mo->floorz + mo->specialf1);
}
auto Level = mo->__GetLevel();
//
// adjust height
@ -2484,7 +2488,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
{
if (!mo->IsNoClip2())
{
mo->AddZ(DAngle(360 / 80.f * level.maptime).Sin() / 8);
mo->AddZ(DAngle(360 / 80.f * Level->maptime).Sin() / 8);
}
if (!(mo->flags8 & MF8_NOFRICTION))
@ -2818,7 +2822,7 @@ void P_NightmareRespawn (AActor *mobj)
z = ONFLOORZ;
// spawn it
mo = AActor::StaticSpawn(mobj->GetClass(), DVector3(mobj->SpawnPoint.X, mobj->SpawnPoint.Y, z), NO_REPLACE, true);
mo = AActor::StaticSpawn(mobj->__GetLevel(), mobj->GetClass(), DVector3(mobj->SpawnPoint.X, mobj->SpawnPoint.Y, z), NO_REPLACE, true);
mo->health = mobj->SpawnHealth();
if (z == ONFLOORZ)
@ -3516,6 +3520,8 @@ void AActor::Tick ()
return;
}
auto Level = __GetLevel();
if (flags5 & MF5_NOINTERACTION)
{
// only do the minimally necessary things here to save time:
@ -3526,7 +3532,7 @@ void AActor::Tick ()
if (!(flags5 & MF5_NOTIMEFREEZE))
{
//Added by MC: Freeze mode.
if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN)
if (bglobal.freeze || Level->flags2 & LEVEL2_FROZEN)
{
// Boss cubes shouldn't be accelerated by timefreeze
if (flags6 & MF6_BOSSCUBE)
@ -3587,7 +3593,7 @@ void AActor::Tick ()
}
// Apply freeze mode.
if ((level.flags2 & LEVEL2_FROZEN) && (player == NULL || player->timefreezer == 0))
if ((Level->flags2 & LEVEL2_FROZEN) && (player == NULL || player->timefreezer == 0))
{
return;
}
@ -3600,7 +3606,7 @@ void AActor::Tick ()
{
// add some smoke behind the rocket
smokecounter = 0;
AActor *th = Spawn("RocketSmokeTrail", Vec3Offset(-Vel), ALLOW_REPLACE);
AActor *th = Spawn(__GetLevel(), "RocketSmokeTrail", Vec3Offset(-Vel), ALLOW_REPLACE);
if (th)
{
th->tics -= pr_rockettrail()&3;
@ -3618,7 +3624,7 @@ void AActor::Tick ()
double xo = -moveangle.Cos() * radius * 2 + pr_rockettrail() / 64.;
double yo = -moveangle.Sin() * radius * 2 + pr_rockettrail() / 64.;
double zo = -Height * Vel.Z / 8. + Height * (2 / 3.);
AActor * th = Spawn("GrenadeSmokeTrail", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
AActor * th = Spawn(__GetLevel(), "GrenadeSmokeTrail", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (th)
{
th->tics -= pr_rockettrail()&3;
@ -3704,7 +3710,7 @@ void AActor::Tick ()
// [RH] Consider carrying sectors here
DVector2 cumm(0, 0);
if ((((flags8 & MF8_INSCROLLSEC) && level.Scrolls.Size() > 0) || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR))
if ((((flags8 & MF8_INSCROLLSEC) && Level->Scrolls.Size() > 0) || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR))
{
double height, waterheight; // killough 4/4/98: add waterheight
const msecnode_t *node;
@ -3728,9 +3734,9 @@ void AActor::Tick ()
sector_t *sec = node->m_sector;
DVector2 scrollv;
if (level.Scrolls.Size() > unsigned(sec->Index()))
if (Level->Scrolls.Size() > unsigned(sec->Index()))
{
scrollv = level.Scrolls[sec->Index()];
scrollv = Level->Scrolls[sec->Index()];
}
else
{
@ -3931,7 +3937,7 @@ void AActor::Tick ()
{
if (player)
{
if (Vel.Z < level.gravity * Sector->gravity * (-1./100)// -655.36f)
if (Vel.Z < Level->gravity * Sector->gravity * (-1./100)// -655.36f)
&& !(flags&MF_NOGRAVITY))
{
PlayerLandedOnThing (this, onmo);
@ -3959,12 +3965,12 @@ void AActor::Tick ()
if ((onmo->flags6 & MF6_BUMPSPECIAL) && ((player != NULL)
|| ((onmo->activationtype & THINGSPEC_MonsterTrigger) && (flags3 & MF3_ISMONSTER))
|| ((onmo->activationtype & THINGSPEC_MissileTrigger) && (flags & MF_MISSILE))
) && (level.maptime > onmo->lastbump)) // Leave the bumper enough time to go away
) && (Level->maptime > onmo->lastbump)) // Leave the bumper enough time to go away
{
if (player == NULL || !(player->cheats & CF_PREDICTING))
{
if (P_ActivateThingSpecial(onmo, this))
onmo->lastbump = level.maptime + TICRATE;
onmo->lastbump = Level->maptime + TICRATE;
}
}
if (Vel.Z != 0 && (BounceFlags & BOUNCE_Actors))
@ -4005,7 +4011,7 @@ void AActor::Tick ()
}
// Check for poison damage, but only once per PoisonPeriod tics (or once per second if none).
if (PoisonDurationReceived && (level.time % (PoisonPeriodReceived ? PoisonPeriodReceived : TICRATE) == 0))
if (PoisonDurationReceived && (Level->time % (PoisonPeriodReceived ? PoisonPeriodReceived : TICRATE) == 0))
{
P_DamageMobj(this, NULL, Poisoner, PoisonDamageReceived, PoisonDamageTypeReceived ? PoisonDamageTypeReceived : (FName)NAME_Poison, 0);
@ -4059,7 +4065,7 @@ void AActor::Tick ()
if (movecount < respawn_monsters)
return;
if (level.time & 31)
if (Level->time & 31)
return;
if (pr_nightmarerespawn() > 4)
@ -4352,7 +4358,8 @@ bool AActor::UpdateWaterLevel(bool dosplash)
else if (oldlevel == 3 && waterlevel < 3)
{
// Our head just came up.
if (player->air_finished > level.time)
auto Level = __GetLevel();
if (player->air_finished > Level->time)
{
// We hadn't run out of air yet.
S_Sound(this, CHAN_VOICE, "*surface", 1, ATTN_NORM);
@ -4377,7 +4384,7 @@ DEFINE_ACTION_FUNCTION(AActor, UpdateWaterLevel)
//
//==========================================================================
AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing)
AActor *AActor::StaticSpawn (FLevelLocals *Level, PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing)
{
if (type == NULL)
{
@ -4392,8 +4399,8 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a
AActor *actor;
actor = static_cast<AActor *>(const_cast<PClassActor *>(type)->CreateNew ());
actor->SpawnTime = level.totaltime;
actor->SpawnOrder = level.spawnindex++;
actor->SpawnTime = Level->totaltime;
actor->SpawnOrder = Level->spawnindex++;
// Set default dialogue
actor->ConversationRoot = GetConversation(actor->GetClass()->TypeName);
@ -4540,39 +4547,39 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a
return NULL;
}
}
if (level.flags & LEVEL_NOALLIES && !actor->player)
if (Level->flags & LEVEL_NOALLIES && !actor->player)
{
actor->flags &= ~MF_FRIENDLY;
}
// [RH] Count monsters whenever they are spawned.
if (actor->CountsAsKill())
{
level.total_monsters++;
Level->total_monsters++;
}
// [RH] Same, for items
if (actor->flags & MF_COUNTITEM)
{
level.total_items++;
Level->total_items++;
}
// And for secrets
if (actor->flags5 & MF5_COUNTSECRET)
{
level.total_secrets++;
Level->total_secrets++;
}
// force scroller check in the first tic.
actor->flags8 |= MF8_INSCROLLSEC;
return actor;
}
DEFINE_ACTION_FUNCTION(AActor, Spawn)
DEFINE_ACTION_FUNCTION(FLevelLocals, Spawn)
{
PARAM_PROLOGUE;
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
PARAM_CLASS_NOT_NULL(type, AActor);
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
PARAM_INT(flags);
ACTION_RETURN_OBJECT(AActor::StaticSpawn(type, DVector3(x, y, z), replace_t(flags)));
ACTION_RETURN_OBJECT(AActor::StaticSpawn(self, type, DVector3(x, y, z), replace_t(flags)));
}
PClassActor *ClassForSpawn(FName classname)
@ -4606,6 +4613,7 @@ void AActor::LevelSpawned ()
void AActor::HandleSpawnFlags ()
{
auto Level = __GetLevel();
if (SpawnFlags & MTF_AMBUSH)
{
flags |= MF_AMBUSH;
@ -4625,7 +4633,7 @@ void AActor::HandleSpawnFlags ()
if (flags & MF_COUNTKILL)
{
flags &= ~MF_COUNTKILL;
level.total_monsters--;
Level->total_monsters--;
}
}
if (SpawnFlags & MTF_SHADOW)
@ -4644,7 +4652,7 @@ void AActor::HandleSpawnFlags ()
{
//Printf("Secret %s in sector %i!\n", GetTag(), Sector->sectornum);
flags5 |= MF5_COUNTSECRET;
level.total_secrets++;
Level->total_secrets++;
}
}
}
@ -4896,7 +4904,7 @@ DEFINE_ACTION_FUNCTION(AActor, AdjustFloorClip)
//
// P_SpawnPlayer
// Called when a player is spawned on the level.
// Called when a player is spawned on the Level->
// Most of the player structure stays unchanged between levels.
//
EXTERN_CVAR (Bool, chasedemo)
@ -4905,7 +4913,7 @@ EXTERN_CVAR(Float, fov)
extern bool demonew;
AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
AActor *P_SpawnPlayer (FLevelLocals *Level, FPlayerStart *mthing, int playernum, int flags)
{
player_t *p;
AActor *mobj, *oldactor;
@ -4989,9 +4997,9 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
spawn.Z = ONFLOORZ;
}
mobj = Spawn (p->cls, spawn, NO_REPLACE);
mobj = Spawn (Level, p->cls, spawn, NO_REPLACE);
if (level.flags & LEVEL_USEPLAYERSTARTZ)
if (Level->flags & LEVEL_USEPLAYERSTARTZ)
{
if (spawn.Z == ONFLOORZ)
mobj->AddZ(mthing->pos.Z);
@ -5017,7 +5025,7 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
VMValue params[] = { mobj, oldactor };
VMCall(func, params, 2, nullptr, 0);
}
level.Behaviors.StopMyScripts (oldactor); // cancel all ENTER/RESPAWN scripts for the voodoo doll
Level->Behaviors.StopMyScripts (oldactor); // cancel all ENTER/RESPAWN scripts for the voodoo doll
}
// [GRB] Reset skin
@ -5100,7 +5108,7 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
VMCall(func, params, 1, nullptr, 0);
}
}
else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN) || sv_singleplayerrespawn ||
else if ((multiplayer || (Level->flags2 & LEVEL2_ALLOWRESPAWN) || sv_singleplayerrespawn ||
!!G_SkillProperty(SKILLP_PlayerRespawn)) && state == PST_REBORN && oldactor != NULL)
{ // Special inventory handling for respawning in coop
IFVM(PlayerPawn, FilterCoopRespawnInventory)
@ -5146,7 +5154,7 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
{
if (state == PST_ENTER || (state == PST_LIVE && !savegamerestore))
{
level.Behaviors.StartTypedScripts (SCRIPT_Enter, p->mo, true);
Level->Behaviors.StartTypedScripts (SCRIPT_Enter, p->mo, true);
}
else if (state == PST_REBORN)
{
@ -5162,7 +5170,7 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
{
if (th->LastHeard == oldactor) th->LastHeard = NULL;
}
for(auto &sec : level.sectors)
for(auto &sec : Level->sectors)
{
if (sec.SoundTarget == oldactor) sec.SoundTarget = nullptr;
}
@ -5170,7 +5178,7 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
DObject::StaticPointerSubstitution (oldactor, p->mo);
E_PlayerRespawned(int(p - players));
level.Behaviors.StartTypedScripts (SCRIPT_Respawn, p->mo, true);
Level->Behaviors.StartTypedScripts (SCRIPT_Respawn, p->mo, true);
}
}
return mobj;
@ -5183,7 +5191,7 @@ AActor *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
// already be in host byte order.
//
// [RH] position is used to weed out unwanted start spots
AActor *P_SpawnMapThing (FMapThing *mthing, int position)
AActor *P_SpawnMapThing (FLevelLocals *Level, FMapThing *mthing, int position)
{
PClassActor *i;
int mask;
@ -5229,7 +5237,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
{
// count deathmatch start positions
FPlayerStart start(mthing, 0);
level.deathmatchstarts.Push(start);
Level->deathmatchstarts.Push(start);
return NULL;
}
@ -5257,7 +5265,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
}
}
if (pnum == -1 || (level.flags & LEVEL_FILTERSTARTS))
if (pnum == -1 || (Level->flags & LEVEL_FILTERSTARTS))
{
// check for appropriate game type
if (deathmatch)
@ -5322,10 +5330,10 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
// save spots for respawning in network games
FPlayerStart start(mthing, pnum+1);
level.playerstarts[pnum] = start;
if (level.flags2 & LEVEL2_RANDOMPLAYERSTARTS)
Level->playerstarts[pnum] = start;
if (Level->flags2 & LEVEL2_RANDOMPLAYERSTARTS)
{ // When using random player starts, all starts count
level.AllPlayerStarts.Push(start);
Level->AllPlayerStarts.Push(start);
}
else
{ // When not using random player starts, later single player
@ -5333,22 +5341,22 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
// ones are for voodoo dolls and not likely to be ideal for
// spawning regular players.
unsigned i;
for (i = 0; i < level.AllPlayerStarts.Size(); ++i)
for (i = 0; i < Level->AllPlayerStarts.Size(); ++i)
{
if (level.AllPlayerStarts[i].type == pnum+1)
if (Level->AllPlayerStarts[i].type == pnum+1)
{
level.AllPlayerStarts[i] = start;
Level->AllPlayerStarts[i] = start;
break;
}
}
if (i == level.AllPlayerStarts.Size())
if (i == Level->AllPlayerStarts.Size())
{
level.AllPlayerStarts.Push(start);
Level->AllPlayerStarts.Push(start);
}
}
if (!deathmatch && !(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS))
if (!deathmatch && !(Level->flags2 & LEVEL2_RANDOMPLAYERSTARTS))
{
return P_SpawnPlayer(&start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
return P_SpawnPlayer(Level, &start, pnum, (Level->flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
return NULL;
}
@ -5408,7 +5416,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
}
// don't spawn any monsters if -nomonsters
if (((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) && info->flags3 & MF3_ISMONSTER )
if (((Level->flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) && info->flags3 & MF3_ISMONSTER )
{
return NULL;
}
@ -5433,7 +5441,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
else
sz = ONFLOORZ;
mobj = AActor::StaticSpawn (i, DVector3(mthing->pos, sz), NO_REPLACE, true);
mobj = AActor::StaticSpawn (Level, i, DVector3(mthing->pos, sz), NO_REPLACE, true);
if (sz == ONFLOORZ)
{
@ -5560,16 +5568,16 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
//===========================================================================
CVAR(Bool, dumpspawnedthings, false, 0)
AActor *SpawnMapThing(int index, FMapThing *mt, int position)
AActor *SpawnMapThing(FLevelLocals *Level, int index, FMapThing *mt, int position)
{
AActor *spawned = P_SpawnMapThing(mt, position);
AActor *spawned = P_SpawnMapThing(Level, mt, position);
if (dumpspawnedthings)
{
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
spawned ? spawned->GetClass()->TypeName.GetChars() : "(none)");
}
T_AddSpawnedThing(&level, spawned);
T_AddSpawnedThing(Level, spawned);
return spawned;
}
@ -5592,7 +5600,7 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, const DVector3 &pos1
if (pufftype == nullptr) return nullptr;
if (!(flags & PF_NORANDOMZ)) pos.Z += pr_spawnpuff.Random2() / 64.;
puff = Spawn(pufftype, pos, ALLOW_REPLACE);
puff = Spawn(source->__GetLevel(), pufftype, pos, ALLOW_REPLACE);
if (puff == NULL) return NULL;
if ((puff->flags4 & MF4_RANDOMIZE) && puff->tics > 0)
@ -5695,7 +5703,7 @@ void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *origina
if (bloodcls != NULL)
{
th = Spawn(bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
th = Spawn(originator->__GetLevel(), bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
th->Vel.Z = 2;
th->Angles.Yaw = dir;
// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
@ -5802,7 +5810,7 @@ void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle)
{
AActor *mo;
mo = Spawn(bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
mo = Spawn(originator->__GetLevel(), bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
mo->target = originator;
mo->Vel.X = pr_splatter.Random2 () / 64.;
mo->Vel.Y = pr_splatter.Random2() / 64.;
@ -5846,7 +5854,7 @@ void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle)
AActor *mo;
mo = Spawn (bloodcls, pos + add, NO_REPLACE); // GetBloodType already performed the replacement
mo = Spawn (originator->__GetLevel(), bloodcls, pos + add, NO_REPLACE); // GetBloodType already performed the replacement
mo->target = originator;
// colorize the blood!
@ -5900,7 +5908,7 @@ void P_RipperBlood (AActor *mo, AActor *bleeder)
if (bloodcls != NULL)
{
AActor *th;
th = Spawn (bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
th = Spawn (bleeder->__GetLevel(), bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement
// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
if (th->flags5 & MF5_PUFFGETSOWNER) th->target = bleeder;
if (gameinfo.gametype == GAME_Heretic)
@ -6039,18 +6047,19 @@ foundone:
if (thing->Mass < 10)
smallsplash = true;
auto Level = thing->__GetLevel();
if (!(thing->flags3 & MF3_DONTSPLASH))
{
if (smallsplash && splash->SmallSplash)
{
mo = Spawn(splash->SmallSplash, pos, ALLOW_REPLACE);
mo = Spawn(Level, splash->SmallSplash, pos, ALLOW_REPLACE);
if (mo) mo->Floorclip += splash->SmallSplashClip;
}
else
{
if (splash->SplashChunk)
{
mo = Spawn(splash->SplashChunk, pos, ALLOW_REPLACE);
mo = Spawn(Level, splash->SplashChunk, pos, ALLOW_REPLACE);
mo->target = thing;
if (splash->ChunkXVelShift != 255)
{
@ -6064,7 +6073,7 @@ foundone:
}
if (splash->SplashBase)
{
mo = Spawn(splash->SplashBase, pos, ALLOW_REPLACE);
mo = Spawn(Level, splash->SplashBase, pos, ALLOW_REPLACE);
}
if (thing->player && !splash->NoAlert && alert)
{
@ -6344,7 +6353,7 @@ AActor *P_SpawnMissileXYZ (DVector3 pos, AActor *source, AActor *dest, PClassAct
pos.Z -= source->Floorclip;
}
AActor *th = Spawn (type, pos, ALLOW_REPLACE);
AActor *th = Spawn (source->__GetLevel(), type, pos, ALLOW_REPLACE);
P_PlaySpawnSound(th, source);
@ -6455,7 +6464,7 @@ AActor *P_OldSpawnMissile(AActor *source, AActor *owner, AActor *dest, PClassAct
{
return nullptr;
}
AActor *th = Spawn (type, source->PosPlusZ(32.), ALLOW_REPLACE);
AActor *th = Spawn (source->__GetLevel(), type, source->PosPlusZ(32.), ALLOW_REPLACE);
P_PlaySpawnSound(th, source);
th->target = owner; // record missile's originator
@ -6569,7 +6578,7 @@ AActor *P_SpawnMissileAngleZSpeed (AActor *source, double z,
z -= source->Floorclip;
}
mo = Spawn (type, source->PosAtZ(z), ALLOW_REPLACE);
mo = Spawn (source->__GetLevel(), type, source->PosAtZ(z), ALLOW_REPLACE);
P_PlaySpawnSound(mo, source);
if (owner == NULL) owner = source;
@ -6602,7 +6611,7 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnMissileAngleZSpeed)
AActor *P_SpawnSubMissile(AActor *source, PClassActor *type, AActor *target)
{
AActor *other = Spawn(type, source->Pos(), ALLOW_REPLACE);
AActor *other = Spawn(source->__GetLevel(), type, source->Pos(), ALLOW_REPLACE);
if (source == nullptr || type == nullptr)
{
@ -6665,6 +6674,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z,
FTranslatedLineTarget scratch;
AActor *defaultobject = GetDefaultByType(type);
DAngle vrange = nofreeaim ? 35. : 0.;
auto Level = source->__GetLevel();
if (!pLineTarget) pLineTarget = &scratch;
if (!(aimflags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim))
@ -6689,7 +6699,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z,
if (source->player != NULL &&
!nofreeaim &&
level.IsFreelookAllowed() &&
Level->IsFreelookAllowed() &&
source->player->userinfo.GetAimDist() <= 0.5)
{
break;
@ -6699,7 +6709,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z,
if (pLineTarget->linetarget == NULL)
{
an = angle;
if (nofreeaim || !level.IsFreelookAllowed())
if (nofreeaim || !Level->IsFreelookAllowed())
{
pitch = 0.;
}
@ -6717,7 +6727,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z,
}
}
DVector3 pos = source->Vec2OffsetZ(x, y, z);
AActor *MissileActor = Spawn (type, pos, ALLOW_REPLACE);
AActor *MissileActor = Spawn (source->__GetLevel(), type, pos, ALLOW_REPLACE);
if (pMissileActor) *pMissileActor = MissileActor;
P_PlaySpawnSound(MissileActor, source);
MissileActor->target = source;
@ -7143,7 +7153,7 @@ void AActor::Revive()
// [RH] If it's a monster, it gets to count as another kill
if (CountsAsKill())
{
level.total_monsters++;
__GetLevel()->total_monsters++;
}
// [ZZ] resurrect hook
@ -7208,21 +7218,22 @@ void AActor::SetTag(const char *def)
void AActor::ClearCounters()
{
auto Level = __GetLevel();
if (CountsAsKill() && health > 0)
{
level.total_monsters--;
Level->total_monsters--;
flags &= ~MF_COUNTKILL;
}
// Same, for items
if (flags & MF_COUNTITEM)
{
level.total_items--;
Level->total_items--;
flags &= ~MF_COUNTITEM;
}
// And finally for secrets
if (flags5 & MF5_COUNTSECRET)
{
level.total_secrets--;
Level->total_secrets--;
flags5 &= ~MF5_COUNTSECRET;
}
}

View file

@ -908,7 +908,7 @@ static void SpawnExtraPlayers(FLevelLocals *Level)
if (playeringame[i] && players[i].mo == NULL)
{
players[i].playerstate = PST_ENTER;
P_SpawnPlayer(&Level->playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
P_SpawnPlayer(Level, &Level->playerstarts[i], i, (Level->flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
}
}

View file

@ -75,8 +75,6 @@
void P_ClearUDMFKeys();
extern AActor *SpawnMapThing (int index, FMapThing *mthing, int position);
extern unsigned int R_OldBlend;
static void P_Shutdown ();
@ -450,7 +448,7 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
{
players[i].mo = nullptr;
FPlayerStart *mthing = G_PickPlayerStart(i);
P_SpawnPlayer(mthing, i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
P_SpawnPlayer(&level, mthing, i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
}
}

View file

@ -71,7 +71,7 @@ void P_SpawnTeleportFog(AActor *mobj, const DVector3 &pos, bool beforeTele, bool
else
{
double fogDelta = mobj->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT;
mo = Spawn((beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType), DVector3(pos, pos.Z + fogDelta), ALLOW_REPLACE);
mo = Spawn(mobj->__GetLevel(), (beforeTele ? mobj->TeleFogSourceType : mobj->TeleFogDestType), DVector3(pos, pos.Z + fogDelta), ALLOW_REPLACE);
}
if (mo != NULL && setTarget)

View file

@ -81,7 +81,7 @@ bool P_Thing_Spawn (int tid, AActor *source, int type, DAngle angle, bool fog, i
}
while (spot != NULL)
{
mobj = Spawn (kind, spot->Pos(), ALLOW_REPLACE);
mobj = Spawn (spot->__GetLevel(), kind, spot->Pos(), ALLOW_REPLACE);
if (mobj != NULL)
{
@ -302,7 +302,7 @@ bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_nam
{
z -= spot->Floorclip;
}
mobj = Spawn (kind, spot->PosAtZ(z), ALLOW_REPLACE);
mobj = Spawn (spot->__GetLevel(), kind, spot->PosAtZ(z), ALLOW_REPLACE);
if (mobj)
{

View file

@ -2858,7 +2858,7 @@ CCMD (loopsound)
}
else
{
AActor *icon = Spawn("SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE);
AActor *icon = Spawn(&level, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE);
if (icon != NULL)
{
S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE);

View file

@ -7600,7 +7600,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
if (ctx.Class != nullptr)
{
PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error, ctx.Version);
PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error, ctx.Version, true);
if (afd != nullptr)
{
@ -7642,6 +7642,20 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
return nullptr;
}
// Redirect Spawn to another function when called from a non-static method of an actor.
// In this special case the missing Level parameter of the static variant can be worked around
// and deprecation is not needed. The only problem is that it is impossible to declare
// the replacement method in a way that lets it get picked automatically, so it needs to be done here.
if (afd->SymbolName == NAME_Spawn)
{
if ((outerflags & VARF_Method) && ctx.Function->OwningClass->isClass() &&
static_cast<PClassType*>(ctx.Function->OwningClass)->Descriptor->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
PFunction *afd2 = FindClassMemberFunction(ctx.Class, ctx.Class, NAME_Spawn2, ScriptPosition, &error, ctx.Version, true);
if (afd2 != nullptr) afd = afd2;
}
}
auto self = (afd->Variants[0].Flags & VARF_Method) ? new FxSelf(ScriptPosition) : nullptr;
auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, false);
delete this;

View file

@ -236,7 +236,7 @@ PFunction *CreateAnonymousFunction(PContainerType *containingclass, PType *retur
//
//==========================================================================
PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *funccls, FName name, FScriptPosition &sc, bool *error, const VersionInfo &version)
PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *funccls, FName name, FScriptPosition &sc, bool *error, const VersionInfo &version, bool nodeprecated)
{
// Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead.
if (name == NAME_ACS_NamedExecuteWithResult) return nullptr;
@ -263,7 +263,8 @@ PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *func
{
sc.Message(MSG_ERROR, "%s is declared protected and not accessible", symbol->SymbolName.GetChars());
}
else if ((funcsym->Variants[0].Flags & VARF_Deprecated) && funcsym->mVersion <= version)
// ZScript will skip this because it prints its own message.
else if ((funcsym->Variants[0].Flags & VARF_Deprecated) && funcsym->mVersion <= version && !nodeprecated)
{
sc.Message(MSG_WARNING, "Call to deprecated function %s", symbol->SymbolName.GetChars());
}

View file

@ -204,7 +204,7 @@ class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestr
FName CheckCastKludges(FName in);
void SetImplicitArgs(TArray<PType *> *args, TArray<uint32_t> *argflags, TArray<FName> *argnames, PContainerType *cls, uint32_t funcflags, int useflags);
PFunction *CreateAnonymousFunction(PContainerType *containingclass, PType *returntype, int flags);
PFunction *FindClassMemberFunction(PContainerType *cls, PContainerType *funccls, FName name, FScriptPosition &sc, bool *error, const VersionInfo &version);
PFunction *FindClassMemberFunction(PContainerType *cls, PContainerType *funccls, FName name, FScriptPosition &sc, bool *error, const VersionInfo &version, bool nodeprecated = false);
void CreateDamageFunction(PNamespace *ns, const VersionInfo &ver, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
//==========================================================================

View file

@ -636,7 +636,18 @@ class Actor : Thinker native
native bool CheckMissileSpawn(double maxdist);
native bool CheckPosition(Vector2 pos, bool actorsonly = false, FCheckPosition tm = null);
native bool TestMobjLocation();
native static Actor Spawn(class<Actor> type, vector3 pos = (0,0,0), int replace = NO_REPLACE);
// Since this depends on the global level variable it should not be used any longer because this may block compatibility with future features.
deprecated("3.8") static Actor Spawn(class<Actor> type, vector3 pos = (0,0,0), int replace = NO_REPLACE)
{
return level.Spawn(type, pos, replace);
}
// Helper so that not all old code needs to be changed for the above deprecation.
protected Actor Spawn2(class<Actor> type, vector3 pos = (0,0,0), int replace = NO_REPLACE)
{
return Level.Spawn(type, pos, replace);
}
native Actor SpawnMissile(Actor dest, class<Actor> type, Actor owner = null);
native Actor SpawnMissileXYZ(Vector3 pos, Actor dest, Class<Actor> type, bool checkspawn = true, Actor owner = null);
native Actor SpawnMissileZ (double z, Actor dest, class<Actor> type);

View file

@ -389,7 +389,7 @@ extend class Actor
//
//===========================================================================
static bool DoGiveInventory(Actor receiver, bool orresult, class<Inventory> mi, int amount, int setreceiver)
private bool DoGiveInventory(Actor receiver, bool orresult, class<Inventory> mi, int amount, int setreceiver)
{
int paramnum = 0;

View file

@ -706,6 +706,7 @@ struct LevelLocals native
native void StartIntermission(Name type, int state) const;
native SpotState GetSpotState(bool create = true);
native void ReplaceTextures(String from, String to, int flags);
native Actor Spawn(class<Actor> type, vector3 pos = (0,0,0), int replace = NO_REPLACE);
native clearscope bool IsPointInMap(vector3 p);

View file

@ -151,9 +151,9 @@ extend class Actor
A_PlaySound("brain/pain", CHAN_VOICE, 1, false, ATTN_NONE);
}
private static void BrainishExplosion(vector3 pos)
private void BrainishExplosion(vector3 pos)
{
Actor boom = Actor.Spawn("Rocket", pos, NO_REPLACE);
Actor boom = Spawn("Rocket", pos, NO_REPLACE);
if (boom)
{
boom.DeathSound = "misc/brainexplode";

View file

@ -258,7 +258,7 @@ class HolyMissile : Actor
mo.bSkullFly = true;
mo.bMissile = false;
}
HolyTail.SpawnSpiritTail (mo);
SpawnSpiritTail (mo);
}
}
}
@ -558,28 +558,6 @@ class HolyTail : Actor
Stop;
}
//============================================================================
//
// SpawnSpiritTail
//
//============================================================================
static void SpawnSpiritTail (Actor spirit)
{
Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE);
if (tail != null)
{
tail.target = spirit; // parent
for (int i = 1; i < 3; i++)
{
Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE);
tail.tracer = next;
tail = next;
}
tail.tracer = null; // last tail bit
}
}
//============================================================================
//
// CHolyTailFollow
@ -674,3 +652,29 @@ class HolyTailTrail : HolyTail
Goto TailTrail;
}
}
extend class Actor
{
//============================================================================
//
// SpawnSpiritTail
//
//============================================================================
void SpawnSpiritTail (Actor spirit)
{
Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE);
if (tail != null)
{
tail.target = spirit; // parent
for (int i = 1; i < 3; i++)
{
Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE);
tail.tracer = next;
tail = next;
}
tail.tracer = null; // last tail bit
}
}
}

View file

@ -234,7 +234,7 @@ class Korax : Actor
spirit.args[1] = 0; // initial look angle
// Spawn a tail for spirit
HolyTail.SpawnSpiritTail (spirit);
SpawnSpiritTail (spirit);
}
//============================================================================

View file

@ -414,12 +414,16 @@ class Sigil : Weapon
{
if (playeringame[i] && players[i].mo != null)
{
GiveSigilPiece (players[i].mo);
players[i].mo.GiveSigilPiece ();
Destroy ();
}
}
return true;
}
}
extend class Actor
{
//============================================================================
//
@ -430,13 +434,13 @@ class Sigil : Weapon
//
//============================================================================
static int GiveSigilPiece (Actor receiver)
int GiveSigilPiece ()
{
Sigil sigl = Sigil(receiver.FindInventory("Sigil"));
Sigil sigl = Sigil(FindInventory("Sigil"));
if (sigl == null)
{
sigl = Sigil(Spawn("Sigil1"));
if (!sigl.CallTryPickup (receiver))
if (!sigl.CallTryPickup (self))
{
sigl.Destroy ();
}