mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2025-01-18 14:41:40 +00:00
Merge remote-tracking branch 'gzdoom/master' into qzdoom
# Conflicts: # src/r_plane.cpp # src/r_plane.h
This commit is contained in:
commit
1c3440e391
104 changed files with 3243 additions and 2832 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -140,7 +140,6 @@ public:
|
|||
double GetSpeedFactor();
|
||||
bool GetNoTeleportFreeze();
|
||||
// Stuff for later when more features are exported.
|
||||
virtual void Travelled();
|
||||
virtual void OwnerDied();
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -161,7 +161,6 @@ public:
|
|||
|
||||
|
||||
virtual void EndPowerup ();
|
||||
void CallEndPowerup();
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -43,9 +43,6 @@
|
|||
#include "gl/utility/gl_templates.h"
|
||||
#include "gl/data/gl_data.h"
|
||||
|
||||
class ASkyViewpoint;
|
||||
|
||||
|
||||
struct GLHorizonInfo
|
||||
{
|
||||
GLSectorPlane plane;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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
|
||||
|
|
141
src/p_mobj.cpp
141
src/p_mobj.cpp
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "g_levellocals.h"
|
||||
#include "r_state.h"
|
||||
#include "p_maputl.h"
|
||||
#include "p_blockmap.h"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) - §orPortals[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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(§orPortals[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(§orPortals[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(§orPortals[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(§orPortals[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(§orPortals[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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
38
src/r_defs.h
38
src/r_defs.h
|
@ -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 §orPortals[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 : §orPortals[portaltransferred];
|
||||
}
|
||||
FSectorPortal *GetTransferredPortal();
|
||||
|
||||
FLinePortal *getPortal() const
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
123
src/s_sndseq.cpp
123
src/s_sndseq.cpp
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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'")
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//==========================================================================
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <stddef.h>
|
||||
#include "r_defs.h"
|
||||
|
||||
class ASkyViewpoint;
|
||||
class ADynamicLight;
|
||||
struct FLightNode;
|
||||
struct FDynamicColormap;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
311
wadsrc/static/zscript/inventory/ammo.txt
Normal file
311
wadsrc/static/zscript/inventory/ammo.txt
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
162
wadsrc/static/zscript/inventory/health.txt
Normal file
162
wadsrc/static/zscript/inventory/health.txt
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
141
wadsrc/static/zscript/inventory/inv_misc.txt
Normal file
141
wadsrc/static/zscript/inventory/inv_misc.txt
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//===========================================================================
|
|
@ -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;
|
||||
}
|
||||
|
401
wadsrc/static/zscript/mapdata.txt
Normal file
401
wadsrc/static/zscript/mapdata.txt
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue