Merge remote-tracking branch 'gzdoom/master' into qzdoom

# Conflicts:
#	src/r_plane.cpp
#	src/r_plane.h
This commit is contained in:
Magnus Norddahl 2017-01-16 06:03:21 +01:00
commit 1c3440e391
104 changed files with 3243 additions and 2832 deletions

View file

@ -1,11 +1,14 @@
The original Doom source code was released by id Software under the
Doom Source Code License. See doomlic.txt.
Parts of the renderer use code from the BUILD engine by Ken Silverman.
See buildlic.txt.
Parts of the voxel code in the software renderer use code from the
BUILD engine by Ken Silverman. See buildlic.txt.
The majority of original code uses a BSD-like lincese. See bsd.txt.
The OpenGL renderer is released under the LGPL v3, except some bits
of code that were inherited fro ZDoomGL.
This software is based in part on the work of the Independent JPEG Group.
This software uses the 'zlib' general purpose compression library by

View file

@ -1200,10 +1200,8 @@ set (PCH_SOURCES
g_inventory/a_ammo.cpp
g_inventory/a_armor.cpp
g_inventory/a_artifacts.cpp
g_inventory/a_health.cpp
g_inventory/a_keys.cpp
g_inventory/a_pickups.cpp
g_inventory/a_puzzleitems.cpp
g_inventory/a_weaponpiece.cpp
g_inventory/a_weapons.cpp
g_strife/strife_sbar.cpp
@ -1213,9 +1211,6 @@ set (PCH_SOURCES
g_shared/a_lightning.cpp
g_shared/a_morph.cpp
g_shared/a_quake.cpp
g_shared/a_skies.cpp
g_shared/a_soundenvironment.cpp
g_shared/a_soundsequence.cpp
g_shared/a_specialspot.cpp
g_shared/hudmessages.cpp
g_shared/sbarinfo.cpp

View file

@ -22,7 +22,6 @@
#include "d_player.h"
#include "vectors.h"
#include "a_ammo.h"
#include "a_health.h"
static FRandom pr_botmove ("BotMove");
@ -360,7 +359,7 @@ void DBot::WhatToGet (AActor *item)
}
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere)
return;
else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina)
else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina)
return;
if ((dest == NULL ||

View file

@ -76,7 +76,6 @@
#include "vmbuilder.h"
#include "a_armor.h"
#include "a_ammo.h"
#include "a_health.h"
// [SO] Just the way Randy said to do it :)
// [RH] Made this CVAR_SERVERINFO
@ -1971,21 +1970,21 @@ static int PatchMisc (int dummy)
barmor->MaxSaveAmount = deh.MaxArmor;
}
AHealth *health;
health = static_cast<AHealth *> (GetDefaultByName ("HealthBonus"));
AInventory *health;
health = static_cast<AInventory *> (GetDefaultByName ("HealthBonus"));
if (health!=NULL)
{
health->MaxAmount = 2 * deh.MaxHealth;
}
health = static_cast<AHealth *> (GetDefaultByName ("Soulsphere"));
health = static_cast<AInventory *> (GetDefaultByName ("Soulsphere"));
if (health!=NULL)
{
health->Amount = deh.SoulsphereHealth;
health->MaxAmount = deh.MaxSoulsphere;
}
health = static_cast<AHealth *> (GetDefaultByName ("MegasphereHealth"));
health = static_cast<AInventory *> (GetDefaultByName ("MegasphereHealth"));
if (health!=NULL)
{
health->Amount = health->MaxAmount = deh.MegasphereHealth;

View file

@ -2248,11 +2248,11 @@ void Net_DoCommand (int type, BYTE **stream, int player)
if (gamestate == GS_LEVEL && !paused)
{
AInventory *item = players[player].mo->Inventory;
auto pitype = PClass::FindActor(NAME_PuzzleItem);
while (item != NULL)
{
AInventory *next = item->Inventory;
if (item->ItemFlags & IF_INVBAR && !(item->IsKindOf(RUNTIME_CLASS(APuzzleItem))))
if (item->ItemFlags & IF_INVBAR && !(item->IsKindOf(pitype)))
{
players[player].mo->UseInventory (item);
}

View file

@ -480,11 +480,11 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld, bool s
changed += players[i].FixPointers (old, notOld);
}
for (auto &s : sectorPortals)
for (auto &s : level.sectorPortals)
{
if (s.mSkybox == old)
{
s.mSkybox = static_cast<ASkyViewpoint*>(notOld);
s.mSkybox = static_cast<AActor*>(notOld);
changed++;
}
}
@ -566,3 +566,16 @@ DEFINE_ACTION_FUNCTION(DObject, GetClassName)
PARAM_SELF_PROLOGUE(DObject);
ACTION_RETURN_INT(self->GetClass()->TypeName);
}
void *DObject::ScriptVar(FName field, PType *type)
{
auto sym = dyn_cast<PField>(GetClass()->Symbols.FindSymbol(field, true));
if (sym && sym->Type == type)
{
return (((char*)this) + sym->Offset);
}
// This is only for internal use so I_Error is fine.
I_Error("Variable %s not found in %s\n", field.GetChars(), GetClass()->TypeName.GetChars());
return nullptr;
}

View file

@ -39,7 +39,7 @@
#include "i_system.h"
class PClass;
class PType;
class FSerializer;
class DObject;
@ -94,8 +94,6 @@ enum
CLASSREG_PClass,
CLASSREG_PClassActor,
CLASSREG_PClassInventory,
CLASSREG_PClassHealth,
CLASSREG_PClassPuzzleItem,
CLASSREG_PClassWeapon,
CLASSREG_PClassPlayerPawn,
CLASSREG_PClassType,
@ -453,6 +451,8 @@ public:
DObject *GCNext; // Next object in this collection list
uint32 ObjectFlags; // Flags for this object
void *ScriptVar(FName field, PType *type);
public:
DObject ();
DObject (PClass *inClass);
@ -476,6 +476,10 @@ public:
virtual void OnDestroy() {}
void Destroy();
// Add other types as needed.
int &IntVar(FName field);
double &FloatVar(FName field);
// If you need to replace one object with another and want to
// change any pointers from the old object to the new object,
// use this method.

View file

@ -329,7 +329,7 @@ static void MarkRoot()
DThinker::MarkRoots();
FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker);
for (auto &s : sectorPortals)
for (auto &s : level.sectorPortals)
{
Mark(s.mSkybox);
}

View file

@ -51,7 +51,6 @@
#include "doomerrors.h"
#include "fragglescript/t_fs.h"
#include "a_keys.h"
#include "a_health.h"
// MACROS ------------------------------------------------------------------
@ -2609,6 +2608,27 @@ PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue
else BitValue = -1;
}
/* PProperty *****************************************************************/
IMPLEMENT_CLASS(PProperty, false, false)
//==========================================================================
//
// PField - Default Constructor
//
//==========================================================================
PProperty::PProperty()
: PSymbol(NAME_None)
{
}
PProperty::PProperty(FName name, TArray<PField *> &fields)
: PSymbol(name)
{
Variables = std::move(fields);
}
/* PPrototype *************************************************************/
IMPLEMENT_CLASS(PPrototype, false, false)
@ -3094,8 +3114,6 @@ PClass *ClassReg::RegisterClass()
&PClass::RegistrationInfo,
&PClassActor::RegistrationInfo,
&PClassInventory::RegistrationInfo,
&PClassHealth::RegistrationInfo,
&PClassPuzzleItem::RegistrationInfo,
&PClassWeapon::RegistrationInfo,
&PClassPlayerPawn::RegistrationInfo,
&PClassType::RegistrationInfo,
@ -3234,7 +3252,7 @@ DObject *PClass::CreateNew() const
ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem);
InitializeSpecials(mem, Defaults);
return (DObject *)mem;
}
@ -3246,7 +3264,7 @@ DObject *PClass::CreateNew() const
//
//==========================================================================
void PClass::InitializeSpecials(void *addr) const
void PClass::InitializeSpecials(void *addr, void *defaults) const
{
// Once we reach a native class, we can stop going up the family tree,
// since native classes handle initialization natively.
@ -3255,10 +3273,10 @@ void PClass::InitializeSpecials(void *addr) const
return;
}
assert(ParentClass != NULL);
ParentClass->InitializeSpecials(addr);
ParentClass->InitializeSpecials(addr, defaults);
for (auto tao : SpecialInits)
{
tao.first->InitializeValue((BYTE*)addr + tao.second, Defaults == nullptr? nullptr : Defaults + tao.second);
tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second);
}
}
@ -3332,7 +3350,7 @@ void PClass::InitializeDefaults()
{
// Copy parent values from the parent defaults.
assert(ParentClass != NULL);
ParentClass->InitializeSpecials(Defaults);
ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults);
for (const PField *field : Fields)
{
@ -3923,6 +3941,13 @@ PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
return sym;
}
void PSymbolTable::RemoveSymbol(PSymbol *sym)
{
auto mysym = Symbols.CheckKey(sym->SymbolName);
if (mysym == nullptr || *mysym != sym) return;
Symbols.Remove(sym->SymbolName);
}
PSymbol *PSymbolTable::ReplaceSymbol(PSymbol *newsym)
{
// If a symbol with a matching name exists, take its place and return it.

View file

@ -29,11 +29,12 @@ enum
VARF_In = (1<<10),
VARF_Out = (1<<11),
VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures)
VARF_Static = (1<<13), // static class data (by necessity read only.)
VARF_Static = (1<<13),
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
VARF_Override = (1<<15), // overrides a virtual function from the parent class.
VARF_Ref = (1<<16), // argument is passed by reference.
VARF_Transient = (1<<17) // don't auto serialize field.
VARF_Transient = (1<<17), // don't auto serialize field.
VARF_Meta = (1<<18), // static class data (by necessity read only.)
};
// Symbol information -------------------------------------------------------
@ -137,6 +138,8 @@ struct PSymbolTable
// to be in the table with this name, if any.
PSymbol *ReplaceSymbol(PSymbol *sym);
void RemoveSymbol(PSymbol *sym);
// Frees all symbols from this table.
void ReleaseSymbols();
@ -621,6 +624,21 @@ protected:
PField();
};
// Struct/class fields ------------------------------------------------------
// A PField describes a symbol that takes up physical space in the struct.
class PProperty : public PSymbol
{
DECLARE_CLASS(PProperty, PSymbol);
public:
PProperty(FName name, TArray<PField *> &variables);
TArray<PField *> Variables;
protected:
PProperty();
};
// Compound types -----------------------------------------------------------
class PEnum : public PNamedType
@ -807,7 +825,7 @@ protected:
enum { MetaClassNum = CLASSREG_PClassClass };
TArray<FTypeAndOffset> SpecialInits;
void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr) const;
void InitializeSpecials(void *addr, void *defaults) const;
void SetSuper();
public:
typedef PClassClass MetaClass;
@ -1041,4 +1059,15 @@ enum ETypeVal : BYTE
VAL_Class,
};
inline int &DObject::IntVar(FName field)
{
return *(int*)ScriptVar(field, TypeSInt32);
}
inline double &DObject::FloatVar(FName field)
{
return *(double*)ScriptVar(field, TypeFloat64);
}
#endif

View file

@ -186,6 +186,13 @@ private:
int texnum;
};
// This is for the script interface which needs to do casts from int to texture.
class FSetTextureID : public FTextureID
{
public:
FSetTextureID(int v) : FTextureID(v) {}
};
// Screenshot buffer image data types

View file

@ -25,6 +25,7 @@
#include "dsectoreffect.h"
#include "gi.h"
#include "p_local.h"
#include "g_levellocals.h"
#include "p_3dmidtex.h"
#include "r_data/r_interpolate.h"
#include "statnums.h"

View file

@ -2623,7 +2623,7 @@ void FParser::SF_MaxPlayerAmmo()
}
else if(t_argc >= 3)
{
AAmmo * iammo = (AAmmo*)players[playernum].mo->FindInventory(ammotype);
auto iammo = players[playernum].mo->FindInventory(ammotype);
amount = intvalue(t_argv[2]);
if(amount < 0) amount = 0;
if (!iammo)
@ -2637,14 +2637,14 @@ void FParser::SF_MaxPlayerAmmo()
for (AInventory *item = players[playernum].mo->Inventory; item != NULL; item = item->Inventory)
{
if (item->IsKindOf(RUNTIME_CLASS(ABackpackItem)))
if (item->IsKindOf(PClass::FindClass(NAME_BackpackItem)))
{
if (t_argc>=4) amount = intvalue(t_argv[3]);
else amount*=2;
break;
}
}
iammo->BackpackMaxAmount=amount;
((AAmmo*)iammo)->BackpackMaxAmount=amount;
}
t_return.type = svt_int;

View file

@ -84,282 +84,8 @@ PClassActor *AAmmo::GetParentAmmo () const
return static_cast<PClassActor *>(type);
}
//===========================================================================
//
// AAmmo :: HandlePickup
//
//===========================================================================
EXTERN_CVAR(Bool, sv_unlimited_pickup)
bool AAmmo::HandlePickup (AInventory *item)
DEFINE_ACTION_FUNCTION(AAmmo, GetParentAmmo)
{
if (GetClass() == item->GetClass() ||
(item->IsKindOf (RUNTIME_CLASS(AAmmo)) && static_cast<AAmmo*>(item)->GetParentAmmo() == GetClass()))
{
if (Amount < MaxAmount || sv_unlimited_pickup)
{
int receiving = item->Amount;
if (!(item->ItemFlags & IF_IGNORESKILL))
{ // extra ammo in baby mode and nightmare mode
receiving = int(receiving * G_SkillProperty(SKILLP_AmmoFactor));
}
int oldamount = Amount;
if (Amount > 0 && Amount + receiving < 0)
{
Amount = 0x7fffffff;
}
else
{
Amount += receiving;
}
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item->ItemFlags |= IF_PICKUPGOOD;
// If the player previously had this ammo but ran out, possibly switch
// to a weapon that uses it, but only if the player doesn't already
// have a weapon pending.
assert (Owner != NULL);
if (oldamount == 0 && Owner != NULL && Owner->player != NULL)
{
barrier_cast<APlayerPawn *>(Owner)->CheckWeaponSwitch(GetClass());
}
}
return true;
}
return false;
PARAM_SELF_PROLOGUE(AAmmo);
ACTION_RETURN_OBJECT(self->GetParentAmmo());
}
//===========================================================================
//
// AAmmo :: CreateCopy
//
//===========================================================================
AInventory *AAmmo::CreateCopy (AActor *other)
{
AInventory *copy;
int amount = Amount;
// extra ammo in baby mode and nightmare mode
if (!(ItemFlags&IF_IGNORESKILL))
{
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
}
if (GetClass()->ParentClass != RUNTIME_CLASS(AAmmo) && GetClass() != RUNTIME_CLASS(AAmmo))
{
PClassActor *type = GetParentAmmo();
if (!GoAway ())
{
Destroy ();
}
copy = static_cast<AInventory *>(Spawn (type));
copy->Amount = amount;
copy->BecomeItem ();
}
else
{
copy = Super::CreateCopy (other);
copy->Amount = amount;
}
if (copy->Amount > copy->MaxAmount)
{ // Don't pick up more ammo than you're supposed to be able to carry.
copy->Amount = copy->MaxAmount;
}
return copy;
}
//===========================================================================
//
// AAmmo :: CreateTossable
//
//===========================================================================
AInventory *AAmmo::CreateTossable()
{
AInventory *copy = Super::CreateTossable();
if (copy != NULL)
{ // Do not increase ammo by dropping it and picking it back up at
// certain skill levels.
copy->ItemFlags |= IF_IGNORESKILL;
}
return copy;
}
// Backpack -----------------------------------------------------------------
IMPLEMENT_CLASS(ABackpackItem, false, false)
DEFINE_FIELD(ABackpackItem, bDepleted)
//===========================================================================
//
// ABackpackItem :: Serialize
//
//===========================================================================
void ABackpackItem::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
auto def = (ABackpackItem*)GetDefault();
arc("bdepleted", bDepleted, def->bDepleted);
}
//===========================================================================
//
// ABackpackItem :: CreateCopy
//
// A backpack is being added to a player who doesn't yet have one. Give them
// every kind of ammo, and increase their max amounts.
//
//===========================================================================
AInventory *ABackpackItem::CreateCopy (AActor *other)
{
// Find every unique type of ammo. Give it to the player if
// he doesn't have it already, and double its maximum capacity.
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
PClass *type = PClassActor::AllActorClasses[i];
if (type->ParentClass == RUNTIME_CLASS(AAmmo))
{
PClassActor *atype = static_cast<PClassActor *>(type);
AAmmo *ammo = static_cast<AAmmo *>(other->FindInventory(atype));
int amount = static_cast<AAmmo *>(GetDefaultByType(type))->BackpackAmount;
// extra ammo in baby mode and nightmare mode
if (!(ItemFlags&IF_IGNORESKILL))
{
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
}
if (amount < 0) amount = 0;
if (ammo == NULL)
{ // The player did not have the ammo. Add it.
ammo = static_cast<AAmmo *>(Spawn(atype));
ammo->Amount = bDepleted ? 0 : amount;
if (ammo->BackpackMaxAmount > ammo->MaxAmount)
{
ammo->MaxAmount = ammo->BackpackMaxAmount;
}
if (ammo->Amount > ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
ammo->AttachToOwner (other);
}
else
{ // The player had the ammo. Give some more.
if (ammo->MaxAmount < ammo->BackpackMaxAmount)
{
ammo->MaxAmount = ammo->BackpackMaxAmount;
}
if (!bDepleted && ammo->Amount < ammo->MaxAmount)
{
ammo->Amount += amount;
if (ammo->Amount > ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
}
}
}
}
return Super::CreateCopy (other);
}
//===========================================================================
//
// ABackpackItem :: HandlePickup
//
// When the player picks up another backpack, just give them more ammo.
//
//===========================================================================
bool ABackpackItem::HandlePickup (AInventory *item)
{
// Since you already have a backpack, that means you already have every
// kind of ammo in your inventory, so we don't need to look at the
// entire PClass list to discover what kinds of ammo exist, and we don't
// have to alter the MaxAmount either.
if (item->IsKindOf (RUNTIME_CLASS(ABackpackItem)))
{
for (AInventory *probe = Owner->Inventory; probe != NULL; probe = probe->Inventory)
{
if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo))
{
if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup)
{
int amount = static_cast<AAmmo*>(probe->GetDefault())->BackpackAmount;
// extra ammo in baby mode and nightmare mode
if (!(item->ItemFlags&IF_IGNORESKILL))
{
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
}
probe->Amount += amount;
if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup)
{
probe->Amount = probe->MaxAmount;
}
}
}
}
// The pickup always succeeds, even if you didn't get anything
item->ItemFlags |= IF_PICKUPGOOD;
return true;
}
return false;
}
//===========================================================================
//
// ABackpackItem :: CreateTossable
//
// The tossed backpack must not give out any more ammo, otherwise a player
// could cheat by dropping their backpack and picking it up for more ammo.
//
//===========================================================================
AInventory *ABackpackItem::CreateTossable ()
{
ABackpackItem *pack = static_cast<ABackpackItem *>(Super::CreateTossable());
if (pack != NULL)
{
pack->bDepleted = true;
}
return pack;
}
//===========================================================================
//
// ABackpackItem :: DetachFromOwner
//
//===========================================================================
void ABackpackItem::DetachFromOwner ()
{
// When removing a backpack, drop the player's ammo maximums to normal
AInventory *item;
for (item = Owner->Inventory; item != NULL; item = item->Inventory)
{
if (item->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo) &&
item->MaxAmount == static_cast<AAmmo*>(item)->BackpackMaxAmount)
{
item->MaxAmount = static_cast<AInventory*>(item->GetDefault())->MaxAmount;
if (item->Amount > item->MaxAmount)
{
item->Amount = item->MaxAmount;
}
}
}
}

View file

@ -7,29 +7,8 @@ class AAmmo : public AInventory
public:
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateTossable () override;
PClassActor *GetParentAmmo () const;
int BackpackAmount, BackpackMaxAmount, DropAmount;
};
// A backpack gives you one clip of each ammo and doubles your
// normal maximum ammo amounts.
class ABackpackItem : public AInventory
{
DECLARE_CLASS (ABackpackItem, AInventory)
public:
virtual void Serialize(FSerializer &arc) override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual void DetachFromOwner () override;
bool bDepleted;
};

View file

@ -904,334 +904,6 @@ void APowerMask::DoEffect ()
}
}
// Light-Amp Powerup ---------------------------------------------------------
IMPLEMENT_CLASS(APowerLightAmp, false, false)
//===========================================================================
//
// APowerLightAmp :: DoEffect
//
//===========================================================================
void APowerLightAmp::DoEffect ()
{
Super::DoEffect ();
if (Owner->player != NULL && Owner->player->fixedcolormap < NUMCOLORMAPS)
{
if (!isBlinking())
{
Owner->player->fixedlightlevel = 1;
}
else
{
Owner->player->fixedlightlevel = -1;
}
}
}
//===========================================================================
//
// APowerLightAmp :: EndEffect
//
//===========================================================================
void APowerLightAmp::EndEffect ()
{
Super::EndEffect();
if (Owner != NULL && Owner->player != NULL && Owner->player->fixedcolormap < NUMCOLORMAPS)
{
Owner->player->fixedlightlevel = -1;
}
}
// Torch Powerup -------------------------------------------------------------
IMPLEMENT_CLASS(APowerTorch, false, false)
//===========================================================================
//
// APowerTorch :: Serialize
//
//===========================================================================
void APowerTorch::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("newtorch", NewTorch)
("newtorchdelta", NewTorchDelta);
}
//===========================================================================
//
// APowerTorch :: DoEffect
//
//===========================================================================
void APowerTorch::DoEffect ()
{
if (Owner == NULL || Owner->player == NULL)
{
return;
}
if (EffectTics <= BLINKTHRESHOLD || Owner->player->fixedcolormap >= NUMCOLORMAPS)
{
Super::DoEffect ();
}
else
{
APowerup::DoEffect ();
if (!(level.time & 16) && Owner->player != NULL)
{
if (NewTorch != 0)
{
if (Owner->player->fixedlightlevel + NewTorchDelta > 7
|| Owner->player->fixedlightlevel + NewTorchDelta < 0
|| NewTorch == Owner->player->fixedlightlevel)
{
NewTorch = 0;
}
else
{
Owner->player->fixedlightlevel += NewTorchDelta;
}
}
else
{
NewTorch = (pr_torch() & 7) + 1;
NewTorchDelta = (NewTorch == Owner->player->fixedlightlevel) ?
0 : ((NewTorch > Owner->player->fixedlightlevel) ? 1 : -1);
}
}
}
}
// Flight (aka Wings of Wrath) powerup ---------------------------------------
IMPLEMENT_CLASS(APowerFlight, false, false)
//===========================================================================
//
// APowerFlight :: Serialize
//
//===========================================================================
void APowerFlight::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("hitcenterframe", HitCenterFrame);
}
//===========================================================================
//
// APowerFlight :: InitEffect
//
//===========================================================================
void APowerFlight::InitEffect ()
{
Super::InitEffect();
Owner->flags2 |= MF2_FLY;
Owner->flags |= MF_NOGRAVITY;
if (Owner->Z() <= Owner->floorz)
{
Owner->Vel.Z = 4;; // thrust the player in the air a bit
}
if (Owner->Vel.Z <= -35)
{ // stop falling scream
S_StopSound (Owner, CHAN_VOICE);
}
}
//===========================================================================
//
// APowerFlight :: DoEffect
//
//===========================================================================
void APowerFlight::Tick ()
{
// The Wings of Wrath only expire in multiplayer and non-hub games
if (!multiplayer && (level.flags2 & LEVEL2_INFINITE_FLIGHT))
{
assert(EffectTics < INT_MAX); // I can't see a game lasting nearly two years, but...
EffectTics++;
}
Super::Tick ();
// Owner->flags |= MF_NOGRAVITY;
// Owner->flags2 |= MF2_FLY;
}
//===========================================================================
//
// APowerFlight :: EndEffect
//
//===========================================================================
void APowerFlight::EndEffect ()
{
Super::EndEffect();
if (Owner == NULL || Owner->player == NULL)
{
return;
}
if (!(Owner->flags7 & MF7_FLYCHEAT))
{
if (Owner->Z() != Owner->floorz)
{
Owner->player->centering = true;
}
Owner->flags2 &= ~MF2_FLY;
Owner->flags &= ~MF_NOGRAVITY;
}
// BorderTopRefresh = screen->GetPageCount (); //make sure the sprite's cleared out
}
//===========================================================================
//
// APowerFlight :: DrawPowerup
//
//===========================================================================
bool APowerFlight::DrawPowerup (int x, int y)
{
// If this item got a valid icon use that instead of the default spinning wings.
if (Icon.isValid())
{
return Super::DrawPowerup(x, y);
}
if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16))
{
FTextureID picnum = TexMan.CheckForTexture ("SPFLY0", FTexture::TEX_MiscPatch);
int frame = (level.time/3) & 15;
if (!picnum.isValid())
{
return false;
}
if (Owner->flags & MF_NOGRAVITY)
{
if (HitCenterFrame && (frame != 15 && frame != 0))
{
screen->DrawTexture (TexMan[picnum+15], x, y,
DTA_HUDRules, HUD_Normal, TAG_DONE);
}
else
{
screen->DrawTexture (TexMan[picnum+frame], x, y,
DTA_HUDRules, HUD_Normal, TAG_DONE);
HitCenterFrame = false;
}
}
else
{
if (!HitCenterFrame && (frame != 15 && frame != 0))
{
screen->DrawTexture (TexMan[picnum+frame], x, y,
DTA_HUDRules, HUD_Normal, TAG_DONE);
HitCenterFrame = false;
}
else
{
screen->DrawTexture (TexMan[picnum+15], x, y,
DTA_HUDRules, HUD_Normal, TAG_DONE);
HitCenterFrame = true;
}
}
}
return true;
}
// Weapon Level 2 (aka Tome of Power) Powerup --------------------------------
IMPLEMENT_CLASS(APowerWeaponLevel2, false, false)
//===========================================================================
//
// APowerWeaponLevel2 :: InitEffect
//
//===========================================================================
void APowerWeaponLevel2::InitEffect ()
{
AWeapon *weapon, *sister;
Super::InitEffect();
if (Owner->player == nullptr)
return;
weapon = Owner->player->ReadyWeapon;
if (weapon == nullptr)
return;
sister = weapon->SisterWeapon;
if (sister == nullptr)
return;
if (!(sister->WeaponFlags & WIF_POWERED_UP))
return;
assert (sister->SisterWeapon == weapon);
if (weapon->GetReadyState() != sister->GetReadyState())
{
Owner->player->ReadyWeapon = sister;
P_SetPsprite(Owner->player, PSP_WEAPON, sister->GetReadyState());
}
else
{
DPSprite *psp = Owner->player->FindPSprite(PSP_WEAPON);
if (psp != nullptr && psp->GetCaller() == Owner->player->ReadyWeapon)
{
// If the weapon changes but the state does not, we have to manually change the PSprite's caller here.
psp->SetCaller(sister);
Owner->player->ReadyWeapon = sister;
}
else
{
// Something went wrong. Initiate a regular weapon change.
Owner->player->PendingWeapon = sister;
}
}
}
//===========================================================================
//
// APowerWeaponLevel2 :: EndEffect
//
//===========================================================================
void APowerWeaponLevel2::EndEffect ()
{
player_t *player = Owner != NULL ? Owner->player : NULL;
Super::EndEffect();
if (player != NULL)
{
if (player->ReadyWeapon != NULL &&
player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP)
{
player->ReadyWeapon->CallEndPowerup ();
}
if (player->PendingWeapon != NULL && player->PendingWeapon != WP_NOCHANGE &&
player->PendingWeapon->WeaponFlags & WIF_POWERED_UP &&
player->PendingWeapon->SisterWeapon != NULL)
{
player->PendingWeapon = player->PendingWeapon->SisterWeapon;
}
}
}
// Speed Powerup -------------------------------------------------------------
@ -1251,178 +923,6 @@ void APowerSpeed::Serialize(FSerializer &arc)
arc("speedflags", SpeedFlags);
}
//===========================================================================
//
// APowerSpeed :: DoEffect
//
//===========================================================================
void APowerSpeed::DoEffect ()
{
Super::DoEffect ();
if (Owner == NULL || Owner->player == NULL)
return;
if (Owner->player->cheats & CF_PREDICTING)
return;
if (SpeedFlags & PSF_NOTRAIL)
return;
if (level.time & 1)
return;
// Check if another speed item is present to avoid multiple drawing of the speed trail.
// Only the last PowerSpeed without PSF_NOTRAIL set will actually draw the trail.
for (AInventory *item = Inventory; item != NULL; item = item->Inventory)
{
if (item->IsKindOf(RUNTIME_CLASS(APowerSpeed)) &&
!(static_cast<APowerSpeed *>(item)->SpeedFlags & PSF_NOTRAIL))
{
return;
}
}
if (Owner->Vel.LengthSquared() <= 12*12)
return;
AActor *speedMo = Spawn("PlayerSpeedTrail", Owner->Pos(), NO_REPLACE);
if (speedMo)
{
speedMo->Angles.Yaw = Owner->Angles.Yaw;
speedMo->Translation = Owner->Translation;
speedMo->target = Owner;
speedMo->sprite = Owner->sprite;
speedMo->frame = Owner->frame;
speedMo->Floorclip = Owner->Floorclip;
// [BC] Also get the scale from the owner.
speedMo->Scale = Owner->Scale;
if (Owner == players[consoleplayer].camera &&
!(Owner->player->cheats & CF_CHASECAM))
{
speedMo->renderflags |= RF_INVISIBLE;
}
}
}
// Targeter powerup ---------------------------------------------------------
IMPLEMENT_CLASS(APowerTargeter, false, false)
void APowerTargeter::Travelled ()
{
CallInitEffect ();
}
void APowerTargeter::InitEffect ()
{
// Why is this called when the inventory isn't even attached yet
// in APowerup::CreateCopy?
if (!Owner->FindInventory(GetClass(), true))
return;
player_t *player;
Super::InitEffect();
if ((player = Owner->player) == nullptr)
return;
FState *state = FindState("Targeter");
if (state != nullptr)
{
P_SetPsprite(player, PSP_TARGETCENTER, state + 0);
P_SetPsprite(player, PSP_TARGETLEFT, state + 1);
P_SetPsprite(player, PSP_TARGETRIGHT, state + 2);
}
player->GetPSprite(PSP_TARGETCENTER)->x = (160-3);
player->GetPSprite(PSP_TARGETCENTER)->y =
player->GetPSprite(PSP_TARGETLEFT)->y =
player->GetPSprite(PSP_TARGETRIGHT)->y = (100-3);
PositionAccuracy ();
}
void APowerTargeter::AttachToOwner(AActor *other)
{
Super::AttachToOwner(other);
// Let's actually properly call this for the targeters.
CallInitEffect();
}
bool APowerTargeter::HandlePickup(AInventory *item)
{
if (Super::HandlePickup(item))
{
CallInitEffect(); // reset the HUD sprites
return true;
}
return false;
}
void APowerTargeter::DoEffect ()
{
Super::DoEffect ();
if (Owner != nullptr && Owner->player != nullptr)
{
player_t *player = Owner->player;
PositionAccuracy ();
if (EffectTics < 5*TICRATE)
{
FState *state = FindState("Targeter");
if (state != nullptr)
{
if (EffectTics & 32)
{
P_SetPsprite(player, PSP_TARGETRIGHT, nullptr);
P_SetPsprite(player, PSP_TARGETLEFT, state + 1);
}
else if (EffectTics & 16)
{
P_SetPsprite(player, PSP_TARGETRIGHT, state + 2);
P_SetPsprite(player, PSP_TARGETLEFT, nullptr);
}
}
}
}
}
void APowerTargeter::EndEffect ()
{
Super::EndEffect();
if (Owner != nullptr && Owner->player != nullptr)
{
// Calling GetPSprite here could crash if we're creating a new game.
// This is because P_SetupLevel nulls the player's mo before destroying
// every DThinker which in turn ends up calling this.
// However P_SetupLevel is only called after G_NewInit which calls
// every player's dtor which destroys all their psprites.
DPSprite *pspr;
if ((pspr = Owner->player->FindPSprite(PSP_TARGETCENTER)) != nullptr) pspr->SetState(nullptr);
if ((pspr = Owner->player->FindPSprite(PSP_TARGETLEFT)) != nullptr) pspr->SetState(nullptr);
if ((pspr = Owner->player->FindPSprite(PSP_TARGETRIGHT)) != nullptr) pspr->SetState(nullptr);
}
}
void APowerTargeter::PositionAccuracy ()
{
player_t *player = Owner->player;
if (player != nullptr)
{
player->GetPSprite(PSP_TARGETLEFT)->x = (160-3) - ((100 - player->mo->accuracy));
player->GetPSprite(PSP_TARGETRIGHT)->x = (160-3)+ ((100 - player->mo->accuracy));
}
}
// Morph powerup ------------------------------------------------------
IMPLEMENT_CLASS(APowerMorph, false, true)

View file

@ -105,55 +105,10 @@ public:
virtual void DoEffect () override;
};
class APowerLightAmp : public APowerup
{
DECLARE_CLASS (APowerLightAmp, APowerup)
protected:
virtual void DoEffect () override;
virtual void EndEffect () override;
};
class APowerTorch : public APowerLightAmp
{
DECLARE_CLASS (APowerTorch, APowerLightAmp)
public:
virtual void Serialize(FSerializer &arc) override;
protected:
virtual void DoEffect () override;
int NewTorch, NewTorchDelta;
};
class APowerFlight : public APowerup
{
DECLARE_CLASS (APowerFlight, APowerup)
public:
virtual bool DrawPowerup (int x, int y) override;
virtual void Serialize(FSerializer &arc) override;
protected:
virtual void InitEffect () override;
virtual void Tick () override;
virtual void EndEffect () override;
private:
bool HitCenterFrame;
};
class APowerWeaponLevel2 : public APowerup
{
DECLARE_CLASS (APowerWeaponLevel2, APowerup)
protected:
virtual void InitEffect () override;
virtual void EndEffect () override;
};
class APowerSpeed : public APowerup
{
DECLARE_CLASS (APowerSpeed, APowerup)
protected:
virtual void DoEffect () override;
virtual void Serialize(FSerializer &arc) override;
public:
int SpeedFlags;
@ -161,19 +116,6 @@ public:
#define PSF_NOTRAIL 1
class APowerTargeter : public APowerup
{
DECLARE_CLASS (APowerTargeter, APowerup)
protected:
virtual void InitEffect () override;
virtual void DoEffect () override;
virtual void EndEffect () override;
void PositionAccuracy ();
virtual void Travelled () override;
virtual void AttachToOwner(AActor *other) override;
virtual bool HandlePickup(AInventory *item) override;
};
class APowerMorph : public APowerup
{
DECLARE_CLASS( APowerMorph, APowerup )

View file

@ -1,301 +0,0 @@
/*
** a_health.cpp
** All health items
**
**---------------------------------------------------------------------------
** Copyright 2000-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "d_player.h"
#include "a_morph.h"
#include "a_health.h"
#include "serializer.h"
//---------------------------------------------------------------------------
//
// FUNC P_GiveBody
//
// Returns false if the body isn't needed at all.
//
//---------------------------------------------------------------------------
bool P_GiveBody (AActor *actor, int num, int max)
{
if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD))
{ // Do not heal dead things.
return false;
}
player_t *player = actor->player;
num = clamp(num, -65536, 65536); // prevent overflows for bad values
if (player != NULL)
{
// Max is 0 by default, preserving default behavior for P_GiveBody()
// calls while supporting AHealth.
if (max <= 0)
{
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina;
// [MH] First step in predictable generic morph effects
if (player->morphTics)
{
if (player->MorphStyle & MORPH_FULLHEALTH)
{
if (!(player->MorphStyle & MORPH_ADDSTAMINA))
{
max -= player->mo->stamina;
}
}
else // old health behaviour
{
max = MAXMORPHHEALTH;
if (player->MorphStyle & MORPH_ADDSTAMINA)
{
max += player->mo->stamina;
}
}
}
}
// [RH] For Strife: A negative body sets you up with a percentage
// of your full health.
if (num < 0)
{
num = max * -num / 100;
if (player->health < num)
{
player->health = num;
actor->health = num;
return true;
}
}
else if (num > 0)
{
if (player->health < max)
{
num = int(num * G_SkillProperty(SKILLP_HealthFactor));
if (num < 1) num = 1;
player->health += num;
if (player->health > max)
{
player->health = max;
}
actor->health = player->health;
return true;
}
}
}
else
{
// Parameter value for max is ignored on monsters, preserving original
// behaviour on AHealth as well as on existing calls to P_GiveBody().
max = actor->SpawnHealth();
if (num < 0)
{
num = max * -num / 100;
if (actor->health < num)
{
actor->health = num;
return true;
}
}
else if (actor->health < max)
{
actor->health += num;
if (actor->health > max)
{
actor->health = max;
}
return true;
}
}
return false;
}
DEFINE_ACTION_FUNCTION(AActor, GiveBody)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(num);
PARAM_INT_DEF(max);
ACTION_RETURN_BOOL(P_GiveBody(self, num, max));
}
//===========================================================================
//
// Classes
//
//===========================================================================
IMPLEMENT_CLASS(PClassHealth, false, false)
IMPLEMENT_CLASS(AHealth, false, false)
DEFINE_FIELD(AHealth, PrevHealth)
//===========================================================================
//
// PClassHealth Constructor
//
//===========================================================================
PClassHealth::PClassHealth()
{
LowHealth = 0;
}
//===========================================================================
//
// PClassHealth :: DeriveData
//
//===========================================================================
void PClassHealth::DeriveData(PClass *newclass)
{
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassHealth)));
Super::DeriveData(newclass);
PClassHealth *newc = static_cast<PClassHealth *>(newclass);
newc->LowHealth = LowHealth;
newc->LowHealthMessage = LowHealthMessage;
}
//===========================================================================
//
// AHealth :: PickupMessage
//
//===========================================================================
FString AHealth::PickupMessage ()
{
int threshold = GetClass()->LowHealth;
if (PrevHealth < threshold)
{
FString message = GetClass()->LowHealthMessage;
if (message.IsNotEmpty())
{
return message;
}
}
return Super::PickupMessage();
}
//===========================================================================
//
// AHealth :: TryPickup
//
//===========================================================================
bool AHealth::TryPickup (AActor *&other)
{
PrevHealth = other->player != NULL ? other->player->health : other->health;
// P_GiveBody adds one new feature, applied only if it is possible to pick up negative health:
// Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount.
if (P_GiveBody(other, Amount, MaxAmount))
{
GoAwayAndDie();
return true;
}
return false;
}
IMPLEMENT_CLASS(AHealthPickup, false, false)
DEFINE_FIELD(AHealthPickup, autousemode)
//===========================================================================
//
// AHealthPickup :: CreateCopy
//
//===========================================================================
AInventory *AHealthPickup::CreateCopy (AActor *other)
{
AInventory *copy = Super::CreateCopy (other);
copy->health = health;
return copy;
}
//===========================================================================
//
// AHealthPickup :: CreateTossable
//
//===========================================================================
AInventory *AHealthPickup::CreateTossable ()
{
AInventory *copy = Super::CreateTossable ();
if (copy != NULL)
{
copy->health = health;
}
return copy;
}
//===========================================================================
//
// AHealthPickup :: HandlePickup
//
//===========================================================================
bool AHealthPickup::HandlePickup (AInventory *item)
{
// HealthPickups that are the same type but have different health amounts
// do not count as the same item.
if (item->health == health)
{
return Super::HandlePickup (item);
}
return false;
}
//===========================================================================
//
// AHealthPickup :: Use
//
//===========================================================================
bool AHealthPickup::Use (bool pickup)
{
return P_GiveBody (Owner, health, 0);
}
//===========================================================================
//
// AHealthPickup :: Serialize
//
//===========================================================================
void AHealthPickup::Serialize(FSerializer &arc)
{
Super::Serialize(arc);
auto def = (AHealthPickup*)GetDefault();
arc("autousemode", autousemode, def->autousemode);
}

View file

@ -1,42 +0,0 @@
#pragma once
#include "a_pickups.h"
// Health is some item that gives the player health when picked up.
class PClassHealth : public PClassInventory
{
DECLARE_CLASS(PClassHealth, PClassInventory)
protected:
public:
PClassHealth();
virtual void DeriveData(PClass *newclass);
FString LowHealthMessage;
int LowHealth;
};
class AHealth : public AInventory
{
DECLARE_CLASS_WITH_META(AHealth, AInventory, PClassHealth)
public:
int PrevHealth;
virtual bool TryPickup (AActor *&other) override;
virtual FString PickupMessage () override;
};
// HealthPickup is some item that gives the player health when used.
class AHealthPickup : public AInventory
{
DECLARE_CLASS (AHealthPickup, AInventory)
public:
int autousemode;
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual bool HandlePickup (AInventory *item) override;
virtual bool Use (bool pickup) override;
};

View file

@ -530,35 +530,8 @@ bool P_CheckKeys (AActor *owner, int keynum, bool remote)
//==========================================================================
IMPLEMENT_CLASS(AKey, false, false)
DEFINE_FIELD(AKey, KeyNumber)
bool AKey::HandlePickup (AInventory *item)
{
// In single player, you can pick up an infinite number of keys
// even though you can only hold one of each.
if (multiplayer)
{
return Super::HandlePickup (item);
}
if (GetClass() == item->GetClass())
{
item->ItemFlags |= IF_PICKUPGOOD;
return true;
}
return false;
}
//===========================================================================
//
//
//===========================================================================
bool AKey::ShouldStay ()
{
return !!multiplayer;
}
//==========================================================================
//
// These functions can be used to get color information for

View file

@ -7,12 +7,7 @@ class AKey : public AInventory
{
DECLARE_CLASS (AKey, AInventory)
public:
virtual bool HandlePickup (AInventory *item) override;
BYTE KeyNumber;
protected:
virtual bool ShouldStay () override;
};
bool P_CheckKeys (AActor *owner, int keynum, bool remote);
@ -21,28 +16,4 @@ void P_DeinitKeyMessages ();
int P_GetMapColorForLock (int lock);
int P_GetMapColorForKey (AInventory *key);
// PuzzleItems work in conjunction with the UsePuzzleItem special
class PClassPuzzleItem : public PClassInventory
{
DECLARE_CLASS(PClassPuzzleItem, PClassInventory);
protected:
public:
virtual void DeriveData(PClass *newclass);
FString PuzzFailMessage;
};
class APuzzleItem : public AInventory
{
DECLARE_CLASS_WITH_META(APuzzleItem, AInventory, PClassPuzzleItem)
public:
virtual bool ShouldStay () override;
virtual bool Use (bool pickup) override;
virtual bool HandlePickup (AInventory *item) override;
int PuzzleItemNumber;
};
#endif

View file

@ -368,19 +368,6 @@ void AInventory::CallDoEffect()
}
//===========================================================================
//
// AInventory :: Travelled
//
// Called when an item in somebody's inventory is carried over to another
// map, in case it needs to do special reinitialization.
//
//===========================================================================
void AInventory::Travelled ()
{
}
//===========================================================================
//
// AInventory :: OwnerDied
@ -486,6 +473,12 @@ bool AInventory::GoAway ()
return true;
}
DEFINE_ACTION_FUNCTION(AInventory, GoAway)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_BOOL(self->GoAway());
}
//===========================================================================
//
// AInventory :: GoAwayAndDie
@ -1286,6 +1279,14 @@ bool AInventory::DrawPowerup (int x, int y)
return false;
}
DEFINE_ACTION_FUNCTION(AInventory, DrawPowerup)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_INT(x);
PARAM_INT(y);
ACTION_RETURN_BOOL(self->DrawPowerup(x, y));
}
//===========================================================================
//
// AInventory :: DoRespawn
@ -1397,7 +1398,7 @@ bool AInventory::TryPickup (AActor *&toucher)
copy->ItemFlags &= ~IF_CREATECOPYMOVED;
}
// Continue onwards with the rest
copy->AttachToOwner (newtoucher);
copy->CallAttachToOwner (newtoucher);
if (ItemFlags & IF_AUTOACTIVATE)
{
if (copy->CallUse (true))

View file

@ -140,7 +140,6 @@ public:
double GetSpeedFactor();
bool GetNoTeleportFreeze();
// Stuff for later when more features are exported.
virtual void Travelled();
virtual void OwnerDied();

View file

@ -1,117 +0,0 @@
/*
** a_puzzleitems.cpp
** Implements Hexen's puzzle items.
**
**---------------------------------------------------------------------------
** Copyright 2002-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "c_console.h"
#include "doomstat.h"
#include "v_font.h"
#include "a_keys.h"
IMPLEMENT_CLASS(PClassPuzzleItem, false, false)
IMPLEMENT_CLASS(APuzzleItem, false, false)
DEFINE_FIELD(APuzzleItem, PuzzleItemNumber)
//===========================================================================
//
//
//
//===========================================================================
void PClassPuzzleItem::DeriveData(PClass *newclass)
{
Super::DeriveData(newclass);
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassPuzzleItem)));
static_cast<PClassPuzzleItem *>(newclass)->PuzzFailMessage = PuzzFailMessage;
}
//===========================================================================
//
//
//
//===========================================================================
bool APuzzleItem::HandlePickup (AInventory *item)
{
// Can't carry more than 1 of each puzzle item in coop netplay
if (multiplayer && !deathmatch && item->GetClass() == GetClass())
{
return true;
}
return Super::HandlePickup (item);
}
//===========================================================================
//
//
//
//===========================================================================
bool APuzzleItem::Use (bool pickup)
{
if (P_UsePuzzleItem (Owner, PuzzleItemNumber))
{
return true;
}
// [RH] Always play the sound if the use fails.
S_Sound (Owner, CHAN_VOICE, "*puzzfail", 1, ATTN_IDLE);
if (Owner != NULL && Owner->CheckLocalView (consoleplayer))
{
FString message = GetClass()->PuzzFailMessage;
if (message.IsNotEmpty() && message[0] == '$') message = GStrings[&message[1]];
if (message.IsEmpty()) message = GStrings("TXT_USEPUZZLEFAILED");
C_MidPrintBold (SmallFont, message);
}
return false;
}
//===========================================================================
//
//
//
//===========================================================================
bool APuzzleItem::ShouldStay ()
{
return !!multiplayer;
}

View file

@ -311,7 +311,7 @@ bool AWeapon::Use (bool pickup)
// weapon, if one exists.
if (SisterWeapon != NULL &&
SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
Owner->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true))
Owner->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true))
{
useweap = SisterWeapon;
}
@ -824,18 +824,6 @@ DEFINE_ACTION_FUNCTION(AWeapon, EndPowerup)
return 0;
}
void AWeapon::CallEndPowerup()
{
IFVIRTUAL(AWeapon, EndPowerup)
{
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else EndPowerup();
}
//===========================================================================
//
// AWeapon :: GetUpState

View file

@ -161,7 +161,6 @@ public:
virtual void EndPowerup ();
void CallEndPowerup();
enum
{

View file

@ -84,6 +84,7 @@
#include "r_utility.h"
#include "p_spec.h"
#include "serializer.h"
#include "virtual.h"
#include "gi.h"
@ -1313,7 +1314,13 @@ void G_FinishTravel ()
{
inv->ChangeStatNum (STAT_INVENTORY);
inv->LinkToWorld (nullptr);
inv->Travelled ();
IFVIRTUALPTR(inv, AInventory, Travelled)
{
VMValue params[1] = { inv };
VMFrameStack stack;
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
}
if (ib_compatflags & BCOMPATF_RESETPLAYERSPEED)
{
@ -1891,6 +1898,7 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, checkswitchrange, LEVEL2_CHECKSWITCHRANGE
DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND)
DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS)
DEFINE_FIELD_BIT(FLevelLocals, flags2, frozen, LEVEL2_FROZEN)
DEFINE_FIELD_BIT(FLevelLocals, flags2, infinite_flight, LEVEL2_INFINITE_FLIGHT)
//==========================================================================
//

View file

@ -32,6 +32,9 @@ struct FLevelLocals
TStaticArray<line_t> lines;
TStaticArray<side_t> sides;
TArray<FSectorPortal> sectorPortals;
DWORD flags;
DWORD flags2;
DWORD flags3;
@ -95,7 +98,37 @@ inline int line_t::Index() const
return int(this - &level.lines[0]);
}
inline FSectorPortal *line_t::GetTransferredPortal()
{
return portaltransferred >= level.sectorPortals.Size() ? (FSectorPortal*)nullptr : &level.sectorPortals[portaltransferred];
}
inline int sector_t::Index() const
{
return int(this - &level.sectors[0]);
}
inline FSectorPortal *sector_t::GetPortal(int plane)
{
return &level.sectorPortals[Portals[plane]];
}
inline double sector_t::GetPortalPlaneZ(int plane)
{
return level.sectorPortals[Portals[plane]].mPlaneZ;
}
inline DVector2 sector_t::GetPortalDisplacement(int plane)
{
return level.sectorPortals[Portals[plane]].mDisplacement;
}
inline int sector_t::GetPortalType(int plane)
{
return level.sectorPortals[Portals[plane]].mType;
}
inline int sector_t::GetOppositePortalGroup(int plane)
{
return level.sectorPortals[Portals[plane]].mDestination->PortalGroup;
}

View file

@ -12,10 +12,6 @@
#include "serializer.h"
#include "r_data/r_translate.h"
static FRandom pr_freezedeath ("FreezeDeath");
static FRandom pr_freeze ("FreezeDeathChunks");
//----------------------------------------------------------------------------
//
// PROC A_NoBlocking
@ -74,137 +70,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_NoBlocking)
return 0;
}
//============================================================================
//
// A_FreezeDeath
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeath)
{
PARAM_SELF_PROLOGUE(AActor);
int t = pr_freezedeath();
self->tics = 75+t+pr_freezedeath();
self->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_ICECORPSE;
self->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE;
self->flags3 |= MF3_CRASHED;
self->Height = self->GetDefault()->Height;
// Remove fuzz effects from frozen actors.
if (self->RenderStyle.BlendOp >= STYLEOP_Fuzz && self->RenderStyle.BlendOp <= STYLEOP_FuzzOrRevSub)
{
self->RenderStyle = STYLE_Normal;
}
S_Sound (self, CHAN_BODY, "misc/freeze", 1, ATTN_NORM);
// [RH] Andy Baker's stealth monsters
if (self->flags & MF_STEALTH)
{
self->Alpha = 1;
self->visdir = 0;
}
if (self->player)
{
self->player->damagecount = 0;
self->player->poisoncount = 0;
self->player->bonuscount = 0;
}
else if (self->flags3 & MF3_ISMONSTER && self->special)
{ // Initiate monster death actions
P_ExecuteSpecial(self->special, NULL, self, false, self->args[0],
self->args[1], self->args[2], self->args[3], self->args[4]);
self->special = 0;
}
return 0;
}
//============================================================================
//
// A_FreezeDeathChunks
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeathChunks)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
int numChunks;
AActor *mo;
if (!self->Vel.isZero() && !(self->flags6 & MF6_SHATTERING))
{
self->tics = 3*TICRATE;
return 0;
}
self->Vel.Zero();
S_Sound (self, CHAN_BODY, "misc/icebreak", 1, ATTN_NORM);
// [RH] In Hexen, this creates a random number of shards (range [24,56])
// with no relation to the size of the self shattering. I think it should
// base the number of shards on the size of the dead thing, so bigger
// things break up into more shards than smaller things.
// An actor with radius 20 and height 64 creates ~40 chunks.
numChunks = MAX<int>(4, int(self->radius * self->Height)/32);
i = (pr_freeze.Random2()) % (numChunks/4);
for (i = MAX (24, numChunks + i); i >= 0; i--)
{
double xo = (pr_freeze() - 128)*self->radius / 128;
double yo = (pr_freeze() - 128)*self->radius / 128;
double zo = (pr_freeze()*self->Height / 255);
mo = Spawn("IceChunk", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + (pr_freeze()%3));
mo->Vel.X = pr_freeze.Random2() / 128.;
mo->Vel.Y = pr_freeze.Random2() / 128.;
mo->Vel.Z = (mo->Z() - self->Z()) / self->Height * 4;
mo->RenderStyle = self->RenderStyle;
mo->Alpha = self->Alpha;
}
}
if (self->player)
{ // attach the player's view to a chunk of ice
AActor *head = Spawn("IceChunkHead", self->PosPlusZ(self->player->mo->ViewHeight), ALLOW_REPLACE);
if (head != NULL)
{
head->Vel.X = pr_freeze.Random2() / 128.;
head->Vel.Y = pr_freeze.Random2() / 128.;
head->Vel.Z = (mo->Z() - self->Z()) / self->Height * 4;
head->health = self->health;
head->Angles.Yaw = self->Angles.Yaw;
if (head->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
{
head->player = self->player;
head->player->mo = static_cast<APlayerPawn*>(head);
self->player = NULL;
head->ObtainInventory (self);
}
head->Angles.Pitch = 0.;
head->RenderStyle = self->RenderStyle;
head->Alpha = self->Alpha;
if (head->player->camera == self)
{
head->player->camera = head;
}
}
}
// [RH] Do some stuff to make this more useful outside Hexen
if (self->flags4 & MF4_BOSSDEATH)
{
A_BossDeath(self);
}
A_Unblock(self, true);
self->SetState(self->FindState(NAME_Null));
return 0;
}
//----------------------------------------------------------------------------
//
// CorpseQueue Routines (used by Hexen)

View file

@ -58,9 +58,9 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
if ((p->mo->GetClass() == spawntype)
&& (p->mo->PlayerFlags & PPF_CANSUPERMORPH)
&& (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE))
&& (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == nullptr))
&& (p->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true) == nullptr))
{ // Make a super chicken
p->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2));
p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2));
}
return false;
}
@ -263,7 +263,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
player->MorphStyle = 0;
player->MorphExitFlash = nullptr;
player->viewheight = mo->ViewHeight;
AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
AInventory *level2 = mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true);
if (level2 != nullptr)
{
level2->Destroy ();

View file

@ -1,152 +0,0 @@
/*
** a_skies.cpp
** Skybox-related actors
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "actor.h"
#include "a_sharedglobal.h"
#include "p_local.h"
#include "p_lnspec.h"
#include "r_sky.h"
#include "r_state.h"
#include "portal.h"
// arg0 = Visibility*4 for this skybox
IMPLEMENT_CLASS(ASkyViewpoint, false, false)
// If this actor has no TID, make it the default sky box
void ASkyViewpoint::BeginPlay ()
{
Super::BeginPlay ();
if (tid == 0 && sectorPortals[0].mSkybox == nullptr)
{
sectorPortals[0].mSkybox = this;
sectorPortals[0].mDestination = Sector;
}
}
void ASkyViewpoint::OnDestroy ()
{
// remove all sector references to ourselves.
for (auto &s : sectorPortals)
{
if (s.mSkybox == this)
{
s.mSkybox = 0;
// This is necessary to entirely disable EE-style skyboxes
// if their viewpoint gets deleted.
s.mFlags |= PORTSF_SKYFLATONLY;
}
}
Super::OnDestroy();
}
IMPLEMENT_CLASS(ASkyCamCompat, false, false)
void ASkyCamCompat::BeginPlay()
{
// Do not call the SkyViewpoint's super method because it would trash our setup
AActor::BeginPlay();
}
//---------------------------------------------------------------------------
// arg0 = tid of matching SkyViewpoint
// A value of 0 means to use a regular stretched texture, in case
// there is a default SkyViewpoint in the level.
//
// arg1 = 0: set both floor and ceiling skybox
// = 1: set only ceiling skybox
// = 2: set only floor skybox
class ASkyPicker : public AActor
{
DECLARE_CLASS (ASkyPicker, AActor)
public:
void PostBeginPlay ();
};
IMPLEMENT_CLASS(ASkyPicker, false, false)
void ASkyPicker::PostBeginPlay ()
{
ASkyViewpoint *box;
Super::PostBeginPlay ();
if (args[0] == 0)
{
box = NULL;
}
else
{
TActorIterator<ASkyViewpoint> iterator (args[0]);
box = iterator.Next ();
}
if (box == NULL && args[0] != 0)
{
Printf ("Can't find SkyViewpoint %d for sector %d\n", args[0], Sector->sectornum);
}
else
{
int boxindex = P_GetSkyboxPortal(box);
// Do not override special portal types, only regular skies.
if (0 == (args[1] & 2))
{
if (Sector->GetPortalType(sector_t::ceiling) == PORTS_SKYVIEWPOINT)
Sector->Portals[sector_t::ceiling] = boxindex;
}
if (0 == (args[1] & 1))
{
if (Sector->GetPortalType(sector_t::floor) == PORTS_SKYVIEWPOINT)
Sector->Portals[sector_t::floor] = boxindex;
}
}
Destroy ();
}
//---------------------------------------------------------------------------
// Stacked sectors.
// arg0 = opacity of plane; 0 = invisible, 255 = fully opaque
IMPLEMENT_CLASS(AStackPoint, false, false)
void AStackPoint::BeginPlay ()
{
// Skip SkyViewpoint's initialization
AActor::BeginPlay ();
}

View file

@ -1,72 +0,0 @@
/*
** a_soundenvironment.cpp
** Actor that controls the reverb settings in its zone
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "info.h"
#include "r_defs.h"
#include "s_sound.h"
#include "r_state.h"
class ASoundEnvironment : public AActor
{
DECLARE_CLASS (ASoundEnvironment, AActor)
public:
void PostBeginPlay ();
void Deactivate (AActor *activator);
void Activate (AActor *deactivator);
};
IMPLEMENT_CLASS(ASoundEnvironment, false, false)
void ASoundEnvironment::PostBeginPlay ()
{
Super::PostBeginPlay ();
if (!(flags2 & MF2_DORMANT))
{
CallActivate (this);
}
}
void ASoundEnvironment::Activate (AActor *activator)
{
Zones[Sector->ZoneNumber].Environment = S_FindEnvironment ((args[0]<<8) | (args[1]));
}
// Deactivate just exists so that you can flag the thing as dormant in an editor
// and not have it take effect. This is so you can use multiple environments in
// a single zone, with only one set not-dormant, so you know which one will take
// effect at the start.
void ASoundEnvironment::Deactivate (AActor *deactivator)
{
flags2 |= MF2_DORMANT;
}

View file

@ -1,193 +0,0 @@
/*
** a_soundsequence.cpp
** Actors for independantly playing sound sequences in a map.
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
** A SoundSequence actor has two modes of operation:
**
** 1. If the sound sequence assigned to it has a slot, then a separate
** SoundSequenceSlot actor is spawned (if not already present), and
** this actor's sound sequence is added to its list of choices. This
** actor is then destroyed, never to be heard from again. The sound
** sequence for the slot is automatically played on the new
** SoundSequenceSlot actor, and it should at some point execute the
** randomsequence command so that it can pick one of the other
** sequences to play. The slot sequence should also end with restart
** so that more than one sequence will have a chance to play.
**
** In this mode, it is very much like world $ambient sounds defined
** in SNDINFO but more flexible.
**
** 2. If the sound sequence assigned to it has no slot, then it will play
** the sequence when activated and cease playing the sequence when
** deactivated.
**
** In this mode, it is very much like point $ambient sounds defined
** in SNDINFO but more flexible.
**
** To assign a sound sequence, set the SoundSequence's first argument to
** the ID of the corresponding environment sequence you want to use. If
** that sequence is a multiple-choice sequence, then the second argument
** selects which choice it picks.
*/
#include <stddef.h>
#include "actor.h"
#include "info.h"
#include "s_sound.h"
#include "m_random.h"
#include "s_sndseq.h"
#include "serializer.h"
// SoundSequenceSlot --------------------------------------------------------
class ASoundSequenceSlot : public AActor
{
DECLARE_CLASS (ASoundSequenceSlot, AActor)
HAS_OBJECT_POINTERS
public:
void Serialize(FSerializer &arc);
TObjPtr<DSeqNode> Sequence;
};
IMPLEMENT_CLASS(ASoundSequenceSlot, false, true)
IMPLEMENT_POINTERS_START(ASoundSequenceSlot)
IMPLEMENT_POINTER(Sequence)
IMPLEMENT_POINTERS_END
//==========================================================================
//
// ASoundSequenceSlot :: Serialize
//
//==========================================================================
void ASoundSequenceSlot::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("sequence", Sequence);
}
// SoundSequence ------------------------------------------------------------
class ASoundSequence : public AActor
{
DECLARE_CLASS (ASoundSequence, AActor)
public:
void OnDestroy() override;
void PostBeginPlay ();
void Activate (AActor *activator);
void Deactivate (AActor *activator);
void MarkPrecacheSounds () const;
};
IMPLEMENT_CLASS(ASoundSequence, false, false)
//==========================================================================
//
// ASoundSequence :: Destroy
//
//==========================================================================
void ASoundSequence::OnDestroy ()
{
SN_StopSequence (this);
Super::OnDestroy();
}
//==========================================================================
//
// ASoundSequence :: PostBeginPlay
//
//==========================================================================
void ASoundSequence::PostBeginPlay ()
{
FName slot = SN_GetSequenceSlot (args[0], SEQ_ENVIRONMENT);
if (slot != NAME_None)
{ // This is a slotted sound, so add it to the master for that slot
ASoundSequenceSlot *master;
TThinkerIterator<ASoundSequenceSlot> locator;
while (NULL != (master = locator.Next ()))
{
if (master->Sequence->GetSequenceName() == slot)
{
break;
}
}
if (master == NULL)
{
master = Spawn<ASoundSequenceSlot> ();
master->Sequence = SN_StartSequence (master, slot, 0);
GC::WriteBarrier(master, master->Sequence);
}
master->Sequence->AddChoice (args[0], SEQ_ENVIRONMENT);
Destroy ();
}
}
//==========================================================================
//
// ASoundSequence :: MarkPrecacheSounds
//
//==========================================================================
void ASoundSequence::MarkPrecacheSounds() const
{
Super::MarkPrecacheSounds();
SN_MarkPrecacheSounds(args[0], SEQ_ENVIRONMENT);
}
//==========================================================================
//
// ASoundSequence :: Activate
//
//==========================================================================
void ASoundSequence::Activate (AActor *activator)
{
SN_StartSequence (this, args[0], SEQ_ENVIRONMENT, args[1]);
}
//==========================================================================
//
// ASoundSequence :: Deactivate
//
//==========================================================================
void ASoundSequence::Deactivate (AActor *activator)
{
SN_StopSequence (this);
}

View file

@ -56,6 +56,7 @@
#include "r_utility.h"
#include "cmdlib.h"
#include "g_levellocals.h"
#include "virtual.h"
#include "../version.h"
@ -1529,13 +1530,22 @@ void DBaseStatusBar::DrawPowerups ()
+ (ST_IsLatencyVisible() ? yshift : 0);
for (item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory)
{
if (item->DrawPowerup (x, y))
IFVIRTUALPTR(item, AInventory, DrawPowerup)
{
x -= POWERUPICONSIZE;
if (x < -POWERUPICONSIZE*5)
VMValue params[3] = { item, x, y };
VMReturn ret;
int retv;
ret.IntAt(&retv);
GlobalVMStack.Call(func, params, 3, &ret, 1);
if (retv)
{
x = -20;
y += POWERUPICONSIZE*2;
x -= POWERUPICONSIZE;
if (x < -POWERUPICONSIZE * 5)
{
x = -20;
y += POWERUPICONSIZE * 2;
}
}
}
}

View file

@ -46,7 +46,6 @@ inline int getExtraLight()
void gl_RecalcVertexHeights(vertex_t * v);
FTextureID gl_GetSpriteFrame(unsigned sprite, int frame, int rot, angle_t ang, bool *mirror);
class AStackPoint;
struct GLSectorStackPortal;
struct FPortal

View file

@ -399,20 +399,15 @@ static void AddToVertex(const sector_t * sec, TArray<int> & list)
static void InitVertexData()
{
TArray<int> * vt_sectorlists;
int i,j,k;
vt_sectorlists = new TArray<int>[level.vertexes.Size()];
auto vt_sectorlists = new TArray<int>[level.vertexes.Size()];
for(auto &line : level.lines)
{
for(j=0;j<2;j++)
for(int j = 0; j < 2; ++j)
{
vertex_t * v = j==0? line.v1 : line.v2;
for(k=0;k<2;k++)
for(int k = 0; k < 2; ++k)
{
sector_t * sec = k==0? line.frontsector : line.backsector;
@ -427,7 +422,7 @@ static void InitVertexData()
}
}
for(i=0;i<level.vertexes.Size();i++)
for(unsigned i = 0; i < level.vertexes.Size(); ++i)
{
auto vert = level.vertexes[i];
int cnt = vt_sectorlists[i].Size();
@ -524,7 +519,7 @@ static void PrepareSegs()
level.sides[0].segs = new seg_t*[realsegs];
level.sides[0].numsegs = 0;
for(int i = 1; i < numsides; i++)
for(unsigned i = 1; i < numsides; i++)
{
level.sides[i].segs = level.sides[i-1].segs + segcount[i-1];
level.sides[i].numsegs = 0;
@ -539,7 +534,7 @@ static void PrepareSegs()
}
// sort the segs
for(int i = 0; i < numsides; i++)
for(unsigned i = 0; i < numsides; i++)
{
if (level.sides[i].numsegs > 1) qsort(level.sides[i].segs, level.sides[i].numsegs, sizeof(seg_t*), segcmp);
}

View file

@ -27,6 +27,7 @@
#include "p_lnspec.h"
#include "p_local.h"
#include "g_levellocals.h"
#include "a_sharedglobal.h"
#include "r_sky.h"
#include "gl/renderer/gl_renderer.h"

View file

@ -43,9 +43,6 @@
#include "gl/utility/gl_templates.h"
#include "gl/data/gl_data.h"
class ASkyViewpoint;
struct GLHorizonInfo
{
GLSectorPlane plane;

View file

@ -767,16 +767,18 @@ void FGLRenderer::SetFixedColormap (player_t *player)
}
else if (cplayer->fixedlightlevel != -1)
{
auto torchtype = PClass::FindActor(NAME_PowerTorch);
auto litetype = PClass::FindActor(NAME_PowerLightAmp);
for(AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory)
{
PalEntry color = in->GetBlend ();
// Need special handling for light amplifiers
if (in->IsKindOf(RUNTIME_CLASS(APowerTorch)))
if (in->IsKindOf(torchtype))
{
gl_fixedcolormap = cplayer->fixedlightlevel + CM_TORCH;
}
else if (in->IsKindOf(RUNTIME_CLASS(APowerLightAmp)))
else if (in->IsKindOf(litetype))
{
gl_fixedcolormap = CM_LITE;
}

View file

@ -218,7 +218,7 @@ void cht_DoCheat (player_t *player, int cheat)
case CHT_POWER:
if (player->mo != NULL && player->health >= 0)
{
item = player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
item = player->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true);
if (item != NULL)
{
item->Destroy ();
@ -226,7 +226,7 @@ void cht_DoCheat (player_t *player, int cheat)
}
else
{
player->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2));
player->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2));
msg = GStrings("TXT_CHEATPOWERON");
}
}
@ -747,6 +747,8 @@ void cht_Give (player_t *player, const char *name, int amount)
if (giveall || stricmp (name, "artifacts") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
@ -754,7 +756,7 @@ void cht_Give (player_t *player, const char *name, int amount)
{
AInventory *def = (AInventory*)GetDefaultByType (type);
if (def->Icon.isValid() && def->MaxAmount > 1 &&
!type->IsDescendantOf (RUNTIME_CLASS(APuzzleItem)) &&
!type->IsDescendantOf (pitype) &&
!type->IsDescendantOf (RUNTIME_CLASS(APowerup)) &&
!type->IsDescendantOf (RUNTIME_CLASS(AArmor)))
{
@ -772,10 +774,11 @@ void cht_Give (player_t *player, const char *name, int amount)
if (giveall || stricmp (name, "puzzlepieces") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS(APuzzleItem)))
if (type->IsDescendantOf (pitype))
{
AInventory *def = (AInventory*)GetDefaultByType (type);
if (def->Icon.isValid())
@ -859,7 +862,7 @@ void cht_Take (player_t *player, const char *name, int amount)
{
PClass *type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf(RUNTIME_CLASS (ABackpackItem)))
if (type->IsDescendantOf(PClass::FindClass(NAME_BackpackItem)))
{
AInventory *pack = player->mo->FindInventory(static_cast<PClassActor *>(type));
@ -954,13 +957,14 @@ void cht_Take (player_t *player, const char *name, int amount)
if (takeall || stricmp (name, "artifacts") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS (AInventory)))
{
if (!type->IsDescendantOf (RUNTIME_CLASS (APuzzleItem)) &&
if (!type->IsDescendantOf (pitype) &&
!type->IsDescendantOf (RUNTIME_CLASS (APowerup)) &&
!type->IsDescendantOf (RUNTIME_CLASS (AArmor)) &&
!type->IsDescendantOf (RUNTIME_CLASS (AWeapon)) &&
@ -980,11 +984,12 @@ void cht_Take (player_t *player, const char *name, int amount)
if (takeall || stricmp (name, "puzzlepieces") == 0)
{
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
type = PClassActor::AllActorClasses[i];
if (type->IsDescendantOf (RUNTIME_CLASS (APuzzleItem)))
if (type->IsDescendantOf (pitype))
{
AActor *puzzlepiece = player->mo->FindInventory(static_cast<PClassActor *>(type));

View file

@ -165,6 +165,22 @@ xx(FlameThrower)
xx(MiniMissileLauncher)
xx(StrifeGrenadeLauncher)
xx(Mauler)
xx(BackpackItem)
xx(PuzzleItem)
xx(PuzzleItemNumber)
xx(HealthPickup)
xx(autousemode)
xx(Ammo)
xx(PowerTargeter)
xx(PowerInvulnerable)
xx(PowerStrength)
xx(PowerInvisibility)
xx(PowerIronFeet)
xx(PowerLightAmp)
xx(PowerWeaponLevel2)
xx(PowerFlight)
xx(PowerSpeed)
xx(PowerTorch)
xx(AcolyteBlue)
xx(SpectralLightningV1)
@ -292,6 +308,7 @@ xx(Random2)
xx(RandomPick)
xx(FRandomPick)
xx(GetClass)
xx(GetParentClass)
xx(GetDefaultByType)
xx(Exp)
xx(Log10)

View file

@ -1053,7 +1053,7 @@ void FNodeBuilder::PrintSet (int l, DWORD set)
Printf (PRINT_LOG, "set %d:\n", l);
for (; set != DWORD_MAX; set = Segs[set].next)
{
Printf (PRINT_LOG, "\t%u(%td)%c%d(%d,%d)-%d(%d,%d)\n", set, Segs[set].frontsector->sectornum,
Printf (PRINT_LOG, "\t%u(%d)%c%d(%d,%d)-%d(%d,%d)\n", set, Segs[set].frontsector->sectornum,
Segs[set].linedef == -1 ? '+' : ':',
Segs[set].v1,
Vertices[Segs[set].v1].x>>16, Vertices[Segs[set].v1].y>>16,

View file

@ -79,7 +79,6 @@
#include "thingdef.h"
#include "math/cmath.h"
#include "a_armor.h"
#include "a_health.h"
#include "g_levellocals.h"
AActor *SingleActorFromTID(int tid, AActor *defactor);
@ -2400,7 +2399,7 @@ static bool DoGiveInventory(AActor *receiver, bool orresult, VM_ARGS)
{
return false;
}
if (item->IsKindOf(RUNTIME_CLASS(AHealth)))
if (item->IsKindOf(PClass::FindActor(NAME_Health)))
{
item->Amount *= amount;
}
@ -5655,7 +5654,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.
AInventory *gift = static_cast<AInventory *>(Spawn(item));
if (gift->IsKindOf(RUNTIME_CLASS(AHealth)))
if (gift->IsKindOf(PClass::FindActor(NAME_Health)))
{
gift->Amount *= amount;
}

View file

@ -376,7 +376,9 @@ static bool LoadGLSegs(FileReader * lump)
{ // check for gl-vertices
segs[i].v1 = &level.vertexes[checkGLVertex3(LittleLong(ml->v1))];
segs[i].v2 = &level.vertexes[checkGLVertex3(LittleLong(ml->v2))];
segs[i].PartnerSeg = LittleLong(ml->partner) == 0xffffffffu? nullptr : &segs[LittleLong(ml->partner)];
const DWORD partner = LittleLong(ml->partner);
segs[i].PartnerSeg = DWORD_MAX == partner ? nullptr : &segs[partner];
if(ml->linedef != 0xffff) // skip minisegs
{

View file

@ -59,7 +59,6 @@
#include "d_netinf.h"
#include "a_morph.h"
#include "virtual.h"
#include "a_health.h"
#include "g_levellocals.h"
static FRandom pr_obituary ("Obituary");
@ -477,7 +476,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags)
if (source->player->morphTics)
{ // Make a super chicken
source->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2));
source->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2));
}
if (deathmatch && cl_showsprees)
@ -840,11 +839,12 @@ void P_AutoUseHealth(player_t *player, int saveHealth)
TArray<AInventory *> NormalHealthItems;
TArray<AInventory *> LargeHealthItems;
auto hptype = PClass::FindActor(NAME_HealthPickup);
for(AInventory *inv = player->mo->Inventory; inv != NULL; inv = inv->Inventory)
{
if (inv->Amount > 0 && inv->IsKindOf(RUNTIME_CLASS(AHealthPickup)))
if (inv->Amount > 0 && inv->IsKindOf(hptype))
{
int mode = static_cast<AHealthPickup*>(inv)->autousemode;
int mode = inv->IntVar(NAME_autousemode);
if (mode == 1) NormalHealthItems.Push(inv);
else if (mode == 2) LargeHealthItems.Push(inv);
@ -884,12 +884,12 @@ void P_AutoUseStrifeHealth (player_t *player)
{
TArray<AInventory *> Items;
auto hptype = PClass::FindActor(NAME_HealthPickup);
for(AInventory *inv = player->mo->Inventory; inv != NULL; inv = inv->Inventory)
{
if (inv->Amount > 0 && inv->IsKindOf(RUNTIME_CLASS(AHealthPickup)))
if (inv->Amount > 0 && inv->IsKindOf(hptype))
{
int mode = static_cast<AHealthPickup*>(inv)->autousemode;
int mode = inv->IntVar(NAME_autousemode);
if (mode == 3) Items.Push(inv);
}
}

View file

@ -2138,11 +2138,12 @@ FUNC(LS_UsePuzzleItem)
if (!it) return false;
// Check player's inventory for puzzle item
auto pitype = PClass::FindActor(NAME_PuzzleItem);
for (item = it->Inventory; item != NULL; item = item->Inventory)
{
if (item->IsKindOf (RUNTIME_CLASS(APuzzleItem)))
if (item->IsKindOf (pitype))
{
if (static_cast<APuzzleItem*>(item)->PuzzleItemNumber == arg0)
if (item->IntVar(NAME_PuzzleItemNumber) == arg0)
{
if (it->UseInventory (item))
{
@ -2840,23 +2841,23 @@ FUNC(LS_SetPlayerProperty)
// Add or remove a power
if (arg2 >= PROP_INVULNERABILITY && arg2 <= PROP_SPEED)
{
static PClass * const *powers[11] =
static ENamedName powers[11] =
{
&RUNTIME_CLASS_CASTLESS(APowerInvulnerable),
&RUNTIME_CLASS_CASTLESS(APowerStrength),
&RUNTIME_CLASS_CASTLESS(APowerInvisibility),
&RUNTIME_CLASS_CASTLESS(APowerIronFeet),
NULL, // MapRevealer
&RUNTIME_CLASS_CASTLESS(APowerLightAmp),
&RUNTIME_CLASS_CASTLESS(APowerWeaponLevel2),
&RUNTIME_CLASS_CASTLESS(APowerFlight),
NULL,
NULL,
&RUNTIME_CLASS_CASTLESS(APowerSpeed)
NAME_PowerInvulnerable,
NAME_PowerStrength,
NAME_PowerInvisibility,
NAME_PowerIronFeet,
NAME_None,
NAME_PowerLightAmp,
NAME_PowerWeaponLevel2,
NAME_PowerFlight,
NAME_None,
NAME_None,
NAME_PowerSpeed
};
int power = arg2 - PROP_INVULNERABILITY;
if (power > 4 && powers[power] == NULL)
if (power > 4 && powers[power] == NAME_None)
{
return false;
}
@ -2867,7 +2868,7 @@ FUNC(LS_SetPlayerProperty)
{ // Give power to activator
if (power != 4)
{
APowerup *item = static_cast<APowerup*>(it->GiveInventoryType(static_cast<PClassActor *>(*powers[power])));
APowerup *item = static_cast<APowerup*>(it->GiveInventoryType(PClass::FindActor(powers[power])));
if (item != NULL && power == 0 && arg1 == 1)
{
item->BlendColor = MakeSpecialColormap(INVERSECOLORMAP);
@ -2882,7 +2883,7 @@ FUNC(LS_SetPlayerProperty)
{ // Take power from activator
if (power != 4)
{
AInventory *item = it->FindInventory(static_cast<PClassActor *>(*powers[power]), true);
AInventory *item = it->FindInventory(PClass::FindActor(powers[power]), true);
if (item != NULL)
{
item->Destroy ();
@ -2907,7 +2908,7 @@ FUNC(LS_SetPlayerProperty)
{ // Give power
if (power != 4)
{
APowerup *item = static_cast<APowerup*>(players[i].mo->GiveInventoryType (static_cast<PClassActor *>(*powers[power])));
APowerup *item = static_cast<APowerup*>(players[i].mo->GiveInventoryType ((PClass::FindActor(powers[power]))));
if (item != NULL && power == 0 && arg1 == 1)
{
item->BlendColor = MakeSpecialColormap(INVERSECOLORMAP);
@ -2922,7 +2923,7 @@ FUNC(LS_SetPlayerProperty)
{ // Take power
if (power != 4)
{
AInventory *item = players[i].mo->FindInventory (static_cast<PClassActor *>(*powers[power]));
AInventory *item = players[i].mo->FindInventory (PClass::FindActor(powers[power]));
if (item != NULL)
{
item->Destroy ();

View file

@ -5515,6 +5515,13 @@ bool P_UsePuzzleItem(AActor *PuzzleItemUser, int PuzzleItemType)
return false;
}
DEFINE_ACTION_FUNCTION(AActor, UsePuzzleItem)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(puzznum);
ACTION_RETURN_BOOL(P_UsePuzzleItem(self, puzznum));
}
//==========================================================================
//
// RADIUS ATTACK

View file

@ -71,8 +71,8 @@
#include "virtual.h"
#include "a_armor.h"
#include "a_ammo.h"
#include "a_health.h"
#include "g_levellocals.h"
#include "a_morph.h"
// MACROS ------------------------------------------------------------------
@ -1199,18 +1199,7 @@ void AActor::ClearInventory()
AInventory *inv = *invp;
if (!(inv->ItemFlags & IF_UNDROPPABLE))
{
// For the sake of undroppable weapons, never remove ammo once
// it has been acquired; just set its amount to 0.
if (inv->IsKindOf(RUNTIME_CLASS(AAmmo)))
{
AAmmo *ammo = static_cast<AAmmo*>(inv);
ammo->Amount = 0;
invp = &inv->Inventory;
}
else
{
inv->Destroy ();
}
inv->DepleteOrDestroy();
}
else if (inv->GetClass() == RUNTIME_CLASS(AHexenArmor))
{
@ -1311,6 +1300,122 @@ void AActor::ObtainInventory (AActor *other)
}
}
DEFINE_ACTION_FUNCTION(AActor, ObtainInventory)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(other, AActor);
self->ObtainInventory(other);
return 0;
}
//---------------------------------------------------------------------------
//
// FUNC P_GiveBody
//
// Returns false if the body isn't needed at all.
//
//---------------------------------------------------------------------------
bool P_GiveBody(AActor *actor, int num, int max)
{
if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD))
{ // Do not heal dead things.
return false;
}
player_t *player = actor->player;
num = clamp(num, -65536, 65536); // prevent overflows for bad values
if (player != NULL)
{
// Max is 0 by default, preserving default behavior for P_GiveBody()
// calls while supporting health pickups.
if (max <= 0)
{
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina;
// [MH] First step in predictable generic morph effects
if (player->morphTics)
{
if (player->MorphStyle & MORPH_FULLHEALTH)
{
if (!(player->MorphStyle & MORPH_ADDSTAMINA))
{
max -= player->mo->stamina;
}
}
else // old health behaviour
{
max = MAXMORPHHEALTH;
if (player->MorphStyle & MORPH_ADDSTAMINA)
{
max += player->mo->stamina;
}
}
}
}
// [RH] For Strife: A negative body sets you up with a percentage
// of your full health.
if (num < 0)
{
num = max * -num / 100;
if (player->health < num)
{
player->health = num;
actor->health = num;
return true;
}
}
else if (num > 0)
{
if (player->health < max)
{
num = int(num * G_SkillProperty(SKILLP_HealthFactor));
if (num < 1) num = 1;
player->health += num;
if (player->health > max)
{
player->health = max;
}
actor->health = player->health;
return true;
}
}
}
else
{
// Parameter value for max is ignored on monsters, preserving original
// behaviour of health as well as on existing calls to P_GiveBody().
max = actor->SpawnHealth();
if (num < 0)
{
num = max * -num / 100;
if (actor->health < num)
{
actor->health = num;
return true;
}
}
else if (actor->health < max)
{
actor->health += num;
if (actor->health > max)
{
actor->health = max;
}
return true;
}
}
return false;
}
DEFINE_ACTION_FUNCTION(AActor, GiveBody)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(num);
PARAM_INT_DEF(max);
ACTION_RETURN_BOOL(P_GiveBody(self, num, max));
}
//============================================================================
//
// AActor :: CheckLocalView
@ -4832,6 +4937,13 @@ void AActor::MarkPrecacheSounds() const
CrushPainSound.MarkUsed();
}
DEFINE_ACTION_FUNCTION(AActor, MarkPrecacheSounds)
{
PARAM_SELF_PROLOGUE(AActor);
self->MarkPrecacheSounds();
return 0;
}
bool AActor::isFast()
{
if (flags5&MF5_ALWAYSFAST) return true;
@ -5539,9 +5651,10 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
// [RH] Other things that shouldn't be spawned depending on dmflags
if (deathmatch || alwaysapplydmflags)
{
// Fixme: This needs to be done differently, it's quite broken.
if (dmflags & DF_NO_HEALTH)
{
if (i->IsDescendantOf (RUNTIME_CLASS(AHealth)))
if (i->IsDescendantOf (PClass::FindActor(NAME_Health)))
return NULL;
if (i->TypeName == NAME_Berserk)
return NULL;

View file

@ -237,7 +237,7 @@ DPSprite *player_t::GetPSprite(PSPLayers layer)
{
if (mo != nullptr)
{
newcaller = mo->FindInventory(RUNTIME_CLASS(APowerTargeter), true);
newcaller = mo->FindInventory(PClass::FindActor(NAME_PowerTargeter), true);
}
}
else if (layer == PSP_STRIFEHANDS)
@ -485,7 +485,7 @@ void P_BringUpWeapon (player_t *player)
if (weapon != nullptr &&
weapon->SisterWeapon &&
weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true))
player->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true))
{
weapon = weapon->SisterWeapon;
}

View file

@ -378,7 +378,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subs
.StringPtr(nullptr, str)
.EndArray();
if (num_verts == level.vertexes.Size() && num_subs == numsubsectors && hasglnodes)
if (num_verts == (int)level.vertexes.Size() && num_subs == numsubsectors && hasglnodes)
{
success = true;
int sub = 0;
@ -964,7 +964,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
arc.Array("sectors", &level.sectors[0], &loadsectors[0], level.sectors.Size());
arc("zones", Zones);
arc("lineportals", linePortals);
arc("sectorportals", sectorPortals);
arc("sectorportals", level.sectorPortals);
if (arc.isReading()) P_CollectLinkedPortals();
DThinker::SerializeThinkers(arc, !hubload);
@ -1009,4 +1009,4 @@ void P_FreeMapDataBackup()
loadsectors.Clear();
loadlines.Clear();
loadsides.Clear();
}
}

View file

@ -529,7 +529,7 @@ void P_SpawnScrollers(void)
FLineIdIterator itr(l->args[0]);
while ((s = itr.Next()) >= 0)
{
if (s != i)
if (s != (int)i)
new DScroller(dx, dy, &level.lines[s], control, accel);
}
break;

View file

@ -21,6 +21,7 @@
//
//-----------------------------------------------------------------------------
#include "g_levellocals.h"
#include "r_state.h"
#include "p_maputl.h"
#include "p_blockmap.h"

View file

@ -1673,12 +1673,6 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt)
ACTION_RETURN_INT(self->GetPlaneLight(pos));
}
class FSetTextureID : public FTextureID
{
public:
FSetTextureID(int v) : FTextureID(v) {}
};
DEFINE_ACTION_FUNCTION(_Sector, SetTexture)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
@ -1837,6 +1831,22 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt)
ACTION_RETURN_INT(ndx);
}
DEFINE_ACTION_FUNCTION(_Sector, SetEnvironmentID)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(envnum);
Zones[self->ZoneNumber].Environment = S_FindEnvironment(envnum);
return 0;
}
DEFINE_ACTION_FUNCTION(_Sector, SetEnvironment)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_STRING(env);
Zones[self->ZoneNumber].Environment = S_FindEnvironment(env);
return 0;
}
//===========================================================================
//
// line_t exports

View file

@ -2298,7 +2298,7 @@ static void P_AllocateSideDefs (MapData *map, int count)
sidetemp[i].a.alpha = SHRT_MIN;
sidetemp[i].a.map = NO_SIDE;
}
auto numsides = map->Size(ML_SIDEDEFS) / sizeof(mapsidedef_t);
int numsides = map->Size(ML_SIDEDEFS) / sizeof(mapsidedef_t);
if (count < numsides)
{
Printf ("Map has %d unused sidedefs\n", numsides - count);

View file

@ -845,12 +845,12 @@ void DWallLightTransfer::DoTransfer (short lightlevel, int target, BYTE flags)
//---------------------------------------------------------------------------
// Upper stacks go in the top sector. Lower stacks go in the bottom sector.
static void SetupFloorPortal (AStackPoint *point)
static void SetupFloorPortal (AActor *point)
{
NActorIterator it (NAME_LowerStackLookOnly, point->tid);
sector_t *Sector = point->Sector;
ASkyViewpoint *skyv = static_cast<ASkyViewpoint*>(it.Next());
if (skyv != NULL)
auto skyv = it.Next();
if (skyv != nullptr)
{
skyv->target = point;
if (Sector->GetAlpha(sector_t::floor) == 1.)
@ -860,12 +860,12 @@ static void SetupFloorPortal (AStackPoint *point)
}
}
static void SetupCeilingPortal (AStackPoint *point)
static void SetupCeilingPortal (AActor *point)
{
NActorIterator it (NAME_UpperStackLookOnly, point->tid);
sector_t *Sector = point->Sector;
ASkyViewpoint *skyv = static_cast<ASkyViewpoint*>(it.Next());
if (skyv != NULL)
auto skyv = it.Next();
if (skyv != nullptr)
{
skyv->target = point;
if (Sector->GetAlpha(sector_t::ceiling) == 1.)
@ -877,9 +877,9 @@ static void SetupCeilingPortal (AStackPoint *point)
void P_SetupPortals()
{
TThinkerIterator<AStackPoint> it;
AStackPoint *pt;
TArray<AStackPoint *> points;
TThinkerIterator<AActor> it("StackPoint");
AActor *pt;
TArray<AActor *> points;
while ((pt = it.Next()))
{
@ -897,21 +897,21 @@ void P_SetupPortals()
}
// the semantics here are incredibly lax so the final setup can only be done once all portals have been created,
// because later stackpoints will happily overwrite info in older ones, if there are multiple links.
for (auto &s : sectorPortals)
for (auto &s : level.sectorPortals)
{
if (s.mType == PORTS_STACKEDSECTORTHING && s.mSkybox)
{
for (auto &ss : sectorPortals)
for (auto &ss : level.sectorPortals)
{
if (ss.mType == PORTS_STACKEDSECTORTHING && ss.mSkybox == s.mSkybox->target)
{
s.mPartner = unsigned((&ss) - &sectorPortals[0]);
s.mPartner = unsigned((&ss) - &level.sectorPortals[0]);
}
}
}
}
// Now we can finally set the displacement and delete the stackpoint reference.
for (auto &s : sectorPortals)
for (auto &s : level.sectorPortals)
{
if (s.mType == PORTS_STACKEDSECTORTHING && s.mSkybox)
{
@ -932,7 +932,7 @@ static void SetPortal(sector_t *sector, int plane, unsigned pnum, double alpha)
if (sector->GetAlpha(sector_t::ceiling) == 1.)
sector->SetAlpha(sector_t::ceiling, alpha);
if (sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY)
if (level.sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY)
sector->SetTexture(sector_t::ceiling, skyflatnum);
}
}
@ -945,7 +945,7 @@ static void SetPortal(sector_t *sector, int plane, unsigned pnum, double alpha)
if (sector->GetAlpha(sector_t::floor) == 1.)
sector->SetAlpha(sector_t::floor, alpha);
if (sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY)
if (level.sectorPortals[pnum].mFlags & PORTSF_SKYFLATONLY)
sector->SetTexture(sector_t::floor, skyflatnum);
}
}
@ -1027,7 +1027,7 @@ void P_SpawnPortal(line_t *line, int sectortag, int plane, int bytealpha, int li
// This searches the viewpoint's sector
// for a skybox line special, gets its tag and transfers the skybox to all tagged sectors.
void P_SpawnSkybox(ASkyViewpoint *origin)
void P_SpawnSkybox(AActor *origin)
{
sector_t *Sector = origin->Sector;
if (Sector == NULL)
@ -1260,8 +1260,8 @@ void P_SpawnSpecials (void)
P_SpawnFriction(); // phares 3/12/98: New friction model using linedefs
P_SpawnPushers(); // phares 3/20/98: New pusher model using linedefs
TThinkerIterator<ASkyCamCompat> it2;
ASkyCamCompat *pt2;
TThinkerIterator<AActor> it2("SkyCamCompat");
AActor *pt2;
while ((pt2 = it2.Next()))
{
P_SpawnSkybox(pt2);

View file

@ -948,7 +948,7 @@ AWeapon *APlayerPawn::BestWeapon(PClassInventory *ammotype)
int bestOrder = INT_MAX;
AInventory *item;
AWeapon *weap;
bool tomed = NULL != FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
bool tomed = NULL != FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true);
// Find the best weapon the player has.
for (item = Inventory; item != NULL; item = item->Inventory)
@ -1048,6 +1048,13 @@ void APlayerPawn::CheckWeaponSwitch(PClassInventory *ammotype)
}
}
DEFINE_ACTION_FUNCTION(APlayerPawn, CheckWeaponSwitch)
{
PARAM_SELF_PROLOGUE(APlayerPawn);
PARAM_OBJECT(ammotype, PClassInventory);
self->CheckWeaponSwitch(ammotype);
return 0;
}
//===========================================================================
//
// APlayerPawn :: GiveDeathmatchInventory

View file

@ -25,6 +25,7 @@
#include "doomdef.h"
#include "p_maputl.h"
#include "sbar.h"
#include "g_levellocals.h"
#include "r_data/r_translate.h"
#include "poly_portal.h"
#include "polyrenderer/poly_renderer.h"
@ -96,7 +97,7 @@ void PolyDrawSectorPortal::SaveGlobals()
if (Portal->mType == PORTS_SKYVIEWPOINT)
{
// Don't let gun flashes brighten the sky box
ASkyViewpoint *sky = barrier_cast<ASkyViewpoint*>(Portal->mSkybox);
AActor *sky = Portal->mSkybox;
extralight = 0;
swrenderer::R_SetVisibility(sky->args[0] * 0.25f);
ViewPos = sky->InterpolatedPosition(r_TicFracF);
@ -115,13 +116,13 @@ void PolyDrawSectorPortal::SaveGlobals()
R_SetViewAngle();
Portal->mFlags |= PORTSF_INSKYBOX;
if (Portal->mPartner > 0) sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX;
if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX;
}
void PolyDrawSectorPortal::RestoreGlobals()
{
Portal->mFlags &= ~PORTSF_INSKYBOX;
if (Portal->mPartner > 0) sectorPortals[Portal->mPartner].mFlags &= ~PORTSF_INSKYBOX;
if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags &= ~PORTSF_INSKYBOX;
camera = savedcamera;
viewsector = savedsector;

View file

@ -62,7 +62,16 @@ FPortalBlockmap PortalBlockmap;
TArray<FLinePortal> linePortals;
TArray<FLinePortal*> linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
TArray<FSectorPortal> sectorPortals;
DEFINE_FIELD(FSectorPortal, mType);
DEFINE_FIELD(FSectorPortal, mFlags);
DEFINE_FIELD(FSectorPortal, mPartner);
DEFINE_FIELD(FSectorPortal, mPlane);
DEFINE_FIELD(FSectorPortal, mOrigin);
DEFINE_FIELD(FSectorPortal, mDestination);
DEFINE_FIELD(FSectorPortal, mDisplacement);
DEFINE_FIELD(FSectorPortal, mPlaneZ);
DEFINE_FIELD(FSectorPortal, mSkybox);
//============================================================================
//
@ -490,15 +499,15 @@ void P_ClearPortals()
Displacements.Create(1);
linePortals.Clear();
linkedPortals.Clear();
sectorPortals.Resize(2);
level.sectorPortals.Resize(2);
// The first entry must always be the default skybox. This is what every sector gets by default.
memset(&sectorPortals[0], 0, sizeof(sectorPortals[0]));
sectorPortals[0].mType = PORTS_SKYVIEWPOINT;
sectorPortals[0].mFlags = PORTSF_SKYFLATONLY;
memset(&level.sectorPortals[0], 0, sizeof(level.sectorPortals[0]));
level.sectorPortals[0].mType = PORTS_SKYVIEWPOINT;
level.sectorPortals[0].mFlags = PORTSF_SKYFLATONLY;
// The second entry will be the default sky. This is for forcing a regular sky through the skybox picker
memset(&sectorPortals[1], 0, sizeof(sectorPortals[0]));
sectorPortals[1].mType = PORTS_SKYVIEWPOINT;
sectorPortals[1].mFlags = PORTSF_SKYFLATONLY;
memset(&level.sectorPortals[1], 0, sizeof(level.sectorPortals[0]));
level.sectorPortals[1].mType = PORTS_SKYVIEWPOINT;
level.sectorPortals[1].mFlags = PORTSF_SKYFLATONLY;
}
//============================================================================
@ -651,22 +660,28 @@ void P_TranslatePortalZ(line_t* src, double& z)
//
//============================================================================
unsigned P_GetSkyboxPortal(ASkyViewpoint *actor)
unsigned P_GetSkyboxPortal(AActor *actor)
{
if (actor == NULL) return 1; // this means a regular sky.
for (unsigned i = 0;i<sectorPortals.Size();i++)
for (unsigned i = 0;i<level.sectorPortals.Size();i++)
{
if (sectorPortals[i].mSkybox == actor) return i;
if (level.sectorPortals[i].mSkybox == actor) return i;
}
unsigned i = sectorPortals.Reserve(1);
memset(&sectorPortals[i], 0, sizeof(sectorPortals[i]));
sectorPortals[i].mType = PORTS_SKYVIEWPOINT;
sectorPortals[i].mFlags = actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(ASkyCamCompat)) ? 0 : PORTSF_SKYFLATONLY;
sectorPortals[i].mSkybox = actor;
sectorPortals[i].mDestination = actor->Sector;
unsigned i = level.sectorPortals.Reserve(1);
memset(&level.sectorPortals[i], 0, sizeof(level.sectorPortals[i]));
level.sectorPortals[i].mType = PORTS_SKYVIEWPOINT;
level.sectorPortals[i].mFlags = actor->GetClass()->IsDescendantOf(PClass::FindActor("SkyCamCompat")) ? 0 : PORTSF_SKYFLATONLY;
level.sectorPortals[i].mSkybox = actor;
level.sectorPortals[i].mDestination = actor->Sector;
return i;
}
DEFINE_ACTION_FUNCTION(FSectorPortal, GetSkyboxPortal)
{
PARAM_PROLOGUE;
PARAM_OBJECT(actor, AActor);
ACTION_RETURN_INT(P_GetSkyboxPortal(actor));
}
//============================================================================
//
// P_GetPortal
@ -677,14 +692,14 @@ unsigned P_GetSkyboxPortal(ASkyViewpoint *actor)
unsigned P_GetPortal(int type, int plane, sector_t *from, sector_t *to, const DVector2 &displacement)
{
unsigned i = sectorPortals.Reserve(1);
memset(&sectorPortals[i], 0, sizeof(sectorPortals[i]));
sectorPortals[i].mType = type;
sectorPortals[i].mPlane = plane;
sectorPortals[i].mOrigin = from;
sectorPortals[i].mDestination = to;
sectorPortals[i].mDisplacement = displacement;
sectorPortals[i].mPlaneZ = type == PORTS_LINKEDPORTAL? from->GetPlaneTexZ(plane) : FLT_MAX;
unsigned i = level.sectorPortals.Reserve(1);
memset(&level.sectorPortals[i], 0, sizeof(level.sectorPortals[i]));
level.sectorPortals[i].mType = type;
level.sectorPortals[i].mPlane = plane;
level.sectorPortals[i].mOrigin = from;
level.sectorPortals[i].mDestination = to;
level.sectorPortals[i].mDisplacement = displacement;
level.sectorPortals[i].mPlaneZ = type == PORTS_LINKEDPORTAL? from->GetPlaneTexZ(plane) : FLT_MAX;
return i;
}
@ -698,14 +713,14 @@ unsigned P_GetPortal(int type, int plane, sector_t *from, sector_t *to, const DV
unsigned P_GetStackPortal(AActor *point, int plane)
{
unsigned i = sectorPortals.Reserve(1);
memset(&sectorPortals[i], 0, sizeof(sectorPortals[i]));
sectorPortals[i].mType = PORTS_STACKEDSECTORTHING;
sectorPortals[i].mPlane = plane;
sectorPortals[i].mOrigin = point->target->Sector;
sectorPortals[i].mDestination = point->Sector;
sectorPortals[i].mPlaneZ = FLT_MAX;
sectorPortals[i].mSkybox = point;
unsigned i = level.sectorPortals.Reserve(1);
memset(&level.sectorPortals[i], 0, sizeof(level.sectorPortals[i]));
level.sectorPortals[i].mType = PORTS_STACKEDSECTORTHING;
level.sectorPortals[i].mPlane = plane;
level.sectorPortals[i].mOrigin = point->target->Sector;
level.sectorPortals[i].mDestination = point->Sector;
level.sectorPortals[i].mPlaneZ = FLT_MAX;
level.sectorPortals[i].mSkybox = point;
return i;
}
@ -971,7 +986,7 @@ void P_CreateLinkedPortals()
int id = 1;
bool bogus = false;
for(auto &s : sectorPortals)
for(auto &s : level.sectorPortals)
{
if (s.mType == PORTS_LINKEDPORTAL)
{

View file

@ -6,7 +6,6 @@
#include "m_bbox.h"
struct FPortalGroupArray;
class ASkyViewpoint;
struct portnode_t;
//============================================================================
//
@ -237,8 +236,6 @@ struct FSectorPortal
}
};
extern TArray<FSectorPortal> sectorPortals;
//============================================================================
//
// Functions
@ -256,7 +253,7 @@ inline int P_NumPortalGroups()
{
return Displacements.size;
}
unsigned P_GetSkyboxPortal(ASkyViewpoint *actor);
unsigned P_GetSkyboxPortal(AActor *actor);
unsigned P_GetPortal(int type, int plane, sector_t *orgsec, sector_t *destsec, const DVector2 &displacement);
unsigned P_GetStackPortal(AActor *point, int plane);

View file

@ -291,8 +291,6 @@ protected:
bool CheckTrigger(AActor *triggerer) const;
};
class ASkyViewpoint;
struct secplane_t
{
// the plane is defined as a*x + b*y + c*z + d = 0
@ -924,32 +922,13 @@ public:
portals[plane] = nullptr;
}
FSectorPortal *GetPortal(int plane)
{
return &sectorPortals[Portals[plane]];
}
FSectorPortal *GetPortal(int plane);
double GetPortalPlaneZ(int plane);
DVector2 GetPortalDisplacement(int plane);
int GetPortalType(int plane);
int GetOppositePortalGroup(int plane);
double GetPortalPlaneZ(int plane)
{
return sectorPortals[Portals[plane]].mPlaneZ;
}
DVector2 GetPortalDisplacement(int plane)
{
return sectorPortals[Portals[plane]].mDisplacement;
}
int GetPortalType(int plane)
{
return sectorPortals[Portals[plane]].mType;
}
int GetOppositePortalGroup(int plane)
{
return sectorPortals[Portals[plane]].mDestination->PortalGroup;
}
void SetVerticesDirty()
void SetVerticesDirty()
{
for (unsigned i = 0; i < e->vertices.Size(); i++) e->vertices[i]->dirty = true;
}
@ -1311,10 +1290,7 @@ struct line_t
alpha = a;
}
FSectorPortal *GetTransferredPortal()
{
return portaltransferred >= sectorPortals.Size() ? (FSectorPortal*)NULL : &sectorPortals[portaltransferred];
}
FSectorPortal *GetTransferredPortal();
FLinePortal *getPortal() const
{

View file

@ -57,6 +57,7 @@
#include "r_utility.h"
#include "d_player.h"
#include "p_local.h"
#include "g_levellocals.h"
#include "p_maputl.h"
#include "math/cmath.h"

View file

@ -425,11 +425,27 @@ void DSeqNode::AddChoice (int seqnum, seqtype_t type)
}
}
DEFINE_ACTION_FUNCTION(DSeqNode, AddChoice)
{
PARAM_SELF_PROLOGUE(DSeqNode);
PARAM_NAME(seq);
PARAM_INT(mode);
self->AddChoice(seq, seqtype_t(mode));
return 0;
}
FName DSeqNode::GetSequenceName () const
{
return Sequences[m_Sequence]->SeqName;
}
DEFINE_ACTION_FUNCTION(DSeqNode, GetSequenceName)
{
PARAM_SELF_PROLOGUE(DSeqNode);
ACTION_RETURN_INT(self->GetSequenceName().GetIndex());
}
IMPLEMENT_CLASS(DSeqActorNode, false, true)
IMPLEMENT_POINTERS_START(DSeqActorNode)
@ -869,6 +885,16 @@ DSeqNode *SN_StartSequence (AActor *actor, int sequence, seqtype_t type, int mod
return NULL;
}
DEFINE_ACTION_FUNCTION(AActor, StartSoundSequenceID)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(seq);
PARAM_INT(type);
PARAM_INT(modenum);
PARAM_BOOL_DEF(nostop);
ACTION_RETURN_POINTER(SN_StartSequence(self, seq, seqtype_t(type), modenum, nostop));
}
DSeqNode *SN_StartSequence (sector_t *sector, int chan, int sequence, seqtype_t type, int modenum, bool nostop)
{
if (!nostop)
@ -882,6 +908,17 @@ DSeqNode *SN_StartSequence (sector_t *sector, int chan, int sequence, seqtype_t
return NULL;
}
DEFINE_ACTION_FUNCTION(_Sector, StartSoundSequenceID)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(chan);
PARAM_INT(seq);
PARAM_INT(type);
PARAM_INT(modenum);
PARAM_BOOL_DEF(nostop);
ACTION_RETURN_POINTER(SN_StartSequence(self, chan, seq, seqtype_t(type), modenum, nostop));
}
DSeqNode *SN_StartSequence (FPolyObj *poly, int sequence, seqtype_t type, int modenum, bool nostop)
{
if (!nostop)
@ -921,6 +958,15 @@ DSeqNode *SN_StartSequence (AActor *actor, FName seqname, int modenum)
return NULL;
}
DEFINE_ACTION_FUNCTION(AActor, StartSoundSequence)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_NAME(seq);
PARAM_INT(modenum);
ACTION_RETURN_POINTER(SN_StartSequence(self, seq, modenum));
}
DSeqNode *SN_StartSequence (sector_t *sec, int chan, const char *seqname, int modenum)
{
int seqnum = FindSequence(seqname);
@ -941,6 +987,15 @@ DSeqNode *SN_StartSequence (sector_t *sec, int chan, FName seqname, int modenum)
return NULL;
}
DEFINE_ACTION_FUNCTION(_Sector, StartSoundSequence)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(chan);
PARAM_NAME(seq);
PARAM_INT(modenum);
ACTION_RETURN_POINTER(SN_StartSequence(self, chan, seq, modenum));
}
DSeqNode *SN_StartSequence (FPolyObj *poly, const char *seqname, int modenum)
{
int seqnum = FindSequence(seqname);
@ -1001,6 +1056,13 @@ DSeqNode *SN_CheckSequence(sector_t *sector, int chan)
return NULL;
}
DEFINE_ACTION_FUNCTION(_Sector, CheckSoundSequence)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(chan);
ACTION_RETURN_POINTER(SN_CheckSequence(self, chan));
}
//==========================================================================
//
// SN_StopSequence
@ -1012,6 +1074,13 @@ void SN_StopSequence (AActor *actor)
SN_DoStop (actor);
}
DEFINE_ACTION_FUNCTION(AActor, StopSoundSequence)
{
PARAM_SELF_PROLOGUE(AActor);
SN_StopSequence(self);
return 0;
}
void SN_StopSequence (sector_t *sector, int chan)
{
DSeqNode *node = SN_CheckSequence(sector, chan);
@ -1021,6 +1090,15 @@ void SN_StopSequence (sector_t *sector, int chan)
}
}
DEFINE_ACTION_FUNCTION(_Sector, StopSoundSequence)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
PARAM_INT(chan);
SN_StopSequence(self, chan);
return 0;
}
void SN_StopSequence (FPolyObj *poly)
{
SN_DoStop (poly);
@ -1090,6 +1168,13 @@ bool SN_IsMakingLoopingSound (sector_t *sector)
return false;
}
DEFINE_ACTION_FUNCTION(_Sector, IsMakingLoopingSound)
{
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
ACTION_RETURN_BOOL(SN_IsMakingLoopingSound(self));
}
//==========================================================================
//
// SN_UpdateActiveSequences
@ -1325,6 +1410,15 @@ FName SN_GetSequenceSlot (int sequence, seqtype_t type)
return NAME_None;
}
DEFINE_ACTION_FUNCTION(DSeqNode, GetSequenceSlot)
{
PARAM_PROLOGUE;
PARAM_INT(seq);
PARAM_INT(type);
ACTION_RETURN_INT(SN_GetSequenceSlot(seq, seqtype_t(type)).GetIndex());
}
//==========================================================================
//
// SN_MarkPrecacheSounds
@ -1351,6 +1445,16 @@ void SN_MarkPrecacheSounds(int sequence, seqtype_t type)
}
}
DEFINE_ACTION_FUNCTION(DSeqNode, MarkPrecacheSounds)
{
PARAM_PROLOGUE;
PARAM_INT(seq);
PARAM_INT(type);
SN_MarkPrecacheSounds(seq, seqtype_t(type));
return 0;
}
//==========================================================================
//
// SN_ChangeNodeData
@ -1462,7 +1566,16 @@ bool SN_AreModesSame(int seqnum, seqtype_t type, int mode1, int mode2)
return true;
}
bool SN_AreModesSame(const char *name, int mode1, int mode2)
DEFINE_ACTION_FUNCTION(DSeqNode, AreModesSameID)
{
PARAM_SELF_PROLOGUE(DSeqNode);
PARAM_INT(seqnum);
PARAM_INT(type);
PARAM_INT(mode);
ACTION_RETURN_BOOL(SN_AreModesSame(seqnum, seqtype_t(type), mode, self->GetModeNum()));
}
bool SN_AreModesSame(FName name, int mode1, int mode2)
{
int seqnum = FindSequence(name);
if (seqnum >= 0)
@ -1473,6 +1586,14 @@ bool SN_AreModesSame(const char *name, int mode1, int mode2)
return true;
}
DEFINE_ACTION_FUNCTION(DSeqNode, AreModesSame)
{
PARAM_SELF_PROLOGUE(DSeqNode);
PARAM_NAME(seq);
PARAM_INT(mode);
ACTION_RETURN_BOOL(SN_AreModesSame(seq, mode, self->GetModeNum()));
}
//==========================================================================
//
// CCMD playsequence

View file

@ -20,7 +20,7 @@ class DSeqNode : public DObject
DECLARE_CLASS (DSeqNode, DObject)
HAS_OBJECT_POINTERS
public:
void Serialize(FSerializer &arc);
void Serialize(FSerializer &arc) override;
void StopAndDestroy ();
void OnDestroy() override;
void Tick ();
@ -91,7 +91,7 @@ void SN_StopSequence (AActor *mobj);
void SN_StopSequence (sector_t *sector, int chan);
void SN_StopSequence (FPolyObj *poly);
bool SN_AreModesSame(int sequence, seqtype_t type, int mode1, int mode2);
bool SN_AreModesSame(const char *name, int mode1, int mode2);
bool SN_AreModesSame(FName name, int mode1, int mode2);
void SN_UpdateActiveSequences (void);
ptrdiff_t SN_GetSequenceOffset (int sequence, SDWORD *sequencePtr);
void SN_DoStop (void *);

View file

@ -54,6 +54,7 @@
#include "d_player.h"
#include "r_state.h"
#include "g_levellocals.h"
#include "virtual.h"
// MACROS ------------------------------------------------------------------
@ -479,7 +480,16 @@ void S_PrecacheLevel ()
// Precache all sounds known to be used by the currently spawned actors.
while ( (actor = iterator.Next()) != NULL )
{
actor->MarkPrecacheSounds();
IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds)
{
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { actor };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else
{
actor->MarkPrecacheSounds();
}
}
for (auto i : gameinfo.PrecachedSounds)
{

View file

@ -146,7 +146,7 @@ std2:
'instanceof' { RET(TK_InstanceOf); }
'auto' { RET(TK_Auto); }
'exec' { RET(TK_Exec); }
'defaultproperties' { RET(TK_DefaultProperties); }
'property' { RET(TK_Property); }
'native' { RET(TK_Native); }
'var' { RET(TK_Var); }
'out' { RET(TK_Out); }

View file

@ -67,6 +67,7 @@ xx(TK_Long, "'long'")
xx(TK_ULong, "'ulong'")
xx(TK_Void, "'void'")
xx(TK_Struct, "'struct'")
xx(TK_Property, "'property'")
xx(TK_Class, "'class'")
xx(TK_Enum, "'enum'")
xx(TK_Name, "'name'")

View file

@ -208,10 +208,6 @@ ExpEmit::ExpEmit(VMFunctionBuilder *build, int type, int count)
void ExpEmit::Free(VMFunctionBuilder *build)
{
if (RegType == REGT_INT && RegNum == 0)
{
int a = 0;
}
if (!Fixed && !Konst && RegType <= REGT_TYPE)
{
build->Registers[RegType].Return(RegNum, RegCount);
@ -1620,6 +1616,17 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
if (fromtype->IsDescendantOf(totype)) goto basereturn;
}
}
else if (basex->ValueType->IsA(RUNTIME_CLASS(PNativeStruct)) && ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType == basex->ValueType)
{
bool writable;
basex->RequestAddress(ctx, &writable);
basex->ValueType = ValueType;
auto x = basex;
basex = nullptr;
delete this;
return x;
}
else if (AreCompatiblePointerTypes(ValueType, basex->ValueType))
{
goto basereturn;
@ -2669,7 +2676,11 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
return nullptr;
}
if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant())
if (left->ValueType == TypeTextureID && right->IsInteger())
{
ValueType = TypeTextureID;
}
else if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant())
{
// This is the only special case of pointer addition that will be accepted - because it is used quite often in the existing game code.
ValueType = TypeState;
@ -2748,6 +2759,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
assert(Operator == '+' || Operator == '-');
ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build);
ExpEmit to;
if (Operator == '+')
{
if (op1.RegType == REGT_POINTER)
@ -2768,7 +2780,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
assert(!op1.Konst);
op1.Free(build);
op2.Free(build);
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
to = ExpEmit(build, ValueType->GetRegType(), ValueType->GetRegCount());
if (IsVector())
{
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
@ -2791,6 +2803,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
assert(ValueType->GetRegType() == REGT_INT);
assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT);
build->Emit(op2.Konst ? OP_ADD_RK : OP_ADD_RR, to.RegNum, op1.RegNum, op2.RegNum);
if (ValueType == TypeTextureID) goto texcheck;
return to;
}
}
@ -2800,7 +2813,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
assert(!op1.Konst || !op2.Konst);
op1.Free(build);
op2.Free(build);
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
to = ExpEmit(build, ValueType->GetRegType(), ValueType->GetRegCount());
if (IsVector())
{
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
@ -2818,9 +2831,23 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
assert(ValueType->GetRegType() == REGT_INT);
assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT);
build->Emit(op1.Konst ? OP_SUB_KR : op2.Konst ? OP_SUB_RK : OP_SUB_RR, to.RegNum, op1.RegNum, op2.RegNum);
if (ValueType == TypeTextureID) goto texcheck;
return to;
}
}
texcheck:
// Do a bounds check for the texture index. Note that count can change at run time so this needs to read the value from the texture manager.
auto * ptr = (FArray*)&TexMan.Textures;
auto * countptr = &ptr->Count;
ExpEmit bndp(build, REGT_POINTER);
ExpEmit bndc(build, REGT_INT);
build->Emit(OP_LKP, bndp.RegNum, build->GetConstantAddress(countptr, ATAG_GENERIC));
build->Emit(OP_LW, bndc.RegNum, bndp.RegNum, build->GetConstantInt(0));
build->Emit(OP_BOUND_R, to.RegNum, bndc.RegNum);
bndp.Free(build);
bndc.Free(build);
return to;
}
//==========================================================================
@ -4436,10 +4463,6 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(expr, ctx);
if (expr->ExprType == EFX_GetDefaultByType)
{
int a = 0;
}
bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer *>(expr->ValueType)->IsConst;
if (constflag)
{
@ -6461,7 +6484,7 @@ FxStructMember::~FxStructMember()
bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable)
{
// Cannot take the address of metadata variables.
if (membervar->Flags & VARF_Static)
if (membervar->Flags & VARF_Meta)
{
return false;
}
@ -6604,7 +6627,7 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build)
obj = newobj;
}
if (membervar->Flags & VARF_Static)
if (membervar->Flags & VARF_Meta)
{
obj.Free(build);
ExpEmit meta(build, REGT_POINTER);
@ -6747,12 +6770,12 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
{
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
SizeAddr = unsigned(parentfield->Offset + parentfield->Type->Align);
SizeAddr = parentfield->Offset + parentfield->Type->Align;
}
else if (Array->ExprType == EFX_GlobalVariable)
{
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
SizeAddr = unsigned(parentfield->Offset + parentfield->Type->Align);
SizeAddr = parentfield->Offset + parentfield->Type->Align;
}
else
{
@ -6836,12 +6859,16 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
{
arraytype = static_cast<PArray*>(Array->ValueType);
}
ExpEmit start = Array->Emit(build);
ExpEmit arrayvar = Array->Emit(build);
ExpEmit start;
ExpEmit bound;
// For resizable arrays we even need to check the bounds if if the index is constant.
if (SizeAddr != ~0u)
{
arrayvar.Free(build);
start = ExpEmit(build, REGT_POINTER);
build->Emit(OP_LP, start.RegNum, arrayvar.RegNum, build->GetConstantInt(0));
auto f = new PField(NAME_None, TypeUInt32, 0, SizeAddr);
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
{
@ -6857,12 +6884,14 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
Array->ValueType = TypeUInt32;
bound = Array->Emit(build);
}
else start = arrayvar;
if (index->isConstant())
{
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
assert(SizeAddr != ~0u || (indexval < arraytype->ElementCount && "Array index out of bounds"));
// For resizable arrays we even need to check the bounds if if the index is constant because they are not known at compile time.
if (SizeAddr != ~0u)
{
ExpEmit indexreg(build, REGT_INT);
@ -6948,19 +6977,11 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
if (AddressRequested)
{
if (!start.Fixed)
{
build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexwork.RegNum);
}
else
{
start.Free(build);
// do not clobber local variables.
ExpEmit temp(build, start.RegType);
build->Emit(OP_ADDA_RR, temp.RegNum, start.RegNum, indexwork.RegNum);
start = temp;
}
return start;
start.Free(build);
// do not clobber local variables.
ExpEmit temp(build, start.RegType);
build->Emit(OP_ADDA_RR, temp.RegNum, start.RegNum, indexwork.RegNum);
return temp;
}
else
{
@ -7256,6 +7277,13 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
}
break;
case NAME_GetParentClass:
if (CheckArgSize(NAME_GetParentClass, ArgList, 0, 0, ScriptPosition))
{
func = new FxGetParentClass(new FxSelf(ScriptPosition));
}
break;
case NAME_GetDefaultByType:
if (CheckArgSize(NAME_GetDefaultByType, ArgList, 1, 1, ScriptPosition))
{
@ -7422,7 +7450,28 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
{
cls = ccls;
staticonly = true;
goto isresolved;
if (ccls->IsKindOf(RUNTIME_CLASS(PClass)))
{
auto clstype = dyn_cast<PClass>(ctx.Function->Variants[0].SelfClass);
if (clstype != nullptr)
{
novirtual = clstype->IsDescendantOf(static_cast<PClass*>(ccls));
if (novirtual)
{
bool error;
PFunction *afd = FindClassMemberFunction(ccls, ctx.Class, MethodName, ScriptPosition, &error);
if ((afd->Variants[0].Flags & VARF_Method) && (afd->Variants[0].Flags & VARF_Virtual))
{
staticonly = false;
novirtual = true;
delete Self;
Self = new FxSelf(ScriptPosition);
Self->ValueType = NewPointer(cls);
}
}
}
}
if (!novirtual) goto isresolved;
}
}
}
@ -7510,6 +7559,13 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
else if (Self->ValueType == TypeString)
{
if (MethodName == NAME_Length) // This is an intrinsic because a dedicated opcode exists for it.
{
auto x = new FxStrLen(Self);
Self = nullptr;
delete this;
return x->Resolve(ctx);
}
// same for String methods. It also uses a hidden struct type to define them.
Self->ValueType = TypeStringStruct;
}
@ -7566,6 +7622,20 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
}
}
if (MethodName == NAME_GetParentClass &&
(Self->IsObject() || Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))))
{
if (ArgList.Size() > 0)
{
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
delete this;
return nullptr;
}
auto x = new FxGetParentClass(Self);
return x->Resolve(ctx);
}
if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
{
auto ptype = static_cast<PPointer *>(Self->ValueType)->PointedType;
@ -7582,6 +7652,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
auto x = new FxGetClass(Self);
return x->Resolve(ctx);
}
cls = static_cast<PStruct *>(ptype);
}
else
@ -7634,20 +7706,23 @@ isresolved:
if (staticonly && (afd->Variants[0].Flags & VARF_Method))
{
auto clstype = dyn_cast<PClass>(ctx.Class);
auto ccls = dyn_cast<PClass>(cls);
if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls))
if (!novirtual || !(afd->Variants[0].Flags & VARF_Virtual))
{
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
delete this;
return nullptr;
}
else
{
// Todo: If this is a qualified call to a parent class function, let it through (but this needs to disable virtual calls later.)
ScriptPosition.Message(MSG_ERROR, "Qualified member call to parent class %s::%s is not yet implemented\n", cls->TypeName.GetChars(), MethodName.GetChars());
delete this;
return nullptr;
auto clstype = dyn_cast<PClass>(ctx.Class);
auto ccls = dyn_cast<PClass>(cls);
if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls))
{
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
delete this;
return nullptr;
}
else
{
// Todo: If this is a qualified call to a parent class function, let it through (but this needs to disable virtual calls later.)
ScriptPosition.Message(MSG_ERROR, "Qualified member call to parent class %s::%s is not yet implemented\n", cls->TypeName.GetChars(), MethodName.GetChars());
delete this;
return nullptr;
}
}
}
@ -8485,7 +8560,7 @@ static int BuiltinFormat(VMValue *args, TArray<VMValue> &defaultparam, int numpa
// various type flags are not supported. not like stuff like 'hh' modifier is to be used in the VM.
// the only combination that is parsed locally is %n$...
bool haveargnums = false;
for (int i = 0; i < fmtstring.Len(); i++)
for (size_t i = 0; i < fmtstring.Len(); i++)
{
char c = fmtstring[i];
if (in_fmt)
@ -8703,6 +8778,39 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build)
//
//==========================================================================
FxStrLen::FxStrLen(FxExpression *self)
:FxExpression(EFX_StrLen, self->ScriptPosition)
{
Self = self;
}
FxStrLen::~FxStrLen()
{
SAFE_DELETE(Self);
}
FxExpression *FxStrLen::Resolve(FCompileContext &ctx)
{
SAFE_RESOLVE(Self, ctx);
assert(Self->ValueType == TypeString);
ValueType = TypeUInt32;
return this;
}
ExpEmit FxStrLen::Emit(VMFunctionBuilder *build)
{
ExpEmit to(build, REGT_INT);
ExpEmit op = Self->Emit(build);
build->Emit(OP_LENS, to.RegNum, op.RegNum);
op.Free(build);
return to;
}
//==========================================================================
//
//
//==========================================================================
FxGetClass::FxGetClass(FxExpression *self)
:FxExpression(EFX_GetClass, self->ScriptPosition)
{
@ -8741,6 +8849,52 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
//
//==========================================================================
FxGetParentClass::FxGetParentClass(FxExpression *self)
:FxExpression(EFX_GetParentClass, self->ScriptPosition)
{
Self = self;
}
FxGetParentClass::~FxGetParentClass()
{
SAFE_DELETE(Self);
}
FxExpression *FxGetParentClass::Resolve(FCompileContext &ctx)
{
SAFE_RESOLVE(Self, ctx);
if (!Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && !Self->IsObject())
{
ScriptPosition.Message(MSG_ERROR, "GetClass() requires an object");
delete this;
return nullptr;
}
ValueType = NewClassPointer(RUNTIME_CLASS(DObject)); //
return this;
}
ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build)
{
ExpEmit op = Self->Emit(build);
op.Free(build);
if (Self->IsObject())
{
ExpEmit to(build, REGT_POINTER);
build->Emit(OP_META, to.RegNum, op.RegNum);
op = to;
op.Free(build);
}
ExpEmit to(build, REGT_POINTER);
build->Emit(OP_LO, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, ParentClass)));
return to;
}
//==========================================================================
//
//
//==========================================================================
FxGetDefaultByType::FxGetDefaultByType(FxExpression *self)
:FxExpression(EFX_GetDefaultByType, self->ScriptPosition)
{

View file

@ -289,6 +289,8 @@ enum EFxType
EFX_CVar,
EFX_NamedNode,
EFX_GetClass,
EFX_GetParentClass,
EFX_StrLen,
EFX_ColorLiteral,
EFX_GetDefaultByType,
EFX_COUNT
@ -1438,7 +1440,7 @@ class FxArrayElement : public FxExpression
public:
FxExpression *Array;
FxExpression *index;
unsigned SizeAddr;
size_t SizeAddr;
bool AddressRequested;
bool AddressWritable;
bool arrayispointer = false;
@ -1571,6 +1573,24 @@ public:
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxVectorBuiltin
//
//==========================================================================
class FxStrLen : public FxExpression
{
FxExpression *Self;
public:
FxStrLen(FxExpression *self);
~FxStrLen();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxGetClass
@ -1589,6 +1609,24 @@ public:
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxGetClass
//
//==========================================================================
class FxGetParentClass : public FxExpression
{
FxExpression *Self;
public:
FxGetParentClass(FxExpression *self);
~FxGetParentClass();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxGetDefaultByType

View file

@ -472,7 +472,7 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int
{ &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs), 1 },
};
#define NUM_FLAG_LISTS (countof(FlagLists))
@ -738,6 +738,9 @@ void InitThingdef()
vertstruct->Size = sizeof(vertex_t);
vertstruct->Align = alignof(vertex_t);
auto sectorportalstruct = NewNativeStruct("SectorPortal", nullptr);
sectorportalstruct->Size = sizeof(FSectorPortal);
sectorportalstruct->Align = alignof(FSectorPortal);
// set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well...
// As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up.
@ -762,11 +765,17 @@ void InitThingdef()
PField *levelf = new PField("level", lstruct, VARF_Native | VARF_Static, (intptr_t)&level);
GlobalSymbols.AddSymbol(levelf);
// Add the sector array to LevelLocals.
// Add the game data arrays to LevelLocals.
lstruct->AddNativeField("sectors", NewPointer(NewResizableArray(sectorstruct), false), myoffsetof(FLevelLocals, sectors), VARF_Native);
lstruct->AddNativeField("lines", NewPointer(NewResizableArray(linestruct), false), myoffsetof(FLevelLocals, lines), VARF_Native);
lstruct->AddNativeField("sides", NewPointer(NewResizableArray(sidestruct), false), myoffsetof(FLevelLocals, sides), VARF_Native);
lstruct->AddNativeField("vertexes", NewPointer(NewResizableArray(vertstruct), false), myoffsetof(FLevelLocals, vertexes), VARF_Native|VARF_ReadOnly);
lstruct->AddNativeField("sectorportals", NewPointer(NewResizableArray(sectorportalstruct), false), myoffsetof(FLevelLocals, sectorPortals), VARF_Native);
auto aact = NewPointer(NewResizableArray(NewClassPointer(RUNTIME_CLASS(AActor))), true);
PField *aacf = new PField("AllActorClasses", aact, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PClassActor::AllActorClasses);
GlobalSymbols.AddSymbol(aacf);
// set up a variable for the DEH data
PStruct *dstruct = NewNativeStruct("DehInfo", nullptr);
@ -905,7 +914,10 @@ DEFINE_ACTION_FUNCTION(FStringTable, Localize)
{
PARAM_PROLOGUE;
PARAM_STRING(label);
ACTION_RETURN_STRING(GStrings(label));
PARAM_BOOL_DEF(prefixed);
if (!prefixed) ACTION_RETURN_STRING(GStrings(label));
if (label[0] != '$') ACTION_RETURN_STRING(label);
ACTION_RETURN_STRING(GStrings(&label[1]));
}
DEFINE_ACTION_FUNCTION(FString, Replace)

View file

@ -71,7 +71,6 @@
#include "a_weaponpiece.h"
#include "vmbuilder.h"
#include "a_ammo.h"
#include "a_health.h"
#include "a_keys.h"
#include "g_levellocals.h"
@ -1969,46 +1968,6 @@ DEFINE_CLASS_PROPERTY(givequest, I, Inventory)
static_cast<PClassInventory *>(info)->GiveQuest = i;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(lowmessage, IT, Health)
{
PROP_INT_PARM(i, 0);
PROP_STRING_PARM(str, 1);
assert(info->IsKindOf(RUNTIME_CLASS(PClassHealth)));
static_cast<PClassHealth *>(info)->LowHealth = i;
static_cast<PClassHealth *>(info)->LowHealthMessage = str;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(autouse, I, HealthPickup)
{
PROP_INT_PARM(i, 0);
defaults->autousemode = i;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(number, I, PuzzleItem)
{
PROP_INT_PARM(i, 0);
defaults->PuzzleItemNumber = i;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(failmessage, T, PuzzleItem)
{
PROP_STRING_PARM(str, 0);
assert(info->IsKindOf(RUNTIME_CLASS(PClassPuzzleItem)));
static_cast<PClassPuzzleItem *>(info)->PuzzFailMessage = str;
}
//==========================================================================
//
//==========================================================================

View file

@ -399,21 +399,30 @@ begin:
reg.s[a] = reg.s[B];
NEXTOP;
OP(MOVEA):
{
ASSERTA(a); ASSERTA(B);
reg.a[a] = reg.a[B];
reg.atag[a] = reg.atag[B];
int b = B;
reg.a[a] = reg.a[b];
reg.atag[a] = reg.atag[b];
NEXTOP;
}
OP(MOVEV2):
{
ASSERTF(a); ASSERTF(B);
reg.f[a] = reg.f[B];
reg.f[a+1] = reg.f[B+1];
int b = B;
reg.f[a] = reg.f[b];
reg.f[a + 1] = reg.f[b + 1];
NEXTOP;
}
OP(MOVEV3):
{
ASSERTF(a); ASSERTF(B);
reg.f[a] = reg.f[B];
reg.f[a+1] = reg.f[B+1];
reg.f[a+2] = reg.f[B+2];
int b = B;
reg.f[a] = reg.f[b];
reg.f[a + 1] = reg.f[b + 1];
reg.f[a + 2] = reg.f[b + 2];
NEXTOP;
}
OP(DYNCAST_R) :
ASSERTA(a); ASSERTA(B); ASSERTA(C);
b = B;

View file

@ -336,6 +336,16 @@ static void PrintStruct(FLispString &out, ZCC_TreeNode *node)
out.Close();
}
static void PrintProperty(FLispString &out, ZCC_TreeNode *node)
{
ZCC_Property *snode = (ZCC_Property *)node;
out.Break();
out.Open("property");
out.AddName(snode->NodeName);
PrintNodes(out, snode->Body, false, true);
out.Close();
}
static void PrintEnum(FLispString &out, ZCC_TreeNode *node)
{
ZCC_Enum *enode = (ZCC_Enum *)node;
@ -934,6 +944,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *
PrintVectorInitializer,
PrintDeclFlags,
PrintExprClassCast,
PrintProperty,
};
FString ZCC_PrintAST(ZCC_TreeNode *root)

View file

@ -209,7 +209,6 @@ class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ }
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; }
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) ACTION. { X.Flags = A.Flags | ZCC_Action; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }
/*----- Dottable Identifier -----*/
@ -265,6 +264,7 @@ class_body(X) ::= LBRACE class_innards(A) RBRACE. { X = A; /*X-overwrites-A*/ }
class_innards(X) ::= . { X = NULL; }
class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); }
%type property_def{ZCC_Property *}
%type struct_def{ZCC_Struct *}
%type enum_def {ZCC_Enum *}
%type states_def {ZCC_States *}
@ -276,6 +276,7 @@ class_member(X) ::= struct_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ }
/*----- Struct Definition -----*/
/* Structs can define variables and enums. */
@ -283,6 +284,30 @@ class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
%type opt_struct_body{ZCC_TreeNode *}
%type struct_body{ZCC_TreeNode *}
%type struct_member{ZCC_TreeNode *}
%type identifier_list{ZCC_Identifier *}
property_def(X) ::= PROPERTY(T) IDENTIFIER(A) COLON identifier_list(B) SEMICOLON.
{
NEW_AST_NODE(Property,def,T);
def->NodeName = A.Name();
def->Body = B;
X = def;
}
identifier_list(X) ::= IDENTIFIER(A).
{
NEW_AST_NODE(Identifier,id,A);
id->Id = A.Name();
X = id;
}
identifier_list(X) ::= states_opt(A) COMMA IDENTIFIER(B).
{
NEW_AST_NODE(Identifier,id,B);
id->Id = B.Name();
X = A; /*X-overwrites-A*/
AppendTreeNodeSibling(X, id);
}
struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
{
@ -747,7 +772,7 @@ type_name(X) ::= DOT dottable_id(A).
/* Type names can also be used as identifiers in contexts where type names
* are not normally allowed. */
%fallback IDENTIFIER
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16.
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY.
/* Aggregate types */
%type aggregate_type {ZCC_Type *}
@ -964,6 +989,7 @@ decl_flag(X) ::= PROTECTED(T). { X.Int = ZCC_Protected; X.SourceLoc = T.SourceL
decl_flag(X) ::= LATENT(T). { X.Int = ZCC_Latent; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= TRANSIENT(T). { X.Int = ZCC_Transient; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; }

View file

@ -120,6 +120,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode)
}
break;
case AST_Property:
cls->Properties.Push(static_cast<ZCC_Property *>(node));
break;
case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break;
@ -396,6 +400,7 @@ int ZCCCompiler::Compile()
CreateStructTypes();
CompileAllConstants();
CompileAllFields();
CompileAllProperties();
InitDefaults();
InitFunctions();
CompileStates();
@ -1269,6 +1274,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
if (field->Flags & ZCC_Native)
{
@ -1277,7 +1283,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Meta)
{
varflags |= VARF_Static|VARF_ReadOnly; // metadata implies readonly
varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly
if (!(field->Flags & ZCC_Native))
{
// Non-native meta data is not implemented yet and requires some groundwork in the class copy code.
@ -1303,7 +1309,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (varflags & VARF_Native)
{
auto querytype = (varflags & VARF_Static) ? type->GetClass() : type;
auto querytype = (varflags & VARF_Meta) ? type->GetClass() : type;
fd = FindField(querytype, FName(name->Name).GetChars());
if (fd == nullptr)
{
@ -1337,6 +1343,68 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
return Fields.Size() == 0;
}
//==========================================================================
//
// ZCCCompiler :: CompileAllProperties
//
// builds the property lists of all actor classes
//
//==========================================================================
void ZCCCompiler::CompileAllProperties()
{
for (auto c : Classes)
{
if (c->Properties.Size() > 0)
CompileProperties(c->Type(), c->Properties, c->Type()->TypeName);
}
}
//==========================================================================
//
// ZCCCompiler :: CompileProperties
//
// builds the internal structure of a single class or struct
//
//==========================================================================
bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Properties, FName prefix)
{
if (!type->IsKindOf(RUNTIME_CLASS(PClassActor)))
{
Error(Properties[0], "Properties can only be defined for actors");
return false;
}
for(auto p : Properties)
{
TArray<PField *> fields;
ZCC_Identifier *id = (ZCC_Identifier *)p->Body;
do
{
auto f = dyn_cast<PField>(type->Symbols.FindSymbol(id->Id, true));
if (f == nullptr)
{
Error(id, "Variable %s not found in %s", FName(id->Id).GetChars(), type->TypeName.GetChars());
}
fields.Push(f);
id = (ZCC_Identifier*)id->SiblingNext;
} while (id != p->Body);
FString qualifiedname;
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
if (prefix == NAME_None) qualifiedname.Format("@property@%s", FName(p->NodeName).GetChars());
else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), FName(p->NodeName).GetChars());
fields.ShrinkToFit();
if (!type->Symbols.AddSymbol(new PProperty(qualifiedname, fields)))
{
Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
}
}
return true;
}
//==========================================================================
//
// ZCCCompiler :: FieldFlagsToString
@ -1841,6 +1909,73 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
}
}
//==========================================================================
//
// Parses an actor property's parameters and calls the handler
//
//==========================================================================
void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag)
{
unsigned parmcount = 1;
ZCC_TreeNode *x = property->Values;
while (x->SiblingNext != property->Values)
{
x = x->SiblingNext;
parmcount++;
}
if (parmcount != prop->Variables.Size())
{
Error(x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size());
return;
}
auto values = Simplify(property->Values, &bag.Info->Symbols, true); // need to do this before the loop so that we can find the head node again.
auto exp = values;
for (auto f : prop->Variables)
{
void *addr;
if (f->Flags & VARF_Meta)
{
addr = ((char*)bag.Info) + f->Offset;
}
else
{
addr = ((char*)defaults) + f->Offset;
}
if (f->Type->IsKindOf(RUNTIME_CLASS(PInt)))
{
static_cast<PInt*>(f->Type)->SetValue(addr, GetInt(exp));
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PFloat)))
{
static_cast<PFloat*>(f->Type)->SetValue(addr, GetDouble(exp));
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PString)))
{
*(FString*)addr = GetString(exp);
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
{
auto cls = PClass::FindClass(GetString(exp));
*(PClass**)addr = cls;
if (!cls->IsDescendantOf(static_cast<PClassPointer*>(f->Type)->ClassRestriction))
{
Error(property, "class %s is not compatible with property type %s", cls->TypeName.GetChars(), static_cast<PClassPointer*>(f->Type)->ClassRestriction->TypeName.GetChars());
}
}
else
{
Error(property, "unhandled property type %s", f->Type->DescriptiveName());
}
exp->ToErrorNode(); // invalidate after processing.
exp = static_cast<ZCC_Expression *>(exp->SiblingNext);
}
}
//==========================================================================
//
// Parses an actor property
@ -1893,6 +2028,17 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
}
else
{
propname.Insert(0, "@property@");
FName name(propname, true);
if (name != NAME_None)
{
auto propp = dyn_cast<PProperty>(cls->Symbols.FindSymbol(name, true));
if (propp != nullptr)
{
DispatchScriptProperty(propp, prop, (AActor *)bag.Info->Defaults, bag);
return;
}
}
Error(prop, "'%s' is an unknown actor property\n", propname.GetChars());
}
}
@ -2306,19 +2452,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
unsigned vindex = ~0u;
if (virtsym != nullptr) vindex = virtsym->Variants[0].Implementation->VirtualIndex;
if (vindex != ~0u || (varflags & VARF_Virtual))
{
// Todo: Check if the declaration is legal.
// First step: compare prototypes - if they do not match the virtual base method does not apply.
// Second step: Check flags. Possible cases:
// 1. Base method is final: Error.
// 2. This method is override: Base virtual method must exist
// 3. This method is virtual but not override: Base may not have a virtual method with the same prototype.
}
if (!(f->Flags & ZCC_Native))
{
if (f->Body == nullptr)

View file

@ -51,6 +51,7 @@ struct ZCC_ClassWork : public ZCC_StructWork
ZCC_Class *cls;
TArray<ZCC_Default *> Defaults;
TArray<ZCC_States *> States;
TArray<ZCC_Property *> Properties;
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
{
@ -67,6 +68,12 @@ struct ZCC_ClassWork : public ZCC_StructWork
}
};
struct ZCC_PropertyWork
{
ZCC_Property *prop;
PSymbolTable *outputtable;
};
struct ZCC_ConstantWork
{
ZCC_ConstantDef *node;
@ -92,6 +99,8 @@ private:
void CompileAllFields();
bool CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false);
void CompileAllProperties();
bool CompileProperties(PClass *type, TArray<ZCC_Property *> &Properties, FName prefix);
FString FlagsToString(uint32_t flags);
PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember);
PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym);
@ -101,6 +110,7 @@ private:
void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg);
void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag);
void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
void DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
int GetInt(ZCC_Expression *expr);
double GetDouble(ZCC_Expression *expr);
const char *GetString(ZCC_Expression *expr, bool silent = false);
@ -114,6 +124,7 @@ private:
TArray<ZCC_ConstantDef *> Constants;
TArray<ZCC_StructWork *> Structs;
TArray<ZCC_ClassWork *> Classes;
TArray<ZCC_PropertyWork *> Properties;
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);

View file

@ -144,6 +144,8 @@ static void InitTokenMap()
TOKENDEF ('{', ZCC_LBRACE);
TOKENDEF ('}', ZCC_RBRACE);
TOKENDEF (TK_Struct, ZCC_STRUCT);
TOKENDEF (TK_Property, ZCC_PROPERTY);
TOKENDEF (TK_Transient, ZCC_TRANSIENT);
TOKENDEF (TK_Enum, ZCC_ENUM);
TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte);
TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte);

View file

@ -35,6 +35,7 @@ enum
ZCC_Extension = 1 << 12,
ZCC_Virtual = 1 << 13,
ZCC_Override = 1 << 14,
ZCC_Transient = 1 << 15,
};
// Function parameter modifiers
@ -104,6 +105,7 @@ enum EZCCTreeNodeType
AST_DeclFlags,
AST_ClassCast,
AST_StaticArrayStatement,
AST_Property,
NUM_AST_NODE_TYPES
};
@ -189,6 +191,11 @@ struct ZCC_Struct : ZCC_NamedNode
PStruct *Type;
};
struct ZCC_Property : ZCC_NamedNode
{
ZCC_TreeNode *Body;
};
struct ZCC_Class : ZCC_Struct
{
ZCC_Identifier *ParentName;

View file

@ -28,6 +28,7 @@
#include "a_sharedglobal.h"
#include "d_net.h"
#include "g_level.h"
#include "g_levellocals.h"
#include "r_wallsetup.h"
#include "v_palette.h"
#include "r_utility.h"

View file

@ -16,7 +16,6 @@
#include <stddef.h>
#include "r_defs.h"
class ASkyViewpoint;
class ADynamicLight;
struct FLightNode;
struct FDynamicColormap;

View file

@ -44,6 +44,7 @@
#include "version.h"
#include "r_utility.h"
#include "r_3dfloors.h"
#include "g_levellocals.h"
#include "swrenderer/drawers/r_draw_rgba.h"
#include "swrenderer/segments/r_clipsegment.h"
#include "swrenderer/segments/r_drawsegment.h"
@ -135,7 +136,7 @@ namespace swrenderer
case PORTS_SKYVIEWPOINT:
{
// Don't let gun flashes brighten the sky box
ASkyViewpoint *sky = barrier_cast<ASkyViewpoint*>(port->mSkybox);
AActor *sky = port->mSkybox;
extralight = 0;
R_SetVisibility(sky->args[0] * 0.25f);
@ -170,7 +171,7 @@ namespace swrenderer
}
port->mFlags |= PORTSF_INSKYBOX;
if (port->mPartner > 0) sectorPortals[port->mPartner].mFlags |= PORTSF_INSKYBOX;
if (port->mPartner > 0) level.sectorPortals[port->mPartner].mFlags |= PORTSF_INSKYBOX;
camera = nullptr;
viewsector = port->mDestination;
assert(viewsector != nullptr);
@ -234,7 +235,7 @@ namespace swrenderer
planes->Render();
port->mFlags &= ~PORTSF_INSKYBOX;
if (port->mPartner > 0) sectorPortals[port->mPartner].mFlags &= ~PORTSF_INSKYBOX;
if (port->mPartner > 0) level.sectorPortals[port->mPartner].mFlags &= ~PORTSF_INSKYBOX;
}
// Draw all the masked textures in a second pass, in the reverse order they

View file

@ -261,6 +261,15 @@ FTextureID FTextureManager::CheckForTexture (const char *name, int usetype, BITF
return FTextureID(-1);
}
DEFINE_ACTION_FUNCTION(_TexMan, CheckForTexture)
{
PARAM_PROLOGUE;
PARAM_STRING(name);
PARAM_INT(type);
PARAM_INT_DEF(flags);
ACTION_RETURN_INT(TexMan.CheckForTexture(name, type, flags).GetIndex());
}
//==========================================================================
//
// FTextureManager :: ListTextures

View file

@ -360,9 +360,11 @@ public:
bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch);
};
class FxAddSub;
// Texture manager
class FTextureManager
{
friend class FxAddSub; // needs access to do a bounds check on the texture ID.
public:
FTextureManager ();
~FTextureManager ();

View file

@ -129,6 +129,16 @@ void DCanvas::DrawTexture (FTexture *img, double x, double y, int tags_first, ..
DrawTextureParms(img, parms);
}
DEFINE_ACTION_FUNCTION(_Screen, DrawHUDTexture)
{
PARAM_PROLOGUE;
PARAM_INT(texid);
PARAM_FLOAT(x);
PARAM_FLOAT(y);
screen->DrawTexture(TexMan(FSetTextureID(texid)), x, y, DTA_HUDRules, HUD_Normal, TAG_END);
return 0;
}
void DCanvas::DrawTextureParms(FTexture *img, DrawParms &parms)
{
#ifndef NO_SWRENDER

View file

@ -1,14 +1,19 @@
#include "zscript/base.txt"
#include "zscript/sounddata.txt"
#include "zscript/mapdata.txt"
#include "zscript/dynarrays.txt"
#include "zscript/constants.txt"
#include "zscript/actor.txt"
#include "zscript/actor_checks.txt"
#include "zscript/shared/inventory.txt"
#include "zscript/shared/inv_misc.txt"
#include "zscript/shared/weapons.txt"
#include "zscript/shared/armor.txt"
#include "zscript/shared/powerups.txt"
#include "zscript/inventory/inventory.txt"
#include "zscript/inventory/inv_misc.txt"
#include "zscript/inventory/weapons.txt"
#include "zscript/inventory/armor.txt"
#include "zscript/inventory/ammo.txt"
#include "zscript/inventory/health.txt"
#include "zscript/inventory/powerups.txt"
#include "zscript/shared/player.txt"
#include "zscript/shared/morph.txt"
#include "zscript/shared/botstuff.txt"

View file

@ -309,6 +309,7 @@ class Actor : Thinker native
virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0);
virtual native bool Slam(Actor victim);
virtual native void Touch(Actor toucher);
virtual native void MarkPrecacheSounds();
// Called by PIT_CheckThing to check if two actos actually can collide.
virtual bool CanCollideWith(Actor other, bool passive)
@ -420,6 +421,10 @@ class Actor : Thinker native
native bool HitFriend();
native bool MonsterMove();
native SeqNode StartSoundSequenceID (int sequence, int type, int modenum, bool nostop = false);
native SeqNode StartSoundSequence (Name seqname, int modenum);
native void StopSoundSequence();
native void FindFloorCeiling(int flags = 0);
native double, double GetFriction();
native bool, Actor TestMobjZ(bool quick = false);
@ -486,7 +491,9 @@ class Actor : Thinker native
native Inventory GiveInventoryType(class<Inventory> itemtype);
native Inventory DropInventory (Inventory item);
native bool UseInventory(Inventory item);
native void ObtainInventory(Actor other);
native bool GiveAmmo (Class<Ammo> type, int amount);
native bool UsePuzzleItem(int PuzzleItemType);
native float AccuracyFactor();
// DECORATE compatible functions
@ -721,13 +728,6 @@ class Actor : Thinker native
native void A_ActiveSound();
native void A_FastChase();
native void A_FreezeDeath();
native void A_FreezeDeathChunks();
void A_GenericFreezeDeath()
{
A_SetTranslation('Ice');
A_FreezeDeath();
}
native void A_PlayerScream();
native void A_SkullPop(class<PlayerChunk> skulltype = "BloodySkull");
native void A_CheckPlayerDone();

View file

@ -1,3 +1,42 @@
struct TexMan
{
enum EUseTypes
{
Type_Any,
Type_Wall,
Type_Flat,
Type_Sprite,
Type_WallPatch,
Type_Build,
Type_SkinSprite,
Type_Decal,
Type_MiscPatch,
Type_FontChar,
Type_Override, // For patches between TX_START/TX_END
Type_Autopage, // Automap background - used to enable the use of FAutomapTexture
Type_SkinGraphic,
Type_Null,
Type_FirstDefined,
};
enum EFlags
{
TryAny = 1,
Overridable = 2,
ReturnFirst = 4,
AllowSkins = 8,
ShortNameOnly = 16,
DontCreate = 32
};
native static TextureID CheckForTexture(String name, int usetype, int flags = TryAny);
}
struct Screen
{
native static void DrawHUDTexture(TextureID tex, double x, double y);
}
class Object native
{
native bool bDestroyed;
@ -143,12 +182,13 @@ struct LevelLocals native
native bool polygrind;
native bool nomonsters;
native bool frozen;
native bool infinite_flight;
// level_info_t *info cannot be done yet.
}
struct StringTable native
{
native static String Localize(String val);
native static String Localize(String val, bool prefixed = false);
}
// a few values of this need to be readable by the play code.
@ -187,369 +227,6 @@ struct F3DFloor native
{
}
struct Vertex native
{
native readonly Vector2 p;
}
struct Side
{
enum ETexpart
{
top=0,
mid=1,
bottom=2
};
enum EWallFlags
{
WALLF_ABSLIGHTING = 1, // Light is absolute instead of relative
WALLF_NOAUTODECALS = 2, // Do not attach impact decals to this wall
WALLF_NOFAKECONTRAST = 4, // Don't do fake contrast for this wall in side_t::GetLightLevel
WALLF_SMOOTHLIGHTING = 8, // Similar to autocontrast but applies to all angles.
WALLF_CLIP_MIDTEX = 16, // Like the line counterpart, but only for this side.
WALLF_WRAP_MIDTEX = 32, // Like the line counterpart, but only for this side.
WALLF_POLYOBJ = 64, // This wall belongs to a polyobject.
WALLF_LIGHT_FOG = 128, // This wall's Light is used even in fog.
};
native Sector sector; // Sector the SideDef is facing.
//DBaseDecal* AttachedDecals; // [RH] Decals bound to the wall
native Line linedef;
native int16 Light;
native uint8 Flags;
native TextureID GetTexture(int which);
native void SetTexture(int which, TextureID tex);
native void SetTextureXOffset(int which, double offset);
native double GetTextureXOffset(int which);
native void AddTextureXOffset(int which, double delta);
native void SetTextureYOffset(int which, double offset);
native double GetTextureYOffset(int which);
native void AddTextureYOffset(int which, double delta);
native void SetTextureXScale(int which, double scale);
native double GetTextureXScale(int which);
native void MultiplyTextureXScale(int which, double delta);
native void SetTextureYScale(int which, double scale);
native double GetTextureYScale(int which);
native void MultiplyTextureYScale(int which, double delta);
//native DInterpolation *SetInterpolation(int position);
//native void StopInterpolation(int position);
native Vertex V1();
native Vertex V2();
native int Index();
};
struct Line native
{
enum ELineFlags
{
ML_BLOCKING =0x00000001, // solid, is an obstacle
ML_BLOCKMONSTERS =0x00000002, // blocks monsters only
ML_TWOSIDED =0x00000004, // backside will not be present at all if not two sided
ML_DONTPEGTOP = 0x00000008, // upper texture unpegged
ML_DONTPEGBOTTOM = 0x00000010, // lower texture unpegged
ML_SECRET = 0x00000020, // don't map as two sided: IT'S A SECRET!
ML_SOUNDBLOCK = 0x00000040, // don't let sound cross two of these
ML_DONTDRAW = 0x00000080, // don't draw on the automap
ML_MAPPED = 0x00000100, // set if already drawn in automap
ML_REPEAT_SPECIAL = 0x00000200, // special is repeatable
ML_ADDTRANS = 0x00000400, // additive translucency (can only be set internally)
// Extended flags
ML_MONSTERSCANACTIVATE = 0x00002000, // [RH] Monsters (as well as players) can activate the line
ML_BLOCK_PLAYERS = 0x00004000,
ML_BLOCKEVERYTHING = 0x00008000, // [RH] Line blocks everything
ML_ZONEBOUNDARY = 0x00010000,
ML_RAILING = 0x00020000,
ML_BLOCK_FLOATERS = 0x00040000,
ML_CLIP_MIDTEX = 0x00080000, // Automatic for every Strife line
ML_WRAP_MIDTEX = 0x00100000,
ML_3DMIDTEX = 0x00200000,
ML_CHECKSWITCHRANGE = 0x00400000,
ML_FIRSTSIDEONLY = 0x00800000, // activated only when crossed from front side
ML_BLOCKPROJECTILE = 0x01000000,
ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line
ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight
ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks
ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING
};
native readonly vertex v1, v2; // vertices, from v1 to v2
native readonly Vector2 delta; // precalculated v2 - v1 for side checking
native uint flags;
native uint activation; // activation type
native int special;
native int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width)
native double alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque)
//native Side sidedef[2];
native readonly double bbox[4]; // bounding box, for the extent of the LineDef.
native readonly Sector frontsector, backsector;
native int validcount; // if == validcount, already checked
native int locknumber; // [Dusk] lock number for special
native readonly uint portalindex;
native readonly uint portaltransferred;
native bool isLinePortal();
native bool isVisualPortal();
native Line getPortalDestination();
native int getPortalAlignment();
native int Index();
}
struct SecPlane native
{
native Vector3 Normal;
native double D;
native double negiC;
native bool isSlope();
native int PointOnSide(Vector3 pos);
native double ZatPoint (Vector2 v);
native double ZatPointDist(Vector2 v, double dist);
native bool isEqual(Secplane other);
native void ChangeHeight(double hdiff);
native double GetChangedHeight(double hdiff);
native double HeightDiff(double oldd, double newd = 0.0);
native double PointToDist(Vector2 xy, double z);
}
// This encapsulates all info Doom's original 'special' field contained - for saving and transferring.
struct SecSpecial
{
Name damagetype;
int damageamount;
short special;
short damageinterval;
short leakydamage;
int Flags;
}
struct Sector native
{
//secplane_t floorplane, ceilingplane; // defined internally
//FDynamicColormap *ColorMap;
native Actor SoundTarget;
native int16 special;
native int16 lightlevel;
native int16 seqType;
native int sky;
native Name SeqName;
native readonly Vector2 centerspot;
native int validcount;
native Actor thinglist;
native double friction, movefactor;
native int terrainnum[2];
// thinker_t for reversable actions
//SectorEffect floordata;
//SectorEffect ceilingdata;
//SectorEffect lightingdata;
enum EPlane
{
floor,
ceiling
};
enum EInterpolationType
{
CeilingMove,
FloorMove,
CeilingScroll,
FloorScroll
};
//Interpolation interpolations[4];
native uint8 soundtraversed;
native int8 stairlock;
native int prevsec;
native int nextsec;
//TStaticPointedArray<line_t *> Lines; // this is defined internally to avoid exposing some overly complicated type to the parser
native readonly Sector heightsec;
native uint bottommap, midmap, topmap;
//struct msecnode_t *touching_thinglist;
//struct msecnode_t *sectorportal_thinglist;
native double gravity;
native Name damagetype;
native int damageamount;
native int16 damageinterval;
native int16 leakydamage;
native uint16 ZoneNumber;
enum ESectorMoreFlags
{
SECMF_FAKEFLOORONLY = 2, // when used as heightsec in R_FakeFlat, only copies floor
SECMF_CLIPFAKEPLANES = 4, // as a heightsec, clip planes to target sector's planes
SECMF_NOFAKELIGHT = 8, // heightsec does not change lighting
SECMF_IGNOREHEIGHTSEC= 16, // heightsec is only for triggering sector actions
SECMF_UNDERWATER = 32, // sector is underwater
SECMF_FORCEDUNDERWATER= 64, // sector is forced to be underwater
SECMF_UNDERWATERMASK = 32+64,
SECMF_DRAWN = 128, // sector has been drawn at least once
SECMF_HIDDEN = 256, // Do not draw on textured automap
}
native uint16 MoreFlags;
enum ESectorFlags
{
SECF_SILENT = 1, // actors in sector make no noise
SECF_NOFALLINGDAMAGE= 2, // No falling damage in this sector
SECF_FLOORDROP = 4, // all actors standing on this floor will remain on it when it lowers very fast.
SECF_NORESPAWN = 8, // players can not respawn in this sector
SECF_FRICTION = 16, // sector has friction enabled
SECF_PUSH = 32, // pushers enabled
SECF_SILENTMOVE = 64, // Sector movement makes mo sound (Eternity got this so this may be useful for an extended cross-port standard.)
SECF_DMGTERRAINFX = 128, // spawns terrain splash when inflicting damage
SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode
SECF_ENDLEVEL = 512, // ends level when health goes below 10
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling.
SECF_WASSECRET = 1 << 30, // a secret that was discovered
SECF_SECRET = 1 << 31, // a secret sector
SECF_DAMAGEFLAGS = SECF_ENDGODMODE|SECF_ENDLEVEL|SECF_DMGTERRAINFX|SECF_HAZARD,
SECF_NOMODIFY = SECF_SECRET|SECF_WASSECRET, // not modifiable by Sector_ChangeFlags
SECF_SPECIALFLAGS = SECF_DAMAGEFLAGS|SECF_FRICTION|SECF_PUSH, // these flags originate from 'special and must be transferrable by floor thinkers
}
enum EMoveResult
{
MOVE_OK,
MOVE_CRUSHED,
MOVE_PASTDEST
};
native uint Flags;
native SectorAction SecActTarget;
native readonly uint Portals[2];
native readonly int PortalGroup;
native readonly int sectornum;
native int Index();
native double, Sector, F3DFloor NextHighestCeilingAt(double x, double y, double bottomz, double topz, int flags = 0);
native double, Sector, F3DFloor NextLowestFloorAt(double x, double y, double z, int flags = 0, double steph = 0);
native void RemoveForceField();
native static Sector PointInSector(Vector2 pt);
native void SetColor(color c, int desat = 0);
native void SetFade(color c);
native bool PlaneMoving(int pos);
native int GetFloorLight();
native int GetCeilingLight();
native Sector GetHeightSec();
native void TransferSpecial(Sector model);
native void GetSpecial(out SecSpecial spec);
native void SetSpecial( SecSpecial spec);
native int GetTerrain(int pos);
native void CheckPortalPlane(int plane);
native double, Sector HighestCeilingAt(Vector2 a);
native double, Sector LowestFloorAt(Vector2 a);
native double, double GetFriction(int plane);
native void SetXOffset(int pos, double o);
native void AddXOffset(int pos, double o);
native double GetXOffset(int pos);
native void SetYOffset(int pos, double o);
native void AddYOffset(int pos, double o);
native double GetYOffset(int pos, bool addbase = true);
native void SetXScale(int pos, double o);
native double GetXScale(int pos);
native void SetYScale(int pos, double o);
native double GetYScale(int pos);
native void SetAngle(int pos, double o);
native double GetAngle(int pos, bool addbase = true);
native void SetBase(int pos, double y, double o);
native void SetAlpha(int pos, double o);
native double GetAlpha(int pos);
native int GetFlags(int pos);
native int GetVisFlags(int pos);
native void ChangeFlags(int pos, int And, int Or);
native int GetPlaneLight(int pos);
native void SetPlaneLight(int pos, int level);
native TextureID GetTexture(int pos);
native void SetTexture(int pos, TextureID tex, bool floorclip = true);
native double GetPlaneTexZ(int pos);
native void SetPlaneTexZ(int pos, double val, bool dirtify = false); // This mainly gets used by init code. The only place where it must set the vertex to dirty is the interpolation code.
native void ChangeLightLevel(int newval);
native void SetLightLevel(int newval);
native int GetLightLevel();
native void AdjustFloorClip();
native bool IsLinked(Sector other, bool ceiling);
native bool PortalBlocksView(int plane);
native bool PortalBlocksSight(int plane);
native bool PortalBlocksMovement(int plane);
native bool PortalBlocksSound(int plane);
native bool PortalIsLinked(int plane);
native void ClearPortal(int plane);
native double GetPortalPlaneZ(int plane);
native Vector2 GetPortalDisplacement(int plane);
native int GetPortalType(int plane);
native int GetOppositePortalGroup(int plane);
native double CenterFloor();
native double CenterCeiling();
native bool TriggerSectorActions(Actor thing, int activation);
native int MoveFloor(double speed, double dest, int crush, int direction, bool hexencrush, bool instant = false);
native int MoveCeiling(double speed, double dest, int crush, int direction, bool hexencrush);
native Sector NextSpecialSector(int type, Sector prev);
native double, Vertex FindLowestFloorSurrounding();
native double, Vertex FindHighestFloorSurrounding();
native double, Vertex FindNextHighestFloor();
native double, Vertex FindNextLowestFloor();
native double, Vertex FindLowestCeilingSurrounding();
native double, Vertex FindHighestCeilingSurrounding();
native double, Vertex FindNextLowestCeiling();
native double, Vertex FindNextHighestCeiling();
native double FindShortestTextureAround();
native double FindShortestUpperAround();
native Sector FindModelFloorSector(double floordestheight);
native Sector FindModelCeilingSector(double floordestheight);
native int FindMinSurroundingLight(int max);
native double, Vertex FindLowestCeilingPoint();
native double, Vertex FindHighestFloorPoint();
bool isSecret()
{
return !!(Flags & SECF_SECRET);
}
bool wasSecret()
{
return !!(Flags & SECF_WASSECRET);
}
void ClearSecret()
{
Flags &= ~SECF_SECRET;
}
}
struct Wads
{
enum WadNamespace

View file

@ -0,0 +1,311 @@
/*
** a_ammo.cpp
** Implements ammo and backpack items.
**
**---------------------------------------------------------------------------
** Copyright 2000-2016 Randy Heit
** Copyright 2006-2017 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class Ammo : Inventory native
{
native int BackpackAmount;
native int BackpackMaxAmount;
Default
{
+INVENTORY.KEEPDEPLETED
Inventory.PickupSound "misc/ammo_pkup";
}
native Class<Actor> GetParentAmmo ();
//===========================================================================
//
// AAmmo :: HandlePickup
//
//===========================================================================
override bool HandlePickup (Inventory item)
{
let ammoitem = Ammo(item);
if (ammoitem != null && ammoitem.GetParentAmmo() == GetClass())
{
if (Amount < MaxAmount || sv_unlimited_pickup)
{
int receiving = item.Amount;
if (!item.bIgnoreSkill)
{ // extra ammo in baby mode and nightmare mode
receiving = int(receiving * G_SkillPropertyFloat(SKILLP_AmmoFactor));
}
int oldamount = Amount;
if (Amount > 0 && Amount + receiving < 0)
{
Amount = 0x7fffffff;
}
else
{
Amount += receiving;
}
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item.bPickupGood = true;
// If the player previously had this ammo but ran out, possibly switch
// to a weapon that uses it, but only if the player doesn't already
// have a weapon pending.
if (oldamount == 0 && Owner != null && Owner.player != null)
{
PlayerPawn(Owner).CheckWeaponSwitch(GetClass());
}
}
return true;
}
return false;
}
//===========================================================================
//
// AAmmo :: CreateCopy
//
//===========================================================================
override Inventory CreateCopy (Actor other)
{
Inventory copy;
int amount = Amount;
// extra ammo in baby mode and nightmare mode
if (!bIgnoreSkill)
{
amount = int(amount * G_SkillPropertyFloat(SKILLP_AmmoFactor));
}
let type = GetParentAmmo();
if (GetClass() == type)
{
if (!GoAway ())
{
Destroy ();
}
copy = Inventory(Spawn (type));
copy.Amount = amount;
copy.BecomeItem ();
}
else
{
copy = Super.CreateCopy (other);
copy.Amount = amount;
}
if (copy.Amount > copy.MaxAmount)
{ // Don't pick up more ammo than you're supposed to be able to carry.
copy.Amount = copy.MaxAmount;
}
return copy;
}
//===========================================================================
//
// AAmmo :: CreateTossable
//
//===========================================================================
override Inventory CreateTossable()
{
Inventory copy = Super.CreateTossable();
if (copy != null)
{ // Do not increase ammo by dropping it and picking it back up at
// certain skill levels.
copy.bIgnoreSkill = true;
}
return copy;
}
}
class BackpackItem : Inventory
{
bool bDepleted;
//===========================================================================
//
// ABackpackItem :: CreateCopy
//
// A backpack is being added to a player who doesn't yet have one. Give them
// every kind of ammo, and increase their max amounts.
//
//===========================================================================
override Inventory CreateCopy (Actor other)
{
// Find every unique type of ammoitem. Give it to the player if
// he doesn't have it already, and double its maximum capacity.
uint end = AllActorClasses.Size();
for (uint i = 0; i < end; ++i)
{
let type = AllActorClasses[i];
if (type.GetParentClass() == 'Ammo')
{
let ammotype = (class<Ammo>)(type);
let ammoitem = Ammo(other.FindInventory(ammotype));
int amount = GetDefaultByType(ammotype).BackpackAmount;
// extra ammo in baby mode and nightmare mode
if (!bIgnoreSkill)
{
amount = int(amount * G_SkillPropertyFloat(SKILLP_AmmoFactor));
}
if (amount < 0) amount = 0;
if (ammoitem == NULL)
{ // The player did not have the ammoitem. Add it.
ammoitem = Ammo(Spawn(ammotype));
ammoitem.Amount = bDepleted ? 0 : amount;
if (ammoitem.BackpackMaxAmount > ammoitem.MaxAmount)
{
ammoitem.MaxAmount = ammoitem.BackpackMaxAmount;
}
if (ammoitem.Amount > ammoitem.MaxAmount)
{
ammoitem.Amount = ammoitem.MaxAmount;
}
ammoitem.AttachToOwner (other);
}
else
{ // The player had the ammoitem. Give some more.
if (ammoitem.MaxAmount < ammoitem.BackpackMaxAmount)
{
ammoitem.MaxAmount = ammoitem.BackpackMaxAmount;
}
if (!bDepleted && ammoitem.Amount < ammoitem.MaxAmount)
{
ammoitem.Amount += amount;
if (ammoitem.Amount > ammoitem.MaxAmount)
{
ammoitem.Amount = ammoitem.MaxAmount;
}
}
}
}
}
return Super.CreateCopy (other);
}
//===========================================================================
//
// ABackpackItem :: HandlePickup
//
// When the player picks up another backpack, just give them more ammoitem.
//
//===========================================================================
override bool HandlePickup (Inventory item)
{
// Since you already have a backpack, that means you already have every
// kind of ammo in your inventory, so we don't need to look at the
// entire PClass list to discover what kinds of ammo exist, and we don't
// have to alter the MaxAmount either.
if (item is 'BackpackItem')
{
for (let probe = Owner.Inv; probe != NULL; probe = probe.Inv)
{
if (probe.GetParentClass() == 'Ammo')
{
if (probe.Amount < probe.MaxAmount || sv_unlimited_pickup)
{
int amount = Ammo(probe).Default.BackpackAmount;
// extra ammo in baby mode and nightmare mode
if (!bIgnoreSkill)
{
amount = int(amount * G_SkillPropertyFloat(SKILLP_AmmoFactor));
}
probe.Amount += amount;
if (probe.Amount > probe.MaxAmount && !sv_unlimited_pickup)
{
probe.Amount = probe.MaxAmount;
}
}
}
}
// The pickup always succeeds, even if you didn't get anything
item.bPickupGood = true;
return true;
}
return false;
}
//===========================================================================
//
// ABackpackItem :: CreateTossable
//
// The tossed backpack must not give out any more ammo, otherwise a player
// could cheat by dropping their backpack and picking it up for more ammoitem.
//
//===========================================================================
override Inventory CreateTossable ()
{
let pack = BackpackItem(Super.CreateTossable());
if (pack != NULL)
{
pack.bDepleted = true;
}
return pack;
}
//===========================================================================
//
// ABackpackItem :: DetachFromOwner
//
//===========================================================================
override void DetachFromOwner ()
{
// When removing a backpack, drop the player's ammo maximums to normal
for (let item = Owner.Inv; item != NULL; item = item.Inv)
{
if (item is 'Ammo' && item.MaxAmount == Ammo(item).BackpackMaxAmount)
{
item.MaxAmount = item.Default.MaxAmount;
if (item.Amount > item.MaxAmount)
{
item.Amount = item.MaxAmount;
}
}
}
}
}

View file

@ -0,0 +1,162 @@
/*
** a_health.cpp
** All health items
**
**---------------------------------------------------------------------------
** Copyright 2000-2016 Randy Heit
** Copyright 2006-2017 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class Health : Inventory
{
transient int PrevHealth;
/*meta*/ int LowHealth;
/*meta*/ String LowHealthMessage;
property LowMessage: LowHealth, LowHealthMessage;
Default
{
Inventory.Amount 1;
Inventory.MaxAmount 0;
Inventory.PickupSound "misc/health_pkup";
}
//===========================================================================
//
// AHealth :: PickupMessage
//
//===========================================================================
override String PickupMessage ()
{
if (PrevHealth < LowHealth)
{
String message = LowHealthMessage;
if (message.Length() != 0)
{
return message;
}
}
return Super.PickupMessage();
}
//===========================================================================
//
// TryPickup
//
//===========================================================================
override bool TryPickup (in out Actor other)
{
PrevHealth = other.player != NULL ? other.player.health : other.health;
// P_GiveBody adds one new feature, applied only if it is possible to pick up negative health:
// Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount.
if (other.GiveBody(Amount, MaxAmount))
{
GoAwayAndDie();
return true;
}
return false;
}
}
class HealthPickup : Inventory
{
int autousemode;
property AutoUse: autousemode;
Default
{
Inventory.DefMaxAmount;
+INVENTORY.INVBAR
}
//===========================================================================
//
// CreateCopy
//
//===========================================================================
override Inventory CreateCopy (Actor other)
{
Inventory copy = Super.CreateCopy (other);
copy.health = health;
return copy;
}
//===========================================================================
//
// CreateTossable
//
//===========================================================================
override Inventory CreateTossable ()
{
Inventory copy = Super.CreateTossable ();
if (copy != NULL)
{
copy.health = health;
}
return copy;
}
//===========================================================================
//
// HandlePickup
//
//===========================================================================
override bool HandlePickup (Inventory item)
{
// HealthPickups that are the same type but have different health amounts
// do not count as the same item.
if (item.health == health)
{
return Super.HandlePickup (item);
}
return false;
}
//===========================================================================
//
// Use
//
//===========================================================================
override bool Use (bool pickup)
{
return Owner.GiveBody (health, 0);
}
}

View file

@ -0,0 +1,141 @@
//===========================================================================
//
//
//
//===========================================================================
class ScoreItem : Inventory
{
Default
{
Height 10;
+COUNTITEM
Inventory.Amount 1;
+Inventory.ALWAYSPICKUP
}
override bool TryPickup (in out Actor toucher)
{
toucher.Score += Amount;
GoAwayAndDie();
return true;
}
}
//===========================================================================
//
//
//
//===========================================================================
class Key : Inventory native
{
native uint8 KeyNumber;
Default
{
+DONTGIB; // Don't disappear due to a crusher
Inventory.InterHubAmount 0;
Inventory.PickupSound "misc/k_pkup";
}
override bool HandlePickup (Inventory item)
{
// In single player, you can pick up an infinite number of keys
// even though you can only hold one of each.
if (multiplayer)
{
return Super.HandlePickup (item);
}
if (GetClass() == item.GetClass())
{
item.bPickupGood = true;
return true;
}
return false;
}
override bool ShouldStay ()
{
return !!multiplayer;
}
}
//===========================================================================
//
// AMapRevealer
//
// A MapRevealer reveals the whole map for the player who picks it up.
// The MapRevealer doesn't actually go in your inventory. Instead, it sets
// a flag on the level.
//
//===========================================================================
class MapRevealer : Inventory
{
override bool TryPickup (in out Actor toucher)
{
level.allmap = true;
GoAwayAndDie ();
return true;
}
}
//===========================================================================
//
//
//
//===========================================================================
class PuzzleItem : Inventory
{
/*meta*/ int PuzzleItemNumber;
/*meta*/ String PuzzFailMessage;
property Number: PuzzleItemNumber;
property FailMessage: PuzzFailMessage;
Default
{
+NOGRAVITY
+INVENTORY.INVBAR
Inventory.DefMaxAmount;
Inventory.UseSound "PuzzleSuccess";
Inventory.PickupSound "misc/i_pkup";
PuzzleItem.FailMessage("$TXT_USEPUZZLEFAILED");
}
override bool HandlePickup (Inventory item)
{
// Can't carry more than 1 of each puzzle item in coop netplay
if (multiplayer && !deathmatch && item.GetClass() == GetClass())
{
return true;
}
return Super.HandlePickup (item);
}
override bool Use (bool pickup)
{
if (Owner == NULL) return false;
if (Owner.UsePuzzleItem (PuzzleItemNumber))
{
return true;
}
// [RH] Always play the sound if the use fails.
Owner.A_PlaySound ("*puzzfail", CHAN_VOICE);
if (Owner.CheckLocalView (consoleplayer))
{
C_MidPrint ("SmallFont", PuzzFailMessage, true);
}
return false;
}
override bool ShouldStay ()
{
return !!multiplayer;
}
}

View file

@ -1,5 +1,6 @@
class Inventory : Actor native
{
const BLINKTHRESHOLD = (4*32);
native Actor Owner; // Who owns this item? NULL if it's still a pickup.
native int Amount; // Amount of item this instance has
@ -37,11 +38,25 @@ class Inventory : Actor native
virtual native void PlayPickupSound(Actor user);
virtual native void AttachToOwner(Actor user);
virtual native void DetachFromOwner();
virtual native bool DrawPowerup(int x, int y);
//===========================================================================
//
// AInventory :: Travelled
//
// Called when an item in somebody's inventory is carried over to another
// map, in case it needs to do special reinitialization.
//
//===========================================================================
virtual void Travelled()
{}
virtual double GetSpeedFactor() { return 1; }
virtual bool GetNoTeleportFreeze() { return false; }
virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {}
native bool GoAway();
native void GoAwayAndDie();
native void BecomeItem();
native void BecomePickup();

View file

@ -104,26 +104,253 @@ class PowerMask : PowerIronFeet native
}
}
class PowerLightAmp : Powerup native
//===========================================================================
//
// LightAmp
//
//===========================================================================
class PowerLightAmp : Powerup
{
Default
{
Powerup.Duration -120;
}
//===========================================================================
//
// APowerLightAmp :: DoEffect
//
//===========================================================================
override void DoEffect ()
{
Super.DoEffect ();
let player = Owner.player;
if (player != NULL && player.fixedcolormap < PlayerInfo.NUMCOLORMAPS)
{
if (!isBlinking())
{
player.fixedlightlevel = 1;
}
else
{
player.fixedlightlevel = -1;
}
}
}
//===========================================================================
//
// APowerLightAmp :: EndEffect
//
//===========================================================================
override void EndEffect ()
{
Super.EndEffect();
if (Owner != NULL && Owner.player != NULL && Owner.player.fixedcolormap < PlayerInfo.NUMCOLORMAPS)
{
Owner.player.fixedlightlevel = -1;
}
}
}
class PowerTorch : PowerLightAmp native {}
//===========================================================================
//
// Torch
//
//===========================================================================
class PowerFlight : Powerup native
class PowerTorch : PowerLightAmp
{
int NewTorch, NewTorchDelta;
override void DoEffect ()
{
if (Owner == NULL || Owner.player == NULL)
{
return;
}
let player = Owner.player;
if (EffectTics <= BLINKTHRESHOLD || player.fixedcolormap >= PlayerInfo.NUMCOLORMAPS)
{
Super.DoEffect ();
}
else
{
Powerup.DoEffect ();
if (!(level.time & 16) && Owner.player != NULL)
{
if (NewTorch != 0)
{
if (player.fixedlightlevel + NewTorchDelta > 7
|| player.fixedlightlevel + NewTorchDelta < 0
|| NewTorch == player.fixedlightlevel)
{
NewTorch = 0;
}
else
{
player.fixedlightlevel += NewTorchDelta;
}
}
else
{
NewTorch = (random[torch]() & 7) + 1;
NewTorchDelta = (NewTorch == Owner.player.fixedlightlevel) ?
0 : ((NewTorch > player.fixedlightlevel) ? 1 : -1);
}
}
}
}
}
//===========================================================================
//
// Flight
//
//===========================================================================
class PowerFlight : Powerup
{
Default
{
Powerup.Duration -60;
+INVENTORY.HUBPOWER
}
bool HitCenterFrame;
//===========================================================================
//
// APowerFlight :: InitEffect
//
//===========================================================================
override void InitEffect ()
{
Super.InitEffect();
Owner.bFly = true;
Owner.bNoGravity = true;
if (Owner.pos.Z <= Owner.floorz)
{
Owner.Vel.Z = 4;; // thrust the player in the air a bit
}
if (Owner.Vel.Z <= -35)
{ // stop falling scream
Owner.A_StopSound (CHAN_VOICE);
}
}
//===========================================================================
//
// APowerFlight :: DoEffect
//
//===========================================================================
override void Tick ()
{
// The Wings of Wrath only expire in multiplayer and non-hub games
if (!multiplayer && level.infinite_flight)
{
EffectTics++;
}
Super.Tick ();
}
//===========================================================================
//
// APowerFlight :: EndEffect
//
//===========================================================================
override void EndEffect ()
{
Super.EndEffect();
if (Owner == NULL || Owner.player == NULL)
{
return;
}
if (!(Owner.bFlyCheat))
{
if (Owner.pos.Z != Owner.floorz)
{
Owner.player.centering = true;
}
Owner.bFly = false;
Owner.bNoGravity = false;
}
}
//===========================================================================
//
// APowerFlight :: DrawPowerup
//
//===========================================================================
override bool DrawPowerup (int x, int y)
{
// If this item got a valid icon use that instead of the default spinning wings.
if (Icon.isValid())
{
return Super.DrawPowerup(x, y);
}
if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16))
{
TextureID picnum = TexMan.CheckForTexture ("SPFLY0", TexMan.Type_MiscPatch);
int frame = (level.time/3) & 15;
if (!picnum.isValid())
{
return false;
}
if (Owner.bNoGravity)
{
if (HitCenterFrame && (frame != 15 && frame != 0))
{
screen.DrawHUDTexture (picnum + 15, x, y);
}
else
{
screen.DrawHUDTexture (picnum + frame, x, y);
HitCenterFrame = false;
}
}
else
{
if (!HitCenterFrame && (frame != 15 && frame != 0))
{
screen.DrawHUDTexture (picnum + frame, x, y);
HitCenterFrame = false;
}
else
{
screen.DrawHUDTexture (picnum+15, x, y);
HitCenterFrame = true;
}
}
}
return true;
}
}
class PowerWeaponLevel2 : Powerup native
//===========================================================================
//
// WeaponLevel2
//
//===========================================================================
class PowerWeaponLevel2 : Powerup
{
Default
{
@ -131,11 +358,100 @@ class PowerWeaponLevel2 : Powerup native
Inventory.Icon "SPINBK0";
+INVENTORY.NOTELEPORTFREEZE
}
//===========================================================================
//
// APowerWeaponLevel2 :: InitEffect
//
//===========================================================================
override void InitEffect ()
{
Super.InitEffect();
let player = Owner.player;
if (player == null)
return;
let weap = player.ReadyWeapon;
if (weap == null)
return;
let sister = weap.SisterWeapon;
if (sister == null)
return;
if (!sister.bPowered_Up)
return;
let ready = sister.GetReadyState();
if (weap.GetReadyState() != ready)
{
player.ReadyWeapon = sister;
player.SetPsprite(PSP_WEAPON, ready);
}
else
{
PSprite psp = player.FindPSprite(PSprite.WEAPON);
if (psp != null && psp.Caller == player.ReadyWeapon)
{
// If the weapon changes but the state does not, we have to manually change the PSprite's caller here.
psp.Caller = sister;
player.ReadyWeapon = sister;
}
else
{
// Something went wrong. Initiate a regular weapon change.
player.PendingWeapon = sister;
}
}
}
//===========================================================================
//
// APowerWeaponLevel2 :: EndEffect
//
//===========================================================================
override void EndEffect ()
{
Super.EndEffect();
if (Owner == null) return;
let player = Owner.player;
if (player != NULL)
{
if (player.ReadyWeapon != NULL && player.ReadyWeapon.bPowered_Up)
{
player.ReadyWeapon.EndPowerup ();
}
if (player.PendingWeapon != NULL && player.PendingWeapon != WP_NOCHANGE &&
player.PendingWeapon.bPowered_Up &&
player.PendingWeapon.SisterWeapon != NULL)
{
player.PendingWeapon = player.PendingWeapon.SisterWeapon;
}
}
}
}
//===========================================================================
//
// Speed
//
//===========================================================================
class PowerSpeed : Powerup native
{
native int SpeedFlags;
const PSF_NOTRAIL = 1;
Default
{
@ -145,7 +461,67 @@ class PowerSpeed : Powerup native
+INVENTORY.NOTELEPORTFREEZE
}
override double GetSpeedFactor() { return Speed; }
override double GetSpeedFactor()
{
return Speed;
}
//===========================================================================
//
// APowerSpeed :: DoEffect
//
//===========================================================================
override void DoEffect ()
{
Super.DoEffect ();
if (Owner == NULL || Owner.player == NULL)
return;
if (Owner.player.cheats & CF_PREDICTING)
return;
if (SpeedFlags & PSF_NOTRAIL)
return;
if (level.time & 1)
return;
// Check if another speed item is present to avoid multiple drawing of the speed trail.
// Only the last PowerSpeed without PSF_NOTRAIL set will actually draw the trail.
for (Inventory item = Inv; item != NULL; item = item.Inv)
{
let sitem = PowerSpeed(item);
if (sitem != null && !(sitem.SpeedFlags & PSF_NOTRAIL))
{
return;
}
}
if (Owner.Vel.Length() <= 12)
return;
Actor speedMo = Spawn("PlayerSpeedTrail", Owner.Pos, NO_REPLACE);
if (speedMo)
{
speedMo.Angle = Owner.Angle;
speedMo.Translation = Owner.Translation;
speedMo.target = Owner;
speedMo.sprite = Owner.sprite;
speedMo.frame = Owner.frame;
speedMo.Floorclip = Owner.Floorclip;
// [BC] Also get the scale from the owner.
speedMo.Scale = Owner.Scale;
if (Owner == players[consoleplayer].camera &&
!(Owner.player.cheats & CF_CHASECAM))
{
speedMo.bInvisible = true;
}
}
}
}
// Player Speed Trail (used by the Speed Powerup) ----------------------------
@ -170,6 +546,12 @@ class PlayerSpeedTrail : Actor
}
}
//===========================================================================
//
// Minotaur
//
//===========================================================================
class PowerMinotaur : Powerup
{
Default
@ -179,7 +561,13 @@ class PowerMinotaur : Powerup
}
}
class PowerTargeter : Powerup native
//===========================================================================
//
// Targeter
//
//===========================================================================
class PowerTargeter : Powerup
{
Default
{
@ -196,6 +584,119 @@ class PowerTargeter : Powerup native
TRGT C -1;
Stop;
}
override void Travelled ()
{
InitEffect ();
}
override void InitEffect ()
{
// Why is this called when the inventory isn't even attached yet
// in APowerup.CreateCopy?
if (!Owner.FindInventory(GetClass(), true))
return;
let player = Owner.player;
Super.InitEffect();
if (player == null)
return;
let stat = FindState("Targeter");
if (stat != null)
{
player.SetPsprite(PSprite.TARGETCENTER, stat);
player.SetPsprite(PSprite.TARGETLEFT, stat + 1);
player.SetPsprite(PSprite.TARGETRIGHT, stat + 2);
}
player.GetPSprite(PSprite.TARGETCENTER).x = (160-3);
player.GetPSprite(PSprite.TARGETCENTER).y =
player.GetPSprite(PSprite.TARGETLEFT).y =
player.GetPSprite(PSprite.TARGETRIGHT).y = (100-3);
PositionAccuracy ();
}
override void AttachToOwner(Actor other)
{
Super.AttachToOwner(other);
// Let's actually properly call this for the targeters.
InitEffect();
}
override bool HandlePickup(Inventory item)
{
if (Super.HandlePickup(item))
{
InitEffect(); // reset the HUD sprites
return true;
}
return false;
}
override void DoEffect ()
{
Super.DoEffect ();
if (Owner != null && Owner.player != null)
{
let player = Owner.player;
PositionAccuracy ();
if (EffectTics < 5*TICRATE)
{
let stat = FindState("Targeter");
if (stat != null)
{
if (EffectTics & 32)
{
player.SetPsprite(PSprite.TARGETRIGHT, null);
player.SetPsprite(PSprite.TARGETLEFT, stat + 1);
}
else if (EffectTics & 16)
{
player.SetPsprite(PSprite.TARGETRIGHT, stat + 2);
player.SetPsprite(PSprite.TARGETLEFT, null);
}
}
}
}
}
override void EndEffect ()
{
Super.EndEffect();
if (Owner != null && Owner.player != null)
{
// Calling GetPSprite here could crash if we're creating a new game.
// This is because P_SetupLevel nulls the player's mo before destroying
// every DThinker which in turn ends up calling this.
// However P_SetupLevel is only called after G_NewInit which calls
// every player's dtor which destroys all their psprites.
let player = Owner.player;
PSprite pspr;
if ((pspr = player.FindPSprite(PSprite.TARGETCENTER)) != null) pspr.SetState(null);
if ((pspr = player.FindPSprite(PSprite.TARGETLEFT)) != null) pspr.SetState(null);
if ((pspr = player.FindPSprite(PSprite.TARGETRIGHT)) != null) pspr.SetState(null);
}
}
private void PositionAccuracy ()
{
let player = Owner.player;
if (player != null)
{
player.GetPSprite(PSprite.TARGETLEFT).x = (160-3) - ((100 - player.mo.accuracy));
player.GetPSprite(PSprite.TARGETRIGHT).x = (160-3)+ ((100 - player.mo.accuracy));
}
}
}
//===========================================================================

View file

@ -125,20 +125,3 @@ class WeaponPiece : Inventory native
}
}
class Ammo : Inventory native
{
native int BackpackAmount;
native int BackpackMaxAmount;
Default
{
+INVENTORY.KEEPDEPLETED
Inventory.PickupSound "misc/ammo_pkup";
}
}
class BackpackItem : Inventory native
{
native bool bDepleted;
}

View file

@ -0,0 +1,401 @@
struct SectorPortal native
{
enum EType
{
TYPE_SKYVIEWPOINT = 0, // a regular skybox
TYPE_STACKEDSECTORTHING, // stacked sectors with the thing method
TYPE_PORTAL, // stacked sectors with Sector_SetPortal
TYPE_LINKEDPORTAL, // linked portal (interactive)
TYPE_PLANE, // EE-style plane portal (not implemented in SW renderer)
TYPE_HORIZON, // EE-style horizon portal (not implemented in SW renderer)
};
enum EFlags
{
FLAG_SKYFLATONLY = 1, // portal is only active on skyflatnum
FLAG_INSKYBOX = 2, // to avoid recursion
};
native int mType;
native int mFlags;
native uint mPartner;
native int mPlane;
native Sector mOrigin;
native Sector mDestination;
native Vector2 mDisplacement;
native double mPlaneZ;
native Actor mSkybox;
native static uint GetSkyboxPortal(Actor actor);
};
struct Vertex native
{
native readonly Vector2 p;
}
struct Side native
{
enum ETexpart
{
top=0,
mid=1,
bottom=2
};
enum EWallFlags
{
WALLF_ABSLIGHTING = 1, // Light is absolute instead of relative
WALLF_NOAUTODECALS = 2, // Do not attach impact decals to this wall
WALLF_NOFAKECONTRAST = 4, // Don't do fake contrast for this wall in side_t::GetLightLevel
WALLF_SMOOTHLIGHTING = 8, // Similar to autocontrast but applies to all angles.
WALLF_CLIP_MIDTEX = 16, // Like the line counterpart, but only for this side.
WALLF_WRAP_MIDTEX = 32, // Like the line counterpart, but only for this side.
WALLF_POLYOBJ = 64, // This wall belongs to a polyobject.
WALLF_LIGHT_FOG = 128, // This wall's Light is used even in fog.
};
native Sector sector; // Sector the SideDef is facing.
//DBaseDecal* AttachedDecals; // [RH] Decals bound to the wall
native Line linedef;
native int16 Light;
native uint8 Flags;
native TextureID GetTexture(int which);
native void SetTexture(int which, TextureID tex);
native void SetTextureXOffset(int which, double offset);
native double GetTextureXOffset(int which);
native void AddTextureXOffset(int which, double delta);
native void SetTextureYOffset(int which, double offset);
native double GetTextureYOffset(int which);
native void AddTextureYOffset(int which, double delta);
native void SetTextureXScale(int which, double scale);
native double GetTextureXScale(int which);
native void MultiplyTextureXScale(int which, double delta);
native void SetTextureYScale(int which, double scale);
native double GetTextureYScale(int which);
native void MultiplyTextureYScale(int which, double delta);
//native DInterpolation *SetInterpolation(int position);
//native void StopInterpolation(int position);
native Vertex V1();
native Vertex V2();
native int Index();
};
struct Line native
{
enum ELineFlags
{
ML_BLOCKING =0x00000001, // solid, is an obstacle
ML_BLOCKMONSTERS =0x00000002, // blocks monsters only
ML_TWOSIDED =0x00000004, // backside will not be present at all if not two sided
ML_DONTPEGTOP = 0x00000008, // upper texture unpegged
ML_DONTPEGBOTTOM = 0x00000010, // lower texture unpegged
ML_SECRET = 0x00000020, // don't map as two sided: IT'S A SECRET!
ML_SOUNDBLOCK = 0x00000040, // don't let sound cross two of these
ML_DONTDRAW = 0x00000080, // don't draw on the automap
ML_MAPPED = 0x00000100, // set if already drawn in automap
ML_REPEAT_SPECIAL = 0x00000200, // special is repeatable
ML_ADDTRANS = 0x00000400, // additive translucency (can only be set internally)
// Extended flags
ML_MONSTERSCANACTIVATE = 0x00002000, // [RH] Monsters (as well as players) can activate the line
ML_BLOCK_PLAYERS = 0x00004000,
ML_BLOCKEVERYTHING = 0x00008000, // [RH] Line blocks everything
ML_ZONEBOUNDARY = 0x00010000,
ML_RAILING = 0x00020000,
ML_BLOCK_FLOATERS = 0x00040000,
ML_CLIP_MIDTEX = 0x00080000, // Automatic for every Strife line
ML_WRAP_MIDTEX = 0x00100000,
ML_3DMIDTEX = 0x00200000,
ML_CHECKSWITCHRANGE = 0x00400000,
ML_FIRSTSIDEONLY = 0x00800000, // activated only when crossed from front side
ML_BLOCKPROJECTILE = 0x01000000,
ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line
ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight
ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks
ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING
};
native readonly vertex v1, v2; // vertices, from v1 to v2
native readonly Vector2 delta; // precalculated v2 - v1 for side checking
native uint flags;
native uint activation; // activation type
native int special;
native int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width)
native double alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque)
native Side sidedef[2];
native readonly double bbox[4]; // bounding box, for the extent of the LineDef.
native readonly Sector frontsector, backsector;
native int validcount; // if == validcount, already checked
native int locknumber; // [Dusk] lock number for special
native readonly uint portalindex;
native readonly uint portaltransferred;
native bool isLinePortal();
native bool isVisualPortal();
native Line getPortalDestination();
native int getPortalAlignment();
native int Index();
}
struct SecPlane native
{
native Vector3 Normal;
native double D;
native double negiC;
native bool isSlope();
native int PointOnSide(Vector3 pos);
native double ZatPoint (Vector2 v);
native double ZatPointDist(Vector2 v, double dist);
native bool isEqual(Secplane other);
native void ChangeHeight(double hdiff);
native double GetChangedHeight(double hdiff);
native double HeightDiff(double oldd, double newd = 0.0);
native double PointToDist(Vector2 xy, double z);
}
// This encapsulates all info Doom's original 'special' field contained - for saving and transferring.
struct SecSpecial
{
Name damagetype;
int damageamount;
short special;
short damageinterval;
short leakydamage;
int Flags;
}
struct Sector native
{
//secplane_t floorplane, ceilingplane; // defined internally
//FDynamicColormap *ColorMap;
native Actor SoundTarget;
native int16 special;
native int16 lightlevel;
native int16 seqType;
native int sky;
native Name SeqName;
native readonly Vector2 centerspot;
native int validcount;
native Actor thinglist;
native double friction, movefactor;
native int terrainnum[2];
// thinker_t for reversable actions
//SectorEffect floordata;
//SectorEffect ceilingdata;
//SectorEffect lightingdata;
enum EPlane
{
floor,
ceiling
};
enum EInterpolationType
{
CeilingMove,
FloorMove,
CeilingScroll,
FloorScroll
};
//Interpolation interpolations[4];
native uint8 soundtraversed;
native int8 stairlock;
native int prevsec;
native int nextsec;
//TStaticPointedArray<line_t *> Lines; // this is defined internally to avoid exposing some overly complicated type to the parser
native readonly Sector heightsec;
native uint bottommap, midmap, topmap;
//struct msecnode_t *touching_thinglist;
//struct msecnode_t *sectorportal_thinglist;
native double gravity;
native Name damagetype;
native int damageamount;
native int16 damageinterval;
native int16 leakydamage;
native readonly uint16 ZoneNumber;
enum ESectorMoreFlags
{
SECMF_FAKEFLOORONLY = 2, // when used as heightsec in R_FakeFlat, only copies floor
SECMF_CLIPFAKEPLANES = 4, // as a heightsec, clip planes to target sector's planes
SECMF_NOFAKELIGHT = 8, // heightsec does not change lighting
SECMF_IGNOREHEIGHTSEC= 16, // heightsec is only for triggering sector actions
SECMF_UNDERWATER = 32, // sector is underwater
SECMF_FORCEDUNDERWATER= 64, // sector is forced to be underwater
SECMF_UNDERWATERMASK = 32+64,
SECMF_DRAWN = 128, // sector has been drawn at least once
SECMF_HIDDEN = 256, // Do not draw on textured automap
}
native uint16 MoreFlags;
enum ESectorFlags
{
SECF_SILENT = 1, // actors in sector make no noise
SECF_NOFALLINGDAMAGE= 2, // No falling damage in this sector
SECF_FLOORDROP = 4, // all actors standing on this floor will remain on it when it lowers very fast.
SECF_NORESPAWN = 8, // players can not respawn in this sector
SECF_FRICTION = 16, // sector has friction enabled
SECF_PUSH = 32, // pushers enabled
SECF_SILENTMOVE = 64, // Sector movement makes mo sound (Eternity got this so this may be useful for an extended cross-port standard.)
SECF_DMGTERRAINFX = 128, // spawns terrain splash when inflicting damage
SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode
SECF_ENDLEVEL = 512, // ends level when health goes below 10
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling.
SECF_WASSECRET = 1 << 30, // a secret that was discovered
SECF_SECRET = 1 << 31, // a secret sector
SECF_DAMAGEFLAGS = SECF_ENDGODMODE|SECF_ENDLEVEL|SECF_DMGTERRAINFX|SECF_HAZARD,
SECF_NOMODIFY = SECF_SECRET|SECF_WASSECRET, // not modifiable by Sector_ChangeFlags
SECF_SPECIALFLAGS = SECF_DAMAGEFLAGS|SECF_FRICTION|SECF_PUSH, // these flags originate from 'special and must be transferrable by floor thinkers
}
enum EMoveResult
{
MOVE_OK,
MOVE_CRUSHED,
MOVE_PASTDEST
};
native uint Flags;
native SectorAction SecActTarget;
native uint Portals[2];
native readonly int PortalGroup;
native readonly int sectornum;
native int Index();
native double, Sector, F3DFloor NextHighestCeilingAt(double x, double y, double bottomz, double topz, int flags = 0);
native double, Sector, F3DFloor NextLowestFloorAt(double x, double y, double z, int flags = 0, double steph = 0);
native void RemoveForceField();
native static Sector PointInSector(Vector2 pt);
native void SetColor(color c, int desat = 0);
native void SetFade(color c);
native bool PlaneMoving(int pos);
native int GetFloorLight();
native int GetCeilingLight();
native Sector GetHeightSec();
native void TransferSpecial(Sector model);
native void GetSpecial(out SecSpecial spec);
native void SetSpecial( SecSpecial spec);
native int GetTerrain(int pos);
native void CheckPortalPlane(int plane);
native double, Sector HighestCeilingAt(Vector2 a);
native double, Sector LowestFloorAt(Vector2 a);
native double, double GetFriction(int plane);
native void SetXOffset(int pos, double o);
native void AddXOffset(int pos, double o);
native double GetXOffset(int pos);
native void SetYOffset(int pos, double o);
native void AddYOffset(int pos, double o);
native double GetYOffset(int pos, bool addbase = true);
native void SetXScale(int pos, double o);
native double GetXScale(int pos);
native void SetYScale(int pos, double o);
native double GetYScale(int pos);
native void SetAngle(int pos, double o);
native double GetAngle(int pos, bool addbase = true);
native void SetBase(int pos, double y, double o);
native void SetAlpha(int pos, double o);
native double GetAlpha(int pos);
native int GetFlags(int pos);
native int GetVisFlags(int pos);
native void ChangeFlags(int pos, int And, int Or);
native int GetPlaneLight(int pos);
native void SetPlaneLight(int pos, int level);
native TextureID GetTexture(int pos);
native void SetTexture(int pos, TextureID tex, bool floorclip = true);
native double GetPlaneTexZ(int pos);
native void SetPlaneTexZ(int pos, double val, bool dirtify = false); // This mainly gets used by init code. The only place where it must set the vertex to dirty is the interpolation code.
native void ChangeLightLevel(int newval);
native void SetLightLevel(int newval);
native int GetLightLevel();
native void AdjustFloorClip();
native bool IsLinked(Sector other, bool ceiling);
native bool PortalBlocksView(int plane);
native bool PortalBlocksSight(int plane);
native bool PortalBlocksMovement(int plane);
native bool PortalBlocksSound(int plane);
native bool PortalIsLinked(int plane);
native void ClearPortal(int plane);
native double GetPortalPlaneZ(int plane);
native Vector2 GetPortalDisplacement(int plane);
native int GetPortalType(int plane);
native int GetOppositePortalGroup(int plane);
native double CenterFloor();
native double CenterCeiling();
native bool TriggerSectorActions(Actor thing, int activation);
native int MoveFloor(double speed, double dest, int crush, int direction, bool hexencrush, bool instant = false);
native int MoveCeiling(double speed, double dest, int crush, int direction, bool hexencrush);
native Sector NextSpecialSector(int type, Sector prev);
native double, Vertex FindLowestFloorSurrounding();
native double, Vertex FindHighestFloorSurrounding();
native double, Vertex FindNextHighestFloor();
native double, Vertex FindNextLowestFloor();
native double, Vertex FindLowestCeilingSurrounding();
native double, Vertex FindHighestCeilingSurrounding();
native double, Vertex FindNextLowestCeiling();
native double, Vertex FindNextHighestCeiling();
native double FindShortestTextureAround();
native double FindShortestUpperAround();
native Sector FindModelFloorSector(double floordestheight);
native Sector FindModelCeilingSector(double floordestheight);
native int FindMinSurroundingLight(int max);
native double, Vertex FindLowestCeilingPoint();
native double, Vertex FindHighestFloorPoint();
native void SetEnvironment(String env);
native void SetEnvironmentID(int envnum);
native SeqNode StartSoundSequenceID (int chan, int sequence, int type, int modenum, bool nostop = false);
native SeqNode StartSoundSequence (int chan, Name seqname, int modenum);
native SeqNode CheckSoundSequence (int chan);
native void StopSoundSequence(int chan);
native bool IsMakingLoopingSound ();
bool isSecret()
{
return !!(Flags & SECF_SECRET);
}
bool wasSecret()
{
return !!(Flags & SECF_WASSECRET);
}
void ClearSecret()
{
Flags &= ~SECF_SECRET;
}
}

View file

@ -71,3 +71,126 @@ class IceChunkHead : PlayerChunk
}
}
extend class Actor
{
//============================================================================
//
// A_FreezeDeath
//
//============================================================================
void A_FreezeDeath()
{
int t = random[freezedeath]();
tics = 75+t+random[freezedeath]();
bSolid = bShootable = bNoBlood = bIceCorpse = bPushable = bTelestomp = bCanPass = bSlidesOnWalls = bCrashed = true;
Height = Default.Height;
A_SetRenderStyle(1, STYLE_Normal);
A_PlaySound ("misc/freeze", CHAN_BODY);
// [RH] Andy Baker's stealth monsters
if (bStealth)
{
Alpha = 1;
visdir = 0;
}
if (player)
{
player.damagecount = 0;
player.poisoncount = 0;
player.bonuscount = 0;
}
else if (bIsMonster && special)
{ // Initiate monster death actions
A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
special = 0;
}
}
//============================================================================
//
// A_FreezeDeathChunks
//
//============================================================================
void A_FreezeDeathChunks()
{
if (Vel != (0,0,0) && !bShattering)
{
tics = 3*TICRATE;
return;
}
Vel = (0,0,0);
A_PlaySound ("misc/icebreak", CHAN_BODY);
// [RH] In Hexen, this creates a random number of shards (range [24,56])
// with no relation to the size of the self shattering. I think it should
// base the number of shards on the size of the dead thing, so bigger
// things break up into more shards than smaller things.
// An actor with radius 20 and height 64 creates ~40 chunks.
int numChunks = max(4, int(radius * Height)/32);
int i = Random[FreezeDeathChunks]() % (numChunks/4);
for (i = max(24, numChunks + i); i >= 0; i--)
{
double xo = (random[FreezeDeathChunks]() - 128)*radius / 128;
double yo = (random[FreezeDeathChunks]() - 128)*radius / 128;
double zo = (random[FreezeDeathChunks]() * Height / 255);
Actor mo = Spawn("IceChunk", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo.SetState (mo.SpawnState + (random[FreezeDeathChunks]()%3));
mo.Vel.X = random2[FreezeDeathChunks]() / 128.;
mo.Vel.Y = random2[FreezeDeathChunks]() / 128.;
mo.Vel.Z = (mo.pos.Z - pos.Z) / Height * 4;
}
}
if (player)
{ // attach the player's view to a chunk of ice
Actor head = Spawn("IceChunkHead", pos + (0, 0, player.mo.ViewHeight), ALLOW_REPLACE);
if (head != null)
{
head.Vel.X = random2[FreezeDeathChunks]() / 128.;
head.Vel.Y = random2[FreezeDeathChunks]() / 128.;
head.Vel.Z = (head.pos.Z - pos.Z) / Height * 4;
head.health = health;
head.Angle = Angle;
if (head is "PlayerPawn")
{
head.player = player;
head.player.mo = PlayerPawn(head);
player = null;
head.ObtainInventory (self);
}
head.Pitch = 0.;
if (head.player.camera == self)
{
head.player.camera = head;
}
}
}
// [RH] Do some stuff to make this more useful outside Hexen
if (bBossDeath)
{
A_BossDeath();
}
A_NoBlocking();
SetStateLabel('null');
}
void A_GenericFreezeDeath()
{
A_SetTranslation('Ice');
A_FreezeDeath();
}
}

View file

@ -1,87 +0,0 @@
class ScoreItem : Inventory
{
Default
{
Height 10;
+COUNTITEM
Inventory.Amount 1;
+Inventory.ALWAYSPICKUP
}
override bool TryPickup (in out Actor toucher)
{
toucher.Score += Amount;
GoAwayAndDie();
return true;
}
}
class Health : Inventory native
{
native int PrevHealth;
Default
{
Inventory.Amount 1;
Inventory.MaxAmount 0;
Inventory.PickupSound "misc/health_pkup";
}
}
class HealthPickup : Inventory native
{
native int autousemode;
Default
{
Inventory.DefMaxAmount;
+INVENTORY.INVBAR
}
}
class Key : Inventory native
{
native uint8 KeyNumber;
Default
{
+DONTGIB; // Don't disappear due to a crusher
Inventory.InterHubAmount 0;
Inventory.PickupSound "misc/k_pkup";
}
}
class MapRevealer : Inventory
{
//===========================================================================
//
// AMapRevealer :: TryPickup
//
// A MapRevealer reveals the whole map for the player who picks it up.
// The MapRevealer doesn't actually go in your inventory. Instead, it sets
// a flag on the level.
//
//===========================================================================
override bool TryPickup (in out Actor toucher)
{
level.allmap = true;
GoAwayAndDie ();
return true;
}
}
class PuzzleItem : Inventory native
{
native int PuzzleItemNumber;
Default
{
+NOGRAVITY
+INVENTORY.INVBAR
Inventory.DefMaxAmount;
Inventory.UseSound "PuzzleSuccess";
Inventory.PickupSound "misc/i_pkup";
}
}

View file

@ -105,6 +105,7 @@ class PlayerPawn : Actor native
native int GetMaxHealth();
native bool ResetAirSupply (bool playgasp = false);
native void CheckWeaponSwitch(class<Inventory> item);
}
class PlayerChunk : PlayerPawn native
@ -127,8 +128,18 @@ class PlayerChunk : PlayerPawn native
class PSprite : Object native
{
enum PSPLayers
{
STRIFEHANDS = -1,
WEAPON = 1,
FLASH = 1000,
TARGETCENTER = 0x7fffffff - 2,
TARGETLEFT,
TARGETRIGHT,
};
native readonly State CurState;
native readonly Actor Caller;
native Actor Caller;
native readonly PSprite Next;
native readonly PlayerInfo Owner;
native SpriteID Sprite;
@ -164,7 +175,11 @@ enum EPlayerState
struct PlayerInfo native // this is what internally is known as player_t
{
native readonly PlayerPawn mo;
// technically engine constants but the only part of the playsim using them is the player.
const NOFIXEDCOLORMAP = -1;
const NUMCOLORMAPS = 32;
native PlayerPawn mo;
native uint8 playerstate;
native uint original_oldbuttons;
native readonly Class<PlayerPawn> cls;

View file

@ -1,4 +1,40 @@
class SkyViewpoint : Actor native
/*
** a_skies.cpp
** Skybox-related actors
**
**---------------------------------------------------------------------------
** Copyright 1998-2016 Randy Heit
** Copyright 2006-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, self list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, self list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from self software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class SkyViewpoint : Actor
{
default
{
@ -7,9 +43,52 @@ class SkyViewpoint : Actor native
+NOGRAVITY
+DONTSPLASH
}
// arg0 = Visibility*4 for self skybox
// If self actor has no TID, make it the default sky box
override void BeginPlay ()
{
Super.BeginPlay ();
if (tid == 0 && level.sectorPortals[0].mSkybox == null)
{
level.sectorPortals[0].mSkybox = self;
level.sectorPortals[0].mDestination = CurSector;
}
}
override void OnDestroy ()
{
// remove all sector references to ourselves.
for (int i = 0; i < level.sectorPortals.Size(); i++)
{
SectorPortal s = level.sectorPortals[i];
if (s.mSkybox == self)
{
s.mSkybox = null;
// This is necessary to entirely disable EE-style skyboxes
// if their viewpoint gets deleted.
s.mFlags |= SectorPortal.FLAG_SKYFLATONLY;
}
}
Super.OnDestroy();
}
}
class SkyPicker : Actor native
//---------------------------------------------------------------------------
// arg0 = tid of matching SkyViewpoint
// A value of 0 means to use a regular stretched texture, in case
// there is a default SkyViewpoint in the level.
//
// arg1 = 0: set both floor and ceiling skybox
// = 1: set only ceiling skybox
// = 2: set only floor skybox
class SkyPicker : Actor
{
default
{
@ -18,14 +97,61 @@ class SkyPicker : Actor native
+NOGRAVITY
+DONTSPLASH
}
override void PostBeginPlay ()
{
Actor box;
Super.PostBeginPlay ();
if (args[0] == 0)
{
box = null;
}
else
{
let it = ActorIterator.Create(args[0], "SkyViewpoint");
box = it.Next ();
}
if (box == null && args[0] != 0)
{
A_Log(format("Can't find SkyViewpoint %d for sector %d\n", args[0], CurSector.Index()));
}
else
{
int boxindex = SectorPortal.GetSkyboxPortal(box);
// Do not override special portal types, only regular skies.
if (0 == (args[1] & 2))
{
if (CurSector.GetPortalType(sector.ceiling) == SectorPortal.TYPE_SKYVIEWPOINT)
CurSector.Portals[sector.ceiling] = boxindex;
}
if (0 == (args[1] & 1))
{
if (CurSector.GetPortalType(sector.floor) == SectorPortal.TYPE_SKYVIEWPOINT)
CurSector.Portals[sector.floor] = boxindex;
}
}
Destroy ();
}
}
class SkyCamCompat : SkyViewpoint native
class SkyCamCompat : SkyViewpoint
{
override void BeginPlay ()
{
// Skip SkyViewpoint's initialization, Actor's is not needed here.
}
}
class StackPoint : SkyViewpoint native
class StackPoint : SkyViewpoint
{
override void BeginPlay ()
{
// Skip SkyViewpoint's initialization, Actor's is not needed here.
}
}
class UpperStackLookOnly : StackPoint

View file

@ -1,5 +1,5 @@
class SoundEnvironment : Actor native
class SoundEnvironment : Actor
{
default
{
@ -8,5 +8,28 @@ class SoundEnvironment : Actor native
+NOGRAVITY
+DONTSPLASH
}
}
override void PostBeginPlay ()
{
Super.PostBeginPlay ();
if (!bDormant)
{
Activate (self);
}
}
override void Activate (Actor activator)
{
CurSector.SetEnvironmentID((args[0]<<8) | (args[1]));
}
// Deactivate just exists so that you can flag the thing as dormant in an editor
// and not have it take effect. This is so you can use multiple environments in
// a single zone, with only one set not-dormant, so you know which one will take
// effect at the start.
override void Deactivate (Actor deactivator)
{
bDormant = true;
}
}

View file

@ -1,3 +1,62 @@
/*
** a_soundsequence.cpp
** Actors for independantly playing sound sequences in a map.
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
** A SoundSequence actor has two modes of operation:
**
** 1. If the sound sequence assigned to it has a slot, then a separate
** SoundSequenceSlot actor is spawned (if not already present), and
** this actor's sound sequence is added to its list of choices. This
** actor is then destroyed, never to be heard from again. The sound
** sequence for the slot is automatically played on the new
** SoundSequenceSlot actor, and it should at some point execute the
** randomsequence command so that it can pick one of the other
** sequences to play. The slot sequence should also end with restart
** so that more than one sequence will have a chance to play.
**
** In this mode, it is very much like world $ambient sounds defined
** in SNDINFO but more flexible.
**
** 2. If the sound sequence assigned to it has no slot, then it will play
** the sequence when activated and cease playing the sequence when
** deactivated.
**
** In this mode, it is very much like point $ambient sounds defined
** in SNDINFO but more flexible.
**
** To assign a sound sequence, set the SoundSequence's first argument to
** the ID of the corresponding environment sequence you want to use. If
** that sequence is a multiple-choice sequence, then the second argument
** selects which choice it picks.
*/
class AmbientSound : Actor native
{
@ -17,7 +76,7 @@ class AmbientSoundNoGravity : AmbientSound
}
}
class SoundSequenceSlot : Actor native
class SoundSequenceSlot : Actor
{
default
{
@ -25,9 +84,11 @@ class SoundSequenceSlot : Actor native
+NOBLOCKMAP
+DONTSPLASH
}
SeqNode sequence;
}
class SoundSequence : Actor native
class SoundSequence : Actor
{
default
{
@ -35,6 +96,86 @@ class SoundSequence : Actor native
+NOBLOCKMAP
+DONTSPLASH
}
//==========================================================================
//
// ASoundSequence :: Destroy
//
//==========================================================================
override void OnDestroy ()
{
StopSoundSequence ();
Super.OnDestroy();
}
//==========================================================================
//
// ASoundSequence :: PostBeginPlay
//
//==========================================================================
override void PostBeginPlay ()
{
Name slot = SeqNode.GetSequenceSlot (args[0], SeqNode.ENVIRONMENT);
if (slot != 'none')
{ // This is a slotted sound, so add it to the master for that slot
SoundSequenceSlot master;
let locator = ThinkerIterator.Create("SoundSequenceSlot");
while ((master = SoundSequenceSlot(locator.Next ())))
{
if (master.Sequence.GetSequenceName() == slot)
{
break;
}
}
if (master == NULL)
{
master = SoundSequenceSlot(Spawn("SoundSequenceSlot"));
master.Sequence = master.StartSoundSequence (slot, 0);
}
master.Sequence.AddChoice (args[0], SeqNode.ENVIRONMENT);
Destroy ();
}
}
//==========================================================================
//
// ASoundSequence :: MarkPrecacheSounds
//
//==========================================================================
override void MarkPrecacheSounds()
{
Super.MarkPrecacheSounds();
SeqNode.MarkPrecacheSounds(args[0], SeqNode.ENVIRONMENT);
}
//==========================================================================
//
// ASoundSequence :: Activate
//
//==========================================================================
override void Activate (Actor activator)
{
StartSoundSequenceID (args[0], SeqNode.ENVIRONMENT, args[1]);
}
//==========================================================================
//
// ASoundSequence :: Deactivate
//
//==========================================================================
override void Deactivate (Actor activator)
{
StopSoundSequence ();
}
}
// Heretic Sound sequences -----------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show more