# 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:
nashmuhandes 2017-01-04 14:32:15 +08:00
commit f45561b5e8
83 changed files with 1602 additions and 1160 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 = &sectors[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)
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -268,7 +268,7 @@ class Sigil : Weapon
action void A_FireSigil1 ()
{
Actor spot;
Actor spot = null;
FTranslatedLineTarget t;
if (player == null || player.ReadyWeapon == null)

View file

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

View file

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