mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-28 06:42:09 +00:00
Merge branch 'master' of https://github.com/rheit/zdoom
This commit is contained in:
commit
5d099590cd
45 changed files with 3177 additions and 1509 deletions
|
@ -1197,7 +1197,9 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE
|
|||
p_plats.cpp
|
||||
p_portals.cpp
|
||||
p_pspr.cpp
|
||||
p_pusher.cpp
|
||||
p_saveg.cpp
|
||||
p_scroll.cpp
|
||||
p_sectors.cpp
|
||||
p_setup.cpp
|
||||
p_sight.cpp
|
||||
|
|
101
src/dobject.cpp
101
src/dobject.cpp
|
@ -257,46 +257,50 @@ DObject::DObject (PClass *inClass)
|
|||
|
||||
DObject::~DObject ()
|
||||
{
|
||||
if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown)
|
||||
if (!PClass::bShutdown)
|
||||
{
|
||||
DObject **probe;
|
||||
PClass *type = GetClass();
|
||||
|
||||
if (!(ObjectFlags & OF_YesReallyDelete))
|
||||
if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown)
|
||||
{
|
||||
Printf ("Warning: '%s' is freed outside the GC process.\n",
|
||||
type != NULL ? type->TypeName.GetChars() : "==some object==");
|
||||
}
|
||||
DObject **probe;
|
||||
|
||||
// Find all pointers that reference this object and NULL them.
|
||||
StaticPointerSubstitution(this, NULL);
|
||||
|
||||
// Now unlink this object from the GC list.
|
||||
for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext))
|
||||
{
|
||||
if (*probe == this)
|
||||
if (!(ObjectFlags & OF_YesReallyDelete))
|
||||
{
|
||||
*probe = ObjNext;
|
||||
if (&ObjNext == GC::SweepPos)
|
||||
{
|
||||
GC::SweepPos = probe;
|
||||
}
|
||||
break;
|
||||
Printf("Warning: '%s' is freed outside the GC process.\n",
|
||||
type != NULL ? type->TypeName.GetChars() : "==some object==");
|
||||
}
|
||||
}
|
||||
|
||||
// If it's gray, also unlink it from the gray list.
|
||||
if (this->IsGray())
|
||||
{
|
||||
for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext))
|
||||
// Find all pointers that reference this object and NULL them.
|
||||
StaticPointerSubstitution(this, NULL);
|
||||
|
||||
// Now unlink this object from the GC list.
|
||||
for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext))
|
||||
{
|
||||
if (*probe == this)
|
||||
{
|
||||
*probe = GCNext;
|
||||
*probe = ObjNext;
|
||||
if (&ObjNext == GC::SweepPos)
|
||||
{
|
||||
GC::SweepPos = probe;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If it's gray, also unlink it from the gray list.
|
||||
if (this->IsGray())
|
||||
{
|
||||
for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext))
|
||||
{
|
||||
if (*probe == this)
|
||||
{
|
||||
*probe = GCNext;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
type->DestroySpecials(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,46 +421,15 @@ void DObject::SerializeUserVars(FArchive &arc)
|
|||
|
||||
if (arc.IsStoring())
|
||||
{
|
||||
// Write all user variables.
|
||||
for (; symt != NULL; symt = symt->ParentSymbolTable)
|
||||
{
|
||||
PSymbolTable::MapType::Iterator it(symt->Symbols);
|
||||
PSymbolTable::MapType::Pair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
PField *var = dyn_cast<PField>(pair->Value);
|
||||
if (var != NULL && !(var->Flags & VARF_Native))
|
||||
{
|
||||
PType *type = var->Type;
|
||||
PArray *arraytype = dyn_cast<PArray>(type);
|
||||
if (arraytype == NULL)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = arraytype->ElementCount;
|
||||
type = arraytype->ElementType;
|
||||
}
|
||||
assert(type == TypeSInt32);
|
||||
varloc = (int *)(reinterpret_cast<BYTE *>(this) + var->Offset);
|
||||
|
||||
arc << var->SymbolName;
|
||||
arc.WriteCount(count);
|
||||
for (j = 0; j < count; ++j)
|
||||
{
|
||||
arc << varloc[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Write terminator.
|
||||
varname = NAME_None;
|
||||
arc << varname;
|
||||
// Write all fields that aren't serialized by native code.
|
||||
GetClass()->WriteValue(arc, this);
|
||||
}
|
||||
else if (SaveVersion >= 4535)
|
||||
{
|
||||
GetClass()->ReadValue(arc, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
{ // Old version that only deals with ints
|
||||
// Read user variables until 'None' is encountered.
|
||||
arc << varname;
|
||||
while (varname != NAME_None)
|
||||
|
|
|
@ -42,6 +42,7 @@ class PClass;
|
|||
class FArchive;
|
||||
|
||||
class DObject;
|
||||
/*
|
||||
class DArgs;
|
||||
class DCanvas;
|
||||
class DConsoleCommand;
|
||||
|
@ -77,6 +78,7 @@ class DFloor;
|
|||
class DFloorWaggle;
|
||||
class DPlat;
|
||||
class DPillar;
|
||||
*/
|
||||
|
||||
class PClassActor;
|
||||
|
||||
|
|
1145
src/dobjtype.cpp
1145
src/dobjtype.cpp
File diff suppressed because it is too large
Load diff
126
src/dobjtype.h
126
src/dobjtype.h
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "vm.h"
|
||||
|
||||
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
|
||||
|
||||
// Variable/parameter/field flags -------------------------------------------
|
||||
|
||||
// Making all these different storage types use a common set of flags seems
|
||||
|
@ -186,11 +188,43 @@ public:
|
|||
|
||||
int FindConversion(PType *target, const Conversion **slots, int numslots);
|
||||
|
||||
// Writes the value of a variable of this type at (addr) to an archive, preceded by
|
||||
// a tag indicating its type. The tag is there so that variable types can be changed
|
||||
// without completely breaking savegames, provided that the change isn't between
|
||||
// totally unrelated types.
|
||||
virtual void WriteValue(FArchive &ar, const void *addr) const;
|
||||
|
||||
// Returns true if the stored value was compatible. False otherwise.
|
||||
// If the value was incompatible, then the memory at *addr is unchanged.
|
||||
virtual bool ReadValue(FArchive &ar, void *addr) const;
|
||||
|
||||
// Skips over a value written with WriteValue
|
||||
static void SkipValue(FArchive &ar);
|
||||
static void SkipValue(FArchive &ar, int tag);
|
||||
|
||||
// Sets the default value for this type at (base + offset)
|
||||
// If the default value is binary 0, then this function doesn't need
|
||||
// to do anything, because PClass::Extend() takes care of that.
|
||||
//
|
||||
// The stroffs array is so that types that need special initialization
|
||||
// and destruction (e.g. strings) can add their offsets to it for special
|
||||
// initialization when the object is created and destruction when the
|
||||
// object is destroyed.
|
||||
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const;
|
||||
|
||||
// Initialize the value, if needed (e.g. strings)
|
||||
virtual void InitializeValue(void *addr, const void *def) const;
|
||||
|
||||
// Destroy the value, if needed (e.g. strings)
|
||||
virtual void DestroyValue(void *addr) const;
|
||||
|
||||
// Sets the value of a variable of this type at (addr)
|
||||
virtual void SetValue(void *addr, int val);
|
||||
virtual void SetValue(void *addr, double val);
|
||||
|
||||
// Gets the value of a variable of this type at (addr)
|
||||
virtual int GetValueInt(void *addr) const;
|
||||
virtual double GetValueFloat(void *addr) const;
|
||||
|
||||
// Gets the opcode to store from a register to memory
|
||||
virtual int GetStoreOp() const;
|
||||
|
@ -319,8 +353,13 @@ class PInt : public PBasicType
|
|||
public:
|
||||
PInt(unsigned int size, bool unsign);
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
virtual void SetValue(void *addr, int val);
|
||||
virtual void SetValue(void *addr, double val);
|
||||
virtual int GetValueInt(void *addr) const;
|
||||
virtual double GetValueFloat(void *addr) const;
|
||||
virtual int GetStoreOp() const;
|
||||
virtual int GetLoadOp() const;
|
||||
virtual int GetRegType() const;
|
||||
|
@ -343,8 +382,13 @@ class PFloat : public PBasicType
|
|||
public:
|
||||
PFloat(unsigned int size);
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
virtual void SetValue(void *addr, int val);
|
||||
virtual void SetValue(void *addr, double val);
|
||||
virtual int GetValueInt(void *addr) const;
|
||||
virtual double GetValueFloat(void *addr) const;
|
||||
virtual int GetStoreOp() const;
|
||||
virtual int GetLoadOp() const;
|
||||
virtual int GetRegType() const;
|
||||
|
@ -375,6 +419,12 @@ public:
|
|||
PString();
|
||||
|
||||
virtual int GetRegType() const;
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const override;
|
||||
void InitializeValue(void *addr, const void *def) const override;
|
||||
void DestroyValue(void *addr) const override;
|
||||
};
|
||||
|
||||
// Variations of integer types ----------------------------------------------
|
||||
|
@ -384,6 +434,9 @@ class PName : public PInt
|
|||
DECLARE_CLASS(PName, PInt);
|
||||
public:
|
||||
PName();
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
};
|
||||
|
||||
class PSound : public PInt
|
||||
|
@ -391,6 +444,9 @@ class PSound : public PInt
|
|||
DECLARE_CLASS(PSound, PInt);
|
||||
public:
|
||||
PSound();
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
};
|
||||
|
||||
class PColor : public PInt
|
||||
|
@ -409,8 +465,13 @@ class PFixed : public PFloat
|
|||
public:
|
||||
PFixed();
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
virtual void SetValue(void *addr, int val);
|
||||
virtual void SetValue(void *addr, double val);
|
||||
virtual int GetValueInt(void *addr) const;
|
||||
virtual double GetValueFloat(void *addr) const;
|
||||
virtual int GetStoreOp() const;
|
||||
virtual int GetLoadOp() const;
|
||||
};
|
||||
|
@ -421,8 +482,13 @@ class PAngle : public PFloat
|
|||
public:
|
||||
PAngle();
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
virtual void SetValue(void *addr, int val);
|
||||
virtual void SetValue(void *addr, double val);
|
||||
virtual int GetValueInt(void *addr) const;
|
||||
virtual double GetValueFloat(void *addr) const;
|
||||
virtual int GetStoreOp() const;
|
||||
virtual int GetLoadOp() const;
|
||||
};
|
||||
|
@ -435,6 +501,9 @@ class PStatePointer : public PBasicType
|
|||
public:
|
||||
PStatePointer();
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
virtual int GetStoreOp() const;
|
||||
virtual int GetLoadOp() const;
|
||||
virtual int GetRegType() const;
|
||||
|
@ -455,6 +524,10 @@ public:
|
|||
|
||||
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
|
||||
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
protected:
|
||||
PPointer();
|
||||
};
|
||||
|
@ -521,6 +594,12 @@ public:
|
|||
|
||||
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
|
||||
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special) const override;
|
||||
|
||||
protected:
|
||||
PArray();
|
||||
};
|
||||
|
@ -575,9 +654,16 @@ public:
|
|||
|
||||
TArray<PField *> Fields;
|
||||
|
||||
PField *AddField(FName name, PType *type, DWORD flags=0);
|
||||
virtual PField *AddField(FName name, PType *type, DWORD flags=0);
|
||||
|
||||
size_t PropagateMark();
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *specials) const override;
|
||||
|
||||
static void WriteFields(FArchive &ar, const void *addr, const TArray<PField *> &fields);
|
||||
bool ReadFields(FArchive &ar, void *addr) const;
|
||||
protected:
|
||||
PStruct();
|
||||
};
|
||||
|
@ -635,11 +721,16 @@ class PClass : public PStruct
|
|||
protected:
|
||||
// We unravel _WITH_META here just as we did for PType.
|
||||
enum { MetaClassNum = CLASSREG_PClassClass };
|
||||
TArray<FTypeAndOffset> SpecialInits;
|
||||
virtual void Derive(PClass *newclass);
|
||||
void InitializeSpecials(void *addr) const;
|
||||
public:
|
||||
typedef PClassClass MetaClass;
|
||||
MetaClass *GetClass() const;
|
||||
|
||||
void WriteValue(FArchive &ar, const void *addr) const override;
|
||||
bool ReadValue(FArchive &ar, void *addr) const override;
|
||||
|
||||
virtual void DeriveData(PClass *newclass) {}
|
||||
static void StaticInit();
|
||||
static void StaticShutdown();
|
||||
|
@ -660,9 +751,10 @@ public:
|
|||
void InsertIntoHash();
|
||||
DObject *CreateNew() const;
|
||||
PClass *CreateDerivedClass(FName name, unsigned int size);
|
||||
unsigned int Extend(unsigned int extension);
|
||||
PField *AddField(FName name, PType *type, DWORD flags=0) override;
|
||||
void InitializeActorInfo();
|
||||
void BuildFlatPointers();
|
||||
void DestroySpecials(void *addr) const;
|
||||
const PClass *NativeClass() const;
|
||||
|
||||
// Returns true if this type is an ancestor of (or same as) the passed type.
|
||||
|
@ -682,6 +774,9 @@ public:
|
|||
}
|
||||
|
||||
// Find a type, given its name.
|
||||
const PClass *FindParentClass(FName name) const;
|
||||
PClass *FindParentClass(FName name) { return const_cast<PClass *>(const_cast<const PClass *>(this)->FindParentClass(name)); }
|
||||
|
||||
static PClass *FindClass(const char *name) { return FindClass(FName(name, true)); }
|
||||
static PClass *FindClass(const FString &name) { return FindClass(FName(name, true)); }
|
||||
static PClass *FindClass(ENamedName name) { return FindClass(FName(name)); }
|
||||
|
@ -832,4 +927,31 @@ public:
|
|||
|
||||
void ReleaseGlobalSymbols();
|
||||
|
||||
// Enumerations for serializing types in an archive -------------------------
|
||||
|
||||
enum ETypeVal : BYTE
|
||||
{
|
||||
VAL_Int8,
|
||||
VAL_UInt8,
|
||||
VAL_Int16,
|
||||
VAL_UInt16,
|
||||
VAL_Int32,
|
||||
VAL_UInt32,
|
||||
VAL_Int64,
|
||||
VAL_UInt64,
|
||||
VAL_Zero,
|
||||
VAL_One,
|
||||
VAL_Float32,
|
||||
VAL_Float64,
|
||||
VAL_Fixed,
|
||||
VAL_BAM,
|
||||
VAL_String,
|
||||
VAL_Name,
|
||||
VAL_Struct,
|
||||
VAL_Array,
|
||||
VAL_Object,
|
||||
VAL_State,
|
||||
VAL_Class,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,6 @@ CVAR (Bool, developer, false, 0)
|
|||
|
||||
// [RH] Feature control cvars
|
||||
CVAR (Bool, var_friction, true, CVAR_SERVERINFO);
|
||||
CVAR (Bool, var_pushers, true, CVAR_SERVERINFO);
|
||||
|
||||
CVAR (Bool, alwaysapplydmflags, false, CVAR_SERVERINFO);
|
||||
|
||||
|
|
|
@ -212,7 +212,6 @@ extern bool ToggleFullscreen;
|
|||
extern int Net_Arbitrator;
|
||||
|
||||
EXTERN_CVAR (Bool, var_friction)
|
||||
EXTERN_CVAR (Bool, var_pushers)
|
||||
|
||||
|
||||
// [RH] Miscellaneous info for DeHackEd support
|
||||
|
|
|
@ -42,6 +42,7 @@ class AActor;
|
|||
class player_t;
|
||||
struct pspdef_s;
|
||||
struct FState;
|
||||
class DThinker;
|
||||
|
||||
class FThinkerIterator;
|
||||
|
||||
|
|
|
@ -721,6 +721,29 @@ void FArchive::Close ()
|
|||
}
|
||||
}
|
||||
|
||||
void FArchive::WriteByte(BYTE val)
|
||||
{
|
||||
m_File->Write(&val, 1);
|
||||
}
|
||||
|
||||
void FArchive::WriteInt16(WORD val)
|
||||
{
|
||||
WORD out = LittleShort(val);
|
||||
m_File->Write(&out, 2);
|
||||
}
|
||||
|
||||
void FArchive::WriteInt32(DWORD val)
|
||||
{
|
||||
int out = LittleLong(val);
|
||||
m_File->Write(&out, 4);
|
||||
}
|
||||
|
||||
void FArchive::WriteInt64(QWORD val)
|
||||
{
|
||||
long long out = SWAP_QWORD(val);
|
||||
m_File->Write(&out, 8);
|
||||
}
|
||||
|
||||
void FArchive::WriteCount (DWORD count)
|
||||
{
|
||||
BYTE out;
|
||||
|
@ -832,6 +855,14 @@ void FArchive::WriteString (const char *str)
|
|||
}
|
||||
}
|
||||
|
||||
void FArchive::WriteString(const FString &str)
|
||||
{
|
||||
// The count includes the '\0' terminator, but we don't
|
||||
// actually write it out.
|
||||
WriteCount(str.Len() + 1);
|
||||
Write(str, str.Len());
|
||||
}
|
||||
|
||||
FArchive &FArchive::operator<< (char *&str)
|
||||
{
|
||||
if (m_Storing)
|
||||
|
@ -868,7 +899,7 @@ FArchive &FArchive::operator<< (FString &str)
|
|||
{
|
||||
if (m_Storing)
|
||||
{
|
||||
WriteString (str.GetChars());
|
||||
WriteString (str);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -883,8 +914,7 @@ FArchive &FArchive::operator<< (FString &str)
|
|||
char *str2 = (char *)alloca(size*sizeof(char));
|
||||
size--;
|
||||
Read (str2, size);
|
||||
str2[size] = 0;
|
||||
str = str2;
|
||||
str = FString(str2, size);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
|
|
|
@ -166,7 +166,14 @@ public:
|
|||
virtual void Write (const void *mem, unsigned int len);
|
||||
virtual void Read (void *mem, unsigned int len);
|
||||
|
||||
void WriteString(const FString &str);
|
||||
void WriteString (const char *str);
|
||||
|
||||
void WriteByte(BYTE val);
|
||||
void WriteInt16(WORD val);
|
||||
void WriteInt32(DWORD val);
|
||||
void WriteInt64(QWORD val);
|
||||
|
||||
void WriteCount (DWORD count);
|
||||
DWORD ReadCount ();
|
||||
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "d_player.h"
|
||||
#include "a_doomglobal.h"
|
||||
#include "w_wad.h"
|
||||
#include "gi.h"
|
||||
#include "zstring.h"
|
||||
|
|
|
@ -84,6 +84,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePistol)
|
|||
return 0;
|
||||
|
||||
P_SetPsprite (self->player, ps_flash, weapon->FindState(NAME_Flash));
|
||||
self->player->psprites[ps_flash].processPending = true;
|
||||
}
|
||||
self->player->mo->PlayAttacking2 ();
|
||||
|
||||
|
@ -275,6 +276,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun)
|
|||
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
|
||||
return 0;
|
||||
P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash));
|
||||
self->player->psprites[ps_flash].processPending = true;
|
||||
}
|
||||
player->mo->PlayAttacking2 ();
|
||||
|
||||
|
@ -311,6 +313,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun2)
|
|||
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 2))
|
||||
return 0;
|
||||
P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash));
|
||||
self->player->psprites[ps_flash].processPending = true;
|
||||
}
|
||||
player->mo->PlayAttacking2 ();
|
||||
|
||||
|
@ -384,12 +387,14 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i
|
|||
{
|
||||
// we're ok so set the state
|
||||
P_SetPsprite (player, ps_flash, flashstate + index);
|
||||
player->psprites[ps_flash].processPending = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// oh, no! The state is beyond the end of the state table so use the original flash state.
|
||||
P_SetPsprite (player, ps_flash, flashstate);
|
||||
player->psprites[ps_flash].processPending = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -406,6 +411,7 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i
|
|||
index = 0;
|
||||
}
|
||||
P_SetPsprite (player, ps_flash, flashstate + index);
|
||||
player->psprites[ps_flash].processPending = true;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -2025,7 +2025,7 @@ void FLevelLocals::Tick ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::AddScroller (DScroller *scroller, int secnum)
|
||||
void FLevelLocals::AddScroller (int secnum)
|
||||
{
|
||||
if (secnum < 0)
|
||||
{
|
||||
|
|
|
@ -383,7 +383,7 @@ struct FSectorScrollValues
|
|||
struct FLevelLocals
|
||||
{
|
||||
void Tick ();
|
||||
void AddScroller (DScroller *, int secnum);
|
||||
void AddScroller (int secnum);
|
||||
|
||||
int time; // time in the hub
|
||||
int maptime; // time in the map
|
||||
|
|
|
@ -37,7 +37,7 @@ DEarthquake::DEarthquake()
|
|||
|
||||
DEarthquake::DEarthquake (AActor *center, int intensityX, int intensityY, int intensityZ, int duration,
|
||||
int damrad, int tremrad, FSoundID quakesound, int flags,
|
||||
double waveSpeedX, double waveSpeedY, double waveSpeedZ)
|
||||
double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint)
|
||||
: DThinker(STAT_EARTHQUAKE)
|
||||
{
|
||||
m_QuakeSFX = quakesound;
|
||||
|
@ -54,6 +54,9 @@ DEarthquake::DEarthquake (AActor *center, int intensityX, int intensityY, int in
|
|||
m_WaveSpeedX = (float)waveSpeedX;
|
||||
m_WaveSpeedY = (float)waveSpeedY;
|
||||
m_WaveSpeedZ = (float)waveSpeedZ;
|
||||
m_Falloff = falloff << FRACBITS;
|
||||
m_Highpoint = highpoint;
|
||||
m_MiniCount = highpoint;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -97,6 +100,14 @@ void DEarthquake::Serialize (FArchive &arc)
|
|||
{
|
||||
arc << m_WaveSpeedX << m_WaveSpeedY << m_WaveSpeedZ;
|
||||
}
|
||||
if (SaveVersion < 4534)
|
||||
{
|
||||
m_Falloff = m_Highpoint = m_MiniCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
arc << m_Falloff << m_Highpoint << m_MiniCount;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -122,6 +133,7 @@ void DEarthquake::Tick ()
|
|||
{
|
||||
S_Sound (m_Spot, CHAN_BODY | CHAN_LOOP, m_QuakeSFX, 1, ATTN_NORM);
|
||||
}
|
||||
|
||||
if (m_DamageRadius > 0)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
@ -158,6 +170,8 @@ void DEarthquake::Tick ()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_MiniCount > 0)
|
||||
m_MiniCount--;
|
||||
if (--m_Countdown == 0)
|
||||
{
|
||||
if (S_IsActorPlayingSomething(m_Spot, CHAN_BODY, m_QuakeSFX))
|
||||
|
@ -168,12 +182,18 @@ void DEarthquake::Tick ()
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DEarthquake :: GetModWave
|
||||
//
|
||||
// QF_WAVE converts intensity into amplitude and unlocks a new property, the
|
||||
// wave length. This is, in short, waves per second. Named waveMultiplier
|
||||
// because that's as the name implies: adds more waves per second.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
fixed_t DEarthquake::GetModWave(double waveMultiplier) const
|
||||
{
|
||||
//QF_WAVE converts intensity into amplitude and unlocks a new property, the wave length.
|
||||
//This is, in short, waves per second (full cycles, mind you, from 0 to 360.)
|
||||
//Named waveMultiplier because that's as the name implies: adds more waves per second.
|
||||
|
||||
double time = m_Countdown - FIXED2DBL(r_TicFrac);
|
||||
return FLOAT2FIXED(sin(waveMultiplier * time * (M_PI * 2 / TICRATE)));
|
||||
}
|
||||
|
@ -189,35 +209,95 @@ fixed_t DEarthquake::GetModWave(double waveMultiplier) const
|
|||
fixed_t DEarthquake::GetModIntensity(fixed_t intensity) const
|
||||
{
|
||||
assert(m_CountdownStart >= m_Countdown);
|
||||
|
||||
intensity += intensity; // always doubled
|
||||
|
||||
if (m_Flags & (QF_SCALEDOWN | QF_SCALEUP))
|
||||
{
|
||||
// Adjustable maximums must use a range between 1 and m_CountdownStart to constrain between no quake and full quake.
|
||||
bool check = !!(m_Highpoint > 0 && m_Highpoint < m_CountdownStart);
|
||||
int divider = (check) ? m_Highpoint : m_CountdownStart;
|
||||
int scalar;
|
||||
|
||||
if ((m_Flags & (QF_SCALEDOWN | QF_SCALEUP)) == (QF_SCALEDOWN | QF_SCALEUP))
|
||||
{
|
||||
scalar = (m_Flags & QF_MAX) ? MAX(m_Countdown, m_CountdownStart - m_Countdown)
|
||||
: MIN(m_Countdown, m_CountdownStart - m_Countdown);
|
||||
if (check)
|
||||
{
|
||||
if (m_MiniCount > 0)
|
||||
scalar = (m_Flags & QF_MAX) ? m_MiniCount : (m_Highpoint - m_MiniCount);
|
||||
else
|
||||
{
|
||||
divider = m_CountdownStart - m_Highpoint;
|
||||
scalar = (m_Flags & QF_MAX) ? (divider - m_Countdown) : m_Countdown;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Defaults to middle of the road.
|
||||
divider = m_CountdownStart;
|
||||
scalar = (m_Flags & QF_MAX) ? MAX(m_Countdown, m_CountdownStart - m_Countdown)
|
||||
: MIN(m_Countdown, m_CountdownStart - m_Countdown);
|
||||
}
|
||||
scalar = (scalar > divider) ? divider : scalar;
|
||||
|
||||
if (m_Flags & QF_FULLINTENSITY)
|
||||
{
|
||||
scalar *= 2;
|
||||
}
|
||||
}
|
||||
else if (m_Flags & QF_SCALEDOWN)
|
||||
else
|
||||
{
|
||||
scalar = m_Countdown;
|
||||
}
|
||||
else // QF_SCALEUP
|
||||
{
|
||||
scalar = m_CountdownStart - m_Countdown;
|
||||
}
|
||||
assert(m_CountdownStart > 0);
|
||||
intensity = Scale(intensity, scalar, m_CountdownStart);
|
||||
if (m_Flags & QF_SCALEDOWN)
|
||||
{
|
||||
scalar = m_Countdown;
|
||||
}
|
||||
else // QF_SCALEUP
|
||||
{
|
||||
scalar = m_CountdownStart - m_Countdown;
|
||||
if (m_Highpoint > 0)
|
||||
{
|
||||
if ((m_Highpoint - m_MiniCount) < divider)
|
||||
scalar = m_Highpoint - m_MiniCount;
|
||||
else
|
||||
scalar = divider;
|
||||
}
|
||||
}
|
||||
scalar = (scalar > divider) ? divider : scalar;
|
||||
}
|
||||
assert(divider > 0);
|
||||
intensity = Scale(intensity, scalar, divider);
|
||||
}
|
||||
return intensity;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DEarthquake :: GetFalloff
|
||||
//
|
||||
// Given the distance of the player from the quake, find the multiplier.
|
||||
// Process everything as doubles, and output again as fixed_t (mainly
|
||||
// because fixed_t was misbehaving here...)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
fixed_t DEarthquake::GetFalloff(fixed_t dist) const
|
||||
{
|
||||
if ((dist < m_Falloff) || (m_Falloff >= m_TremorRadius) || (m_Falloff <= 0) || (m_TremorRadius - m_Falloff <= 0))
|
||||
{ //Player inside the minimum falloff range, or safety check kicked in.
|
||||
return FRACUNIT;
|
||||
}
|
||||
else if ((dist > m_Falloff) && (dist < m_TremorRadius))
|
||||
{ //Player inside the radius, and outside the min distance for falloff.
|
||||
fixed_t tremorsize = m_TremorRadius - m_Falloff;
|
||||
assert(tremorsize > 0);
|
||||
return (FRACUNIT - FixedDiv((dist - m_Falloff), tremorsize));
|
||||
}
|
||||
else
|
||||
{ //Shouldn't happen.
|
||||
return FRACUNIT;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DEarthquake::StaticGetQuakeIntensity
|
||||
|
@ -247,12 +327,16 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger
|
|||
fixed_t dist = quake->m_Spot->AproxDistance (victim, true);
|
||||
if (dist < quake->m_TremorRadius)
|
||||
{
|
||||
fixed_t falloff = quake->GetFalloff(dist);
|
||||
++count;
|
||||
fixed_t x = quake->GetModIntensity(quake->m_IntensityX);
|
||||
fixed_t y = quake->GetModIntensity(quake->m_IntensityY);
|
||||
fixed_t z = quake->GetModIntensity(quake->m_IntensityZ);
|
||||
|
||||
|
||||
if (!(quake->m_Flags & QF_WAVE))
|
||||
{
|
||||
jiggers.Falloff = MAX(falloff, jiggers.Falloff);
|
||||
if (quake->m_Flags & QF_RELATIVE)
|
||||
{
|
||||
jiggers.RelIntensityX = MAX(x, jiggers.RelIntensityX);
|
||||
|
@ -268,6 +352,7 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger
|
|||
}
|
||||
else
|
||||
{
|
||||
jiggers.WFalloff = MAX(falloff, jiggers.WFalloff);
|
||||
fixed_t mx = FixedMul(x, quake->GetModWave(quake->m_WaveSpeedX));
|
||||
fixed_t my = FixedMul(y, quake->GetModWave(quake->m_WaveSpeedY));
|
||||
fixed_t mz = FixedMul(z, quake->GetModWave(quake->m_WaveSpeedZ));
|
||||
|
@ -304,7 +389,7 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger
|
|||
|
||||
bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration,
|
||||
int damrad, int tremrad, FSoundID quakesfx, int flags,
|
||||
double waveSpeedX, double waveSpeedY, double waveSpeedZ)
|
||||
double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint)
|
||||
{
|
||||
AActor *center;
|
||||
bool res = false;
|
||||
|
@ -318,7 +403,7 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY,
|
|||
if (activator != NULL)
|
||||
{
|
||||
new DEarthquake(activator, intensityX, intensityY, intensityZ, duration, damrad, tremrad,
|
||||
quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ);
|
||||
quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +414,7 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY,
|
|||
{
|
||||
res = true;
|
||||
new DEarthquake(center, intensityX, intensityY, intensityZ, duration, damrad, tremrad,
|
||||
quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ);
|
||||
quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,6 +422,6 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY,
|
|||
}
|
||||
|
||||
bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx)
|
||||
{ //Maintains original behavior by passing 0 to intensityZ, and flags.
|
||||
return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0);
|
||||
{ //Maintains original behavior by passing 0 to intensityZ, flags, and everything else after QSFX.
|
||||
return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
|
|
@ -157,6 +157,7 @@ struct FQuakeJiggers
|
|||
int RelIntensityX, RelIntensityY, RelIntensityZ;
|
||||
int OffsetX, OffsetY, OffsetZ;
|
||||
int RelOffsetX, RelOffsetY, RelOffsetZ;
|
||||
int Falloff, WFalloff;
|
||||
};
|
||||
|
||||
class DEarthquake : public DThinker
|
||||
|
@ -166,7 +167,7 @@ class DEarthquake : public DThinker
|
|||
public:
|
||||
DEarthquake(AActor *center, int intensityX, int intensityY, int intensityZ, int duration,
|
||||
int damrad, int tremrad, FSoundID quakesfx, int flags,
|
||||
double waveSpeedX, double waveSpeedY, double waveSpeedZ);
|
||||
double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint);
|
||||
|
||||
void Serialize (FArchive &arc);
|
||||
void Tick ();
|
||||
|
@ -178,9 +179,12 @@ public:
|
|||
int m_Flags;
|
||||
fixed_t m_IntensityX, m_IntensityY, m_IntensityZ;
|
||||
float m_WaveSpeedX, m_WaveSpeedY, m_WaveSpeedZ;
|
||||
fixed_t m_Falloff;
|
||||
int m_Highpoint, m_MiniCount;
|
||||
|
||||
fixed_t GetModIntensity(int intensity) const;
|
||||
fixed_t GetModWave(double waveMultiplier) const;
|
||||
fixed_t GetFalloff(fixed_t dist) const;
|
||||
|
||||
static int StaticGetQuakeIntensities(AActor *viewer, FQuakeJiggers &jiggers);
|
||||
|
||||
|
|
|
@ -206,67 +206,31 @@ class SBarInfoCommandFlowControl : public SBarInfoCommand
|
|||
{
|
||||
public:
|
||||
SBarInfoCommandFlowControl(SBarInfo *script) : SBarInfoCommand(script), truth(false) {}
|
||||
~SBarInfoCommandFlowControl()
|
||||
{
|
||||
for(unsigned int i = 0;i < 2;i++)
|
||||
{
|
||||
for(unsigned int j = 0;j < commands[i].Size();j++)
|
||||
delete commands[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar)
|
||||
{
|
||||
for(unsigned int i = 0;i < commands[truth].Size();i++)
|
||||
commands[truth][i]->Draw(block, statusBar);
|
||||
for(auto command : commands[truth])
|
||||
command->Draw(block, statusBar);
|
||||
}
|
||||
int NumCommands() const { return commands[truth].Size(); }
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
bool elseBlock = false;
|
||||
SBarInfoCommand *cmd = NULL;
|
||||
// Should loop no more than twice.
|
||||
while(true)
|
||||
{
|
||||
if(sc.CheckToken('{'))
|
||||
{
|
||||
while((cmd = NextCommand(sc)) != NULL)
|
||||
{
|
||||
cmd->Parse(sc, fullScreenOffsets);
|
||||
commands[!elseBlock].Push(cmd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((cmd = NextCommand(sc)) != NULL)
|
||||
{
|
||||
cmd->Parse(sc, fullScreenOffsets);
|
||||
commands[!elseBlock].Push(cmd);
|
||||
}
|
||||
else
|
||||
sc.ScriptError("Missing command for flow control statement.");
|
||||
}
|
||||
|
||||
if(!elseBlock && sc.CheckToken(TK_Else))
|
||||
{
|
||||
elseBlock = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ParseBlock(commands[1], sc, fullScreenOffsets);
|
||||
if(sc.CheckToken(TK_Else))
|
||||
ParseBlock(commands[0], sc, fullScreenOffsets);
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
for(unsigned int i = 0;i < 2;i++)
|
||||
{
|
||||
for(unsigned int j = 0;j < commands[i].Size();j++)
|
||||
commands[i][j]->Reset();
|
||||
for(auto command : commands[i])
|
||||
command->Reset();
|
||||
}
|
||||
}
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
for(unsigned int i = 0;i < commands[truth].Size();i++)
|
||||
commands[truth][i]->Tick(block, statusBar, hudChanged);
|
||||
for(auto command : commands[truth])
|
||||
command->Tick(block, statusBar, hudChanged);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -283,11 +247,65 @@ class SBarInfoCommandFlowControl : public SBarInfoCommand
|
|||
Tick(block, statusBar, true);
|
||||
}
|
||||
|
||||
void Negate()
|
||||
{
|
||||
swapvalues(commands[0], commands[1]);
|
||||
}
|
||||
|
||||
private:
|
||||
void ParseBlock(TDeletingArray<SBarInfoCommand *> &commands, FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
if(sc.CheckToken('{'))
|
||||
{
|
||||
while(SBarInfoCommand *cmd = NextCommand(sc))
|
||||
{
|
||||
cmd->Parse(sc, fullScreenOffsets);
|
||||
commands.Push(cmd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(SBarInfoCommand *cmd = NextCommand(sc))
|
||||
{
|
||||
cmd->Parse(sc, fullScreenOffsets);
|
||||
commands.Push(cmd);
|
||||
}
|
||||
else
|
||||
sc.ScriptError("Missing command for flow control statement.");
|
||||
}
|
||||
}
|
||||
|
||||
SBarInfoCommand *NextCommand(FScanner &sc);
|
||||
|
||||
bool truth;
|
||||
TArray<SBarInfoCommand *> commands[2];
|
||||
TDeletingArray<SBarInfoCommand *> commands[2];
|
||||
bool truth;
|
||||
};
|
||||
|
||||
class SBarInfoNegatableFlowControl : public SBarInfoCommandFlowControl
|
||||
{
|
||||
public:
|
||||
SBarInfoNegatableFlowControl(SBarInfo *script) : SBarInfoCommandFlowControl(script) {}
|
||||
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
bool negate = false;
|
||||
if(sc.CheckToken(TK_Identifier))
|
||||
{
|
||||
if(sc.Compare("not"))
|
||||
negate = true;
|
||||
else
|
||||
sc.UnGet();
|
||||
}
|
||||
|
||||
ParseNegatable(sc, fullScreenOffsets);
|
||||
|
||||
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
|
||||
|
||||
if(negate)
|
||||
Negate();
|
||||
}
|
||||
|
||||
virtual void ParseNegatable(FScanner &sc, bool fullScreenOffsets) {}
|
||||
};
|
||||
|
||||
class SBarInfoMainBlock : public SBarInfoCommandFlowControl
|
||||
|
|
|
@ -1830,33 +1830,17 @@ const char* const CommandGameMode::modeNames[] =
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CommandUsesAmmo : public SBarInfoCommandFlowControl
|
||||
class CommandUsesAmmo : public SBarInfoNegatableFlowControl
|
||||
{
|
||||
public:
|
||||
CommandUsesAmmo(SBarInfo *script) : SBarInfoCommandFlowControl(script),
|
||||
negate(false)
|
||||
{
|
||||
}
|
||||
CommandUsesAmmo(SBarInfo *script) : SBarInfoNegatableFlowControl(script) {}
|
||||
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
if(sc.CheckToken(TK_Identifier))
|
||||
{
|
||||
if(sc.Compare("not"))
|
||||
negate = true;
|
||||
else
|
||||
sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String);
|
||||
}
|
||||
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
|
||||
}
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
|
||||
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
SetTruth((statusBar->CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL)) ^ negate, block, statusBar);
|
||||
SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL), block, statusBar);
|
||||
}
|
||||
protected:
|
||||
bool negate;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1872,7 +1856,7 @@ class CommandUsesSecondaryAmmo : public CommandUsesAmmo
|
|||
{
|
||||
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
SetTruth((statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2) ^ negate, block, statusBar);
|
||||
SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2, block, statusBar);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2890,28 +2874,18 @@ class CommandDrawBar : public SBarInfoCommand
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CommandIsSelected : public SBarInfoCommandFlowControl
|
||||
class CommandIsSelected : public SBarInfoNegatableFlowControl
|
||||
{
|
||||
public:
|
||||
CommandIsSelected(SBarInfo *script) : SBarInfoCommandFlowControl(script),
|
||||
negate(false)
|
||||
CommandIsSelected(SBarInfo *script) : SBarInfoNegatableFlowControl(script)
|
||||
{
|
||||
weapon[0] = NULL;
|
||||
weapon[1] = NULL;
|
||||
}
|
||||
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
void ParseNegatable(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
if(sc.CheckToken(TK_Identifier))
|
||||
{
|
||||
if(sc.Compare("not"))
|
||||
{
|
||||
negate = true;
|
||||
if(!sc.CheckToken(TK_StringConst))
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
if(!sc.CheckToken(TK_Identifier))
|
||||
sc.MustGetToken(TK_StringConst);
|
||||
for(int i = 0;i < 2;i++)
|
||||
{
|
||||
|
@ -2930,24 +2904,18 @@ class CommandIsSelected : public SBarInfoCommandFlowControl
|
|||
else
|
||||
break;
|
||||
}
|
||||
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
|
||||
}
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
|
||||
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
if(statusBar->CPlayer->ReadyWeapon != NULL)
|
||||
{
|
||||
const PClass *readyWeapon = statusBar->CPlayer->ReadyWeapon->GetClass();
|
||||
SetTruth(((weapon[1] != NULL) &&
|
||||
((negate && (weapon[0] != readyWeapon && weapon[1] != readyWeapon)) ||
|
||||
(!negate && (weapon[0] == readyWeapon || weapon[1] == readyWeapon)))) ||
|
||||
((weapon[1] == NULL) &&
|
||||
((!negate && weapon[0] == readyWeapon) || (negate && weapon[0] != readyWeapon))), block, statusBar);
|
||||
SetTruth(weapon[0] == readyWeapon || (weapon[1] && weapon[1] == readyWeapon), block, statusBar);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
bool negate;
|
||||
const PClass *weapon[2];
|
||||
};
|
||||
|
||||
|
@ -3245,26 +3213,20 @@ FRandom CommandDrawGem::pr_chainwiggle; //use the same method of chain wiggling
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CommandWeaponAmmo : public SBarInfoCommandFlowControl
|
||||
class CommandWeaponAmmo : public SBarInfoNegatableFlowControl
|
||||
{
|
||||
public:
|
||||
CommandWeaponAmmo(SBarInfo *script) : SBarInfoCommandFlowControl(script),
|
||||
conditionAnd(false), negate(false)
|
||||
CommandWeaponAmmo(SBarInfo *script) : SBarInfoNegatableFlowControl(script),
|
||||
conditionAnd(false)
|
||||
{
|
||||
ammo[0] = NULL;
|
||||
ammo[1] = NULL;
|
||||
}
|
||||
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
void ParseNegatable(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
if(!sc.CheckToken(TK_StringConst))
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
if(sc.Compare("not") && sc.TokenType == TK_Identifier)
|
||||
{
|
||||
negate = true;
|
||||
if(!sc.CheckToken(TK_StringConst))
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
}
|
||||
for(int i = 0;i < 2;i++)
|
||||
{
|
||||
ammo[i] = PClass::FindClass(sc.String);
|
||||
|
@ -3289,11 +3251,10 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl
|
|||
else
|
||||
break;
|
||||
}
|
||||
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
|
||||
}
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
|
||||
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
if(statusBar->CPlayer->ReadyWeapon != NULL)
|
||||
{
|
||||
|
@ -3301,41 +3262,25 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl
|
|||
const PClass *AmmoType2 = statusBar->CPlayer->ReadyWeapon->AmmoType2;
|
||||
bool usesammo1 = (AmmoType1 != NULL);
|
||||
bool usesammo2 = (AmmoType2 != NULL);
|
||||
if(negate && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble.
|
||||
{
|
||||
SetTruth(true, block, statusBar);
|
||||
return;
|
||||
}
|
||||
//if(!usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble.
|
||||
//{
|
||||
// SetTruth(false, block, statusBar);
|
||||
// return;
|
||||
//}
|
||||
//Or means only 1 ammo type needs to match and means both need to match.
|
||||
if(ammo[1] != NULL)
|
||||
{
|
||||
bool match1 = ((usesammo1 && (AmmoType1 == ammo[0] || AmmoType1 == ammo[1])) || !usesammo1);
|
||||
bool match2 = ((usesammo2 && (AmmoType2 == ammo[0] || AmmoType2 == ammo[1])) || !usesammo2);
|
||||
if((!conditionAnd && (match1 || match2)) || (conditionAnd && (match1 && match2)))
|
||||
{
|
||||
if(!negate)
|
||||
{
|
||||
SetTruth(true, block, statusBar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(negate)
|
||||
{
|
||||
SetTruth(true, block, statusBar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else //Every thing here could probably be one long if statement but then it would be more confusing.
|
||||
else
|
||||
{
|
||||
if((usesammo1 && (AmmoType1 == ammo[0])) || (usesammo2 && (AmmoType2 == ammo[0])))
|
||||
{
|
||||
if(!negate)
|
||||
{
|
||||
SetTruth(true, block, statusBar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(negate)
|
||||
{
|
||||
SetTruth(true, block, statusBar);
|
||||
return;
|
||||
|
@ -3345,33 +3290,26 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl
|
|||
SetTruth(false, block, statusBar);
|
||||
}
|
||||
protected:
|
||||
bool conditionAnd;
|
||||
bool negate;
|
||||
const PClass *ammo[2];
|
||||
bool conditionAnd;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CommandInInventory : public SBarInfoCommandFlowControl
|
||||
class CommandInInventory : public SBarInfoNegatableFlowControl
|
||||
{
|
||||
public:
|
||||
CommandInInventory(SBarInfo *script) : SBarInfoCommandFlowControl(script),
|
||||
conditionAnd(false), negate(false)
|
||||
CommandInInventory(SBarInfo *script) : SBarInfoNegatableFlowControl(script),
|
||||
conditionAnd(false)
|
||||
{
|
||||
item[0] = item[1] = NULL;
|
||||
amount[0] = amount[1] = 0;
|
||||
}
|
||||
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
void ParseNegatable(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
if(!sc.CheckToken(TK_StringConst))
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
if(sc.Compare("not") && sc.TokenType == TK_Identifier)
|
||||
{
|
||||
negate = true;
|
||||
if(!sc.CheckToken(TK_StringConst))
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
}
|
||||
for(int i = 0;i < 2;i++)
|
||||
{
|
||||
item[i] = PClass::FindActor(sc.String);
|
||||
|
@ -3402,11 +3340,10 @@ class CommandInInventory : public SBarInfoCommandFlowControl
|
|||
else
|
||||
break;
|
||||
}
|
||||
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
|
||||
}
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
|
||||
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
AInventory *invItem[2] = { statusBar->CPlayer->mo->FindInventory(item[0]), statusBar->CPlayer->mo->FindInventory(item[1]) };
|
||||
if (invItem[0] != NULL && amount[0] > 0 && invItem[0]->Amount < amount[0]) invItem[0] = NULL;
|
||||
|
@ -3415,16 +3352,15 @@ class CommandInInventory : public SBarInfoCommandFlowControl
|
|||
if (item[1])
|
||||
{
|
||||
if (conditionAnd)
|
||||
SetTruth((invItem[0] && invItem[1]) != negate, block, statusBar);
|
||||
SetTruth(invItem[0] && invItem[1], block, statusBar);
|
||||
else
|
||||
SetTruth((invItem[0] || invItem[1]) != negate, block, statusBar);
|
||||
SetTruth(invItem[0] || invItem[1], block, statusBar);
|
||||
}
|
||||
else
|
||||
SetTruth((invItem[0] != NULL) != negate, block, statusBar);
|
||||
SetTruth(invItem[0] != NULL, block, statusBar);
|
||||
}
|
||||
protected:
|
||||
bool conditionAnd;
|
||||
bool negate;
|
||||
PClassActor *item[2];
|
||||
int amount[2];
|
||||
};
|
||||
|
@ -3459,42 +3395,48 @@ class CommandAlpha : public SBarInfoMainBlock
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CommandIfHealth : public SBarInfoCommandFlowControl
|
||||
class CommandIfHealth : public SBarInfoNegatableFlowControl
|
||||
{
|
||||
public:
|
||||
CommandIfHealth(SBarInfo *script) : SBarInfoCommandFlowControl(script),
|
||||
negate(false), percentage(false)
|
||||
CommandIfHealth(SBarInfo *script) : SBarInfoNegatableFlowControl(script),
|
||||
percentage(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Parse(FScanner &sc, bool fullScreenOffsets)
|
||||
void ParseNegatable(FScanner &sc, bool fullScreenOffsets)
|
||||
{
|
||||
if (sc.CheckToken(TK_Identifier))
|
||||
{
|
||||
if (sc.Compare("not"))
|
||||
negate = true;
|
||||
else
|
||||
sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String);
|
||||
}
|
||||
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
percentage = sc.CheckToken('%');
|
||||
hpamount = sc.Number;
|
||||
|
||||
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
|
||||
}
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
|
||||
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
int phealth = percentage ? statusBar->CPlayer->mo->health * 100 / statusBar->CPlayer->mo->GetMaxHealth() : statusBar->CPlayer->mo->health;
|
||||
|
||||
SetTruth((phealth >= hpamount) ^ negate, block, statusBar);
|
||||
SetTruth(phealth >= hpamount, block, statusBar);
|
||||
}
|
||||
protected:
|
||||
bool negate;
|
||||
bool percentage;
|
||||
int hpamount;
|
||||
bool percentage;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CommandIfInvulnerable : public SBarInfoNegatableFlowControl
|
||||
{
|
||||
public:
|
||||
CommandIfInvulnerable(SBarInfo *script) : SBarInfoNegatableFlowControl(script)
|
||||
{
|
||||
}
|
||||
|
||||
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
|
||||
{
|
||||
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
|
||||
|
||||
SetTruth((statusBar->CPlayer->mo->flags2 & MF2_INVULNERABLE) || (statusBar->CPlayer->cheats & (CF_GODMODE | CF_GODMODE2)), block, statusBar);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -3509,6 +3451,7 @@ static const char *SBarInfoCommandNames[] =
|
|||
"isselected", "usesammo", "usessecondaryammo",
|
||||
"hasweaponpiece", "inventorybarnotvisible",
|
||||
"weaponammo", "ininventory", "alpha", "ifhealth",
|
||||
"ifinvulnerable",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -3522,6 +3465,7 @@ enum SBarInfoCommands
|
|||
SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO,
|
||||
SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE,
|
||||
SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH,
|
||||
SBARINFO_IFINVULNERABLE,
|
||||
};
|
||||
|
||||
SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc)
|
||||
|
@ -3555,6 +3499,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc)
|
|||
case SBARINFO_ININVENTORY: return new CommandInInventory(script);
|
||||
case SBARINFO_ALPHA: return new CommandAlpha(script);
|
||||
case SBARINFO_IFHEALTH: return new CommandIfHealth(script);
|
||||
case SBARINFO_IFINVULNERABLE: return new CommandIfInvulnerable(script);
|
||||
}
|
||||
|
||||
sc.ScriptError("Unknown command '%s'.\n", sc.String);
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "v_palette.h"
|
||||
#include "d_player.h"
|
||||
#include "farchive.h"
|
||||
#include "a_hexenglobal.h"
|
||||
#include "gstrings.h"
|
||||
|
||||
#include "../version.h"
|
||||
|
|
|
@ -714,6 +714,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade)
|
|||
return 0;
|
||||
|
||||
P_SetPsprite (player, ps_flash, flash);
|
||||
self->player->psprites[ps_flash].processPending = true;
|
||||
|
||||
if (grenadetype != NULL)
|
||||
{
|
||||
|
|
|
@ -4529,12 +4529,12 @@ int DLevelScript::LineFromID(int id)
|
|||
}
|
||||
}
|
||||
|
||||
bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType *&type)
|
||||
bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType *&type, bool readonly)
|
||||
{
|
||||
PField *var = dyn_cast<PField>(self->GetClass()->Symbols.FindSymbol(varname, true));
|
||||
PArray *arraytype;
|
||||
|
||||
if (var == NULL || (var->Flags & VARF_Native))
|
||||
if (var == NULL || (!readonly && (var->Flags & VARF_Native)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -4557,6 +4557,17 @@ bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType *
|
|||
return false;
|
||||
}
|
||||
addr = baddr;
|
||||
// We don't want Int subclasses like Name or Color to be accessible,
|
||||
// but we do want to support Float subclasses like Fixed.
|
||||
if (!type->IsA(RUNTIME_CLASS(PInt)) && !type->IsKindOf(RUNTIME_CLASS(PFloat)))
|
||||
{
|
||||
// For reading, we also support Name and String types.
|
||||
if (readonly && (type->IsA(RUNTIME_CLASS(PName)) || type->IsA(RUNTIME_CLASS(PString))))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4565,9 +4576,16 @@ static void SetUserVariable(AActor *self, FName varname, int index, int value)
|
|||
void *addr;
|
||||
PType *type;
|
||||
|
||||
if (GetVarAddrType(self, varname, index, addr, type))
|
||||
if (GetVarAddrType(self, varname, index, addr, type, false))
|
||||
{
|
||||
type->SetValue(addr, value);
|
||||
if (!type->IsKindOf(RUNTIME_CLASS(PFloat)))
|
||||
{
|
||||
type->SetValue(addr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
type->SetValue(addr, FIXED2DBL(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4576,9 +4594,24 @@ static int GetUserVariable(AActor *self, FName varname, int index)
|
|||
void *addr;
|
||||
PType *type;
|
||||
|
||||
if (GetVarAddrType(self, varname, index, addr, type))
|
||||
if (GetVarAddrType(self, varname, index, addr, type, true))
|
||||
{
|
||||
return type->GetValueInt(addr);
|
||||
if (type->IsKindOf(RUNTIME_CLASS(PFloat)))
|
||||
{
|
||||
return FLOAT2FIXED(type->GetValueFloat(addr));
|
||||
}
|
||||
else if (type->IsA(RUNTIME_CLASS(PName)))
|
||||
{
|
||||
return GlobalACSStrings.AddString(FName(ENamedName(type->GetValueInt(addr))).GetChars());
|
||||
}
|
||||
else if (type->IsA(RUNTIME_CLASS(PString)))
|
||||
{
|
||||
return GlobalACSStrings.AddString(*(FString *)addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return type->GetValueInt(addr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -5747,7 +5780,9 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
|
|||
argCount > 8 ? args[8] : 0,
|
||||
argCount > 9 ? FIXED2DBL(args[9]) : 1.0,
|
||||
argCount > 10 ? FIXED2DBL(args[10]) : 1.0,
|
||||
argCount > 11 ? FIXED2DBL(args[11]) : 1.0 );
|
||||
argCount > 11 ? FIXED2DBL(args[11]) : 1.0,
|
||||
argCount > 12 ? args[12] : 0,
|
||||
argCount > 13 ? args[13] : 0);
|
||||
}
|
||||
|
||||
case ACSF_SetLineActivation:
|
||||
|
|
|
@ -46,6 +46,14 @@ inline FArchive &operator<< (FArchive &arc, DCeiling::ECeiling &type)
|
|||
return arc;
|
||||
}
|
||||
|
||||
inline FArchive &operator<< (FArchive &arc, DCeiling::ECrushMode &type)
|
||||
{
|
||||
BYTE val = (BYTE)type;
|
||||
arc << val;
|
||||
type = (DCeiling::ECrushMode)val;
|
||||
return arc;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// CEILINGS
|
||||
|
@ -80,7 +88,7 @@ void DCeiling::Serialize (FArchive &arc)
|
|||
<< m_NewSpecial
|
||||
<< m_Tag
|
||||
<< m_OldDirection
|
||||
<< m_Hexencrush;
|
||||
<< m_CrushMode;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
@ -159,7 +167,7 @@ void DCeiling::Tick ()
|
|||
|
||||
case -1:
|
||||
// DOWN
|
||||
res = MoveCeiling (m_Speed, m_BottomHeight, m_Crush, m_Direction, m_Hexencrush);
|
||||
res = MoveCeiling (m_Speed, m_BottomHeight, m_Crush, m_Direction, m_CrushMode == ECrushMode::crushHexen);
|
||||
|
||||
if (res == pastdest)
|
||||
{
|
||||
|
@ -196,7 +204,7 @@ void DCeiling::Tick ()
|
|||
{
|
||||
case ceilCrushAndRaise:
|
||||
case ceilLowerAndCrush:
|
||||
if (m_Speed1 == FRACUNIT && m_Speed2 == FRACUNIT)
|
||||
if (m_CrushMode == ECrushMode::crushSlowdown)
|
||||
m_Speed = FRACUNIT / 8;
|
||||
break;
|
||||
|
||||
|
@ -224,7 +232,7 @@ DCeiling::DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent)
|
|||
: DMovingCeiling (sec)
|
||||
{
|
||||
m_Crush = -1;
|
||||
m_Hexencrush = false;
|
||||
m_CrushMode = ECrushMode::crushDoom;
|
||||
m_Speed = m_Speed1 = speed1;
|
||||
m_Speed2 = speed2;
|
||||
m_Silent = silent;
|
||||
|
@ -238,7 +246,7 @@ DCeiling::DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent)
|
|||
|
||||
DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag,
|
||||
fixed_t speed, fixed_t speed2, fixed_t height,
|
||||
int crush, int silent, int change, bool hexencrush)
|
||||
int crush, int silent, int change, ECrushMode hexencrush)
|
||||
{
|
||||
fixed_t targheight = 0; // Silence, GCC
|
||||
|
||||
|
@ -387,7 +395,7 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line,
|
|||
ceiling->m_Tag = tag;
|
||||
ceiling->m_Type = type;
|
||||
ceiling->m_Crush = crush;
|
||||
ceiling->m_Hexencrush = hexencrush;
|
||||
ceiling->m_CrushMode = hexencrush;
|
||||
|
||||
// Do not interpolate instant movement ceilings.
|
||||
// Note for ZDoomGL: Check to make sure that you update the sector
|
||||
|
@ -476,7 +484,7 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line,
|
|||
|
||||
bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line,
|
||||
int tag, fixed_t speed, fixed_t speed2, fixed_t height,
|
||||
int crush, int silent, int change, bool hexencrush)
|
||||
int crush, int silent, int change, DCeiling::ECrushMode hexencrush)
|
||||
{
|
||||
int secnum;
|
||||
bool rtn;
|
||||
|
|
|
@ -47,8 +47,7 @@
|
|||
|
||||
#include "b_bot.h" //Added by MC:
|
||||
|
||||
#include "ravenshared.h"
|
||||
#include "a_hexenglobal.h"
|
||||
#include "d_player.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "a_pickups.h"
|
||||
#include "gi.h"
|
||||
|
|
252
src/p_lnspec.cpp
252
src/p_lnspec.cpp
|
@ -88,9 +88,21 @@ int LS_SetGlobalFogParameter(line_t *ln, AActor *it, bool backSide, int arg0, in
|
|||
#define OCTICS(a) (((a)*TICRATE)/8)
|
||||
#define BYTEANGLE(a) ((angle_t)((a)<<24))
|
||||
#define CRUSH(a) ((a) > 0? (a) : -1)
|
||||
#define CRUSHTYPE(a) ((a)==1? false : (a)==2? true : gameinfo.gametype == GAME_Hexen)
|
||||
#define CHANGE(a) (((a) >= 0 && (a)<=7)? ChangeMap[a]:0)
|
||||
|
||||
static bool CRUSHTYPE(int a)
|
||||
{
|
||||
return ((a) == 1 ? false : (a) == 2 ? true : gameinfo.gametype == GAME_Hexen);
|
||||
}
|
||||
|
||||
static DCeiling::ECrushMode CRUSHTYPE(int a, bool withslowdown)
|
||||
{
|
||||
static DCeiling::ECrushMode map[] = { DCeiling::ECrushMode::crushDoom, DCeiling::ECrushMode::crushHexen, DCeiling::ECrushMode::crushSlowdown };
|
||||
if (a >= 1 && a <= 3) return map[a - 1];
|
||||
if (gameinfo.gametype == GAME_Hexen) return DCeiling::ECrushMode::crushHexen;
|
||||
return withslowdown? DCeiling::ECrushMode::crushSlowdown : DCeiling::ECrushMode::crushDoom;
|
||||
}
|
||||
|
||||
static FRandom pr_glass ("GlassBreak");
|
||||
|
||||
// There are aliases for the ACS specials that take names instead of numbers.
|
||||
|
@ -632,43 +644,43 @@ FUNC(LS_Pillar_Open)
|
|||
FUNC(LS_Ceiling_LowerByValue)
|
||||
// Ceiling_LowerByValue (tag, speed, height, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseByValue)
|
||||
// Ceiling_RaiseByValue (tag, speed, height, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3), false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerByValueTimes8)
|
||||
// Ceiling_LowerByValueTimes8 (tag, speed, height, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseByValueTimes8)
|
||||
// Ceiling_RaiseByValueTimes8 (tag, speed, height, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3), false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushAndRaise)
|
||||
// Ceiling_CrushAndRaise (tag, speed, crush, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3, false));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerAndCrush)
|
||||
// Ceiling_LowerAndCrush (tag, speed, crush, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3));
|
||||
return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3, arg1 == 8));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerAndCrushDist)
|
||||
// Ceiling_LowerAndCrush (tag, speed, crush, dist, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4, arg1 == 8));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushStop)
|
||||
|
@ -680,141 +692,141 @@ FUNC(LS_Ceiling_CrushStop)
|
|||
FUNC(LS_Ceiling_CrushRaiseAndStay)
|
||||
// Ceiling_CrushRaiseAndStay (tag, speed, crush, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3, false));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_MoveToValueTimes8)
|
||||
// Ceiling_MoveToValueTimes8 (tag, speed, height, negative, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilMoveToValue, ln, arg0, SPEED(arg1), 0,
|
||||
arg2*FRACUNIT*8*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4), false);
|
||||
arg2*FRACUNIT*8*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_MoveToValue)
|
||||
// Ceiling_MoveToValue (tag, speed, height, negative, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilMoveToValue, ln, arg0, SPEED(arg1), 0,
|
||||
arg2*FRACUNIT*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4), false);
|
||||
arg2*FRACUNIT*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerToHighestFloor)
|
||||
// Ceiling_LowerToHighestFloor (tag, speed, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerInstant)
|
||||
// Ceiling_LowerInstant (tag, unused, height, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, CRUSH(arg4), 0, CHANGE(arg3), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, CRUSH(arg4), 0, CHANGE(arg3));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseInstant)
|
||||
// Ceiling_RaiseInstant (tag, unused, height, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3), false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushRaiseAndStayA)
|
||||
// Ceiling_CrushRaiseAndStayA (tag, dnspeed, upspeed, damage, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4, false));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushRaiseAndStaySilA)
|
||||
// Ceiling_CrushRaiseAndStaySilA (tag, dnspeed, upspeed, damage, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4, false));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushAndRaiseA)
|
||||
// Ceiling_CrushAndRaiseA (tag, dnspeed, upspeed, damage, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4, arg1 == 8 && arg2 == 8));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushAndRaiseDist)
|
||||
// Ceiling_CrushAndRaiseDist (tag, dist, speed, damage, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4, arg2 == 8));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushAndRaiseSilentA)
|
||||
// Ceiling_CrushAndRaiseSilentA (tag, dnspeed, upspeed, damage, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4, arg1 == 8 && arg2 == 8));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_CrushAndRaiseSilentDist)
|
||||
// Ceiling_CrushAndRaiseSilentDist (tag, dist, upspeed, damage, crushtype)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 1, 0, CRUSHTYPE(arg4));
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 1, 0, CRUSHTYPE(arg4, arg2 == 8));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseToNearest)
|
||||
// Ceiling_RaiseToNearest (tag, speed, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0);
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseToHighest)
|
||||
// Ceiling_RaiseToHighest (tag, speed, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0);
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseToLowest)
|
||||
// Ceiling_RaiseToLowest (tag, speed, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToLowest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToLowest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0);
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseToHighestFloor)
|
||||
// Ceiling_RaiseToHighestFloor (tag, speed, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0);
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_RaiseByTexture)
|
||||
// Ceiling_RaiseByTexture (tag, speed, change)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseByTexture, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseByTexture, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0);
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerToLowest)
|
||||
// Ceiling_LowerToLowest (tag, speed, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerToNearest)
|
||||
// Ceiling_LowerToNearest (tag, speed, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToNearest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToNearest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_ToHighestInstant)
|
||||
// Ceiling_ToHighestInstant (tag, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToHighest, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToHighest, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_ToFloorInstant)
|
||||
// Ceiling_ToFloorInstant (tag, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToFloor, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1), false);
|
||||
return EV_DoCeiling (DCeiling::ceilRaiseToFloor, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerToFloor)
|
||||
// Ceiling_LowerToFloor (tag, speed, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerToFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4));
|
||||
}
|
||||
|
||||
FUNC(LS_Ceiling_LowerByTexture)
|
||||
// Ceiling_LowerByTexture (tag, speed, change, crush)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilLowerByTexture, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4), false);
|
||||
return EV_DoCeiling (DCeiling::ceilLowerByTexture, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4));
|
||||
}
|
||||
|
||||
FUNC(LS_Generic_Ceiling)
|
||||
|
@ -845,14 +857,14 @@ FUNC(LS_Generic_Ceiling)
|
|||
}
|
||||
|
||||
return EV_DoCeiling (type, ln, arg0, SPEED(arg1), SPEED(arg1), arg2*FRACUNIT,
|
||||
(arg4 & 16) ? 20 : -1, 0, arg4 & 7, false);
|
||||
(arg4 & 16) ? 20 : -1, 0, arg4 & 7);
|
||||
}
|
||||
|
||||
FUNC(LS_Generic_Crusher)
|
||||
// Generic_Crusher (tag, dnspeed, upspeed, silent, damage)
|
||||
{
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1),
|
||||
SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, false);
|
||||
SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, (arg1 <= 24 && arg2 <= 24)? DCeiling::ECrushMode::crushSlowdown : DCeiling::ECrushMode::crushDoom);
|
||||
}
|
||||
|
||||
FUNC(LS_Generic_Crusher2)
|
||||
|
@ -860,7 +872,7 @@ FUNC(LS_Generic_Crusher2)
|
|||
{
|
||||
// same as above but uses Hexen's crushing method.
|
||||
return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1),
|
||||
SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, true);
|
||||
SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, DCeiling::ECrushMode::crushHexen);
|
||||
}
|
||||
|
||||
FUNC(LS_Plat_PerpetualRaise)
|
||||
|
@ -1948,7 +1960,7 @@ FUNC(LS_FloorAndCeiling_RaiseByValue)
|
|||
FUNC(LS_FloorAndCeiling_LowerRaise)
|
||||
// FloorAndCeiling_LowerRaise (tag, fspeed, cspeed, boomemu)
|
||||
{
|
||||
bool res = EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0, false);
|
||||
bool res = EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0);
|
||||
// The switch based Boom equivalents of FloorandCeiling_LowerRaise do incorrect checks
|
||||
// which cause the floor only to move when the ceiling fails to do so.
|
||||
// To avoid problems with maps that have incorrect args this only uses a
|
||||
|
@ -2137,51 +2149,9 @@ FUNC(LS_Sector_ChangeFlags)
|
|||
return rtn;
|
||||
}
|
||||
|
||||
struct FThinkerCollection
|
||||
{
|
||||
int RefNum;
|
||||
DThinker *Obj;
|
||||
};
|
||||
|
||||
static TArray<FThinkerCollection> Collection;
|
||||
|
||||
void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type)
|
||||
{
|
||||
// Find pushers already attached to the sector, and change their parameters.
|
||||
{
|
||||
TThinkerIterator<DPusher> iterator;
|
||||
FThinkerCollection collect;
|
||||
|
||||
while ( (collect.Obj = iterator.Next ()) )
|
||||
{
|
||||
if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch (type, tag)) >= 0)
|
||||
{
|
||||
((DPusher *)collect.Obj)->ChangeValues (magnitude, angle);
|
||||
Collection.Push (collect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t numcollected = Collection.Size ();
|
||||
int secnum;
|
||||
|
||||
// Now create pushers for any sectors that don't already have them.
|
||||
FSectorTagIterator itr(tag);
|
||||
while ((secnum = itr.Next()) >= 0)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < numcollected; i++)
|
||||
{
|
||||
if (Collection[i].RefNum == sectors[secnum].sectornum)
|
||||
break;
|
||||
}
|
||||
if (i == numcollected)
|
||||
{
|
||||
new DPusher (type, NULL, magnitude, angle, NULL, secnum);
|
||||
}
|
||||
}
|
||||
Collection.Clear ();
|
||||
}
|
||||
void AdjustPusher(int tag, int magnitude, int angle, bool wind);
|
||||
|
||||
FUNC(LS_Sector_SetWind)
|
||||
// Sector_SetWind (tag, amount, angle)
|
||||
|
@ -2189,7 +2159,7 @@ FUNC(LS_Sector_SetWind)
|
|||
if (arg3)
|
||||
return false;
|
||||
|
||||
AdjustPusher (arg0, arg1, arg2, DPusher::p_wind);
|
||||
AdjustPusher (arg0, arg1, arg2, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2199,7 +2169,7 @@ FUNC(LS_Sector_SetCurrent)
|
|||
if (arg3)
|
||||
return false;
|
||||
|
||||
AdjustPusher (arg0, arg1, arg2, DPusher::p_current);
|
||||
AdjustPusher (arg0, arg1, arg2, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2241,76 +2211,9 @@ FUNC(LS_Sector_SetLink)
|
|||
return false;
|
||||
}
|
||||
|
||||
void SetWallScroller(int id, int sidechoice, fixed_t dx, fixed_t dy, EScrollPos Where);
|
||||
void SetScroller(int tag, EScroll type, fixed_t dx, fixed_t dy);
|
||||
|
||||
static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int Where)
|
||||
{
|
||||
Where &=7;
|
||||
if (Where == 0) return;
|
||||
|
||||
if ((dx | dy) == 0)
|
||||
{
|
||||
// Special case: Remove the scroller, because the deltas are both 0.
|
||||
TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
|
||||
DScroller *scroller;
|
||||
|
||||
while ( (scroller = iterator.Next ()) )
|
||||
{
|
||||
int wallnum = scroller->GetWallNum ();
|
||||
|
||||
if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) &&
|
||||
int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum &&
|
||||
Where == scroller->GetScrollParts())
|
||||
{
|
||||
scroller->Destroy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find scrollers already attached to the matching walls, and change
|
||||
// their rates.
|
||||
{
|
||||
TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
|
||||
FThinkerCollection collect;
|
||||
|
||||
while ( (collect.Obj = iterator.Next ()) )
|
||||
{
|
||||
if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 &&
|
||||
tagManager.LineHasID(sides[collect.RefNum].linedef, id) &&
|
||||
int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum &&
|
||||
Where == ((DScroller *)collect.Obj)->GetScrollParts())
|
||||
{
|
||||
((DScroller *)collect.Obj)->SetRate (dx, dy);
|
||||
Collection.Push (collect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t numcollected = Collection.Size ();
|
||||
int linenum;
|
||||
|
||||
// Now create scrollers for any walls that don't already have them.
|
||||
FLineIdIterator itr(id);
|
||||
while ((linenum = itr.Next()) >= 0)
|
||||
{
|
||||
if (lines[linenum].sidedef[sidechoice] != NULL)
|
||||
{
|
||||
int sidenum = int(lines[linenum].sidedef[sidechoice] - sides);
|
||||
unsigned int i;
|
||||
for (i = 0; i < numcollected; i++)
|
||||
{
|
||||
if (Collection[i].RefNum == sidenum)
|
||||
break;
|
||||
}
|
||||
if (i == numcollected)
|
||||
{
|
||||
new DScroller (DScroller::sc_side, dx, dy, -1, sidenum, 0, Where);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collection.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
FUNC(LS_Scroll_Texture_Both)
|
||||
// Scroll_Texture_Both (id, left, right, up, down)
|
||||
|
@ -2332,7 +2235,7 @@ FUNC(LS_Scroll_Texture_Both)
|
|||
sidechoice = 0;
|
||||
}
|
||||
|
||||
SetWallScroller (arg0, sidechoice, dx, dy, 7);
|
||||
SetWallScroller (arg0, sidechoice, dx, dy, scw_all);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2343,47 +2246,10 @@ FUNC(LS_Scroll_Wall)
|
|||
if (arg0 == 0)
|
||||
return false;
|
||||
|
||||
SetWallScroller (arg0, !!arg3, arg1, arg2, arg4);
|
||||
SetWallScroller (arg0, !!arg3, arg1, arg2, EScrollPos(arg4));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SetScroller (int tag, DScroller::EScrollType type, fixed_t dx, fixed_t dy)
|
||||
{
|
||||
TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
|
||||
DScroller *scroller;
|
||||
int i;
|
||||
|
||||
// Check if there is already a scroller for this tag
|
||||
// If at least one sector with this tag is scrolling, then they all are.
|
||||
// If the deltas are both 0, we don't remove the scroller, because a
|
||||
// displacement/accelerative scroller might have been set up, and there's
|
||||
// no way to create one after the level is fully loaded.
|
||||
i = 0;
|
||||
while ( (scroller = iterator.Next ()) )
|
||||
{
|
||||
if (scroller->IsType (type))
|
||||
{
|
||||
if (tagManager.SectorHasTag(scroller->GetAffectee (), tag))
|
||||
{
|
||||
i++;
|
||||
scroller->SetRate (dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0 || (dx|dy) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to create scrollers for the sector(s)
|
||||
FSectorTagIterator itr(tag);
|
||||
while ((i = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller (type, dx, dy, -1, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: For the next two functions, x-move and y-move are
|
||||
// 0-based, not 128-based as they are if they appear on lines.
|
||||
// Note also that parameter ordering is different.
|
||||
|
@ -2396,19 +2262,19 @@ FUNC(LS_Scroll_Floor)
|
|||
|
||||
if (arg3 == 0 || arg3 == 2)
|
||||
{
|
||||
SetScroller (arg0, DScroller::sc_floor, -dx, dy);
|
||||
SetScroller (arg0, EScroll::sc_floor, -dx, dy);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetScroller (arg0, DScroller::sc_floor, 0, 0);
|
||||
SetScroller (arg0, EScroll::sc_floor, 0, 0);
|
||||
}
|
||||
if (arg3 > 0)
|
||||
{
|
||||
SetScroller (arg0, DScroller::sc_carry, dx, dy);
|
||||
SetScroller (arg0, EScroll::sc_carry, dx, dy);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetScroller (arg0, DScroller::sc_carry, 0, 0);
|
||||
SetScroller (arg0, EScroll::sc_carry, 0, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2419,7 +2285,7 @@ FUNC(LS_Scroll_Ceiling)
|
|||
fixed_t dx = arg1 * FRACUNIT/32;
|
||||
fixed_t dy = arg2 * FRACUNIT/32;
|
||||
|
||||
SetScroller (arg0, DScroller::sc_ceiling, -dx, dy);
|
||||
SetScroller (arg0, EScroll::sc_ceiling, -dx, dy);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -5363,7 +5363,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo
|
|||
points *= thing->GetClass()->RDFactor / (float)FRACUNIT;
|
||||
|
||||
// points and bombdamage should be the same sign
|
||||
if (((points * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
|
||||
if (((int(points) * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
|
||||
{ // OK to damage; target is in direct path
|
||||
double vz;
|
||||
double thrust;
|
||||
|
|
485
src/p_pusher.cpp
Normal file
485
src/p_pusher.cpp
Normal file
|
@ -0,0 +1,485 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// $Id:$
|
||||
//
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
//
|
||||
// This source is available for distribution and/or modification
|
||||
// only under the terms of the DOOM Source Code License as
|
||||
// published by id Software. All rights reserved.
|
||||
//
|
||||
// The source is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
||||
// for more details.
|
||||
//
|
||||
// $Log:$
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Initializes and implements BOOM linedef triggers for
|
||||
// Wind/Current
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "actor.h"
|
||||
#include "p_spec.h"
|
||||
#include "farchive.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "c_cvars.h"
|
||||
#include "p_maputl.h"
|
||||
#include "p_local.h"
|
||||
#include "d_player.h"
|
||||
|
||||
CVAR(Bool, var_pushers, true, CVAR_SERVERINFO);
|
||||
|
||||
// phares 3/20/98: added new model of Pushers for push/pull effects
|
||||
|
||||
class DPusher : public DThinker
|
||||
{
|
||||
DECLARE_CLASS (DPusher, DThinker)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
enum EPusher
|
||||
{
|
||||
p_push,
|
||||
p_pull,
|
||||
p_wind,
|
||||
p_current
|
||||
};
|
||||
|
||||
DPusher ();
|
||||
DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee);
|
||||
void Serialize (FArchive &arc);
|
||||
int CheckForSectorMatch (EPusher type, int tag);
|
||||
void ChangeValues (int magnitude, int angle)
|
||||
{
|
||||
angle_t ang = ((angle_t)(angle<<24)) >> ANGLETOFINESHIFT;
|
||||
m_Xmag = (magnitude * finecosine[ang]) >> FRACBITS;
|
||||
m_Ymag = (magnitude * finesine[ang]) >> FRACBITS;
|
||||
m_Magnitude = magnitude;
|
||||
}
|
||||
|
||||
void Tick ();
|
||||
|
||||
protected:
|
||||
EPusher m_Type;
|
||||
TObjPtr<AActor> m_Source;// Point source if point pusher
|
||||
int m_Xmag; // X Strength
|
||||
int m_Ymag; // Y Strength
|
||||
int m_Magnitude; // Vector strength for point pusher
|
||||
int m_Radius; // Effective radius for point pusher
|
||||
int m_X; // X of point source if point pusher
|
||||
int m_Y; // Y of point source if point pusher
|
||||
int m_Affectee; // Number of affected sector
|
||||
|
||||
friend bool PIT_PushThing (AActor *thing);
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_POINTY_CLASS (DPusher)
|
||||
DECLARE_POINTER (m_Source)
|
||||
END_POINTERS
|
||||
|
||||
DPusher::DPusher ()
|
||||
{
|
||||
}
|
||||
|
||||
inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type)
|
||||
{
|
||||
BYTE val = (BYTE)type;
|
||||
arc << val;
|
||||
type = (DPusher::EPusher)val;
|
||||
return arc;
|
||||
}
|
||||
|
||||
void DPusher::Serialize (FArchive &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc << m_Type
|
||||
<< m_Source
|
||||
<< m_Xmag
|
||||
<< m_Ymag
|
||||
<< m_Magnitude
|
||||
<< m_Radius
|
||||
<< m_X
|
||||
<< m_Y
|
||||
<< m_Affectee;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PUSH/PULL EFFECT
|
||||
//
|
||||
// phares 3/20/98: Start of push/pull effects
|
||||
//
|
||||
// This is where push/pull effects are applied to objects in the sectors.
|
||||
//
|
||||
// There are four kinds of push effects
|
||||
//
|
||||
// 1) Pushing Away
|
||||
//
|
||||
// Pushes you away from a point source defined by the location of an
|
||||
// MT_PUSH Thing. The force decreases linearly with distance from the
|
||||
// source. This force crosses sector boundaries and is felt w/in a circle
|
||||
// whose center is at the MT_PUSH. The force is felt only if the point
|
||||
// MT_PUSH can see the target object.
|
||||
//
|
||||
// 2) Pulling toward
|
||||
//
|
||||
// Same as Pushing Away except you're pulled toward an MT_PULL point
|
||||
// source. This force crosses sector boundaries and is felt w/in a circle
|
||||
// whose center is at the MT_PULL. The force is felt only if the point
|
||||
// MT_PULL can see the target object.
|
||||
//
|
||||
// 3) Wind
|
||||
//
|
||||
// Pushes you in a constant direction. Full force above ground, half
|
||||
// force on the ground, nothing if you're below it (water).
|
||||
//
|
||||
// 4) Current
|
||||
//
|
||||
// Pushes you in a constant direction. No force above ground, full
|
||||
// force if on the ground or below it (water).
|
||||
//
|
||||
// The magnitude of the force is controlled by the length of a controlling
|
||||
// linedef. The force vector for types 3 & 4 is determined by the angle
|
||||
// of the linedef, and is constant.
|
||||
//
|
||||
// For each sector where these effects occur, the sector special type has
|
||||
// to have the PUSH_MASK bit set. If this bit is turned off by a switch
|
||||
// at run-time, the effect will not occur. The controlling sector for
|
||||
// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing.
|
||||
|
||||
|
||||
#define PUSH_FACTOR 7
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// Add a push thinker to the thinker list
|
||||
|
||||
DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle,
|
||||
AActor *source, int affectee)
|
||||
{
|
||||
m_Source = source;
|
||||
m_Type = type;
|
||||
if (l)
|
||||
{
|
||||
m_Xmag = l->dx>>FRACBITS;
|
||||
m_Ymag = l->dy>>FRACBITS;
|
||||
m_Magnitude = P_AproxDistance (m_Xmag, m_Ymag);
|
||||
}
|
||||
else
|
||||
{ // [RH] Allow setting magnitude and angle with parameters
|
||||
ChangeValues (magnitude, angle);
|
||||
}
|
||||
if (source) // point source exist?
|
||||
{
|
||||
m_Radius = (m_Magnitude) << (FRACBITS+1); // where force goes to zero
|
||||
m_X = m_Source->X();
|
||||
m_Y = m_Source->Y();
|
||||
}
|
||||
m_Affectee = affectee;
|
||||
}
|
||||
|
||||
int DPusher::CheckForSectorMatch (EPusher type, int tag)
|
||||
{
|
||||
if (m_Type == type && tagManager.SectorHasTag(m_Affectee, tag))
|
||||
return m_Affectee;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// T_Pusher looks for all objects that are inside the radius of
|
||||
// the effect.
|
||||
//
|
||||
void DPusher::Tick ()
|
||||
{
|
||||
sector_t *sec;
|
||||
AActor *thing;
|
||||
msecnode_t *node;
|
||||
int xspeed,yspeed;
|
||||
int ht;
|
||||
|
||||
if (!var_pushers)
|
||||
return;
|
||||
|
||||
sec = sectors + m_Affectee;
|
||||
|
||||
// Be sure the special sector type is still turned on. If so, proceed.
|
||||
// Else, bail out; the sector type has been changed on us.
|
||||
|
||||
if (!(sec->Flags & SECF_PUSH))
|
||||
return;
|
||||
|
||||
// For constant pushers (wind/current) there are 3 situations:
|
||||
//
|
||||
// 1) Affected Thing is above the floor.
|
||||
//
|
||||
// Apply the full force if wind, no force if current.
|
||||
//
|
||||
// 2) Affected Thing is on the ground.
|
||||
//
|
||||
// Apply half force if wind, full force if current.
|
||||
//
|
||||
// 3) Affected Thing is below the ground (underwater effect).
|
||||
//
|
||||
// Apply no force if wind, full force if current.
|
||||
//
|
||||
// Apply the effect to clipped players only for now.
|
||||
//
|
||||
// In Phase II, you can apply these effects to Things other than players.
|
||||
// [RH] No Phase II, but it works with anything having MF2_WINDTHRUST now.
|
||||
|
||||
if (m_Type == p_push)
|
||||
{
|
||||
// Seek out all pushable things within the force radius of this
|
||||
// point pusher. Crosses sectors, so use blockmap.
|
||||
|
||||
FPortalGroupArray check(FPortalGroupArray::PGA_NoSectorPortals); // no sector portals because this thing is utterly z-unaware.
|
||||
FMultiBlockThingsIterator it(check, m_X, m_Y, 0, 0, m_Radius, false, m_Source->Sector);
|
||||
FMultiBlockThingsIterator::CheckResult cres;
|
||||
|
||||
|
||||
while (it.Next(&cres))
|
||||
{
|
||||
AActor *thing = cres.thing;
|
||||
// Normal ZDoom is based only on the WINDTHRUST flag, with the noclip cheat as an exemption.
|
||||
bool pusharound = ((thing->flags2 & MF2_WINDTHRUST) && !(thing->flags & MF_NOCLIP));
|
||||
|
||||
// MBF allows any sentient or shootable thing to be affected, but players with a fly cheat aren't.
|
||||
if (compatflags & COMPATF_MBFMONSTERMOVE)
|
||||
{
|
||||
pusharound = ((pusharound || (thing->IsSentient()) || (thing->flags & MF_SHOOTABLE)) // Add categories here
|
||||
&& (!(thing->player && (thing->flags & (MF_NOGRAVITY))))); // Exclude flying players here
|
||||
}
|
||||
|
||||
if ((pusharound) )
|
||||
{
|
||||
int sx = m_X;
|
||||
int sy = m_Y;
|
||||
int dist = thing->AproxDistance (sx, sy);
|
||||
int speed = (m_Magnitude - ((dist>>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1);
|
||||
|
||||
// If speed <= 0, you're outside the effective radius. You also have
|
||||
// to be able to see the push/pull source point.
|
||||
|
||||
if ((speed > 0) && (P_CheckSight (thing, m_Source, SF_IGNOREVISIBILITY)))
|
||||
{
|
||||
angle_t pushangle = thing->AngleTo(sx, sy);
|
||||
if (m_Source->GetClass()->TypeName == NAME_PointPusher)
|
||||
pushangle += ANG180; // away
|
||||
pushangle >>= ANGLETOFINESHIFT;
|
||||
thing->vel.x += FixedMul (speed, finecosine[pushangle]);
|
||||
thing->vel.y += FixedMul (speed, finesine[pushangle]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// constant pushers p_wind and p_current
|
||||
|
||||
node = sec->touching_thinglist; // things touching this sector
|
||||
for ( ; node ; node = node->m_snext)
|
||||
{
|
||||
thing = node->m_thing;
|
||||
if (!(thing->flags2 & MF2_WINDTHRUST) || (thing->flags & MF_NOCLIP))
|
||||
continue;
|
||||
|
||||
sector_t *hsec = sec->GetHeightSec();
|
||||
fixedvec3 pos = thing->PosRelative(sec);
|
||||
if (m_Type == p_wind)
|
||||
{
|
||||
if (hsec == NULL)
|
||||
{ // NOT special water sector
|
||||
if (thing->Z() > thing->floorz) // above ground
|
||||
{
|
||||
xspeed = m_Xmag; // full force
|
||||
yspeed = m_Ymag;
|
||||
}
|
||||
else // on ground
|
||||
{
|
||||
xspeed = (m_Xmag)>>1; // half force
|
||||
yspeed = (m_Ymag)>>1;
|
||||
}
|
||||
}
|
||||
else // special water sector
|
||||
{
|
||||
ht = hsec->floorplane.ZatPoint(pos);
|
||||
if (thing->Z() > ht) // above ground
|
||||
{
|
||||
xspeed = m_Xmag; // full force
|
||||
yspeed = m_Ymag;
|
||||
}
|
||||
else if (thing->player->viewz < ht) // underwater
|
||||
{
|
||||
xspeed = yspeed = 0; // no force
|
||||
}
|
||||
else // wading in water
|
||||
{
|
||||
xspeed = (m_Xmag)>>1; // half force
|
||||
yspeed = (m_Ymag)>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // p_current
|
||||
{
|
||||
const secplane_t *floor;
|
||||
|
||||
if (hsec == NULL)
|
||||
{ // NOT special water sector
|
||||
floor = &sec->floorplane;
|
||||
}
|
||||
else
|
||||
{ // special water sector
|
||||
floor = &hsec->floorplane;
|
||||
}
|
||||
if (thing->Z() > floor->ZatPoint(pos))
|
||||
{ // above ground
|
||||
xspeed = yspeed = 0; // no force
|
||||
}
|
||||
else
|
||||
{ // on ground/underwater
|
||||
xspeed = m_Xmag; // full force
|
||||
yspeed = m_Ymag;
|
||||
}
|
||||
}
|
||||
thing->vel.x += xspeed<<(FRACBITS-PUSH_FACTOR);
|
||||
thing->vel.y += yspeed<<(FRACBITS-PUSH_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing,
|
||||
// NULL otherwise.
|
||||
|
||||
AActor *P_GetPushThing (int s)
|
||||
{
|
||||
AActor* thing;
|
||||
sector_t* sec;
|
||||
|
||||
sec = sectors + s;
|
||||
thing = sec->thinglist;
|
||||
|
||||
while (thing &&
|
||||
thing->GetClass()->TypeName != NAME_PointPusher &&
|
||||
thing->GetClass()->TypeName != NAME_PointPuller)
|
||||
{
|
||||
thing = thing->snext;
|
||||
}
|
||||
return thing;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// Initialize the sectors where pushers are present
|
||||
//
|
||||
|
||||
void P_SpawnPushers ()
|
||||
{
|
||||
int i;
|
||||
line_t *l = lines;
|
||||
int s;
|
||||
|
||||
for (i = 0; i < numlines; i++, l++)
|
||||
{
|
||||
switch (l->special)
|
||||
{
|
||||
case Sector_SetWind: // wind
|
||||
{
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
new DPusher(DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s);
|
||||
l->special = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case Sector_SetCurrent: // current
|
||||
{
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
new DPusher(DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s);
|
||||
l->special = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case PointPush_SetForce: // push/pull
|
||||
if (l->args[0]) { // [RH] Find thing by sector
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
AActor *thing = P_GetPushThing (s);
|
||||
if (thing) { // No MT_P* means no effect
|
||||
// [RH] Allow narrowing it down by tid
|
||||
if (!l->args[1] || l->args[1] == thing->tid)
|
||||
new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2],
|
||||
0, thing, s);
|
||||
}
|
||||
}
|
||||
} else { // [RH] Find thing by tid
|
||||
AActor *thing;
|
||||
FActorIterator iterator (l->args[1]);
|
||||
|
||||
while ( (thing = iterator.Next ()) )
|
||||
{
|
||||
if (thing->GetClass()->TypeName == NAME_PointPusher ||
|
||||
thing->GetClass()->TypeName == NAME_PointPuller)
|
||||
{
|
||||
new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2],
|
||||
0, thing, int(thing->Sector - sectors));
|
||||
}
|
||||
}
|
||||
}
|
||||
l->special = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdjustPusher (int tag, int magnitude, int angle, bool wind)
|
||||
{
|
||||
DPusher::EPusher type = wind? DPusher::p_wind : DPusher::p_current;
|
||||
|
||||
// Find pushers already attached to the sector, and change their parameters.
|
||||
TArray<FThinkerCollection> Collection;
|
||||
{
|
||||
TThinkerIterator<DPusher> iterator;
|
||||
FThinkerCollection collect;
|
||||
|
||||
while ( (collect.Obj = iterator.Next ()) )
|
||||
{
|
||||
if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch (type, tag)) >= 0)
|
||||
{
|
||||
((DPusher *)collect.Obj)->ChangeValues (magnitude, angle);
|
||||
Collection.Push (collect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t numcollected = Collection.Size ();
|
||||
int secnum;
|
||||
|
||||
// Now create pushers for any sectors that don't already have them.
|
||||
FSectorTagIterator itr(tag);
|
||||
while ((secnum = itr.Next()) >= 0)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < numcollected; i++)
|
||||
{
|
||||
if (Collection[i].RefNum == sectors[secnum].sectornum)
|
||||
break;
|
||||
}
|
||||
if (i == numcollected)
|
||||
{
|
||||
new DPusher (type, NULL, magnitude, angle, NULL, secnum);
|
||||
}
|
||||
}
|
||||
}
|
719
src/p_scroll.cpp
Normal file
719
src/p_scroll.cpp
Normal file
|
@ -0,0 +1,719 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// $Id:$
|
||||
//
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
//
|
||||
// This source is available for distribution and/or modification
|
||||
// only under the terms of the DOOM Source Code License as
|
||||
// published by id Software. All rights reserved.
|
||||
//
|
||||
// The source is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
||||
// for more details.
|
||||
//
|
||||
// $Log:$
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Initializes and implements BOOM linedef triggers for
|
||||
// Scrollers/Conveyors
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "actor.h"
|
||||
#include "p_spec.h"
|
||||
#include "farchive.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "r_data/r_interpolate.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// killough 3/7/98: Add generalized scroll effects
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class DScroller : public DThinker
|
||||
{
|
||||
DECLARE_CLASS (DScroller, DThinker)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
|
||||
DScroller (EScroll type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all);
|
||||
DScroller (fixed_t dx, fixed_t dy, const line_t *l, int control, int accel, EScrollPos scrollpos = EScrollPos::scw_all);
|
||||
void Destroy();
|
||||
|
||||
void Serialize (FArchive &arc);
|
||||
void Tick ();
|
||||
|
||||
bool AffectsWall (int wallnum) const { return m_Type == EScroll::sc_side && m_Affectee == wallnum; }
|
||||
int GetWallNum () const { return m_Type == EScroll::sc_side ? m_Affectee : -1; }
|
||||
void SetRate (fixed_t dx, fixed_t dy) { m_dx = dx; m_dy = dy; }
|
||||
bool IsType (EScroll type) const { return type == m_Type; }
|
||||
int GetAffectee () const { return m_Affectee; }
|
||||
EScrollPos GetScrollParts() const { return m_Parts; }
|
||||
|
||||
protected:
|
||||
EScroll m_Type; // Type of scroll effect
|
||||
fixed_t m_dx, m_dy; // (dx,dy) scroll speeds
|
||||
int m_Affectee; // Number of affected sidedef, sector, tag, or whatever
|
||||
int m_Control; // Control sector (-1 if none) used to control scrolling
|
||||
fixed_t m_LastHeight; // Last known height of control sector
|
||||
fixed_t m_vdx, m_vdy; // Accumulated velocity if accelerative
|
||||
int m_Accel; // Whether it's accelerative
|
||||
EScrollPos m_Parts; // Which parts of a sidedef are being scrolled?
|
||||
TObjPtr<DInterpolation> m_Interpolations[3];
|
||||
|
||||
private:
|
||||
DScroller ()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_POINTY_CLASS (DScroller)
|
||||
DECLARE_POINTER (m_Interpolations[0])
|
||||
DECLARE_POINTER (m_Interpolations[1])
|
||||
DECLARE_POINTER (m_Interpolations[2])
|
||||
END_POINTERS
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline FArchive &operator<< (FArchive &arc, EScroll &type)
|
||||
{
|
||||
BYTE val = (BYTE)type;
|
||||
arc << val;
|
||||
type = (EScroll)val;
|
||||
return arc;
|
||||
}
|
||||
|
||||
inline FArchive &operator<< (FArchive &arc, EScrollPos &type)
|
||||
{
|
||||
int val = (int)type;
|
||||
arc << val;
|
||||
type = (EScrollPos)val;
|
||||
return arc;
|
||||
}
|
||||
|
||||
EScrollPos operator &(EScrollPos one, EScrollPos two)
|
||||
{
|
||||
return EScrollPos(int(one) & int(two));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DScroller::Serialize (FArchive &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc << m_Type
|
||||
<< m_dx << m_dy
|
||||
<< m_Affectee
|
||||
<< m_Control
|
||||
<< m_LastHeight
|
||||
<< m_vdx << m_vdy
|
||||
<< m_Accel
|
||||
<< m_Parts
|
||||
<< m_Interpolations[0]
|
||||
<< m_Interpolations[1]
|
||||
<< m_Interpolations[2];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// [RH] Compensate for rotated sector textures by rotating the scrolling
|
||||
// in the opposite direction.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void RotationComp(const sector_t *sec, int which, fixed_t dx, fixed_t dy, fixed_t &tdx, fixed_t &tdy)
|
||||
{
|
||||
angle_t an = sec->GetAngle(which);
|
||||
if (an == 0)
|
||||
{
|
||||
tdx = dx;
|
||||
tdy = dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
an = an >> ANGLETOFINESHIFT;
|
||||
fixed_t ca = -finecosine[an];
|
||||
fixed_t sa = -finesine[an];
|
||||
tdx = DMulScale16(dx, ca, -dy, sa);
|
||||
tdy = DMulScale16(dy, ca, dx, sa);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// killough 2/28/98:
|
||||
//
|
||||
// This function, with the help of r_plane.c and r_bsp.c, supports generalized
|
||||
// scrolling floors and walls, with optional mobj-carrying properties, e.g.
|
||||
// conveyor belts, rivers, etc. A linedef with a special type affects all
|
||||
// tagged sectors the same way, by creating scrolling and/or object-carrying
|
||||
// properties. Multiple linedefs may be used on the same sector and are
|
||||
// cumulative, although the special case of scrolling a floor and carrying
|
||||
// things on it, requires only one linedef. The linedef's direction determines
|
||||
// the scrolling direction, and the linedef's length determines the scrolling
|
||||
// speed. This was designed so that an edge around the sector could be used to
|
||||
// control the direction of the sector's scrolling, which is usually what is
|
||||
// desired.
|
||||
//
|
||||
// Process the active scrollers.
|
||||
//
|
||||
// This is the main scrolling code
|
||||
// killough 3/7/98
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DScroller::Tick ()
|
||||
{
|
||||
fixed_t dx = m_dx, dy = m_dy, tdx, tdy;
|
||||
|
||||
if (m_Control != -1)
|
||||
{ // compute scroll amounts based on a sector's height changes
|
||||
fixed_t height = sectors[m_Control].CenterFloor () +
|
||||
sectors[m_Control].CenterCeiling ();
|
||||
fixed_t delta = height - m_LastHeight;
|
||||
m_LastHeight = height;
|
||||
dx = FixedMul(dx, delta);
|
||||
dy = FixedMul(dy, delta);
|
||||
}
|
||||
|
||||
// killough 3/14/98: Add acceleration
|
||||
if (m_Accel)
|
||||
{
|
||||
m_vdx = dx += m_vdx;
|
||||
m_vdy = dy += m_vdy;
|
||||
}
|
||||
|
||||
if (!(dx | dy)) // no-op if both (x,y) offsets are 0
|
||||
return;
|
||||
|
||||
switch (m_Type)
|
||||
{
|
||||
case EScroll::sc_side: // killough 3/7/98: Scroll wall texture
|
||||
if (m_Parts & EScrollPos::scw_top)
|
||||
{
|
||||
sides[m_Affectee].AddTextureXOffset(side_t::top, dx);
|
||||
sides[m_Affectee].AddTextureYOffset(side_t::top, dy);
|
||||
}
|
||||
if (m_Parts & EScrollPos::scw_mid && (sides[m_Affectee].linedef->backsector == NULL ||
|
||||
!(sides[m_Affectee].linedef->flags&ML_3DMIDTEX)))
|
||||
{
|
||||
sides[m_Affectee].AddTextureXOffset(side_t::mid, dx);
|
||||
sides[m_Affectee].AddTextureYOffset(side_t::mid, dy);
|
||||
}
|
||||
if (m_Parts & EScrollPos::scw_bottom)
|
||||
{
|
||||
sides[m_Affectee].AddTextureXOffset(side_t::bottom, dx);
|
||||
sides[m_Affectee].AddTextureYOffset(side_t::bottom, dy);
|
||||
}
|
||||
break;
|
||||
|
||||
case EScroll::sc_floor: // killough 3/7/98: Scroll floor texture
|
||||
RotationComp(§ors[m_Affectee], sector_t::floor, dx, dy, tdx, tdy);
|
||||
sectors[m_Affectee].AddXOffset(sector_t::floor, tdx);
|
||||
sectors[m_Affectee].AddYOffset(sector_t::floor, tdy);
|
||||
break;
|
||||
|
||||
case EScroll::sc_ceiling: // killough 3/7/98: Scroll ceiling texture
|
||||
RotationComp(§ors[m_Affectee], sector_t::ceiling, dx, dy, tdx, tdy);
|
||||
sectors[m_Affectee].AddXOffset(sector_t::ceiling, tdx);
|
||||
sectors[m_Affectee].AddYOffset(sector_t::ceiling, tdy);
|
||||
break;
|
||||
|
||||
// [RH] Don't actually carry anything here. That happens later.
|
||||
case EScroll::sc_carry:
|
||||
level.Scrolls[m_Affectee].ScrollX += dx;
|
||||
level.Scrolls[m_Affectee].ScrollY += dy;
|
||||
break;
|
||||
|
||||
case EScroll::sc_carry_ceiling: // to be added later
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Add_Scroller()
|
||||
//
|
||||
// Add a generalized scroller to the thinker list.
|
||||
//
|
||||
// type: the enumerated type of scrolling: floor, ceiling, floor carrier,
|
||||
// wall, floor carrier & scroller
|
||||
//
|
||||
// (dx,dy): the direction and speed of the scrolling or its acceleration
|
||||
//
|
||||
// control: the sector whose heights control this scroller's effect
|
||||
// remotely, or -1 if no control sector
|
||||
//
|
||||
// affectee: the index of the affected object (sector or sidedef)
|
||||
//
|
||||
// accel: non-zero if this is an accelerative effect
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DScroller::DScroller (EScroll type, fixed_t dx, fixed_t dy,
|
||||
int control, int affectee, int accel, EScrollPos scrollpos)
|
||||
: DThinker (STAT_SCROLLER)
|
||||
{
|
||||
m_Type = type;
|
||||
m_dx = dx;
|
||||
m_dy = dy;
|
||||
m_Accel = accel;
|
||||
m_Parts = scrollpos;
|
||||
m_vdx = m_vdy = 0;
|
||||
if ((m_Control = control) != -1)
|
||||
m_LastHeight =
|
||||
sectors[control].CenterFloor () + sectors[control].CenterCeiling ();
|
||||
m_Affectee = affectee;
|
||||
m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EScroll::sc_carry:
|
||||
level.AddScroller (affectee);
|
||||
break;
|
||||
|
||||
case EScroll::sc_side:
|
||||
sides[affectee].Flags |= WALLF_NOAUTODECALS;
|
||||
if (m_Parts & EScrollPos::scw_top)
|
||||
{
|
||||
m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top);
|
||||
}
|
||||
if (m_Parts & EScrollPos::scw_mid && (sides[m_Affectee].linedef->backsector == NULL ||
|
||||
!(sides[m_Affectee].linedef->flags&ML_3DMIDTEX)))
|
||||
{
|
||||
m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid);
|
||||
}
|
||||
if (m_Parts & EScrollPos::scw_bottom)
|
||||
{
|
||||
m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom);
|
||||
}
|
||||
break;
|
||||
|
||||
case EScroll::sc_floor:
|
||||
m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::FloorScroll, false);
|
||||
break;
|
||||
|
||||
case EScroll::sc_ceiling:
|
||||
m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::CeilingScroll, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DScroller::Destroy ()
|
||||
{
|
||||
for(int i=0;i<3;i++)
|
||||
{
|
||||
if (m_Interpolations[i] != NULL)
|
||||
{
|
||||
m_Interpolations[i]->DelRef();
|
||||
m_Interpolations[i] = NULL;
|
||||
}
|
||||
}
|
||||
Super::Destroy();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Adds wall scroller. Scroll amount is rotated with respect to wall's
|
||||
// linedef first, so that scrolling towards the wall in a perpendicular
|
||||
// direction is translated into vertical motion, while scrolling along
|
||||
// the wall in a parallel direction is translated into horizontal motion.
|
||||
//
|
||||
// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DScroller::DScroller (fixed_t dx, fixed_t dy, const line_t *l,
|
||||
int control, int accel, EScrollPos scrollpos)
|
||||
: DThinker (STAT_SCROLLER)
|
||||
{
|
||||
fixed_t x = abs(l->dx), y = abs(l->dy), d;
|
||||
if (y > x)
|
||||
d = x, x = y, y = d;
|
||||
d = FixedDiv (x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90)
|
||||
>> ANGLETOFINESHIFT]);
|
||||
x = -FixedDiv (FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d);
|
||||
y = -FixedDiv (FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d);
|
||||
|
||||
m_Type = EScroll::sc_side;
|
||||
m_dx = x;
|
||||
m_dy = y;
|
||||
m_vdx = m_vdy = 0;
|
||||
m_Accel = accel;
|
||||
m_Parts = scrollpos;
|
||||
if ((m_Control = control) != -1)
|
||||
m_LastHeight = sectors[control].CenterFloor() + sectors[control].CenterCeiling();
|
||||
m_Affectee = int(l->sidedef[0] - sides);
|
||||
sides[m_Affectee].Flags |= WALLF_NOAUTODECALS;
|
||||
m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL;
|
||||
|
||||
if (m_Parts & EScrollPos::scw_top)
|
||||
{
|
||||
m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top);
|
||||
}
|
||||
if (m_Parts & EScrollPos::scw_mid && (sides[m_Affectee].linedef->backsector == NULL ||
|
||||
!(sides[m_Affectee].linedef->flags&ML_3DMIDTEX)))
|
||||
{
|
||||
m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid);
|
||||
}
|
||||
if (m_Parts & EScrollPos::scw_bottom)
|
||||
{
|
||||
m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom);
|
||||
}
|
||||
}
|
||||
|
||||
// Amount (dx,dy) vector linedef is shifted right to get scroll amount
|
||||
#define SCROLL_SHIFT 5
|
||||
#define SCROLLTYPE(i) EScrollPos(((i) <= 0) || ((i) & ~7) ? 7 : (i))
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Initialize the scrollers
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void P_SpawnScrollers(void)
|
||||
{
|
||||
int i;
|
||||
line_t *l = lines;
|
||||
TArray<int> copyscrollers;
|
||||
|
||||
for (i = 0; i < numlines; i++)
|
||||
{
|
||||
if (lines[i].special == Sector_CopyScroller)
|
||||
{
|
||||
// don't allow copying the scroller if the sector has the same tag as it would just duplicate it.
|
||||
if (!tagManager.SectorHasTag(lines[i].frontsector, lines[i].args[0]))
|
||||
{
|
||||
copyscrollers.Push(i);
|
||||
}
|
||||
lines[i].special = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < numlines; i++, l++)
|
||||
{
|
||||
fixed_t dx; // direction and speed of scrolling
|
||||
fixed_t dy;
|
||||
int control = -1, accel = 0; // no control sector or acceleration
|
||||
int special = l->special;
|
||||
|
||||
// Check for undefined parameters that are non-zero and output messages for them.
|
||||
// We don't report for specials we don't understand.
|
||||
FLineSpecial *spec = P_GetLineSpecialInfo(special);
|
||||
if (spec != NULL)
|
||||
{
|
||||
int max = spec->map_args;
|
||||
for (unsigned arg = max; arg < countof(l->args); ++arg)
|
||||
{
|
||||
if (l->args[arg] != 0)
|
||||
{
|
||||
Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n",
|
||||
i, special, spec->name, arg+1, l->args[arg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// killough 3/7/98: Types 245-249 are same as 250-254 except that the
|
||||
// first side's sector's heights cause scrolling when they change, and
|
||||
// this linedef controls the direction and speed of the scrolling. The
|
||||
// most complicated linedef since donuts, but powerful :)
|
||||
//
|
||||
// killough 3/15/98: Add acceleration. Types 214-218 are the same but
|
||||
// are accelerative.
|
||||
|
||||
// [RH] Assume that it's a scroller and zero the line's special.
|
||||
l->special = 0;
|
||||
|
||||
dx = dy = 0; // Shut up, GCC
|
||||
|
||||
if (special == Scroll_Ceiling ||
|
||||
special == Scroll_Floor ||
|
||||
special == Scroll_Texture_Model)
|
||||
{
|
||||
if (l->args[1] & 3)
|
||||
{
|
||||
// if 1, then displacement
|
||||
// if 2, then accelerative (also if 3)
|
||||
control = int(l->sidedef[0]->sector - sectors);
|
||||
if (l->args[1] & 2)
|
||||
accel = 1;
|
||||
}
|
||||
if (special == Scroll_Texture_Model ||
|
||||
l->args[1] & 4)
|
||||
{
|
||||
// The line housing the special controls the
|
||||
// direction and speed of scrolling.
|
||||
dx = l->dx >> SCROLL_SHIFT;
|
||||
dy = l->dy >> SCROLL_SHIFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The speed and direction are parameters to the special.
|
||||
dx = (l->args[3] - 128) * (FRACUNIT / 32);
|
||||
dy = (l->args[4] - 128) * (FRACUNIT / 32);
|
||||
}
|
||||
}
|
||||
|
||||
switch (special)
|
||||
{
|
||||
int s;
|
||||
|
||||
case Scroll_Ceiling:
|
||||
{
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller(EScroll::sc_ceiling, -dx, dy, control, s, accel);
|
||||
}
|
||||
for (unsigned j = 0; j < copyscrollers.Size(); j++)
|
||||
{
|
||||
line_t *line = &lines[copyscrollers[j]];
|
||||
|
||||
if (line->args[0] == l->args[0] && (line->args[1] & 1))
|
||||
{
|
||||
new DScroller(EScroll::sc_ceiling, -dx, dy, control, int(line->frontsector - sectors), accel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Scroll_Floor:
|
||||
if (l->args[2] != 1)
|
||||
{ // scroll the floor texture
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller (EScroll::sc_floor, -dx, dy, control, s, accel);
|
||||
}
|
||||
for(unsigned j = 0;j < copyscrollers.Size(); j++)
|
||||
{
|
||||
line_t *line = &lines[copyscrollers[j]];
|
||||
|
||||
if (line->args[0] == l->args[0] && (line->args[1] & 2))
|
||||
{
|
||||
new DScroller (EScroll::sc_floor, -dx, dy, control, int(line->frontsector-sectors), accel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (l->args[2] > 0)
|
||||
{ // carry objects on the floor
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller (EScroll::sc_carry, dx, dy, control, s, accel);
|
||||
}
|
||||
for(unsigned j = 0;j < copyscrollers.Size(); j++)
|
||||
{
|
||||
line_t *line = &lines[copyscrollers[j]];
|
||||
|
||||
if (line->args[0] == l->args[0] && (line->args[1] & 4))
|
||||
{
|
||||
new DScroller (EScroll::sc_carry, dx, dy, control, int(line->frontsector-sectors), accel);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// killough 3/1/98: scroll wall according to linedef
|
||||
// (same direction and speed as scrolling floors)
|
||||
case Scroll_Texture_Model:
|
||||
{
|
||||
FLineIdIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
if (s != i)
|
||||
new DScroller(dx, dy, lines + s, control, accel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Scroll_Texture_Offsets:
|
||||
// killough 3/2/98: scroll according to sidedef offsets
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (EScroll::sc_side, -sides[s].GetTextureXOffset(side_t::mid),
|
||||
sides[s].GetTextureYOffset(side_t::mid), -1, s, accel, SCROLLTYPE(l->args[0]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Left:
|
||||
l->special = special; // Restore the special, for compat_useblocking's benefit.
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (EScroll::sc_side, l->args[0] * (FRACUNIT/64), 0,
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Right:
|
||||
l->special = special;
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (EScroll::sc_side, l->args[0] * (-FRACUNIT/64), 0,
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Up:
|
||||
l->special = special;
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (EScroll::sc_side, 0, l->args[0] * (FRACUNIT/64),
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Down:
|
||||
l->special = special;
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (EScroll::sc_side, 0, l->args[0] * (-FRACUNIT/64),
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Both:
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
if (l->args[0] == 0) {
|
||||
dx = (l->args[1] - l->args[2]) * (FRACUNIT/64);
|
||||
dy = (l->args[4] - l->args[3]) * (FRACUNIT/64);
|
||||
new DScroller (EScroll::sc_side, dx, dy, -1, s, accel);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// [RH] It wasn't a scroller after all, so restore the special.
|
||||
l->special = special;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Modify a wall scroller
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, EScrollPos Where)
|
||||
{
|
||||
Where = Where & scw_all;
|
||||
if (Where == 0) return;
|
||||
|
||||
if ((dx | dy) == 0)
|
||||
{
|
||||
// Special case: Remove the scroller, because the deltas are both 0.
|
||||
TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
|
||||
DScroller *scroller;
|
||||
|
||||
while ( (scroller = iterator.Next ()) )
|
||||
{
|
||||
int wallnum = scroller->GetWallNum ();
|
||||
|
||||
if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) &&
|
||||
int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum &&
|
||||
Where == scroller->GetScrollParts())
|
||||
{
|
||||
scroller->Destroy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find scrollers already attached to the matching walls, and change
|
||||
// their rates.
|
||||
TArray<FThinkerCollection> Collection;
|
||||
{
|
||||
TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
|
||||
FThinkerCollection collect;
|
||||
|
||||
while ( (collect.Obj = iterator.Next ()) )
|
||||
{
|
||||
if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 &&
|
||||
tagManager.LineHasID(sides[collect.RefNum].linedef, id) &&
|
||||
int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum &&
|
||||
Where == ((DScroller *)collect.Obj)->GetScrollParts())
|
||||
{
|
||||
((DScroller *)collect.Obj)->SetRate (dx, dy);
|
||||
Collection.Push (collect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t numcollected = Collection.Size ();
|
||||
int linenum;
|
||||
|
||||
// Now create scrollers for any walls that don't already have them.
|
||||
FLineIdIterator itr(id);
|
||||
while ((linenum = itr.Next()) >= 0)
|
||||
{
|
||||
if (lines[linenum].sidedef[sidechoice] != NULL)
|
||||
{
|
||||
int sidenum = int(lines[linenum].sidedef[sidechoice] - sides);
|
||||
unsigned int i;
|
||||
for (i = 0; i < numcollected; i++)
|
||||
{
|
||||
if (Collection[i].RefNum == sidenum)
|
||||
break;
|
||||
}
|
||||
if (i == numcollected)
|
||||
{
|
||||
new DScroller (EScroll::sc_side, dx, dy, -1, sidenum, 0, Where);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetScroller (int tag, EScroll type, fixed_t dx, fixed_t dy)
|
||||
{
|
||||
TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
|
||||
DScroller *scroller;
|
||||
int i;
|
||||
|
||||
// Check if there is already a scroller for this tag
|
||||
// If at least one sector with this tag is scrolling, then they all are.
|
||||
// If the deltas are both 0, we don't remove the scroller, because a
|
||||
// displacement/accelerative scroller might have been set up, and there's
|
||||
// no way to create one after the level is fully loaded.
|
||||
i = 0;
|
||||
while ( (scroller = iterator.Next ()) )
|
||||
{
|
||||
if (scroller->IsType (type))
|
||||
{
|
||||
if (tagManager.SectorHasTag(scroller->GetAffectee (), tag))
|
||||
{
|
||||
i++;
|
||||
scroller->SetRate (dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0 || (dx|dy) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to create scrollers for the sector(s)
|
||||
FSectorTagIterator itr(tag);
|
||||
while ((i = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller (type, dx, dy, -1, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void P_CreateScroller(EScroll type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, EScrollPos scrollpos)
|
||||
{
|
||||
new DScroller(type, dx, dy, control, affectee, accel, scrollpos);
|
||||
}
|
|
@ -952,7 +952,7 @@ fixed_t sector_t::NextHighestCeilingAt(fixed_t x, fixed_t y, fixed_t bottomz, fi
|
|||
fixed_t delta1 = bottomz - (ff_bottom + ((ff_top - ff_bottom) / 2));
|
||||
fixed_t delta2 = topz - (ff_bottom + ((ff_top - ff_bottom) / 2));
|
||||
|
||||
if (ff_bottom < realceil && abs(delta1) >= abs(delta2))
|
||||
if (ff_bottom < realceil && abs(delta1) > abs(delta2))
|
||||
{
|
||||
if (resultsec) *resultsec = sec;
|
||||
if (resultffloor) *resultffloor = rover;
|
||||
|
|
866
src/p_spec.cpp
866
src/p_spec.cpp
|
@ -84,74 +84,11 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
|
|||
|
||||
EXTERN_CVAR(Bool, cl_predict_specials)
|
||||
|
||||
IMPLEMENT_POINTY_CLASS (DScroller)
|
||||
DECLARE_POINTER (m_Interpolations[0])
|
||||
DECLARE_POINTER (m_Interpolations[1])
|
||||
DECLARE_POINTER (m_Interpolations[2])
|
||||
END_POINTERS
|
||||
|
||||
IMPLEMENT_POINTY_CLASS (DPusher)
|
||||
DECLARE_POINTER (m_Source)
|
||||
END_POINTERS
|
||||
|
||||
inline FArchive &operator<< (FArchive &arc, DScroller::EScrollType &type)
|
||||
{
|
||||
BYTE val = (BYTE)type;
|
||||
arc << val;
|
||||
type = (DScroller::EScrollType)val;
|
||||
return arc;
|
||||
}
|
||||
|
||||
DScroller::DScroller ()
|
||||
{
|
||||
}
|
||||
|
||||
void DScroller::Serialize (FArchive &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc << m_Type
|
||||
<< m_dx << m_dy
|
||||
<< m_Affectee
|
||||
<< m_Control
|
||||
<< m_LastHeight
|
||||
<< m_vdx << m_vdy
|
||||
<< m_Accel
|
||||
<< m_Parts
|
||||
<< m_Interpolations[0]
|
||||
<< m_Interpolations[1]
|
||||
<< m_Interpolations[2];
|
||||
}
|
||||
|
||||
DPusher::DPusher ()
|
||||
{
|
||||
}
|
||||
|
||||
inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type)
|
||||
{
|
||||
BYTE val = (BYTE)type;
|
||||
arc << val;
|
||||
type = (DPusher::EPusher)val;
|
||||
return arc;
|
||||
}
|
||||
|
||||
void DPusher::Serialize (FArchive &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc << m_Type
|
||||
<< m_Source
|
||||
<< m_Xmag
|
||||
<< m_Ymag
|
||||
<< m_Magnitude
|
||||
<< m_Radius
|
||||
<< m_X
|
||||
<< m_Y
|
||||
<< m_Affectee;
|
||||
}
|
||||
|
||||
// killough 3/7/98: Initialize generalized scrolling
|
||||
static void P_SpawnScrollers();
|
||||
void P_SpawnScrollers();
|
||||
static void P_SpawnFriction (); // phares 3/16/98
|
||||
static void P_SpawnPushers (); // phares 3/20/98
|
||||
void P_SpawnPushers (); // phares 3/20/98
|
||||
|
||||
|
||||
// [RH] Check dmflags for noexit and respond accordingly
|
||||
|
@ -1230,7 +1167,7 @@ void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers)
|
|||
break;
|
||||
|
||||
case dSector_DoorRaiseIn5Mins:
|
||||
new DDoor (sector, DDoor::doorWaitRaise, 2*FRACUNIT, TICRATE*30/7, 5*60*TICRATE, 0);
|
||||
new DDoor (sector, DDoor::doorWaitRaise, 2*FRACUNIT, TICRATE*30/7, 0, 5*60*TICRATE);
|
||||
break;
|
||||
|
||||
case dFriction_Low:
|
||||
|
@ -1260,7 +1197,7 @@ void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers)
|
|||
if (!nothinkers)
|
||||
{
|
||||
new DStrobe(sector, STROBEBRIGHT, FASTDARK, false);
|
||||
new DScroller(DScroller::sc_floor, -((FRACUNIT / 2) << 3),
|
||||
P_CreateScroller(EScroll::sc_floor, -((FRACUNIT / 2) << 3),
|
||||
0, -1, int(sector - sectors), 0);
|
||||
}
|
||||
keepspecial = true;
|
||||
|
@ -1321,13 +1258,13 @@ void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers)
|
|||
int i = sector->special - Scroll_North_Slow;
|
||||
fixed_t dx = hexenScrollies[i][0] * (FRACUNIT/2);
|
||||
fixed_t dy = hexenScrollies[i][1] * (FRACUNIT/2);
|
||||
if (!nothinkers) new DScroller (DScroller::sc_floor, dx, dy, -1, int(sector-sectors), 0);
|
||||
if (!nothinkers) P_CreateScroller(EScroll::sc_floor, dx, dy, -1, int(sector-sectors), 0);
|
||||
}
|
||||
else if (sector->special >= Carry_East5 &&
|
||||
sector->special <= Carry_East35)
|
||||
{ // Heretic scroll special
|
||||
// Only east scrollers also scroll the texture
|
||||
if (!nothinkers) new DScroller (DScroller::sc_floor,
|
||||
if (!nothinkers) P_CreateScroller(EScroll::sc_floor,
|
||||
(-FRACUNIT/2)<<(sector->special - Carry_East5),
|
||||
0, -1, int(sector-sectors), 0);
|
||||
}
|
||||
|
@ -1560,458 +1497,6 @@ void P_SpawnSpecials (void)
|
|||
FBehavior::StaticStartTypedScripts (SCRIPT_Open, NULL, false);
|
||||
}
|
||||
|
||||
// killough 2/28/98:
|
||||
//
|
||||
// This function, with the help of r_plane.c and r_bsp.c, supports generalized
|
||||
// scrolling floors and walls, with optional mobj-carrying properties, e.g.
|
||||
// conveyor belts, rivers, etc. A linedef with a special type affects all
|
||||
// tagged sectors the same way, by creating scrolling and/or object-carrying
|
||||
// properties. Multiple linedefs may be used on the same sector and are
|
||||
// cumulative, although the special case of scrolling a floor and carrying
|
||||
// things on it, requires only one linedef. The linedef's direction determines
|
||||
// the scrolling direction, and the linedef's length determines the scrolling
|
||||
// speed. This was designed so that an edge around the sector could be used to
|
||||
// control the direction of the sector's scrolling, which is usually what is
|
||||
// desired.
|
||||
//
|
||||
// Process the active scrollers.
|
||||
//
|
||||
// This is the main scrolling code
|
||||
// killough 3/7/98
|
||||
|
||||
// [RH] Compensate for rotated sector textures by rotating the scrolling
|
||||
// in the opposite direction.
|
||||
static void RotationComp(const sector_t *sec, int which, fixed_t dx, fixed_t dy, fixed_t &tdx, fixed_t &tdy)
|
||||
{
|
||||
angle_t an = sec->GetAngle(which);
|
||||
if (an == 0)
|
||||
{
|
||||
tdx = dx;
|
||||
tdy = dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
an = an >> ANGLETOFINESHIFT;
|
||||
fixed_t ca = -finecosine[an];
|
||||
fixed_t sa = -finesine[an];
|
||||
tdx = DMulScale16(dx, ca, -dy, sa);
|
||||
tdy = DMulScale16(dy, ca, dx, sa);
|
||||
}
|
||||
}
|
||||
|
||||
void DScroller::Tick ()
|
||||
{
|
||||
fixed_t dx = m_dx, dy = m_dy, tdx, tdy;
|
||||
|
||||
if (m_Control != -1)
|
||||
{ // compute scroll amounts based on a sector's height changes
|
||||
fixed_t height = sectors[m_Control].CenterFloor () +
|
||||
sectors[m_Control].CenterCeiling ();
|
||||
fixed_t delta = height - m_LastHeight;
|
||||
m_LastHeight = height;
|
||||
dx = FixedMul(dx, delta);
|
||||
dy = FixedMul(dy, delta);
|
||||
}
|
||||
|
||||
// killough 3/14/98: Add acceleration
|
||||
if (m_Accel)
|
||||
{
|
||||
m_vdx = dx += m_vdx;
|
||||
m_vdy = dy += m_vdy;
|
||||
}
|
||||
|
||||
if (!(dx | dy)) // no-op if both (x,y) offsets are 0
|
||||
return;
|
||||
|
||||
switch (m_Type)
|
||||
{
|
||||
case sc_side: // killough 3/7/98: Scroll wall texture
|
||||
if (m_Parts & scw_top)
|
||||
{
|
||||
sides[m_Affectee].AddTextureXOffset(side_t::top, dx);
|
||||
sides[m_Affectee].AddTextureYOffset(side_t::top, dy);
|
||||
}
|
||||
if (m_Parts & scw_mid && (sides[m_Affectee].linedef->backsector == NULL ||
|
||||
!(sides[m_Affectee].linedef->flags&ML_3DMIDTEX)))
|
||||
{
|
||||
sides[m_Affectee].AddTextureXOffset(side_t::mid, dx);
|
||||
sides[m_Affectee].AddTextureYOffset(side_t::mid, dy);
|
||||
}
|
||||
if (m_Parts & scw_bottom)
|
||||
{
|
||||
sides[m_Affectee].AddTextureXOffset(side_t::bottom, dx);
|
||||
sides[m_Affectee].AddTextureYOffset(side_t::bottom, dy);
|
||||
}
|
||||
break;
|
||||
|
||||
case sc_floor: // killough 3/7/98: Scroll floor texture
|
||||
RotationComp(§ors[m_Affectee], sector_t::floor, dx, dy, tdx, tdy);
|
||||
sectors[m_Affectee].AddXOffset(sector_t::floor, tdx);
|
||||
sectors[m_Affectee].AddYOffset(sector_t::floor, tdy);
|
||||
break;
|
||||
|
||||
case sc_ceiling: // killough 3/7/98: Scroll ceiling texture
|
||||
RotationComp(§ors[m_Affectee], sector_t::ceiling, dx, dy, tdx, tdy);
|
||||
sectors[m_Affectee].AddXOffset(sector_t::ceiling, tdx);
|
||||
sectors[m_Affectee].AddYOffset(sector_t::ceiling, tdy);
|
||||
break;
|
||||
|
||||
// [RH] Don't actually carry anything here. That happens later.
|
||||
case sc_carry:
|
||||
level.Scrolls[m_Affectee].ScrollX += dx;
|
||||
level.Scrolls[m_Affectee].ScrollY += dy;
|
||||
break;
|
||||
|
||||
case sc_carry_ceiling: // to be added later
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Add_Scroller()
|
||||
//
|
||||
// Add a generalized scroller to the thinker list.
|
||||
//
|
||||
// type: the enumerated type of scrolling: floor, ceiling, floor carrier,
|
||||
// wall, floor carrier & scroller
|
||||
//
|
||||
// (dx,dy): the direction and speed of the scrolling or its acceleration
|
||||
//
|
||||
// control: the sector whose heights control this scroller's effect
|
||||
// remotely, or -1 if no control sector
|
||||
//
|
||||
// affectee: the index of the affected object (sector or sidedef)
|
||||
//
|
||||
// accel: non-zero if this is an accelerative effect
|
||||
//
|
||||
|
||||
DScroller::DScroller (EScrollType type, fixed_t dx, fixed_t dy,
|
||||
int control, int affectee, int accel, int scrollpos)
|
||||
: DThinker (STAT_SCROLLER)
|
||||
{
|
||||
m_Type = type;
|
||||
m_dx = dx;
|
||||
m_dy = dy;
|
||||
m_Accel = accel;
|
||||
m_Parts = scrollpos;
|
||||
m_vdx = m_vdy = 0;
|
||||
if ((m_Control = control) != -1)
|
||||
m_LastHeight =
|
||||
sectors[control].CenterFloor () + sectors[control].CenterCeiling ();
|
||||
m_Affectee = affectee;
|
||||
m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case sc_carry:
|
||||
level.AddScroller (this, affectee);
|
||||
break;
|
||||
|
||||
case sc_side:
|
||||
sides[affectee].Flags |= WALLF_NOAUTODECALS;
|
||||
if (m_Parts & scw_top)
|
||||
{
|
||||
m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top);
|
||||
}
|
||||
if (m_Parts & scw_mid && (sides[m_Affectee].linedef->backsector == NULL ||
|
||||
!(sides[m_Affectee].linedef->flags&ML_3DMIDTEX)))
|
||||
{
|
||||
m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid);
|
||||
}
|
||||
if (m_Parts & scw_bottom)
|
||||
{
|
||||
m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom);
|
||||
}
|
||||
break;
|
||||
|
||||
case sc_floor:
|
||||
m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::FloorScroll, false);
|
||||
break;
|
||||
|
||||
case sc_ceiling:
|
||||
m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::CeilingScroll, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DScroller::Destroy ()
|
||||
{
|
||||
for(int i=0;i<3;i++)
|
||||
{
|
||||
if (m_Interpolations[i] != NULL)
|
||||
{
|
||||
m_Interpolations[i]->DelRef();
|
||||
m_Interpolations[i] = NULL;
|
||||
}
|
||||
}
|
||||
Super::Destroy();
|
||||
}
|
||||
|
||||
// Adds wall scroller. Scroll amount is rotated with respect to wall's
|
||||
// linedef first, so that scrolling towards the wall in a perpendicular
|
||||
// direction is translated into vertical motion, while scrolling along
|
||||
// the wall in a parallel direction is translated into horizontal motion.
|
||||
//
|
||||
// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff
|
||||
|
||||
DScroller::DScroller (fixed_t dx, fixed_t dy, const line_t *l,
|
||||
int control, int accel, int scrollpos)
|
||||
: DThinker (STAT_SCROLLER)
|
||||
{
|
||||
fixed_t x = abs(l->dx), y = abs(l->dy), d;
|
||||
if (y > x)
|
||||
d = x, x = y, y = d;
|
||||
d = FixedDiv (x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90)
|
||||
>> ANGLETOFINESHIFT]);
|
||||
x = -FixedDiv (FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d);
|
||||
y = -FixedDiv (FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d);
|
||||
|
||||
m_Type = sc_side;
|
||||
m_dx = x;
|
||||
m_dy = y;
|
||||
m_vdx = m_vdy = 0;
|
||||
m_Accel = accel;
|
||||
m_Parts = scrollpos;
|
||||
if ((m_Control = control) != -1)
|
||||
m_LastHeight = sectors[control].CenterFloor() + sectors[control].CenterCeiling();
|
||||
m_Affectee = int(l->sidedef[0] - sides);
|
||||
sides[m_Affectee].Flags |= WALLF_NOAUTODECALS;
|
||||
m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL;
|
||||
|
||||
if (m_Parts & scw_top)
|
||||
{
|
||||
m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top);
|
||||
}
|
||||
if (m_Parts & scw_mid && (sides[m_Affectee].linedef->backsector == NULL ||
|
||||
!(sides[m_Affectee].linedef->flags&ML_3DMIDTEX)))
|
||||
{
|
||||
m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid);
|
||||
}
|
||||
if (m_Parts & scw_bottom)
|
||||
{
|
||||
m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom);
|
||||
}
|
||||
}
|
||||
|
||||
// Amount (dx,dy) vector linedef is shifted right to get scroll amount
|
||||
#define SCROLL_SHIFT 5
|
||||
#define SCROLLTYPE(i) (((i) <= 0) || ((i) & ~7) ? 7 : (i))
|
||||
|
||||
// Initialize the scrollers
|
||||
static void P_SpawnScrollers(void)
|
||||
{
|
||||
int i;
|
||||
line_t *l = lines;
|
||||
TArray<int> copyscrollers;
|
||||
|
||||
for (i = 0; i < numlines; i++)
|
||||
{
|
||||
if (lines[i].special == Sector_CopyScroller)
|
||||
{
|
||||
// don't allow copying the scroller if the sector has the same tag as it would just duplicate it.
|
||||
if (!tagManager.SectorHasTag(lines[i].frontsector, lines[i].args[0]))
|
||||
{
|
||||
copyscrollers.Push(i);
|
||||
}
|
||||
lines[i].special = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < numlines; i++, l++)
|
||||
{
|
||||
fixed_t dx; // direction and speed of scrolling
|
||||
fixed_t dy;
|
||||
int control = -1, accel = 0; // no control sector or acceleration
|
||||
int special = l->special;
|
||||
|
||||
// Check for undefined parameters that are non-zero and output messages for them.
|
||||
// We don't report for specials we don't understand.
|
||||
FLineSpecial *spec = P_GetLineSpecialInfo(special);
|
||||
if (spec != NULL)
|
||||
{
|
||||
int max = spec->map_args;
|
||||
for (unsigned arg = max; arg < countof(l->args); ++arg)
|
||||
{
|
||||
if (l->args[arg] != 0)
|
||||
{
|
||||
Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n",
|
||||
i, special, spec->name, arg+1, l->args[arg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// killough 3/7/98: Types 245-249 are same as 250-254 except that the
|
||||
// first side's sector's heights cause scrolling when they change, and
|
||||
// this linedef controls the direction and speed of the scrolling. The
|
||||
// most complicated linedef since donuts, but powerful :)
|
||||
//
|
||||
// killough 3/15/98: Add acceleration. Types 214-218 are the same but
|
||||
// are accelerative.
|
||||
|
||||
// [RH] Assume that it's a scroller and zero the line's special.
|
||||
l->special = 0;
|
||||
|
||||
dx = dy = 0; // Shut up, GCC
|
||||
|
||||
if (special == Scroll_Ceiling ||
|
||||
special == Scroll_Floor ||
|
||||
special == Scroll_Texture_Model)
|
||||
{
|
||||
if (l->args[1] & 3)
|
||||
{
|
||||
// if 1, then displacement
|
||||
// if 2, then accelerative (also if 3)
|
||||
control = int(l->sidedef[0]->sector - sectors);
|
||||
if (l->args[1] & 2)
|
||||
accel = 1;
|
||||
}
|
||||
if (special == Scroll_Texture_Model ||
|
||||
l->args[1] & 4)
|
||||
{
|
||||
// The line housing the special controls the
|
||||
// direction and speed of scrolling.
|
||||
dx = l->dx >> SCROLL_SHIFT;
|
||||
dy = l->dy >> SCROLL_SHIFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The speed and direction are parameters to the special.
|
||||
dx = (l->args[3] - 128) * (FRACUNIT / 32);
|
||||
dy = (l->args[4] - 128) * (FRACUNIT / 32);
|
||||
}
|
||||
}
|
||||
|
||||
switch (special)
|
||||
{
|
||||
int s;
|
||||
|
||||
case Scroll_Ceiling:
|
||||
{
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller(DScroller::sc_ceiling, -dx, dy, control, s, accel);
|
||||
}
|
||||
for (unsigned j = 0; j < copyscrollers.Size(); j++)
|
||||
{
|
||||
line_t *line = &lines[copyscrollers[j]];
|
||||
|
||||
if (line->args[0] == l->args[0] && (line->args[1] & 1))
|
||||
{
|
||||
new DScroller(DScroller::sc_ceiling, -dx, dy, control, int(line->frontsector - sectors), accel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Scroll_Floor:
|
||||
if (l->args[2] != 1)
|
||||
{ // scroll the floor texture
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller (DScroller::sc_floor, -dx, dy, control, s, accel);
|
||||
}
|
||||
for(unsigned j = 0;j < copyscrollers.Size(); j++)
|
||||
{
|
||||
line_t *line = &lines[copyscrollers[j]];
|
||||
|
||||
if (line->args[0] == l->args[0] && (line->args[1] & 2))
|
||||
{
|
||||
new DScroller (DScroller::sc_floor, -dx, dy, control, int(line->frontsector-sectors), accel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (l->args[2] > 0)
|
||||
{ // carry objects on the floor
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
new DScroller (DScroller::sc_carry, dx, dy, control, s, accel);
|
||||
}
|
||||
for(unsigned j = 0;j < copyscrollers.Size(); j++)
|
||||
{
|
||||
line_t *line = &lines[copyscrollers[j]];
|
||||
|
||||
if (line->args[0] == l->args[0] && (line->args[1] & 4))
|
||||
{
|
||||
new DScroller (DScroller::sc_carry, dx, dy, control, int(line->frontsector-sectors), accel);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// killough 3/1/98: scroll wall according to linedef
|
||||
// (same direction and speed as scrolling floors)
|
||||
case Scroll_Texture_Model:
|
||||
{
|
||||
FLineIdIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
if (s != i)
|
||||
new DScroller(dx, dy, lines + s, control, accel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Scroll_Texture_Offsets:
|
||||
// killough 3/2/98: scroll according to sidedef offsets
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (DScroller::sc_side, -sides[s].GetTextureXOffset(side_t::mid),
|
||||
sides[s].GetTextureYOffset(side_t::mid), -1, s, accel, SCROLLTYPE(l->args[0]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Left:
|
||||
l->special = special; // Restore the special, for compat_useblocking's benefit.
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (DScroller::sc_side, l->args[0] * (FRACUNIT/64), 0,
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Right:
|
||||
l->special = special;
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (DScroller::sc_side, l->args[0] * (-FRACUNIT/64), 0,
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Up:
|
||||
l->special = special;
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (DScroller::sc_side, 0, l->args[0] * (FRACUNIT/64),
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Down:
|
||||
l->special = special;
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
new DScroller (DScroller::sc_side, 0, l->args[0] * (-FRACUNIT/64),
|
||||
-1, s, accel, SCROLLTYPE(l->args[1]));
|
||||
break;
|
||||
|
||||
case Scroll_Texture_Both:
|
||||
s = int(lines[i].sidedef[0] - sides);
|
||||
if (l->args[0] == 0) {
|
||||
dx = (l->args[1] - l->args[2]) * (FRACUNIT/64);
|
||||
dy = (l->args[4] - l->args[3]) * (FRACUNIT/64);
|
||||
new DScroller (DScroller::sc_side, dx, dy, -1, s, accel);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// [RH] It wasn't a scroller after all, so restore the special.
|
||||
l->special = special;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// killough 3/7/98 -- end generalized scroll effects
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRICTION EFFECTS
|
||||
|
@ -2144,345 +1629,6 @@ void P_SetSectorFriction (int tag, int amount, bool alterFlag)
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PUSH/PULL EFFECT
|
||||
//
|
||||
// phares 3/20/98: Start of push/pull effects
|
||||
//
|
||||
// This is where push/pull effects are applied to objects in the sectors.
|
||||
//
|
||||
// There are four kinds of push effects
|
||||
//
|
||||
// 1) Pushing Away
|
||||
//
|
||||
// Pushes you away from a point source defined by the location of an
|
||||
// MT_PUSH Thing. The force decreases linearly with distance from the
|
||||
// source. This force crosses sector boundaries and is felt w/in a circle
|
||||
// whose center is at the MT_PUSH. The force is felt only if the point
|
||||
// MT_PUSH can see the target object.
|
||||
//
|
||||
// 2) Pulling toward
|
||||
//
|
||||
// Same as Pushing Away except you're pulled toward an MT_PULL point
|
||||
// source. This force crosses sector boundaries and is felt w/in a circle
|
||||
// whose center is at the MT_PULL. The force is felt only if the point
|
||||
// MT_PULL can see the target object.
|
||||
//
|
||||
// 3) Wind
|
||||
//
|
||||
// Pushes you in a constant direction. Full force above ground, half
|
||||
// force on the ground, nothing if you're below it (water).
|
||||
//
|
||||
// 4) Current
|
||||
//
|
||||
// Pushes you in a constant direction. No force above ground, full
|
||||
// force if on the ground or below it (water).
|
||||
//
|
||||
// The magnitude of the force is controlled by the length of a controlling
|
||||
// linedef. The force vector for types 3 & 4 is determined by the angle
|
||||
// of the linedef, and is constant.
|
||||
//
|
||||
// For each sector where these effects occur, the sector special type has
|
||||
// to have the PUSH_MASK bit set. If this bit is turned off by a switch
|
||||
// at run-time, the effect will not occur. The controlling sector for
|
||||
// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing.
|
||||
|
||||
|
||||
#define PUSH_FACTOR 7
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// Add a push thinker to the thinker list
|
||||
|
||||
DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle,
|
||||
AActor *source, int affectee)
|
||||
{
|
||||
m_Source = source;
|
||||
m_Type = type;
|
||||
if (l)
|
||||
{
|
||||
m_Xmag = l->dx>>FRACBITS;
|
||||
m_Ymag = l->dy>>FRACBITS;
|
||||
m_Magnitude = P_AproxDistance (m_Xmag, m_Ymag);
|
||||
}
|
||||
else
|
||||
{ // [RH] Allow setting magnitude and angle with parameters
|
||||
ChangeValues (magnitude, angle);
|
||||
}
|
||||
if (source) // point source exist?
|
||||
{
|
||||
m_Radius = (m_Magnitude) << (FRACBITS+1); // where force goes to zero
|
||||
m_X = m_Source->X();
|
||||
m_Y = m_Source->Y();
|
||||
}
|
||||
m_Affectee = affectee;
|
||||
}
|
||||
|
||||
int DPusher::CheckForSectorMatch (EPusher type, int tag)
|
||||
{
|
||||
if (m_Type == type && tagManager.SectorHasTag(m_Affectee, tag))
|
||||
return m_Affectee;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// T_Pusher looks for all objects that are inside the radius of
|
||||
// the effect.
|
||||
//
|
||||
void DPusher::Tick ()
|
||||
{
|
||||
sector_t *sec;
|
||||
AActor *thing;
|
||||
msecnode_t *node;
|
||||
int xspeed,yspeed;
|
||||
int ht;
|
||||
|
||||
if (!var_pushers)
|
||||
return;
|
||||
|
||||
sec = sectors + m_Affectee;
|
||||
|
||||
// Be sure the special sector type is still turned on. If so, proceed.
|
||||
// Else, bail out; the sector type has been changed on us.
|
||||
|
||||
if (!(sec->Flags & SECF_PUSH))
|
||||
return;
|
||||
|
||||
// For constant pushers (wind/current) there are 3 situations:
|
||||
//
|
||||
// 1) Affected Thing is above the floor.
|
||||
//
|
||||
// Apply the full force if wind, no force if current.
|
||||
//
|
||||
// 2) Affected Thing is on the ground.
|
||||
//
|
||||
// Apply half force if wind, full force if current.
|
||||
//
|
||||
// 3) Affected Thing is below the ground (underwater effect).
|
||||
//
|
||||
// Apply no force if wind, full force if current.
|
||||
//
|
||||
// Apply the effect to clipped players only for now.
|
||||
//
|
||||
// In Phase II, you can apply these effects to Things other than players.
|
||||
// [RH] No Phase II, but it works with anything having MF2_WINDTHRUST now.
|
||||
|
||||
if (m_Type == p_push)
|
||||
{
|
||||
// Seek out all pushable things within the force radius of this
|
||||
// point pusher. Crosses sectors, so use blockmap.
|
||||
|
||||
FPortalGroupArray check(FPortalGroupArray::PGA_NoSectorPortals); // no sector portals because this thing is utterly z-unaware.
|
||||
FMultiBlockThingsIterator it(check, m_X, m_Y, 0, 0, m_Radius, false, m_Source->Sector);
|
||||
FMultiBlockThingsIterator::CheckResult cres;
|
||||
|
||||
|
||||
while (it.Next(&cres))
|
||||
{
|
||||
AActor *thing = cres.thing;
|
||||
// Normal ZDoom is based only on the WINDTHRUST flag, with the noclip cheat as an exemption.
|
||||
bool pusharound = ((thing->flags2 & MF2_WINDTHRUST) && !(thing->flags & MF_NOCLIP));
|
||||
|
||||
// MBF allows any sentient or shootable thing to be affected, but players with a fly cheat aren't.
|
||||
if (compatflags & COMPATF_MBFMONSTERMOVE)
|
||||
{
|
||||
pusharound = ((pusharound || (thing->IsSentient()) || (thing->flags & MF_SHOOTABLE)) // Add categories here
|
||||
&& (!(thing->player && (thing->flags & (MF_NOGRAVITY))))); // Exclude flying players here
|
||||
}
|
||||
|
||||
if ((pusharound) )
|
||||
{
|
||||
int sx = m_X;
|
||||
int sy = m_Y;
|
||||
int dist = thing->AproxDistance (sx, sy);
|
||||
int speed = (m_Magnitude - ((dist>>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1);
|
||||
|
||||
// If speed <= 0, you're outside the effective radius. You also have
|
||||
// to be able to see the push/pull source point.
|
||||
|
||||
if ((speed > 0) && (P_CheckSight (thing, m_Source, SF_IGNOREVISIBILITY)))
|
||||
{
|
||||
angle_t pushangle = thing->AngleTo(sx, sy);
|
||||
if (m_Source->GetClass()->TypeName == NAME_PointPusher)
|
||||
pushangle += ANG180; // away
|
||||
pushangle >>= ANGLETOFINESHIFT;
|
||||
thing->vel.x += FixedMul (speed, finecosine[pushangle]);
|
||||
thing->vel.y += FixedMul (speed, finesine[pushangle]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// constant pushers p_wind and p_current
|
||||
|
||||
node = sec->touching_thinglist; // things touching this sector
|
||||
for ( ; node ; node = node->m_snext)
|
||||
{
|
||||
thing = node->m_thing;
|
||||
if (!(thing->flags2 & MF2_WINDTHRUST) || (thing->flags & MF_NOCLIP))
|
||||
continue;
|
||||
|
||||
sector_t *hsec = sec->GetHeightSec();
|
||||
fixedvec3 pos = thing->PosRelative(sec);
|
||||
if (m_Type == p_wind)
|
||||
{
|
||||
if (hsec == NULL)
|
||||
{ // NOT special water sector
|
||||
if (thing->Z() > thing->floorz) // above ground
|
||||
{
|
||||
xspeed = m_Xmag; // full force
|
||||
yspeed = m_Ymag;
|
||||
}
|
||||
else // on ground
|
||||
{
|
||||
xspeed = (m_Xmag)>>1; // half force
|
||||
yspeed = (m_Ymag)>>1;
|
||||
}
|
||||
}
|
||||
else // special water sector
|
||||
{
|
||||
ht = hsec->floorplane.ZatPoint(pos);
|
||||
if (thing->Z() > ht) // above ground
|
||||
{
|
||||
xspeed = m_Xmag; // full force
|
||||
yspeed = m_Ymag;
|
||||
}
|
||||
else if (thing->player->viewz < ht) // underwater
|
||||
{
|
||||
xspeed = yspeed = 0; // no force
|
||||
}
|
||||
else // wading in water
|
||||
{
|
||||
xspeed = (m_Xmag)>>1; // half force
|
||||
yspeed = (m_Ymag)>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // p_current
|
||||
{
|
||||
const secplane_t *floor;
|
||||
|
||||
if (hsec == NULL)
|
||||
{ // NOT special water sector
|
||||
floor = &sec->floorplane;
|
||||
}
|
||||
else
|
||||
{ // special water sector
|
||||
floor = &hsec->floorplane;
|
||||
}
|
||||
if (thing->Z() > floor->ZatPoint(pos))
|
||||
{ // above ground
|
||||
xspeed = yspeed = 0; // no force
|
||||
}
|
||||
else
|
||||
{ // on ground/underwater
|
||||
xspeed = m_Xmag; // full force
|
||||
yspeed = m_Ymag;
|
||||
}
|
||||
}
|
||||
thing->vel.x += xspeed<<(FRACBITS-PUSH_FACTOR);
|
||||
thing->vel.y += yspeed<<(FRACBITS-PUSH_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing,
|
||||
// NULL otherwise.
|
||||
|
||||
AActor *P_GetPushThing (int s)
|
||||
{
|
||||
AActor* thing;
|
||||
sector_t* sec;
|
||||
|
||||
sec = sectors + s;
|
||||
thing = sec->thinglist;
|
||||
|
||||
while (thing &&
|
||||
thing->GetClass()->TypeName != NAME_PointPusher &&
|
||||
thing->GetClass()->TypeName != NAME_PointPuller)
|
||||
{
|
||||
thing = thing->snext;
|
||||
}
|
||||
return thing;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
//
|
||||
// Initialize the sectors where pushers are present
|
||||
//
|
||||
|
||||
static void P_SpawnPushers ()
|
||||
{
|
||||
int i;
|
||||
line_t *l = lines;
|
||||
int s;
|
||||
|
||||
for (i = 0; i < numlines; i++, l++)
|
||||
{
|
||||
switch (l->special)
|
||||
{
|
||||
case Sector_SetWind: // wind
|
||||
{
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
new DPusher(DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s);
|
||||
l->special = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case Sector_SetCurrent: // current
|
||||
{
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
new DPusher(DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s);
|
||||
l->special = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case PointPush_SetForce: // push/pull
|
||||
if (l->args[0]) { // [RH] Find thing by sector
|
||||
FSectorTagIterator itr(l->args[0]);
|
||||
while ((s = itr.Next()) >= 0)
|
||||
{
|
||||
AActor *thing = P_GetPushThing (s);
|
||||
if (thing) { // No MT_P* means no effect
|
||||
// [RH] Allow narrowing it down by tid
|
||||
if (!l->args[1] || l->args[1] == thing->tid)
|
||||
new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2],
|
||||
0, thing, s);
|
||||
}
|
||||
}
|
||||
} else { // [RH] Find thing by tid
|
||||
AActor *thing;
|
||||
FActorIterator iterator (l->args[1]);
|
||||
|
||||
while ( (thing = iterator.Next ()) )
|
||||
{
|
||||
if (thing->GetClass()->TypeName == NAME_PointPusher ||
|
||||
thing->GetClass()->TypeName == NAME_PointPuller)
|
||||
{
|
||||
new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2],
|
||||
0, thing, int(thing->Sector - sectors));
|
||||
}
|
||||
}
|
||||
}
|
||||
l->special = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// phares 3/20/98: End of Pusher effects
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void sector_t::AdjustFloorClip () const
|
||||
{
|
||||
msecnode_t *node;
|
||||
|
|
141
src/p_spec.h
141
src/p_spec.h
|
@ -32,6 +32,32 @@
|
|||
class FScanner;
|
||||
struct level_info_t;
|
||||
|
||||
struct FThinkerCollection
|
||||
{
|
||||
int RefNum;
|
||||
DThinker *Obj;
|
||||
};
|
||||
|
||||
enum class EScroll : int
|
||||
{
|
||||
sc_side,
|
||||
sc_floor,
|
||||
sc_ceiling,
|
||||
sc_carry,
|
||||
sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings
|
||||
};
|
||||
|
||||
enum EScrollPos : int
|
||||
{
|
||||
scw_top = 1,
|
||||
scw_mid = 2,
|
||||
scw_bottom = 4,
|
||||
scw_all = 7,
|
||||
};
|
||||
|
||||
void P_CreateScroller(EScroll type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all);
|
||||
|
||||
|
||||
//jff 2/23/98 identify the special classes that can share sectors
|
||||
|
||||
typedef enum
|
||||
|
@ -41,107 +67,10 @@ typedef enum
|
|||
lighting_special,
|
||||
} special_e;
|
||||
|
||||
// killough 3/7/98: Add generalized scroll effects
|
||||
|
||||
class DScroller : public DThinker
|
||||
{
|
||||
DECLARE_CLASS (DScroller, DThinker)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
enum EScrollType
|
||||
{
|
||||
sc_side,
|
||||
sc_floor,
|
||||
sc_ceiling,
|
||||
sc_carry,
|
||||
sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings
|
||||
};
|
||||
enum EScrollPos
|
||||
{
|
||||
scw_top=1,
|
||||
scw_mid=2,
|
||||
scw_bottom=4,
|
||||
scw_all=7,
|
||||
};
|
||||
|
||||
DScroller (EScrollType type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, int scrollpos = scw_all);
|
||||
DScroller (fixed_t dx, fixed_t dy, const line_t *l, int control, int accel, int scrollpos = scw_all);
|
||||
void Destroy();
|
||||
|
||||
void Serialize (FArchive &arc);
|
||||
void Tick ();
|
||||
|
||||
bool AffectsWall (int wallnum) const { return m_Type == sc_side && m_Affectee == wallnum; }
|
||||
int GetWallNum () const { return m_Type == sc_side ? m_Affectee : -1; }
|
||||
void SetRate (fixed_t dx, fixed_t dy) { m_dx = dx; m_dy = dy; }
|
||||
bool IsType (EScrollType type) const { return type == m_Type; }
|
||||
int GetAffectee () const { return m_Affectee; }
|
||||
int GetScrollParts() const { return m_Parts; }
|
||||
|
||||
protected:
|
||||
EScrollType m_Type; // Type of scroll effect
|
||||
fixed_t m_dx, m_dy; // (dx,dy) scroll speeds
|
||||
int m_Affectee; // Number of affected sidedef, sector, tag, or whatever
|
||||
int m_Control; // Control sector (-1 if none) used to control scrolling
|
||||
fixed_t m_LastHeight; // Last known height of control sector
|
||||
fixed_t m_vdx, m_vdy; // Accumulated velocity if accelerative
|
||||
int m_Accel; // Whether it's accelerative
|
||||
int m_Parts; // Which parts of a sidedef are being scrolled?
|
||||
TObjPtr<DInterpolation> m_Interpolations[3];
|
||||
|
||||
private:
|
||||
DScroller ();
|
||||
};
|
||||
|
||||
// Factor to scale scrolling effect into mobj-carrying properties = 3/32.
|
||||
// (This is so scrolling floors and objects on them can move at same speed.)
|
||||
enum { CARRYFACTOR = (3*FRACUNIT >> 5) };
|
||||
|
||||
// phares 3/20/98: added new model of Pushers for push/pull effects
|
||||
|
||||
class DPusher : public DThinker
|
||||
{
|
||||
DECLARE_CLASS (DPusher, DThinker)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
enum EPusher
|
||||
{
|
||||
p_push,
|
||||
p_pull,
|
||||
p_wind,
|
||||
p_current
|
||||
};
|
||||
|
||||
DPusher ();
|
||||
DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee);
|
||||
void Serialize (FArchive &arc);
|
||||
int CheckForSectorMatch (EPusher type, int tag);
|
||||
void ChangeValues (int magnitude, int angle)
|
||||
{
|
||||
angle_t ang = ((angle_t)(angle<<24)) >> ANGLETOFINESHIFT;
|
||||
m_Xmag = (magnitude * finecosine[ang]) >> FRACBITS;
|
||||
m_Ymag = (magnitude * finesine[ang]) >> FRACBITS;
|
||||
m_Magnitude = magnitude;
|
||||
}
|
||||
|
||||
void Tick ();
|
||||
|
||||
protected:
|
||||
EPusher m_Type;
|
||||
TObjPtr<AActor> m_Source;// Point source if point pusher
|
||||
int m_Xmag; // X Strength
|
||||
int m_Ymag; // Y Strength
|
||||
int m_Magnitude; // Vector strength for point pusher
|
||||
int m_Radius; // Effective radius for point pusher
|
||||
int m_X; // X of point source if point pusher
|
||||
int m_Y; // Y of point source if point pusher
|
||||
int m_Affectee; // Number of affected sector
|
||||
|
||||
friend bool PIT_PushThing (AActor *thing);
|
||||
};
|
||||
|
||||
bool PIT_PushThing (AActor *thing);
|
||||
|
||||
// Define values for map objects
|
||||
#define MO_TELEPORTMAN 14
|
||||
|
||||
|
@ -535,7 +464,7 @@ public:
|
|||
};
|
||||
|
||||
DDoor (sector_t *sector);
|
||||
DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int topcountdown, int lightTag);
|
||||
DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int lightTag, int topcountdown);
|
||||
|
||||
void Serialize (FArchive &arc);
|
||||
void Tick ();
|
||||
|
@ -647,6 +576,14 @@ public:
|
|||
genCeilingChg
|
||||
};
|
||||
|
||||
enum class ECrushMode
|
||||
{
|
||||
crushDoom = 0,
|
||||
crushHexen = 1,
|
||||
crushSlowdown = 2
|
||||
};
|
||||
|
||||
|
||||
DCeiling (sector_t *sec);
|
||||
DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent);
|
||||
|
||||
|
@ -655,7 +592,7 @@ public:
|
|||
|
||||
static DCeiling *Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag,
|
||||
fixed_t speed, fixed_t speed2, fixed_t height,
|
||||
int crush, int silent, int change, bool hexencrush);
|
||||
int crush, int silent, int change, ECrushMode hexencrush);
|
||||
|
||||
protected:
|
||||
ECeiling m_Type;
|
||||
|
@ -665,7 +602,7 @@ protected:
|
|||
fixed_t m_Speed1; // [RH] dnspeed of crushers
|
||||
fixed_t m_Speed2; // [RH] upspeed of crushers
|
||||
int m_Crush;
|
||||
bool m_Hexencrush;
|
||||
ECrushMode m_CrushMode;
|
||||
int m_Silent;
|
||||
int m_Direction; // 1 = up, 0 = waiting, -1 = down
|
||||
|
||||
|
@ -688,7 +625,7 @@ private:
|
|||
|
||||
bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line,
|
||||
int tag, fixed_t speed, fixed_t speed2, fixed_t height,
|
||||
int crush, int silent, int change, bool hexencrush);
|
||||
int crush, int silent, int change, DCeiling::ECrushMode hexencrush = DCeiling::ECrushMode::crushDoom);
|
||||
bool EV_CeilingCrushStop (int tag);
|
||||
void P_ActivateInStasisCeiling (int tag);
|
||||
|
||||
|
@ -944,7 +881,7 @@ void P_DoDeferedScripts (void);
|
|||
//
|
||||
// [RH] p_quake.c
|
||||
//
|
||||
bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ);
|
||||
bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint);
|
||||
bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "r_defs.h"
|
||||
#include "m_bbox.h"
|
||||
|
||||
class DPolyAction;
|
||||
|
||||
struct FPolyVertex
|
||||
{
|
||||
|
|
|
@ -155,6 +155,8 @@ static void BuildBlockmap()
|
|||
|
||||
void FLinePortalTraverse::AddLineIntercepts(int bx, int by)
|
||||
{
|
||||
if (by < 0 || by >= bmapheight || bx < 0 || bx >= bmapwidth) return;
|
||||
|
||||
FPortalBlock &block = PortalBlockmap(bx, by);
|
||||
|
||||
for (unsigned i = 0; i<block.portallines.Size(); i++)
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include "v_video.h"
|
||||
#include "doomstat.h"
|
||||
#include "st_stuff.h"
|
||||
#include "a_hexenglobal.h"
|
||||
#include "g_game.h"
|
||||
#include "g_level.h"
|
||||
#include "r_data/r_translate.h"
|
||||
|
|
|
@ -896,10 +896,9 @@ void R_AddInterpolationPoint(const fixedvec3a &vec)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset)
|
||||
static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset, fixed_t falloff, fixed_t wfalloff)
|
||||
{
|
||||
fixed_t randumb;
|
||||
|
||||
if (intensity == 0)
|
||||
{
|
||||
randumb = 0;
|
||||
|
@ -908,7 +907,8 @@ static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset)
|
|||
{
|
||||
randumb = pr_torchflicker(intensity * 2) - intensity;
|
||||
}
|
||||
return FixedMul(factor, randumb + offset);
|
||||
fixed_t rn2 = (FixedMul(wfalloff,offset) + FixedMul(falloff, randumb));
|
||||
return FixedMul(factor, rn2);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -1050,34 +1050,33 @@ void R_SetupFrame (AActor *actor)
|
|||
if ((jiggers.RelIntensityX | jiggers.RelOffsetX) != 0)
|
||||
{
|
||||
int ang = (camera->angle) >> ANGLETOFINESHIFT;
|
||||
fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityX, jiggers.RelOffsetX);
|
||||
fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityX, jiggers.RelOffsetX, jiggers.Falloff, jiggers.WFalloff);
|
||||
viewx += FixedMul(finecosine[ang], power);
|
||||
viewy += FixedMul(finesine[ang], power);
|
||||
}
|
||||
if ((jiggers.RelIntensityY | jiggers.RelOffsetY) != 0)
|
||||
{
|
||||
int ang = (camera->angle + ANG90) >> ANGLETOFINESHIFT;
|
||||
fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityY, jiggers.RelOffsetY);
|
||||
fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityY, jiggers.RelOffsetY, jiggers.Falloff, jiggers.WFalloff);
|
||||
viewx += FixedMul(finecosine[ang], power);
|
||||
viewy += FixedMul(finesine[ang], power);
|
||||
}
|
||||
// FIXME: Relative Z is not relative
|
||||
// [MC]On it! Will be introducing pitch after QF_WAVE.
|
||||
if ((jiggers.RelIntensityZ | jiggers.RelOffsetZ) != 0)
|
||||
{
|
||||
viewz += QuakePower(quakefactor, jiggers.RelIntensityZ, jiggers.RelOffsetZ);
|
||||
viewz += QuakePower(quakefactor, jiggers.RelIntensityZ, jiggers.RelOffsetZ, jiggers.Falloff, jiggers.WFalloff);
|
||||
}
|
||||
if ((jiggers.IntensityX | jiggers.OffsetX) != 0)
|
||||
{
|
||||
viewx += QuakePower(quakefactor, jiggers.IntensityX, jiggers.OffsetX);
|
||||
viewx += QuakePower(quakefactor, jiggers.IntensityX, jiggers.OffsetX, jiggers.Falloff, jiggers.WFalloff);
|
||||
}
|
||||
if ((jiggers.IntensityY | jiggers.OffsetY) != 0)
|
||||
{
|
||||
viewy += QuakePower(quakefactor, jiggers.IntensityY, jiggers.OffsetY);
|
||||
viewy += QuakePower(quakefactor, jiggers.IntensityY, jiggers.OffsetY, jiggers.Falloff, jiggers.WFalloff);
|
||||
}
|
||||
if ((jiggers.IntensityZ | jiggers.OffsetZ) != 0)
|
||||
{
|
||||
viewz += QuakePower(quakefactor, jiggers.IntensityZ, jiggers.OffsetZ);
|
||||
viewz += QuakePower(quakefactor, jiggers.IntensityZ, jiggers.OffsetZ, jiggers.Falloff, jiggers.WFalloff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
#include "cmdlib.h"
|
||||
#include "p_terrain.h"
|
||||
#include "decallib.h"
|
||||
#include "a_doomglobal.h"
|
||||
#include "autosegs.h"
|
||||
#include "i_cd.h"
|
||||
#include "stats.h"
|
||||
|
|
48
src/tarray.h
48
src/tarray.h
|
@ -39,6 +39,7 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <inttypes.h> // for intptr_t
|
||||
|
@ -93,17 +94,23 @@ public:
|
|||
{
|
||||
return &Array[0];
|
||||
}
|
||||
const_iterator begin() const
|
||||
{
|
||||
return &Array[0];
|
||||
}
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return &Array[0];
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return &Array[Count];
|
||||
}
|
||||
|
||||
const_iterator cbegin() const
|
||||
const_iterator end() const
|
||||
{
|
||||
return &Array[0];
|
||||
return &Array[Count];
|
||||
}
|
||||
|
||||
const_iterator cend() const
|
||||
{
|
||||
return &Array[Count];
|
||||
|
@ -137,11 +144,17 @@ public:
|
|||
Count = 0;
|
||||
Array = (T *)M_Malloc (sizeof(T)*max);
|
||||
}
|
||||
TArray (const TArray<T> &other)
|
||||
TArray (const TArray<T,TT> &other)
|
||||
{
|
||||
DoCopy (other);
|
||||
}
|
||||
TArray<T> &operator= (const TArray<T> &other)
|
||||
TArray (TArray<T,TT> &&other)
|
||||
{
|
||||
Array = other.Array; other.Array = NULL;
|
||||
Most = other.Most; other.Most = 0;
|
||||
Count = other.Count; other.Count = 0;
|
||||
}
|
||||
TArray<T,TT> &operator= (const TArray<T,TT> &other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
|
@ -157,6 +170,21 @@ public:
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
TArray<T,TT> &operator= (TArray<T,TT> &&other)
|
||||
{
|
||||
if (Array)
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
DoDelete (0, Count-1);
|
||||
}
|
||||
M_Free (Array);
|
||||
}
|
||||
Array = other.Array; other.Array = NULL;
|
||||
Most = other.Most; other.Most = 0;
|
||||
Count = other.Count; other.Count = 0;
|
||||
return *this;
|
||||
}
|
||||
~TArray ()
|
||||
{
|
||||
if (Array)
|
||||
|
@ -417,6 +445,14 @@ template<class T, class TT=T>
|
|||
class TDeletingArray : public TArray<T, TT>
|
||||
{
|
||||
public:
|
||||
TDeletingArray() : TArray<T,TT>() {}
|
||||
TDeletingArray(TDeletingArray<T,TT> &&other) : TArray<T,TT>(std::move(other)) {}
|
||||
TDeletingArray<T,TT> &operator=(TDeletingArray<T,TT> &&other)
|
||||
{
|
||||
TArray<T,TT>::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
~TDeletingArray<T, TT> ()
|
||||
{
|
||||
for (unsigned int i = 0; i < TArray<T,TT>::Size(); ++i)
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <utility>
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -204,7 +205,7 @@ template<class T>
|
|||
inline
|
||||
void swapvalues (T &a, T &b)
|
||||
{
|
||||
T temp = a; a = b; b = temp;
|
||||
T temp = std::move(a); a = std::move(b); b = std::move(temp);
|
||||
}
|
||||
|
||||
#endif //__TEMPLATES_H__
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
#include "m_argv.h"
|
||||
#include "p_local.h"
|
||||
#include "doomerrors.h"
|
||||
#include "a_hexenglobal.h"
|
||||
#include "a_weaponpiece.h"
|
||||
#include "p_conversation.h"
|
||||
#include "v_text.h"
|
||||
|
|
|
@ -2761,6 +2761,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
//
|
||||
// A_LogFloat
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogFloat)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_FLOAT(num);
|
||||
IGNORE_FORMAT_PRE
|
||||
Printf("%H\n", num);
|
||||
IGNORE_FORMAT_POST
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// A_SetTranslucent
|
||||
|
@ -4681,22 +4697,46 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar)
|
||||
static PField *GetVar(AActor *self, FName varname)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_NAME (varname);
|
||||
PARAM_INT (value);
|
||||
|
||||
PField *var = dyn_cast<PField>(self->GetClass()->Symbols.FindSymbol(varname, true));
|
||||
|
||||
if (var == NULL || (var->Flags & VARF_Native) || !var->Type->IsKindOf(RUNTIME_CLASS(PBasicType)))
|
||||
{
|
||||
Printf("%s is not a user variable in class %s\n", varname.GetChars(),
|
||||
self->GetClass()->TypeName.GetChars());
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_NAME (varname);
|
||||
PARAM_INT (value);
|
||||
|
||||
// Set the value of the specified user variable.
|
||||
var->Type->SetValue(reinterpret_cast<BYTE *>(self) + var->Offset, value);
|
||||
PField *var = GetVar(self, varname);
|
||||
if (var != nullptr)
|
||||
{
|
||||
var->Type->SetValue(reinterpret_cast<BYTE *>(self) + var->Offset, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVarFloat)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_NAME (varname);
|
||||
PARAM_FLOAT (value);
|
||||
|
||||
// Set the value of the specified user variable.
|
||||
PField *var = GetVar(self, varname);
|
||||
if (var != nullptr)
|
||||
{
|
||||
var->Type->SetValue(reinterpret_cast<BYTE *>(self) + var->Offset, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4706,13 +4746,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray)
|
||||
static PField *GetArrayVar(AActor *self, FName varname, int pos)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_NAME (varname);
|
||||
PARAM_INT (pos);
|
||||
PARAM_INT (value);
|
||||
|
||||
PField *var = dyn_cast<PField>(self->GetClass()->Symbols.FindSymbol(varname, true));
|
||||
|
||||
if (var == NULL || (var->Flags & VARF_Native) ||
|
||||
|
@ -4721,17 +4756,48 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray)
|
|||
{
|
||||
Printf("%s is not a user array in class %s\n", varname.GetChars(),
|
||||
self->GetClass()->TypeName.GetChars());
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
PArray *arraytype = static_cast<PArray *>(var->Type);
|
||||
if ((unsigned)pos >= arraytype->ElementCount)
|
||||
if ((unsigned)pos >= static_cast<PArray *>(var->Type)->ElementCount)
|
||||
{
|
||||
Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(),
|
||||
self->GetClass()->TypeName.GetChars());
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_NAME (varname);
|
||||
PARAM_INT (pos);
|
||||
PARAM_INT (value);
|
||||
|
||||
// Set the value of the specified user array at index pos.
|
||||
arraytype->ElementType->SetValue(reinterpret_cast<BYTE *>(self) + var->Offset + arraytype->ElementSize * pos, value);
|
||||
PField *var = GetArrayVar(self, varname, pos);
|
||||
if (var != nullptr)
|
||||
{
|
||||
PArray *arraytype = static_cast<PArray *>(var->Type);
|
||||
arraytype->ElementType->SetValue(reinterpret_cast<BYTE *>(self) + var->Offset + arraytype->ElementSize * pos, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArrayFloat)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_NAME (varname);
|
||||
PARAM_INT (pos);
|
||||
PARAM_FLOAT (value);
|
||||
|
||||
// Set the value of the specified user array at index pos.
|
||||
PField *var = GetArrayVar(self, varname, pos);
|
||||
if (var != nullptr)
|
||||
{
|
||||
PArray *arraytype = static_cast<PArray *>(var->Type);
|
||||
arraytype->ElementType->SetValue(reinterpret_cast<BYTE *>(self) + var->Offset + arraytype->ElementSize * pos, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4969,7 +5035,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake)
|
|||
// A_QuakeEx
|
||||
//
|
||||
// Extended version of A_Quake. Takes individual axis into account and can
|
||||
// take a flag.
|
||||
// take flags.
|
||||
//===========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_QuakeEx)
|
||||
|
@ -4986,7 +5052,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_QuakeEx)
|
|||
PARAM_FLOAT_OPT(mulWaveX) { mulWaveX = 1.; }
|
||||
PARAM_FLOAT_OPT(mulWaveY) { mulWaveY = 1.; }
|
||||
PARAM_FLOAT_OPT(mulWaveZ) { mulWaveZ = 1.; }
|
||||
P_StartQuakeXYZ(self, 0, intensityX, intensityY, intensityZ, duration, damrad, tremrad, sound, flags, mulWaveX, mulWaveY, mulWaveZ);
|
||||
PARAM_INT_OPT(falloff) { falloff = 0; }
|
||||
PARAM_INT_OPT(highpoint) { highpoint = 0; }
|
||||
P_StartQuakeXYZ(self, 0, intensityX, intensityY, intensityZ, duration, damrad, tremrad, sound, flags, mulWaveX, mulWaveY, mulWaveZ, falloff, highpoint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -6694,14 +6762,20 @@ enum CBF
|
|||
CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self.
|
||||
CBF_DROPOFF = 1 << 5, //Check for dropoffs.
|
||||
CBF_NOACTORS = 1 << 6, //Don't check actors.
|
||||
CBF_ABSOLUTEPOS = 1 << 7, //Absolute position for offsets.
|
||||
CBF_ABSOLUTEANGLE = 1 << 8, //Absolute angle for offsets.
|
||||
};
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE;
|
||||
PARAM_STATE(block)
|
||||
PARAM_INT_OPT(flags) { flags = 0; }
|
||||
PARAM_INT_OPT(ptr) { ptr = AAPTR_DEFAULT; }
|
||||
PARAM_INT_OPT(flags) { flags = 0; }
|
||||
PARAM_INT_OPT(ptr) { ptr = AAPTR_DEFAULT; }
|
||||
PARAM_FIXED_OPT(xofs) { xofs = 0; }
|
||||
PARAM_FIXED_OPT(yofs) { yofs = 0; }
|
||||
PARAM_FIXED_OPT(zofs) { zofs = 0; }
|
||||
PARAM_ANGLE_OPT(angle) { angle = 0; }
|
||||
|
||||
AActor *mobj = COPY_AAPTR(self, ptr);
|
||||
|
||||
|
@ -6711,8 +6785,48 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
|
|||
ACTION_RETURN_STATE(NULL);
|
||||
}
|
||||
|
||||
if (!(flags & CBF_ABSOLUTEANGLE))
|
||||
{
|
||||
angle += self->angle;
|
||||
}
|
||||
|
||||
angle_t ang = angle >> ANGLETOFINESHIFT;
|
||||
fixedvec3 oldpos = mobj->Pos();
|
||||
fixedvec3 pos;
|
||||
|
||||
if (flags & CBF_ABSOLUTEPOS)
|
||||
{
|
||||
pos.x = xofs;
|
||||
pos.y = yofs;
|
||||
pos.z = zofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = mobj->Vec3Offset(
|
||||
FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]),
|
||||
FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]),
|
||||
mobj->Z() + zofs);
|
||||
}
|
||||
|
||||
// Next, try checking the position based on the sensitivity desired.
|
||||
// If checking for dropoffs, set the z so we can have maximum flexibility.
|
||||
// Otherwise, set origin and set it back after testing.
|
||||
|
||||
bool checker = false;
|
||||
if (flags & CBF_DROPOFF)
|
||||
{
|
||||
mobj->SetZ(pos.z);
|
||||
checker = P_CheckMove(mobj, pos.x, pos.y);
|
||||
mobj->SetZ(oldpos.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj->SetOrigin(pos, true);
|
||||
checker = P_TestMobjLocation(mobj);
|
||||
mobj->SetOrigin(oldpos, true);
|
||||
}
|
||||
|
||||
//Nothing to block it so skip the rest.
|
||||
bool checker = (flags & CBF_DROPOFF) ? P_CheckMove(mobj, mobj->X(), mobj->Y()) : P_TestMobjLocation(mobj);
|
||||
if (checker)
|
||||
{
|
||||
ACTION_RETURN_STATE(NULL);
|
||||
|
|
|
@ -2994,10 +2994,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
|||
}
|
||||
|
||||
ValueType = arraytype->ElementType;
|
||||
if (ValueType->GetRegType() != REGT_INT)
|
||||
if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT)
|
||||
{
|
||||
// int arrays only for now
|
||||
ScriptPosition.Message(MSG_ERROR, "Only integer arrays are supported.");
|
||||
ScriptPosition.Message(MSG_ERROR, "Only numeric arrays are supported.");
|
||||
delete this;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3014,7 +3014,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
|||
ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit start = Array->Emit(build);
|
||||
ExpEmit dest(build, REGT_INT);
|
||||
PArray *const arraytype = static_cast<PArray*>(Array->ValueType);
|
||||
PType *const elementtype = arraytype->ElementType;
|
||||
ExpEmit dest(build, elementtype->GetRegType());
|
||||
|
||||
if (start.Konst)
|
||||
{
|
||||
ExpEmit tmpstart(build, REGT_POINTER);
|
||||
|
@ -3024,19 +3027,30 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
|||
if (index->isConstant())
|
||||
{
|
||||
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
|
||||
if (indexval >= static_cast<PArray*>(Array->ValueType)->ElementCount)
|
||||
if (indexval >= arraytype->ElementCount)
|
||||
{
|
||||
I_Error("Array index out of bounds");
|
||||
}
|
||||
indexval <<= 2;
|
||||
build->Emit(OP_LW, dest.RegNum, start.RegNum, build->GetConstantInt(indexval));
|
||||
indexval *= arraytype->ElementSize;
|
||||
build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum,
|
||||
start.RegNum, build->GetConstantInt(indexval));
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpEmit indexv(index->Emit(build));
|
||||
build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, 2);
|
||||
build->Emit(OP_BOUND, indexv.RegNum, static_cast<PArray*>(Array->ValueType)->ElementCount);
|
||||
build->Emit(OP_LW_R, dest.RegNum, start.RegNum, indexv.RegNum);
|
||||
int shiftbits = 0;
|
||||
while (1u << shiftbits < arraytype->ElementSize)
|
||||
{
|
||||
shiftbits++;
|
||||
}
|
||||
assert(1 << shiftbits == arraytype->ElementSize && "Element sizes other than power of 2 are not implemented");
|
||||
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
|
||||
if (shiftbits > 0)
|
||||
{
|
||||
build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits);
|
||||
}
|
||||
build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that
|
||||
dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register
|
||||
indexv.Free(build);
|
||||
}
|
||||
start.Free(build);
|
||||
|
|
|
@ -531,14 +531,14 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl
|
|||
sc.ScriptError("Native classes may not have user variables");
|
||||
}
|
||||
|
||||
// Read the type and make sure it's int.
|
||||
// Read the type and make sure it's acceptable.
|
||||
sc.MustGetAnyToken();
|
||||
if (sc.TokenType != TK_Int)
|
||||
if (sc.TokenType != TK_Int && sc.TokenType != TK_Float)
|
||||
{
|
||||
sc.ScriptMessage("User variables must be of type int");
|
||||
sc.ScriptMessage("User variables must be of type 'int' or 'float'");
|
||||
FScriptPosition::ErrorCounter++;
|
||||
}
|
||||
type = TypeSInt32;
|
||||
type = sc.TokenType == TK_Int ? (PType *)TypeSInt32 : (PType *)TypeFloat64;
|
||||
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
// For now, restrict user variables to those that begin with "user_" to guarantee
|
||||
|
@ -584,11 +584,9 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl
|
|||
}
|
||||
sc.MustGetToken(';');
|
||||
|
||||
PField *sym = new PField(symname, type, 0);
|
||||
sym->Offset = cls->Extend(sizeof(int) * maxelems);
|
||||
if (symt->AddSymbol(sym) == NULL)
|
||||
PField *sym = cls->AddField(symname, type, 0);
|
||||
if (sym == NULL)
|
||||
{
|
||||
delete sym;
|
||||
sc.ScriptMessage ("'%s' is already defined in '%s'.",
|
||||
symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global");
|
||||
FScriptPosition::ErrorCounter++;
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "v_palette.h"
|
||||
#include "d_player.h"
|
||||
#include "farchive.h"
|
||||
#include "a_hexenglobal.h"
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ const char *GetVersionString();
|
|||
|
||||
// Use 4500 as the base git save version, since it's higher than the
|
||||
// SVN revision ever got.
|
||||
#define SAVEVER 4533
|
||||
#define SAVEVER 4535
|
||||
|
||||
#define SAVEVERSTRINGIFY2(x) #x
|
||||
#define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x)
|
||||
|
|
|
@ -203,6 +203,7 @@ ACTOR Actor native //: Thinker
|
|||
action native A_PrintBold(string whattoprint, float time = 0, name fontname = "");
|
||||
action native A_Log(string whattoprint);
|
||||
action native A_LogInt(int whattoprint);
|
||||
action native A_LogFloat(float whattoprint);
|
||||
action native A_SetTranslucent(float alpha, int style = 0);
|
||||
action native A_FadeIn(float reduce = 0.1, int flags = 0);
|
||||
action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true
|
||||
|
@ -275,9 +276,11 @@ ACTOR Actor native //: Thinker
|
|||
action native A_SetArg(int pos, int value);
|
||||
action native A_SetUserVar(name varname, int value);
|
||||
action native A_SetUserArray(name varname, int index, int value);
|
||||
action native A_SetUserVarFloat(name varname, float value);
|
||||
action native A_SetUserArrayFloat(name varname, int index, float value);
|
||||
action native A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0);
|
||||
action native A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake");
|
||||
action native A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1);
|
||||
action native A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1, int falloff = 0, int highpoint = 0);
|
||||
action native A_SetTics(int tics);
|
||||
action native A_SetDamageType(name damagetype);
|
||||
action native A_DropItem(class<Actor> item, int dropamount = -1, int chance = 256);
|
||||
|
@ -317,7 +320,7 @@ ACTOR Actor native //: Thinker
|
|||
action native A_SetRipMax(int maximum);
|
||||
action native A_SetChaseThreshold(int threshold, bool def = false, int ptr = AAPTR_DEFAULT);
|
||||
action native state A_CheckProximity(state jump, class<Actor> classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
action native state A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
action native state A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0);
|
||||
action native state A_CheckSightOrRange(float distance, state label, bool two_dimension = false);
|
||||
action native state A_CheckRange(float distance, state label, bool two_dimension = false);
|
||||
action native bool A_FaceMovementDirection(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
|
|
|
@ -527,6 +527,8 @@ enum
|
|||
CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self.
|
||||
CBF_DROPOFF = 1 << 5, //Check for dropoffs.
|
||||
CBF_NOACTORS = 1 << 6, //Don't check actors.
|
||||
CBF_ABSOLUTEPOS = 1 << 7, //Absolute position for offsets.
|
||||
CBF_ABSOLUTEANGLE = 1 << 8, //Absolute angle for offsets.
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
Loading…
Reference in a new issue