This commit is contained in:
Christoph Oelckers 2016-04-04 12:06:42 +02:00
commit 5d099590cd
45 changed files with 3177 additions and 1509 deletions

View file

@ -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

View file

@ -257,10 +257,12 @@ DObject::DObject (PClass *inClass)
DObject::~DObject ()
{
if (!PClass::bShutdown)
{
PClass *type = GetClass();
if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown)
{
DObject **probe;
PClass *type = GetClass();
if (!(ObjectFlags & OF_YesReallyDelete))
{
@ -298,6 +300,8 @@ DObject::~DObject ()
}
}
}
type->DestroySpecials(this);
}
}
void DObject::Destroy ()
@ -417,46 +421,15 @@ void DObject::SerializeUserVars(FArchive &arc)
if (arc.IsStoring())
{
// Write all user variables.
for (; symt != NULL; symt = symt->ParentSymbolTable)
// Write all fields that aren't serialized by native code.
GetClass()->WriteValue(arc, this);
}
else if (SaveVersion >= 4535)
{
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;
GetClass()->ReadValue(arc, this);
}
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;
}
else
{
{ // Old version that only deals with ints
// Read user variables until 'None' is encountered.
arc << varname;
while (varname != NAME_None)

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -42,6 +42,7 @@ class AActor;
class player_t;
struct pspdef_s;
struct FState;
class DThinker;
class FThinkerIterator;

View file

@ -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;

View file

@ -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 ();

View file

@ -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"

View file

@ -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;
}
//

View file

@ -2025,7 +2025,7 @@ void FLevelLocals::Tick ()
//
//==========================================================================
void FLevelLocals::AddScroller (DScroller *scroller, int secnum)
void FLevelLocals::AddScroller (int secnum)
{
if (secnum < 0)
{

View file

@ -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

View file

@ -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))
{
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
{
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;
}
assert(m_CountdownStart > 0);
intensity = Scale(intensity, scalar, m_CountdownStart);
}
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);
}

View file

@ -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);

View file

@ -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);
TDeletingArray<SBarInfoCommand *> commands[2];
bool truth;
TArray<SBarInfoCommand *> commands[2];
};
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

View file

@ -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);

View file

@ -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"

View file

@ -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)
{

View file

@ -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,10 +4576,17 @@ 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))
{
if (!type->IsKindOf(RUNTIME_CLASS(PFloat)))
{
type->SetValue(addr, value);
}
else
{
type->SetValue(addr, FIXED2DBL(value));
}
}
}
static int GetUserVariable(AActor *self, FName varname, int index)
@ -4576,10 +4594,25 @@ 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))
{
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:

View file

@ -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;

View file

@ -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"

View file

@ -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;
}

View file

@ -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
View 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
View 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(&sectors[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(&sectors[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);
}

View file

@ -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;

View file

@ -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(&sectors[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(&sectors[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;

View file

@ -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

View file

@ -5,6 +5,7 @@
#include "r_defs.h"
#include "m_bbox.h"
class DPolyAction;
struct FPolyVertex
{

View file

@ -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++)

View file

@ -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"

View file

@ -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);
}
}
}

View file

@ -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"

View file

@ -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];
}
iterator end()
const_iterator begin() const
{
return &Array[Count];
return &Array[0];
}
const_iterator cbegin() const
{
return &Array[0];
}
iterator end()
{
return &Array[Count];
}
const_iterator end() const
{
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)

View file

@ -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__

View file

@ -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"

View file

@ -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 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.
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 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.
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,6 +6762,8 @@ 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)
@ -6702,6 +6772,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
PARAM_STATE(block)
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);

View file

@ -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);

View file

@ -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++;

View file

@ -52,7 +52,6 @@
#include "v_palette.h"
#include "d_player.h"
#include "farchive.h"
#include "a_hexenglobal.h"

View file

@ -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)

View file

@ -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);

View file

@ -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