mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-03-12 21:28:17 +00:00
Merge branch 'master' of https://github.com/coelckers/gzdoom
# Conflicts: # wadsrc_bm/static/filter/doom.doom1/gldefs.bm # wadsrc_bm/static/filter/heretic/gldefs.bm # wadsrc_bm/static/filter/hexen/gldefs.bm # wadsrc_bm/static/filter/strife/gldefs.bm # wadsrc_lights/static/filter/doom.doom1/gldefs.txt # wadsrc_lights/static/filter/heretic/gldefs.txt # wadsrc_lights/static/filter/hexen/gldefs.txt # wadsrc_lights/static/filter/strife/gldefs.txt
This commit is contained in:
commit
f45561b5e8
83 changed files with 1602 additions and 1160 deletions
|
@ -302,6 +302,33 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
|||
set( CMAKE_CXX_FLAGS_MINSIZEREL "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}" )
|
||||
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" )
|
||||
|
||||
# Support for the GCC/Clang sanitizers.
|
||||
set( WITH_ASAN 0 CACHE BOOL "Enable the Address Sanitizer")
|
||||
if( NOT CMAKE_COMPILER_IS_GNUCXX )
|
||||
set( WITH_MSAN 0 CACHE BOOL "Enable the Memory Sanitizer")
|
||||
endif( NOT CMAKE_COMPILER_IS_GNUCXX )
|
||||
set( WITH_UBSAN 0 CACHE BOOL "Enable the Undefined Behavior Sanitizer")
|
||||
if( WITH_MSAN )
|
||||
if ( WITH_ASAN OR WITH_UBSAN )
|
||||
message( SEND_ERROR "You can't use MSAN with either ASAN or UBSAN." )
|
||||
endif ( WITH_ASAN OR WITH_UBSAN )
|
||||
endif( WITH_MSAN )
|
||||
|
||||
set( SANITIZER_FLAG "" )
|
||||
if( WITH_ASAN )
|
||||
set( SANITIZER_FLAG "-fsanitize=address" )
|
||||
if ( WITH_UBSAN )
|
||||
set( SANITIZER_FLAG "${SANITIZER_FLAG},undefined" )
|
||||
endif( WITH_UBSAN )
|
||||
elseif( WITH_MSAN )
|
||||
set( SANITIZER_FLAG "-fsanitize=memory" )
|
||||
elseif( WITH_UBSAN )
|
||||
set( SANITIZER_FLAG "-fsanitize=undefined" )
|
||||
endif( WITH_ASAN )
|
||||
|
||||
set( CMAKE_CXX_FLAGS "${SANITIZER_FLAG} ${CMAKE_CXX_FLAGS}" )
|
||||
set( CMAKE_C_FLAGS "${SANITIZER_FLAG} ${CMAKE_C_FLAGS}" )
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.5")
|
||||
set( CMAKE_C_FLAGS "-Wno-unused-result ${CMAKE_C_FLAGS}" )
|
||||
set( CMAKE_CXX_FLAGS "-Wno-unused-result ${CMAKE_CXX_FLAGS}" )
|
||||
|
|
|
@ -3051,7 +3051,7 @@ void AM_Drawer ()
|
|||
return;
|
||||
|
||||
bool allmap = (level.flags2 & LEVEL2_ALLMAP) != 0;
|
||||
bool allthings = allmap && players[consoleplayer].mo->FindInventory(RUNTIME_CLASS(APowerScanner), true) != NULL;
|
||||
bool allthings = allmap && players[consoleplayer].mo->FindInventory(PClass::FindActor(NAME_PowerScanner), true) != nullptr;
|
||||
|
||||
if (am_portaloverlay)
|
||||
{
|
||||
|
|
|
@ -1890,16 +1890,16 @@ static int PatchMisc (int dummy)
|
|||
"Minotaur",
|
||||
NULL
|
||||
};
|
||||
static const PClass * const *types[] =
|
||||
static const char *const types[] =
|
||||
{
|
||||
&RUNTIME_CLASS_CASTLESS(APowerInvulnerable),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerStrength),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerInvisibility),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerIronFeet),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerLightAmp),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerWeaponLevel2),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerSpeed),
|
||||
&RUNTIME_CLASS_CASTLESS(APowerMinotaur)
|
||||
"PowerInvulnerable",
|
||||
"PowerStrength",
|
||||
"PowerInvisibility",
|
||||
"PowerIronFeet",
|
||||
"PowerLightAmp",
|
||||
"PowerWeaponLevel2",
|
||||
"PowerSpeed",
|
||||
"PowerMinotaur"
|
||||
};
|
||||
int i;
|
||||
|
||||
|
@ -1925,7 +1925,7 @@ static int PatchMisc (int dummy)
|
|||
}
|
||||
else if (a > 0)
|
||||
{
|
||||
static_cast<APowerup *>(GetDefaultByType (*types[i]))->BlendColor = PalEntry(
|
||||
static_cast<APowerup *>(GetDefaultByName (types[i]))->BlendColor = PalEntry(
|
||||
BYTE(clamp(a,0.f,1.f)*255.f),
|
||||
clamp(r,0,255),
|
||||
clamp(g,0,255),
|
||||
|
@ -1933,7 +1933,7 @@ static int PatchMisc (int dummy)
|
|||
}
|
||||
else
|
||||
{
|
||||
static_cast<APowerup *>(GetDefaultByType (*types[i]))->BlendColor = 0;
|
||||
static_cast<APowerup *>(GetDefaultByName (types[i]))->BlendColor = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -560,6 +560,7 @@ void PType::StaticInit()
|
|||
RUNTIME_CLASS(PClassPointer)->TypeTableType = RUNTIME_CLASS(PClassPointer);
|
||||
RUNTIME_CLASS(PEnum)->TypeTableType = RUNTIME_CLASS(PEnum);
|
||||
RUNTIME_CLASS(PArray)->TypeTableType = RUNTIME_CLASS(PArray);
|
||||
RUNTIME_CLASS(PResizableArray)->TypeTableType = RUNTIME_CLASS(PResizableArray);
|
||||
RUNTIME_CLASS(PDynArray)->TypeTableType = RUNTIME_CLASS(PDynArray);
|
||||
RUNTIME_CLASS(PMap)->TypeTableType = RUNTIME_CLASS(PMap);
|
||||
RUNTIME_CLASS(PStruct)->TypeTableType = RUNTIME_CLASS(PStruct);
|
||||
|
@ -1652,7 +1653,11 @@ void PPointer::SetPointer(void *base, unsigned offset, TArray<size_t> *special)
|
|||
|
||||
void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const
|
||||
{
|
||||
if (PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
|
||||
if (PointedType->IsKindOf(RUNTIME_CLASS(PClassClass)))
|
||||
{
|
||||
ar(key, *(PClass **)addr);
|
||||
}
|
||||
else if (PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
|
||||
{
|
||||
ar(key, *(DObject **)addr);
|
||||
}
|
||||
|
@ -1671,7 +1676,13 @@ void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) con
|
|||
|
||||
bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
|
||||
{
|
||||
if (PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
|
||||
if (PointedType->IsKindOf(RUNTIME_CLASS(PClassClass)))
|
||||
{
|
||||
bool res = false;
|
||||
::Serialize(ar, key, *(PClass **)addr, (PClass**)nullptr);
|
||||
return res;
|
||||
}
|
||||
else if (PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
|
||||
{
|
||||
bool res = false;
|
||||
::Serialize(ar, key, *(DObject **)addr, nullptr, &res);
|
||||
|
@ -2040,6 +2051,80 @@ PArray *NewArray(PType *type, unsigned int count)
|
|||
return (PArray *)atype;
|
||||
}
|
||||
|
||||
/* PArray *****************************************************************/
|
||||
|
||||
IMPLEMENT_CLASS(PResizableArray, false, false)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PArray - Default Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PResizableArray::PResizableArray()
|
||||
{
|
||||
mDescriptiveName = "ResizableArray";
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PArray - Parameterized Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PResizableArray::PResizableArray(PType *etype)
|
||||
: PArray(etype, 0)
|
||||
{
|
||||
mDescriptiveName.Format("ResizableArray<%s>", etype->DescriptiveName());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PArray :: IsMatch
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool PResizableArray::IsMatch(intptr_t id1, intptr_t id2) const
|
||||
{
|
||||
const PType *elemtype = (const PType *)id1;
|
||||
unsigned int count = (unsigned int)(intptr_t)id2;
|
||||
|
||||
return elemtype == ElementType && count == 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PArray :: GetTypeIDs
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void PResizableArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const
|
||||
{
|
||||
id1 = (intptr_t)ElementType;
|
||||
id2 = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// NewResizableArray
|
||||
//
|
||||
// Returns a PArray for the given type and size, making sure not to create
|
||||
// duplicates.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PResizableArray *NewResizableArray(PType *type)
|
||||
{
|
||||
size_t bucket;
|
||||
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, &bucket);
|
||||
if (atype == NULL)
|
||||
{
|
||||
atype = new PResizableArray(type);
|
||||
TypeTable.AddType(atype, RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, bucket);
|
||||
}
|
||||
return (PResizableArray *)atype;
|
||||
}
|
||||
|
||||
/* PDynArray **************************************************************/
|
||||
|
||||
IMPLEMENT_CLASS(PDynArray, false, true)
|
||||
|
|
|
@ -660,6 +660,20 @@ protected:
|
|||
PArray();
|
||||
};
|
||||
|
||||
class PResizableArray : public PArray
|
||||
{
|
||||
DECLARE_CLASS(PResizableArray, PArray);
|
||||
HAS_OBJECT_POINTERS;
|
||||
public:
|
||||
PResizableArray(PType *etype);
|
||||
|
||||
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
|
||||
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
|
||||
|
||||
protected:
|
||||
PResizableArray();
|
||||
};
|
||||
|
||||
class PDynArray : public PCompoundType
|
||||
{
|
||||
DECLARE_CLASS(PDynArray, PCompoundType);
|
||||
|
@ -919,6 +933,7 @@ extern FTypeTable TypeTable;
|
|||
// Returns a type from the TypeTable. Will create one if it isn't present.
|
||||
PMap *NewMap(PType *keytype, PType *valuetype);
|
||||
PArray *NewArray(PType *type, unsigned int count);
|
||||
PResizableArray *NewResizableArray(PType *type);
|
||||
PDynArray *NewDynArray(PType *type);
|
||||
PPointer *NewPointer(PType *type, bool isconst = false);
|
||||
PClassPointer *NewClassPointer(PClass *restrict);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "v_palette.h"
|
||||
#include "serializer.h"
|
||||
#include "r_utility.h"
|
||||
#include "virtual.h"
|
||||
|
||||
#include "r_data/colormaps.h"
|
||||
|
||||
|
@ -182,6 +183,25 @@ void APowerup::InitEffect ()
|
|||
{
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(APowerup, InitEffect)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(APowerup);
|
||||
self->InitEffect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APowerup::CallInitEffect()
|
||||
{
|
||||
IFVIRTUAL(APowerup, InitEffect)
|
||||
{
|
||||
VMValue params[1] = { (DObject*)this };
|
||||
VMFrameStack stack;
|
||||
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
|
||||
}
|
||||
else InitEffect();
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerup :: DoEffect
|
||||
|
@ -230,6 +250,25 @@ void APowerup::EndEffect ()
|
|||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(APowerup, EndEffect)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(APowerup);
|
||||
self->EndEffect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APowerup::CallEndEffect()
|
||||
{
|
||||
IFVIRTUAL(APowerup, EndEffect)
|
||||
{
|
||||
VMValue params[1] = { (DObject*)this };
|
||||
VMFrameStack stack;
|
||||
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
|
||||
}
|
||||
else EndEffect();
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerup :: Destroy
|
||||
|
@ -238,7 +277,7 @@ void APowerup::EndEffect ()
|
|||
|
||||
void APowerup::Destroy ()
|
||||
{
|
||||
EndEffect ();
|
||||
CallEndEffect ();
|
||||
Super::Destroy ();
|
||||
}
|
||||
|
||||
|
@ -325,7 +364,7 @@ AInventory *APowerup::CreateCopy (AActor *other)
|
|||
// properly attached to anything yet.
|
||||
Owner = other;
|
||||
// Actually activate the powerup.
|
||||
InitEffect ();
|
||||
CallInitEffect ();
|
||||
// Clear the Owner field, unless it was
|
||||
// changed by the activation, for example,
|
||||
// if this instance is a morph powerup;
|
||||
|
@ -599,7 +638,7 @@ void APowerInvisibility::InitEffect ()
|
|||
flags5 &= ~(Owner->flags5 & INVISIBILITY_FLAGS5);
|
||||
Owner->flags5 |= flags5 & INVISIBILITY_FLAGS5;
|
||||
|
||||
DoEffect();
|
||||
CallDoEffect();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1251,17 +1290,13 @@ void APowerSpeed::DoEffect ()
|
|||
}
|
||||
}
|
||||
|
||||
// Minotaur (aka Dark Servant) powerup ---------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerMinotaur, false, false)
|
||||
|
||||
// Targeter powerup ---------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerTargeter, false, false)
|
||||
|
||||
void APowerTargeter::Travelled ()
|
||||
{
|
||||
InitEffect ();
|
||||
CallInitEffect ();
|
||||
}
|
||||
|
||||
void APowerTargeter::InitEffect ()
|
||||
|
@ -1299,14 +1334,14 @@ void APowerTargeter::AttachToOwner(AActor *other)
|
|||
Super::AttachToOwner(other);
|
||||
|
||||
// Let's actually properly call this for the targeters.
|
||||
InitEffect();
|
||||
CallInitEffect();
|
||||
}
|
||||
|
||||
bool APowerTargeter::HandlePickup(AInventory *item)
|
||||
{
|
||||
if (Super::HandlePickup(item))
|
||||
{
|
||||
InitEffect(); // reset the HUD sprites
|
||||
CallInitEffect(); // reset the HUD sprites
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1370,482 +1405,6 @@ void APowerTargeter::PositionAccuracy ()
|
|||
}
|
||||
}
|
||||
|
||||
// Frightener Powerup --------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerFrightener, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerFrightener :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerFrightener::InitEffect ()
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
Owner->player->cheats |= CF_FRIGHTENING;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerFrightener :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerFrightener::EndEffect ()
|
||||
{
|
||||
Super::EndEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
Owner->player->cheats &= ~CF_FRIGHTENING;
|
||||
}
|
||||
|
||||
// Buddha Powerup --------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerBuddha, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerBuddha :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerBuddha::InitEffect ()
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
Owner->player->cheats |= CF_BUDDHA;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerBuddha :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerBuddha::EndEffect ()
|
||||
{
|
||||
Super::EndEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
Owner->player->cheats &= ~CF_BUDDHA;
|
||||
}
|
||||
|
||||
// Scanner powerup ----------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerScanner, false, false)
|
||||
|
||||
// Time freezer powerup -----------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS( APowerTimeFreezer, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerTimeFreezer :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerTimeFreezer::InitEffect()
|
||||
{
|
||||
int freezemask;
|
||||
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner == NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
// When this powerup is in effect, pause the music.
|
||||
S_PauseSound(false, false);
|
||||
|
||||
// Give the player and his teammates the power to move when time is frozen.
|
||||
freezemask = 1 << (Owner->player - players);
|
||||
Owner->player->timefreezer |= freezemask;
|
||||
for (int i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] &&
|
||||
players[i].mo != NULL &&
|
||||
players[i].mo->IsTeammate(Owner)
|
||||
)
|
||||
{
|
||||
players[i].timefreezer |= freezemask;
|
||||
}
|
||||
}
|
||||
|
||||
// [RH] The effect ends one tic after the counter hits zero, so make
|
||||
// sure we start at an odd count.
|
||||
EffectTics += !(EffectTics & 1);
|
||||
if ((EffectTics & 1) == 0)
|
||||
{
|
||||
EffectTics++;
|
||||
}
|
||||
// Make sure the effect starts and ends on an even tic.
|
||||
if ((level.time & 1) == 0)
|
||||
{
|
||||
level.flags2 |= LEVEL2_FROZEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compensate for skipped tic, but beware of overflow.
|
||||
if(EffectTics < INT_MAX)
|
||||
EffectTics++;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerTimeFreezer :: DoEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerTimeFreezer::DoEffect()
|
||||
{
|
||||
Super::DoEffect();
|
||||
// [RH] Do not change LEVEL_FROZEN on odd tics, or the Revenant's tracer
|
||||
// will get thrown off.
|
||||
// [ED850] Don't change it if the player is predicted either.
|
||||
if (level.time & 1 || (Owner != NULL && Owner->player != NULL && Owner->player->cheats & CF_PREDICTING))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// [RH] The "blinking" can't check against EffectTics exactly or it will
|
||||
// never happen, because InitEffect ensures that EffectTics will always
|
||||
// be odd when level.time is even.
|
||||
if ( EffectTics > 4*32
|
||||
|| (( EffectTics > 3*32 && EffectTics <= 4*32 ) && ((EffectTics + 1) & 15) != 0 )
|
||||
|| (( EffectTics > 2*32 && EffectTics <= 3*32 ) && ((EffectTics + 1) & 7) != 0 )
|
||||
|| (( EffectTics > 32 && EffectTics <= 2*32 ) && ((EffectTics + 1) & 3) != 0 )
|
||||
|| (( EffectTics > 0 && EffectTics <= 1*32 ) && ((EffectTics + 1) & 1) != 0 ))
|
||||
level.flags2 |= LEVEL2_FROZEN;
|
||||
else
|
||||
level.flags2 &= ~LEVEL2_FROZEN;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerTimeFreezer :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerTimeFreezer::EndEffect()
|
||||
{
|
||||
int i;
|
||||
|
||||
Super::EndEffect();
|
||||
|
||||
// If there is an owner, remove the timefreeze flag corresponding to
|
||||
// her from all players.
|
||||
if (Owner != NULL && Owner->player != NULL)
|
||||
{
|
||||
int freezemask = ~(1 << (Owner->player - players));
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
players[i].timefreezer &= freezemask;
|
||||
}
|
||||
}
|
||||
|
||||
// Are there any players who still have timefreezer bits set?
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (playeringame[i] && players[i].timefreezer != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAXPLAYERS)
|
||||
{
|
||||
// No, so allow other actors to move about freely once again.
|
||||
level.flags2 &= ~LEVEL2_FROZEN;
|
||||
|
||||
// Also, turn the music back on.
|
||||
S_ResumeSound(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Damage powerup ------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerDamage, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDamage :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDamage::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
// Use sound channel 5 to avoid interference with other actions.
|
||||
if (Owner != NULL) S_Sound(Owner, 5, SeeSound, 1.0f, ATTN_NONE);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDamage :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDamage::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
// Use sound channel 5 to avoid interference with other actions.
|
||||
if (Owner != NULL) S_Sound(Owner, 5, DeathSound, 1.0f, ATTN_NONE);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDamage :: ModifyDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDamage::ModifyDamage(int damage, FName damageType, int &newdamage, bool passive)
|
||||
{
|
||||
if (!passive && damage > 0)
|
||||
{
|
||||
int newdam;
|
||||
DmgFactors *df = GetClass()->DamageFactors;
|
||||
if (df != NULL && df->CountUsed() != 0)
|
||||
{
|
||||
newdam = MAX(1, df->Apply(damageType, damage));// don't allow zero damage as result of an underflow
|
||||
}
|
||||
else
|
||||
{
|
||||
newdam = damage * 4;
|
||||
}
|
||||
if (Owner != NULL && newdam > damage) S_Sound(Owner, 5, ActiveSound, 1.0f, ATTN_NONE);
|
||||
newdamage = damage = newdam;
|
||||
}
|
||||
if (Inventory != NULL) Inventory->ModifyDamage(damage, damageType, newdamage, passive);
|
||||
}
|
||||
|
||||
// Quarter damage powerup ------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerProtection, false, false)
|
||||
|
||||
#define PROTECTION_FLAGS3 (MF3_NORADIUSDMG | MF3_DONTMORPH | MF3_DONTSQUASH | MF3_DONTBLAST | MF3_NOTELEOTHER)
|
||||
#define PROTECTION_FLAGS5 (MF5_NOPAIN | MF5_DONTRIP)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerProtection :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerProtection::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner != NULL)
|
||||
{
|
||||
S_Sound(Owner, CHAN_AUTO, SeeSound, 1.0f, ATTN_NONE);
|
||||
|
||||
// Transfer various protection flags if owner does not already have them.
|
||||
// If the owner already has the flag, clear it from the powerup.
|
||||
// If the powerup still has a flag set, add it to the owner.
|
||||
flags3 &= ~(Owner->flags3 & PROTECTION_FLAGS3);
|
||||
Owner->flags3 |= flags3 & PROTECTION_FLAGS3;
|
||||
|
||||
flags5 &= ~(Owner->flags5 & PROTECTION_FLAGS5);
|
||||
Owner->flags5 |= flags5 & PROTECTION_FLAGS5;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerProtection :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerProtection::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
if (Owner != NULL)
|
||||
{
|
||||
S_Sound(Owner, CHAN_AUTO, DeathSound, 1.0f, ATTN_NONE);
|
||||
Owner->flags3 &= ~(flags3 & PROTECTION_FLAGS3);
|
||||
Owner->flags5 &= ~(flags5 & PROTECTION_FLAGS5);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerProtection :: AbsorbDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerProtection::ModifyDamage(int damage, FName damageType, int &newdamage, bool passive)
|
||||
{
|
||||
if (passive && damage > 0)
|
||||
{
|
||||
int newdam;
|
||||
DmgFactors *df = GetClass()->DamageFactors;
|
||||
if (df != NULL && df->CountUsed() != 0)
|
||||
{
|
||||
newdam = MAX(0, df->Apply(damageType, damage));
|
||||
}
|
||||
else
|
||||
{
|
||||
newdam = damage / 4;
|
||||
}
|
||||
if (Owner != NULL && newdam < damage) S_Sound(Owner, CHAN_AUTO, ActiveSound, 1.0f, ATTN_NONE);
|
||||
newdamage = damage = newdam;
|
||||
}
|
||||
if (Inventory != NULL)
|
||||
{
|
||||
Inventory->ModifyDamage(damage, damageType, newdamage, passive);
|
||||
}
|
||||
}
|
||||
|
||||
// Drain rune -------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerDrain, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ARuneDrain :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDrain::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
// Give the player the power to drain life from opponents when he damages them.
|
||||
Owner->player->cheats |= CF_DRAIN;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ARuneDrain :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDrain::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner != NULL && Owner->player != NULL)
|
||||
{
|
||||
// Take away the drain power.
|
||||
Owner->player->cheats &= ~CF_DRAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Regeneration rune -------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerRegeneration, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerRegeneration :: DoEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerRegeneration::DoEffect()
|
||||
{
|
||||
Super::DoEffect();
|
||||
if (Owner != NULL && Owner->health > 0 && (level.time & 31) == 0)
|
||||
{
|
||||
if (P_GiveBody(Owner, int(Strength)))
|
||||
{
|
||||
S_Sound(Owner, CHAN_ITEM, "*regenerate", 1, ATTN_NORM );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// High jump rune -------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerHighJump, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ARuneHighJump :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerHighJump::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
// Give the player the power to jump much higher.
|
||||
Owner->player->cheats |= CF_HIGHJUMP;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ARuneHighJump :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerHighJump::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner != NULL && Owner->player != NULL)
|
||||
{
|
||||
// Take away the high jump power.
|
||||
Owner->player->cheats &= ~CF_HIGHJUMP;
|
||||
}
|
||||
}
|
||||
|
||||
// Double firing speed rune ---------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerDoubleFiringSpeed, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDoubleFiringSpeed :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDoubleFiringSpeed::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
// Give the player the power to shoot twice as fast.
|
||||
Owner->player->cheats |= CF_DOUBLEFIRINGSPEED;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDoubleFiringSpeed :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDoubleFiringSpeed::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner != NULL && Owner->player != NULL)
|
||||
{
|
||||
// Take away the shooting twice as fast power.
|
||||
Owner->player->cheats &= ~CF_DOUBLEFIRINGSPEED;
|
||||
}
|
||||
}
|
||||
|
||||
// Morph powerup ------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerMorph, false, true)
|
||||
|
@ -1862,7 +1421,6 @@ DEFINE_FIELD(APowerMorph, MorphFlash)
|
|||
DEFINE_FIELD(APowerMorph, UnMorphFlash)
|
||||
DEFINE_FIELD(APowerMorph, MorphStyle)
|
||||
DEFINE_FIELD(APowerMorph, MorphedPlayer)
|
||||
DEFINE_FIELD(APowerMorph, bInUndoMorph)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
@ -1880,116 +1438,3 @@ void APowerMorph::Serialize(FSerializer &arc)
|
|||
("morphedplayer", MorphedPlayer);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerMorph :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerMorph::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner != nullptr && Owner->player != nullptr && PlayerClass != nullptr)
|
||||
{
|
||||
player_t *realplayer = Owner->player; // Remember the identity of the player
|
||||
if (P_MorphPlayer(realplayer, realplayer, PlayerClass, INT_MAX/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash))
|
||||
{
|
||||
Owner = realplayer->mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
|
||||
ItemFlags |= IF_CREATECOPYMOVED; // Let the caller know the "real" owner has changed (to the morphed actor)
|
||||
MorphedPlayer = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
|
||||
}
|
||||
else // morph failed - give the caller an opportunity to fail the pickup completely
|
||||
{
|
||||
ItemFlags |= IF_INITEFFECTFAILED; // Let the caller know that the activation failed (can fail the pickup if appropriate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerMorph :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerMorph::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
|
||||
// Abort if owner already destroyed or unmorphed
|
||||
if (Owner == nullptr || MorphedPlayer == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if owner is dead; their Die() method will
|
||||
// take care of any required unmorphing on death.
|
||||
if (MorphedPlayer->health <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Unmorph if possible
|
||||
if (!bInUndoMorph)
|
||||
{
|
||||
int savedMorphTics = MorphedPlayer->morphTics;
|
||||
P_UndoPlayerMorph (MorphedPlayer, MorphedPlayer, 0, !!(MorphedPlayer->MorphStyle & MORPH_UNDOALWAYS));
|
||||
|
||||
// Abort if unmorph failed; in that case,
|
||||
// set the usual retry timer and return.
|
||||
if (MorphedPlayer != NULL && MorphedPlayer->morphTics)
|
||||
{
|
||||
// Transfer retry timeout
|
||||
// to the powerup's timer.
|
||||
EffectTics = MorphedPlayer->morphTics;
|
||||
// Reload negative morph tics;
|
||||
// use actual value; it may
|
||||
// be in use for animation.
|
||||
MorphedPlayer->morphTics = savedMorphTics;
|
||||
// Try again some time later
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Unmorph suceeded
|
||||
MorphedPlayer = NULL;
|
||||
}
|
||||
|
||||
// Infinite Ammo Powerup -----------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerInfiniteAmmo, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerInfiniteAmmo :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerInfiniteAmmo::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner== NULL || Owner->player == NULL)
|
||||
return;
|
||||
|
||||
// Give the player infinite ammo
|
||||
Owner->player->cheats |= CF_INFINITEAMMO;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerInfiniteAmmo :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerInfiniteAmmo::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner != NULL && Owner->player != NULL)
|
||||
{
|
||||
// Take away the limitless ammo
|
||||
Owner->player->cheats &= ~CF_INFINITEAMMO;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,15 @@ public:
|
|||
FNameNoInit Mode;
|
||||
double Strength;
|
||||
|
||||
protected:
|
||||
public:
|
||||
virtual void InitEffect ();
|
||||
virtual void DoEffect () override;
|
||||
virtual void EndEffect ();
|
||||
|
||||
protected:
|
||||
void CallInitEffect();
|
||||
void CallEndEffect();
|
||||
|
||||
friend void EndAllPowerupEffects(AInventory *item);
|
||||
friend void InitAllPowerupEffects(AInventory *item);
|
||||
};
|
||||
|
@ -156,16 +160,6 @@ public:
|
|||
|
||||
#define PSF_NOTRAIL 1
|
||||
|
||||
class APowerMinotaur : public APowerup
|
||||
{
|
||||
DECLARE_CLASS (APowerMinotaur, APowerup)
|
||||
};
|
||||
|
||||
class APowerScanner : public APowerup
|
||||
{
|
||||
DECLARE_CLASS (APowerScanner, APowerup)
|
||||
};
|
||||
|
||||
class APowerTargeter : public APowerup
|
||||
{
|
||||
DECLARE_CLASS (APowerTargeter, APowerup)
|
||||
|
@ -179,88 +173,6 @@ protected:
|
|||
virtual bool HandlePickup(AInventory *item) override;
|
||||
};
|
||||
|
||||
class APowerFrightener : public APowerup
|
||||
{
|
||||
DECLARE_CLASS (APowerFrightener, APowerup)
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
};
|
||||
|
||||
class APowerBuddha : public APowerup
|
||||
{
|
||||
DECLARE_CLASS (APowerBuddha, APowerup)
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
};
|
||||
|
||||
class APowerTimeFreezer : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerTimeFreezer, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect() override;
|
||||
virtual void DoEffect() override;
|
||||
virtual void EndEffect() override;
|
||||
};
|
||||
|
||||
class APowerDamage : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerDamage, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) override;
|
||||
};
|
||||
|
||||
class APowerProtection : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerProtection, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) override;
|
||||
};
|
||||
|
||||
class APowerDrain : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerDrain, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect() override;
|
||||
virtual void EndEffect() override;
|
||||
};
|
||||
|
||||
class APowerRegeneration : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerRegeneration, APowerup )
|
||||
protected:
|
||||
virtual void DoEffect() override;
|
||||
};
|
||||
|
||||
class APowerHighJump : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerHighJump, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect() override;
|
||||
virtual void EndEffect() override;
|
||||
};
|
||||
|
||||
class APowerDoubleFiringSpeed : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerDoubleFiringSpeed, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect() override;
|
||||
virtual void EndEffect() override;
|
||||
};
|
||||
|
||||
class APowerInfiniteAmmo : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerInfiniteAmmo, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect() override;
|
||||
virtual void EndEffect() override;
|
||||
};
|
||||
|
||||
class APowerMorph : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerMorph, APowerup )
|
||||
|
@ -268,18 +180,12 @@ class APowerMorph : public APowerup
|
|||
public:
|
||||
|
||||
virtual void Serialize(FSerializer &arc) override;
|
||||
void SetNoCallUndoMorph() { bInUndoMorph = true; }
|
||||
|
||||
// Variables
|
||||
PClassPlayerPawn *PlayerClass;
|
||||
PClassActor *MorphFlash, *UnMorphFlash;
|
||||
int MorphStyle;
|
||||
player_t *MorphedPlayer;
|
||||
bool bInUndoMorph; // Because P_UndoPlayerMorph() can call EndEffect recursively
|
||||
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
};
|
||||
|
||||
#endif //__A_ARTIFACTS_H__
|
||||
|
|
|
@ -724,9 +724,10 @@ void AInventory::AbsorbDamage (int damage, FName damageType, int &newdamage)
|
|||
|
||||
void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, bool passive)
|
||||
{
|
||||
if (Inventory != NULL)
|
||||
IFVIRTUAL(AInventory, ModifyDamage)
|
||||
{
|
||||
Inventory->ModifyDamage (damage, damageType, newdamage, passive);
|
||||
VMValue params[5] = { (DObject*)this, damage, int(damageType), &newdamage, passive };
|
||||
GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ public:
|
|||
|
||||
// still need to be done.
|
||||
virtual void AbsorbDamage(int damage, FName damageType, int &newdamage);
|
||||
virtual void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive);
|
||||
void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive);
|
||||
|
||||
// visual stuff is for later. Right now the VM has not yet access to the needed functionality.
|
||||
virtual bool DrawPowerup(int x, int y);
|
||||
|
|
|
@ -1888,6 +1888,7 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, monsterfallingdamage, LEVEL2_MONSTERFALLI
|
|||
DEFINE_FIELD_BIT(FLevelLocals, flags2, checkswitchrange, LEVEL2_CHECKSWITCHRANGE)
|
||||
DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND)
|
||||
DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS)
|
||||
DEFINE_FIELD_BIT(FLevelLocals, flags2, frozen, LEVEL2_FROZEN)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -154,6 +154,18 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
|
|||
return true;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_PlayerInfo, MorphPlayer)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(player_t);
|
||||
PARAM_POINTER(victim, player_t);
|
||||
PARAM_CLASS(spawntype, APlayerPawn);
|
||||
PARAM_INT(duration);
|
||||
PARAM_INT(style);
|
||||
PARAM_CLASS_DEF(enter_flash, AActor);
|
||||
PARAM_CLASS_DEF(exit_flash, AActor);
|
||||
ACTION_RETURN_BOOL(P_MorphPlayer(self, victim, spawntype, duration, style, enter_flash, exit_flash));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// FUNC P_UndoPlayerMorph
|
||||
|
@ -204,14 +216,15 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
|
|||
mo->target = pmo->target;
|
||||
mo->tracer = pmo->tracer;
|
||||
pmo->player = nullptr;
|
||||
mo->alternative = pmo->alternative = nullptr;
|
||||
|
||||
// Remove the morph power if the morph is being undone prematurely.
|
||||
auto pmtype = PClass::FindActor("PowerMorph");
|
||||
for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next)
|
||||
{
|
||||
next = item->Inventory;
|
||||
if (item->IsKindOf(RUNTIME_CLASS(APowerMorph)))
|
||||
if (item->IsKindOf(pmtype))
|
||||
{
|
||||
static_cast<APowerMorph *>(item)->SetNoCallUndoMorph();
|
||||
item->Destroy();
|
||||
}
|
||||
}
|
||||
|
@ -348,8 +361,6 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
|
|||
beastweap->Destroy ();
|
||||
}
|
||||
}
|
||||
mo->alternative = nullptr;
|
||||
pmo->alternative = nullptr;
|
||||
pmo->Destroy ();
|
||||
// Restore playerclass armor to its normal amount.
|
||||
AHexenArmor *hxarmor = mo->FindInventory<AHexenArmor>();
|
||||
|
@ -585,7 +596,7 @@ void EndAllPowerupEffects(AInventory *item)
|
|||
{
|
||||
if (item->IsKindOf(RUNTIME_CLASS(APowerup)))
|
||||
{
|
||||
static_cast<APowerup *>(item)->EndEffect();
|
||||
static_cast<APowerup *>(item)->CallEndEffect();
|
||||
}
|
||||
item = item->Inventory;
|
||||
}
|
||||
|
@ -605,7 +616,7 @@ void InitAllPowerupEffects(AInventory *item)
|
|||
{
|
||||
if (item->IsKindOf(RUNTIME_CLASS(APowerup)))
|
||||
{
|
||||
static_cast<APowerup *>(item)->InitEffect();
|
||||
static_cast<APowerup *>(item)->CallInitEffect();
|
||||
}
|
||||
item = item->Inventory;
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ class SBarInfoCommand
|
|||
}
|
||||
EColorRange GetTranslation(FScanner &sc)
|
||||
{
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
if (!sc.CheckToken(TK_Null)) sc.MustGetToken(TK_Identifier);
|
||||
EColorRange returnVal = CR_UNTRANSLATED;
|
||||
FString namedTranslation; //we must send in "[translation]"
|
||||
const BYTE *trans_ptr;
|
||||
|
|
|
@ -305,7 +305,6 @@ static void PrepareTransparentDoors(sector_t * sector)
|
|||
int notextures=0;
|
||||
int nobtextures=0;
|
||||
int selfref=0;
|
||||
int i;
|
||||
sector_t * nextsec=NULL;
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
@ -323,15 +322,15 @@ static void PrepareTransparentDoors(sector_t * sector)
|
|||
|
||||
if (sector->transdoor)
|
||||
{
|
||||
for (i=0; i<sector->linecount; i++)
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
if (sector->lines[i]->frontsector==sector->lines[i]->backsector)
|
||||
if (ln->frontsector == ln->backsector)
|
||||
{
|
||||
selfref++;
|
||||
continue;
|
||||
}
|
||||
|
||||
sector_t * sec=getNextSector(sector->lines[i], sector);
|
||||
sector_t * sec=getNextSector(ln, sector);
|
||||
if (sec==NULL)
|
||||
{
|
||||
solidwall=true;
|
||||
|
@ -341,15 +340,15 @@ static void PrepareTransparentDoors(sector_t * sector)
|
|||
{
|
||||
nextsec=sec;
|
||||
|
||||
int side = sector->lines[i]->sidedef[0]->sector == sec;
|
||||
int side = ln->sidedef[0]->sector == sec;
|
||||
|
||||
if (sector->GetPlaneTexZ(sector_t::floor)!=sec->GetPlaneTexZ(sector_t::floor)+1. || sec->floorplane.isSlope())
|
||||
{
|
||||
sector->transdoor=false;
|
||||
return;
|
||||
}
|
||||
if (!sector->lines[i]->sidedef[1-side]->GetTexture(side_t::top).isValid()) notextures++;
|
||||
if (!sector->lines[i]->sidedef[1-side]->GetTexture(side_t::bottom).isValid()) nobtextures++;
|
||||
if (!ln->sidedef[1-side]->GetTexture(side_t::top).isValid()) notextures++;
|
||||
if (!ln->sidedef[1-side]->GetTexture(side_t::bottom).isValid()) nobtextures++;
|
||||
}
|
||||
}
|
||||
if (sector->GetTexture(sector_t::ceiling)==skyflatnum)
|
||||
|
@ -358,19 +357,19 @@ static void PrepareTransparentDoors(sector_t * sector)
|
|||
return;
|
||||
}
|
||||
|
||||
if (selfref+nobtextures!=sector->linecount)
|
||||
if (selfref+nobtextures!=sector->Lines.Size())
|
||||
{
|
||||
sector->transdoor=false;
|
||||
}
|
||||
|
||||
if (selfref+notextures!=sector->linecount)
|
||||
if (selfref+notextures!=sector->Lines.Size())
|
||||
{
|
||||
// This is a crude attempt to fix an incorrect transparent door effect I found in some
|
||||
// WolfenDoom maps but considering the amount of code required to handle it I left it in.
|
||||
// Do this only if the sector only contains one-sided walls or ones with no lower texture.
|
||||
if (solidwall)
|
||||
{
|
||||
if (solidwall+nobtextures+selfref==sector->linecount && nextsec)
|
||||
if (solidwall+nobtextures+selfref==sector->Lines.Size() && nextsec)
|
||||
{
|
||||
sector->heightsec=nextsec;
|
||||
sector->heightsec->MoreFlags=0;
|
||||
|
@ -604,9 +603,8 @@ void gl_PreprocessLevel()
|
|||
PrepareTransparentDoors(§ors[i]);
|
||||
|
||||
// This ignores vertices only used for seg splitting because those aren't needed here
|
||||
for(int j = 0; j < sectors[i].linecount; j++)
|
||||
for(auto l : sectors[i].Lines)
|
||||
{
|
||||
line_t *l = sectors[i].lines[j];
|
||||
if (l->sidedef[0]->Flags & WALLF_POLYOBJ) continue; // don't bother with polyobjects
|
||||
|
||||
int vtnum1 = int(l->v1 - vertexes);
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#include "gl/dynlights/gl_dynlight.h"
|
||||
#include "gl/utility/gl_convert.h"
|
||||
#include "gl/utility/gl_templates.h"
|
||||
#include "gl/system//gl_interface.h"
|
||||
|
||||
EXTERN_CVAR(Int, vid_renderer)
|
||||
|
||||
|
@ -127,6 +128,10 @@ void AVavoomLightWhite::BeginPlay ()
|
|||
args[LIGHT_GREEN] = 128;
|
||||
args[LIGHT_BLUE] = 128;
|
||||
|
||||
if (gl.legacyMode && (flags4 & MF4_ATTENUATE))
|
||||
{
|
||||
m_Radius[0] = m_Radius[0] * 2 / 3;
|
||||
}
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
|
@ -140,6 +145,11 @@ void AVavoomLightColor::BeginPlay ()
|
|||
args[LIGHT_GREEN] = l_args[2] >> 1;
|
||||
args[LIGHT_BLUE] = l_args[3] >> 1;
|
||||
|
||||
if (gl.legacyMode && (flags4 & MF4_ATTENUATE))
|
||||
{
|
||||
m_Radius[0] = m_Radius[0] * 2 / 3;
|
||||
}
|
||||
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
|
@ -195,6 +205,12 @@ void ADynamicLight::BeginPlay()
|
|||
m_Radius[1] = args[LIGHT_SECONDARY_INTENSITY];
|
||||
specialf1 = DAngle(double(SpawnAngle)).Normalized360().Degrees;
|
||||
visibletoplayer = true;
|
||||
|
||||
if (gl.legacyMode && (flags4 & MF4_ATTENUATE))
|
||||
{
|
||||
m_Radius[0] = m_Radius[0] * 2 / 3;
|
||||
m_Radius[1] = m_Radius[1] * 2 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "stats.h"
|
||||
#include "zstring.h"
|
||||
#include "d_dehacked.h"
|
||||
#include "v_text.h"
|
||||
|
||||
|
||||
#include "gl/dynlights/gl_dynlight.h"
|
||||
|
@ -55,6 +56,7 @@
|
|||
#include "gl/utility/gl_clock.h"
|
||||
#include "gl/utility/gl_convert.h"
|
||||
#include "gl/data/gl_data.h"
|
||||
#include "gl/system//gl_interface.h"
|
||||
|
||||
int ScriptDepth;
|
||||
void gl_InitGlow(FScanner &sc);
|
||||
|
@ -125,6 +127,7 @@ public:
|
|||
void SetParameter(double p) { m_Param = p; }
|
||||
void SetArg(int arg, BYTE val) { m_Args[arg] = val; }
|
||||
BYTE GetArg(int arg) { return m_Args[arg]; }
|
||||
uint8_t GetAttenuate() const { return m_attenuate; }
|
||||
void SetOffset(float* ft) { m_Pos.X = ft[0]; m_Pos.Z = ft[1]; m_Pos.Y = ft[2]; }
|
||||
void SetSubtractive(bool subtract) { m_subtractive = subtract; }
|
||||
void SetAdditive(bool add) { m_additive = add; }
|
||||
|
@ -309,6 +312,12 @@ void gl_AddLightDefaults(FLightDefaults *defaults)
|
|||
}
|
||||
}
|
||||
|
||||
if (gl.legacyMode && (defaults->GetAttenuate()))
|
||||
{
|
||||
defaults->SetArg(LIGHT_INTENSITY, defaults->GetArg(LIGHT_INTENSITY) * 2 / 3);
|
||||
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_SECONDARY_INTENSITY) * 2 / 3);
|
||||
}
|
||||
|
||||
LightDefaults.Push(defaults);
|
||||
}
|
||||
|
||||
|
@ -436,11 +445,11 @@ void gl_ParsePulseLight(FScanner &sc)
|
|||
defaults->SetOffset(floatTriple);
|
||||
break;
|
||||
case LIGHTTAG_SIZE:
|
||||
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
|
||||
intVal = clamp<int>(gl_ParseInt(sc), 0, 1024);
|
||||
defaults->SetArg(LIGHT_INTENSITY, intVal);
|
||||
break;
|
||||
case LIGHTTAG_SECSIZE:
|
||||
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
|
||||
intVal = clamp<int>(gl_ParseInt(sc), 0, 1024);
|
||||
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
|
||||
break;
|
||||
case LIGHTTAG_INTERVAL:
|
||||
|
@ -1336,12 +1345,13 @@ void gl_DoParseDefs(FScanner &sc, int workingLump)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void gl_LoadGLDefs(const char * defsLump)
|
||||
void gl_LoadGLDefs(const char *defsLump)
|
||||
{
|
||||
int workingLump, lastLump;
|
||||
static const char *gldefsnames[] = { "GLDEFS", defsLump, nullptr };
|
||||
|
||||
lastLump = 0;
|
||||
while ((workingLump = Wads.FindLump(defsLump, &lastLump)) != -1)
|
||||
while ((workingLump = Wads.FindLumpMulti(gldefsnames, &lastLump)) != -1)
|
||||
{
|
||||
FScanner sc(workingLump);
|
||||
gl_DoParseDefs(sc, workingLump);
|
||||
|
@ -1383,8 +1393,7 @@ void gl_ParseDefs()
|
|||
break;
|
||||
}
|
||||
gl_ParseVavoomSkybox();
|
||||
if (defsLump != NULL) gl_LoadGLDefs(defsLump);
|
||||
gl_LoadGLDefs("GLDEFS");
|
||||
gl_LoadGLDefs(defsLump);
|
||||
gl_InitializeActorLights();
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ struct FColormap
|
|||
LightColor = from->Color;
|
||||
desaturation = from->Desaturate;
|
||||
FadeColor = from->Fade;
|
||||
FadeColor.a = 0;
|
||||
blendfactor = from->Color.a;
|
||||
fogdensity = from->Fade.a*2;
|
||||
return * this;
|
||||
|
|
|
@ -320,9 +320,8 @@ void GLFlat::DrawSkyboxSector(int pass, bool processlights)
|
|||
float minx = FLT_MAX, miny = FLT_MAX;
|
||||
float maxx = -FLT_MAX, maxy = -FLT_MAX;
|
||||
|
||||
for (int i = 0; i < sector->linecount; i++)
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
line_t *ln = sector->lines[i];
|
||||
float x = ln->v1->fX();
|
||||
float y = ln->v1->fY();
|
||||
if (x < minx) minx = x;
|
||||
|
|
|
@ -296,11 +296,10 @@ void GLSprite::Draw(int pass)
|
|||
if (!gl_isBlack(Colormap.FadeColor))
|
||||
{
|
||||
float dist=Dist2(ViewPos.X, ViewPos.Y, x,y);
|
||||
|
||||
if (!Colormap.FadeColor.a) Colormap.FadeColor.a=clamp<int>(255-lightlevel,60,255);
|
||||
int fogd = gl_GetFogDensity(lightlevel, Colormap.FadeColor, Colormap.fogdensity);
|
||||
|
||||
// this value was determined by trial and error and is scale dependent!
|
||||
float factor=0.05f+exp(-Colormap.FadeColor.a*dist/62500.f);
|
||||
float factor = 0.05f + exp(-fogd*dist / 62500.f);
|
||||
fuzzalpha*=factor;
|
||||
minalpha*=factor;
|
||||
}
|
||||
|
@ -387,6 +386,7 @@ void GLSprite::Draw(int pass)
|
|||
|
||||
FColormap thiscm;
|
||||
thiscm.FadeColor = Colormap.FadeColor;
|
||||
thiscm.fogdensity = Colormap.fogdensity;
|
||||
thiscm.CopyFrom3DLight(&(*lightlist)[i]);
|
||||
if (glset.nocoloredspritelighting)
|
||||
{
|
||||
|
|
|
@ -195,7 +195,7 @@ void GLWall::RenderWall(int textured)
|
|||
gl_RenderState.ApplyLightIndex(dynlightindex);
|
||||
if (gl.buffermethod != BM_DEFERRED)
|
||||
{
|
||||
MakeVertices(!(textured&RWF_NOSPLIT));
|
||||
MakeVertices(!!(textured&RWF_NOSPLIT));
|
||||
}
|
||||
else if (vertcount == 0)
|
||||
{
|
||||
|
|
|
@ -168,7 +168,7 @@ void gl_LoadExtensions()
|
|||
}
|
||||
|
||||
// The minimum requirement for the modern render path are GL 3.0 + uniform buffers
|
||||
if (gl_version < 3.0f || (gl_version < 3.1f && !CheckExtension("GL_ARB_uniform_buffer_object")))
|
||||
if (gl_version < 3.0f || (gl_version < 3.1f && !CheckExtension("GL_ARB_uniform_buffer_object") && strstr(gl.vendorstring, "X.Org") == nullptr))
|
||||
{
|
||||
gl.legacyMode = true;
|
||||
gl.lightmethod = LM_LEGACY;
|
||||
|
|
|
@ -447,6 +447,11 @@ FMaterial::FMaterial(FTexture * tx, bool expanded)
|
|||
}
|
||||
else if (tx->bHasCanvas)
|
||||
{
|
||||
if (tx->gl_info.shaderindex >= FIRST_USER_SHADER)
|
||||
{
|
||||
mShaderIndex = tx->gl_info.shaderindex;
|
||||
}
|
||||
// no brightmap for cameratexture
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -354,6 +354,7 @@ xx(MapSpot)
|
|||
xx(PatrolPoint)
|
||||
xx(PatrolSpecial)
|
||||
xx(Communicator)
|
||||
xx(PowerScanner)
|
||||
|
||||
// Textmap properties
|
||||
//xx(X)
|
||||
|
@ -755,6 +756,7 @@ xx(ResolveState)
|
|||
xx(DamageFunction)
|
||||
xx(Length)
|
||||
xx(Unit)
|
||||
xx(Size)
|
||||
xx(StateLabel)
|
||||
xx(SpriteID)
|
||||
xx(TextureID)
|
||||
|
|
|
@ -216,7 +216,7 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag
|
|||
//==========================================================================
|
||||
static int P_Set3DFloor(line_t * line, int param, int param2, int alpha)
|
||||
{
|
||||
int s, i;
|
||||
int s;
|
||||
int flags;
|
||||
int tag = line->args[0];
|
||||
sector_t * sec = line->frontsector, *ss;
|
||||
|
@ -230,10 +230,8 @@ static int P_Set3DFloor(line_t * line, int param, int param2, int alpha)
|
|||
{
|
||||
flags = FF_EXISTS | FF_RENDERALL | FF_SOLID | FF_INVERTSECTOR;
|
||||
alpha = 255;
|
||||
for (i = 0; i < sec->linecount; i++)
|
||||
for (auto l: sec->Lines)
|
||||
{
|
||||
line_t * l = sec->lines[i];
|
||||
|
||||
if (l->special == Sector_SetContents && l->frontsector == sec)
|
||||
{
|
||||
alpha = clamp<int>(l->args[1], 0, 100);
|
||||
|
|
|
@ -167,10 +167,8 @@ void P_Attach3dMidtexLinesToSector(sector_t *sector, int lineid, int tag, bool c
|
|||
int sec;
|
||||
while ((sec = it.Next()) >= 0)
|
||||
{
|
||||
for (int line = 0; line < sectors[sec].linecount; line ++)
|
||||
for (auto ln : sectors[sec].Lines)
|
||||
{
|
||||
line_t *ln = sectors[sec].lines[line];
|
||||
|
||||
if (lineid != 0 && !tagManager.LineHasID(ln, lineid)) continue;
|
||||
|
||||
if (ln->frontsector == NULL || ln->backsector == NULL || !(ln->flags & ML_3DMIDTEX))
|
||||
|
|
|
@ -243,7 +243,7 @@ bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int t
|
|||
|
||||
// new door thinker
|
||||
DCeiling *ceiling = new DCeiling (sec, speed, speed2, silent & ~4);
|
||||
vertex_t *spot = sec->lines[0]->v1;
|
||||
vertex_t *spot = sec->Lines[0]->v1;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
|
|
@ -287,10 +287,9 @@ void DDoor::DoorSound(bool raise, DSeqNode *curseq) const
|
|||
|
||||
// Search the front top textures of 2-sided lines on the door sector
|
||||
// for a door sound to use.
|
||||
for (int i = 0; i < m_Sector->linecount; ++i)
|
||||
for (auto line : m_Sector->Lines)
|
||||
{
|
||||
const char *texname;
|
||||
line_t *line = m_Sector->lines[i];
|
||||
|
||||
if (line->backsector == NULL)
|
||||
continue;
|
||||
|
@ -695,14 +694,14 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay,
|
|||
m_Line1 = line;
|
||||
m_Line2 = line;
|
||||
|
||||
for (int i = 0; i < sec->linecount; ++i)
|
||||
for (auto l : sec->Lines)
|
||||
{
|
||||
if (sec->lines[i] == line)
|
||||
if (l == line)
|
||||
continue;
|
||||
|
||||
if (sec->lines[i]->sidedef[0]->GetTexture(side_t::top) == line->sidedef[0]->GetTexture(side_t::top))
|
||||
if (l->sidedef[0]->GetTexture(side_t::top) == line->sidedef[0]->GetTexture(side_t::top))
|
||||
{
|
||||
m_Line2 = sec->lines[i];
|
||||
m_Line2 = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -790,9 +789,8 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
|
|||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; tag != 0 && i < sec->linecount; ++i)
|
||||
for (auto line : sec->Lines)
|
||||
{
|
||||
line = sec->lines[i];
|
||||
if (line->backsector == NULL)
|
||||
{
|
||||
continue;
|
||||
|
|
|
@ -162,10 +162,8 @@ static void P_RecursiveSound(sector_t *sec, AActor *soundtarget, bool splash, AA
|
|||
bool checkabove = !sec->PortalBlocksSound(sector_t::ceiling);
|
||||
bool checkbelow = !sec->PortalBlocksSound(sector_t::floor);
|
||||
|
||||
for (int i = 0; i < sec->linecount; i++)
|
||||
for (auto check : sec->Lines)
|
||||
{
|
||||
line_t *check = sec->lines[i];
|
||||
|
||||
// check sector portals
|
||||
// I wish there was a better method to do this than randomly looking through the portal at a few places...
|
||||
if (checkabove)
|
||||
|
|
|
@ -579,7 +579,6 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line,
|
|||
int osecnum; //jff 3/4/98 save old loop index
|
||||
double height;
|
||||
double stairstep;
|
||||
int i;
|
||||
int newsecnum = -1;
|
||||
FTextureID texture;
|
||||
int ok;
|
||||
|
@ -670,18 +669,18 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line,
|
|||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < sec->linecount; i++)
|
||||
for (auto line : sec->Lines)
|
||||
{
|
||||
if ( !((sec->lines[i])->flags & ML_TWOSIDED) )
|
||||
if ( !(line->flags & ML_TWOSIDED) )
|
||||
continue;
|
||||
|
||||
tsec = (sec->lines[i])->frontsector;
|
||||
tsec = line->frontsector;
|
||||
newsecnum = (int)(tsec-sectors);
|
||||
|
||||
if (secnum != newsecnum)
|
||||
continue;
|
||||
|
||||
tsec = (sec->lines[i])->backsector;
|
||||
tsec = line->backsector;
|
||||
if (!tsec) continue; //jff 5/7/98 if no backside, continue
|
||||
newsecnum = (int)(tsec - sectors);
|
||||
|
||||
|
@ -766,7 +765,6 @@ bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed)
|
|||
sector_t* s3;
|
||||
int secnum;
|
||||
bool rtn;
|
||||
int i;
|
||||
DFloor* floor;
|
||||
vertex_t* spot;
|
||||
double height;
|
||||
|
@ -783,19 +781,19 @@ bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed)
|
|||
continue; // safe now, because we check that tag is non-0 in the looping condition [fdari]
|
||||
|
||||
rtn = true;
|
||||
s2 = getNextSector (s1->lines[0], s1); // s2 is pool's sector
|
||||
s2 = getNextSector (s1->Lines[0], s1); // s2 is pool's sector
|
||||
if (!s2) // note lowest numbered line around
|
||||
continue; // pillar must be two-sided
|
||||
|
||||
if (s2->PlaneMoving(sector_t::floor))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < s2->linecount; i++)
|
||||
for (auto ln : s2->Lines)
|
||||
{
|
||||
if (!(s2->lines[i]->flags & ML_TWOSIDED) ||
|
||||
(s2->lines[i]->backsector == s1))
|
||||
if (!(ln->flags & ML_TWOSIDED) ||
|
||||
(ln->backsector == s1))
|
||||
continue;
|
||||
s3 = s2->lines[i]->backsector;
|
||||
s3 = ln->backsector;
|
||||
|
||||
// Spawn rising slime
|
||||
floor = new DFloor (s2);
|
||||
|
|
|
@ -1546,7 +1546,7 @@ dopain:
|
|||
target->SetState (target->SeeState);
|
||||
}
|
||||
}
|
||||
else if ((damage > 0 || fakedPain) && source != target->target && target->OkayToSwitchTarget (source))
|
||||
else if (source != target->target && target->OkayToSwitchTarget (source))
|
||||
{
|
||||
// Target actor is not intent on another actor,
|
||||
// so make him chase after source
|
||||
|
|
|
@ -541,9 +541,9 @@ void EV_TurnTagLightsOff (int tag)
|
|||
sector_t *sector = sectors + secnum;
|
||||
int min = sector->lightlevel;
|
||||
|
||||
for (int i = 0; i < sector->linecount; i++)
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
sector_t *tsec = getNextSector (sector->lines[i],sector);
|
||||
sector_t *tsec = getNextSector (ln, sector);
|
||||
if (!tsec)
|
||||
continue;
|
||||
if (tsec->lightlevel < min)
|
||||
|
@ -575,11 +575,9 @@ void EV_LightTurnOn (int tag, int bright)
|
|||
// surrounding sector
|
||||
if (bright < 0)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < sector->linecount; j++)
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
sector_t *temp = getNextSector (sector->lines[j], sector);
|
||||
sector_t *temp = getNextSector(ln, sector);
|
||||
|
||||
if (!temp)
|
||||
continue;
|
||||
|
@ -623,11 +621,11 @@ void EV_LightTurnOnPartway (int tag, double frac)
|
|||
while ((secnum = it.Next()) >= 0)
|
||||
{
|
||||
sector_t *temp, *sector = §ors[secnum];
|
||||
int j, bright = 0, min = sector->lightlevel;
|
||||
int bright = 0, min = sector->lightlevel;
|
||||
|
||||
for (j = 0; j < sector->linecount; ++j)
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
if ((temp = getNextSector (sector->lines[j], sector)) != NULL)
|
||||
if ((temp = getNextSector (ln, sector)) != nullptr)
|
||||
{
|
||||
if (temp->lightlevel > bright)
|
||||
{
|
||||
|
|
|
@ -92,12 +92,6 @@ TArray<spechit_t> portalhit;
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, CanCollideWith)
|
||||
{
|
||||
// No need to check the parameters, as they are not even used.
|
||||
ACTION_RETURN_BOOL(true);
|
||||
}
|
||||
|
||||
bool P_CanCollideWith(AActor *tmthing, AActor *thing)
|
||||
{
|
||||
static unsigned VIndex = ~0u;
|
||||
|
@ -1299,7 +1293,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch
|
|||
if ((tm.thing->Z() >= topz) || (tm.thing->Top() <= thing->Z()))
|
||||
return true;
|
||||
}
|
||||
// If they are not allowed to overlap, the rest of this function still needs to be executed.
|
||||
else return unblocking; // This may not really make sense, but Heretic depends on the broken implementation.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3802,7 +3802,7 @@ void AActor::Tick ()
|
|||
// by the order in the inventory, not the order in the thinker table
|
||||
while (item != NULL && item->Owner == this)
|
||||
{
|
||||
item->DoEffect();
|
||||
item->CallDoEffect();
|
||||
item = item->Inventory;
|
||||
}
|
||||
|
||||
|
@ -7421,8 +7421,12 @@ DEFINE_ACTION_FUNCTION(AActor, ClearCounters)
|
|||
|
||||
int AActor::GetModifiedDamage(FName damagetype, int damage, bool passive)
|
||||
{
|
||||
if (Inventory != nullptr)
|
||||
Inventory->ModifyDamage(damage, damagetype, damage, passive);
|
||||
auto inv = Inventory;
|
||||
while (inv != nullptr)
|
||||
{
|
||||
inv->ModifyDamage(damage, damagetype, damage, passive);
|
||||
inv = inv->Inventory;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
@ -7848,6 +7852,25 @@ DEFINE_ACTION_FUNCTION(AActor, CountsAsKill)
|
|||
ACTION_RETURN_FLOAT(self->CountsAsKill());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, ApplyDamageFactors)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_CLASS(itemcls, AInventory);
|
||||
PARAM_NAME(damagetype);
|
||||
PARAM_INT(damage);
|
||||
PARAM_INT(defdamage);
|
||||
|
||||
DmgFactors *df = itemcls->DamageFactors;
|
||||
if (df != nullptr && df->CountUsed() != 0)
|
||||
{
|
||||
ACTION_RETURN_INT(df->Apply(damagetype, damage));
|
||||
}
|
||||
else
|
||||
{
|
||||
ACTION_RETURN_INT(defdamage);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// DropItem handling
|
||||
|
|
|
@ -46,12 +46,8 @@
|
|||
sector_t *sector_t::NextSpecialSector (int type, sector_t *nogood) const
|
||||
{
|
||||
sector_t *tsec;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto ln : Lines)
|
||||
{
|
||||
line_t *ln = lines[i];
|
||||
|
||||
if (NULL != (tsec = getNextSector (ln, this)) &&
|
||||
tsec != nogood &&
|
||||
tsec->special == type)
|
||||
|
@ -68,21 +64,18 @@ sector_t *sector_t::NextSpecialSector (int type, sector_t *nogood) const
|
|||
//
|
||||
double sector_t::FindLowestFloorSurrounding (vertex_t **v) const
|
||||
{
|
||||
int i;
|
||||
sector_t *other;
|
||||
line_t *check;
|
||||
double floor;
|
||||
double ofloor;
|
||||
vertex_t *spot;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
floor = floorplane.ZatPoint(spot);
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
ofloor = other->floorplane.ZatPoint (check->v1);
|
||||
|
@ -112,21 +105,18 @@ double sector_t::FindLowestFloorSurrounding (vertex_t **v) const
|
|||
//
|
||||
double sector_t::FindHighestFloorSurrounding (vertex_t **v) const
|
||||
{
|
||||
int i;
|
||||
line_t *check;
|
||||
sector_t *other;
|
||||
double floor;
|
||||
double ofloor;
|
||||
vertex_t *spot;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
floor = -FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
ofloor = other->floorplane.ZatPoint (check->v1);
|
||||
|
@ -167,18 +157,15 @@ double sector_t::FindNextHighestFloor (vertex_t **v) const
|
|||
double ofloor, floor;
|
||||
sector_t *other;
|
||||
vertex_t *spot;
|
||||
line_t *check;
|
||||
int i;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
height = floorplane.ZatPoint(spot);
|
||||
heightdiff = FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
ofloor = other->floorplane.ZatPoint (check->v1);
|
||||
|
@ -222,18 +209,15 @@ double sector_t::FindNextLowestFloor (vertex_t **v) const
|
|||
double ofloor, floor;
|
||||
sector_t *other;
|
||||
vertex_t *spot;
|
||||
line_t *check;
|
||||
int i;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
height = floorplane.ZatPoint (spot);
|
||||
heightdiff = FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
ofloor = other->floorplane.ZatPoint (check->v1);
|
||||
|
@ -276,19 +260,15 @@ double sector_t::FindNextLowestCeiling (vertex_t **v) const
|
|||
double oceil, ceil;
|
||||
sector_t *other;
|
||||
vertex_t *spot;
|
||||
line_t *check;
|
||||
int i;
|
||||
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::floor);
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
height = ceilingplane.ZatPoint(spot);
|
||||
heightdiff = FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
oceil = other->ceilingplane.ZatPoint(check->v1);
|
||||
|
@ -331,18 +311,15 @@ double sector_t::FindNextHighestCeiling (vertex_t **v) const
|
|||
double oceil, ceil;
|
||||
sector_t *other;
|
||||
vertex_t *spot;
|
||||
line_t *check;
|
||||
int i;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
height = ceilingplane.ZatPoint(spot);
|
||||
heightdiff = FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
oceil = other->ceilingplane.ZatPoint(check->v1);
|
||||
|
@ -377,17 +354,14 @@ double sector_t::FindLowestCeilingSurrounding (vertex_t **v) const
|
|||
double oceil;
|
||||
sector_t *other;
|
||||
vertex_t *spot;
|
||||
line_t *check;
|
||||
int i;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
height = FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
oceil = other->ceilingplane.ZatPoint(check->v1);
|
||||
|
@ -419,17 +393,14 @@ double sector_t::FindHighestCeilingSurrounding (vertex_t **v) const
|
|||
double oceil;
|
||||
sector_t *other;
|
||||
vertex_t *spot;
|
||||
line_t *check;
|
||||
int i;
|
||||
|
||||
if (linecount == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
if (Lines.Size() == 0) return GetPlaneTexZ(sector_t::ceiling);
|
||||
|
||||
spot = lines[0]->v1;
|
||||
spot = Lines[0]->v1;
|
||||
height = -FLT_MAX;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
check = lines[i];
|
||||
if (NULL != (other = getNextSector (check, this)))
|
||||
{
|
||||
oceil = other->ceilingplane.ZatPoint(check->v1);
|
||||
|
@ -480,12 +451,12 @@ double sector_t::FindShortestTextureAround () const
|
|||
{
|
||||
double minsize = FLT_MAX;
|
||||
|
||||
for (int i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
if (lines[i]->flags & ML_TWOSIDED)
|
||||
if (check->flags & ML_TWOSIDED)
|
||||
{
|
||||
CheckShortestTex (lines[i]->sidedef[0]->GetTexture(side_t::bottom), minsize);
|
||||
CheckShortestTex (lines[i]->sidedef[1]->GetTexture(side_t::bottom), minsize);
|
||||
CheckShortestTex (check->sidedef[0]->GetTexture(side_t::bottom), minsize);
|
||||
CheckShortestTex (check->sidedef[1]->GetTexture(side_t::bottom), minsize);
|
||||
}
|
||||
}
|
||||
return minsize < FLT_MAX ? minsize : TexMan[0]->GetHeight();
|
||||
|
@ -506,12 +477,12 @@ double sector_t::FindShortestUpperAround () const
|
|||
{
|
||||
double minsize = FLT_MAX;
|
||||
|
||||
for (int i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
if (lines[i]->flags & ML_TWOSIDED)
|
||||
if (check->flags & ML_TWOSIDED)
|
||||
{
|
||||
CheckShortestTex (lines[i]->sidedef[0]->GetTexture(side_t::top), minsize);
|
||||
CheckShortestTex (lines[i]->sidedef[1]->GetTexture(side_t::top), minsize);
|
||||
CheckShortestTex (check->sidedef[0]->GetTexture(side_t::top), minsize);
|
||||
CheckShortestTex (check->sidedef[1]->GetTexture(side_t::top), minsize);
|
||||
}
|
||||
}
|
||||
return minsize < FLT_MAX ? minsize : TexMan[0]->GetHeight();
|
||||
|
@ -534,17 +505,14 @@ double sector_t::FindShortestUpperAround () const
|
|||
//
|
||||
sector_t *sector_t::FindModelFloorSector (double floordestheight) const
|
||||
{
|
||||
int i;
|
||||
sector_t *sec;
|
||||
|
||||
//jff 5/23/98 don't disturb sec->linecount while searching
|
||||
// but allow early exit in old demos
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
sec = getNextSector (lines[i], this);
|
||||
sec = getNextSector (check, this);
|
||||
if (sec != NULL &&
|
||||
(sec->floorplane.ZatPoint(lines[i]->v1) == floordestheight ||
|
||||
sec->floorplane.ZatPoint(lines[i]->v2) == floordestheight))
|
||||
(sec->floorplane.ZatPoint(check->v1) == floordestheight ||
|
||||
sec->floorplane.ZatPoint(check->v2) == floordestheight))
|
||||
{
|
||||
return sec;
|
||||
}
|
||||
|
@ -570,17 +538,14 @@ sector_t *sector_t::FindModelFloorSector (double floordestheight) const
|
|||
//
|
||||
sector_t *sector_t::FindModelCeilingSector (double floordestheight) const
|
||||
{
|
||||
int i;
|
||||
sector_t *sec;
|
||||
|
||||
//jff 5/23/98 don't disturb sec->linecount while searching
|
||||
// but allow early exit in old demos
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
sec = getNextSector (lines[i], this);
|
||||
sec = getNextSector (check, this);
|
||||
if (sec != NULL &&
|
||||
(sec->ceilingplane.ZatPoint(lines[i]->v1) == floordestheight ||
|
||||
sec->ceilingplane.ZatPoint(lines[i]->v2) == floordestheight))
|
||||
(sec->ceilingplane.ZatPoint(check->v1) == floordestheight ||
|
||||
sec->ceilingplane.ZatPoint(check->v2) == floordestheight))
|
||||
{
|
||||
return sec;
|
||||
}
|
||||
|
@ -593,13 +558,10 @@ sector_t *sector_t::FindModelCeilingSector (double floordestheight) const
|
|||
//
|
||||
int sector_t::FindMinSurroundingLight (int min) const
|
||||
{
|
||||
int i;
|
||||
line_t* line;
|
||||
sector_t* check;
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto line : Lines)
|
||||
{
|
||||
line = lines[i];
|
||||
if (NULL != (check = getNextSector (line, this)) &&
|
||||
check->lightlevel < min)
|
||||
{
|
||||
|
@ -614,8 +576,6 @@ int sector_t::FindMinSurroundingLight (int min) const
|
|||
//
|
||||
double sector_t::FindHighestFloorPoint (vertex_t **v) const
|
||||
{
|
||||
int i;
|
||||
line_t *line;
|
||||
double height = -FLT_MAX;
|
||||
double probeheight;
|
||||
vertex_t *spot = NULL;
|
||||
|
@ -624,15 +584,14 @@ double sector_t::FindHighestFloorPoint (vertex_t **v) const
|
|||
{
|
||||
if (v != NULL)
|
||||
{
|
||||
if (linecount == 0) *v = &vertexes[0];
|
||||
else *v = lines[0]->v1;
|
||||
if (Lines.Size() == 0) *v = &vertexes[0];
|
||||
else *v = Lines[0]->v1;
|
||||
}
|
||||
return -floorplane.fD();
|
||||
}
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto line : Lines)
|
||||
{
|
||||
line = lines[i];
|
||||
probeheight = floorplane.ZatPoint(line->v1);
|
||||
if (probeheight > height)
|
||||
{
|
||||
|
@ -656,8 +615,6 @@ double sector_t::FindHighestFloorPoint (vertex_t **v) const
|
|||
//
|
||||
double sector_t::FindLowestCeilingPoint (vertex_t **v) const
|
||||
{
|
||||
int i;
|
||||
line_t *line;
|
||||
double height = FLT_MAX;
|
||||
double probeheight;
|
||||
vertex_t *spot = NULL;
|
||||
|
@ -666,15 +623,14 @@ double sector_t::FindLowestCeilingPoint (vertex_t **v) const
|
|||
{
|
||||
if (v != NULL)
|
||||
{
|
||||
if (linecount == 0) *v = &vertexes[0];
|
||||
else *v = lines[0]->v1;
|
||||
if (Lines.Size() == 0) *v = &vertexes[0];
|
||||
else *v = Lines[0]->v1;
|
||||
}
|
||||
return ceilingplane.fD();
|
||||
}
|
||||
|
||||
for (i = 0; i < linecount; i++)
|
||||
for (auto line : Lines)
|
||||
{
|
||||
line = lines[i];
|
||||
probeheight = ceilingplane.ZatPoint(line->v1);
|
||||
if (probeheight < height)
|
||||
{
|
||||
|
@ -739,15 +695,14 @@ DEFINE_ACTION_FUNCTION(_Sector, SetFade)
|
|||
|
||||
void sector_t::ClosestPoint(const DVector2 &in, DVector2 &out) const
|
||||
{
|
||||
int i;
|
||||
double x = in.X, y = in.Y;
|
||||
double bestdist = HUGE_VAL;
|
||||
double bestx = 0, besty = 0;
|
||||
|
||||
for (i = 0; i < linecount; ++i)
|
||||
for (auto check : Lines)
|
||||
{
|
||||
vertex_t *v1 = lines[i]->v1;
|
||||
vertex_t *v2 = lines[i]->v2;
|
||||
vertex_t *v1 = check->v1;
|
||||
vertex_t *v2 = check->v2;
|
||||
double a = v2->fX() - v1->fX();
|
||||
double b = v2->fY() - v1->fY();
|
||||
double den = a*a + b*b;
|
||||
|
@ -1134,9 +1089,8 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt)
|
|||
|
||||
void sector_t::RemoveForceField()
|
||||
{
|
||||
for (int i = 0; i < linecount; ++i)
|
||||
for (auto line : Lines)
|
||||
{
|
||||
line_t *line = lines[i];
|
||||
if (line->backsector != NULL && line->special == ForceField)
|
||||
{
|
||||
line->flags &= ~(ML_BLOCKING | ML_BLOCKEVERYTHING);
|
||||
|
@ -1378,8 +1332,7 @@ DEFINE_FIELD_X(Sector, sector_t, soundtraversed)
|
|||
DEFINE_FIELD_X(Sector, sector_t, stairlock)
|
||||
DEFINE_FIELD_X(Sector, sector_t, prevsec)
|
||||
DEFINE_FIELD_X(Sector, sector_t, nextsec)
|
||||
DEFINE_FIELD_X(Sector, sector_t, linecount)
|
||||
DEFINE_FIELD_X(Sector, sector_t, lines)
|
||||
DEFINE_FIELD_X(Sector, sector_t, Lines)
|
||||
DEFINE_FIELD_X(Sector, sector_t, heightsec)
|
||||
DEFINE_FIELD_X(Sector, sector_t, bottommap)
|
||||
DEFINE_FIELD_X(Sector, sector_t, midmap)
|
||||
|
|
|
@ -776,16 +776,13 @@ static void SetTextureNoErr (side_t *side, int position, DWORD *color, const cha
|
|||
|
||||
void P_FloodZone (sector_t *sec, int zonenum)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sec->ZoneNumber == zonenum)
|
||||
return;
|
||||
|
||||
sec->ZoneNumber = zonenum;
|
||||
|
||||
for (i = 0; i < sec->linecount; ++i)
|
||||
for (auto check : sec->Lines)
|
||||
{
|
||||
line_t *check = sec->lines[i];
|
||||
sector_t *other;
|
||||
|
||||
if (check->sidedef[1] == NULL || (check->flags & ML_ZONEBOUNDARY))
|
||||
|
@ -3094,7 +3091,6 @@ static void P_GroupLines (bool buildmap)
|
|||
cycle_t times[16];
|
||||
int* linesDoneInEachSector;
|
||||
int i;
|
||||
int j;
|
||||
int total;
|
||||
line_t* li;
|
||||
sector_t* sector;
|
||||
|
@ -3141,13 +3137,13 @@ static void P_GroupLines (bool buildmap)
|
|||
}
|
||||
else
|
||||
{
|
||||
li->frontsector->linecount++;
|
||||
li->frontsector->Lines.Count++;
|
||||
total++;
|
||||
}
|
||||
|
||||
if (li->backsector && li->backsector != li->frontsector)
|
||||
{
|
||||
li->backsector->linecount++;
|
||||
li->backsector->Lines.Count++;
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
@ -3166,7 +3162,7 @@ static void P_GroupLines (bool buildmap)
|
|||
|
||||
for (sector = sectors, i = 0; i < numsectors; i++, sector++)
|
||||
{
|
||||
if (sector->linecount == 0)
|
||||
if (sector->Lines.Count == 0)
|
||||
{
|
||||
Printf ("Sector %i (tag %i) has no lines\n", i, tagManager.GetFirstSectorTag(sector));
|
||||
// 0 the sector's tag so that no specials can use it
|
||||
|
@ -3174,8 +3170,8 @@ static void P_GroupLines (bool buildmap)
|
|||
}
|
||||
else
|
||||
{
|
||||
sector->lines = lineb_p;
|
||||
lineb_p += sector->linecount;
|
||||
sector->Lines.Array = lineb_p;
|
||||
lineb_p += sector->Lines.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3183,26 +3179,25 @@ static void P_GroupLines (bool buildmap)
|
|||
{
|
||||
if (li->frontsector != NULL)
|
||||
{
|
||||
li->frontsector->lines[linesDoneInEachSector[li->frontsector - sectors]++] = li;
|
||||
li->frontsector->Lines[linesDoneInEachSector[li->frontsector - sectors]++] = li;
|
||||
}
|
||||
if (li->backsector != NULL && li->backsector != li->frontsector)
|
||||
{
|
||||
li->backsector->lines[linesDoneInEachSector[li->backsector - sectors]++] = li;
|
||||
li->backsector->Lines[linesDoneInEachSector[li->backsector - sectors]++] = li;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, sector = sectors; i < numsectors; ++i, ++sector)
|
||||
{
|
||||
if (linesDoneInEachSector[i] != sector->linecount)
|
||||
if (linesDoneInEachSector[i] != sector->Lines.Size())
|
||||
{
|
||||
I_Error("P_GroupLines: miscounted");
|
||||
}
|
||||
if (sector->linecount > 3)
|
||||
if (sector->Lines.Size() > 3)
|
||||
{
|
||||
bbox.ClearBox();
|
||||
for (j = 0; j < sector->linecount; ++j)
|
||||
for (auto li : sector->Lines)
|
||||
{
|
||||
li = sector->lines[j];
|
||||
bbox.AddToBox(li->v1->fPos());
|
||||
bbox.AddToBox(li->v2->fPos());
|
||||
}
|
||||
|
@ -3211,15 +3206,15 @@ static void P_GroupLines (bool buildmap)
|
|||
sector->centerspot.X = (bbox.Right() + bbox.Left()) / 2;
|
||||
sector->centerspot.Y = (bbox.Top() + bbox.Bottom()) / 2;
|
||||
}
|
||||
else if (sector->linecount > 0)
|
||||
else if (sector->Lines.Size() > 0)
|
||||
{
|
||||
// For triangular sectors the above does not calculate good points unless the longest of the triangle's lines is perfectly horizontal and vertical
|
||||
DVector2 pos = { 0,0 };
|
||||
for (int i = 0; i < sector->linecount; i++)
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
pos += sector->lines[i]->v1->fPos() + sector->lines[i]->v2->fPos();
|
||||
pos += ln->v1->fPos() + ln->v2->fPos();
|
||||
}
|
||||
sector->centerspot = pos / (2 * sector->linecount);
|
||||
sector->centerspot = pos / (2 * sector->Lines.Size());
|
||||
}
|
||||
}
|
||||
delete[] linesDoneInEachSector;
|
||||
|
|
|
@ -201,10 +201,8 @@ void P_SetSlope (secplane_t *plane, bool setCeil, int xyangi, int zangi, const D
|
|||
|
||||
void P_VavoomSlope(sector_t * sec, int id, const DVector3 &pos, int which)
|
||||
{
|
||||
for (int i=0;i<sec->linecount;i++)
|
||||
for(auto l : sec->Lines)
|
||||
{
|
||||
line_t * l=sec->lines[i];
|
||||
|
||||
if (l->args[0]==id)
|
||||
{
|
||||
DVector3 v1, v2, cross;
|
||||
|
@ -306,16 +304,16 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt,
|
|||
for (int i = 0; i < numsectors; i++)
|
||||
{
|
||||
sector_t *sec = §ors[i];
|
||||
if (sec->linecount != 3) continue; // only works with triangular sectors
|
||||
if (sec->Lines.Size() != 3) continue; // only works with triangular sectors
|
||||
|
||||
DVector3 vt1, vt2, vt3, cross;
|
||||
DVector3 vec1, vec2;
|
||||
int vi1, vi2, vi3;
|
||||
|
||||
vi1 = int(sec->lines[0]->v1 - vertexes);
|
||||
vi2 = int(sec->lines[0]->v2 - vertexes);
|
||||
vi3 = (sec->lines[1]->v1 == sec->lines[0]->v1 || sec->lines[1]->v1 == sec->lines[0]->v2)?
|
||||
int(sec->lines[1]->v2 - vertexes) : int(sec->lines[1]->v1 - vertexes);
|
||||
vi1 = int(sec->Lines[0]->v1 - vertexes);
|
||||
vi2 = int(sec->Lines[0]->v2 - vertexes);
|
||||
vi3 = (sec->Lines[1]->v1 == sec->Lines[0]->v1 || sec->Lines[1]->v1 == sec->Lines[0]->v2)?
|
||||
int(sec->Lines[1]->v2 - vertexes) : int(sec->Lines[1]->v1 - vertexes);
|
||||
|
||||
vt1 = DVector3(vertexes[vi1].fPos(), 0);
|
||||
vt2 = DVector3(vertexes[vi2].fPos(), 0);
|
||||
|
@ -332,7 +330,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt,
|
|||
vt2.Z = h2? *h2 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling);
|
||||
vt3.Z = h3? *h3 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling);
|
||||
|
||||
if (P_PointOnLineSidePrecise(vertexes[vi3].fX(), vertexes[vi3].fY(), sec->lines[0]) == 0)
|
||||
if (P_PointOnLineSidePrecise(vertexes[vi3].fX(), vertexes[vi3].fY(), sec->Lines[0]) == 0)
|
||||
{
|
||||
vec1 = vt2 - vt3;
|
||||
vec2 = vt1 - vt3;
|
||||
|
@ -451,9 +449,7 @@ static void P_AlignPlane(sector_t *sec, line_t *line, int which)
|
|||
{
|
||||
sector_t *refsec;
|
||||
double bestdist;
|
||||
vertex_t *refvert = (*sec->lines)->v1; // Shut up, GCC
|
||||
int i;
|
||||
line_t **probe;
|
||||
vertex_t *refvert = sec->Lines[0]->v1; // Shut up, GCC
|
||||
|
||||
if (line->backsector == NULL)
|
||||
return;
|
||||
|
@ -461,22 +457,22 @@ static void P_AlignPlane(sector_t *sec, line_t *line, int which)
|
|||
// Find furthest vertex from the reference line. It, along with the two ends
|
||||
// of the line, will define the plane.
|
||||
bestdist = 0;
|
||||
for (i = sec->linecount * 2, probe = sec->lines; i > 0; i--)
|
||||
for (auto ln : sec->Lines)
|
||||
{
|
||||
double dist;
|
||||
vertex_t *vert;
|
||||
|
||||
if (i & 1)
|
||||
vert = (*probe++)->v2;
|
||||
else
|
||||
vert = (*probe)->v1;
|
||||
dist = fabs((line->v1->fY() - vert->fY()) * line->Delta().X -
|
||||
(line->v1->fX() - vert->fX()) * line->Delta().Y);
|
||||
|
||||
if (dist > bestdist)
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
bestdist = dist;
|
||||
refvert = vert;
|
||||
double dist;
|
||||
vertex_t *vert;
|
||||
|
||||
vert = i == 0 ? ln->v1 : ln->v2;
|
||||
dist = fabs((line->v1->fY() - vert->fY()) * line->Delta().X -
|
||||
(line->v1->fX() - vert->fX()) * line->Delta().Y);
|
||||
|
||||
if (dist > bestdist)
|
||||
{
|
||||
bestdist = dist;
|
||||
refvert = vert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1039,10 +1039,8 @@ void P_SpawnSkybox(ASkyViewpoint *origin)
|
|||
}
|
||||
if (Sector)
|
||||
{
|
||||
line_t * refline = NULL;
|
||||
for (short i = 0; i < Sector->linecount; i++)
|
||||
for(auto refline : Sector->Lines)
|
||||
{
|
||||
refline = Sector->lines[i];
|
||||
if (refline->special == Sector_SetPortal && refline->args[1] == 2)
|
||||
{
|
||||
// We found the setup linedef for this skybox, so let's use it for our init.
|
||||
|
|
|
@ -1576,6 +1576,22 @@ public:
|
|||
sec->reflect[sector_t::ceiling] = (float)CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_floorglowcolor:
|
||||
sec->planes[sector_t::floor].GlowColor = CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_floorglowheight:
|
||||
sec->planes[sector_t::floor].GlowHeight = (float)CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_ceilingglowcolor:
|
||||
sec->planes[sector_t::ceiling].GlowColor = CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_ceilingglowheight:
|
||||
sec->planes[sector_t::ceiling].GlowHeight = (float)CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_MoreIds:
|
||||
// delay parsing of the tag string until parsing of the sector is complete
|
||||
// This ensures that the ID is always the first tag in the list.
|
||||
|
|
|
@ -801,9 +801,8 @@ static bool CollectSectors(int groupid, sector_t *origin)
|
|||
{
|
||||
sector_t *sec = list[i];
|
||||
|
||||
for (int j = 0; j < sec->linecount; j++)
|
||||
for (auto line : sec->Lines)
|
||||
{
|
||||
line_t *line = sec->lines[j];
|
||||
sector_t *other = line->frontsector == sec ? line->backsector : line->frontsector;
|
||||
if (other != NULL && other != sec && other->PortalGroup != groupid)
|
||||
{
|
||||
|
@ -1096,9 +1095,9 @@ void P_CreateLinkedPortals()
|
|||
// set a flag on each line connecting to a plane portal sector. This is used to reduce the amount of checks in P_CheckSight.
|
||||
if (sectors[i].PortalIsLinked(sector_t::floor) || sectors[i].PortalIsLinked(sector_t::ceiling))
|
||||
{
|
||||
for (int j = 0; j < sectors[i].linecount; j++)
|
||||
for(auto ln : sectors[i].Lines)
|
||||
{
|
||||
sectors[i].lines[j]->flags |= ML_PORTALCONNECT;
|
||||
ln->flags |= ML_PORTALCONNECT;
|
||||
}
|
||||
}
|
||||
if (sectors[i].PortalIsLinked(sector_t::ceiling) && sectors[i].PortalIsLinked(sector_t::floor))
|
||||
|
|
|
@ -1038,8 +1038,7 @@ public:
|
|||
// jff 2/26/98 lockout machinery for stairbuilding
|
||||
SBYTE stairlock; // -2 on first locked -1 after thinker done 0 normally
|
||||
|
||||
short linecount;
|
||||
struct line_t **lines; // [linecount] size
|
||||
TStaticPointedArray<line_t *> Lines;
|
||||
|
||||
// killough 3/7/98: support flat heights drawn at another sector's heights
|
||||
sector_t *heightsec; // other sector, or NULL if no other sector
|
||||
|
|
|
@ -1853,6 +1853,15 @@ void S_PauseSound (bool notmusic, bool notsfx)
|
|||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DObject, S_PauseSound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_BOOL(notmusic);
|
||||
PARAM_BOOL(notsfx);
|
||||
S_PauseSound(notmusic, notsfx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_ResumeSound
|
||||
|
@ -1874,6 +1883,14 @@ void S_ResumeSound (bool notsfx)
|
|||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_BOOL(notsfx);
|
||||
S_ResumeSound(notsfx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_SetSoundPaused
|
||||
|
|
|
@ -208,6 +208,10 @@ ExpEmit::ExpEmit(VMFunctionBuilder *build, int type, int count)
|
|||
|
||||
void ExpEmit::Free(VMFunctionBuilder *build)
|
||||
{
|
||||
if (RegType == REGT_INT && RegNum == 0)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
if (!Fixed && !Konst && RegType <= REGT_TYPE)
|
||||
{
|
||||
build->Registers[RegType].Return(RegNum, RegCount);
|
||||
|
@ -4584,8 +4588,14 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
|
|||
else
|
||||
{
|
||||
// Use the register returned by the true condition as the
|
||||
// target for the false condition.
|
||||
out = trueop;
|
||||
// target for the false condition, if temporary.
|
||||
// If this is a local variable we need another register for the result.
|
||||
if (trueop.Fixed)
|
||||
{
|
||||
out = ExpEmit(build, trueop.RegType);
|
||||
build->Emit(truex->ValueType->GetMoveOp(), out.RegNum, trueop.RegNum, 0);
|
||||
}
|
||||
else out = trueop;
|
||||
}
|
||||
}
|
||||
// Make sure to skip the false path.
|
||||
|
@ -6620,6 +6630,7 @@ FxArrayElement::FxArrayElement(FxExpression *base, FxExpression *_index)
|
|||
index = _index;
|
||||
AddressRequested = false;
|
||||
AddressWritable = false;
|
||||
SizeAddr = ~0u;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -6691,10 +6702,31 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
|||
arraytype = static_cast<PArray*>(ptype->PointedType);
|
||||
arrayispointer = true;
|
||||
}
|
||||
if (index->isConstant())
|
||||
|
||||
if (Array->IsResizableArray())
|
||||
{
|
||||
// if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
|
||||
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
||||
{
|
||||
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
||||
SizeAddr = unsigned(parentfield->Offset + parentfield->Type->Align);
|
||||
}
|
||||
else if (Array->ExprType == EFX_GlobalVariable)
|
||||
{
|
||||
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
||||
SizeAddr = unsigned(parentfield->Offset + parentfield->Type->Align);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Invalid resizable array");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if (index->isConstant())
|
||||
{
|
||||
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
|
||||
if (indexval >= arraytype->ElementCount)
|
||||
if (indexval >= arraytype->ElementCount && !Array->IsResizableArray())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
|
||||
delete this;
|
||||
|
@ -6767,21 +6799,40 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
|||
arraytype = static_cast<PArray*>(Array->ValueType);
|
||||
}
|
||||
ExpEmit start = Array->Emit(build);
|
||||
ExpEmit bound;
|
||||
|
||||
/* what was this for?
|
||||
if (start.Konst)
|
||||
// For resizable arrays we even need to check the bounds if if the index is constant.
|
||||
if (SizeAddr != ~0u)
|
||||
{
|
||||
ExpEmit tmpstart(build, REGT_POINTER);
|
||||
build->Emit(OP_LKP, tmpstart.RegNum, start.RegNum);
|
||||
start.Free(build);
|
||||
start = tmpstart;
|
||||
auto f = new PField(NAME_None, TypeUInt32, 0, SizeAddr);
|
||||
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
||||
{
|
||||
static_cast<FxStructMember *>(Array)->membervar = f;
|
||||
static_cast<FxStructMember *>(Array)->AddressRequested = false;
|
||||
}
|
||||
else if (Array->ExprType == EFX_GlobalVariable)
|
||||
{
|
||||
static_cast<FxGlobalVariable *>(Array)->membervar = f;
|
||||
static_cast<FxGlobalVariable *>(Array)->AddressRequested = false;
|
||||
}
|
||||
|
||||
Array->ValueType = TypeUInt32;
|
||||
bound = Array->Emit(build);
|
||||
}
|
||||
*/
|
||||
|
||||
if (index->isConstant())
|
||||
{
|
||||
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
|
||||
assert(indexval < arraytype->ElementCount && "Array index out of bounds");
|
||||
assert(SizeAddr != ~0u || (indexval < arraytype->ElementCount && "Array index out of bounds"));
|
||||
|
||||
if (SizeAddr != ~0u)
|
||||
{
|
||||
ExpEmit indexreg(build, REGT_INT);
|
||||
build->EmitLoadInt(indexreg.RegNum, indexval);
|
||||
build->Emit(OP_BOUND_R, indexreg.RegNum, bound.RegNum);
|
||||
indexreg.Free(build);
|
||||
bound.Free(build);
|
||||
}
|
||||
if (AddressRequested)
|
||||
{
|
||||
if (indexval != 0)
|
||||
|
@ -6821,9 +6872,12 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
|||
else
|
||||
{
|
||||
ExpEmit indexv(index->Emit(build));
|
||||
// Todo: For dynamically allocated arrays (like global sector and linedef tables) we need to get the bound value in here somehow.
|
||||
// Right now their bounds are not properly checked for.
|
||||
if (arraytype->ElementCount > 65535)
|
||||
if (SizeAddr != ~0u)
|
||||
{
|
||||
build->Emit(OP_BOUND_R, indexv.RegNum, bound.RegNum);
|
||||
bound.Free(build);
|
||||
}
|
||||
else if (arraytype->ElementCount > 65535)
|
||||
{
|
||||
build->Emit(OP_BOUND_K, indexv.RegNum, build->GetConstantInt(arraytype->ElementCount));
|
||||
}
|
||||
|
@ -7347,7 +7401,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
// Note: These builtins would better be relegated to the actual type objects, instead of polluting this file, but that's a task for later.
|
||||
|
||||
// Texture builtins.
|
||||
if (Self->ValueType == TypeTextureID)
|
||||
else if (Self->ValueType == TypeTextureID)
|
||||
{
|
||||
if (MethodName == NAME_IsValid || MethodName == NAME_IsNull || MethodName == NAME_Exists || MethodName == NAME_SetInvalid || MethodName == NAME_SetNull)
|
||||
{
|
||||
|
@ -7390,7 +7444,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
if (Self->IsVector())
|
||||
else if (Self->IsVector())
|
||||
{
|
||||
// handle builtins: Vectors got 2: Length and Unit.
|
||||
if (MethodName == NAME_Length || MethodName == NAME_Unit)
|
||||
|
@ -7408,11 +7462,63 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
if (Self->ValueType == TypeString)
|
||||
else if (Self->ValueType == TypeString)
|
||||
{
|
||||
// same for String methods. It also uses a hidden struct type to define them.
|
||||
Self->ValueType = TypeStringStruct;
|
||||
}
|
||||
else if (Self->IsArray())
|
||||
{
|
||||
if (MethodName == NAME_Size)
|
||||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (!Self->IsResizableArray())
|
||||
{
|
||||
auto atype = Self->ValueType;
|
||||
if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) atype = static_cast<PPointer*>(ValueType)->PointedType;
|
||||
auto size = static_cast<PArray*>(atype)->ElementCount;
|
||||
auto x = new FxConstant(size, ScriptPosition);
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resizable arrays can only be defined in C code and they can only exist in pointer form to reduce their impact on the code generator.
|
||||
if (Self->ExprType == EFX_StructMember || Self->ExprType == EFX_ClassMember)
|
||||
{
|
||||
auto member = static_cast<FxStructMember*>(Self);
|
||||
auto newfield = new PField(NAME_None, TypeUInt32, VARF_ReadOnly, member->membervar->Offset + member->membervar->Type->Align); // the size is stored right behind the pointer.
|
||||
member->membervar = newfield;
|
||||
Self = nullptr;
|
||||
delete this;
|
||||
member->ValueType = TypeUInt32;
|
||||
return member;
|
||||
}
|
||||
else if (Self->ExprType == EFX_GlobalVariable)
|
||||
{
|
||||
auto member = static_cast<FxGlobalVariable*>(Self);
|
||||
auto newfield = new PField(NAME_None, TypeUInt32, VARF_ReadOnly, member->membervar->Offset + member->membervar->Type->Align); // the size is stored right behind the pointer.
|
||||
member->membervar = newfield;
|
||||
Self = nullptr;
|
||||
delete this;
|
||||
member->ValueType = TypeUInt32;
|
||||
return member;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should never happen because resizable arrays cannot be defined in scripts.
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot retrieve size of array");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
|
||||
{
|
||||
|
|
|
@ -326,6 +326,8 @@ public:
|
|||
bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; };
|
||||
bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); }
|
||||
bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); }
|
||||
bool IsArray() const { return ValueType->IsKindOf(RUNTIME_CLASS(PArray)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PArray))); }
|
||||
bool IsResizableArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PResizableArray))); } // can only exist in pointer form.
|
||||
|
||||
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
||||
void EmitStatement(VMFunctionBuilder *build);
|
||||
|
@ -426,6 +428,13 @@ public:
|
|||
isresolved = true;
|
||||
}
|
||||
|
||||
FxConstant(unsigned int val, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos)
|
||||
{
|
||||
ValueType = value.Type = TypeUInt32;
|
||||
value.Int = val;
|
||||
isresolved = true;
|
||||
}
|
||||
|
||||
FxConstant(double val, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos)
|
||||
{
|
||||
ValueType = value.Type = TypeFloat64;
|
||||
|
@ -1427,6 +1436,7 @@ class FxArrayElement : public FxExpression
|
|||
public:
|
||||
FxExpression *Array;
|
||||
FxExpression *index;
|
||||
unsigned SizeAddr;
|
||||
bool AddressRequested;
|
||||
bool AddressWritable;
|
||||
bool arrayispointer = false;
|
||||
|
|
|
@ -357,13 +357,30 @@ static FFlagDef MoreFlagDefs[] =
|
|||
DEFINE_DUMMY_FLAG(FASTER, true), // obsolete, replaced by 'Fast' state flag
|
||||
DEFINE_DUMMY_FLAG(FASTMELEE, true), // obsolete, replaced by 'Fast' state flag
|
||||
|
||||
// Deprecated name as an alias
|
||||
DEFINE_FLAG2_DEPRECATED(MF4_DONTHARMCLASS, DONTHURTSPECIES, AActor, flags4),
|
||||
|
||||
// Various Skulltag flags that are quite irrelevant to ZDoom
|
||||
DEFINE_DUMMY_FLAG(NONETID, false), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(ALLOWCLIENTSPAWN, false), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(CLIENTSIDEONLY, false), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(SERVERSIDEONLY, false), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(EXPLODEONDEATH, true), // seems useless
|
||||
DEFINE_FLAG2_DEPRECATED(MF4_DONTHARMCLASS, DONTHURTSPECIES, AActor, flags4), // Deprecated name as an alias
|
||||
// [BC] New DECORATE flag defines here.
|
||||
DEFINE_DUMMY_FLAG(BLUETEAM, false),
|
||||
DEFINE_DUMMY_FLAG(REDTEAM, false),
|
||||
DEFINE_DUMMY_FLAG(USESPECIAL, false),
|
||||
DEFINE_DUMMY_FLAG(BASEHEALTH, false),
|
||||
DEFINE_DUMMY_FLAG(SUPERHEALTH, false),
|
||||
DEFINE_DUMMY_FLAG(BASEARMOR, false),
|
||||
DEFINE_DUMMY_FLAG(SUPERARMOR, false),
|
||||
DEFINE_DUMMY_FLAG(SCOREPILLAR, false),
|
||||
DEFINE_DUMMY_FLAG(NODE, false),
|
||||
DEFINE_DUMMY_FLAG(USESTBOUNCESOUND, false),
|
||||
DEFINE_DUMMY_FLAG(EXPLODEONDEATH, true),
|
||||
DEFINE_DUMMY_FLAG(DONTIDENTIFYTARGET, false), // [CK]
|
||||
|
||||
// Skulltag netcode-based flags.
|
||||
// [BB] New DECORATE network related flag defines here.
|
||||
DEFINE_DUMMY_FLAG(NONETID, false),
|
||||
DEFINE_DUMMY_FLAG(ALLOWCLIENTSPAWN, false),
|
||||
DEFINE_DUMMY_FLAG(CLIENTSIDEONLY, false),
|
||||
DEFINE_DUMMY_FLAG(SERVERSIDEONLY, false),
|
||||
};
|
||||
|
||||
static FFlagDef InventoryFlagDefs[] =
|
||||
|
@ -391,6 +408,8 @@ static FFlagDef InventoryFlagDefs[] =
|
|||
DEFINE_FLAG(IF, TRANSFER, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, NOTELEPORTFREEZE, AInventory, ItemFlags),
|
||||
|
||||
DEFINE_DUMMY_FLAG(FORCERESPAWNINSURVIVAL, false),
|
||||
|
||||
DEFINE_DEPRECATED_FLAG(PICKUPFLASH),
|
||||
DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),
|
||||
};
|
||||
|
@ -691,12 +710,8 @@ static int fieldcmp(const void * a, const void * b)
|
|||
|
||||
void InitThingdef()
|
||||
{
|
||||
PType *TypeActor = NewPointer(RUNTIME_CLASS(AActor));
|
||||
// Create all global variables here because this cannot be done on the script side and really isn't worth adding support for.
|
||||
|
||||
PStruct *sstruct = NewNativeStruct("Sector", nullptr);
|
||||
auto sptr = NewPointer(sstruct);
|
||||
sstruct->AddNativeField("soundtarget", TypeActor, myoffsetof(sector_t, SoundTarget));
|
||||
|
||||
// expose the global validcount variable.
|
||||
PField *vcf = new PField("validcount", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&validcount);
|
||||
GlobalSymbols.AddSymbol(vcf);
|
||||
|
@ -727,7 +742,7 @@ void InitThingdef()
|
|||
// set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well...
|
||||
// As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up.
|
||||
pstruct = NewNativeStruct("Sector", nullptr);
|
||||
pstruct->AddNativeField("lines", NewPointer(NewArray(NewPointer(NewNativeStruct("line", nullptr), false), 0x40000), false), myoffsetof(sector_t, lines), VARF_Native);
|
||||
pstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(NewNativeStruct("line", nullptr), false)), false), myoffsetof(sector_t, Lines), VARF_Native);
|
||||
|
||||
parray = NewArray(TypeBool, MAXPLAYERS);
|
||||
playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
|
||||
|
|
|
@ -857,14 +857,15 @@ void FFunctionBuildList::Build()
|
|||
|
||||
// Allocate registers for the function's arguments and create local variable nodes before starting to resolve it.
|
||||
VMFunctionBuilder buildit(item.Func->GetImplicitArgs());
|
||||
for(unsigned i=0;i<item.Func->Variants[0].Proto->ArgumentTypes.Size();i++)
|
||||
for (unsigned i = 0; i < item.Func->Variants[0].Proto->ArgumentTypes.Size(); i++)
|
||||
{
|
||||
auto type = item.Func->Variants[0].Proto->ArgumentTypes[i];
|
||||
auto name = item.Func->Variants[0].ArgNames[i];
|
||||
auto flags = item.Func->Variants[0].ArgFlags[i];
|
||||
// this won't get resolved and won't get emitted. It is only needed so that the code generator can retrieve the necessary info about this argument to do its work.
|
||||
auto local = new FxLocalVariableDeclaration(type, name, nullptr, flags, FScriptPosition());
|
||||
local->RegNum = buildit.Registers[type->GetRegType()].Get(type->GetRegCount());
|
||||
auto local = new FxLocalVariableDeclaration(type, name, nullptr, flags, FScriptPosition());
|
||||
if (!(flags & VARF_Out)) local->RegNum = buildit.Registers[type->GetRegType()].Get(type->GetRegCount());
|
||||
else local->RegNum = buildit.Registers[REGT_POINTER].Get(1);
|
||||
ctx.FunctionArgs.Push(local);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
#include "r_state.h"
|
||||
#include "textures/textures.h"
|
||||
#include "math/cmath.h"
|
||||
#include "stats.h"
|
||||
|
||||
extern cycle_t VMCycles[10];
|
||||
extern int VMCalls[10];
|
||||
|
||||
// This must be a separate function because the VC compiler would otherwise allocate memory on the stack for every separate instance of the exception object that may get thrown.
|
||||
void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...);
|
||||
|
|
|
@ -609,7 +609,9 @@ begin:
|
|||
{
|
||||
try
|
||||
{
|
||||
VMCycles[0].Unclock();
|
||||
numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C);
|
||||
VMCycles[0].Clock();
|
||||
}
|
||||
catch (CVMAbortException &err)
|
||||
{
|
||||
|
@ -621,6 +623,7 @@ begin:
|
|||
}
|
||||
else
|
||||
{
|
||||
VMCalls[0]++;
|
||||
VMScriptFunction *script = static_cast<VMScriptFunction *>(call);
|
||||
VMFrame *newf = stack->AllocFrame(script);
|
||||
VMFillParams(reg.param + f->NumParam - B, newf, B);
|
||||
|
@ -663,7 +666,10 @@ begin:
|
|||
{
|
||||
try
|
||||
{
|
||||
return static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret);
|
||||
VMCycles[0].Unclock();
|
||||
auto r = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret);
|
||||
VMCycles[0].Clock();
|
||||
return r;
|
||||
}
|
||||
catch (CVMAbortException &err)
|
||||
{
|
||||
|
@ -675,6 +681,7 @@ begin:
|
|||
}
|
||||
else
|
||||
{ // FIXME: Not a true tail call
|
||||
VMCalls[0]++;
|
||||
VMScriptFunction *script = static_cast<VMScriptFunction *>(call);
|
||||
VMFrame *newf = stack->AllocFrame(script);
|
||||
VMFillParams(reg.param + f->NumParam - B, newf, B);
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
#include <new>
|
||||
#include "dobject.h"
|
||||
#include "v_text.h"
|
||||
#include "stats.h"
|
||||
|
||||
cycle_t VMCycles[10];
|
||||
int VMCalls[10];
|
||||
|
||||
IMPLEMENT_CLASS(VMException, false, false)
|
||||
IMPLEMENT_CLASS(VMFunction, true, true)
|
||||
|
@ -463,12 +467,30 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur
|
|||
}
|
||||
else
|
||||
{
|
||||
AllocFrame(static_cast<VMScriptFunction *>(func));
|
||||
allocated = true;
|
||||
VMFillParams(params, TopFrame(), numparams);
|
||||
int numret = VMExec(this, static_cast<VMScriptFunction *>(func)->Code, results, numresults);
|
||||
PopFrame();
|
||||
return numret;
|
||||
auto code = static_cast<VMScriptFunction *>(func)->Code;
|
||||
// handle empty functions consisting of a single return explicitly so that empty virtual callbacks do not need to set up an entire VM frame.
|
||||
if (code->word == 0x0080804e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (code->word == 0x0004804e)
|
||||
{
|
||||
if (numresults == 0) return 0;
|
||||
results[0].SetInt(static_cast<VMScriptFunction *>(func)->KonstD[0]);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
VMCycles[0].Clock();
|
||||
VMCalls[0]++;
|
||||
AllocFrame(static_cast<VMScriptFunction *>(func));
|
||||
allocated = true;
|
||||
VMFillParams(params, TopFrame(), numparams);
|
||||
int numret = VMExec(this, code, results, numresults);
|
||||
PopFrame();
|
||||
VMCycles[0].Unclock();
|
||||
return numret;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (VMException *exception)
|
||||
|
@ -573,3 +595,17 @@ void ThrowVMException(VMException *x)
|
|||
{
|
||||
throw x;
|
||||
}
|
||||
|
||||
|
||||
ADD_STAT(VM)
|
||||
{
|
||||
double added = 0;
|
||||
int addedc = 0;
|
||||
for (auto d : VMCycles) added += d.TimeMS();
|
||||
for (auto d : VMCalls) addedc += d;
|
||||
memmove(&VMCycles[1], &VMCycles[0], 9 * sizeof(cycle_t));
|
||||
memmove(&VMCalls[1], &VMCalls[0], 9 * sizeof(int));
|
||||
VMCycles[0].Reset();
|
||||
VMCalls[0] = 0;
|
||||
return FStringf("VM time in last 10 tics: %f ms, %d calls", added, addedc);
|
||||
}
|
|
@ -414,15 +414,7 @@ void ParseScripts()
|
|||
while ((lump = Wads.FindLump("ZSCRIPT", &lastlump)) != -1)
|
||||
{
|
||||
DoParse(lump);
|
||||
if (!Args->CheckParm("-zscript"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
Printf(TEXTCOLOR_PURPLE "WARNING!!!\n");
|
||||
Printf(TEXTCOLOR_PURPLE "As of this version, Zscript is still considered a feature in development which can change " TEXTCOLOR_RED "WITHOUT WARNING!!!\n");
|
||||
Printf(TEXTCOLOR_PURPLE "Use at your own risk!\n");
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
106
src/tarray.h
106
src/tarray.h
|
@ -467,6 +467,112 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// A non-resizable array
|
||||
// This is meant for data that can be replaced but is otherwise static as long as it exists.
|
||||
// The reason for it is to replace any global pointer/counter pairs with something that can
|
||||
// be reliably accessed by the scripting VM and which can use 'for' iterator syntax.
|
||||
|
||||
// This is split into two, so that it also can be used to wrap arrays that are not directly allocated.
|
||||
// This first class only gets a reference to some data but won't own it.
|
||||
template <class T>
|
||||
class TStaticPointedArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef TIterator<T> iterator;
|
||||
typedef TIterator<const T> const_iterator;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return &Array[0];
|
||||
}
|
||||
const_iterator begin() const
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
void Init(T *ptr, unsigned cnt)
|
||||
{
|
||||
Array = ptr;
|
||||
Count = cnt;
|
||||
}
|
||||
// Return a reference to an element
|
||||
T &operator[] (size_t index) const
|
||||
{
|
||||
return Array[index];
|
||||
}
|
||||
unsigned int Size() const
|
||||
{
|
||||
return Count;
|
||||
}
|
||||
// Some code needs to access these directly so they cannot be private.
|
||||
T *Array;
|
||||
unsigned int Count;
|
||||
};
|
||||
|
||||
// This second type owns its data, it can delete and reallocate it, but it cannot
|
||||
// resize the array or repurpose its old contents if new ones are about to be created.
|
||||
template <class T>
|
||||
class TStaticArray : public TStaticPointedArray<T>
|
||||
{
|
||||
public:
|
||||
|
||||
////////
|
||||
// This is a dummy constructor that does nothing. The purpose of this
|
||||
// is so you can create a global TArray in the data segment that gets
|
||||
// used by code before startup without worrying about the constructor
|
||||
// resetting it after it's already been used. You MUST NOT use it for
|
||||
// heap- or stack-allocated TArrays.
|
||||
enum ENoInit
|
||||
{
|
||||
NoInit
|
||||
};
|
||||
TStaticArray(ENoInit dummy)
|
||||
{
|
||||
}
|
||||
////////
|
||||
TStaticArray()
|
||||
{
|
||||
this->Count = 0;
|
||||
this->Array = NULL;
|
||||
}
|
||||
// This is not supposed to be copyable.
|
||||
TStaticArray(const TStaticArray<T> &other) = delete;
|
||||
|
||||
~TStaticArray()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
if (this->Array) delete[] this->Array;
|
||||
}
|
||||
void Alloc(unsigned int amount)
|
||||
{
|
||||
Clear();
|
||||
this->Array = new T[amount];
|
||||
this->Count = amount;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// TAutoGrowArray -----------------------------------------------------------
|
||||
// An array with accessors that automatically grow the array as needed.
|
||||
// It can still be used as a normal TArray if needed. ACS uses this for
|
||||
|
|
|
@ -696,7 +696,7 @@ void FString::StripRight ()
|
|||
{
|
||||
size_t max = Len(), i;
|
||||
if (max == 0) return;
|
||||
for (i = --max; i-- > 0; )
|
||||
for (i = --max; i > 0; i--)
|
||||
{
|
||||
if (!isspace((unsigned char)Chars[i]))
|
||||
break;
|
||||
|
@ -728,7 +728,7 @@ void FString::StripRight (const char *charset)
|
|||
{
|
||||
size_t max = Len(), i;
|
||||
if (max == 0) return;
|
||||
for (i = --max; i-- > 0; )
|
||||
for (i = --max; i > 0; i--)
|
||||
{
|
||||
if (!strchr (charset, Chars[i]))
|
||||
break;
|
||||
|
|
|
@ -270,16 +270,22 @@ class Actor : Thinker native
|
|||
virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0);
|
||||
virtual native bool Slam(Actor victim);
|
||||
virtual native void Touch(Actor toucher);
|
||||
virtual native bool CanCollideWith(Actor other, bool passive); // This is an empty native function, it's native for the sole reason of performance as this is in a performance critical spot.
|
||||
|
||||
// Called when an actor is to be reflected by a disc of repulsion.
|
||||
// Returns true to continue normal blast processing.
|
||||
virtual bool SpecialBlastHandling (Actor source, double strength) // this is entirely on the script side with no native part at all.
|
||||
// Called by PIT_CheckThing to check if two actos actually can collide.
|
||||
virtual bool CanCollideWith(Actor other, bool passive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int SpecialMissileHit (Actor victim) // for this no native version exists
|
||||
// Called when an actor is to be reflected by a disc of repulsion.
|
||||
// Returns true to continue normal blast processing.
|
||||
virtual bool SpecialBlastHandling (Actor source, double strength)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is called before a missile gets exploded.
|
||||
virtual int SpecialMissileHit (Actor victim)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -288,6 +294,7 @@ class Actor : Thinker native
|
|||
native static int GetSpriteIndex(name sprt);
|
||||
native static double GetDefaultSpeed(class<Actor> type);
|
||||
native static class<Actor> GetSpawnableType(int spawnnum);
|
||||
native static int ApplyDamageFactors(class<Inventory> itemcls, Name damagetype, int damage, int defdamage);
|
||||
native void RemoveFromHash();
|
||||
native void ChangeTid(int newtid);
|
||||
native static int FindUniqueTid(int start = 0, int limit = 0);
|
||||
|
|
|
@ -9,6 +9,8 @@ class Object native
|
|||
native static vector3, int G_PickPlayerStart(int pnum, int flags = 0);
|
||||
native static int GameType();
|
||||
native static void S_Sound (Sound sound_id, int channel, float volume = 1, float attenuation = ATTN_NORM);
|
||||
native static void S_PauseSound (bool notmusic, bool notsfx);
|
||||
native static void S_ResumeSound (bool notsfx);
|
||||
native static bool S_ChangeMusic(String music_name, int order = 0, bool looping = true, bool force = false);
|
||||
native static void C_MidPrint(string fontname, string textlabel, bool bold = false); // always uses the stringtable.
|
||||
native static uint BAM(double angle);
|
||||
|
@ -138,6 +140,7 @@ struct LevelLocals native
|
|||
native bool checkswitchrange;
|
||||
native bool polygrind;
|
||||
native bool nomonsters;
|
||||
native bool frozen;
|
||||
// level_info_t *info cannot be done yet.
|
||||
}
|
||||
|
||||
|
@ -273,8 +276,7 @@ struct Sector native
|
|||
native int prevsec;
|
||||
native int nextsec;
|
||||
|
||||
native readonly int16 linecount;
|
||||
//line_t **lines; // this is defined internally to avoid exposing some overly complicated type to the parser
|
||||
//TStaticPointedArray<line_t *> Lines; // this is defined internally to avoid exposing some overly complicated type to the parser
|
||||
|
||||
native readonly Sector heightsec;
|
||||
|
||||
|
|
|
@ -1055,3 +1055,32 @@ enum EPuffFlags
|
|||
PF_HITTHINGBLEED = 8,
|
||||
PF_NORANDOMZ = 16
|
||||
};
|
||||
|
||||
enum EPlayerCheats
|
||||
{
|
||||
CF_NOCLIP = 1 << 0, // No clipping, walk through barriers.
|
||||
CF_GODMODE = 1 << 1, // No damage, no health loss.
|
||||
CF_NOVELOCITY = 1 << 2, // Not really a cheat, just a debug aid.
|
||||
CF_NOTARGET = 1 << 3, // [RH] Monsters don't target
|
||||
CF_FLY = 1 << 4, // [RH] Flying player
|
||||
CF_CHASECAM = 1 << 5, // [RH] Put camera behind player
|
||||
CF_FROZEN = 1 << 6, // [RH] Don't let the player move
|
||||
CF_REVERTPLEASE = 1 << 7, // [RH] Stick camera in player's head if (s)he moves
|
||||
CF_STEPLEFT = 1 << 9, // [RH] Play left footstep sound next time
|
||||
CF_FRIGHTENING = 1 << 10, // [RH] Scare monsters away
|
||||
CF_INSTANTWEAPSWITCH= 1 << 11, // [RH] Switch weapons instantly
|
||||
CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use
|
||||
CF_PREDICTING = 1 << 13, // [RH] Player movement is being predicted
|
||||
CF_INTERPVIEW = 1 << 14, // [RH] view was changed outside of input, so interpolate one frame
|
||||
CF_DRAIN = 1 << 16, // Player owns a drain powerup
|
||||
CF_HIGHJUMP = 1 << 18, // more Skulltag flags. Implementation not guaranteed though. ;)
|
||||
CF_REFLECTION = 1 << 19,
|
||||
CF_PROSPERITY = 1 << 20,
|
||||
CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact
|
||||
CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths.
|
||||
CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact
|
||||
CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either.
|
||||
CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either.
|
||||
CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die
|
||||
CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip
|
||||
};
|
||||
|
|
|
@ -116,10 +116,13 @@ extend class Actor
|
|||
A_FaceTarget ();
|
||||
|
||||
Actor fog = Spawn (fire, target.Pos, ALLOW_REPLACE);
|
||||
tracer = fog;
|
||||
fog.target = self;
|
||||
fog.tracer = self.target;
|
||||
fog.A_Fire(0);
|
||||
if (fog != null)
|
||||
{
|
||||
tracer = fog;
|
||||
fog.target = self;
|
||||
fog.tracer = self.target;
|
||||
fog.A_Fire(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ extend class Actor
|
|||
}
|
||||
else
|
||||
{
|
||||
spit.special2 = int((targ.pos.x - pos.x) / spit.Vel.y);
|
||||
spit.special2 = int((targ.pos.x - pos.x) / spit.Vel.x);
|
||||
}
|
||||
// [GZ] Calculates when the projectile will have reached destination
|
||||
spit.special2 += level.maptime;
|
||||
|
|
|
@ -188,6 +188,7 @@ extend class Actor
|
|||
|
||||
// Now launch mushroom cloud
|
||||
Actor aimtarget = Spawn("Mapspot", pos, NO_REPLACE); // We need something to aim at.
|
||||
if (aimtarget == null) return;
|
||||
Actor owner = (flags & MSF_DontHurt) ? target : self;
|
||||
aimtarget.Height = Height;
|
||||
|
||||
|
|
|
@ -233,10 +233,13 @@ extend class Actor
|
|||
SpawnPuff ("BulletPuff", pos, angle, angle, 3);
|
||||
Actor smoke = Spawn ("RevenantTracerSmoke", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE);
|
||||
|
||||
smoke.Vel.Z = 1.;
|
||||
smoke.tics -= random[Tracer](0, 3);
|
||||
if (smoke.tics < 1)
|
||||
smoke.tics = 1;
|
||||
if (smoke != null)
|
||||
{
|
||||
smoke.Vel.Z = 1.;
|
||||
smoke.tics -= random[Tracer](0, 3);
|
||||
if (smoke.tics < 1)
|
||||
smoke.tics = 1;
|
||||
}
|
||||
|
||||
// The rest of this function was identical with Strife's version, except for the angle being used.
|
||||
A_Tracer2(16.875);
|
||||
|
|
|
@ -282,7 +282,7 @@ class ScriptedMarine : Actor
|
|||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_Refire (bool ignoremissile, statelabel jumpto = null)
|
||||
void A_M_Refire (bool ignoremissile = false, statelabel jumpto = null)
|
||||
{
|
||||
if (target == null || target.health <= 0)
|
||||
{
|
||||
|
|
|
@ -335,11 +335,14 @@ extend class Actor
|
|||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Actor mo = Spawn("Feather", pos + (0, 0, 20), NO_REPLACE);
|
||||
mo.target = self;
|
||||
mo.Vel.X = Random2[Feathers]() / 256.;
|
||||
mo.Vel.Y = Random2[Feathers]() / 256.;
|
||||
mo.Vel.Z = 1. + random[Feathers]() / 128.;
|
||||
mo.SetState (mo.SpawnState + (random[Feathers]()&7));
|
||||
if (mo != null)
|
||||
{
|
||||
mo.target = self;
|
||||
mo.Vel.X = Random2[Feathers]() / 256.;
|
||||
mo.Vel.Y = Random2[Feathers]() / 256.;
|
||||
mo.Vel.Z = 1. + random[Feathers]() / 128.;
|
||||
mo.SetState (mo.SpawnState + (random[Feathers]()&7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -166,13 +166,14 @@ class Sorcerer1 : Actor
|
|||
{
|
||||
bSolid = false;
|
||||
Actor mo = Spawn("Sorcerer2", Pos, ALLOW_REPLACE);
|
||||
mo.Translation = Translation;
|
||||
mo.SetStateLabel("Rise");
|
||||
mo.angle = angle;
|
||||
mo.CopyFriendliness (self, true);
|
||||
if (mo != null)
|
||||
{
|
||||
mo.Translation = Translation;
|
||||
mo.SetStateLabel("Rise");
|
||||
mo.angle = angle;
|
||||
mo.CopyFriendliness (self, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -434,9 +435,12 @@ class Sorcerer2FX1 : Actor
|
|||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Actor mo = Spawn("Sorcerer2FXSpark", pos, ALLOW_REPLACE);
|
||||
mo.Vel.X = Random2[BlueSpark]() / 128.;
|
||||
mo.Vel.Y = Random2[BlueSpark]() / 128.;
|
||||
mo.Vel.Z = 1. + Random[BlueSpark]() / 256.;
|
||||
if (mo != null)
|
||||
{
|
||||
mo.Vel.X = Random2[BlueSpark]() / 128.;
|
||||
mo.Vel.Y = Random2[BlueSpark]() / 128.;
|
||||
mo.Vel.Z = 1. + Random[BlueSpark]() / 256.;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ Class ArtiTimeBomb : Inventory
|
|||
override bool Use (bool pickup)
|
||||
{
|
||||
Actor mo = Spawn("ActivatedTimeBomb", Owner.Vec3Angle(24., Owner.angle, - Owner.Floorclip), ALLOW_REPLACE);
|
||||
mo.target = Owner;
|
||||
if (mo != null) mo.target = Owner;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,14 +102,20 @@ class HereticImp : Actor
|
|||
bNoGravity = false;
|
||||
|
||||
chunk = Spawn("HereticImpChunk1", pos, ALLOW_REPLACE);
|
||||
chunk.vel.x = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.y = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.z = 9;
|
||||
if (chunk != null)
|
||||
{
|
||||
chunk.vel.x = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.y = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.z = 9;
|
||||
}
|
||||
|
||||
chunk = Spawn("HereticImpChunk2", pos, ALLOW_REPLACE);
|
||||
chunk.vel.x = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.y = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.z = 9;
|
||||
if (chunk != null)
|
||||
{
|
||||
chunk.vel.x = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.y = random2[ImpExplode]() / 64.;
|
||||
chunk.vel.z = 9;
|
||||
}
|
||||
|
||||
if (extremecrash)
|
||||
{
|
||||
|
|
|
@ -60,10 +60,13 @@ class Pod : Actor
|
|||
for (int count = chance > 240 ? 2 : 1; count; count--)
|
||||
{
|
||||
Actor goo = Spawn(gootype, pos + (0, 0, 48), ALLOW_REPLACE);
|
||||
goo.target = self;
|
||||
goo.Vel.X = Random2[PodPain]() / 128.;
|
||||
goo.Vel.Y = Random2[PodPain]() / 128.;
|
||||
goo.Vel.Z = 0.5 + random[PodPain]() / 128.;
|
||||
if (goo != null)
|
||||
{
|
||||
goo.target = self;
|
||||
goo.Vel.X = Random2[PodPain]() / 128.;
|
||||
goo.Vel.Y = Random2[PodPain]() / 128.;
|
||||
goo.Vel.Z = 0.5 + random[PodPain]() / 128.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,12 +298,15 @@ class Volcano : Actor
|
|||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Actor blast = Spawn("VolcanoBlast", pos + (0, 0, 44), ALLOW_REPLACE);
|
||||
blast.target = self;
|
||||
blast.Angle = random[VolcanoBlast]() * (360 / 256.);
|
||||
blast.VelFromAngle(1.);
|
||||
blast.Vel.Z = 2.5 + random[VolcanoBlast]() / 64.;
|
||||
blast.A_PlaySound ("world/volcano/shoot", CHAN_BODY);
|
||||
blast.CheckMissileSpawn (radius);
|
||||
if (blast != null)
|
||||
{
|
||||
blast.target = self;
|
||||
blast.Angle = random[VolcanoBlast]() * (360 / 256.);
|
||||
blast.VelFromAngle(1.);
|
||||
blast.Vel.Z = 2.5 + random[VolcanoBlast]() / 64.;
|
||||
blast.A_PlaySound ("world/volcano/shoot", CHAN_BODY);
|
||||
blast.CheckMissileSpawn (radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,12 +102,15 @@ class Ironlich : Actor
|
|||
{
|
||||
A_PlaySound ("ironlich/attack1", CHAN_BODY);
|
||||
}
|
||||
fire.target = baseFire.target;
|
||||
fire.angle = baseFire.angle;
|
||||
fire.Vel = baseFire.Vel;
|
||||
fire.SetDamage(0);
|
||||
fire.health = (i+1) * 2;
|
||||
fire.CheckMissileSpawn (radius);
|
||||
if (fire != null)
|
||||
{
|
||||
fire.target = baseFire.target;
|
||||
fire.angle = baseFire.angle;
|
||||
fire.Vel = baseFire.Vel;
|
||||
fire.SetDamage(0);
|
||||
fire.health = (i+1) * 2;
|
||||
fire.CheckMissileSpawn (radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,11 +170,14 @@ class HeadFX1 : Actor
|
|||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
Actor shard = Spawn("HeadFX2", Pos, ALLOW_REPLACE);
|
||||
shard.target = target;
|
||||
shard.angle = i*45.;
|
||||
shard.VelFromAngle();
|
||||
shard.Vel.Z = -.6;
|
||||
shard.CheckMissileSpawn (radius);
|
||||
if (shard != null)
|
||||
{
|
||||
shard.target = target;
|
||||
shard.angle = i*45.;
|
||||
shard.VelFromAngle();
|
||||
shard.Vel.Z = -.6;
|
||||
shard.CheckMissileSpawn (radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,8 +163,11 @@ class RedAxe : KnightAxe
|
|||
double xo = random2[DripBlood]() / 32.0;
|
||||
double yo = random2[DripBlood]() / 32.0;
|
||||
Actor mo = Spawn ("Blood", Vec3Offset(xo, yo, 0.), ALLOW_REPLACE);
|
||||
mo.Vel.X = random2[DripBlood]() / 64.0;
|
||||
mo.Vel.Y = random2[DripBlood]() / 64.0;
|
||||
mo.Gravity = 1./8;
|
||||
if (mo != null)
|
||||
{
|
||||
mo.Vel.X = random2[DripBlood]() / 64.0;
|
||||
mo.Vel.Y = random2[DripBlood]() / 64.0;
|
||||
mo.Gravity = 1./8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,10 +160,13 @@ class BlasterFX1 : FastProjectile
|
|||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
Actor ripper = Spawn("Ripper", pos, ALLOW_REPLACE);
|
||||
ripper.target = target;
|
||||
ripper.angle = i*45;
|
||||
ripper.VelFromAngle();
|
||||
ripper.CheckMissileSpawn (radius);
|
||||
if (ripper != null)
|
||||
{
|
||||
ripper.target = target;
|
||||
ripper.angle = i*45;
|
||||
ripper.VelFromAngle();
|
||||
ripper.CheckMissileSpawn (radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,14 +60,17 @@ class Mace : HereticWeapon
|
|||
if (random[MaceAtk]() < 28)
|
||||
{
|
||||
Actor ball = Spawn("MaceFX2", Pos + (0, 0, 28 - Floorclip), ALLOW_REPLACE);
|
||||
ball.Vel.Z = 2 - clamp(tan(pitch), -5, 5);
|
||||
ball.target = self;
|
||||
ball.angle = self.angle;
|
||||
ball.AddZ(ball.Vel.Z);
|
||||
ball.VelFromAngle();
|
||||
ball.Vel += Vel.xy / 2;
|
||||
ball.A_PlaySound ("weapons/maceshoot", CHAN_BODY);
|
||||
ball.CheckMissileSpawn (radius);
|
||||
if (ball != null)
|
||||
{
|
||||
ball.Vel.Z = 2 - clamp(tan(pitch), -5, 5);
|
||||
ball.target = self;
|
||||
ball.angle = self.angle;
|
||||
ball.AddZ(ball.Vel.Z);
|
||||
ball.VelFromAngle();
|
||||
ball.Vel += Vel.xy / 2;
|
||||
ball.A_PlaySound ("weapons/maceshoot", CHAN_BODY);
|
||||
ball.CheckMissileSpawn (radius);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -260,18 +263,24 @@ class MaceFX2 : MaceFX1
|
|||
SetState (SpawnState);
|
||||
|
||||
Actor tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE);
|
||||
tiny.target = target;
|
||||
tiny.angle = angle + 90.;
|
||||
tiny.VelFromAngle(Vel.Z - 1.);
|
||||
tiny.Vel += (Vel.XY * .5, Vel.Z);
|
||||
tiny.CheckMissileSpawn (radius);
|
||||
if (tiny != null)
|
||||
{
|
||||
tiny.target = target;
|
||||
tiny.angle = angle + 90.;
|
||||
tiny.VelFromAngle(Vel.Z - 1.);
|
||||
tiny.Vel += (Vel.XY * .5, Vel.Z);
|
||||
tiny.CheckMissileSpawn (radius);
|
||||
}
|
||||
|
||||
tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE);
|
||||
tiny.target = target;
|
||||
tiny.angle = angle - 90.;
|
||||
tiny.VelFromAngle(Vel.Z - 1.);
|
||||
tiny.Vel += (Vel.XY * .5, Vel.Z);
|
||||
tiny.CheckMissileSpawn (radius);
|
||||
if (tiny != null)
|
||||
{
|
||||
tiny.target = target;
|
||||
tiny.angle = angle - 90.;
|
||||
tiny.VelFromAngle(Vel.Z - 1.);
|
||||
tiny.Vel += (Vel.XY * .5, Vel.Z);
|
||||
tiny.CheckMissileSpawn (radius);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,16 +151,19 @@ class PhoenixRodPowered : PhoenixRod
|
|||
|
||||
slope += 0.1;
|
||||
Actor mo = Spawn("PhoenixFX2", spawnpos, ALLOW_REPLACE);
|
||||
mo.target = self;
|
||||
mo.Angle = Angle;
|
||||
mo.VelFromAngle();
|
||||
mo.Vel.XY += Vel.XY;
|
||||
mo.Vel.Z = mo.Speed * slope;
|
||||
if (mo != null)
|
||||
{
|
||||
mo.target = self;
|
||||
mo.Angle = Angle;
|
||||
mo.VelFromAngle();
|
||||
mo.Vel.XY += Vel.XY;
|
||||
mo.Vel.Z = mo.Speed * slope;
|
||||
mo.CheckMissileSpawn (radius);
|
||||
}
|
||||
if (!player.refire)
|
||||
{
|
||||
A_PlaySound("weapons/phoenixpowshoot", CHAN_WEAPON, 1, true);
|
||||
}
|
||||
mo.CheckMissileSpawn (radius);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -239,10 +242,16 @@ class PhoenixFX1 : Actor
|
|||
//[RH] Heretic never sets the target for seeking
|
||||
//P_SeekerMissile (self, 5, 10);
|
||||
Actor puff = Spawn("PhoenixPuff", Pos, ALLOW_REPLACE);
|
||||
puff.Vel.XY = AngleToVector(Angle + 90, 1.3);
|
||||
if (puff != null)
|
||||
{
|
||||
puff.Vel.XY = AngleToVector(Angle + 90, 1.3);
|
||||
}
|
||||
|
||||
puff = Spawn("PhoenixPuff", Pos, ALLOW_REPLACE);
|
||||
puff.Vel.XY = AngleToVector(Angle - 90, 1.3);
|
||||
if (puff != null)
|
||||
{
|
||||
puff.Vel.XY = AngleToVector(Angle - 90, 1.3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ extend class Actor
|
|||
// [RH] Floor and ceiling huggers should not be blasted vertically.
|
||||
if (!victim.bFloorHugger && !victim.bCeilingHugger)
|
||||
{
|
||||
mo.Vel.Z = victim.Vel.Z = 8;
|
||||
victim.Vel.Z = 8;
|
||||
if (mo != null) mo.Vel.Z = 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -567,14 +567,17 @@ class HolyTail : Actor
|
|||
static void SpawnSpiritTail (Actor spirit)
|
||||
{
|
||||
Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE);
|
||||
tail.target = spirit; // parent
|
||||
for (int i = 1; i < 3; i++)
|
||||
if (tail != null)
|
||||
{
|
||||
Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE);
|
||||
tail.tracer = next;
|
||||
tail = next;
|
||||
tail.target = spirit; // parent
|
||||
for (int i = 1; i < 3; i++)
|
||||
{
|
||||
Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE);
|
||||
tail.tracer = next;
|
||||
tail = next;
|
||||
}
|
||||
tail.tracer = null; // last tail bit
|
||||
}
|
||||
tail.tracer = null; // last tail bit
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
|
@ -255,10 +255,13 @@ class ArtiPoisonBag : Inventory
|
|||
}
|
||||
|
||||
class<Actor> spawntype = GetFlechetteType(other);
|
||||
Inventory copy = Inventory(Spawn (spawntype));
|
||||
copy.Amount = Amount;
|
||||
copy.MaxAmount = MaxAmount;
|
||||
GoAwayAndDie ();
|
||||
let copy = Inventory(Spawn (spawntype));
|
||||
if (copy != null)
|
||||
{
|
||||
copy.Amount = Amount;
|
||||
copy.MaxAmount = MaxAmount;
|
||||
GoAwayAndDie ();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class LittleFly : Actor
|
|||
return fallback;
|
||||
}
|
||||
// Try neighboring sectors
|
||||
for (int i = 0; i < sec.linecount; ++i)
|
||||
for (int i = 0; i < sec.lines.Size(); ++i)
|
||||
{
|
||||
line ln = sec.lines[i];
|
||||
sector sec2 = (ln.frontsector == sec) ? ln.backsector : ln.frontsector;
|
||||
|
|
|
@ -315,17 +315,20 @@ class Korax : Actor
|
|||
private void SpawnKoraxMissile (Vector3 pos, Actor dest, Class<Actor> type)
|
||||
{
|
||||
Actor th = Spawn (type, pos, ALLOW_REPLACE);
|
||||
th.target = self; // Originator
|
||||
double an = th.AngleTo(dest);
|
||||
if (dest.bShadow)
|
||||
{ // Invisible target
|
||||
an += Random2[KoraxMissile]() * (45/256.);
|
||||
if (th != null)
|
||||
{
|
||||
th.target = self; // Originator
|
||||
double an = th.AngleTo(dest);
|
||||
if (dest.bShadow)
|
||||
{ // Invisible target
|
||||
an += Random2[KoraxMissile]() * (45/256.);
|
||||
}
|
||||
th.angle = an;
|
||||
th.VelFromAngle();
|
||||
double dist = dest.DistanceBySpeed(th, th.Speed);
|
||||
th.Vel.Z = (dest.pos.z - pos.Z + 30) / dist;
|
||||
th.CheckMissileSpawn(radius);
|
||||
}
|
||||
th.angle = an;
|
||||
th.VelFromAngle();
|
||||
double dist = dest.DistanceBySpeed(th, th.Speed);
|
||||
th.Vel.Z = (dest.pos.z - pos.Z + 30) / dist;
|
||||
th.CheckMissileSpawn(radius);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
|
@ -262,7 +262,7 @@ class Minotaur : Actor
|
|||
type = "PunchPuff";
|
||||
}
|
||||
Actor puff = Spawn (type, Pos, ALLOW_REPLACE);
|
||||
puff.Vel.Z = 2;
|
||||
if (puff != null) puff.Vel.Z = 2;
|
||||
special1--;
|
||||
}
|
||||
else
|
||||
|
@ -710,9 +710,12 @@ class MinotaurFX2 : MinotaurFX1
|
|||
double y = Random2[MntrFloorFire]() / 64.;
|
||||
|
||||
Actor mo = Spawn("MinotaurFX3", Vec2OffsetZ(x, y, floorz), ALLOW_REPLACE);
|
||||
mo.target = target;
|
||||
mo.Vel.X = MinVel; // Force block checking
|
||||
mo.CheckMissileSpawn (radius);
|
||||
if (mo != null)
|
||||
{
|
||||
mo.target = target;
|
||||
mo.Vel.X = MinVel; // Force block checking
|
||||
mo.CheckMissileSpawn (radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ class Inventory : Actor native
|
|||
|
||||
virtual double GetSpeedFactor() { return 1; }
|
||||
virtual bool GetNoTeleportFreeze() { return false; }
|
||||
virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {}
|
||||
|
||||
native void GoAwayAndDie();
|
||||
native void BecomeItem();
|
||||
|
|
|
@ -251,6 +251,7 @@ FWeaponSlots weapons;
|
|||
*/
|
||||
|
||||
|
||||
native bool MorphPlayer(playerinfo p, Class<PlayerPawn> spawntype, int duration, int style, Class<Actor> enter_flash = null, Class<Actor> exit_flash = null);
|
||||
native bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false);
|
||||
native bool PoisonPlayer(Actor poisoner, Actor source, int poison);
|
||||
native void PoisonDamage(Actor source, int damage, bool playPainSound);
|
||||
|
|
|
@ -25,6 +25,9 @@ class Powerup : Inventory native
|
|||
|
||||
// Note, that while this is an inventory flag, it only has meaning on an active powerup.
|
||||
override bool GetNoTeleportFreeze() { return bNoTeleportFreeze; }
|
||||
|
||||
native virtual void InitEffect();
|
||||
native virtual void EndEffect();
|
||||
|
||||
}
|
||||
|
||||
|
@ -166,7 +169,7 @@ class PlayerSpeedTrail : Actor
|
|||
}
|
||||
}
|
||||
|
||||
class PowerMinotaur : Powerup native
|
||||
class PowerMinotaur : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
|
@ -194,23 +197,81 @@ class PowerTargeter : Powerup native
|
|||
}
|
||||
}
|
||||
|
||||
class PowerFrightener : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Frightener
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerFrightener : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -60;
|
||||
}
|
||||
|
||||
override void InitEffect ()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner== null || Owner.player == null)
|
||||
return;
|
||||
|
||||
Owner.player.cheats |= CF_FRIGHTENING;
|
||||
}
|
||||
|
||||
override void EndEffect ()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
if (Owner== null || Owner.player == null)
|
||||
return;
|
||||
|
||||
Owner.player.cheats &= ~CF_FRIGHTENING;
|
||||
}
|
||||
}
|
||||
|
||||
class PowerBuddha : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Buddha
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerBuddha : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -60;
|
||||
}
|
||||
|
||||
override void InitEffect ()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner== null || Owner.player == null)
|
||||
return;
|
||||
|
||||
Owner.player.cheats |= CF_BUDDHA;
|
||||
}
|
||||
|
||||
override void EndEffect ()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
if (Owner== null || Owner.player == null)
|
||||
return;
|
||||
|
||||
Owner.player.cheats &= ~CF_BUDDHA;
|
||||
}
|
||||
}
|
||||
|
||||
class PowerScanner : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Scanner (this is active just by being present)
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerScanner : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
|
@ -219,50 +280,460 @@ class PowerScanner : Powerup native
|
|||
}
|
||||
}
|
||||
|
||||
class PowerTimeFreezer : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// TimeFreezer
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerTimeFreezer : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -12;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
int freezemask;
|
||||
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner == null || Owner.player == null)
|
||||
return;
|
||||
|
||||
// When this powerup is in effect, pause the music.
|
||||
S_PauseSound(false, false);
|
||||
|
||||
// Give the player and his teammates the power to move when time is frozen.
|
||||
freezemask = 1 << Owner.PlayerNumber();
|
||||
Owner.player.timefreezer |= freezemask;
|
||||
for (int i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] &&
|
||||
players[i].mo != null &&
|
||||
players[i].mo.IsTeammate(Owner)
|
||||
)
|
||||
{
|
||||
players[i].timefreezer |= freezemask;
|
||||
}
|
||||
}
|
||||
|
||||
// [RH] The effect ends one tic after the counter hits zero, so make
|
||||
// sure we start at an odd count.
|
||||
EffectTics += !(EffectTics & 1);
|
||||
if ((EffectTics & 1) == 0)
|
||||
{
|
||||
EffectTics++;
|
||||
}
|
||||
// Make sure the effect starts and ends on an even tic.
|
||||
if ((level.time & 1) == 0)
|
||||
{
|
||||
level.frozen = true;;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compensate for skipped tic, but beware of overflow.
|
||||
if(EffectTics < 0x7fffffff)
|
||||
EffectTics++;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerTimeFreezer :: DoEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
// [RH] Do not change LEVEL_FROZEN on odd tics, or the Revenant's tracer
|
||||
// will get thrown off.
|
||||
// [ED850] Don't change it if the player is predicted either.
|
||||
if (level.time & 1 || (Owner != null && Owner.player != null && Owner.player.cheats & CF_PREDICTING))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// [RH] The "blinking" can't check against EffectTics exactly or it will
|
||||
// never happen, because InitEffect ensures that EffectTics will always
|
||||
// be odd when level.time is even.
|
||||
level.frozen = ( EffectTics > 4*32
|
||||
|| (( EffectTics > 3*32 && EffectTics <= 4*32 ) && ((EffectTics + 1) & 15) != 0 )
|
||||
|| (( EffectTics > 2*32 && EffectTics <= 3*32 ) && ((EffectTics + 1) & 7) != 0 )
|
||||
|| (( EffectTics > 32 && EffectTics <= 2*32 ) && ((EffectTics + 1) & 3) != 0 )
|
||||
|| (( EffectTics > 0 && EffectTics <= 1*32 ) && ((EffectTics + 1) & 1) != 0 ));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerTimeFreezer :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
// If there is an owner, remove the timefreeze flag corresponding to
|
||||
// her from all players.
|
||||
if (Owner != null && Owner.player != null)
|
||||
{
|
||||
int freezemask = ~(1 << Owner.PlayerNumber());
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
players[i].timefreezer &= freezemask;
|
||||
}
|
||||
}
|
||||
|
||||
// Are there any players who still have timefreezer bits set?
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (playeringame[i] && players[i].timefreezer != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No, so allow other actors to move about freely once again.
|
||||
level.frozen = false;
|
||||
|
||||
// Also, turn the music back on.
|
||||
S_ResumeSound(false);
|
||||
}
|
||||
}
|
||||
|
||||
class PowerDamage : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Damage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerDamage : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -25;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.A_PlaySound(SeeSound, CHAN_5, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.A_PlaySound(DeathSound, CHAN_5, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ModifyDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive)
|
||||
{
|
||||
if (!passive && damage > 0)
|
||||
{
|
||||
newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage * 4));
|
||||
if (Owner != null && newdamage > damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PowerProtection : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Protection
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerProtection : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -25;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
let o = Owner; // copy to a local variable for quicker access.
|
||||
if (o != null)
|
||||
{
|
||||
o.A_PlaySound(SeeSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
|
||||
// Transfer various protection flags if owner does not already have them.
|
||||
// If the owner already has the flag, clear it from the powerup.
|
||||
// If the powerup still has a flag set, add it to the owner.
|
||||
bNoRadiusDmg &= !o.bNoRadiusDmg;
|
||||
o.bNoRadiusDmg |= bNoRadiusDmg;
|
||||
|
||||
bDontMorph &= !o.bDontMorph;
|
||||
o.bDontMorph |= bDontMorph;
|
||||
|
||||
bDontSquash &= !o.bDontSquash;
|
||||
o.bDontSquash |= bDontSquash;
|
||||
|
||||
bDontBlast &= !o.bDontBlast;
|
||||
o.bDontBlast |= bDontBlast;
|
||||
|
||||
bNoTeleOther &= !o.bNoTeleOther;
|
||||
o.bNoTeleOther |= bNoTeleOther;
|
||||
|
||||
bNoPain &= !o.bNoPain;
|
||||
o.bNoPain |= bNoPain;
|
||||
|
||||
bDontRip &= !o.bDontRip;
|
||||
o.bDontRip |= bDontRip;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
let o = Owner; // copy to a local variable for quicker access.
|
||||
if (o != null)
|
||||
{
|
||||
o.A_PlaySound(DeathSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
|
||||
o.bNoRadiusDmg &= !bNoRadiusDmg;
|
||||
o.bDontMorph &= !bDontMorph;
|
||||
o.bDontSquash &= !bDontSquash;
|
||||
o.bDontBlast &= !bDontBlast;
|
||||
o.bNoTeleOther &= !bNoTeleOther;
|
||||
o.bNoPain &= !bNoPain;
|
||||
o.bDontRip &= !bDontRip;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// AbsorbDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive)
|
||||
{
|
||||
if (passive && damage > 0)
|
||||
{
|
||||
newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage / 4));
|
||||
if (Owner != null && newdamage < damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PowerDrain : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Drain
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerDrain : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -60;
|
||||
}
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Give the player the power to drain life from opponents when he damages them.
|
||||
Owner.player.cheats |= CF_DRAIN;
|
||||
}
|
||||
}
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Take away the drain power.
|
||||
Owner.player.cheats &= ~CF_DRAIN;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PowerRegeneration : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Regeneration
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerRegeneration : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -120;
|
||||
Powerup.Strength 5;
|
||||
}
|
||||
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if (Owner != null && Owner.health > 0 && (level.time & 31) == 0)
|
||||
{
|
||||
if (Owner.GiveBody(int(Strength)))
|
||||
{
|
||||
Owner.A_PlaySound("*regenerate", CHAN_ITEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PowerHighJump : Powerup native {}
|
||||
//===========================================================================
|
||||
//
|
||||
// HighJump
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerDoubleFiringSpeed : Powerup native {}
|
||||
class PowerHighJump : Powerup
|
||||
{
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Give the player the power to jump much higher.
|
||||
Owner.player.cheats |= CF_HIGHJUMP;
|
||||
}
|
||||
}
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Take away the high jump power.
|
||||
Owner.player.cheats &= ~CF_HIGHJUMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// DoubleFiringSpeed
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerDoubleFiringSpeed : Powerup
|
||||
{
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Give the player the power to shoot twice as fast.
|
||||
Owner.player.cheats |= CF_DOUBLEFIRINGSPEED;
|
||||
}
|
||||
}
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Take away the shooting twice as fast power.
|
||||
Owner.player.cheats &= ~CF_DOUBLEFIRINGSPEED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InfiniteAmmo
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerInfiniteAmmo : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -30;
|
||||
}
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Give the player infinite ammo
|
||||
Owner.player.cheats |= CF_INFINITEAMMO;
|
||||
}
|
||||
}
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
// Nothing to do if there's no owner.
|
||||
if (Owner!= null && Owner.player != null)
|
||||
{
|
||||
// Take away the limitless ammo
|
||||
Owner.player.cheats &= ~CF_INFINITEAMMO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// PowerMorph
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerMorph : Powerup native
|
||||
{
|
||||
|
@ -270,19 +741,82 @@ class PowerMorph : Powerup native
|
|||
native Class<Actor> MorphFlash, UnMorphFlash;
|
||||
native int MorphStyle;
|
||||
native PlayerInfo MorphedPlayer;
|
||||
native bool bInUndoMorph;
|
||||
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -40;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerInfiniteAmmo : Powerup native
|
||||
{
|
||||
Default
|
||||
override void InitEffect()
|
||||
{
|
||||
Powerup.Duration -30;
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner != null && Owner.player != null && PlayerClass != null)
|
||||
{
|
||||
let realplayer = Owner.player; // Remember the identity of the player
|
||||
if (realplayer.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash))
|
||||
{
|
||||
Owner = realplayer.mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
|
||||
bCreateCopyMoved = true; // Let the caller know the "real" owner has changed (to the morphed actor)
|
||||
MorphedPlayer = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
|
||||
}
|
||||
else // morph failed - give the caller an opportunity to fail the pickup completely
|
||||
{
|
||||
bInitEffectFailed = true; // Let the caller know that the activation failed (can fail the pickup if appropriate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
|
||||
// Abort if owner already destroyed or unmorphed
|
||||
if (Owner == null || MorphedPlayer == null || Owner.alternative == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if owner is dead; their Die() method will
|
||||
// take care of any required unmorphing on death.
|
||||
if (MorphedPlayer.health <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int savedMorphTics = MorphedPlayer.morphTics;
|
||||
MorphedPlayer.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS));
|
||||
|
||||
// Abort if unmorph failed; in that case,
|
||||
// set the usual retry timer and return.
|
||||
if (MorphedPlayer != null && MorphedPlayer.morphTics)
|
||||
{
|
||||
// Transfer retry timeout
|
||||
// to the powerup's timer.
|
||||
EffectTics = MorphedPlayer.morphTics;
|
||||
// Reload negative morph tics;
|
||||
// use actual value; it may
|
||||
// be in use for animation.
|
||||
MorphedPlayer.morphTics = savedMorphTics;
|
||||
// Try again some time later
|
||||
return;
|
||||
}
|
||||
// Unmorph suceeded
|
||||
MorphedPlayer = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -202,9 +202,12 @@ class EntityBoss : SpectralMonster
|
|||
Vector3 pos = spot.Vec3Angle(secondRadius, an, tracer ? 70. : 0.);
|
||||
|
||||
second = Spawn("EntitySecond", pos, ALLOW_REPLACE);
|
||||
second.CopyFriendliness(self, true);
|
||||
second.A_FaceTarget();
|
||||
second.VelFromAngle(i == 0? 4.8828125 : secondRadius * 4., an);
|
||||
if (second != null)
|
||||
{
|
||||
second.CopyFriendliness(self, true);
|
||||
second.A_FaceTarget();
|
||||
second.VelFromAngle(i == 0? 4.8828125 : secondRadius * 4., an);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -171,9 +171,12 @@ class Inquisitor : Actor
|
|||
void A_TossArm ()
|
||||
{
|
||||
Actor foo = Spawn("InquisitorArm", Pos + (0,0,24), ALLOW_REPLACE);
|
||||
foo.angle = angle - 90. + Random2[Inquisitor]() * (360./1024.);
|
||||
foo.VelFromAngle(foo.Speed / 8);
|
||||
foo.Vel.Z = random[Inquisitor]() / 64.;
|
||||
if (foo != null)
|
||||
{
|
||||
foo.angle = angle - 90. + Random2[Inquisitor]() * (360./1024.);
|
||||
foo.VelFromAngle(foo.Speed / 8);
|
||||
foo.Vel.Z = random[Inquisitor]() / 64.;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -163,6 +163,10 @@ class TeleporterBeacon : Inventory
|
|||
{
|
||||
Actor owner = target;
|
||||
Actor rebel = Spawn("Rebel1", (pos.xy, floorz), ALLOW_REPLACE);
|
||||
if (rebel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!rebel.TryMove (rebel.Pos.xy, true))
|
||||
{
|
||||
rebel.Destroy ();
|
||||
|
|
|
@ -268,7 +268,7 @@ class Sigil : Weapon
|
|||
|
||||
action void A_FireSigil1 ()
|
||||
{
|
||||
Actor spot;
|
||||
Actor spot = null;
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (player == null || player.ReadyWeapon == null)
|
||||
|
|
|
@ -62,11 +62,13 @@ class SpectralMonster : Actor
|
|||
return;
|
||||
|
||||
Actor foo = Spawn("SpectralLightningV2", Pos + (0, 0, 32), ALLOW_REPLACE);
|
||||
|
||||
foo.Vel.Z = -12;
|
||||
foo.target = self;
|
||||
foo.FriendPlayer = 0;
|
||||
foo.tracer = target;
|
||||
if (foo != null)
|
||||
{
|
||||
foo.Vel.Z = -12;
|
||||
foo.target = self;
|
||||
foo.FriendPlayer = 0;
|
||||
foo.tracer = target;
|
||||
}
|
||||
|
||||
Angle -= 90.;
|
||||
for (int i = 0; i < 20; ++i)
|
||||
|
@ -217,9 +219,11 @@ class SpectralLightningH1 : SpectralLightningBase
|
|||
void A_SpectralLightningTail ()
|
||||
{
|
||||
Actor foo = Spawn("SpectralLightningHTail", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE);
|
||||
|
||||
foo.Angle = Angle;
|
||||
foo.FriendPlayer = FriendPlayer;
|
||||
if (foo != null)
|
||||
{
|
||||
foo.Angle = Angle;
|
||||
foo.FriendPlayer = FriendPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,15 +390,21 @@ class SpectralLightningSpot : SpectralLightningDeath1
|
|||
|
||||
Actor flash = Spawn (cls, Vec2OffsetZ(xo, yo, ONCEILINGZ), ALLOW_REPLACE);
|
||||
|
||||
flash.target = target;
|
||||
flash.Vel.Z = -18;
|
||||
flash.FriendPlayer = FriendPlayer;
|
||||
if (flash != null)
|
||||
{
|
||||
flash.target = target;
|
||||
flash.Vel.Z = -18;
|
||||
flash.FriendPlayer = FriendPlayer;
|
||||
}
|
||||
|
||||
flash = Spawn("SpectralLightningV2", (pos.xy, ONCEILINGZ), ALLOW_REPLACE);
|
||||
|
||||
flash.target = target;
|
||||
flash.Vel.Z = -18;
|
||||
flash.FriendPlayer = FriendPlayer;
|
||||
if (flash != null)
|
||||
{
|
||||
flash.target = target;
|
||||
flash.Vel.Z = -18;
|
||||
flash.FriendPlayer = FriendPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -133,7 +133,10 @@ extend class Actor
|
|||
void A_DropFire()
|
||||
{
|
||||
Actor drop = Spawn("FireDroplet", pos + (0,0,24), ALLOW_REPLACE);
|
||||
drop.Vel.Z = -1.;
|
||||
if (drop != null)
|
||||
{
|
||||
drop.Vel.Z = -1.;
|
||||
}
|
||||
A_Explode(64, 64, XF_NOSPLASH, damagetype: 'Fire');
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue