Merge branch 'zscript' of https://github.com/rheit/zdoom into gz-zscript

# Conflicts:
#	wadsrc/static/zscript.txt
This commit is contained in:
Christoph Oelckers 2016-11-30 18:46:23 +01:00
commit c927aca2a0
400 changed files with 27669 additions and 25633 deletions

View file

@ -179,8 +179,12 @@ if( WIN32 )
comdlg32
ws2_32
setupapi
oleaut32
DelayImp )
oleaut32 )
if( NOT ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set( ZDOOM_LIBS ${ZDOOM_LIBS} DelayImp )
endif()
if( DX_dxguid_LIBRARY )
list( APPEND ZDOOM_LIBS "${DX_dxguid_LIBRARY}" )
endif()
@ -755,15 +759,7 @@ if( WIN32 )
set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} )
set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} )
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
# CMake is not set up to compile and link rc files with GCC. :(
add_custom_command( OUTPUT zdoom-rc.o
COMMAND windres -o zdoom-rc.o -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zdoom.rc
DEPENDS win32/zdoom.rc )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} zdoom-rc.o )
else()
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} win32/zdoom.rc )
endif()
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} win32/zdoom.rc )
elseif( APPLE )
if( OSX_COCOA_BACKEND )
set( SYSTEM_SOURCES_DIR posix posix/cocoa )
@ -878,11 +874,8 @@ endif()
file( GLOB HEADER_FILES
${EXTRA_HEADER_DIRS}
fragglescript/*.h
g_doom/*.h
g_heretic/*.h
g_hexen/*.h
g_raven/*.h
g_shared/*.h
g_inventory/*.h
g_strife/*.h
intermission/*.h
menu/*.h
@ -927,61 +920,6 @@ set( NOT_COMPILED_SOURCE_FILES
${OTHER_SYSTEM_SOURCES}
sc_man_scanner.h
sc_man_scanner.re
g_doom/a_bossbrain.cpp
g_doom/a_doomweaps.cpp
g_doom/a_painelemental.cpp
g_doom/a_scriptedmarine.cpp
g_heretic/a_chicken.cpp
g_heretic/a_dsparil.cpp
g_heretic/a_hereticartifacts.cpp
g_heretic/a_hereticweaps.cpp
g_heretic/a_ironlich.cpp
g_hexen/a_blastradius.cpp
g_hexen/a_boostarmor.cpp
g_hexen/a_clericflame.cpp
g_hexen/a_clericholy.cpp
g_hexen/a_clericmace.cpp
g_hexen/a_clericstaff.cpp
g_hexen/a_dragon.cpp
g_hexen/a_fighteraxe.cpp
g_hexen/a_fighterhammer.cpp
g_hexen/a_fighterplayer.cpp
g_hexen/a_fighterquietus.cpp
g_hexen/a_flechette.cpp
g_hexen/a_flies.cpp
g_hexen/a_fog.cpp
g_hexen/a_healingradius.cpp
g_hexen/a_heresiarch.cpp
g_hexen/a_hexenspecialdecs.cpp
g_hexen/a_iceguy.cpp
g_hexen/a_korax.cpp
g_hexen/a_magecone.cpp
g_hexen/a_magelightning.cpp
g_hexen/a_magestaff.cpp
g_hexen/a_pig.cpp
g_hexen/a_serpent.cpp
g_hexen/a_spike.cpp
g_hexen/a_summon.cpp
g_hexen/a_teleportother.cpp
g_hexen/a_wraith.cpp
g_strife/a_acolyte.cpp
g_strife/a_alienspectres.cpp
g_strife/a_coin.cpp
g_strife/a_crusader.cpp
g_strife/a_entityboss.cpp
g_strife/a_inquisitor.cpp
g_strife/a_loremaster.cpp
g_strife/a_oracle.cpp
g_strife/a_programmer.cpp
g_strife/a_reaver.cpp
g_strife/a_rebels.cpp
g_strife/a_sentinel.cpp
g_strife/a_spectral.cpp
g_strife/a_stalker.cpp
g_strife/a_strifeitems.cpp
g_strife/a_strifeweapons.cpp
g_strife/a_templar.cpp
g_strife/a_thingstoblowup.cpp
g_shared/sbarinfo_commands.cpp
xlat/xlat_parser.y
xlat_parser.c
@ -1314,44 +1252,32 @@ set (PCH_SOURCES
w_wad.cpp
wi_stuff.cpp
zstrformat.cpp
g_doom/a_doommisc.cpp
g_heretic/a_hereticmisc.cpp
g_hexen/a_hexenmisc.cpp
g_raven/a_artitele.cpp
g_raven/a_minotaur.cpp
g_strife/a_strifestuff.cpp
g_inventory/a_ammo.cpp
g_inventory/a_armor.cpp
g_inventory/a_artifacts.cpp
g_inventory/a_health.cpp
g_inventory/a_keys.cpp
g_inventory/a_pickups.cpp
g_inventory/a_puzzleitems.cpp
g_inventory/a_weaponpiece.cpp
g_inventory/a_weapons.cpp
g_strife/strife_sbar.cpp
g_shared/a_action.cpp
g_shared/a_armor.cpp
g_shared/a_artifacts.cpp
g_shared/a_bridge.cpp
g_shared/a_camera.cpp
g_shared/a_debris.cpp
g_shared/a_decals.cpp
g_shared/a_fastprojectile.cpp
g_shared/a_flashfader.cpp
g_shared/a_fountain.cpp
g_shared/a_hatetarget.cpp
g_shared/a_keys.cpp
g_shared/a_lightning.cpp
g_shared/a_mapmarker.cpp
g_shared/a_morph.cpp
g_shared/a_movingcamera.cpp
g_shared/a_pickups.cpp
g_shared/a_puzzleitems.cpp
g_shared/a_quake.cpp
g_shared/a_randomspawner.cpp
g_shared/a_secrettrigger.cpp
g_shared/a_sectoraction.cpp
g_shared/a_setcolor.cpp
g_shared/a_skies.cpp
g_shared/a_soundenvironment.cpp
g_shared/a_soundsequence.cpp
g_shared/a_spark.cpp
g_shared/a_specialspot.cpp
g_shared/a_waterzone.cpp
g_shared/a_weaponpiece.cpp
g_shared/a_weapons.cpp
g_shared/hudmessages.cpp
g_shared/sbarinfo.cpp
g_shared/sbar_mugshot.cpp
@ -1470,12 +1396,9 @@ endif()
target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma )
include_directories( .
g_doom
g_heretic
g_hexen
g_raven
g_strife
g_shared
g_inventory
oplsynth
sound
textures
@ -1604,12 +1527,9 @@ source_group("External\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/m
source_group("External\\RapidJSON" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rapidjson/.+")
source_group("Externak\\SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+")
source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fragglescript/.+")
source_group("Games\\Doom Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_doom/.+")
source_group("Games\\Heretic Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_heretic/.+")
source_group("Games\\Hexen Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_hexen/.+")
source_group("Games\\Raven Shared" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_raven/.+")
source_group("Games\\Strife Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_strife/.+")
source_group("Intermission" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/intermission/.+")
source_group("Inventory" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_inventory/.+")
source_group("Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/menu/.+")
source_group("OpenGL Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/.+")
source_group("OpenGL Renderer\\Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/data/.+")

View file

@ -52,8 +52,8 @@
// a single section during the final link. (.rdata is the standard section
// for initialized read-only data.)
#pragma comment(linker, "/merge:.areg=.rdata /merge:.creg=.rdata /merge:.greg=.rdata")
#pragma comment(linker, "/merge:.yreg=.rdata")
#pragma comment(linker, "/merge:.areg=.rdata /merge:.creg=.rdata /merge:.freg=.rdata")
#pragma comment(linker, "/merge:.greg=.rdata /merge:.yreg=.rdata")
#pragma section(".areg$a",read)
__declspec(allocate(".areg$a")) void *const ARegHead = 0;
@ -61,6 +61,9 @@ __declspec(allocate(".areg$a")) void *const ARegHead = 0;
#pragma section(".creg$a",read)
__declspec(allocate(".creg$a")) void *const CRegHead = 0;
#pragma section(".freg$a",read)
__declspec(allocate(".freg$a")) void *const FRegHead = 0;
#pragma section(".greg$a",read)
__declspec(allocate(".greg$a")) void *const GRegHead = 0;
@ -97,6 +100,7 @@ __declspec(allocate(".yreg$a")) void *const YRegHead = 0;
void *const ARegHead __attribute__((section(SECTION_AREG))) = 0;
void *const CRegHead __attribute__((section(SECTION_CREG))) = 0;
void *const FRegHead __attribute__((section(SECTION_FREG))) = 0;
void *const GRegHead __attribute__((section(SECTION_GREG))) = 0;
void *const YRegHead __attribute__((section(SECTION_YREG))) = 0;

View file

@ -385,6 +385,8 @@ enum ActorFlag7
MF7_USEKILLSCRIPTS = 0x00800000, // [JM] Use "KILL" Script on death if not forced by GameInfo.
MF7_NOKILLSCRIPTS = 0x01000000, // [JM] No "KILL" Script on death whatsoever, even if forced by GameInfo.
MF7_SPRITEANGLE = 0x02000000, // [MC] Utilize the SpriteAngle property and lock the rotation to the degrees specified.
MF7_SMASHABLE = 0x04000000, // dies if hitting the floor.
MF7_NOSHIELDREFLECT = 0x08000000, // will not be reflected by shields.
};
// --- mobj.renderflags ---
@ -565,7 +567,6 @@ class DDropItem : public DObject
{
DECLARE_CLASS(DDropItem, DObject)
HAS_OBJECT_POINTERS
HAS_FIELDS
public:
DDropItem *Next;
FName Name;
@ -579,17 +580,18 @@ const double MinVel = EQUAL_EPSILON;
class AActor : public DThinker
{
DECLARE_CLASS_WITH_META (AActor, DThinker, PClassActor)
HAS_FIELDS
HAS_OBJECT_POINTERS
public:
AActor () throw();
AActor (const AActor &other) throw();
AActor &operator= (const AActor &other);
void Destroy ();
~AActor ();
void Serialize(FSerializer &arc);
void PostSerialize();
virtual void Destroy() override;
virtual void Serialize(FSerializer &arc) override;
virtual void PostSerialize() override;
virtual void PostBeginPlay() override; // Called immediately before the actor's first tick
virtual void Tick() override;
static AActor *StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false);
@ -613,43 +615,51 @@ public:
bool CheckNoDelay();
virtual void BeginPlay(); // Called immediately after the actor is created
virtual void PostBeginPlay(); // Called immediately before the actor's first tick
void CallBeginPlay();
void LevelSpawned(); // Called after BeginPlay if this actor was spawned by the world
virtual void HandleSpawnFlags(); // Translates SpawnFlags into in-game flags.
virtual void MarkPrecacheSounds() const; // Marks sounds used by this actor for precaching.
virtual void Activate (AActor *activator);
virtual void Deactivate (AActor *activator);
void CallActivate(AActor *activator);
virtual void Tick ();
virtual void Deactivate(AActor *activator);
void CallDeactivate(AActor *activator);
// Called when actor dies
virtual void Die (AActor *source, AActor *inflictor, int dmgflags = 0);
void CallDie(AActor *source, AActor *inflictor, int dmgflags = 0);
// Perform some special damage action. Returns the amount of damage to do.
// Returning -1 signals the damage routine to exit immediately
virtual int DoSpecialDamage (AActor *target, int damage, FName damagetype);
int CallDoSpecialDamage(AActor *target, int damage, FName damagetype);
// Like DoSpecialDamage, but called on the actor receiving the damage.
virtual int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype);
int CallTakeSpecialDamage(AActor *inflictor, AActor *source, int damage, FName damagetype);
// Actor had MF_SKULLFLY set and rammed into something
// Returns false to stop moving and true to keep moving
virtual bool Slam(AActor *victim);
bool CallSlam(AActor *victim);
// Something just touched this actor.
virtual void Touch(AActor *toucher);
void CallTouch(AActor *toucher);
// Centaurs and ettins squeal when electrocuted, poisoned, or "holy"-ed
// Made a metadata property so no longer virtual
void Howl ();
// Actor just hit the floor
virtual void HitFloor ();
// plays bouncing sound
void PlayBounceSound(bool onfloor);
// Called when an actor with MF_MISSILE and MF2_FLOORBOUNCE hits the floor
virtual bool FloorBounceMissile (secplane_t &plane);
// Called when an actor is to be reflected by a disc of repulsion.
// Returns true to continue normal blast processing.
virtual bool SpecialBlastHandling (AActor *source, double strength);
bool FloorBounceMissile (secplane_t &plane);
// Called by RoughBlockCheck
bool IsOkayToAttack (AActor *target);
@ -657,27 +667,28 @@ public:
// Plays the actor's ActiveSound if its voice isn't already making noise.
void PlayActiveSound ();
// Actor had MF_SKULLFLY set and rammed into something
// Returns false to stop moving and true to keep moving
virtual bool Slam (AActor *victim);
void RestoreSpecialPosition();
// Called by PIT_CheckThing() and needed for some Hexen things.
// Returns -1 for normal behavior, 0 to return false, and 1 to return true.
// I'm not sure I like it this way, but it will do for now.
virtual int SpecialMissileHit (AActor *victim);
// (virtual on the script side only)
int SpecialMissileHit (AActor *victim);
// Returns true if it's okay to switch target to "other" after being attacked by it.
virtual bool OkayToSwitchTarget (AActor *other);
bool OkayToSwitchTarget (AActor *other);
// Something just touched this actor.
virtual void Touch (AActor *toucher);
// Note: Although some of the inventory functions are virtual, this
// is not exposed to scripts, as the only class overriding them is
// APlayerPawn for some specific handling for players. None of this
// should ever be overridden by custom classes.
// Adds the item to this actor's inventory and sets its Owner.
virtual void AddInventory (AInventory *item);
// Give an item to the actor and pick it up.
// Returns true if the item pickup succeeded.
virtual bool GiveInventory (PClassInventory *type, int amount, bool givecheat = false);
bool GiveInventory (PClassInventory *type, int amount, bool givecheat = false);
// Removes the item from the inventory list.
virtual void RemoveInventory (AInventory *item);
@ -691,7 +702,7 @@ public:
virtual bool UseInventory (AInventory *item);
// Tosses an item out of the inventory.
virtual AInventory *DropInventory (AInventory *item);
AInventory *DropInventory (AInventory *item);
// Removes all items from the inventory.
void ClearInventory();
@ -755,10 +766,10 @@ public:
DVector3 GetPortalTransition(double byoffset, sector_t **pSec = NULL);
// What species am I?
virtual FName GetSpecies();
FName GetSpecies();
// set translation
void SetTranslation(const char *trname);
void SetTranslation(FName trname);
double GetBobOffset(double ticfrac = 0) const
{
@ -1416,6 +1427,7 @@ public:
}
int ApplyDamageFactor(FName damagetype, int damage) const;
int GetModifiedDamage(FName damagetype, int damage, bool passive);
// begin of GZDoom specific additions
@ -1450,6 +1462,10 @@ public:
return base;
}
void Reinit()
{
base = nullptr;
}
private:
AActor *base;
int id;

View file

@ -2963,8 +2963,8 @@ void AM_drawAuthorMarkers ()
// [RH] Draw any actors derived from AMapMarker on the automap.
// If args[0] is 0, then the actor's sprite is drawn at its own location.
// Otherwise, its sprite is drawn at the location of any actors whose TIDs match args[0].
TThinkerIterator<AMapMarker> it (STAT_MAPMARKER);
AMapMarker *mark;
TThinkerIterator<AActor> it ("MapMarker", STAT_MAPMARKER);
AActor *mark;
while ((mark = it.Next()) != NULL)
{

View file

@ -49,6 +49,10 @@ extern REGINFO ARegTail;
extern REGINFO CRegHead;
extern REGINFO CRegTail;
// List of class fields
extern REGINFO FRegHead;
extern REGINFO FRegTail;
// List of properties
extern REGINFO GRegHead;
extern REGINFO GRegTail;

View file

@ -15,7 +15,7 @@
#include "serializer.h"
#include "d_player.h"
IMPLEMENT_CLASS(DBot, false, true, false, false)
IMPLEMENT_CLASS(DBot, false, true)
IMPLEMENT_POINTERS_START(DBot)
IMPLEMENT_POINTER(dest)

View file

@ -21,6 +21,8 @@
#include "d_event.h"
#include "d_player.h"
#include "vectors.h"
#include "a_ammo.h"
#include "a_health.h"
static FRandom pr_botmove ("BotMove");

View file

@ -65,6 +65,7 @@
#include "g_level.h"
#include "d_event.h"
#include "d_player.h"
#include "gstrings.h"
#include "c_consolebuffer.h"
#include "gi.h"
@ -1729,6 +1730,20 @@ void C_MidPrintBold (FFont *font, const char *msg)
}
}
DEFINE_ACTION_FUNCTION(DObject, C_MidPrint)
{
PARAM_PROLOGUE;
PARAM_STRING(font);
PARAM_STRING(text);
PARAM_BOOL_DEF(bold);
FFont *fnt = FFont::FindFont(font);
const char *txt = text[0] == '$'? GStrings(&text[1]) : text.GetChars();
if (!bold) C_MidPrint(fnt, txt);
else C_MidPrintBold(fnt, txt);
return 0;
}
/****** Tab completion code ******/
struct TabData

View file

@ -1093,7 +1093,7 @@ BitVal (bitval)
ECVarType FFlagCVar::GetRealType () const
{
return CVAR_Dummy;
return CVAR_DummyBool;
}
UCVarValue FFlagCVar::GetGenericRep (ECVarType type) const
@ -1197,7 +1197,7 @@ BitVal (bitval)
ECVarType FMaskCVar::GetRealType () const
{
return CVAR_Dummy;
return CVAR_DummyInt;
}
UCVarValue FMaskCVar::GetGenericRep (ECVarType type) const

View file

@ -81,13 +81,17 @@ enum ECVarType
CVAR_Float,
CVAR_String,
CVAR_Color, // stored as CVAR_Int
CVAR_Dummy, // just redirects to another cvar
CVAR_DummyBool, // just redirects to another cvar
CVAR_DummyInt, // just redirects to another cvar
CVAR_Dummy, // Unknown
CVAR_GUID
};
class FConfigFile;
class AActor;
class FxCVar;
class FBaseCVar
{
public:
@ -211,6 +215,7 @@ void C_DeinitConsole();
class FBoolCVar : public FBaseCVar
{
friend class FxCVar;
public:
FBoolCVar (const char *name, bool def, uint32 flags, void (*callback)(FBoolCVar &)=NULL);
@ -236,6 +241,7 @@ protected:
class FIntCVar : public FBaseCVar
{
friend class FxCVar;
public:
FIntCVar (const char *name, int def, uint32 flags, void (*callback)(FIntCVar &)=NULL);
@ -263,6 +269,7 @@ protected:
class FFloatCVar : public FBaseCVar
{
friend class FxCVar;
public:
FFloatCVar (const char *name, float def, uint32 flags, void (*callback)(FFloatCVar &)=NULL);
@ -289,6 +296,7 @@ protected:
class FStringCVar : public FBaseCVar
{
friend class FxCVar;
public:
FStringCVar (const char *name, const char *def, uint32 flags, void (*callback)(FStringCVar &)=NULL);
~FStringCVar ();
@ -315,6 +323,7 @@ protected:
class FColorCVar : public FIntCVar
{
friend class FxCVar;
public:
FColorCVar (const char *name, int def, uint32 flags, void (*callback)(FColorCVar &)=NULL);
@ -339,6 +348,7 @@ protected:
class FFlagCVar : public FBaseCVar
{
friend class FxCVar;
public:
FFlagCVar (const char *name, FIntCVar &realvar, uint32 bitval);
@ -367,6 +377,7 @@ protected:
class FMaskCVar : public FBaseCVar
{
friend class FxCVar;
public:
FMaskCVar (const char *name, FIntCVar &realvar, uint32 bitval);

View file

@ -187,7 +187,7 @@ static const char *KeyConfCommands[] =
// CODE --------------------------------------------------------------------
IMPLEMENT_CLASS(DWaitingCommand, false, false, false, false)
IMPLEMENT_CLASS(DWaitingCommand, false, false)
void DWaitingCommand::Serialize(FSerializer &arc)
{
@ -225,7 +225,7 @@ void DWaitingCommand::Tick ()
}
}
IMPLEMENT_CLASS(DStoredCommand, false, false, false, false)
IMPLEMENT_CLASS(DStoredCommand, false, false)
DStoredCommand::DStoredCommand ()
{

View file

@ -31,6 +31,7 @@
#include "templates.h"
#include "d_net.h"
#include "d_event.h"
#include "a_armor.h"
#define QUEUESIZE 128
#define MESSAGESIZE 128

View file

@ -70,11 +70,13 @@
#include "doomerrors.h"
#include "p_effect.h"
#include "serializer.h"
#include "vm.h"
#include "thingdef.h"
#include "info.h"
#include "v_text.h"
#include "vmbuilder.h"
#include "a_armor.h"
#include "a_ammo.h"
#include "a_health.h"
// [SO] Just the way Randy said to do it :)
// [RH] Made this CVAR_SERVERINFO
@ -167,36 +169,25 @@ static TArray<CodePointerAlias> MBFCodePointers;
struct AmmoPerAttack
{
VMNativeFunction **func;
ENamedName func;
int ammocount;
VMFunction *ptr;
};
DECLARE_ACTION(A_Punch)
DECLARE_ACTION(A_FirePistol)
DECLARE_ACTION(A_FireShotgun)
DECLARE_ACTION(A_FireShotgun2)
DECLARE_ACTION(A_FireCGun)
DECLARE_ACTION(A_FireMissile)
DECLARE_ACTION(A_Saw)
DECLARE_ACTION(A_FirePlasma)
DECLARE_ACTION(A_FireBFG)
DECLARE_ACTION(A_FireOldBFG)
DECLARE_ACTION(A_FireRailgun)
// Default ammo use of the various weapon attacks
static AmmoPerAttack AmmoPerAttacks[] = {
{ &A_Punch_VMPtr, 0},
{ &A_FirePistol_VMPtr, 1},
{ &A_FireShotgun_VMPtr, 1},
{ &A_FireShotgun2_VMPtr, 2},
{ &A_FireCGun_VMPtr, 1},
{ &A_FireMissile_VMPtr, 1},
{ &A_Saw_VMPtr, 0},
{ &A_FirePlasma_VMPtr, 1},
{ &A_FireBFG_VMPtr, -1}, // uses deh.BFGCells
{ &A_FireOldBFG_VMPtr, 1},
{ &A_FireRailgun_VMPtr, 1},
{ NULL, 0}
{ NAME_A_Punch, 0},
{ NAME_A_FirePistol, 1},
{ NAME_A_FireShotgun, 1},
{ NAME_A_FireShotgun2, 2},
{ NAME_A_FireCGun, 1},
{ NAME_A_FireMissile, 1},
{ NAME_A_Saw, 0},
{ NAME_A_FirePlasma, 1},
{ NAME_A_FireBFG, -1}, // uses deh.BFGCells
{ NAME_A_FireOldBFG, 1},
{ NAME_A_FireRailgun, 1},
{ NAME_None, 0}
};
@ -224,6 +215,12 @@ DehInfo deh =
40, // BFG cells per shot
};
DEFINE_FIELD_X(DehInfo, DehInfo, MaxSoulsphere)
DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionStyle)
DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionAlpha)
DEFINE_FIELD_X(DehInfo, DehInfo, NoAutofreeze)
DEFINE_FIELD_X(DehInfo, DehInfo, BFGCells)
// Doom identified pickup items by their sprites. ZDoom prefers to use their
// class type to identify them instead. To support the traditional Doom
// behavior, for every thing touched by dehacked that has the MF_PICKUP flag,
@ -231,7 +228,7 @@ DehInfo deh =
// from the original actor's defaults. The original actor is then changed to
// spawn the new class.
IMPLEMENT_CLASS(ADehackedPickup, false, true, false, false)
IMPLEMENT_CLASS(ADehackedPickup, false, true)
IMPLEMENT_POINTERS_START(ADehackedPickup)
IMPLEMENT_POINTER(RealPickup)
@ -1476,28 +1473,21 @@ static int PatchFrame (int frameNum)
if (info != &dummy)
{
info->DefineFlags |= SDF_DEHACKED; // Signals the state has been modified by dehacked
info->StateFlags |= STF_DEHACKED; // Signals the state has been modified by dehacked
if ((unsigned)(frame & 0x7fff) > 63)
{
Printf ("Frame %d: Subnumber must be in range [0,63]\n", frameNum);
Printf("Frame %d: Subnumber must be in range [0,63]\n", frameNum);
}
info->Tics = tics;
info->Misc1 = misc1;
info->Frame = frame & 0x3f;
info->Fullbright = frame & 0x8000 ? true : false;
if (frame & 0x8000) info->StateFlags |= STF_FULLBRIGHT;
else info->StateFlags &= ~STF_FULLBRIGHT;
}
return result;
}
// there is exactly one place where this is needed and we do not want to expose the state internals to ZSCRIPT.
DEFINE_ACTION_FUNCTION(AActor, isDEHState)
{
PARAM_PROLOGUE;
PARAM_POINTER(state, FState);
ACTION_RETURN_BOOL(state != nullptr && (state->DefineFlags & SDF_DEHACKED));
}
static int PatchSprite (int sprNum)
{
int result;
@ -3094,9 +3084,14 @@ void FinishDehPatch ()
break; // State has already been checked so we reached a loop
}
StateVisited[state] = true;
for(unsigned j = 0; AmmoPerAttacks[j].func != NULL; j++)
for(unsigned j = 0; AmmoPerAttacks[j].func != NAME_None; j++)
{
if (state->ActionFunc == *AmmoPerAttacks[j].func)
if (AmmoPerAttacks[j].ptr == nullptr)
{
auto p = dyn_cast<PFunction>(RUNTIME_CLASS(AStateProvider)->Symbols.FindSymbol(AmmoPerAttacks[j].func, true));
if (p != nullptr) AmmoPerAttacks[j].ptr = p->Variants[0].Implementation;
}
if (state->ActionFunc == AmmoPerAttacks[j].ptr)
{
found = true;
int use = AmmoPerAttacks[j].ammocount;
@ -3150,7 +3145,7 @@ bool ADehackedPickup::TryPickup (AActor *&toucher)
return false;
}
const char *ADehackedPickup::PickupMessage ()
FString ADehackedPickup::PickupMessage ()
{
if (RealPickup != nullptr)
return RealPickup->PickupMessage ();
@ -3160,7 +3155,7 @@ const char *ADehackedPickup::PickupMessage ()
bool ADehackedPickup::ShouldStay ()
{
if (RealPickup != nullptr)
return RealPickup->ShouldStay ();
return RealPickup->CallShouldStay ();
else return true;
}

View file

@ -41,8 +41,8 @@ class ADehackedPickup : public AInventory
DECLARE_CLASS (ADehackedPickup, AInventory)
HAS_OBJECT_POINTERS
public:
void Destroy ();
const char *PickupMessage ();
void Destroy() override;
FString PickupMessage ();
bool ShouldRespawn ();
bool ShouldStay ();
bool TryPickup (AActor *&toucher);

View file

@ -109,7 +109,6 @@
#include "p_local.h"
#include "autosegs.h"
#include "fragglescript/t_fs.h"
#include "vm.h"
EXTERN_CVAR(Bool, hud_althud)
void DrawHUD();

View file

@ -61,6 +61,7 @@
#include "p_spec.h"
#include "hardware.h"
#include "r_utility.h"
#include "a_keys.h"
#include "intermission/intermission.h"
EXTERN_CVAR (Int, disableautosave)

View file

@ -30,6 +30,7 @@
#include "doomstat.h"
#include "a_artifacts.h"
#include "a_weapons.h"
// The player data structure depends on a number
// of other structs: items (internal inventory),
@ -105,29 +106,36 @@ public:
virtual void Serialize(FSerializer &arc);
virtual void PostBeginPlay();
virtual void Tick();
virtual void AddInventory (AInventory *item);
virtual void RemoveInventory (AInventory *item);
virtual bool UseInventory (AInventory *item);
virtual void MarkPrecacheSounds () const;
virtual void PostBeginPlay() override;
virtual void Tick() override;
virtual void AddInventory (AInventory *item) override;
virtual void RemoveInventory (AInventory *item) override;
virtual bool UseInventory (AInventory *item) override;
virtual void MarkPrecacheSounds () const override;
virtual void BeginPlay () override;
virtual void Die (AActor *source, AActor *inflictor, int dmgflags) override;
virtual bool UpdateWaterLevel (bool splash) override;
virtual void PlayIdle ();
virtual void PlayRunning ();
virtual void ThrowPoisonBag ();
virtual void TweakSpeeds (double &forwardmove, double &sidemove);
virtual void MorphPlayerThink ();
virtual void ActivateMorphWeapon ();
bool ResetAirSupply (bool playgasp = true);
int GetMaxHealth() const;
void TweakSpeeds (double &forwardmove, double &sidemove);
void MorphPlayerThink ();
void ActivateMorphWeapon ();
AWeapon *PickNewWeapon (PClassAmmo *ammotype);
AWeapon *BestWeapon (PClassAmmo *ammotype);
void CheckWeaponSwitch(PClassAmmo *ammotype);
virtual void GiveDeathmatchInventory ();
virtual void FilterCoopRespawnInventory (APlayerPawn *oldplayer);
void GiveDeathmatchInventory ();
void FilterCoopRespawnInventory (APlayerPawn *oldplayer);
void SetupWeaponSlots ();
void GiveDefaultInventory ();
// These are virtual on the script side only.
void PlayIdle();
void PlayRunning();
void PlayAttacking ();
void PlayAttacking2 ();
const char *GetSoundClass () const;
enum EInvulState
@ -138,8 +146,6 @@ public:
INVUL_GetAlpha
};
void BeginPlay ();
void Die (AActor *source, AActor *inflictor, int dmgflags);
int crouchsprite;
int MaxHealth;
@ -171,10 +177,6 @@ public:
// [SP] ViewBob Multiplier
double ViewBob;
bool UpdateWaterLevel (bool splash);
bool ResetAirSupply (bool playgasp = true);
int GetMaxHealth() const;
};
class APlayerChunk : public APlayerPawn
@ -253,11 +255,10 @@ enum
WF_USER4OK = 1 << 11,
};
#define WPIECE1 1
#define WPIECE2 2
#define WPIECE3 4
#define WP_NOCHANGE ((AWeapon*)~0)
// The VM cannot deal with this as an invalid pointer because it performs a read barrier on every object pointer read.
// This doesn't have to point to a valid weapon, though, because WP_NOCHANGE is never dereferenced, but it must point to a valid object
// and the class descriptor just works fine for that.
#define WP_NOCHANGE ((AWeapon*)RUNTIME_CLASS_CASTLESS(AWeapon))
#define MAXPLAYERNAME 15

View file

@ -119,7 +119,7 @@ protected:
DDecalThinker () : DThinker (STAT_DECALTHINKER) {}
};
IMPLEMENT_CLASS(DDecalThinker, false, true, false, false)
IMPLEMENT_CLASS(DDecalThinker, false, true)
IMPLEMENT_POINTERS_START(DDecalThinker)
IMPLEMENT_POINTER(TheDecal)
@ -1153,7 +1153,7 @@ FDecalAnimator::~FDecalAnimator ()
{
}
IMPLEMENT_CLASS(DDecalFader, false, false, false, false)
IMPLEMENT_CLASS(DDecalFader, false, false)
void DDecalFader::Serialize(FSerializer &arc)
{
@ -1202,7 +1202,7 @@ DThinker *FDecalFaderAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const
return fader;
}
IMPLEMENT_CLASS(DDecalStretcher, false, false, false, false)
IMPLEMENT_CLASS(DDecalStretcher, false, false)
void DDecalStretcher::Serialize(FSerializer &arc)
{
@ -1290,7 +1290,7 @@ void DDecalStretcher::Tick ()
}
}
IMPLEMENT_CLASS(DDecalSlider, false, false, false, false)
IMPLEMENT_CLASS(DDecalSlider, false, false)
void DDecalSlider::Serialize(FSerializer &arc)
{
@ -1370,7 +1370,7 @@ FDecalAnimator *FDecalLib::FindAnimator (const char *name)
return NULL;
}
IMPLEMENT_CLASS(DDecalColorer, false, false, false, false)
IMPLEMENT_CLASS(DDecalColorer, false, false)
void DDecalColorer::Serialize(FSerializer &arc)
{

View file

@ -61,15 +61,18 @@ ClassReg DObject::RegistrationInfo =
nullptr, // MyClass
"DObject", // Name
nullptr, // ParentType
&DVMObject<DObject>::RegistrationInfo, // VMExport
nullptr,
nullptr, // Pointers
&DObject::InPlaceConstructor, // ConstructNative
&DObject::InitNativeFields, // InitNatives
nullptr,
sizeof(DObject), // SizeOf
CLASSREG_PClass, // MetaClassNum
};
_DECLARE_TI(DObject)
// This bit is needed in the playsim - but give it a less crappy name.
DEFINE_FIELD_BIT(DObject,ObjectFlags, bDestroyed, OF_EuthanizeMe)
//==========================================================================
//
//
@ -347,18 +350,6 @@ DObject::~DObject ()
//
//==========================================================================
void DObject::InitNativeFields()
{
auto meta = RUNTIME_CLASS(DObject);
meta->AddNativeField("bDestroyed", TypeSInt32, myoffsetof(DObject, ObjectFlags), VARF_ReadOnly, OF_EuthanizeMe);
}
//==========================================================================
//
//
//
//==========================================================================
void DObject::Destroy ()
{
ObjectFlags = (ObjectFlags & ~OF_Fixed) | OF_EuthanizeMe;
@ -367,17 +358,10 @@ void DObject::Destroy ()
DEFINE_ACTION_FUNCTION(DObject, Destroy)
{
PARAM_SELF_PROLOGUE(DObject);
self->VMSuperCall();
self->Destroy();
return 0;
}
DEFINE_ACTION_FUNCTION(DObject, GetClass)
{
PARAM_SELF_PROLOGUE(DObject);
ACTION_RETURN_OBJECT(self->GetClass());
}
//==========================================================================
//
//

View file

@ -110,7 +110,7 @@ struct ClassReg
PClass *MyClass;
const char *Name;
ClassReg *ParentType;
ClassReg *VMExport;
ClassReg *_VMExport;
const size_t *Pointers;
void (*ConstructNative)(void *);
void(*InitNatives)();
@ -150,9 +150,6 @@ protected: \
#define HAS_OBJECT_POINTERS \
static const size_t PointerOffsets[];
#define HAS_FIELDS \
static void InitNativeFields();
#if defined(_MSC_VER)
# pragma section(".creg$u",read)
# define _DECLARE_TI(cls) __declspec(allocate(".creg$u")) ClassReg * const cls::RegistrationInfoPtr = &cls::RegistrationInfo;
@ -160,23 +157,23 @@ protected: \
# define _DECLARE_TI(cls) ClassReg * const cls::RegistrationInfoPtr __attribute__((section(SECTION_CREG))) = &cls::RegistrationInfo;
#endif
#define _IMP_PCLASS(cls, ptrs, create, initn, vmexport) \
#define _IMP_PCLASS(cls, ptrs, create) \
ClassReg cls::RegistrationInfo = {\
nullptr, \
#cls, \
&cls::Super::RegistrationInfo, \
vmexport, \
nullptr, \
ptrs, \
create, \
initn, \
nullptr, \
sizeof(cls), \
cls::MetaClassNum }; \
_DECLARE_TI(cls) \
PClass *cls::StaticType() const { return RegistrationInfo.MyClass; }
#define IMPLEMENT_CLASS(cls, isabstract, ptrs, fields, vmexport) \
#define IMPLEMENT_CLASS(cls, isabstract, ptrs) \
_X_CONSTRUCTOR_##isabstract(cls) \
_IMP_PCLASS(cls, _X_POINTERS_##ptrs(cls), _X_ABSTRACT_##isabstract(cls), _X_FIELDS_##fields(cls), _X_VMEXPORT_##vmexport(cls))
_IMP_PCLASS(cls, _X_POINTERS_##ptrs(cls), _X_ABSTRACT_##isabstract(cls))
// Taking the address of a field in an object at address 1 instead of
// address 0 keeps GCC from complaining about possible misuse of offsetof.
@ -187,13 +184,13 @@ protected: \
// Possible arguments for the IMPLEMENT_CLASS macro
#define _X_POINTERS_true(cls) cls::PointerOffsets
#define _X_POINTERS_false(cls) nullptr
#define _X_FIELDS_true(cls) cls::InitNativeFields
#define _X_FIELDS_true(cls) nullptr
#define _X_FIELDS_false(cls) nullptr
#define _X_CONSTRUCTOR_true(cls)
#define _X_CONSTRUCTOR_false(cls) void cls::InPlaceConstructor(void *mem) { new((EInPlace *)mem) cls; }
#define _X_ABSTRACT_true(cls) nullptr
#define _X_ABSTRACT_false(cls) cls::InPlaceConstructor
#define _X_VMEXPORT_true(cls) &DVMObject<cls>::RegistrationInfo
#define _X_VMEXPORT_true(cls) nullptr
#define _X_VMEXPORT_false(cls) nullptr
enum EObjectFlags
@ -445,7 +442,6 @@ public:
virtual PClass *StaticType() const { return RegistrationInfo.MyClass; }
static ClassReg RegistrationInfo, * const RegistrationInfoPtr;
static void InPlaceConstructor (void *mem);
static void InitNativeFields();
typedef PClass MetaClass;
private:
typedef DObject ThisClass;
@ -471,11 +467,6 @@ public:
void SerializeUserVars(FSerializer &arc);
virtual void Serialize(FSerializer &arc);
void VMSuperCall()
{
ObjectFlags |= OF_SuperCall;
}
void ClearClass()
{
Class = NULL;
@ -485,7 +476,7 @@ public:
// that don't call their base class.
void CheckIfSerialized () const;
virtual void Destroy ();
virtual void Destroy();
// If you need to replace one object with another and want to
// change any pointers from the old object to the new object,

View file

@ -77,7 +77,6 @@
#include "r_utility.h"
#include "menu/menu.h"
#include "intermission/intermission.h"
#include "vm.h"
// MACROS ------------------------------------------------------------------
@ -126,7 +125,7 @@ public:
int SideNum;
};
IMPLEMENT_CLASS(DSectorMarker, false, false, false, false)
IMPLEMENT_CLASS(DSectorMarker, false, false)
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------

View file

@ -45,10 +45,13 @@
#include "autosegs.h"
#include "v_text.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "a_weaponpiece.h"
#include "d_player.h"
#include "doomerrors.h"
#include "fragglescript/t_fs.h"
#include "a_keys.h"
#include "a_health.h"
// MACROS ------------------------------------------------------------------
@ -81,10 +84,14 @@ PString *TypeString;
PName *TypeName;
PSound *TypeSound;
PColor *TypeColor;
PTextureID *TypeTextureID;
PSpriteID *TypeSpriteID;
PStatePointer *TypeState;
PStateLabel *TypeStateLabel;
PStruct *TypeVector2;
PStruct *TypeVector3;
PStruct *TypeColorStruct;
PStruct *TypeStringStruct;
PPointer *TypeNullPtr;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -94,8 +101,8 @@ static const size_t TheEnd = ~(size_t)0;
// CODE --------------------------------------------------------------------
IMPLEMENT_CLASS(PErrorType, false, false, false, false)
IMPLEMENT_CLASS(PVoidType, false, false, false, false)
IMPLEMENT_CLASS(PErrorType, false, false)
IMPLEMENT_CLASS(PVoidType, false, false)
void DumpTypeTable()
{
@ -142,7 +149,7 @@ void DumpTypeTable()
/* PClassType *************************************************************/
IMPLEMENT_CLASS(PClassType, false, false, false, false)
IMPLEMENT_CLASS(PClassType, false, false)
//==========================================================================
//
@ -170,7 +177,7 @@ void PClassType::DeriveData(PClass *newclass)
/* PClassClass ************************************************************/
IMPLEMENT_CLASS(PClassClass, false, false, false, false)
IMPLEMENT_CLASS(PClassClass, false, false)
//==========================================================================
//
@ -188,7 +195,7 @@ PClassClass::PClassClass()
/* PType ******************************************************************/
IMPLEMENT_CLASS(PType, true, true, false, false)
IMPLEMENT_CLASS(PType, true, true)
IMPLEMENT_POINTERS_START(PType)
IMPLEMENT_POINTER(HashNext)
@ -419,6 +426,16 @@ void PType::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset>
{
}
//==========================================================================
//
// PType :: SetDefaultValue
//
//==========================================================================
void PType::SetPointer(void *base, unsigned offset, TArray<size_t> *stroffs) const
{
}
//==========================================================================
//
// PType :: InitializeValue
@ -535,15 +552,17 @@ void PType::StaticInit()
RUNTIME_CLASS(PString)->TypeTableType = RUNTIME_CLASS(PString);
RUNTIME_CLASS(PName)->TypeTableType = RUNTIME_CLASS(PName);
RUNTIME_CLASS(PSound)->TypeTableType = RUNTIME_CLASS(PSound);
RUNTIME_CLASS(PSpriteID)->TypeTableType = RUNTIME_CLASS(PSpriteID);
RUNTIME_CLASS(PTextureID)->TypeTableType = RUNTIME_CLASS(PTextureID);
RUNTIME_CLASS(PColor)->TypeTableType = RUNTIME_CLASS(PColor);
RUNTIME_CLASS(PPointer)->TypeTableType = RUNTIME_CLASS(PPointer);
RUNTIME_CLASS(PClassPointer)->TypeTableType = RUNTIME_CLASS(PClassPointer);
RUNTIME_CLASS(PEnum)->TypeTableType = RUNTIME_CLASS(PEnum);
RUNTIME_CLASS(PArray)->TypeTableType = RUNTIME_CLASS(PArray);
RUNTIME_CLASS(PDynArray)->TypeTableType = RUNTIME_CLASS(PDynArray);
RUNTIME_CLASS(PVector)->TypeTableType = RUNTIME_CLASS(PVector);
RUNTIME_CLASS(PMap)->TypeTableType = RUNTIME_CLASS(PMap);
RUNTIME_CLASS(PStruct)->TypeTableType = RUNTIME_CLASS(PStruct);
RUNTIME_CLASS(PNativeStruct)->TypeTableType = RUNTIME_CLASS(PNativeStruct);
RUNTIME_CLASS(PPrototype)->TypeTableType = RUNTIME_CLASS(PPrototype);
RUNTIME_CLASS(PClass)->TypeTableType = RUNTIME_CLASS(PClass);
RUNTIME_CLASS(PStatePointer)->TypeTableType = RUNTIME_CLASS(PStatePointer);
@ -568,6 +587,22 @@ void PType::StaticInit()
TypeTable.AddType(TypeState = new PStatePointer);
TypeTable.AddType(TypeStateLabel = new PStateLabel);
TypeTable.AddType(TypeNullPtr = new PPointer);
TypeTable.AddType(TypeSpriteID = new PSpriteID);
TypeTable.AddType(TypeTextureID = new PTextureID);
TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
TypeStringStruct = NewNativeStruct(NAME_String, nullptr);
#ifdef __BIG_ENDIAN__
TypeColorStruct->AddField(NAME_a, TypeUInt8);
TypeColorStruct->AddField(NAME_r, TypeUInt8);
TypeColorStruct->AddField(NAME_g, TypeUInt8);
TypeColorStruct->AddField(NAME_b, TypeUInt8);
#else
TypeColorStruct->AddField(NAME_b, TypeUInt8);
TypeColorStruct->AddField(NAME_g, TypeUInt8);
TypeColorStruct->AddField(NAME_r, TypeUInt8);
TypeColorStruct->AddField(NAME_a, TypeUInt8);
#endif
TypeVector2 = new PStruct(NAME_Vector2, nullptr);
TypeVector2->AddField(NAME_X, TypeFloat64);
@ -583,8 +618,8 @@ void PType::StaticInit()
TypeVector3->AddField(NAME_X, TypeFloat64);
TypeVector3->AddField(NAME_Y, TypeFloat64);
TypeVector3->AddField(NAME_Z, TypeFloat64);
// allow accessing xy as a vector2. This is marked native because it's not supposed to be serialized.
TypeVector3->Symbols.AddSymbol(new PField(NAME_XY, TypeVector2, VARF_Native, 0));
// allow accessing xy as a vector2. This is not supposed to be serialized so it's marked transient
TypeVector3->Symbols.AddSymbol(new PField(NAME_XY, TypeVector2, VARF_Transient, 0));
TypeTable.AddType(TypeVector3);
TypeVector3->loadOp = OP_LV3;
TypeVector3->storeOp = OP_SV3;
@ -617,7 +652,7 @@ void PType::StaticInit()
/* PBasicType *************************************************************/
IMPLEMENT_CLASS(PBasicType, true, false, false, false)
IMPLEMENT_CLASS(PBasicType, true, false)
//==========================================================================
//
@ -643,11 +678,11 @@ PBasicType::PBasicType(unsigned int size, unsigned int align)
/* PCompoundType **********************************************************/
IMPLEMENT_CLASS(PCompoundType, true, false, false, false)
IMPLEMENT_CLASS(PCompoundType, true, false)
/* PNamedType *************************************************************/
IMPLEMENT_CLASS(PNamedType, true, true, false, false)
IMPLEMENT_CLASS(PNamedType, true, true)
IMPLEMENT_POINTERS_START(PNamedType)
IMPLEMENT_POINTER(Outer)
@ -695,7 +730,7 @@ FString PNamedType::QualifiedName() const
/* PInt *******************************************************************/
IMPLEMENT_CLASS(PInt, false, false, false, false)
IMPLEMENT_CLASS(PInt, false, false)
//==========================================================================
//
@ -934,7 +969,7 @@ double PInt::GetValueFloat(void *addr) const
/* PBool ******************************************************************/
IMPLEMENT_CLASS(PBool, false, false, false, false)
IMPLEMENT_CLASS(PBool, false, false)
//==========================================================================
//
@ -955,7 +990,7 @@ PBool::PBool()
/* PFloat *****************************************************************/
IMPLEMENT_CLASS(PFloat, false, false, false, false)
IMPLEMENT_CLASS(PFloat, false, false)
//==========================================================================
//
@ -1205,7 +1240,7 @@ void PFloat::SetOps()
/* PString ****************************************************************/
IMPLEMENT_CLASS(PString, false, false, false, false)
IMPLEMENT_CLASS(PString, false, false)
//==========================================================================
//
@ -1214,7 +1249,7 @@ IMPLEMENT_CLASS(PString, false, false, false, false)
//==========================================================================
PString::PString()
: PBasicType(sizeof(FString), __alignof(FString))
: PBasicType(sizeof(FString), alignof(FString))
{
mDescriptiveName = "String";
storeOp = OP_SS;
@ -1302,7 +1337,7 @@ void PString::DestroyValue(void *addr) const
/* PName ******************************************************************/
IMPLEMENT_CLASS(PName, false, false, false, false)
IMPLEMENT_CLASS(PName, false, false)
//==========================================================================
//
@ -1314,7 +1349,7 @@ PName::PName()
: PInt(sizeof(FName), true, false)
{
mDescriptiveName = "Name";
assert(sizeof(FName) == __alignof(FName));
assert(sizeof(FName) == alignof(FName));
}
//==========================================================================
@ -1350,9 +1385,94 @@ bool PName::ReadValue(FSerializer &ar, const char *key, void *addr) const
}
}
/* PSpriteID ******************************************************************/
IMPLEMENT_CLASS(PSpriteID, false, false)
//==========================================================================
//
// PName Default Constructor
//
//==========================================================================
PSpriteID::PSpriteID()
: PInt(sizeof(int), true, true)
{
mDescriptiveName = "SpriteID";
}
//==========================================================================
//
// PName :: WriteValue
//
//==========================================================================
void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) const
{
int32_t val = *(int*)addr;
ar.Sprite(key, val, nullptr);
}
//==========================================================================
//
// PName :: ReadValue
//
//==========================================================================
bool PSpriteID::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
int32_t val;
ar.Sprite(key, val, nullptr);
*(int*)addr = val;
return true;
}
/* PTextureID ******************************************************************/
IMPLEMENT_CLASS(PTextureID, false, false)
//==========================================================================
//
// PTextureID Default Constructor
//
//==========================================================================
PTextureID::PTextureID()
: PInt(sizeof(FTextureID), true, false)
{
mDescriptiveName = "TextureID";
assert(sizeof(FTextureID) == alignof(FTextureID));
}
//==========================================================================
//
// PTextureID :: WriteValue
//
//==========================================================================
void PTextureID::WriteValue(FSerializer &ar, const char *key, const void *addr) const
{
FTextureID val = *(FTextureID*)addr;
ar(key, val);
}
//==========================================================================
//
// PTextureID :: ReadValue
//
//==========================================================================
bool PTextureID::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
FTextureID val;
ar(key, val);
*(FTextureID*)addr = val;
return true;
}
/* PSound *****************************************************************/
IMPLEMENT_CLASS(PSound, false, false, false, false)
IMPLEMENT_CLASS(PSound, false, false)
//==========================================================================
//
@ -1364,7 +1484,7 @@ PSound::PSound()
: PInt(sizeof(FSoundID), true)
{
mDescriptiveName = "Sound";
assert(sizeof(FSoundID) == __alignof(FSoundID));
assert(sizeof(FSoundID) == alignof(FSoundID));
}
//==========================================================================
@ -1402,7 +1522,7 @@ bool PSound::ReadValue(FSerializer &ar, const char *key, void *addr) const
/* PColor *****************************************************************/
IMPLEMENT_CLASS(PColor, false, false, false, false)
IMPLEMENT_CLASS(PColor, false, false)
//==========================================================================
//
@ -1414,12 +1534,12 @@ PColor::PColor()
: PInt(sizeof(PalEntry), true)
{
mDescriptiveName = "Color";
assert(sizeof(PalEntry) == __alignof(PalEntry));
assert(sizeof(PalEntry) == alignof(PalEntry));
}
/* PStateLabel *****************************************************************/
IMPLEMENT_CLASS(PStateLabel, false, false, false, false)
IMPLEMENT_CLASS(PStateLabel, false, false)
//==========================================================================
//
@ -1433,53 +1553,9 @@ PStateLabel::PStateLabel()
mDescriptiveName = "StateLabel";
}
/* PStatePointer **********************************************************/
IMPLEMENT_CLASS(PStatePointer, false, false, false, false)
//==========================================================================
//
// PStatePointer Default Constructor
//
//==========================================================================
PStatePointer::PStatePointer()
: PBasicType(sizeof(FState *), __alignof(FState *))
{
mDescriptiveName = "State";
storeOp = OP_SP;
loadOp = OP_LP;
moveOp = OP_MOVEA;
RegType = REGT_POINTER;
}
//==========================================================================
//
// PStatePointer :: WriteValue
//
//==========================================================================
void PStatePointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const
{
ar(key, *(FState **)addr);
}
//==========================================================================
//
// PStatePointer :: ReadValue
//
//==========================================================================
bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
bool res = false;
::Serialize(ar, key, *(FState **)addr, nullptr, &res);
return res;
}
/* PPointer ***************************************************************/
IMPLEMENT_CLASS(PPointer, false, true, false, false)
IMPLEMENT_CLASS(PPointer, false, true)
IMPLEMENT_POINTERS_START(PPointer)
IMPLEMENT_POINTER(PointedType)
@ -1492,7 +1568,7 @@ IMPLEMENT_POINTERS_END
//==========================================================================
PPointer::PPointer()
: PBasicType(sizeof(void *), __alignof(void *)), PointedType(NULL), IsConst(false)
: PBasicType(sizeof(void *), alignof(void *)), PointedType(NULL), IsConst(false)
{
mDescriptiveName = "NullPointer";
SetOps();
@ -1505,7 +1581,7 @@ PPointer::PPointer()
//==========================================================================
PPointer::PPointer(PType *pointsat, bool isconst)
: PBasicType(sizeof(void *), __alignof(void *)), PointedType(pointsat), IsConst(isconst)
: PBasicType(sizeof(void *), alignof(void *)), PointedType(pointsat), IsConst(isconst)
{
mDescriptiveName.Format("Pointer<%s%s>", pointsat->DescriptiveName(), isconst? "readonly " : "");
SetOps();
@ -1551,6 +1627,21 @@ void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const
id2 = 0;
}
//==========================================================================
//
// PPointer :: SetDefaultValue
//
//==========================================================================
void PPointer::SetPointer(void *base, unsigned offset, TArray<size_t> *special) const
{
if (PointedType != nullptr && PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
{
// Add to the list of pointers for this class.
special->Push(offset);
}
}
//==========================================================================
//
// PPointer :: WriteValue
@ -1601,16 +1692,58 @@ PPointer *NewPointer(PType *type, bool isconst)
PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket);
if (ptype == NULL)
{
ptype = new PPointer(type);
ptype = new PPointer(type, isconst);
TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket);
}
return static_cast<PPointer *>(ptype);
}
/* PStatePointer **********************************************************/
IMPLEMENT_CLASS(PStatePointer, false, false)
//==========================================================================
//
// PStatePointer Default Constructor
//
//==========================================================================
PStatePointer::PStatePointer()
{
mDescriptiveName = "Pointer<State>";
PointedType = NewNativeStruct(NAME_State, nullptr);
IsConst = true;
}
//==========================================================================
//
// PStatePointer :: WriteValue
//
//==========================================================================
void PStatePointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const
{
ar(key, *(FState **)addr);
}
//==========================================================================
//
// PStatePointer :: ReadValue
//
//==========================================================================
bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
bool res = false;
::Serialize(ar, key, *(FState **)addr, nullptr, &res);
return res;
}
/* PClassPointer **********************************************************/
IMPLEMENT_CLASS(PClassPointer, false, true, false, false)
IMPLEMENT_CLASS(PClassPointer,false, true)
IMPLEMENT_POINTERS_START(PClassPointer)
IMPLEMENT_POINTER(ClassRestriction)
@ -1691,7 +1824,7 @@ PClassPointer *NewClassPointer(PClass *restrict)
/* PEnum ******************************************************************/
IMPLEMENT_CLASS(PEnum, false, true, false, false)
IMPLEMENT_CLASS(PEnum, false, true)
IMPLEMENT_POINTERS_START(PEnum)
IMPLEMENT_POINTER(ValueType)
@ -1744,7 +1877,7 @@ PEnum *NewEnum(FName name, PTypeBase *outer)
/* PArray *****************************************************************/
IMPLEMENT_CLASS(PArray, false, true, false, false)
IMPLEMENT_CLASS(PArray, false, true)
IMPLEMENT_POINTERS_START(PArray)
IMPLEMENT_POINTER(ElementType)
@ -1870,6 +2003,20 @@ void PArray::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset>
}
}
//==========================================================================
//
// PArray :: SetDefaultValue
//
//==========================================================================
void PArray::SetPointer(void *base, unsigned offset, TArray<size_t> *special) const
{
for (unsigned i = 0; i < ElementCount; ++i)
{
ElementType->SetPointer(base, offset + i*ElementSize, special);
}
}
//==========================================================================
//
// NewArray
@ -1891,59 +2038,9 @@ PArray *NewArray(PType *type, unsigned int count)
return (PArray *)atype;
}
/* PVector ****************************************************************/
IMPLEMENT_CLASS(PVector, false, false, false, false)
//==========================================================================
//
// PVector - Default Constructor
//
//==========================================================================
PVector::PVector()
: PArray(TypeFloat32, 3)
{
mDescriptiveName = "Vector";
}
//==========================================================================
//
// PVector - Parameterized Constructor
//
//==========================================================================
PVector::PVector(unsigned int size)
: PArray(TypeFloat32, size)
{
mDescriptiveName.Format("Vector<%d>", size);
assert(size >= 2 && size <= 4);
}
//==========================================================================
//
// NewVector
//
// Returns a PVector with the given dimension, making sure not to create
// duplicates.
//
//==========================================================================
PVector *NewVector(unsigned int size)
{
size_t bucket;
PType *type = TypeTable.FindType(RUNTIME_CLASS(PVector), (intptr_t)TypeFloat32, size, &bucket);
if (type == NULL)
{
type = new PVector(size);
TypeTable.AddType(type, RUNTIME_CLASS(PVector), (intptr_t)TypeFloat32, size, bucket);
}
return (PVector *)type;
}
/* PDynArray **************************************************************/
IMPLEMENT_CLASS(PDynArray, false, true, false, false)
IMPLEMENT_CLASS(PDynArray, false, true)
IMPLEMENT_POINTERS_START(PDynArray)
IMPLEMENT_POINTER(ElementType)
@ -1960,7 +2057,7 @@ PDynArray::PDynArray()
{
mDescriptiveName = "DynArray";
Size = sizeof(FArray);
Align = __alignof(FArray);
Align = alignof(FArray);
}
//==========================================================================
@ -1974,7 +2071,7 @@ PDynArray::PDynArray(PType *etype)
{
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
Size = sizeof(FArray);
Align = __alignof(FArray);
Align = alignof(FArray);
}
//==========================================================================
@ -2026,7 +2123,7 @@ PDynArray *NewDynArray(PType *type)
/* PMap *******************************************************************/
IMPLEMENT_CLASS(PMap, false, true, false, false)
IMPLEMENT_CLASS(PMap, false, true)
IMPLEMENT_POINTERS_START(PMap)
IMPLEMENT_POINTER(KeyType)
@ -2044,7 +2141,7 @@ PMap::PMap()
{
mDescriptiveName = "Map";
Size = sizeof(FMap);
Align = __alignof(FMap);
Align = alignof(FMap);
}
//==========================================================================
@ -2058,7 +2155,7 @@ PMap::PMap(PType *keytype, PType *valtype)
{
mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName());
Size = sizeof(FMap);
Align = __alignof(FMap);
Align = alignof(FMap);
}
//==========================================================================
@ -2110,7 +2207,7 @@ PMap *NewMap(PType *keytype, PType *valuetype)
/* PStruct ****************************************************************/
IMPLEMENT_CLASS(PStruct, false, false, false, false)
IMPLEMENT_CLASS(PStruct, false, false)
//==========================================================================
//
@ -2148,13 +2245,30 @@ void PStruct::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset
{
for (const PField *field : Fields)
{
if (!(field->Flags & VARF_Native))
if (!(field->Flags & VARF_Transient))
{
field->Type->SetDefaultValue(base, unsigned(offset + field->Offset), special);
}
}
}
//==========================================================================
//
// PStruct :: SetPointer
//
//==========================================================================
void PStruct::SetPointer(void *base, unsigned offset, TArray<size_t> *special) const
{
for (const PField *field : Fields)
{
if (!(field->Flags & VARF_Transient))
{
field->Type->SetPointer(base, unsigned(offset + field->Offset), special);
}
}
}
//==========================================================================
//
// PStruct :: WriteValue
@ -2198,8 +2312,8 @@ void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray<PField
for (unsigned i = 0; i < fields.Size(); ++i)
{
const PField *field = fields[i];
// Skip fields with native serialization
if (!(field->Flags & VARF_Native))
// Skip fields without or with native serialization
if (!(field->Flags & VARF_Transient))
{
field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const BYTE *)addr + field->Offset);
}
@ -2285,7 +2399,7 @@ PField *PStruct::AddField(FName name, PType *type, DWORD flags)
PField *PStruct::AddNativeField(FName name, PType *type, size_t address, DWORD flags, int bitvalue)
{
PField *field = new PField(name, type, flags|VARF_Native, address, bitvalue);
PField *field = new PField(name, type, flags|VARF_Native|VARF_Transient, address, bitvalue);
if (Symbols.AddSymbol(field) == nullptr)
{ // name is already in use
@ -2329,9 +2443,47 @@ PStruct *NewStruct(FName name, PTypeBase *outer)
return static_cast<PStruct *>(stype);
}
/* PNativeStruct ****************************************************************/
IMPLEMENT_CLASS(PNativeStruct, false, false)
//==========================================================================
//
// PNativeStruct - Parameterized Constructor
//
//==========================================================================
PNativeStruct::PNativeStruct(FName name)
: PStruct(name, nullptr)
{
mDescriptiveName.Format("NativeStruct<%s>", name.GetChars());
Size = 0;
HasNativeFields = true;
}
//==========================================================================
//
// NewNativeStruct
// Returns a PNativeStruct for the given name and container, making sure not to
// create duplicates.
//
//==========================================================================
PNativeStruct *NewNativeStruct(FName name, PTypeBase *outer)
{
size_t bucket;
PType *stype = TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, &bucket);
if (stype == NULL)
{
stype = new PNativeStruct(name);
TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket);
}
return static_cast<PNativeStruct *>(stype);
}
/* PField *****************************************************************/
IMPLEMENT_CLASS(PField, false, false, false, false)
IMPLEMENT_CLASS(PField, false, false)
//==========================================================================
//
@ -2348,8 +2500,7 @@ PField::PField()
PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue)
: PSymbol(name), Offset(offset), Type(type), Flags(flags)
{
BitValue = bitvalue;
if (bitvalue != -1)
if (bitvalue != 0)
{
BitValue = 0;
unsigned val = bitvalue;
@ -2369,14 +2520,15 @@ PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue
else
{
// Just abort. Bit fields should only be defined internally.
I_FatalError("Trying to create an invalid bit field element: %s", name.GetChars());
I_Error("Trying to create an invalid bit field element: %s", name.GetChars());
}
}
else BitValue = -1;
}
/* PPrototype *************************************************************/
IMPLEMENT_CLASS(PPrototype, false, false, false, false)
IMPLEMENT_CLASS(PPrototype, false, false)
//==========================================================================
//
@ -2462,7 +2614,7 @@ PPrototype *NewPrototype(const TArray<PType *> &rettypes, const TArray<PType *>
/* PFunction **************************************************************/
IMPLEMENT_CLASS(PFunction, false, false, false, false)
IMPLEMENT_CLASS(PFunction, false, false)
//==========================================================================
//
@ -2489,7 +2641,7 @@ size_t PFunction::PropagateMark()
//
//==========================================================================
unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags)
unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags)
{
Variant variant;
@ -2497,6 +2649,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArra
assert(Variants.Size() == 0);
variant.Flags = flags;
variant.UseFlags = useflags;
variant.Proto = proto;
variant.ArgFlags = std::move(argflags);
variant.ArgNames = std::move(argnames);
@ -2511,7 +2664,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArra
assert(proto->ArgumentTypes.Size() > 0);
auto selftypeptr = dyn_cast<PPointer>(proto->ArgumentTypes[0]);
assert(selftypeptr != nullptr);
variant.SelfClass = dyn_cast<PClass>(selftypeptr->PointedType);
variant.SelfClass = dyn_cast<PStruct>(selftypeptr->PointedType);
assert(variant.SelfClass != nullptr);
}
else
@ -2524,7 +2677,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArra
/* PClass *****************************************************************/
IMPLEMENT_CLASS(PClass, false, true, false, false)
IMPLEMENT_CLASS(PClass, false, true)
IMPLEMENT_POINTERS_START(PClass)
IMPLEMENT_POINTER(ParentClass)
@ -2544,10 +2697,10 @@ static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *
if (type != NULL)
{
RecurseWriteFields(type->ParentClass, ar, addr);
// Don't write this part if it has no non-native variables
// Don't write this part if it has no non-transient variables
for (unsigned i = 0; i < type->Fields.Size(); ++i)
{
if (!(type->Fields[i]->Flags & VARF_Native))
if (!(type->Fields[i]->Flags & VARF_Transient))
{
// Tag this section with the class it came from in case
// a more-derived class has variables that shadow a less-
@ -2741,6 +2894,7 @@ void PClass::StaticShutdown ()
uniqueFPs.Push(const_cast<size_t *>(type->FlatPointers));
}
}
type->Destroy();
}
for (i = 0; i < uniqueFPs.Size(); ++i)
{
@ -2758,7 +2912,6 @@ void PClass::StaticShutdown ()
{
auto cr = ((ClassReg *)*probe);
cr->MyClass = nullptr;
if (cr->VMExport != nullptr) cr->VMExport->MyClass = nullptr;
}
}
@ -2885,10 +3038,6 @@ PClass *ClassReg::RegisterClass()
{
cls->ParentClass = ParentType->RegisterClass();
}
if (VMExport != nullptr)
{
cls->VMExported = VMExport->RegisterClass();
}
return cls;
}
@ -3095,14 +3244,9 @@ void PClass::InitializeDefaults()
assert(ParentClass != NULL);
ParentClass->InitializeSpecials(Defaults);
// and initialize our own special values.
auto it = Symbols.GetIterator();
PSymbolTable::MapType::Pair *pair;
while (it.NextPair(pair))
for (const PField *field : Fields)
{
auto field = dyn_cast<PField>(pair->Value);
if (field != nullptr && !(field->Flags & VARF_Native))
if (!(field->Flags & VARF_Native))
{
field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits);
}
@ -3154,6 +3298,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
// see if we can reuse the existing class. This is only possible if the inheritance is identical. Otherwise it needs to be replaced.
if (this == existclass->ParentClass)
{
existclass->ObjectFlags &= OF_Transient;
return existclass;
}
}
@ -3320,7 +3465,7 @@ void PClass::BuildFlatPointers ()
return;
}
else if (ParentClass == NULL)
{ // No parent: FlatPointers is the same as Pointers.
{ // No parent (i.e. DObject: FlatPointers is the same as Pointers.
if (Pointers == NULL)
{ // No pointers: Make FlatPointers a harmless non-NULL.
FlatPointers = &TheEnd;
@ -3333,7 +3478,19 @@ void PClass::BuildFlatPointers ()
else
{
ParentClass->BuildFlatPointers ();
if (Pointers == NULL)
TArray<size_t> ScriptPointers;
// Collect all pointers in scripted fields. These are not part of the Pointers list.
for (auto field : Fields)
{
if (!(field->Flags & VARF_Native))
{
field->Type->SetPointer(Defaults, unsigned(field->Offset), &ScriptPointers);
}
}
if (Pointers == nullptr && ScriptPointers.Size() == 0)
{ // No new pointers: Just use the same FlatPointers as the parent.
FlatPointers = ParentClass->FlatPointers;
}
@ -3341,20 +3498,34 @@ void PClass::BuildFlatPointers ()
{ // New pointers: Create a new FlatPointers array and add them.
int numPointers, numSuperPointers;
// Count pointers defined by this class.
for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++)
{ }
if (Pointers != nullptr)
{
// Count pointers defined by this class.
for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++)
{
}
}
else numPointers = 0;
// Count pointers defined by superclasses.
for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++)
{ }
// Concatenate them into a new array
size_t *flat = new size_t[numPointers + numSuperPointers + 1];
size_t *flat = new size_t[numPointers + numSuperPointers + ScriptPointers.Size() + 1];
if (numSuperPointers > 0)
{
memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers);
}
memcpy (flat + numSuperPointers, Pointers, sizeof(size_t)*(numPointers+1));
if (numPointers > 0)
{
memcpy(flat + numSuperPointers, Pointers, sizeof(size_t)*numPointers);
}
if (ScriptPointers.Size() > 0)
{
memcpy(flat + numSuperPointers + numPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size());
}
flat[numSuperPointers + numPointers + ScriptPointers.Size()] = ~(size_t)0;
FlatPointers = flat;
}
}
@ -3378,6 +3549,16 @@ const PClass *PClass::NativeClass() const
return cls;
}
VMFunction *PClass::FindFunction(FName clsname, FName funcname)
{
auto cls = PClass::FindActor(clsname);
if (!cls) return nullptr;
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol(funcname, true));
if (!func) return nullptr;
return func->Variants[0].Implementation;
}
/* FTypeTable **************************************************************/
//==========================================================================
@ -3546,19 +3727,19 @@ CCMD(typetable)
// Symbol tables ------------------------------------------------------------
IMPLEMENT_CLASS(PTypeBase, true, false, false, false);
IMPLEMENT_CLASS(PSymbol, true, false, false, false);
IMPLEMENT_CLASS(PSymbolConst, false, false, false, false);
IMPLEMENT_CLASS(PSymbolConstNumeric, false, false, false, false);
IMPLEMENT_CLASS(PSymbolConstString, false, false, false, false);
IMPLEMENT_CLASS(PSymbolTreeNode, false, false, false, false)
IMPLEMENT_CLASS(PSymbolType, false, true, false, false)
IMPLEMENT_CLASS(PTypeBase, true, false);
IMPLEMENT_CLASS(PSymbol, true, false);
IMPLEMENT_CLASS(PSymbolConst, false, false);
IMPLEMENT_CLASS(PSymbolConstNumeric, false, false);
IMPLEMENT_CLASS(PSymbolConstString, false, false);
IMPLEMENT_CLASS(PSymbolTreeNode, false, false)
IMPLEMENT_CLASS(PSymbolType, false, true)
IMPLEMENT_POINTERS_START(PSymbolType)
IMPLEMENT_POINTER(Type)
IMPLEMENT_POINTERS_END
IMPLEMENT_CLASS(PSymbolVMFunction, false, true, false, false)
IMPLEMENT_CLASS(PSymbolVMFunction, false, true)
IMPLEMENT_POINTERS_START(PSymbolVMFunction)
IMPLEMENT_POINTER(Function)

View file

@ -5,10 +5,10 @@
#error You must #include "dobject.h" to get dobjtype.h
#endif
#include "vm.h"
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
#include "vm.h"
// Variable/parameter/field flags -------------------------------------------
// Making all these different storage types use a common set of flags seems
@ -19,7 +19,7 @@ enum
VARF_Optional = (1<<0), // func param is optional
VARF_Method = (1<<1), // func has an implied self parameter
VARF_Action = (1<<2), // func has implied owner and state parameters
VARF_Native = (1<<3), // func is native code/don't auto serialize field
VARF_Native = (1<<3), // func is native code, field is natively defined
VARF_ReadOnly = (1<<4), // field is read only, do not write to it
VARF_Private = (1<<5), // field is private to containing class
VARF_Protected = (1<<6), // field is only accessible by containing class and children.
@ -32,6 +32,8 @@ enum
VARF_Static = (1<<13), // static class data (by necessity read only.)
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
VARF_Override = (1<<15), // overrides a virtual function from the parent class.
VARF_Ref = (1<<16), // argument is passed by reference.
VARF_Transient = (1<<17) // don't auto serialize field.
};
// Symbol information -------------------------------------------------------
@ -252,6 +254,7 @@ public:
// initialization when the object is created and destruction when the
// object is destroyed.
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const;
virtual void SetPointer(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL) const;
// Initialize the value, if needed (e.g. strings)
virtual void InitializeValue(void *addr, const void *def) const;
@ -513,6 +516,26 @@ public:
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
};
class PSpriteID : public PInt
{
DECLARE_CLASS(PSpriteID, PInt);
public:
PSpriteID();
void WriteValue(FSerializer &ar, const char *key, const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key, void *addr) const override;
};
class PTextureID : public PInt
{
DECLARE_CLASS(PTextureID, PInt);
public:
PTextureID();
void WriteValue(FSerializer &ar, const char *key, const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key, void *addr) const override;
};
class PColor : public PInt
{
DECLARE_CLASS(PColor, PInt);
@ -529,16 +552,6 @@ public:
// Pointers -----------------------------------------------------------------
class PStatePointer : public PBasicType
{
DECLARE_CLASS(PStatePointer, PBasicType);
public:
PStatePointer();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
};
class PPointer : public PBasicType
{
DECLARE_CLASS(PPointer, PBasicType);
@ -552,6 +565,7 @@ public:
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override;
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
@ -560,6 +574,17 @@ protected:
void SetOps();
};
class PStatePointer : public PPointer
{
DECLARE_CLASS(PStatePointer, PPointer);
public:
PStatePointer();
void WriteValue(FSerializer &ar, const char *key, const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key, void *addr) const override;
};
class PClassPointer : public PPointer
{
DECLARE_CLASS(PClassPointer, PPointer);
@ -569,6 +594,9 @@ public:
class PClass *ClassRestriction;
// this is only here to block PPointer's implementation
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override {}
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
protected:
@ -583,7 +611,7 @@ class PField : public PSymbol
DECLARE_CLASS(PField, PSymbol);
HAS_OBJECT_POINTERS
public:
PField(FName name, PType *type, DWORD flags = 0, size_t offset = 0, int bitvalue = -1);
PField(FName name, PType *type, DWORD flags = 0, size_t offset = 0, int bitvalue = 0);
size_t Offset;
PType *Type;
@ -626,22 +654,12 @@ public:
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special) const override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special) const override;
protected:
PArray();
};
// A vector is an array with extra operations.
class PVector : public PArray
{
DECLARE_CLASS(PVector, PArray);
HAS_OBJECT_POINTERS;
public:
PVector(unsigned int size);
protected:
PVector();
};
class PDynArray : public PCompoundType
{
DECLARE_CLASS(PDynArray, PCompoundType);
@ -683,13 +701,14 @@ public:
bool HasNativeFields;
virtual PField *AddField(FName name, PType *type, DWORD flags=0);
virtual PField *AddNativeField(FName name, PType *type, size_t address, DWORD flags = 0, int bitvalue = -1);
virtual PField *AddNativeField(FName name, PType *type, size_t address, DWORD flags = 0, int bitvalue = 0);
size_t PropagateMark();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *specials) const override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *specials) const override;
static void WriteFields(FSerializer &ar, const void *addr, const TArray<PField *> &fields);
bool ReadFields(FSerializer &ar, void *addr) const;
@ -697,6 +716,15 @@ protected:
PStruct();
};
// a native struct will always be abstract and cannot be instantiated. All variables are references.
// In addition, native structs can have methods (but no virtual ones.)
class PNativeStruct : public PStruct
{
DECLARE_CLASS(PNativeStruct, PStruct);
public:
PNativeStruct(FName name = NAME_None);
};
class PPrototype : public PCompoundType
{
DECLARE_CLASS(PPrototype, PCompoundType);
@ -724,13 +752,14 @@ public:
VMFunction *Implementation;
TArray<DWORD> ArgFlags; // Should be the same length as Proto->ArgumentTypes
TArray<FName> ArgNames; // we need the names to access them later when the function gets compiled.
DWORD Flags;
PClass *SelfClass;
uint32_t Flags;
int UseFlags;
PStruct *SelfClass;
};
TArray<Variant> Variants;
PClass *OwningClass = nullptr;
PStruct *OwningClass = nullptr;
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags);
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags);
int GetImplicitArgs()
{
if (Variants[0].Flags & VARF_Action) return 3;
@ -740,7 +769,7 @@ public:
size_t PropagateMark();
PFunction(PClass *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
PFunction(PStruct *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
};
// Meta-info for every class derived from DObject ---------------------------
@ -751,9 +780,9 @@ enum
};
class PClassClass;
class PClass : public PStruct
class PClass : public PNativeStruct
{
DECLARE_CLASS(PClass, PStruct);
DECLARE_CLASS(PClass, PNativeStruct);
HAS_OBJECT_POINTERS;
protected:
// We unravel _WITH_META here just as we did for PType.
@ -780,7 +809,6 @@ public:
// Per-class information -------------------------------------
PClass *ParentClass; // the class this class derives from
PClass *VMExported; // this is here to allow script classes to override native virtual functions
const size_t *Pointers; // object pointers defined by this class *only*
const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default
BYTE *Defaults;
@ -831,6 +859,7 @@ public:
static PClassActor *FindActor(const FString &name) { return FindActor(FName(name, true)); }
static PClassActor *FindActor(ENamedName name) { return FindActor(FName(name)); }
static PClassActor *FindActor(FName name);
static VMFunction *FindFunction(FName cls, FName func);
PClass *FindClassTentative(FName name);
static TArray<PClass *> AllClasses;
@ -888,7 +917,6 @@ struct FTypeTable
extern FTypeTable TypeTable;
// Returns a type from the TypeTable. Will create one if it isn't present.
PVector *NewVector(unsigned int size);
PMap *NewMap(PType *keytype, PType *valuetype);
PArray *NewArray(PType *type, unsigned int count);
PDynArray *NewDynArray(PType *type);
@ -896,6 +924,7 @@ PPointer *NewPointer(PType *type, bool isconst = false);
PClassPointer *NewClassPointer(PClass *restrict);
PEnum *NewEnum(FName name, PTypeBase *outer);
PStruct *NewStruct(FName name, PTypeBase *outer);
PNativeStruct *NewNativeStruct(FName name, PTypeBase *outer);
PPrototype *NewPrototype(const TArray<PType *> &rettypes, const TArray<PType *> &argtypes);
// Built-in types -----------------------------------------------------------
@ -911,8 +940,12 @@ extern PString *TypeString;
extern PName *TypeName;
extern PSound *TypeSound;
extern PColor *TypeColor;
extern PTextureID *TypeTextureID;
extern PSpriteID *TypeSpriteID;
extern PStruct *TypeVector2;
extern PStruct *TypeVector3;
extern PStruct *TypeColorStruct;
extern PStruct *TypeStringStruct;
extern PStatePointer *TypeState;
extern PStateLabel *TypeStateLabel;
extern PPointer *TypeNullPtr;

View file

@ -264,12 +264,14 @@ char ( &_ArraySizeHelper( T (&array)[N] ))[N];
#ifdef __MACH__
#define SECTION_AREG "__DATA,areg"
#define SECTION_CREG "__DATA,creg"
#define SECTION_FREG "__DATA,freg"
#define SECTION_GREG "__DATA,greg"
#define SECTION_MREG "__DATA,mreg"
#define SECTION_YREG "__DATA,yreg"
#else
#define SECTION_AREG "areg"
#define SECTION_CREG "creg"
#define SECTION_FREG "freg"
#define SECTION_GREG "greg"
#define SECTION_MREG "mreg"
#define SECTION_YREG "yreg"

View file

@ -31,7 +31,7 @@
#include "serializer.h"
#include "doomstat.h"
IMPLEMENT_CLASS(DSectorEffect, false, false, false, false)
IMPLEMENT_CLASS(DSectorEffect, false, false)
DSectorEffect::DSectorEffect ()
: DThinker(STAT_SECTOREFFECT)
@ -71,7 +71,7 @@ void DSectorEffect::Serialize(FSerializer &arc)
arc("sector", m_Sector);
}
IMPLEMENT_CLASS(DMover, false, true, false, false)
IMPLEMENT_CLASS(DMover, false, true)
IMPLEMENT_POINTERS_START(DMover)
IMPLEMENT_POINTER(interpolation)
@ -108,7 +108,7 @@ void DMover::StopInterpolation(bool force)
}
}
IMPLEMENT_CLASS(DMovingFloor, false, false, false, false)
IMPLEMENT_CLASS(DMovingFloor, false, false)
DMovingFloor::DMovingFloor ()
{
@ -121,7 +121,7 @@ DMovingFloor::DMovingFloor (sector_t *sector)
interpolation = sector->SetInterpolation(sector_t::FloorMove, true);
}
IMPLEMENT_CLASS(DMovingCeiling, false, false, false, false)
IMPLEMENT_CLASS(DMovingCeiling, false, false)
DMovingCeiling::DMovingCeiling ()
{

View file

@ -12,7 +12,7 @@ public:
void Serialize(FSerializer &arc);
void Destroy();
void Destroy() override;
sector_t *GetSector() const { return m_Sector; }
@ -35,7 +35,7 @@ protected:
DMover ();
void Serialize(FSerializer &arc);
void Destroy();
void Destroy() override;
};
class DMovingFloor : public DMover

View file

@ -48,7 +48,7 @@ extern cycle_t BotSupportCycles;
extern cycle_t ActionCycles;
extern int BotWTG;
IMPLEMENT_CLASS(DThinker, false, false, false, true)
IMPLEMENT_CLASS(DThinker, false, false)
DThinker *NextToThink;
@ -56,6 +56,12 @@ FThinkerList DThinker::Thinkers[MAX_STATNUM+2];
FThinkerList DThinker::FreshThinkers[MAX_STATNUM+1];
bool DThinker::bSerialOverride = false;
//==========================================================================
//
//
//
//==========================================================================
void FThinkerList::AddTail(DThinker *thinker)
{
assert(thinker->PrevThinker == NULL && thinker->NextThinker == NULL);
@ -80,6 +86,12 @@ void FThinkerList::AddTail(DThinker *thinker)
GC::WriteBarrier(Sentinel, thinker);
}
//==========================================================================
//
//
//
//==========================================================================
DThinker *FThinkerList::GetHead() const
{
if (Sentinel == NULL || Sentinel->NextThinker == Sentinel)
@ -90,6 +102,12 @@ DThinker *FThinkerList::GetHead() const
return Sentinel->NextThinker;
}
//==========================================================================
//
//
//
//==========================================================================
DThinker *FThinkerList::GetTail() const
{
if (Sentinel == NULL || Sentinel->PrevThinker == Sentinel)
@ -99,11 +117,23 @@ DThinker *FThinkerList::GetTail() const
return Sentinel->PrevThinker;
}
//==========================================================================
//
//
//
//==========================================================================
bool FThinkerList::IsEmpty() const
{
return Sentinel == NULL || Sentinel->NextThinker == NULL;
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::SaveList(FSerializer &arc, DThinker *node)
{
if (node != NULL)
@ -234,6 +264,12 @@ void DThinker::Destroy ()
Super::Destroy();
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::Remove()
{
if (this == NextToThink)
@ -254,14 +290,53 @@ void DThinker::Remove()
PrevThinker = NULL;
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::PostBeginPlay ()
{
}
DEFINE_ACTION_FUNCTION(DThinker, PostBeginPlay)
{
PARAM_SELF_PROLOGUE(DThinker);
self->PostBeginPlay();
return 0;
}
void DThinker::CallPostBeginPlay()
{
IFVIRTUAL(DThinker, PostBeginPlay)
{
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else
{
PostBeginPlay();
}
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::PostSerialize()
{
}
//==========================================================================
//
//
//
//==========================================================================
DThinker *DThinker::FirstThinker (int statnum)
{
DThinker *node;
@ -282,6 +357,12 @@ DThinker *DThinker::FirstThinker (int statnum)
return node;
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::ChangeStatNum (int statnum)
{
FThinkerList *list;
@ -305,7 +386,19 @@ void DThinker::ChangeStatNum (int statnum)
list->AddTail(this);
}
DEFINE_ACTION_FUNCTION(DThinker, ChangeStatNum)
{
PARAM_SELF_PROLOGUE(DThinker);
PARAM_INT(stat);
self->ChangeStatNum(stat);
return 0;
}
//==========================================================================
//
// Mark the first thinker of each list
//
//==========================================================================
void DThinker::MarkRoots()
{
for (int i = 0; i <= MAX_STATNUM; ++i)
@ -316,7 +409,12 @@ void DThinker::MarkRoots()
GC::Mark(Thinkers[MAX_STATNUM+1].Sentinel);
}
//==========================================================================
//
// Destroy every thinker
//
//==========================================================================
void DThinker::DestroyAllThinkers ()
{
int i;
@ -333,6 +431,12 @@ void DThinker::DestroyAllThinkers ()
GC::FullGC();
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::DestroyThinkersInList (FThinkerList &list)
{
if (list.Sentinel != NULL)
@ -347,6 +451,12 @@ void DThinker::DestroyThinkersInList (FThinkerList &list)
}
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::RunThinkers ()
{
int i, count;
@ -377,6 +487,12 @@ void DThinker::RunThinkers ()
ThinkCycles.Unclock();
}
//==========================================================================
//
//
//
//==========================================================================
int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
{
int count = 0;
@ -399,7 +515,7 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
node->Remove();
dest->AddTail(node);
}
node->PostBeginPlay();
node->CallPostBeginPlay();
}
else if (dest != NULL)
{
@ -408,7 +524,7 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
if (!(node->ObjectFlags & OF_EuthanizeMe))
{ // Only tick thinkers not scheduled for destruction
node->Tick();
node->CallTick();
node->ObjectFlags &= ~OF_JustSpawned;
GC::CheckGC();
}
@ -417,10 +533,40 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
return count;
}
//==========================================================================
//
//
//
//==========================================================================
void DThinker::Tick ()
{
}
DEFINE_ACTION_FUNCTION(DThinker, Tick)
{
PARAM_SELF_PROLOGUE(DThinker);
self->Tick();
return 0;
}
void DThinker::CallTick()
{
IFVIRTUAL(DThinker, Tick)
{
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else Tick();
}
//==========================================================================
//
//
//
//==========================================================================
size_t DThinker::PropagateMark()
{
// Do not choke on partially initialized objects (as happens when loading a savegame fails)
@ -434,6 +580,12 @@ size_t DThinker::PropagateMark()
return Super::PropagateMark();
}
//==========================================================================
//
//
//
//==========================================================================
FThinkerIterator::FThinkerIterator (const PClass *type, int statnum)
{
if ((unsigned)statnum > MAX_STATNUM)
@ -451,6 +603,12 @@ FThinkerIterator::FThinkerIterator (const PClass *type, int statnum)
m_SearchingFresh = false;
}
//==========================================================================
//
//
//
//==========================================================================
FThinkerIterator::FThinkerIterator (const PClass *type, int statnum, DThinker *prev)
{
if ((unsigned)statnum > MAX_STATNUM)
@ -475,12 +633,24 @@ FThinkerIterator::FThinkerIterator (const PClass *type, int statnum, DThinker *p
}
}
//==========================================================================
//
//
//
//==========================================================================
void FThinkerIterator::Reinit ()
{
m_CurrThinker = DThinker::Thinkers[m_Stat].GetHead();
m_SearchingFresh = false;
}
//==========================================================================
//
//
//
//==========================================================================
DThinker *FThinkerIterator::Next (bool exact)
{
if (m_ParentType == NULL)
@ -526,8 +696,13 @@ DThinker *FThinkerIterator::Next (bool exact)
return NULL;
}
//==========================================================================
//
// This is for scripting, which needs the iterator wrapped into an object with the needed functions exported.
// Unfortunately we cannot have templated type conversions in scripts.
//
//==========================================================================
class DThinkerIterator : public DObject, public FThinkerIterator
{
DECLARE_CLASS(DThinkerIterator, DObject)
@ -539,7 +714,7 @@ public:
}
};
IMPLEMENT_CLASS(DThinkerIterator, false, false, false, false);
IMPLEMENT_CLASS(DThinkerIterator, false, false);
DEFINE_ACTION_FUNCTION(DThinkerIterator, Create)
{
PARAM_PROLOGUE;
@ -561,8 +736,11 @@ DEFINE_ACTION_FUNCTION(DThinkerIterator, Reinit)
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
ADD_STAT (think)
{

View file

@ -66,10 +66,12 @@ class DThinker : public DObject
DECLARE_CLASS (DThinker, DObject)
public:
DThinker (int statnum = STAT_DEFAULT) throw();
void Destroy ();
void Destroy () override;
virtual ~DThinker ();
virtual void Tick ();
void CallTick();
virtual void PostBeginPlay (); // Called just before the first tick
void CallPostBeginPlay();
virtual void PostSerialize();
size_t PropagateMark();

View file

@ -69,6 +69,7 @@
#include "p_setup.h"
#include "p_spec.h"
#include "r_utility.h"
#include "a_ammo.h"
#include "math/cmath.h"
static FRandom pr_script("FScript");
@ -1785,7 +1786,7 @@ public:
void Destroy() { Super::Destroy(); m_Sector->lightingdata=NULL; }
};
IMPLEMENT_CLASS(DLightLevel, false, false, false, false)
IMPLEMENT_CLASS(DLightLevel, false, false)
void DLightLevel::Serialize(FSerializer &arc)
{
@ -2975,7 +2976,7 @@ void FParser::SF_ObjAwaken(void)
if(mo)
{
mo->Activate(Script->trigger);
mo->CallActivate(Script->trigger);
}
}

View file

@ -71,7 +71,7 @@
//
//==========================================================================
IMPLEMENT_CLASS(DFsSection, false, true, false, false)
IMPLEMENT_CLASS(DFsSection, false, true)
IMPLEMENT_POINTERS_START(DFsSection)
IMPLEMENT_POINTER(next)

View file

@ -99,7 +99,7 @@ AActor *trigger_obj;
//
//==========================================================================
IMPLEMENT_CLASS(DFsScript, false, true, false, false)
IMPLEMENT_CLASS(DFsScript, false, true)
IMPLEMENT_POINTERS_START(DFsScript)
IMPLEMENT_POINTER(parent)
@ -269,7 +269,7 @@ void DFsScript::ParseScript(char *position)
//
//==========================================================================
IMPLEMENT_CLASS(DRunningScript, false, true, false, false)
IMPLEMENT_CLASS(DRunningScript, false, true)
IMPLEMENT_POINTERS_START(DRunningScript)
IMPLEMENT_POINTER(prev)
@ -380,7 +380,7 @@ void DRunningScript::Serialize(FSerializer &arc)
//
//==========================================================================
IMPLEMENT_CLASS(DFraggleThinker, false, true, false, false)
IMPLEMENT_CLASS(DFraggleThinker, false, true)
IMPLEMENT_POINTERS_START(DFraggleThinker)
IMPLEMENT_POINTER(RunningScripts)

View file

@ -337,7 +337,7 @@ public:
DFsScript();
~DFsScript();
void Destroy();
void Destroy() override;
void Serialize(FSerializer &ar);
DFsVariable *NewVariable(const char *name, int vtype);
@ -652,7 +652,7 @@ class DRunningScript : public DObject
public:
DRunningScript(AActor *trigger=NULL, DFsScript *owner = NULL, int index = 0) ;
void Destroy();
void Destroy() override;
void Serialize(FSerializer &arc);
TObjPtr<DFsScript> script;
@ -687,7 +687,7 @@ public:
bool nocheckposition;
DFraggleThinker();
void Destroy();
void Destroy() override;
void Serialize(FSerializer & arc);

View file

@ -179,7 +179,7 @@ AActor* actorvalue(const svalue_t &svalue)
//
//==========================================================================
IMPLEMENT_CLASS(DFsVariable, false, true, false, false)
IMPLEMENT_CLASS(DFsVariable, false, true)
IMPLEMENT_POINTERS_START(DFsVariable)
IMPLEMENT_POINTER(next)

View file

@ -1,80 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "m_random.h"
#include "p_local.h"
#include "p_enemy.h"
#include "s_sound.h"
#include "statnums.h"
#include "a_specialspot.h"
#include "vm.h"
#include "doomstat.h"
#include "g_level.h"
*/
static FRandom pr_spawnfly ("SpawnFly");
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS_DEF(spawntype, AActor);
DSpotState *state = DSpotState::GetSpotState();
AActor *targ;
AActor *spit;
bool isdefault = false;
// shoot a cube at current target
targ = state->GetNextInList(PClass::FindActor("BossTarget"), G_SkillProperty(SKILLP_EasyBossBrain));
if (targ != NULL)
{
if (spawntype == NULL)
{
spawntype = PClass::FindActor("SpawnShot");
isdefault = true;
}
// spawn brain missile
spit = P_SpawnMissile (self, targ, spawntype);
if (spit != NULL)
{
// Boss cubes should move freely to their destination so it's
// probably best to disable all collision detection for them.
if (spit->flags & MF_NOCLIP) spit->flags5 |= MF5_NOINTERACTION;
spit->target = targ;
spit->master = self;
// [RH] Do this correctly for any trajectory. Doom would divide by 0
// if the target had the same y coordinate as the spitter.
if (spit->Vel.X == 0 && spit->Vel.Y == 0)
{
spit->special2 = 0;
}
else if (fabs(spit->Vel.Y) > fabs(spit->Vel.X))
{
spit->special2 = int((targ->Y() - self->Y()) / spit->Vel.Y);
}
else
{
spit->special2 = int((targ->X() - self->X()) / spit->Vel.X);
}
// [GZ] Calculates when the projectile will have reached destination
spit->special2 += level.maptime;
spit->flags6 |= MF6_BOSSCUBE;
}
if (!isdefault)
{
S_Sound(self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NONE);
}
else
{
// compatibility fallback
S_Sound (self, CHAN_WEAPON, "brain/spit", 1, ATTN_NONE);
}
}
return 0;
}

View file

@ -1,43 +0,0 @@
#ifndef __A_DOOMGLOBAL_H__
#define __A_DOOMGLOBAL_H__
#include "info.h"
class AScriptedMarine : public AActor
{
DECLARE_CLASS (AScriptedMarine, AActor)
public:
enum EMarineWeapon
{
WEAPON_Dummy,
WEAPON_Fist,
WEAPON_BerserkFist,
WEAPON_Chainsaw,
WEAPON_Pistol,
WEAPON_Shotgun,
WEAPON_SuperShotgun,
WEAPON_Chaingun,
WEAPON_RocketLauncher,
WEAPON_PlasmaRifle,
WEAPON_Railgun,
WEAPON_BFG
};
void Activate (AActor *activator);
void Deactivate (AActor *activator);
void BeginPlay ();
void Tick ();
void SetWeapon (EMarineWeapon);
void SetSprite (PClassActor *source);
void Serialize(FSerializer &arc);
int CurrentWeapon;
protected:
bool GetWeaponStates(int weap, FState *&melee, FState *&missile);
int SpriteOverride;
};
#endif //__A_DOOMGLOBAL_H__

View file

@ -1,29 +0,0 @@
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "p_spec.h"
#include "a_sharedglobal.h"
#include "m_random.h"
#include "gi.h"
#include "doomstat.h"
#include "gstrings.h"
#include "vm.h"
#include "g_level.h"
#include "p_enemy.h"
#include "a_doomglobal.h"
#include "a_specialspot.h"
#include "templates.h"
#include "m_bbox.h"
#include "portal.h"
#include "d_player.h"
#include "p_maputl.h"
#include "serializer.h"
#include "g_shared/a_pickups.h"
#include "vm.h"
// Include all the other Doom stuff here to reduce compile time
#include "a_bossbrain.cpp"
#include "a_doomweaps.cpp"
#include "a_painelemental.cpp"
#include "a_scriptedmarine.cpp"

View file

@ -1,743 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "s_sound.h"
#include "m_random.h"
#include "a_pickups.h"
#include "d_player.h"
#include "p_pspr.h"
#include "p_local.h"
#include "gstrings.h"
#include "p_effect.h"
#include "gi.h"
#include "templates.h"
#include "vm.h"
#include "doomstat.h"
*/
static FRandom pr_punch ("Punch");
static FRandom pr_saw ("Saw");
static FRandom pr_fireshotgun2 ("FireSG2");
static FRandom pr_fireplasma ("FirePlasma");
static FRandom pr_firerail ("FireRail");
static FRandom pr_bfgspray ("BFGSpray");
static FRandom pr_oldbfg ("OldBFG");
//
// A_Punch
//
DEFINE_ACTION_FUNCTION(AActor, A_Punch)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle pitch;
FTranslatedLineTarget t;
if (self->player != NULL)
{
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && !(weapon->WeaponFlags & WIF_DEHAMMO) && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
}
damage = (pr_punch()%10+1)<<1;
if (self->FindInventory<APowerStrength>())
damage *= 10;
angle = self->Angles.Yaw + pr_punch.Random2() * (5.625 / 256);
pitch = P_AimLineAttack (self, angle, MELEERANGE);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, LAF_ISMELEEATTACK, &t);
// turn to face target
if (t.linetarget)
{
S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
self->Angles.Yaw = t.angleFromSource;
}
return 0;
}
//
// A_FirePistol
//
DEFINE_ACTION_FUNCTION(AActor, A_FirePistol)
{
PARAM_ACTION_PROLOGUE(AActor);
bool accurate;
if (self->player != nullptr)
{
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != nullptr && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
P_SetPsprite(self->player, PSP_FLASH, weapon->FindState(NAME_Flash), true);
}
self->player->mo->PlayAttacking2 ();
accurate = !self->player->refire;
}
else
{
accurate = true;
}
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
P_GunShot (self, accurate, PClass::FindActor(NAME_BulletPuff), P_BulletSlope (self));
return 0;
}
//
// A_Saw
//
enum SAW_Flags
{
SF_NORANDOM = 1,
SF_RANDOMLIGHTMISS = 2,
SF_RANDOMLIGHTHIT = 4,
SF_NOUSEAMMOMISS = 8,
SF_NOUSEAMMO = 16,
SF_NOPULLIN = 32,
SF_NOTURN = 64,
SF_STEALARMOR = 128,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_SOUND_DEF (fullsound)
PARAM_SOUND_DEF (hitsound)
PARAM_INT_DEF (damage)
PARAM_CLASS_DEF (pufftype, AActor)
PARAM_INT_DEF (flags)
PARAM_FLOAT_DEF (range)
PARAM_ANGLE_DEF (spread_xy)
PARAM_ANGLE_DEF (spread_z)
PARAM_FLOAT_DEF (lifesteal)
PARAM_INT_DEF (lifestealmax)
PARAM_CLASS_DEF (armorbonustype, ABasicArmorBonus)
DAngle angle;
DAngle slope;
player_t *player;
FTranslatedLineTarget t;
int actualdamage;
if (NULL == (player = self->player))
{
return 0;
}
if (pufftype == NULL)
{
pufftype = PClass::FindActor(NAME_BulletPuff);
}
if (damage == 0)
{
damage = 2;
}
if (!(flags & SF_NORANDOM))
{
damage *= (pr_saw()%10+1);
}
if (range == 0)
{
range = SAWRANGE;
}
angle = self->Angles.Yaw + spread_xy * (pr_saw.Random2() / 255.);
slope = P_AimLineAttack (self, angle, range, &t) + spread_z * (pr_saw.Random2() / 255.);
AWeapon *weapon = self->player->ReadyWeapon;
if ((weapon != NULL) && !(flags & SF_NOUSEAMMO) && !(!t.linetarget && (flags & SF_NOUSEAMMOMISS)) && !(weapon->WeaponFlags & WIF_DEHAMMO) && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
P_LineAttack (self, angle, range, slope, damage, NAME_Melee, pufftype, false, &t, &actualdamage);
if (!t.linetarget)
{
if ((flags & SF_RANDOMLIGHTMISS) && (pr_saw() > 64))
{
player->extralight = !player->extralight;
}
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
return 0;
}
if (flags & SF_RANDOMLIGHTHIT)
{
int randVal = pr_saw();
if (randVal < 64)
{
player->extralight = 0;
}
else if (randVal < 160)
{
player->extralight = 1;
}
else
{
player->extralight = 2;
}
}
if (lifesteal && !(t.linetarget->flags5 & MF5_DONTDRAIN))
{
if (flags & SF_STEALARMOR)
{
if (armorbonustype == NULL)
{
armorbonustype = dyn_cast<ABasicArmorBonus::MetaClass>(PClass::FindClass("ArmorBonus"));
}
if (armorbonustype != NULL)
{
assert(armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus)));
ABasicArmorBonus *armorbonus = static_cast<ABasicArmorBonus *>(Spawn(armorbonustype));
armorbonus->SaveAmount = int(armorbonus->SaveAmount * actualdamage * lifesteal);
armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax;
armorbonus->flags |= MF_DROPPED;
armorbonus->ClearCounters();
if (!armorbonus->CallTryPickup (self))
{
armorbonus->Destroy ();
}
}
}
else
{
P_GiveBody (self, int(actualdamage * lifesteal), lifestealmax);
}
}
S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
// turn to face target
if (!(flags & SF_NOTURN))
{
DAngle anglediff = deltaangle(self->Angles.Yaw, t.angleFromSource);
if (anglediff < 0.0)
{
if (anglediff < -4.5)
self->Angles.Yaw = angle + 90.0 / 21;
else
self->Angles.Yaw -= 4.5;
}
else
{
if (anglediff > 4.5)
self->Angles.Yaw = angle - 90.0 / 21;
else
self->Angles.Yaw += 4.5;
}
}
if (!(flags & SF_NOPULLIN))
self->flags |= MF_JUSTATTACKED;
return 0;
}
//
// A_FireShotgun
//
DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun)
{
PARAM_ACTION_PROLOGUE(AActor);
int i;
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
S_Sound (self, CHAN_WEAPON, "weapons/shotgf", 1, ATTN_NORM);
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != nullptr && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
P_SetPsprite(player, PSP_FLASH, weapon->FindState(NAME_Flash), true);
}
player->mo->PlayAttacking2 ();
DAngle pitch = P_BulletSlope (self);
for (i = 0; i < 7; i++)
{
P_GunShot (self, false, PClass::FindActor(NAME_BulletPuff), pitch);
}
return 0;
}
//
// A_FireShotgun2
//
DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun2)
{
PARAM_ACTION_PROLOGUE(AActor);
int i;
DAngle angle;
int damage;
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
S_Sound (self, CHAN_WEAPON, "weapons/sshotf", 1, ATTN_NORM);
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != nullptr && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 2))
return 0;
P_SetPsprite(player, PSP_FLASH, weapon->FindState(NAME_Flash), true);
}
player->mo->PlayAttacking2 ();
DAngle pitch = P_BulletSlope (self);
for (i=0 ; i<20 ; i++)
{
damage = 5*(pr_fireshotgun2()%3+1);
angle = self->Angles.Yaw + pr_fireshotgun2.Random2() * (11.25 / 256);
// Doom adjusts the bullet slope by shifting a random number [-255,255]
// left 5 places. At 2048 units away, this means the vertical position
// of the shot can deviate as much as 255 units from nominal. So using
// some simple trigonometry, that means the vertical angle of the shot
// can deviate by as many as ~7.097 degrees or ~84676099 BAMs.
P_LineAttack (self,
angle,
PLAYERMISSILERANGE,
pitch + pr_fireshotgun2.Random2() * (7.097 / 256), damage,
NAME_Hitscan, NAME_BulletPuff);
}
return 0;
}
//------------------------------------------------------------------------------------
//
// Setting a random flash like some of Doom's weapons can easily crash when the
// definition is overridden incorrectly so let's check that the state actually exists.
// Be aware though that this will not catch all DEHACKED related problems. But it will
// find all DECORATE related ones.
//
//------------------------------------------------------------------------------------
void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int index)
{
PClassActor *cls = weapon->GetClass();
while (cls != RUNTIME_CLASS(AWeapon))
{
if (flashstate >= cls->OwnedStates && flashstate < cls->OwnedStates + cls->NumOwnedStates)
{
// The flash state belongs to this class.
// Now let's check if the actually wanted state does also
if (flashstate + index < cls->OwnedStates + cls->NumOwnedStates)
{
// we're ok so set the state
P_SetPsprite(player, PSP_FLASH, flashstate + index, true);
return;
}
else
{
// oh, no! The state is beyond the end of the state table so use the original flash state.
P_SetPsprite(player, PSP_FLASH, flashstate, true);
return;
}
}
// try again with parent class
cls = static_cast<PClassActor *>(cls->ParentClass);
}
// if we get here the state doesn't seem to belong to any class in the inheritance chain
// This can happen with Dehacked if the flash states are remapped.
// The only way to check this would be to go through all Dehacked modifiable actors, convert
// their states into a single flat array and find the correct one.
// Rather than that, just check to make sure it belongs to something.
if (FState::StaticFindStateOwner(flashstate + index) == NULL)
{ // Invalid state. With no index offset, it should at least be valid.
index = 0;
}
P_SetPsprite(player, PSP_FLASH, flashstate + index, true);
}
//
// A_FireCGun
//
DEFINE_ACTION_FUNCTION(AActor, A_FireCGun)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (self == nullptr || nullptr == (player = self->player))
{
return 0;
}
AWeapon *weapon = player->ReadyWeapon;
if (weapon != nullptr && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
FState *flash = weapon->FindState(NAME_Flash);
if (flash != nullptr)
{
// [RH] Fix for Sparky's messed-up Dehacked patch! Blargh!
FState * atk = weapon->FindState(NAME_Fire);
int theflash = clamp (int(player->GetPSprite(PSP_WEAPON)->GetState() - atk), 0, 1);
if (flash[theflash].sprite != flash->sprite)
{
theflash = 0;
}
P_SetSafeFlash (weapon, player, flash, theflash);
}
}
player->mo->PlayAttacking2 ();
P_GunShot (self, !player->refire, PClass::FindActor(NAME_BulletPuff), P_BulletSlope (self));
return 0;
}
//
// A_FireMissile
//
DEFINE_ACTION_FUNCTION(AActor, A_FireMissile)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
}
P_SpawnPlayerMissile (self, PClass::FindActor("Rocket"));
return 0;
}
//
// A_FireSTGrenade: not exactly backported from ST, but should work the same
//
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireSTGrenade)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_CLASS_DEF(grenade, AActor);
player_t *player;
if (grenade == NULL)
return 0;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
// Temporarily raise the pitch to send the grenade slightly upwards
DAngle SavedPlayerPitch = self->Angles.Pitch;
self->Angles.Pitch -= 6.328125; //(1152 << F RACBITS);
P_SpawnPlayerMissile(self, grenade);
self->Angles.Pitch = SavedPlayerPitch;
return 0;
}
//
// A_FirePlasma
//
DEFINE_ACTION_FUNCTION(AActor, A_FirePlasma)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
FState *flash = weapon->FindState(NAME_Flash);
if (flash != NULL)
{
P_SetSafeFlash(weapon, player, flash, (pr_fireplasma()&1));
}
}
P_SpawnPlayerMissile (self, PClass::FindActor("PlasmaBall"));
return 0;
}
//
// [RH] A_FireRailgun
//
static void FireRailgun(AActor *self, int offset_xy, bool fromweapon)
{
int damage;
player_t *player;
if (NULL == (player = self->player))
{
return;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && fromweapon)
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return;
FState *flash = weapon->FindState(NAME_Flash);
if (flash != NULL)
{
P_SetSafeFlash(weapon, player, flash, (pr_firerail()&1));
}
}
damage = deathmatch ? 100 : 150;
FRailParams p;
p.source = self;
p.damage = damage;
p.offset_xy = offset_xy;
P_RailAttack (&p);
}
DEFINE_ACTION_FUNCTION(AActor, A_FireRailgun)
{
PARAM_ACTION_PROLOGUE(AActor);
FireRailgun(self, 0, ACTION_CALL_FROM_PSPRITE());
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_FireRailgunRight)
{
PARAM_ACTION_PROLOGUE(AActor);
FireRailgun(self, 10, ACTION_CALL_FROM_PSPRITE());
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_FireRailgunLeft)
{
PARAM_ACTION_PROLOGUE(AActor);
FireRailgun(self, -10, ACTION_CALL_FROM_PSPRITE());
return 0;
}
//
// A_FireBFG
//
DEFINE_ACTION_FUNCTION(AActor, A_FireBFG)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && ACTION_CALL_FROM_PSPRITE())
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, deh.BFGCells))
return 0;
}
P_SpawnPlayerMissile (self, 0, 0, 0, PClass::FindActor("BFGBall"), self->Angles.Yaw, NULL, NULL, !!(dmflags2 & DF2_NO_FREEAIMBFG));
return 0;
}
//
// A_BFGSpray
// Spawn a BFG explosion on every monster in view
//
enum BFG_Flags
{
BFGF_HURTSOURCE = 1,
BFGF_MISSILEORIGIN = 2,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS_DEF (spraytype, AActor)
PARAM_INT_DEF (numrays)
PARAM_INT_DEF (damagecnt)
PARAM_ANGLE_DEF (angle)
PARAM_FLOAT_DEF (distance)
PARAM_ANGLE_DEF (vrange)
PARAM_INT_DEF (defdamage)
PARAM_INT_DEF (flags)
int i;
int j;
int damage;
DAngle an;
FTranslatedLineTarget t;
AActor *originator;
if (spraytype == NULL) spraytype = PClass::FindActor("BFGExtra");
if (numrays <= 0) numrays = 40;
if (damagecnt <= 0) damagecnt = 15;
if (angle == 0) angle = 90.;
if (distance <= 0) distance = 16 * 64;
if (vrange == 0) vrange = 32.;
// [RH] Don't crash if no target
if (!self->target)
return 0;
// [XA] Set the originator of the rays to the projectile (self) if
// the new flag is set, else set it to the player (self->target)
originator = (flags & BFGF_MISSILEORIGIN) ? self : (AActor *)(self->target);
// offset angles from its attack angle
for (i = 0; i < numrays; i++)
{
an = self->Angles.Yaw - angle / 2 + angle / numrays*i;
P_AimLineAttack(originator, an, distance, &t, vrange);
if (t.linetarget != NULL)
{
AActor *spray = Spawn(spraytype, t.linetarget->PosPlusZ(t.linetarget->Height / 4), ALLOW_REPLACE);
int dmgFlags = 0;
FName dmgType = NAME_BFGSplash;
if (spray != NULL)
{
if ((spray->flags6 & MF6_MTHRUSPECIES && self->target->GetSpecies() == t.linetarget->GetSpecies()) ||
(!(flags & BFGF_HURTSOURCE) && self->target == t.linetarget)) // [XA] Don't hit oneself unless we say so.
{
spray->Destroy(); // [MC] Remove it because technically, the spray isn't trying to "hit" them.
continue;
}
if (spray->flags5 & MF5_PUFFGETSOWNER) spray->target = self->target;
if (spray->flags3 & MF3_FOILINVUL) dmgFlags |= DMG_FOILINVUL;
if (spray->flags7 & MF7_FOILBUDDHA) dmgFlags |= DMG_FOILBUDDHA;
dmgType = spray->DamageType;
}
if (defdamage == 0)
{
damage = 0;
for (j = 0; j < damagecnt; ++j)
damage += (pr_bfgspray() & 7) + 1;
}
else
{
// if this is used, damagecnt will be ignored
damage = defdamage;
}
int newdam = P_DamageMobj(t.linetarget, originator, self->target, damage, dmgType, dmgFlags|DMG_USEANGLE, t.angleFromSource.Degrees);
P_TraceBleed(newdam > 0 ? newdam : damage, &t, self);
}
}
return 0;
}
//
// A_FireOldBFG
//
// This function emulates Doom's Pre-Beta BFG
// By Lee Killough 6/6/98, 7/11/98, 7/19/98, 8/20/98
//
// This code may not be used in other mods without appropriate credit given.
// Code leeches will be telefragged.
DEFINE_ACTION_FUNCTION(AActor, A_FireOldBFG)
{
PARAM_ACTION_PROLOGUE(AActor);
PClassActor *plasma[] = { PClass::FindActor("PlasmaBall1"), PClass::FindActor("PlasmaBall2") };
AActor * mo = NULL;
player_t *player;
bool doesautoaim = false;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (!ACTION_CALL_FROM_PSPRITE()) weapon = NULL;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return 0;
doesautoaim = !(weapon->WeaponFlags & WIF_NOAUTOAIM);
weapon->WeaponFlags |= WIF_NOAUTOAIM; // No autoaiming that gun
}
self->player->extralight = 2;
// Save values temporarily
DAngle SavedPlayerAngle = self->Angles.Yaw;
DAngle SavedPlayerPitch = self->Angles.Pitch;
for (int i = 0; i < 2; i++) // Spawn two plasma balls in sequence
{
self->Angles.Yaw += ((pr_oldbfg()&127) - 64) * (90./768);
self->Angles.Pitch += ((pr_oldbfg()&127) - 64) * (90./640);
mo = P_SpawnPlayerMissile (self, plasma[i]);
// Restore saved values
self->Angles.Yaw = SavedPlayerAngle;
self->Angles.Pitch = SavedPlayerPitch;
}
if (doesautoaim && weapon != NULL)
{ // Restore autoaim setting
weapon->WeaponFlags &= ~WIF_NOAUTOAIM;
}
return 0;
}

View file

@ -1,158 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_enemy.h"
#include "p_local.h"
#include "a_action.h"
#include "templates.h"
#include "m_bbox.h"
#include "vm.h"
#include "doomstat.h"
*/
enum PA_Flags
{
PAF_NOSKULLATTACK = 1,
PAF_AIMFACING = 2,
PAF_NOTARGET = 4,
};
//
// A_PainShootSkull
// Spawn a lost soul and launch it at the target
//
void A_PainShootSkull (VMFrameStack *stack, AActor *self, DAngle Angle, PClassActor *spawntype, int flags = 0, int limit = -1)
{
AActor *other;
double prestep;
if (spawntype == NULL) spawntype = PClass::FindActor("LostSoul");
assert(spawntype != NULL);
if (self->DamageType == NAME_Massacre) return;
// [RH] check to make sure it's not too close to the ceiling
if (self->Top() + 8 > self->ceilingz)
{
if (self->flags & MF_FLOAT)
{
self->Vel.Z -= 2;
self->flags |= MF_INFLOAT;
self->flags4 |= MF4_VFRICTION;
}
return;
}
// [RH] make this optional
if (limit == -1 && (i_compatflags & COMPATF_LIMITPAIN))
limit = 21;
if (limit)
{
// count total number of skulls currently on the level
// if there are already 21 skulls on the level, don't spit another one
int count = limit;
FThinkerIterator iterator (spawntype);
DThinker *othink;
while ( (othink = iterator.Next ()) )
{
if (--count == 0)
return;
}
}
// okay, there's room for another one
prestep = 4 + (self->radius + GetDefaultByType(spawntype)->radius) * 1.5;
// NOTE: The following code contains some advance work for line-to-line portals which is currenty inactive.
DVector2 dist = Angle.ToVector(prestep);
DVector3 pos = self->Vec3Offset(dist.X, dist.Y, 8., true);
DVector3 src = self->Pos();
for (int i = 0; i < 2; i++)
{
// Check whether the Lost Soul is being fired through a 1-sided // phares
// wall or an impassible line, or a "monsters can't cross" line.// |
// If it is, then we don't allow the spawn. // V
FBoundingBox box(MIN(src.X, pos.X), MIN(src.Y, pos.Y), MAX(src.X, pos.X), MAX(src.Y, pos.Y));
FBlockLinesIterator it(box);
line_t *ld;
bool inportal = false;
while ((ld = it.Next()))
{
if (ld->isLinePortal() && i == 0)
{
if (P_PointOnLineSidePrecise(src, ld) == 0 &&
P_PointOnLineSidePrecise(pos, ld) == 1)
{
// crossed a portal line from front to back, we need to repeat the check on the other side as well.
inportal = true;
}
}
else if (!(ld->flags & ML_TWOSIDED) ||
(ld->flags & (ML_BLOCKING | ML_BLOCKMONSTERS | ML_BLOCKEVERYTHING)))
{
if (box.inRange(ld))
{
if (P_PointOnLineSidePrecise(src, ld) != P_PointOnLineSidePrecise(pos, ld))
return; // line blocks trajectory // ^
}
}
}
if (!inportal) break;
// recalculate position and redo the check on the other side of the portal
pos = self->Vec3Offset(dist.X, dist.Y, 8.);
src.X = pos.X - dist.X;
src.Y = pos.Y - dist.Y;
}
other = Spawn (spawntype, pos, ALLOW_REPLACE);
// Check to see if the new Lost Soul's z value is above the
// ceiling of its new sector, or below the floor. If so, kill it.
if (other->Top() > other->Sector->HighestCeilingAt(other) ||
other->Z() < other->Sector->LowestFloorAt(other))
{
// kill it immediately
P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^
return; // |
} // phares
// Check for movements.
if (!P_CheckPosition (other, other->Pos()))
{
// kill it immediately
P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);
return;
}
// [RH] Lost souls hate the same things as their pain elementals
other->CopyFriendliness (self, !(flags & PAF_NOTARGET));
if (!(flags & PAF_NOSKULLATTACK))
{
DECLARE_VMFUNC(AActor, A_SkullAttack);
CallAction(stack, A_SkullAttack, other);
}
}
DEFINE_ACTION_FUNCTION(AActor, A_PainShootSkull)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(spawntype, AActor);
PARAM_FLOAT(angle);
PARAM_INT_DEF(flags);
PARAM_INT_DEF(limit);
A_PainShootSkull(stack, self, angle, spawntype, flags, limit);
return 0;
}

View file

@ -1,654 +0,0 @@
/*
#include "actor.h"
#include "p_enemy.h"
#include "a_action.h"
#include "m_random.h"
#include "p_local.h"
#include "a_doomglobal.h"
#include "s_sound.h"
#include "r_data/r_translate.h"
#include "vm.h"
#include "g_level.h"
*/
#define MARINE_PAIN_CHANCE 160
static FRandom pr_m_refire ("SMarineRefire");
static FRandom pr_m_punch ("SMarinePunch");
static FRandom pr_m_gunshot ("SMarineGunshot");
static FRandom pr_m_saw ("SMarineSaw");
static FRandom pr_m_fireshotgun2 ("SMarineFireSSG");
IMPLEMENT_CLASS(AScriptedMarine, false, false, false, false)
void AScriptedMarine::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
auto def = (AScriptedMarine*)GetDefault();
arc.Sprite("spriteoverride", SpriteOverride, &def->SpriteOverride)
("currentweapon", CurrentWeapon, def->CurrentWeapon);
}
void AScriptedMarine::Activate (AActor *activator)
{
if (flags2 & MF2_DORMANT)
{
flags2 &= ~MF2_DORMANT;
tics = 1;
}
}
void AScriptedMarine::Deactivate (AActor *activator)
{
if (!(flags2 & MF2_DORMANT))
{
flags2 |= MF2_DORMANT;
tics = -1;
}
}
bool AScriptedMarine::GetWeaponStates(int weap, FState *&melee, FState *&missile)
{
static ENamedName WeaponNames[] =
{
NAME_None,
NAME_Fist,
NAME_Berserk,
NAME_Chainsaw,
NAME_Pistol,
NAME_Shotgun,
NAME_SSG,
NAME_Chaingun,
NAME_Rocket,
NAME_Plasma,
NAME_Railgun,
NAME_BFG
};
if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy;
melee = FindState(NAME_Melee, WeaponNames[weap], true);
missile = FindState(NAME_Missile, WeaponNames[weap], true);
return melee != NULL || missile != NULL;
}
void AScriptedMarine::BeginPlay ()
{
Super::BeginPlay ();
// Set the current weapon
for(int i=WEAPON_Dummy; i<=WEAPON_BFG; i++)
{
FState *melee, *missile;
if (GetWeaponStates(i, melee, missile))
{
if (melee == MeleeState && missile == MissileState)
{
CurrentWeapon = i;
}
}
}
}
void AScriptedMarine::Tick ()
{
Super::Tick ();
// Override the standard sprite, if desired
if (SpriteOverride != 0 && sprite == SpawnState->sprite)
{
sprite = SpriteOverride;
}
if (special1 != 0)
{
if (CurrentWeapon == WEAPON_SuperShotgun)
{ // Play SSG reload sounds
int ticks = level.maptime - special1;
if (ticks < 47)
{
switch (ticks)
{
case 14:
S_Sound (this, CHAN_WEAPON, "weapons/sshoto", 1, ATTN_NORM);
break;
case 28:
S_Sound (this, CHAN_WEAPON, "weapons/sshotl", 1, ATTN_NORM);
break;
case 41:
S_Sound (this, CHAN_WEAPON, "weapons/sshotc", 1, ATTN_NORM);
break;
}
}
else
{
special1 = 0;
}
}
else
{ // Wait for a long refire time
if (level.maptime >= special1)
{
special1 = 0;
}
else
{
flags |= MF_JUSTATTACKED;
}
}
}
}
//============================================================================
//
// A_M_Refire
//
//============================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Refire)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(ignoremissile);
if (self->target == NULL || self->target->health <= 0)
{
if (self->MissileState && pr_m_refire() < 160)
{ // Look for a new target most of the time
if (P_LookForPlayers (self, true, NULL) && P_CheckMissileRange (self))
{ // Found somebody new and in range, so don't stop shooting
return 0;
}
}
self->SetState (self->state + 1);
return 0;
}
if (((ignoremissile || self->MissileState == NULL) && !self->CheckMeleeRange ()) ||
!P_CheckSight (self, self->target) ||
pr_m_refire() < 4) // Small chance of stopping even when target not dead
{
self->SetState (self->state + 1);
}
return 0;
}
//============================================================================
//
// A_M_SawRefire
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_SawRefire)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL || self->target->health <= 0)
{
self->SetState (self->state + 1);
return 0;
}
if (!self->CheckMeleeRange ())
{
self->SetState (self->state + 1);
}
return 0;
}
//============================================================================
//
// A_MarineNoise
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MarineNoise)
{
PARAM_SELF_PROLOGUE(AActor);
if (static_cast<AScriptedMarine *>(self)->CurrentWeapon == AScriptedMarine::WEAPON_Chainsaw)
{
S_Sound (self, CHAN_WEAPON, "weapons/sawidle", 1, ATTN_NORM);
}
return 0;
}
//============================================================================
//
// A_MarineChase
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MarineChase)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_MarineNoise, self);
A_Chase (stack, self);
return 0;
}
//============================================================================
//
// A_MarineLook
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MarineLook)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_MarineNoise, self);
CALL_ACTION(A_Look, self);
return 0;
}
//============================================================================
//
// A_M_Saw
//
//============================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Saw)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_SOUND_DEF (fullsound)
PARAM_SOUND_DEF (hitsound)
PARAM_INT_DEF (damage)
PARAM_CLASS_DEF (pufftype, AActor)
if (self->target == NULL)
return 0;
if (pufftype == NULL)
{
pufftype = PClass::FindActor(NAME_BulletPuff);
}
if (damage == 0)
{
damage = 2;
}
A_FaceTarget (self);
if (self->CheckMeleeRange ())
{
DAngle angle;
FTranslatedLineTarget t;
damage *= (pr_m_saw()%10+1);
angle = self->Angles.Yaw + pr_m_saw.Random2() * (5.625 / 256);
P_LineAttack (self, angle, SAWRANGE,
P_AimLineAttack (self, angle, SAWRANGE), damage,
NAME_Melee, pufftype, false, &t);
if (!t.linetarget)
{
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
return 0;
}
S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
// turn to face target
angle = t.angleFromSource;
DAngle anglediff = deltaangle(self->Angles.Yaw, angle);
if (anglediff < 0.0)
{
if (anglediff < -4.5)
self->Angles.Yaw = angle + 90.0 / 21;
else
self->Angles.Yaw -= 4.5;
}
else
{
if (anglediff > 4.5)
self->Angles.Yaw = angle - 90.0 / 21;
else
self->Angles.Yaw += 4.5;
}
}
else
{
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
}
//A_Chase (self);
return 0;
}
//============================================================================
//
// A_M_Punch
//
//============================================================================
static void MarinePunch(AActor *self, int damagemul)
{
DAngle angle;
int damage;
DAngle pitch;
FTranslatedLineTarget t;
if (self->target == NULL)
return;
damage = ((pr_m_punch()%10+1) << 1) * damagemul;
A_FaceTarget (self);
angle = self->Angles.Yaw + pr_m_punch.Random2() * (5.625 / 256);
pitch = P_AimLineAttack (self, angle, MELEERANGE);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true, &t);
// turn to face target
if (t.linetarget)
{
S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
self->Angles.Yaw = t.angleFromSource;
}
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Punch)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(mult);
MarinePunch(self, mult);
return 0;
}
//============================================================================
//
// P_GunShot2
//
//============================================================================
void P_GunShot2 (AActor *mo, bool accurate, DAngle pitch, PClassActor *pufftype)
{
DAngle angle;
int damage;
damage = 5*(pr_m_gunshot()%3+1);
angle = mo->Angles.Yaw;
if (!accurate)
{
angle += pr_m_gunshot.Random2() * (5.625 / 256);
}
P_LineAttack (mo, angle, MISSILERANGE, pitch, damage, NAME_Hitscan, pufftype);
}
//============================================================================
//
// A_M_FirePistol
//
//============================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_FirePistol)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL(accurate);
if (self->target == NULL)
return 0;
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, accurate, P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE),
PClass::FindActor(NAME_BulletPuff));
return 0;
}
//============================================================================
//
// A_M_FireShotgun
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle pitch;
if (self->target == NULL)
return 0;
S_Sound (self, CHAN_WEAPON, "weapons/shotgf", 1, ATTN_NORM);
A_FaceTarget (self);
pitch = P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE);
for (int i = 0; i < 7; ++i)
{
P_GunShot2 (self, false, pitch, PClass::FindActor(NAME_BulletPuff));
}
self->special1 = level.maptime + 27;
return 0;
}
//============================================================================
//
// A_M_CheckAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_CheckAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->special1 != 0 || self->target == NULL)
{
self->SetState (self->FindState("SkipAttack"));
}
else
{
A_FaceTarget (self);
}
return 0;
}
//============================================================================
//
// A_M_FireShotgun2
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun2)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle pitch;
if (self->target == NULL)
return 0;
S_Sound (self, CHAN_WEAPON, "weapons/sshotf", 1, ATTN_NORM);
A_FaceTarget (self);
pitch = P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE);
for (int i = 0; i < 20; ++i)
{
int damage = 5*(pr_m_fireshotgun2()%3+1);
DAngle angle = self->Angles.Yaw + pr_m_fireshotgun2.Random2() * (11.25 / 256);
P_LineAttack (self, angle, MISSILERANGE,
pitch + pr_m_fireshotgun2.Random2() * (7.097 / 256), damage,
NAME_Hitscan, NAME_BulletPuff);
}
self->special1 = level.maptime;
return 0;
}
//============================================================================
//
// A_M_FireCGun
//
//============================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_FireCGun)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL(accurate);
if (self->target == NULL)
return 0;
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, accurate, P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE),
PClass::FindActor(NAME_BulletPuff));
return 0;
}
//============================================================================
//
// A_M_FireMissile
//
// Giving a marine a rocket launcher is probably a bad idea unless you pump
// up his health, because he's just as likely to kill himself as he is to
// kill anything else with it.
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_FireMissile)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
return 0;
if (self->CheckMeleeRange ())
{ // If too close, punch it
MarinePunch(self, 1);
}
else
{
A_FaceTarget (self);
P_SpawnMissile (self, self->target, PClass::FindActor("Rocket"));
}
return 0;
}
//============================================================================
//
// A_M_FireRailgun
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_FireRailgun)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
return 0;
CALL_ACTION(A_MonsterRail, self);
self->special1 = level.maptime + 50;
return 0;
}
//============================================================================
//
// A_M_FirePlasma
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_FirePlasma)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
return 0;
A_FaceTarget (self);
P_SpawnMissile (self, self->target, PClass::FindActor("PlasmaBall"));
self->special1 = level.maptime + 20;
return 0;
}
//============================================================================
//
// A_M_BFGsound
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_BFGsound)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
return 0;
if (self->special1 != 0)
{
self->SetState (self->SeeState);
}
else
{
A_FaceTarget (self);
S_Sound (self, CHAN_WEAPON, "weapons/bfgf", 1, ATTN_NORM);
// Don't interrupt the firing sequence
self->PainChance = 0;
}
return 0;
}
//============================================================================
//
// A_M_FireBFG
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_FireBFG)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
return 0;
A_FaceTarget (self);
P_SpawnMissile (self, self->target, PClass::FindActor("BFGBall"));
self->special1 = level.maptime + 30;
self->PainChance = MARINE_PAIN_CHANCE;
return 0;
}
//---------------------------------------------------------------------------
void AScriptedMarine::SetWeapon (EMarineWeapon type)
{
if (GetWeaponStates(type, MeleeState, MissileState))
{
static const char *classes[] = {
"ScriptedMarine",
"MarineFist",
"MarineBerserk",
"MarineChainsaw",
"MarinePistol",
"MarineShotgun",
"MarineSSG",
"MarineChaingun",
"MarineRocket",
"MarinePlasma",
"MarineRailgun",
"MarineBFG"
};
const PClass *cls = PClass::FindClass(classes[type]);
if (cls != NULL)
DecalGenerator = GetDefaultByType(cls)->DecalGenerator;
else
DecalGenerator = NULL;
}
}
void AScriptedMarine::SetSprite (PClassActor *source)
{
if (source == NULL)
{ // A valid actor class wasn't passed, so use the standard sprite
SpriteOverride = sprite = GetClass()->OwnedStates[0].sprite;
// Copy the standard scaling
Scale = GetDefault()->Scale;
}
else
{ // Use the same sprite and scaling the passed class spawns with
SpriteOverride = sprite = GetDefaultByType (source)->SpawnState->sprite;
Scale = GetDefaultByType(source)->Scale;
}
}

View file

@ -478,15 +478,15 @@ CCMD (useflechette)
{ // Select from one of arti_poisonbag1-3, whichever the player has
static const ENamedName bagnames[3] =
{
NAME_ArtiPoisonBag3, // use type 3 first because that's the default when the player has none specified.
NAME_ArtiPoisonBag1,
NAME_ArtiPoisonBag2,
NAME_ArtiPoisonBag3
NAME_ArtiPoisonBag2
};
if (who == NULL)
return;
PClassActor *type = GetFlechetteType(who);
PClassActor *type = who->FlechetteType;
if (type != NULL)
{
AInventory *item;
@ -497,7 +497,7 @@ CCMD (useflechette)
}
}
// The default flechette could not be found. Try all 3 types then.
// The default flechette could not be found, or the player had no default. Try all 3 types then.
for (int j = 0; j < 3; ++j)
{
AInventory *item;
@ -1536,6 +1536,36 @@ static FPlayerStart *SelectRandomDeathmatchSpot (int playernum, unsigned int sel
return &deathmatchstarts[i];
}
DEFINE_ACTION_FUNCTION(DObject, G_PickDeathmatchStart)
{
PARAM_PROLOGUE;
unsigned int selections = deathmatchstarts.Size();
DVector3 pos;
int angle;
if (selections == 0)
{
angle = INT_MAX;
pos = DVector3(0, 0, 0);
}
else
{
unsigned int i = pr_dmspawn() % selections;
angle = deathmatchstarts[i].angle;
pos = deathmatchstarts[i].pos;
}
if (numret > 1)
{
ret[1].SetInt(angle);
numret = 2;
}
if (numret > 0)
{
ret[0].SetVector(pos);
}
return numret;
}
void G_DeathMatchSpawnPlayer (int playernum)
{
unsigned int selections;
@ -1577,6 +1607,7 @@ void G_DeathMatchSpawnPlayer (int playernum)
if (mo != NULL) P_PlayerStartStomp(mo);
}
//
// G_PickPlayerStart
//
@ -1614,6 +1645,24 @@ FPlayerStart *G_PickPlayerStart(int playernum, int flags)
return &playerstarts[playernum];
}
DEFINE_ACTION_FUNCTION(DObject, G_PickPlayerStart)
{
PARAM_PROLOGUE;
PARAM_INT(playernum);
PARAM_INT_DEF(flags);
auto ps = G_PickPlayerStart(playernum, flags);
if (numret > 1)
{
ret[1].SetInt(ps? ps->angle : 0);
numret = 2;
}
if (numret > 0)
{
ret[0].SetVector(ps ? ps->pos : DVector3(0, 0, 0));
}
return numret;
}
//
// G_QueueBody
//

View file

@ -1,233 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "a_pickups.h"
#include "p_local.h"
#include "a_sharedglobal.h"
#include "p_enemy.h"
#include "d_event.h"
#include "gstrings.h"
#include "vm.h"
*/
void P_UpdateBeak (AActor *actor);
static FRandom pr_chickenplayerthink ("ChickenPlayerThink");
static FRandom pr_chicattack ("ChicAttack");
static FRandom pr_feathers ("Feathers");
static FRandom pr_beakatkpl1 ("BeakAtkPL1");
static FRandom pr_beakatkpl2 ("BeakAtkPL2");
class AChickenPlayer : public APlayerPawn
{
DECLARE_CLASS (AChickenPlayer, APlayerPawn)
public:
void MorphPlayerThink ();
};
IMPLEMENT_CLASS(AChickenPlayer, false, false, false, false)
void AChickenPlayer::MorphPlayerThink ()
{
if (health > 0)
{ // Handle beak movement
P_UpdateBeak (this);
}
if (player->morphTics & 15)
{
return;
}
if (Vel.X == 0 && Vel.Y == 0 && pr_chickenplayerthink () < 160)
{ // Twitch view angle
Angles.Yaw += pr_chickenplayerthink.Random2() * (360. / 256. / 32.);
}
if ((Z() <= floorz) && (pr_chickenplayerthink() < 32))
{ // Jump and noise
Vel.Z += JumpZ;
FState * painstate = FindState(NAME_Pain);
if (painstate != NULL) SetState (painstate);
}
if (pr_chickenplayerthink () < 48)
{ // Just noise
S_Sound (this, CHAN_VOICE, "chicken/active", 1, ATTN_NORM);
}
}
//----------------------------------------------------------------------------
//
// PROC A_ChicAttack
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_ChicAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
{
return 0;
}
if (self->CheckMeleeRange())
{
int damage = 1 + (pr_chicattack() & 1);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Feathers
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Feathers)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
int count;
AActor *mo;
if (self->health > 0)
{ // Pain
count = pr_feathers() < 32 ? 2 : 1;
}
else
{ // Death
count = 5 + (pr_feathers()&3);
}
for (i = 0; i < count; i++)
{
mo = Spawn("Feather", self->PosPlusZ(20.), NO_REPLACE);
mo->target = self;
mo->Vel.X = pr_feathers.Random2() / 256.;
mo->Vel.Y = pr_feathers.Random2() / 256.;
mo->Vel.Z = 1. + pr_feathers() / 128.;
mo->SetState (mo->SpawnState + (pr_feathers()&7));
}
return 0;
}
//---------------------------------------------------------------------------
//
// PROC P_UpdateBeak
//
//---------------------------------------------------------------------------
void P_UpdateBeak (AActor *self)
{
DPSprite *pspr;
if (self->player != nullptr && (pspr = self->player->FindPSprite(PSP_WEAPON)) != nullptr)
{
pspr->y = WEAPONTOP + self->player->chickenPeck / 2;
}
}
//---------------------------------------------------------------------------
//
// PROC A_BeakRaise
//
//---------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BeakRaise)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP;
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetReadyState());
return 0;
}
//----------------------------------------------------------------------------
//
// PROC P_PlayPeck
//
//----------------------------------------------------------------------------
void P_PlayPeck (AActor *chicken)
{
S_Sound (chicken, CHAN_VOICE, "chicken/peck", 1, ATTN_NORM);
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL1
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL1)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
damage = 1 + (pr_beakatkpl1()&3);
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t);
if (t.linetarget)
{
player->mo->Angles.Yaw = t.angleFromSource;
}
P_PlayPeck (player->mo);
player->chickenPeck = 12;
player->GetPSprite(PSP_WEAPON)->Tics -= pr_beakatkpl1() & 7;
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL2
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL2)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
damage = pr_beakatkpl2.HitDice (4);
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t);
if (t.linetarget)
{
player->mo->Angles.Yaw = t.angleFromSource;
}
P_PlayPeck (player->mo);
player->chickenPeck = 12;
player->GetPSprite(PSP_WEAPON)->Tics -= pr_beakatkpl2()&3;
return 0;
}

View file

@ -1,327 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "p_enemy.h"
#include "a_action.h"
#include "s_sound.h"
#include "m_random.h"
#include "a_sharedglobal.h"
#include "gstrings.h"
#include "a_specialspot.h"
#include "vm.h"
#include "g_level.h"
*/
static FRandom pr_s2fx1 ("S2FX1");
static FRandom pr_scrc1atk ("Srcr1Attack");
static FRandom pr_dst ("D'SparilTele");
static FRandom pr_s2d ("Srcr2Decide");
static FRandom pr_s2a ("Srcr2Attack");
static FRandom pr_bluespark ("BlueSpark");
//----------------------------------------------------------------------------
//
// PROC A_Sor1Pain
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Sor1Pain)
{
PARAM_SELF_PROLOGUE(AActor);
self->special1 = 20; // Number of steps to walk fast
CALL_ACTION(A_Pain, self);
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Sor1Chase
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Sor1Chase)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->special1)
{
self->special1--;
self->tics -= 3;
}
A_Chase(stack, self);
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Srcr1Attack
//
// Sorcerer demon attack.
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Srcr1Attack)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
DAngle angle;
if (!self->target)
{
return 0;
}
S_Sound (self, CHAN_BODY, self->AttackSound, 1, ATTN_NORM);
if (self->CheckMeleeRange ())
{
int damage = pr_scrc1atk.HitDice (8);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
PClassActor *fx = PClass::FindActor("SorcererFX1");
if (self->health > (self->SpawnHealth()/3)*2)
{ // Spit one fireball
P_SpawnMissileZ (self, self->Z() + 48, self->target, fx );
}
else
{ // Spit three fireballs
mo = P_SpawnMissileZ (self, self->Z() + 48, self->target, fx);
if (mo != NULL)
{
angle = mo->Angles.Yaw;
P_SpawnMissileAngleZ(self, self->Z() + 48, fx, angle - 3, mo->Vel.Z);
P_SpawnMissileAngleZ(self, self->Z() + 48, fx, angle + 3, mo->Vel.Z);
}
if (self->health < self->SpawnHealth()/3)
{ // Maybe attack again
if (self->special1)
{ // Just attacked, so don't attack again
self->special1 = 0;
}
else
{ // Set state to attack again
self->special1 = 1;
self->SetState (self->FindState("Missile2"));
}
}
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_SorcererRise
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_SorcererRise)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
self->flags &= ~MF_SOLID;
mo = Spawn("Sorcerer2", self->Pos(), ALLOW_REPLACE);
mo->Translation = self->Translation;
mo->SetState (mo->FindState("Rise"));
mo->Angles.Yaw = self->Angles.Yaw;
mo->CopyFriendliness (self, true);
return 0;
}
//----------------------------------------------------------------------------
//
// PROC P_DSparilTeleport
//
//----------------------------------------------------------------------------
void P_DSparilTeleport (AActor *actor)
{
DVector3 prev;
AActor *mo;
AActor *spot;
DSpotState *state = DSpotState::GetSpotState();
if (state == NULL) return;
spot = state->GetSpotWithMinMaxDistance(PClass::FindActor("BossSpot"), actor->X(), actor->Y(), 128, 0);
if (spot == NULL) return;
prev = actor->Pos();
if (P_TeleportMove (actor, spot->Pos(), false))
{
mo = Spawn("Sorcerer2Telefade", prev, ALLOW_REPLACE);
if (mo) mo->Translation = actor->Translation;
S_Sound (mo, CHAN_BODY, "misc/teleport", 1, ATTN_NORM);
actor->SetState (actor->FindState("Teleport"));
S_Sound (actor, CHAN_BODY, "misc/teleport", 1, ATTN_NORM);
actor->SetZ(actor->floorz);
actor->Angles.Yaw = spot->Angles.Yaw;
actor->Vel.Zero();
}
}
//----------------------------------------------------------------------------
//
// PROC A_Srcr2Decide
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Srcr2Decide)
{
PARAM_SELF_PROLOGUE(AActor);
static const int chance[] =
{
192, 120, 120, 120, 64, 64, 32, 16, 0
};
unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8);
if (chanceindex >= countof(chance))
{
chanceindex = countof(chance) - 1;
}
if (pr_s2d() < chance[chanceindex])
{
P_DSparilTeleport (self);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Srcr2Attack
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Srcr2Attack)
{
PARAM_SELF_PROLOGUE(AActor);
int chance;
if (!self->target)
{
return 0;
}
S_Sound (self, CHAN_BODY, self->AttackSound, 1, ATTN_NONE);
if (self->CheckMeleeRange())
{
int damage = pr_s2a.HitDice (20);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
chance = self->health < self->SpawnHealth()/2 ? 96 : 48;
if (pr_s2a() < chance)
{ // Wizard spawners
PClassActor *fx = PClass::FindActor("Sorcerer2FX2");
if (fx)
{
P_SpawnMissileAngle(self, fx, self->Angles.Yaw - 45, 0.5);
P_SpawnMissileAngle(self, fx, self->Angles.Yaw + 45, 0.5);
}
}
else
{ // Blue bolt
P_SpawnMissile (self, self->target, PClass::FindActor("Sorcerer2FX1"));
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_BlueSpark
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BlueSpark)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
AActor *mo;
for (i = 0; i < 2; i++)
{
mo = Spawn("Sorcerer2FXSpark", self->Pos(), ALLOW_REPLACE);
mo->Vel.X = pr_bluespark.Random2() / 128.;
mo->Vel.Y = pr_bluespark.Random2() / 128.;
mo->Vel.Z = 1. + pr_bluespark() / 256.;
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_GenWizard
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_GenWizard)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = Spawn("Wizard", self->Pos(), ALLOW_REPLACE);
if (mo != NULL)
{
mo->AddZ(-mo->GetDefault()->Height / 2, false);
if (!P_TestMobjLocation (mo))
{ // Didn't fit
mo->ClearCounters();
mo->Destroy ();
}
else
{ // [RH] Make the new wizards inherit D'Sparil's target
mo->CopyFriendliness (self->target, true);
self->Vel.Zero();
self->SetState (self->FindState(NAME_Death));
self->flags &= ~MF_MISSILE;
mo->master = self->target;
P_SpawnTeleportFog(self, self->Pos(), false, true);
}
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Sor2DthInit
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Sor2DthInit)
{
PARAM_SELF_PROLOGUE(AActor);
self->special1 = 7; // Animation loop counter
P_Massacre (); // Kill monsters early
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Sor2DthLoop
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Sor2DthLoop)
{
PARAM_SELF_PROLOGUE(AActor);
if (--self->special1)
{ // Need to loop
self->SetState (self->FindState("DeathLoop"));
}
return 0;
}

View file

@ -1,75 +0,0 @@
/*
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "vm.h"
*/
// Tome of power ------------------------------------------------------------
class AArtiTomeOfPower : public APowerupGiver
{
DECLARE_CLASS (AArtiTomeOfPower, APowerupGiver)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiTomeOfPower, false, false, false, false)
bool AArtiTomeOfPower::Use (bool pickup)
{
if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYTOMEOFPOWER))
{ // Attempt to undo chicken
if (!P_UndoPlayerMorph (Owner->player, Owner->player, MORPH_UNDOBYTOMEOFPOWER))
{ // Failed
if (!(Owner->player->MorphStyle & MORPH_FAILNOTELEFRAG))
{
P_DamageMobj (Owner, NULL, NULL, TELEFRAG_DAMAGE, NAME_Telefrag);
}
}
else
{ // Succeeded
S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_IDLE);
}
return true;
}
else
{
return Super::Use (pickup);
}
}
// Time bomb ----------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_TimeBomb)
{
PARAM_SELF_PROLOGUE(AActor);
self->AddZ(32, false);
self->RenderStyle = STYLE_Add;
self->Alpha = 1.;
P_RadiusAttack (self, self->target, 128, 128, self->DamageType, RADF_HURTSOURCE);
P_CheckSplash(self, 128);
return 0;
}
class AArtiTimeBomb : public AInventory
{
DECLARE_CLASS (AArtiTimeBomb, AInventory)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiTimeBomb, false, false, false, false)
bool AArtiTimeBomb::Use (bool pickup)
{
AActor *mo = Spawn("ActivatedTimeBomb",
Owner->Vec3Angle(24., Owner->Angles.Yaw, - Owner->Floorclip), ALLOW_REPLACE);
mo->target = Owner;
return true;
}

View file

@ -1,217 +0,0 @@
#include "actor.h"
#include "info.h"
#include "a_pickups.h"
#include "a_action.h"
#include "m_random.h"
#include "p_local.h"
#include "s_sound.h"
#include "gstrings.h"
#include "vm.h"
#include "p_enemy.h"
#include "a_specialspot.h"
#include "g_level.h"
#include "a_sharedglobal.h"
#include "templates.h"
#include "r_data/r_translate.h"
#include "doomstat.h"
#include "d_player.h"
#include "a_morph.h"
#include "p_spec.h"
#include "serializer.h"
#include "vm.h"
// Include all the other Heretic stuff here to reduce compile time
#include "a_chicken.cpp"
#include "a_dsparil.cpp"
#include "a_hereticartifacts.cpp"
#include "a_hereticweaps.cpp"
#include "a_ironlich.cpp"
static FRandom pr_podpain ("PodPain");
static FRandom pr_makepod ("MakePod");
static FRandom pr_teleg ("TeleGlitter");
static FRandom pr_teleg2 ("TeleGlitter2");
static FRandom pr_volcano ("VolcanoSet");
static FRandom pr_blast ("VolcanoBlast");
static FRandom pr_volcimpact ("VolcBallImpact");
//----------------------------------------------------------------------------
//
// PROC A_PodPain
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PodPain)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS_DEF (gootype, AActor)
int count;
int chance;
AActor *goo;
chance = pr_podpain ();
if (chance < 128)
{
return 0;
}
for (count = chance > 240 ? 2 : 1; count; count--)
{
goo = Spawn(gootype, self->PosPlusZ(48.), ALLOW_REPLACE);
goo->target = self;
goo->Vel.X = pr_podpain.Random2() / 128.;
goo->Vel.Y = pr_podpain.Random2() / 128.;
goo->Vel.Z = 0.5 + pr_podpain() / 128.;
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_RemovePod
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_RemovePod)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
if ( (mo = self->master) )
{
if (mo->special1 > 0)
{
mo->special1--;
}
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MakePod
//
//----------------------------------------------------------------------------
#define MAX_GEN_PODS 16
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MakePod)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS_DEF(podtype, AActor)
AActor *mo;
if (self->special1 == MAX_GEN_PODS)
{ // Too many generated pods
return 0;
}
mo = Spawn(podtype, self->PosAtZ(ONFLOORZ), ALLOW_REPLACE);
if (!P_CheckPosition (mo, mo->Pos()))
{ // Didn't fit
mo->Destroy ();
return 0;
}
mo->SetState (mo->FindState("Grow"));
mo->Thrust(pr_makepod() * (360. / 256), 4.5);
S_Sound (mo, CHAN_BODY, self->AttackSound, 1, ATTN_IDLE);
self->special1++; // Increment generated pod count
mo->master = self; // Link the generator to the pod
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_AccTeleGlitter
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_AccTeleGlitter)
{
PARAM_SELF_PROLOGUE(AActor);
if (++self->health > 35)
{
self->Vel.Z *= 1.5;
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_VolcanoSet
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_VolcanoSet)
{
PARAM_SELF_PROLOGUE(AActor);
self->tics = 105 + (pr_volcano() & 127);
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_VolcanoBlast
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_VolcanoBlast)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
int count;
AActor *blast;
count = 1 + (pr_blast() % 3);
for (i = 0; i < count; i++)
{
blast = Spawn("VolcanoBlast", self->PosPlusZ(44.), ALLOW_REPLACE);
blast->target = self;
blast->Angles.Yaw = pr_blast() * (360 / 256.);
blast->VelFromAngle(1.);
blast->Vel.Z = 2.5 + pr_blast() / 64.;
S_Sound (blast, CHAN_BODY, "world/volcano/shoot", 1, ATTN_NORM);
P_CheckMissileSpawn (blast, self->radius);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_VolcBallImpact
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_VolcBallImpact)
{
PARAM_SELF_PROLOGUE(AActor);
unsigned int i;
AActor *tiny;
if (self->Z() <= self->floorz)
{
self->flags |= MF_NOGRAVITY;
self->Gravity = 1;
self->AddZ(28);
//self->Vel.Z = 3;
}
P_RadiusAttack (self, self->target, 25, 25, NAME_Fire, RADF_HURTSOURCE);
for (i = 0; i < 4; i++)
{
tiny = Spawn("VolcanoTBlast", self->Pos(), ALLOW_REPLACE);
tiny->target = self;
tiny->Angles.Yaw = 90.*i;
tiny->VelFromAngle(0.7);
tiny->Vel.Z = 1. + pr_volcimpact() / 128.;
P_CheckMissileSpawn (tiny, self->radius);
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,213 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_local.h"
#include "p_enemy.h"
#include "a_action.h"
#include "gstrings.h"
#include "vm.h"
#include "g_level.h"
*/
static FRandom pr_foo ("WhirlwindDamage");
static FRandom pr_atk ("LichAttack");
static FRandom pr_seek ("WhirlwindSeek");
class AWhirlwind : public AActor
{
DECLARE_CLASS (AWhirlwind, AActor)
public:
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
};
IMPLEMENT_CLASS(AWhirlwind, false, false, false, false)
int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
int randVal;
if (!(target->flags7 & MF7_DONTTHRUST))
{
target->Angles.Yaw += pr_foo.Random2() * (360 / 4096.);
target->Vel.X += pr_foo.Random2() / 64.;
target->Vel.Y += pr_foo.Random2() / 64.;
}
if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_DONTTHRUST))
{
randVal = pr_foo();
if (randVal > 160)
{
randVal = 160;
}
target->Vel.Z += randVal / 32.;
if (target->Vel.Z > 12)
{
target->Vel.Z = 12;
}
}
if (!(level.time & 7))
{
P_DamageMobj (target, NULL, this->target, 3, NAME_Melee);
}
return -1;
}
//----------------------------------------------------------------------------
//
// PROC A_LichAttack
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_LichAttack)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
AActor *fire;
AActor *baseFire;
AActor *mo;
AActor *target;
int randAttack;
static const int atkResolve1[] = { 50, 150 };
static const int atkResolve2[] = { 150, 200 };
// Ice ball (close 20% : far 60%)
// Fire column (close 40% : far 20%)
// Whirlwind (close 40% : far 20%)
// Distance threshold = 8 cells
target = self->target;
if (target == NULL)
{
return 0;
}
A_FaceTarget (self);
if (self->CheckMeleeRange ())
{
int damage = pr_atk.HitDice (6);
int newdam = P_DamageMobj (target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, target, self);
return 0;
}
int dist = self->Distance2D(target) > 8 * 64;
randAttack = pr_atk ();
if (randAttack < atkResolve1[dist])
{ // Ice ball
P_SpawnMissile (self, target, PClass::FindActor("HeadFX1"));
S_Sound (self, CHAN_BODY, "ironlich/attack2", 1, ATTN_NORM);
}
else if (randAttack < atkResolve2[dist])
{ // Fire column
baseFire = P_SpawnMissile (self, target, PClass::FindActor("HeadFX3"));
if (baseFire != NULL)
{
baseFire->SetState (baseFire->FindState("NoGrow"));
for (i = 0; i < 5; i++)
{
fire = Spawn("HeadFX3", baseFire->Pos(), ALLOW_REPLACE);
if (i == 0)
{
S_Sound (self, CHAN_BODY, "ironlich/attack1", 1, ATTN_NORM);
}
fire->target = baseFire->target;
fire->Angles.Yaw = baseFire->Angles.Yaw;
fire->Vel = baseFire->Vel;
fire->SetDamage(0);
fire->health = (i+1) * 2;
P_CheckMissileSpawn (fire, self->radius);
}
}
}
else
{ // Whirlwind
mo = P_SpawnMissile (self, target, RUNTIME_CLASS(AWhirlwind));
if (mo != NULL)
{
mo->AddZ(-32);
mo->tracer = target;
mo->health = 20*TICRATE; // Duration
S_Sound (self, CHAN_BODY, "ironlich/attack3", 1, ATTN_NORM);
}
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_WhirlwindSeek
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_WhirlwindSeek)
{
PARAM_SELF_PROLOGUE(AActor);
self->health -= 3;
if (self->health < 0)
{
self->Vel.Zero();
self->SetState(self->FindState(NAME_Death));
self->flags &= ~MF_MISSILE;
return 0;
}
if ((self->threshold -= 3) < 0)
{
self->threshold = 58 + (pr_seek() & 31);
S_Sound(self, CHAN_BODY, "ironlich/attack3", 1, ATTN_NORM);
}
if (self->tracer && self->tracer->flags&MF_SHADOW)
{
return 0;
}
P_SeekerMissile(self, 10, 30);
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_LichIceImpact
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_LichIceImpact)
{
PARAM_SELF_PROLOGUE(AActor);
unsigned int i;
AActor *shard;
for (i = 0; i < 8; i++)
{
shard = Spawn("HeadFX2", self->Pos(), ALLOW_REPLACE);
shard->target = self->target;
shard->Angles.Yaw = i*45.;
shard->VelFromAngle();
shard->Vel.Z = -.6;
P_CheckMissileSpawn (shard, self->radius);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_LichFireGrow
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_LichFireGrow)
{
PARAM_SELF_PROLOGUE(AActor);
self->health--;
self->AddZ(9.);
if (self->health == 0)
{
self->RestoreDamage();
self->SetState (self->FindState("NoGrow"));
}
return 0;
}

View file

@ -1,158 +0,0 @@
/*
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "p_enemy.h"
#include "s_sound.h"
*/
/* For reference, the default values:
#define BLAST_RADIUS_DIST 255*F.RACUNIT
#define BLAST_SPEED 20*F.RACUNIT
#define BLAST_FULLSTRENGTH 255
*/
// Disc of Repulsion --------------------------------------------------------
//==========================================================================
//
// AArtiBlastRadius :: BlastActor
//
//==========================================================================
void BlastActor (AActor *victim, double strength, double speed, AActor *Owner, PClassActor *blasteffect, bool dontdamage)
{
DAngle angle;
AActor *mo;
DVector3 pos;
if (!victim->SpecialBlastHandling (Owner, strength))
{
return;
}
angle = Owner->AngleTo(victim);
DVector2 move = angle.ToVector(speed);
victim->Vel.X = move.X;
victim->Vel.Y = move.Y;
// Spawn blast puff
angle -= 180.;
pos = victim->Vec3Offset(
(victim->radius + 1) * angle.Cos(),
(victim->radius + 1) * angle.Sin(),
(victim->Height / 2) - victim->Floorclip);
mo = Spawn (blasteffect, pos, ALLOW_REPLACE);
if (mo)
{
mo->Vel.X = victim->Vel.X;
mo->Vel.Y = victim->Vel.Y;
}
if (victim->flags & MF_MISSILE)
{
// [RH] Floor and ceiling huggers should not be blasted vertically.
if (!(victim->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)))
{
mo->Vel.Z = victim->Vel.Z = 8;
}
}
else
{
victim->Vel.Z = 1000. / victim->Mass;
}
if (victim->player)
{
// Players handled automatically
}
else if (!dontdamage)
{
victim->flags2 |= MF2_BLASTED;
}
if (victim->flags6 & MF6_TOUCHY)
{ // Touchy objects die when blasted
victim->flags6 &= ~MF6_ARMED; // Disarm
P_DamageMobj(victim, Owner, Owner, victim->health, NAME_Melee, DMG_FORCED);
}
}
enum
{
BF_USEAMMO = 1,
BF_DONTWARN = 2,
BF_AFFECTBOSSES = 4,
BF_NOIMPACTDAMAGE = 8,
};
//==========================================================================
//
// AArtiBlastRadius :: Activate
//
// Blast all actors away
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_Blast)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_INT_DEF (blastflags)
PARAM_FLOAT_DEF (strength)
PARAM_FLOAT_DEF (radius)
PARAM_FLOAT_DEF (speed)
PARAM_CLASS_DEF (blasteffect, AActor)
PARAM_SOUND_DEF (blastsound)
AActor *mo;
TThinkerIterator<AActor> iterator;
if (self->player && (blastflags & BF_USEAMMO) && ACTION_CALL_FROM_PSPRITE())
{
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL && !weapon->DepleteAmmo(weapon->bAltFire))
{
return 0;
}
}
S_Sound (self, CHAN_AUTO, blastsound, 1, ATTN_NORM);
if (!(blastflags & BF_DONTWARN))
{
P_NoiseAlert (self, self);
}
while ( (mo = iterator.Next ()) )
{
if ((mo == self) || ((mo->flags2 & MF2_BOSS) && !(blastflags & BF_AFFECTBOSSES))
|| (mo->flags2 & MF2_DORMANT) || (mo->flags3 & MF3_DONTBLAST))
{ // Not a valid monster: originator, boss, dormant, or otherwise protected
continue;
}
if ((mo->flags & MF_ICECORPSE) || (mo->flags3 & MF3_CANBLAST))
{
// Let these special cases go
}
else if ((mo->flags3 & MF3_ISMONSTER) && (mo->health <= 0))
{
continue;
}
else if (!(mo->player) &&
!(mo->flags & MF_MISSILE) &&
!(mo->flags3 & (MF3_ISMONSTER|MF3_CANBLAST)) &&
!(mo->flags6 & (MF6_TOUCHY|MF6_VULNERABLE)))
{ // Must be monster, player, missile, touchy or vulnerable
continue;
}
if (self->Distance2D(mo) > radius)
{ // Out of range
continue;
}
if (mo->Sector->PortalGroup != self->Sector->PortalGroup && !P_CheckSight(self, mo))
{
// in another region and cannot be seen.
continue;
}
BlastActor (mo, strength, speed, self, blasteffect, !!(blastflags & BF_NOIMPACTDAMAGE));
}
return 0;
}

View file

@ -1,64 +0,0 @@
/*
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "gi.h"
#include "s_sound.h"
*/
// Boost Armor Artifact (Dragonskin Bracers) --------------------------------
class AArtiBoostArmor : public AInventory
{
DECLARE_CLASS (AArtiBoostArmor, AInventory)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiBoostArmor, false, false, false, false)
bool AArtiBoostArmor::Use (bool pickup)
{
int count = 0;
if (gameinfo.gametype == GAME_Hexen)
{
AHexenArmor *armor;
for (int i = 0; i < 4; ++i)
{
armor = Spawn<AHexenArmor>();
armor->flags |= MF_DROPPED;
armor->health = i;
armor->Amount = 1;
if (!armor->CallTryPickup (Owner))
{
armor->Destroy ();
}
else
{
count++;
}
}
return count != 0;
}
else
{
ABasicArmorBonus *armor = Spawn<ABasicArmorBonus>();
armor->flags |= MF_DROPPED;
armor->SaveAmount = 50;
armor->MaxSaveAmount = 300;
if (!armor->CallTryPickup (Owner))
{
armor->Destroy ();
return false;
}
else
{
return true;
}
}
}

View file

@ -1,170 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
const double FLAMESPEED = 0.45;
const double FLAMEROTSPEED = 2.;
static FRandom pr_missile ("CFlameMissile");
void A_CFlameAttack (AActor *);
void A_CFlameRotate (AActor *);
void A_CFlamePuff (AActor *);
void A_CFlameMissile (AActor *);
// Flame Missile ------------------------------------------------------------
class ACFlameMissile : public AFastProjectile
{
DECLARE_CLASS (ACFlameMissile, AFastProjectile)
public:
void BeginPlay ();
void Effect ();
};
IMPLEMENT_CLASS(ACFlameMissile, false, false, false, false)
void ACFlameMissile::BeginPlay ()
{
special1 = 2;
}
void ACFlameMissile::Effect ()
{
if (!--special1)
{
special1 = 4;
double newz = Z() - 12;
if (newz < floorz)
{
newz = floorz;
}
AActor *mo = Spawn ("CFlameFloor", PosAtZ(newz), ALLOW_REPLACE);
if (mo)
{
mo->Angles.Yaw = Angles.Yaw;
}
}
}
//============================================================================
//
// A_CFlameAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CFlameAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
P_SpawnPlayerMissile (self, RUNTIME_CLASS(ACFlameMissile));
S_Sound (self, CHAN_WEAPON, "ClericFlameFire", 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_CFlamePuff
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CFlamePuff)
{
PARAM_SELF_PROLOGUE(AActor);
self->renderflags &= ~RF_INVISIBLE;
self->Vel.Zero();
S_Sound (self, CHAN_BODY, "ClericFlameExplode", 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_CFlameMissile
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CFlameMissile)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
DAngle an;
double dist;
AActor *mo;
self->renderflags &= ~RF_INVISIBLE;
S_Sound (self, CHAN_BODY, "ClericFlameExplode", 1, ATTN_NORM);
AActor *BlockingMobj = self->BlockingMobj;
if (BlockingMobj && BlockingMobj->flags&MF_SHOOTABLE)
{ // Hit something, so spawn the flame circle around the thing
dist = BlockingMobj->radius + 18;
for (i = 0; i < 4; i++)
{
an = i*45.;
mo = Spawn ("CircleFlame", BlockingMobj->Vec3Angle(dist, an, 5), ALLOW_REPLACE);
if (mo)
{
mo->Angles.Yaw = an;
mo->target = self->target;
mo->VelFromAngle(FLAMESPEED);
mo->specialf1 = mo->Vel.X;
mo->specialf2 = mo->Vel.Y;
mo->tics -= pr_missile()&3;
}
mo = Spawn("CircleFlame", BlockingMobj->Vec3Angle(dist, an, 5), ALLOW_REPLACE);
if(mo)
{
mo->Angles.Yaw = an + 180.;
mo->target = self->target;
mo->VelFromAngle(-FLAMESPEED);
mo->specialf1 = mo->Vel.X;
mo->specialf2 = mo->Vel.Y;
mo->tics -= pr_missile()&3;
}
}
self->SetState (self->SpawnState);
}
return 0;
}
//============================================================================
//
// A_CFlameRotate
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CFlameRotate)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle an = self->Angles.Yaw + 90.;
self->VelFromAngle(FLAMEROTSPEED, an);
self->Vel += DVector2(self->specialf1, self->specialf2);
self->Angles.Yaw += 6.;
return 0;
}

View file

@ -1,516 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "m_random.h"
#include "s_sound.h"
#include "a_hexenglobal.h"
#include "gstrings.h"
#include "a_weaponpiece.h"
#include "vm.h"
#include "g_level.h"
#include "doomstat.h"
*/
#define BLAST_FULLSTRENGTH 255
static FRandom pr_holyatk2 ("CHolyAtk2");
static FRandom pr_holyseeker ("CHolySeeker");
static FRandom pr_holyweave ("CHolyWeave");
static FRandom pr_holyseek ("CHolySeek");
static FRandom pr_checkscream ("CCheckScream");
static FRandom pr_spiritslam ("CHolySlam");
static FRandom pr_wraithvergedrop ("WraithvergeDrop");
void SpawnSpiritTail (AActor *spirit);
//==========================================================================
// Cleric's Wraithverge (Holy Symbol?) --------------------------------------
class ACWeapWraithverge : public AClericWeapon
{
DECLARE_CLASS (ACWeapWraithverge, AClericWeapon)
public:
void Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("cholycount", CHolyCount);
}
PalEntry GetBlend ()
{
if (paletteflash & PF_HEXENWEAPONS)
{
if (CHolyCount == 3)
return PalEntry(128, 70, 70, 70);
else if (CHolyCount == 2)
return PalEntry(128, 100, 100, 100);
else if (CHolyCount == 1)
return PalEntry(128, 130, 130, 130);
else
return PalEntry(0, 0, 0, 0);
}
else
{
return PalEntry (CHolyCount * 128 / 3, 131, 131, 131);
}
}
BYTE CHolyCount;
};
IMPLEMENT_CLASS(ACWeapWraithverge, false, false, false, false)
// Holy Spirit --------------------------------------------------------------
IMPLEMENT_CLASS(AHolySpirit, false, false, false, false)
bool AHolySpirit::Slam(AActor *thing)
{
if (thing->flags&MF_SHOOTABLE && thing != target)
{
if (multiplayer && !deathmatch && thing->player && target->player)
{ // don't attack other co-op players
return true;
}
if (thing->flags2&MF2_REFLECTIVE
&& (thing->player || thing->flags2&MF2_BOSS))
{
tracer = target;
target = thing;
return true;
}
if (thing->flags3&MF3_ISMONSTER || thing->player)
{
tracer = thing;
}
if (pr_spiritslam() < 96)
{
int dam = 12;
if (thing->player || thing->flags2&MF2_BOSS)
{
dam = 3;
// ghost burns out faster when attacking players/bosses
health -= 6;
}
P_DamageMobj(thing, this, target, dam, NAME_Melee);
if (pr_spiritslam() < 128)
{
Spawn("HolyPuff", Pos(), ALLOW_REPLACE);
S_Sound(this, CHAN_WEAPON, "SpiritAttack", 1, ATTN_NORM);
if (thing->flags3&MF3_ISMONSTER && pr_spiritslam() < 128)
{
thing->Howl();
}
}
}
if (thing->health <= 0)
{
tracer = NULL;
}
}
return true;
}
bool AHolySpirit::SpecialBlastHandling (AActor *source, double strength)
{
if (tracer == source)
{
tracer = target;
target = source;
GC::WriteBarrier(this, source);
}
return true;
}
//============================================================================
//
// A_CHolyAttack2
//
// Spawns the spirits
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CHolyAttack2)
{
PARAM_SELF_PROLOGUE(AActor);
int j;
AActor *mo;
for (j = 0; j < 4; j++)
{
mo = Spawn<AHolySpirit> (self->Pos(), ALLOW_REPLACE);
if (!mo)
{
continue;
}
switch (j)
{ // float bob index
case 0:
mo->WeaveIndexZ = pr_holyatk2() & 7; // upper-left
break;
case 1:
mo->WeaveIndexZ = 32 + (pr_holyatk2() & 7); // upper-right
break;
case 2:
mo->WeaveIndexXY = 32 + (pr_holyatk2() & 7); // lower-left
break;
case 3:
mo->WeaveIndexXY = 32 + (pr_holyatk2() & 7);
mo->WeaveIndexZ = 32 + (pr_holyatk2() & 7);
break;
}
mo->SetZ(self->Z());
mo->Angles.Yaw = self->Angles.Yaw + 67.5 - 45.*j;
mo->Thrust();
mo->target = self->target;
mo->args[0] = 10; // initial turn value
mo->args[1] = 0; // initial look angle
if (deathmatch)
{ // Ghosts last slightly less longer in DeathMatch
mo->health = 85;
}
if (self->tracer)
{
mo->tracer = self->tracer;
mo->flags |= MF_NOCLIP|MF_SKULLFLY;
mo->flags &= ~MF_MISSILE;
}
SpawnSpiritTail (mo);
}
return 0;
}
//============================================================================
//
// SpawnSpiritTail
//
//============================================================================
void SpawnSpiritTail (AActor *spirit)
{
AActor *tail, *next;
int i;
tail = Spawn ("HolyTail", spirit->Pos(), ALLOW_REPLACE);
tail->target = spirit; // parent
for (i = 1; i < 3; i++)
{
next = Spawn ("HolyTailTrail", spirit->Pos(), ALLOW_REPLACE);
tail->tracer = next;
tail = next;
}
tail->tracer = NULL; // last tail bit
}
//============================================================================
//
// A_CHolyAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CHolyAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
ACWeapWraithverge *weapon = static_cast<ACWeapWraithverge *> (self->player->ReadyWeapon);
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
AActor *missile = P_SpawnPlayerMissile (self, 0,0,0, PClass::FindActor("HolyMissile"), self->Angles.Yaw, &t);
if (missile != NULL && !t.unlinked)
{
missile->tracer = t.linetarget;
}
weapon->CHolyCount = 3;
S_Sound (self, CHAN_WEAPON, "HolySymbolFire", 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_CHolyPalette
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CHolyPalette)
{
PARAM_ACTION_PROLOGUE(AActor);
if (self->player != NULL)
{
ACWeapWraithverge *weapon = static_cast<ACWeapWraithverge *> (self->player->ReadyWeapon);
if (weapon != NULL && weapon->CHolyCount != 0)
{
weapon->CHolyCount--;
}
}
return 0;
}
//============================================================================
//
// CHolyTailFollow
//
//============================================================================
static void CHolyTailFollow(AActor *actor, double dist)
{
AActor *child;
DAngle an;
double oldDistance, newDistance;
while (actor)
{
child = actor->tracer;
if (child)
{
an = actor->AngleTo(child);
oldDistance = child->Distance2D(actor);
if (P_TryMove(child, actor->Pos().XY() + an.ToVector(dist), true))
{
newDistance = child->Distance2D(actor) - 1;
if (oldDistance < 1)
{
if (child->Z() < actor->Z())
{
child->SetZ(actor->Z() - dist);
}
else
{
child->SetZ(actor->Z() + dist);
}
}
else
{
child->SetZ(actor->Z() + (newDistance * (child->Z() - actor->Z()) / oldDistance));
}
}
}
actor = child;
dist -= 1;
}
}
//============================================================================
//
// CHolyTailRemove
//
//============================================================================
static void CHolyTailRemove (AActor *actor)
{
AActor *next;
while (actor)
{
next = actor->tracer;
actor->Destroy ();
actor = next;
}
}
//============================================================================
//
// A_CHolyTail
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CHolyTail)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *parent;
parent = self->target;
if (parent == NULL || parent->health <= 0) // better check for health than current state - it's safer!
{ // Ghost removed, so remove all tail parts
CHolyTailRemove (self);
return 0;
}
else
{
if (P_TryMove(self, parent->Vec2Angle(14., parent->Angles.Yaw, true), true))
{
self->SetZ(parent->Z() - 5.);
}
CHolyTailFollow(self, 10);
}
return 0;
}
//============================================================================
//
// CHolyFindTarget
//
//============================================================================
static void CHolyFindTarget (AActor *actor)
{
AActor *target;
if ( (target = P_RoughMonsterSearch (actor, 6, true)) )
{
actor->tracer = target;
actor->flags |= MF_NOCLIP|MF_SKULLFLY;
actor->flags &= ~MF_MISSILE;
}
}
//============================================================================
//
// CHolySeekerMissile
//
// Similar to P_SeekerMissile, but seeks to a random Z on the target
//============================================================================
static void CHolySeekerMissile (AActor *actor, DAngle thresh, DAngle turnMax)
{
int dir;
DAngle delta;
AActor *target;
double newZ;
double deltaZ;
target = actor->tracer;
if (target == NULL)
{
return;
}
if (!(target->flags&MF_SHOOTABLE)
|| (!(target->flags3&MF3_ISMONSTER) && !target->player))
{ // Target died/target isn't a player or creature
actor->tracer = NULL;
actor->flags &= ~(MF_NOCLIP | MF_SKULLFLY);
actor->flags |= MF_MISSILE;
CHolyFindTarget(actor);
return;
}
dir = P_FaceMobj (actor, target, &delta);
if (delta > thresh)
{
delta /= 2;
if (delta > turnMax)
{
delta = turnMax;
}
}
if (dir)
{ // Turn clockwise
actor->Angles.Yaw += delta;
}
else
{ // Turn counter clockwise
actor->Angles.Yaw -= delta;
}
actor->VelFromAngle();
if (!(level.time&15)
|| actor->Z() > target->Top()
|| actor->Top() < target->Z())
{
newZ = target->Z() + ((pr_holyseeker()*target->Height) / 256.);
deltaZ = newZ - actor->Z();
if (fabs(deltaZ) > 15)
{
if (deltaZ > 0)
{
deltaZ = 15;
}
else
{
deltaZ = -15;
}
}
actor->Vel.Z = deltaZ / actor->DistanceBySpeed(target, actor->Speed);
}
return;
}
//============================================================================
//
// A_CHolySeek
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CHolySeek)
{
PARAM_SELF_PROLOGUE(AActor);
self->health--;
if (self->health <= 0)
{
self->Vel.X /= 4;
self->Vel.Y /= 4;
self->Vel.Z = 0;
self->SetState (self->FindState(NAME_Death));
self->tics -= pr_holyseek()&3;
return 0;
}
if (self->tracer)
{
CHolySeekerMissile (self, (double)self->args[0], self->args[0]*2.);
if (!((level.time+7)&15))
{
self->args[0] = 5+(pr_holyseek()/20);
}
}
int xyspeed = (pr_holyweave() % 5);
int zspeed = (pr_holyweave() % 5);
A_Weave(self, xyspeed, zspeed, 4., 2.);
return 0;
}
//============================================================================
//
// A_CHolyCheckScream
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CHolyCheckScream)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_CHolySeek, self);
if (pr_checkscream() < 20)
{
S_Sound (self, CHAN_VOICE, "SpiritActive", 1, ATTN_NORM);
}
if (!self->tracer)
{
CHolyFindTarget(self);
}
return 0;
}
//============================================================================
//
// A_ClericAttack
// (for the ClericBoss)
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ClericAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target) return 0;
AActor * missile = P_SpawnMissileZ (self, self->Z() + 40., self->target, PClass::FindActor ("HolyMissile"));
if (missile != NULL) missile->tracer = NULL; // No initial target
S_Sound (self, CHAN_WEAPON, "HolySymbolFire", 1, ATTN_NORM);
return 0;
}

View file

@ -1,59 +0,0 @@
/*
#include "m_random.h"
#include "p_local.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
static FRandom pr_maceatk ("CMaceAttack");
//===========================================================================
//
// A_CMaceAttack
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CMaceAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
int i;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
PClassActor *hammertime = PClass::FindActor("HammerPuff");
damage = 25+(pr_maceatk()&15);
for (i = 0; i < 16; i++)
{
for (int j = 1; j >= -1; j -= 2)
{
angle = player->mo->Angles.Yaw + j*i*(45. / 16);
slope = P_AimLineAttack(player->mo, angle, 2 * MELEERANGE, &t);
if (t.linetarget)
{
P_LineAttack(player->mo, angle, 2 * MELEERANGE, slope, damage, NAME_Melee, hammertime, true, &t);
if (t.linetarget != NULL)
{
AdjustPlayerAngle(player->mo, &t);
return 0;
}
}
}
}
// didn't find any creatures, so try to strike any walls
player->mo->weaponspecial = 0;
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, hammertime);
return 0;
}

View file

@ -1,199 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
static FRandom pr_staffcheck ("CStaffCheck");
static FRandom pr_blink ("CStaffBlink");
// Serpent Staff Missile ----------------------------------------------------
class ACStaffMissile : public AActor
{
DECLARE_CLASS (ACStaffMissile, AActor)
public:
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
};
IMPLEMENT_CLASS(ACStaffMissile, false, false, false, false)
int ACStaffMissile::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
// Cleric Serpent Staff does poison damage
if (target->player)
{
P_PoisonPlayer (target->player, this, this->target, 20);
damage >>= 1;
}
return damage;
}
//============================================================================
//
// A_CStaffCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck)
{
PARAM_ACTION_PROLOGUE(AActor);
APlayerPawn *pmo;
int damage;
int newLife, max;
DAngle angle;
DAngle slope;
int i;
player_t *player;
FTranslatedLineTarget t;
PClassActor *puff;
if (nullptr == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
pmo = player->mo;
damage = 20 + (pr_staffcheck() & 15);
max = pmo->GetMaxHealth();
puff = PClass::FindActor("CStaffPuff");
for (i = 0; i < 3; i++)
{
for (int j = 1; j >= -1; j -= 2)
{
angle = pmo->Angles.Yaw + j*i*(45. / 16);
slope = P_AimLineAttack(pmo, angle, 1.5 * MELEERANGE, &t, 0., ALF_CHECK3D);
if (t.linetarget)
{
P_LineAttack(pmo, angle, 1.5 * MELEERANGE, slope, damage, NAME_Melee, puff, false, &t);
if (t.linetarget != nullptr)
{
pmo->Angles.Yaw = t.angleFromSource;
if (((t.linetarget->player && (!t.linetarget->IsTeammate(pmo) || level.teamdamage != 0)) || t.linetarget->flags3&MF3_ISMONSTER)
&& (!(t.linetarget->flags2&(MF2_DORMANT | MF2_INVULNERABLE))))
{
newLife = player->health + (damage >> 3);
newLife = newLife > max ? max : newLife;
if (newLife > player->health)
{
pmo->health = player->health = newLife;
}
if (weapon != nullptr)
{
FState * newstate = weapon->FindState("Drain");
if (newstate != nullptr) P_SetPsprite(player, PSP_WEAPON, newstate);
}
}
if (weapon != nullptr)
{
weapon->DepleteAmmo(weapon->bAltFire, false);
}
}
return 0;
}
}
}
return 0;
}
//============================================================================
//
// A_CStaffAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CStaffAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
AActor *mo;
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(ACStaffMissile), self->Angles.Yaw - 3.0);
if (mo)
{
mo->WeaveIndexXY = 32;
}
mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(ACStaffMissile), self->Angles.Yaw + 3.0);
if (mo)
{
mo->WeaveIndexXY = 0;
}
S_Sound (self, CHAN_WEAPON, "ClericCStaffFire", 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_CStaffMissileSlither
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CStaffMissileSlither)
{
PARAM_SELF_PROLOGUE(AActor);
A_Weave(self, 3, 0, 1., 0.);
return 0;
}
//============================================================================
//
// A_CStaffInitBlink
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CStaffInitBlink)
{
PARAM_ACTION_PROLOGUE(AActor);
self->weaponspecial = (pr_blink()>>1)+20;
return 0;
}
//============================================================================
//
// A_CStaffCheckBlink
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheckBlink)
{
PARAM_ACTION_PROLOGUE(AActor);
if (self->player && self->player->ReadyWeapon)
{
if (!--self->weaponspecial)
{
P_SetPsprite(self->player, PSP_WEAPON, self->player->ReadyWeapon->FindState ("Blink"));
self->weaponspecial = (pr_blink()+50)>>2;
}
else
{
DoReadyWeapon(self);
}
}
return 0;
}

View file

@ -1,311 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_enemy.h"
#include "p_local.h"
#include "a_action.h"
#include "m_random.h"
#include "s_sound.h"
#include "vm.h"
*/
static FRandom pr_dragonseek ("DragonSeek");
static FRandom pr_dragonflight ("DragonFlight");
static FRandom pr_dragonflap ("DragonFlap");
static FRandom pr_dragonfx2 ("DragonFX2");
DECLARE_ACTION(A_DragonFlight)
//============================================================================
//
// DragonSeek
//
//============================================================================
static void DragonSeek (AActor *actor, DAngle thresh, DAngle turnMax)
{
int dir;
double dist;
DAngle delta;
AActor *target;
int i;
DAngle bestAngle;
DAngle angleToSpot, angleToTarget;
AActor *mo;
target = actor->tracer;
if(target == NULL)
{
return;
}
dir = P_FaceMobj (actor, target, &delta);
if (delta > thresh)
{
delta /= 2;
if (delta > turnMax)
{
delta = turnMax;
}
}
if (dir)
{ // Turn clockwise
actor->Angles.Yaw += delta;
}
else
{ // Turn counter clockwise
actor->Angles.Yaw -= delta;
}
actor->VelFromAngle();
dist = actor->DistanceBySpeed(target, actor->Speed);
if (actor->Top() < target->Z() ||
target->Top() < actor->Z())
{
actor->Vel.Z = (target->Z() - actor->Z()) / dist;
}
if (target->flags&MF_SHOOTABLE && pr_dragonseek() < 64)
{ // attack the destination mobj if it's attackable
AActor *oldTarget;
if (absangle(actor->Angles.Yaw, actor->AngleTo(target)) < 22.5)
{
oldTarget = actor->target;
actor->target = target;
if (actor->CheckMeleeRange ())
{
int damage = pr_dragonseek.HitDice (10);
int newdam = P_DamageMobj (actor->target, actor, actor, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, actor->target, actor);
S_Sound (actor, CHAN_WEAPON, actor->AttackSound, 1, ATTN_NORM);
}
else if (pr_dragonseek() < 128 && P_CheckMissileRange(actor))
{
P_SpawnMissile(actor, target, PClass::FindActor("DragonFireball"));
S_Sound (actor, CHAN_WEAPON, actor->AttackSound, 1, ATTN_NORM);
}
actor->target = oldTarget;
}
}
if (dist < 4)
{ // Hit the target thing
if (actor->target && pr_dragonseek() < 200)
{
AActor *bestActor = NULL;
bestAngle = 360.;
angleToTarget = actor->AngleTo(actor->target);
for (i = 0; i < 5; i++)
{
if (!target->args[i])
{
continue;
}
FActorIterator iterator (target->args[i]);
mo = iterator.Next ();
if (mo == NULL)
{
continue;
}
angleToSpot = actor->AngleTo(mo);
DAngle diff = absangle(angleToSpot, angleToTarget);
if (diff < bestAngle)
{
bestAngle = diff;
bestActor = mo;
}
}
if (bestActor != NULL)
{
actor->tracer = bestActor;
}
}
else
{
// [RH] Don't lock up if the dragon doesn't have any
// targets defined
for (i = 0; i < 5; ++i)
{
if (target->args[i] != 0)
{
break;
}
}
if (i < 5)
{
do
{
i = (pr_dragonseek()>>2)%5;
} while(!target->args[i]);
FActorIterator iterator (target->args[i]);
actor->tracer = iterator.Next ();
}
}
}
}
//============================================================================
//
// A_DragonInitFlight
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonInitFlight)
{
PARAM_SELF_PROLOGUE(AActor);
FActorIterator iterator (self->tid);
do
{ // find the first tid identical to the dragon's tid
self->tracer = iterator.Next ();
if (self->tracer == NULL)
{
self->SetState (self->SpawnState);
return 0;
}
} while (self->tracer == self);
self->RemoveFromHash ();
return 0;
}
//============================================================================
//
// A_DragonFlight
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonFlight)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle angle;
DragonSeek (self, 4., 8.);
if (self->target)
{
if(!(self->target->flags&MF_SHOOTABLE))
{ // target died
self->target = NULL;
return 0;
}
angle = absangle(self->Angles.Yaw, self->AngleTo(self->target));
if (angle <22.5 && self->CheckMeleeRange())
{
int damage = pr_dragonflight.HitDice (8);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
}
else if (angle <= 20)
{
self->SetState (self->MissileState);
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
}
}
else
{
P_LookForPlayers (self, true, NULL);
}
return 0;
}
//============================================================================
//
// A_DragonFlap
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonFlap)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_DragonFlight, self);
if (pr_dragonflap() < 240)
{
S_Sound (self, CHAN_BODY, "DragonWingflap", 1, ATTN_NORM);
}
else
{
self->PlayActiveSound ();
}
return 0;
}
//============================================================================
//
// A_DragonAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonAttack)
{
PARAM_SELF_PROLOGUE(AActor);
P_SpawnMissile (self, self->target, PClass::FindActor("DragonFireball"));
return 0;
}
//============================================================================
//
// A_DragonFX2
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonFX2)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
int delay;
delay = 16+(pr_dragonfx2()>>3);
for (i = 1+(pr_dragonfx2()&3); i; i--)
{
double xo = (pr_dragonfx2() - 128) / 4.;
double yo = (pr_dragonfx2() - 128) / 4.;
double zo = (pr_dragonfx2() - 128) / 16.;
mo = Spawn ("DragonExplosion", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->tics = delay+(pr_dragonfx2()&3)*i*2;
mo->target = self->target;
}
}
return 0;
}
//============================================================================
//
// A_DragonPain
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonPain)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_Pain, self);
if (!self->tracer)
{ // no destination spot yet
self->SetState (self->SeeState);
}
return 0;
}
//============================================================================
//
// A_DragonCheckCrash
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DragonCheckCrash)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->Z() <= self->floorz)
{
self->SetState (self->FindState ("Crash"));
}
return 0;
}

View file

@ -1,277 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
DECLARE_ACTION(A_Raise)
#define AXERANGE (2.25 * MELEERANGE)
static FRandom pr_axeatk ("FAxeAtk");
// The Fighter's Axe --------------------------------------------------------
class AFWeapAxe : public AFighterWeapon
{
DECLARE_CLASS (AFWeapAxe, AFighterWeapon)
public:
FState *GetUpState ();
FState *GetDownState ();
FState *GetReadyState ();
FState *GetAtkState (bool hold);
};
IMPLEMENT_CLASS(AFWeapAxe, false, false, false, false)
FState *AFWeapAxe::GetUpState ()
{
return Ammo1->Amount ? FindState ("SelectGlow") : Super::GetUpState();
}
FState *AFWeapAxe::GetDownState ()
{
return Ammo1->Amount ? FindState ("DeselectGlow") : Super::GetDownState();
}
FState *AFWeapAxe::GetReadyState ()
{
return Ammo1->Amount ? FindState ("ReadyGlow") : Super::GetReadyState();
}
FState *AFWeapAxe::GetAtkState (bool hold)
{
return Ammo1->Amount ? FindState ("FireGlow") : Super::GetAtkState(hold);
}
//============================================================================
//
// A_FAxeCheckReady
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FAxeCheckReady)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
if (player->ReadyWeapon->Ammo1->Amount)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState ("ReadyGlow"));
}
else
{
DoReadyWeapon(self);
}
return 0;
}
//============================================================================
//
// A_FAxeCheckReadyG
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FAxeCheckReadyG)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
if (player->ReadyWeapon->Ammo1->Amount <= 0)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState ("Ready"));
}
else
{
DoReadyWeapon(self);
}
return 0;
}
//============================================================================
//
// A_FAxeCheckUp
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FAxeCheckUp)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
if (player->ReadyWeapon->Ammo1->Amount)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState ("SelectGlow"));
}
else
{
CALL_ACTION(A_Raise, self);
}
return 0;
}
//============================================================================
//
// A_FAxeCheckUpG
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FAxeCheckUpG)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
if (player->ReadyWeapon->Ammo1->Amount <= 0)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState ("Select"));
}
else
{
CALL_ACTION(A_Raise, self);
}
return 0;
}
//============================================================================
//
// A_FAxeCheckAtk
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FAxeCheckAtk)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
if (player->ReadyWeapon->Ammo1->Amount)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState ("FireGlow"));
}
return 0;
}
//============================================================================
//
// A_FAxeAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FAxeAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int power;
int damage;
DAngle slope;
int i;
int useMana;
player_t *player;
AWeapon *weapon;
PClassActor *pufftype;
FTranslatedLineTarget t;
if (nullptr == (player = self->player))
{
return 0;
}
AActor *pmo=player->mo;
damage = 40+(pr_axeatk()&15);
damage += pr_axeatk()&7;
power = 0;
weapon = player->ReadyWeapon;
if (player->ReadyWeapon->Ammo1->Amount > 0)
{
damage <<= 1;
power = 6;
pufftype = PClass::FindActor ("AxePuffGlow");
useMana = 1;
}
else
{
pufftype = PClass::FindActor ("AxePuff");
useMana = 0;
}
for (i = 0; i < 16; i++)
{
for (int j = 1; j >= -1; j -= 2)
{
angle = pmo->Angles.Yaw + j*i*(45. / 16);
slope = P_AimLineAttack(pmo, angle, AXERANGE, &t);
if (t.linetarget)
{
P_LineAttack(pmo, angle, AXERANGE, slope, damage, NAME_Melee, pufftype, true, &t);
if (t.linetarget != nullptr)
{
if (t.linetarget->flags3&MF3_ISMONSTER || t.linetarget->player)
{
t.linetarget->Thrust(t.angleFromSource, power);
}
AdjustPlayerAngle(pmo, &t);
useMana++;
goto axedone;
}
}
}
}
// didn't find any creatures, so try to strike any walls
pmo->weaponspecial = 0;
angle = pmo->Angles.Yaw;
slope = P_AimLineAttack (pmo, angle, MELEERANGE);
P_LineAttack (pmo, angle, MELEERANGE, slope, damage, NAME_Melee, pufftype, true);
axedone:
if (useMana == 2)
{
AWeapon *weapon = player->ReadyWeapon;
if (weapon != nullptr)
{
weapon->DepleteAmmo (weapon->bAltFire, false);
if ((weapon->Ammo1 == nullptr || weapon->Ammo1->Amount == 0) &&
(!(weapon->WeaponFlags & WIF_PRIMARY_USES_BOTH) ||
weapon->Ammo2 == nullptr || weapon->Ammo2->Amount == 0))
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState ("Fire") + 5);
}
}
}
return 0;
}

View file

@ -1,124 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
const double HAMMER_RANGE = 1.5 * MELEERANGE;
static FRandom pr_hammeratk ("FHammerAtk");
//============================================================================
//
// A_FHammerAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
int i;
player_t *player;
FTranslatedLineTarget t;
PClassActor *hammertime;
if (NULL == (player = self->player))
{
return 0;
}
AActor *pmo=player->mo;
damage = 60+(pr_hammeratk()&63);
hammertime = PClass::FindActor("HammerPuff");
for (i = 0; i < 16; i++)
{
for (int j = 1; j >= -1; j -= 2)
{
angle = pmo->Angles.Yaw + j*i*(45. / 32);
slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE, &t, 0., ALF_CHECK3D);
if (t.linetarget != NULL)
{
P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, hammertime, true, &t);
if (t.linetarget != NULL)
{
AdjustPlayerAngle(pmo, &t);
if (t.linetarget->flags3 & MF3_ISMONSTER || t.linetarget->player)
{
t.linetarget->Thrust(t.angleFromSource, 10);
}
pmo->weaponspecial = false; // Don't throw a hammer
goto hammerdone;
}
}
}
}
// didn't find any targets in meleerange, so set to throw out a hammer
angle = pmo->Angles.Yaw;
slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE, NULL, 0., ALF_CHECK3D);
if (P_LineAttack (pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, hammertime, true) != NULL)
{
pmo->weaponspecial = false;
}
else
{
pmo->weaponspecial = true;
}
hammerdone:
// Don't spawn a hammer if the player doesn't have enough mana
if (player->ReadyWeapon == NULL ||
!player->ReadyWeapon->CheckAmmo (player->ReadyWeapon->bAltFire ?
AWeapon::AltFire : AWeapon::PrimaryFire, false, true))
{
pmo->weaponspecial = false;
}
return 0;
}
//============================================================================
//
// A_FHammerThrow
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FHammerThrow)
{
PARAM_ACTION_PROLOGUE(AActor);
AActor *mo;
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
if (!player->mo->weaponspecial)
{
return 0;
}
AWeapon *weapon = player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire, false))
return 0;
}
mo = P_SpawnPlayerMissile (player->mo, PClass::FindActor("HammerMissile"));
if (mo)
{
mo->special1 = 0;
}
return 0;
}

View file

@ -1,135 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
IMPLEMENT_CLASS(AFighterWeapon, false, false, false, false)
IMPLEMENT_CLASS(AClericWeapon, false, false, false, false)
IMPLEMENT_CLASS(AMageWeapon, false, false, false, false)
static FRandom pr_fpatk ("FPunchAttack");
//============================================================================
//
// AdjustPlayerAngle
//
//============================================================================
#define MAX_ANGLE_ADJUST (5.)
void AdjustPlayerAngle (AActor *pmo, FTranslatedLineTarget *t)
{
// normally this will adjust relative to the actual direction to the target,
// but with arbitrary portals that cannot be calculated so using the actual
// attack angle is the only option.
DAngle atkangle = t->unlinked ? t->angleFromSource : pmo->AngleTo(t->linetarget);
DAngle difference = deltaangle(pmo->Angles.Yaw, atkangle);
if (fabs(difference) > MAX_ANGLE_ADJUST)
{
if (difference > 0)
{
pmo->Angles.Yaw += MAX_ANGLE_ADJUST;
}
else
{
pmo->Angles.Yaw -= MAX_ANGLE_ADJUST;
}
}
else
{
pmo->Angles.Yaw = t->angleFromSource;
}
}
//============================================================================
//
// TryPunch
//
// Returns true if an actor was punched, false if not.
//
//============================================================================
static bool TryPunch(APlayerPawn *pmo, DAngle angle, int damage, int power)
{
PClassActor *pufftype;
FTranslatedLineTarget t;
DAngle slope;
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE, &t);
if (t.linetarget != NULL)
{
if (++pmo->weaponspecial >= 3)
{
damage <<= 1;
power *= 3;
pufftype = PClass::FindActor("HammerPuff");
}
else
{
pufftype = PClass::FindActor("PunchPuff");
}
P_LineAttack (pmo, angle, 2*MELEERANGE, slope, damage, NAME_Melee, pufftype, true, &t);
if (t.linetarget != NULL)
{
if (t.linetarget->player != NULL ||
(t.linetarget->Mass != INT_MAX && (t.linetarget->flags3 & MF3_ISMONSTER)))
{
t.linetarget->Thrust(t.angleFromSource, power);
}
AdjustPlayerAngle (pmo, &t);
return true;
}
}
return false;
}
//============================================================================
//
// A_FPunchAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FPunchAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
int damage;
int i;
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
APlayerPawn *pmo = player->mo;
damage = 40+(pr_fpatk()&15);
for (i = 0; i < 16; i++)
{
if (TryPunch(pmo, pmo->Angles.Yaw + i*(45./16), damage, 2) ||
TryPunch(pmo, pmo->Angles.Yaw - i*(45./16), damage, 2))
{ // hit something
if (pmo->weaponspecial >= 3)
{
pmo->weaponspecial = 0;
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState("Fire2"));
S_Sound (pmo, CHAN_VOICE, "*fistgrunt", 1, ATTN_NORM);
}
return 0;
}
}
// didn't find any creatures, so try to strike any walls
pmo->weaponspecial = 0;
DAngle slope = P_AimLineAttack (pmo, pmo->Angles.Yaw, MELEERANGE);
P_LineAttack (pmo, pmo->Angles.Yaw, MELEERANGE, slope, damage, NAME_Melee, PClass::FindActor("PunchPuff"), true);
return 0;
}

View file

@ -1,146 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "a_weaponpiece.h"
#include "vm.h"
*/
static FRandom pr_quietusdrop ("QuietusDrop");
static FRandom pr_fswordflame ("FSwordFlame");
//==========================================================================
//============================================================================
//
// A_DropQuietusPieces
//
//============================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropWeaponPieces)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(p1, AActor);
PARAM_CLASS(p2, AActor);
PARAM_CLASS(p3, AActor);
for (int i = 0, j = 0; i < 3; ++i)
{
PClassActor *cls = j == 0 ? p1 : j == 1 ? p2 : p3;
if (cls)
{
AActor *piece = Spawn (cls, self->Pos(), ALLOW_REPLACE);
if (piece != NULL)
{
piece->Vel = self->Vel + DAngle(i*120.).ToVector(1);
piece->flags |= MF_DROPPED;
j = (j == 0) ? (pr_quietusdrop() & 1) + 1 : 3-j;
}
}
}
return 0;
}
// Fighter Sword Missile ----------------------------------------------------
class AFSwordMissile : public AActor
{
DECLARE_CLASS (AFSwordMissile, AActor)
public:
int DoSpecialDamage(AActor *victim, int damage, FName damagetype);
};
IMPLEMENT_CLASS(AFSwordMissile, false, false, false, false)
int AFSwordMissile::DoSpecialDamage(AActor *victim, int damage, FName damagetype)
{
if (victim->player)
{
damage -= damage >> 2;
}
return damage;
}
//============================================================================
//
// A_FSwordAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FSwordAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
P_SpawnPlayerMissile (self, 0, 0, -10, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw + (45./4));
P_SpawnPlayerMissile (self, 0, 0, -5, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw + (45./8));
P_SpawnPlayerMissile (self, 0, 0, 0, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw);
P_SpawnPlayerMissile (self, 0, 0, 5, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw - (45./8));
P_SpawnPlayerMissile (self, 0, 0, 10, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw - (45./4));
S_Sound (self, CHAN_WEAPON, "FighterSwordFire", 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_FSwordFlames
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FSwordFlames)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
for (i = 1+(pr_fswordflame()&3); i; i--)
{
double xo = (pr_fswordflame() - 128) / 16.;
double yo = (pr_fswordflame() - 128) / 16.;
double zo = (pr_fswordflame() - 128) / 8.;
Spawn ("FSwordFlame", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
}
return 0;
}
//============================================================================
//
// A_FighterAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FighterAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target) return 0;
P_SpawnMissileAngle(self, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw + (45. / 4), 0);
P_SpawnMissileAngle(self, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw + (45. / 8), 0);
P_SpawnMissileAngle(self, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw, 0);
P_SpawnMissileAngle(self, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw - (45. / 8), 0);
P_SpawnMissileAngle(self, RUNTIME_CLASS(AFSwordMissile), self->Angles.Yaw - (45. / 4), 0);
S_Sound (self, CHAN_WEAPON, "FighterSwordFire", 1, ATTN_NORM);
return 0;
}

View file

@ -1,455 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "m_random.h"
#include "a_action.h"
#include "a_hexenglobal.h"
#include "w_wad.h"
#include "vm.h"
#include "g_level.h"
*/
EXTERN_CVAR(Bool, sv_unlimited_pickup)
static FRandom pr_poisonbag ("PoisonBag");
static FRandom pr_poisoncloud ("PoisonCloud");
static FRandom pr_poisoncloudd ("PoisonCloudDamage");
DECLARE_ACTION(A_CheckThrowBomb)
// Poison Bag Artifact (Flechette) ------------------------------------------
IMPLEMENT_CLASS(AArtiPoisonBag, false, false, false, false)
// Poison Bag 1 (The Cleric's) ----------------------------------------------
class AArtiPoisonBag1 : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBag1, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiPoisonBag1, false, false, false, false)
bool AArtiPoisonBag1::Use (bool pickup)
{
AActor *mo = Spawn("PoisonBag", Owner->Vec3Offset(
16 * Owner->Angles.Yaw.Cos(),
24 * Owner->Angles.Yaw.Sin(),
-Owner->Floorclip + 8), ALLOW_REPLACE);
if (mo)
{
mo->target = Owner;
return true;
}
return false;
}
// Poison Bag 2 (The Mage's) ------------------------------------------------
class AArtiPoisonBag2 : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBag2, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiPoisonBag2, false, false, false, false)
bool AArtiPoisonBag2::Use (bool pickup)
{
AActor *mo = Spawn("FireBomb", Owner->Vec3Offset(
16 * Owner->Angles.Yaw.Cos(),
24 * Owner->Angles.Yaw.Sin(),
-Owner->Floorclip + 8), ALLOW_REPLACE);
if (mo)
{
mo->target = Owner;
return true;
}
return false;
}
// Poison Bag 3 (The Fighter's) ---------------------------------------------
class AArtiPoisonBag3 : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBag3, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiPoisonBag3, false, false, false, false)
bool AArtiPoisonBag3::Use (bool pickup)
{
AActor *mo;
mo = Spawn("ThrowingBomb", Owner->PosPlusZ(35. - Owner->Floorclip + (Owner->player? Owner->player->crouchoffset : 0)), ALLOW_REPLACE);
if (mo)
{
mo->Angles.Yaw = Owner->Angles.Yaw + (((pr_poisonbag() & 7) - 4) * (360./256.));
/* Original flight code from Hexen
* mo->momz = 4*F.RACUNIT+((player->lookdir)<<(F.RACBITS-4));
* mo->z += player->lookdir<<(F.RACBITS-4);
* P_ThrustMobj(mo, mo->angle, mo->info->speed);
* mo->momx += player->mo->momx>>1;
* mo->momy += player->mo->momy>>1;
*/
// When looking straight ahead, it uses a z velocity of 4 while the xy velocity
// is as set by the projectile. To accommodate this with a proper trajectory, we
// aim the projectile ~20 degrees higher than we're looking at and increase the
// speed we fire at accordingly.
DAngle orgpitch = -Owner->Angles.Pitch;
DAngle modpitch = clamp<DAngle>(-Owner->Angles.Pitch + 20, -89., 89.);
DAngle angle = mo->Angles.Yaw;
double speed = DVector2(mo->Speed, 4.).Length();
double xyscale = speed * modpitch.Cos();
mo->Vel.Z = speed * modpitch.Sin();
mo->Vel.X = xyscale * angle.Cos() + Owner->Vel.X / 2;
mo->Vel.Y = xyscale * angle.Sin() + Owner->Vel.Y / 2;
mo->AddZ(mo->Speed * orgpitch.Sin());
mo->target = Owner;
mo->tics -= pr_poisonbag()&3;
P_CheckMissileSpawn(mo, Owner->radius);
return true;
}
return false;
}
// Poison Bag 4 (Generic Giver) ----------------------------------------------
class AArtiPoisonBagGiver : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBagGiver, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiPoisonBagGiver, false, false, false, false)
bool AArtiPoisonBagGiver::Use (bool pickup)
{
PClassActor *missiletype = PClass::FindActor(this->GetClass()->MissileName);
if (missiletype != NULL)
{
AActor *mo = Spawn (missiletype, Owner->Pos(), ALLOW_REPLACE);
if (mo != NULL)
{
if (mo->IsKindOf(RUNTIME_CLASS(AInventory)))
{
AInventory *inv = static_cast<AInventory *>(mo);
if (inv->CallTryPickup(Owner))
return true;
}
mo->Destroy(); // Destroy if not inventory or couldn't be picked up
}
}
return false;
}
// Poison Bag 5 (Generic Thrower) ----------------------------------------------
class AArtiPoisonBagShooter : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBagShooter, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiPoisonBagShooter, false, false, false, false)
bool AArtiPoisonBagShooter::Use (bool pickup)
{
PClassActor *missiletype = PClass::FindActor(this->GetClass()->MissileName);
if (missiletype != NULL)
{
AActor *mo = P_SpawnPlayerMissile(Owner, missiletype);
if (mo != NULL)
{
// automatic handling of seeker missiles
if (mo->flags2 & MF2_SEEKERMISSILE)
{
mo->tracer = Owner->target;
}
return true;
}
}
return false;
}
//============================================================================
//
// GetFlechetteType
//
//============================================================================
PClassActor *GetFlechetteType(AActor *other)
{
PClassActor *spawntype = NULL;
if (other->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
{
spawntype = static_cast<APlayerPawn*>(other)->FlechetteType;
}
if (spawntype == NULL)
{
// default fallback if nothing valid defined.
spawntype = RUNTIME_CLASS(AArtiPoisonBag3);
}
return spawntype;
}
//============================================================================
//
// AArtiPoisonBag :: HandlePickup
//
//============================================================================
bool AArtiPoisonBag::HandlePickup (AInventory *item)
{
// Only do special handling when picking up the base class
if (item->GetClass() != RUNTIME_CLASS(AArtiPoisonBag))
{
return Super::HandlePickup (item);
}
if (GetClass() == GetFlechetteType(Owner))
{
if (Amount < MaxAmount || sv_unlimited_pickup)
{
Amount += item->Amount;
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item->ItemFlags |= IF_PICKUPGOOD;
}
return true;
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
//============================================================================
//
// AArtiPoisonBag :: CreateCopy
//
//============================================================================
AInventory *AArtiPoisonBag::CreateCopy (AActor *other)
{
// Only the base class gets special handling
if (GetClass() != RUNTIME_CLASS(AArtiPoisonBag))
{
return Super::CreateCopy (other);
}
AInventory *copy;
PClassActor *spawntype = GetFlechetteType(other);
copy = static_cast<AInventory *>(Spawn (spawntype));
copy->Amount = Amount;
copy->MaxAmount = MaxAmount;
GoAwayAndDie ();
return copy;
}
//============================================================================
//
// AArtiPoisonBag :: BeginPlay
//
//============================================================================
void AArtiPoisonBag::BeginPlay ()
{
Super::BeginPlay ();
// If a subclass's specific icon is not defined, let it use the base class's.
if (!Icon.isValid())
{
AInventory *defbag;
// Why doesn't this work?
//defbag = GetDefault<AArtiPoisonBag>();
defbag = (AInventory *)GetDefaultByType (RUNTIME_CLASS(AArtiPoisonBag));
Icon = defbag->Icon;
}
}
// Poison Cloud -------------------------------------------------------------
class APoisonCloud : public AActor
{
DECLARE_CLASS (APoisonCloud, AActor)
public:
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
void BeginPlay ();
};
IMPLEMENT_CLASS(APoisonCloud, false, false, false, false)
void APoisonCloud::BeginPlay ()
{
Vel.X = MinVel; // missile objects must move to impact other objects
special1 = 24+(pr_poisoncloud()&7);
special2 = 0;
}
int APoisonCloud::DoSpecialDamage (AActor *victim, int damage, FName damagetype)
{
if (victim->player)
{
bool mate = (target != NULL && victim->player != target->player && victim->IsTeammate (target));
bool dopoison;
if (!mate)
{
dopoison = victim->player->poisoncount < 4;
}
else
{
dopoison = victim->player->poisoncount < (int)(4. * level.teamdamage);
}
if (dopoison)
{
int damage = 15 + (pr_poisoncloudd()&15);
if (mate)
{
damage = (int)(damage * level.teamdamage);
}
// Handle passive damage modifiers (e.g. PowerProtection)
if (victim->Inventory != NULL)
{
victim->Inventory->ModifyDamage(damage, damagetype, damage, true);
}
// Modify with damage factors
damage = victim->ApplyDamageFactor(damagetype, damage);
if (damage > 0)
{
P_PoisonDamage (victim->player, this,
15+(pr_poisoncloudd()&15), false); // Don't play painsound
// If successful, play the poison sound.
if (P_PoisonPlayer (victim->player, this, this->target, 50))
S_Sound (victim, CHAN_VOICE, "*poison", 1, ATTN_NORM);
}
}
return -1;
}
else if (!(victim->flags3 & MF3_ISMONSTER))
{ // only damage monsters/players with the poison cloud
return -1;
}
return damage;
}
//===========================================================================
//
// A_PoisonBagInit
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagInit)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = Spawn<APoisonCloud> (self->PosPlusZ(28.), ALLOW_REPLACE);
if (mo)
{
mo->target = self->target;
}
return 0;
}
//===========================================================================
//
// A_PoisonBagCheck
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagCheck)
{
PARAM_SELF_PROLOGUE(AActor);
if (--self->special1 <= 0)
{
self->SetState (self->FindState ("Death"));
}
else
{
return 0;
}
return 0;
}
//===========================================================================
//
// A_PoisonBagDamage
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagDamage)
{
PARAM_SELF_PROLOGUE(AActor);
int bobIndex;
P_RadiusAttack (self, self->target, 4, 40, self->DamageType, RADF_HURTSOURCE);
bobIndex = self->special2;
self->AddZ(BobSin(bobIndex) / 16);
self->special2 = (bobIndex + 1) & 63;
return 0;
}
//===========================================================================
//
// A_CheckThrowBomb
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CheckThrowBomb)
{
PARAM_SELF_PROLOGUE(AActor);
if (--self->health <= 0)
{
self->SetState (self->FindState(NAME_Death));
}
return 0;
}
//===========================================================================
//
// A_CheckThrowBomb2
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CheckThrowBomb2)
{
PARAM_SELF_PROLOGUE(AActor);
// [RH] Check using actual velocity, although the vel.z < 2 check still stands
if (self->Vel.Z < 2 && self->Vel.LengthSquared() < (9./4.))
{
self->SetState (self->SpawnState + 6);
self->SetZ(self->floorz);
self->Vel.Z = 0;
self->BounceFlags = BOUNCE_None;
self->flags &= ~MF_MISSILE;
}
CALL_ACTION(A_CheckThrowBomb, self);
return 0;
}

View file

@ -1,110 +0,0 @@
static FRandom pr_fly("GetOffMeFly");
//===========================================================================
//
// FindCorpse
//
// Finds a corpse to buzz around. We can't use a blockmap check because
// corpses generally aren't linked into the blockmap.
//
//===========================================================================
static AActor *FindCorpse(AActor *fly, sector_t *sec, int recurselimit)
{
AActor *fallback = NULL;
sec->validcount = validcount;
// Search the current sector
for (AActor *check = sec->thinglist; check != NULL; check = check->snext)
{
if (check == fly)
continue;
if (!(check->flags & MF_CORPSE))
continue;
if (!P_CheckSight(fly, check))
continue;
fallback = check;
if (pr_fly(2)) // 50% chance to try to pick a different corpse
continue;
return check;
}
if (--recurselimit <= 0 || (fallback != NULL && pr_fly(2)))
{
return fallback;
}
// Try neighboring sectors
for (int i = 0; i < sec->linecount; ++i)
{
line_t *line = sec->lines[i];
sector_t *sec2 = (line->frontsector == sec) ? line->backsector : line->frontsector;
if (sec2 != NULL && sec2->validcount != validcount)
{
AActor *neighbor = FindCorpse(fly, sec2, recurselimit);
if (neighbor != NULL)
{
return neighbor;
}
}
}
return fallback;
}
DEFINE_ACTION_FUNCTION(AActor, A_FlySearch)
{
// The version from the retail beta is not so great for general use:
// 1. Pick one of the first fifty thinkers at random.
// 2. Starting from that thinker, find one that is an actor, not itself,
// and within sight. Give up after 100 sequential thinkers.
// It's effectively useless if there are more than 150 thinkers on a map.
//
// So search the sectors instead. We can't potentially find something all
// the way on the other side of the map and we can't find invisible corpses,
// but at least we aren't crippled on maps with lots of stuff going on.
PARAM_SELF_PROLOGUE(AActor);
validcount++;
AActor *other = FindCorpse(self, self->Sector, 5);
if (other != NULL)
{
self->target = other;
self->SetState(self->FindState("Buzz"));
}
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_FlyBuzz)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *targ = self->target;
if (targ == NULL || !(targ->flags & MF_CORPSE) || pr_fly() < 5)
{
self->SetIdle();
return 0;
}
self->Angles.Yaw = self->AngleTo(targ);
self->args[0]++;
if (!P_TryMove(self, self->Pos().XY() + self->Angles.Yaw.ToVector(6), true))
{
self->SetIdle(true);
return 0;
}
if (self->args[0] & 2)
{
self->Vel.X += (pr_fly() - 128) / 512.;
self->Vel.Y += (pr_fly() - 128) / 512.;
}
int zrand = pr_fly();
if (targ->Z() + 5. < self->Z() && zrand > 150)
{
zrand = -zrand;
}
self->Vel.Z = zrand / 512.;
if (pr_fly() < 40)
{
S_Sound(self, CHAN_VOICE, self->ActiveSound, 0.5f, ATTN_STATIC);
}
return 0;
}

View file

@ -1,99 +0,0 @@
/*
#include "m_random.h"
#include "p_local.h"
#include "vm.h"
*/
static FRandom pr_fogspawn ("FogSpawn");
//==========================================================================
// Fog Variables:
//
// args[0] Speed (0..10) of fog
// args[1] Angle of spread (0..128)
// args[2] Frequency of spawn (1..10)
// args[3] Lifetime countdown
// args[4] Boolean: fog moving?
// special1 Internal: Counter for spawn frequency
// WeaveIndexZ Internal: Index into floatbob table
//
//==========================================================================
//==========================================================================
//
// A_FogSpawn
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FogSpawn)
{
PARAM_SELF_PROLOGUE(AActor);
static const char *fogs[3] =
{
"FogPatchSmall",
"FogPatchMedium",
"FogPatchLarge"
};
AActor *mo = NULL;
int delta;
if (self->special1-- > 0)
{
return 0;
}
self->special1 = self->args[2]; // Reset frequency count
mo = Spawn (fogs[pr_fogspawn()%3], self->Pos(), ALLOW_REPLACE);
if (mo)
{
delta = self->args[1];
if (delta==0) delta=1;
mo->Angles.Yaw = self->Angles.Yaw + (((pr_fogspawn() % delta) - (delta >> 1)) * (360 / 256.));
mo->target = self;
if (self->args[0] < 1) self->args[0] = 1;
mo->args[0] = (pr_fogspawn() % (self->args[0]))+1; // Random speed
mo->args[3] = self->args[3]; // Set lifetime
mo->args[4] = 1; // Set to moving
mo->WeaveIndexZ = pr_fogspawn()&63;
}
return 0;
}
//==========================================================================
//
// A_FogMove
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FogMove)
{
PARAM_SELF_PROLOGUE(AActor);
double speed = self->args[0];
int weaveindex;
if (!self->args[4])
{
return 0;
}
if (self->args[3]-- <= 0)
{
self->SetState (self->FindState(NAME_Death), true);
return 0;
}
if ((self->args[3] % 4) == 0)
{
weaveindex = self->WeaveIndexZ;
self->AddZ(BobSin(weaveindex) / 2);
self->WeaveIndexZ = (weaveindex + 1) & 63;
}
self->VelFromAngle(speed);
return 0;
}

View file

@ -1,95 +0,0 @@
/*
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "m_random.h"
#include "a_action.h"
#include "a_hexenglobal.h"
#include "gi.h"
#include "doomstat.h"
*/
#define HEAL_RADIUS_DIST 255.
static FRandom pr_healradius ("HealRadius");
// Healing Radius Artifact --------------------------------------------------
class AArtiHealingRadius : public AInventory
{
DECLARE_CLASS (AArtiHealingRadius, AInventory)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiHealingRadius, false, false, false, false)
bool AArtiHealingRadius::Use (bool pickup)
{
bool effective = false;
FName mode;
if (Owner->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
{
mode = static_cast<PClassPlayerPawn *>(Owner->GetClass())->HealingRadiusType;
}
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] &&
players[i].mo != NULL &&
players[i].mo->health > 0 &&
players[i].mo->Distance2D (Owner) <= HEAL_RADIUS_DIST)
{
// Q: Is it worth it to make this selectable as a player property?
// A: Probably not - but it sure doesn't hurt.
bool gotsome=false;
switch (mode)
{
case NAME_Armor:
for (int j = 0; j < 4; ++j)
{
AHexenArmor *armor = Spawn<AHexenArmor> ();
armor->health = j;
armor->Amount = 1;
if (!armor->CallTryPickup (players[i].mo))
{
armor->Destroy ();
}
else
{
gotsome = true;
}
}
break;
case NAME_Mana:
{
int amount = 50 + (pr_healradius() % 50);
if (players[i].mo->GiveAmmo (dyn_cast<PClassAmmo>(PClass::FindClass(NAME_Mana1)), amount) ||
players[i].mo->GiveAmmo (dyn_cast<PClassAmmo>(PClass::FindClass(NAME_Mana2)), amount))
{
gotsome = true;
}
break;
}
default:
//case NAME_Health:
gotsome = P_GiveBody (players[i].mo, 50 + (pr_healradius()%50));
break;
}
if (gotsome)
{
S_Sound (players[i].mo, CHAN_AUTO, "MysticIncant", 1, ATTN_NORM);
effective=true;
}
}
}
return effective;
}

View file

@ -1,968 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "a_action.h"
#include "m_random.h"
#include "a_hexenglobal.h"
#include "i_system.h"
#include "vm.h"
#include "g_level.h"
*/
//============================================================================
//
// Sorcerer stuff
//
// Sorcerer Variables
// specialf1 Angle of ball 1 (all others relative to that)
// StopBall which ball to stop at in stop mode (MT_???)
// args[0] Defense time
// args[1] Number of full rotations since stopping mode
// args[2] Target orbit speed for acceleration/deceleration
// args[3] Movement mode (see SORC_ macros)
// args[4] Current ball orbit speed
// Sorcerer Ball Variables
// specialf1 Previous angle of ball (for woosh)
// special2 Countdown of rapid fire (FX4)
//============================================================================
#define SORCBALL_INITIAL_SPEED 7
#define SORCBALL_TERMINAL_SPEED 25
#define SORCBALL_SPEED_ROTATIONS 5
#define SORC_DEFENSE_TIME 255
#define SORC_DEFENSE_HEIGHT 45
#define BOUNCE_TIME_UNIT (35/2)
#define SORCFX4_RAPIDFIRE_TIME (6*3) // 3 seconds
#define SORCFX4_SPREAD_ANGLE 20
#define SORC_DECELERATE 0
#define SORC_ACCELERATE 1
#define SORC_STOPPING 2
#define SORC_FIRESPELL 3
#define SORC_STOPPED 4
#define SORC_NORMAL 5
#define SORC_FIRING_SPELL 6
#define BALL1_ANGLEOFFSET 0.
#define BALL2_ANGLEOFFSET 120.
#define BALL3_ANGLEOFFSET 240.
void A_SlowBalls (AActor *actor);
void A_StopBalls (AActor *actor);
void A_AccelBalls (AActor *actor);
void A_DecelBalls (AActor *actor);
void A_SorcOffense2 (AActor *actor);
void A_DoBounceCheck (AActor *actor, const char *sound);
static FRandom pr_heresiarch ("Heresiarch");
// The Heresiarch him/itself ------------------------------------------------
class AHeresiarch : public AActor
{
DECLARE_CLASS (AHeresiarch, AActor)
public:
PClassActor *StopBall;
DAngle BallAngle;
void Serialize(FSerializer &arc);
void Die (AActor *source, AActor *inflictor, int dmgflags);
};
IMPLEMENT_CLASS(AHeresiarch, false, false, false, false)
void AHeresiarch::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("stopball", StopBall)
("ballangle", BallAngle);
}
void AHeresiarch::Die (AActor *source, AActor *inflictor, int dmgflags)
{
// The heresiarch just executes a script instead of a special upon death
int script = special;
special = 0;
Super::Die (source, inflictor, dmgflags);
if (script != 0)
{
P_StartScript (this, NULL, script, level.MapName, NULL, 0, 0);
}
}
// Base class for the balls flying around the Heresiarch's head -------------
class ASorcBall : public AActor
{
DECLARE_CLASS (ASorcBall, AActor)
public:
virtual void DoFireSpell ();
virtual void SorcUpdateBallAngle ();
virtual void CastSorcererSpell ();
DAngle AngleOffset;
DAngle OldAngle;
void Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("angleoffset", AngleOffset)
("oldangle", OldAngle);
}
bool SpecialBlastHandling (AActor *source, double strength)
{ // don't blast sorcerer balls
return false;
}
};
IMPLEMENT_CLASS(ASorcBall, false, false, false, false)
// First ball (purple) - fires projectiles ----------------------------------
class ASorcBall1 : public ASorcBall
{
DECLARE_CLASS (ASorcBall1, ASorcBall)
public:
void BeginPlay ()
{
Super::BeginPlay ();
AngleOffset = BALL1_ANGLEOFFSET;
}
virtual void DoFireSpell ();
virtual void SorcUpdateBallAngle ();
virtual void CastSorcererSpell ();
};
IMPLEMENT_CLASS(ASorcBall1, false, false, false, false)
// Second ball (blue) - generates the shield --------------------------------
class ASorcBall2 : public ASorcBall
{
DECLARE_CLASS (ASorcBall2, ASorcBall)
public:
void BeginPlay ()
{
Super::BeginPlay ();
AngleOffset = BALL2_ANGLEOFFSET;
}
virtual void CastSorcererSpell ();
};
IMPLEMENT_CLASS(ASorcBall2, false, false, false, false)
// Third ball (green) - summons Bishops -------------------------------------
class ASorcBall3 : public ASorcBall
{
DECLARE_CLASS (ASorcBall3, ASorcBall)
public:
void BeginPlay ()
{
Super::BeginPlay ();
AngleOffset = BALL3_ANGLEOFFSET;
}
virtual void CastSorcererSpell ();
};
IMPLEMENT_CLASS(ASorcBall3, false, false, false, false)
// Sorcerer spell 1 (The burning, bouncing head thing) ----------------------
//============================================================================
//
// SorcBall::DoFireSpell
//
//============================================================================
void ASorcBall::DoFireSpell ()
{
CastSorcererSpell ();
target->args[3] = SORC_STOPPED;
}
//============================================================================
//
// SorcBall1::DoFireSpell
//
//============================================================================
void ASorcBall1::DoFireSpell ()
{
if (pr_heresiarch() < 200)
{
S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE);
special2 = SORCFX4_RAPIDFIRE_TIME;
args[4] = 128;
target->args[3] = SORC_FIRING_SPELL;
}
else
{
Super::DoFireSpell ();
}
}
//============================================================================
//
// A_SorcSpinBalls
//
// Spawn spinning balls above head - actor is sorcerer
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcSpinBalls)
{
PARAM_SELF_PROLOGUE(AHeresiarch);
AActor *mo;
self->SpawnState += 2; // [RH] Don't spawn balls again
A_SlowBalls(self);
self->args[0] = 0; // Currently no defense
self->args[3] = SORC_NORMAL;
self->args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed
self->BallAngle = 1.;
DVector3 pos = self->PosPlusZ(-self->Floorclip + self->Height);
mo = Spawn("SorcBall1", pos, NO_REPLACE);
if (mo)
{
mo->target = self;
mo->special2 = SORCFX4_RAPIDFIRE_TIME;
}
mo = Spawn("SorcBall2", pos, NO_REPLACE);
if (mo) mo->target = self;
mo = Spawn("SorcBall3", pos, NO_REPLACE);
if (mo) mo->target = self;
return 0;
}
//============================================================================
//
// A_SorcBallOrbit
//
// - actor is ball
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcBallOrbit)
{
PARAM_SELF_PROLOGUE(ASorcBall);
// [RH] If no parent, then die instead of crashing
if (self->target == NULL)
{
self->SetState (self->FindState(NAME_Pain));
return 0;
}
int mode = self->target->args[3];
AHeresiarch *parent = barrier_cast<AHeresiarch *>(self->target);
double dist = parent->radius - (self->radius*2);
#if 0
// This cannot happen anymore because this is defined locally in SorcBall
if (!self->IsKindOf (RUNTIME_CLASS(ASorcBall)))
{
I_Error ("Corrupted sorcerer:\nTried to use a %s", self->GetClass()->TypeName.GetChars());
}
#endif
if (self->target->health <= 0)
{
self->SetState (self->FindState(NAME_Pain));
return 0;
}
DAngle prevangle = self->OldAngle;
DAngle baseangle = parent->BallAngle;
DAngle angle = baseangle + self->AngleOffset;
self->Angles.Yaw = angle;
switch (mode)
{
case SORC_NORMAL: // Balls rotating normally
self->SorcUpdateBallAngle ();
break;
case SORC_DECELERATE: // Balls decelerating
A_DecelBalls(self);
self->SorcUpdateBallAngle ();
break;
case SORC_ACCELERATE: // Balls accelerating
A_AccelBalls(self);
self->SorcUpdateBallAngle ();
break;
case SORC_STOPPING: // Balls stopping
if ((parent->StopBall == self->GetClass()) &&
(parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
absangle(angle, parent->Angles.Yaw) < 42.1875)
{
// Can stop now
self->target->args[3] = SORC_FIRESPELL;
self->target->args[4] = 0;
// Set angle so self angle == sorcerer angle
parent->BallAngle = parent->Angles.Yaw - self->AngleOffset;
}
else
{
self->SorcUpdateBallAngle ();
}
break;
case SORC_FIRESPELL: // Casting spell
if (parent->StopBall == self->GetClass())
{
// Put sorcerer into special throw spell anim
if (parent->health > 0)
parent->SetState (parent->FindState("Attack1"));
self->DoFireSpell ();
}
break;
case SORC_FIRING_SPELL:
if (parent->StopBall == self->GetClass())
{
if (self->special2-- <= 0)
{
// Done rapid firing
parent->args[3] = SORC_STOPPED;
// Back to orbit balls
if (parent->health > 0)
parent->SetState (parent->FindState("Attack2"));
}
else
{
// Do rapid fire spell
A_SorcOffense2(self);
}
}
break;
case SORC_STOPPED: // Balls stopped
default:
break;
}
if ( angle.BAMs() < prevangle.BAMs() && (parent->args[4]==SORCBALL_TERMINAL_SPEED))
{
parent->args[1]++; // Bump rotation counter
// Completed full rotation - make woosh sound
S_Sound (self, CHAN_BODY, "SorcererBallWoosh", 1, ATTN_NORM);
}
self->OldAngle = angle; // Set previous angle
DVector3 pos = parent->Vec3Angle(dist, angle, -parent->Floorclip + parent->Height);
self->SetOrigin (pos, true);
self->floorz = parent->floorz;
self->ceilingz = parent->ceilingz;
return 0;
}
//============================================================================
//
// A_SpeedBalls
//
// Set balls to speed mode - self is sorcerer
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SpeedBalls)
{
PARAM_SELF_PROLOGUE(AActor);
self->args[3] = SORC_ACCELERATE; // speed mode
self->args[2] = SORCBALL_TERMINAL_SPEED; // target speed
return 0;
}
//============================================================================
//
// A_SlowBalls
//
// Set balls to slow mode - actor is sorcerer
//
//============================================================================
void A_SlowBalls(AActor *self)
{
self->args[3] = SORC_DECELERATE; // slow mode
self->args[2] = SORCBALL_INITIAL_SPEED; // target speed
}
//============================================================================
//
// A_StopBalls
//
// Instant stop when rotation gets to ball in special2
// self is sorcerer
//
//============================================================================
void A_StopBalls(AActor *scary)
{
AHeresiarch *self = static_cast<AHeresiarch *> (scary);
int chance = pr_heresiarch();
self->args[3] = SORC_STOPPING; // stopping mode
self->args[1] = 0; // Reset rotation counter
if ((self->args[0] <= 0) && (chance < 200))
{
self->StopBall = RUNTIME_CLASS(ASorcBall2); // Blue
}
else if((self->health < (self->SpawnHealth() >> 1)) &&
(chance < 200))
{
self->StopBall = RUNTIME_CLASS(ASorcBall3); // Green
}
else
{
self->StopBall = RUNTIME_CLASS(ASorcBall1); // Yellow
}
}
//============================================================================
//
// A_AccelBalls
//
// Increase ball orbit speed - actor is ball
//
//============================================================================
void A_AccelBalls(AActor *self)
{
AActor *sorc = self->target;
if (sorc->args[4] < sorc->args[2])
{
sorc->args[4]++;
}
else
{
sorc->args[3] = SORC_NORMAL;
if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
{
// Reached terminal velocity - stop balls
A_StopBalls(sorc);
}
}
}
//============================================================================
//
// A_DecelBalls
//
// Decrease ball orbit speed - actor is ball
//
//============================================================================
void A_DecelBalls(AActor *self)
{
AActor *sorc = self->target;
if (sorc->args[4] > sorc->args[2])
{
sorc->args[4]--;
}
else
{
sorc->args[3] = SORC_NORMAL;
}
}
//============================================================================
//
// ASorcBall1::SorcUpdateBallAngle
//
// Update angle if first ball
//============================================================================
void ASorcBall1::SorcUpdateBallAngle ()
{
barrier_cast<AHeresiarch*>(target)->BallAngle += target->args[4];
}
//============================================================================
//
// ASorcBall::SorcUpdateBallAngle
//
//============================================================================
void ASorcBall::SorcUpdateBallAngle ()
{
}
//============================================================================
//
// ASorcBall::CastSorcererSpell
//
// Make noise and change the parent sorcerer's animation
//
//============================================================================
void ASorcBall::CastSorcererSpell ()
{
S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE);
// Put sorcerer into throw spell animation
if (target->health > 0)
target->SetState (target->FindState("Attack2"));
}
//============================================================================
//
// ASorcBall2::CastSorcererSpell
//
// Defensive
//
//============================================================================
void ASorcBall2::CastSorcererSpell ()
{
Super::CastSorcererSpell ();
AActor *parent = target;
AActor *mo;
mo = Spawn("SorcFX2", PosPlusZ(parent->Floorclip + SORC_DEFENSE_HEIGHT), ALLOW_REPLACE);
parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE;
parent->args[0] = SORC_DEFENSE_TIME;
if (mo) mo->target = parent;
}
//============================================================================
//
// ASorcBall3::CastSorcererSpell
//
// Reinforcements
//
//============================================================================
void ASorcBall3::CastSorcererSpell ()
{
Super::CastSorcererSpell ();
AActor *mo;
DAngle ang1, ang2;
AActor *parent = target;
ang1 = Angles.Yaw.Degrees - 45;
ang2 = Angles.Yaw.Degrees + 45;
PClassActor *cls = PClass::FindActor("SorcFX3");
if (health < (SpawnHealth()/3))
{ // Spawn 2 at a time
mo = P_SpawnMissileAngle(parent, cls, ang1, 4.);
if (mo) mo->target = parent;
mo = P_SpawnMissileAngle(parent, cls, ang2, 4.);
if (mo) mo->target = parent;
}
else
{
if (pr_heresiarch() < 128)
ang1 = ang2;
mo = P_SpawnMissileAngle(parent, cls, ang1, 4.);
if (mo) mo->target = parent;
}
}
/*
void A_SpawnReinforcements(AActor *actor)
{
AActor *parent = self->target;
AActor *mo;
DAngle ang;
ang = P_Random();
mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5.);
if (mo) mo->target = parent;
}
*/
//============================================================================
//
// SorcBall1::CastSorcererSpell
//
// Offensive
//
//============================================================================
void ASorcBall1::CastSorcererSpell ()
{
Super::CastSorcererSpell ();
AActor *mo;
DAngle ang1, ang2;
AActor *parent = target;
ang1 = Angles.Yaw.Degrees + 70;
ang2 = Angles.Yaw.Degrees - 70;
PClassActor *cls = PClass::FindActor("SorcFX1");
mo = P_SpawnMissileAngle (parent, cls, ang1, 0);
if (mo)
{
mo->target = parent;
mo->tracer = parent->target;
mo->args[4] = BOUNCE_TIME_UNIT;
mo->args[3] = 15; // Bounce time in seconds
}
mo = P_SpawnMissileAngle (parent, cls, ang2, 0);
if (mo)
{
mo->target = parent;
mo->tracer = parent->target;
mo->args[4] = BOUNCE_TIME_UNIT;
mo->args[3] = 15; // Bounce time in seconds
}
}
//============================================================================
//
// A_SorcOffense2
//
// Actor is ball
//
//============================================================================
void A_SorcOffense2(AActor *self)
{
DAngle ang1;
AActor *mo;
double delta;
int index;
AActor *parent = self->target;
AActor *dest = parent->target;
double dist;
// [RH] If no enemy, then don't try to shoot.
if (dest == NULL)
{
return;
}
index = self->args[4];
self->args[4] = (self->args[4] + 15) & 255;
delta = DAngle(index * (360 / 256.f)).Sin() * SORCFX4_SPREAD_ANGLE;
ang1 = self->Angles.Yaw + delta;
mo = P_SpawnMissileAngle(parent, PClass::FindActor("SorcFX4"), ang1, 0);
if (mo)
{
mo->special2 = 35*5/2; // 5 seconds
dist = mo->DistanceBySpeed(dest, mo->Speed);
mo->Vel.Z = (dest->Z() - mo->Z()) / dist;
}
}
//============================================================================
//
// A_SorcBossAttack
//
// Resume ball spinning
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcBossAttack)
{
PARAM_SELF_PROLOGUE(AActor);
self->args[3] = SORC_ACCELERATE;
self->args[2] = SORCBALL_INITIAL_SPEED;
return 0;
}
//============================================================================
//
// A_SpawnFizzle
//
// spell cast magic fizzle
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SpawnFizzle)
{
PARAM_SELF_PROLOGUE(AActor);
int speed = (int)self->Speed;
DAngle rangle;
AActor *mo;
int ix;
DVector3 pos = self->Vec3Angle(5., self->Angles.Yaw, -self->Floorclip + self->Height / 2. );
for (ix=0; ix<5; ix++)
{
mo = Spawn("SorcSpark1", pos, ALLOW_REPLACE);
if (mo)
{
rangle = self->Angles.Yaw + (pr_heresiarch() % 5) * (4096 / 360.);
mo->Vel.X = (pr_heresiarch() % speed) * rangle.Cos();
mo->Vel.Y = (pr_heresiarch() % speed) * rangle.Sin();
mo->Vel.Z = 2;
}
}
return 0;
}
//============================================================================
//
// A_SorcFX1Seek
//
// Yellow spell - offense
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcFX1Seek)
{
PARAM_SELF_PROLOGUE(AActor);
A_DoBounceCheck (self, "SorcererHeadScream");
P_SeekerMissile(self, 2, 6);
return 0;
}
//============================================================================
//
// A_SorcFX2Split
//
// Blue spell - defense
//
//============================================================================
//
// FX2 Variables
// specialf1 current angle
// special2
// args[0] 0 = CW, 1 = CCW
// args[1]
//============================================================================
// Split ball in two
DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Split)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = Spawn(self->GetClass(), self->Pos(), NO_REPLACE);
if (mo)
{
mo->target = self->target;
mo->args[0] = 0; // CW
mo->specialf1 = self->Angles.Yaw.Degrees; // Set angle
mo->SetState (mo->FindState("Orbit"));
}
mo = Spawn(self->GetClass(), self->Pos(), NO_REPLACE);
if (mo)
{
mo->target = self->target;
mo->args[0] = 1; // CCW
mo->specialf1 = self->Angles.Yaw.Degrees; // Set angle
mo->SetState (mo->FindState("Orbit"));
}
self->Destroy ();
return 0;
}
//============================================================================
//
// A_SorcFX2Orbit
//
// Orbit FX2 about sorcerer
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Orbit)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle angle;
DVector3 pos;
AActor *parent = self->target;
// [RH] If no parent, then disappear
if (parent == NULL)
{
self->Destroy();
return 0;
}
double dist = parent->radius;
if ((parent->health <= 0) || // Sorcerer is dead
(!parent->args[0])) // Time expired
{
self->SetState (self->FindState(NAME_Death));
parent->args[0] = 0;
parent->flags2 &= ~MF2_REFLECTIVE;
parent->flags2 &= ~MF2_INVULNERABLE;
}
if (self->args[0] && (parent->args[0]-- <= 0)) // Time expired
{
self->SetState (self->FindState(NAME_Death));
parent->args[0] = 0;
parent->flags2 &= ~MF2_REFLECTIVE;
}
// Move to new position based on angle
if (self->args[0]) // Counter clock-wise
{
self->specialf1 += 10;
angle = self->specialf1;
pos = parent->Vec3Angle(dist, angle, parent->Floorclip + SORC_DEFENSE_HEIGHT);
pos.Z += 15 * angle.Cos();
// Spawn trailer
Spawn("SorcFX2T1", pos, ALLOW_REPLACE);
}
else // Clock wise
{
self->specialf1 -= 10;
angle = self->specialf1;
pos = parent->Vec3Angle(dist, angle, parent->Floorclip + SORC_DEFENSE_HEIGHT);
pos.Z += 20 * angle.Sin();
// Spawn trailer
Spawn("SorcFX2T1", pos, ALLOW_REPLACE);
}
self->SetOrigin (pos, true);
self->floorz = parent->floorz;
self->ceilingz = parent->ceilingz;
return 0;
}
//============================================================================
//
// A_SpawnBishop
//
// Green spell - spawn bishops
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SpawnBishop)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = Spawn("Bishop", self->Pos(), ALLOW_REPLACE);
if (mo)
{
if (!P_TestMobjLocation(mo))
{
mo->ClearCounters();
mo->Destroy ();
}
else if (self->target != NULL)
{ // [RH] Make the new bishops inherit the Heriarch's target
mo->CopyFriendliness (self->target, true);
mo->master = self->target;
}
}
self->Destroy ();
return 0;
}
//============================================================================
//
// A_SorcererBishopEntry
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcererBishopEntry)
{
PARAM_SELF_PROLOGUE(AActor);
Spawn("SorcFX3Explosion", self->Pos(), ALLOW_REPLACE);
S_Sound (self, CHAN_VOICE, self->SeeSound, 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_SorcFX4Check
//
// FX4 - rapid fire balls
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcFX4Check)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->special2-- <= 0)
{
self->SetState (self->FindState(NAME_Death));
}
return 0;
}
//============================================================================
//
// A_SorcBallPop
//
// Ball death - bounce away in a random direction
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SorcBallPop)
{
PARAM_SELF_PROLOGUE(AActor);
S_Sound (self, CHAN_BODY, "SorcererBallPop", 1, ATTN_NONE);
self->flags &= ~MF_NOGRAVITY;
self->Gravity = 1. / 8;;
self->Vel.X = ((pr_heresiarch()%10)-5);
self->Vel.Y = ((pr_heresiarch()%10)-5);
self->Vel.Z = (2+(pr_heresiarch()%3));
self->args[4] = BOUNCE_TIME_UNIT; // Bounce time unit
self->args[3] = 5; // Bounce time in seconds
return 0;
}
//============================================================================
//
// A_DoBounceCheck
//
//============================================================================
void A_DoBounceCheck (AActor *self, const char *sound)
{
if (self->args[4]-- <= 0)
{
if (self->args[3]-- <= 0)
{
self->SetState (self->FindState(NAME_Death));
S_Sound (self, CHAN_BODY, sound, 1, ATTN_NONE);
}
else
{
self->args[4] = BOUNCE_TIME_UNIT;
}
}
}
//============================================================================
//
// A_BounceCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_BounceCheck)
{
PARAM_SELF_PROLOGUE(AActor);
A_DoBounceCheck (self, "SorcererBigBallExplode");
return 0;
}

View file

@ -1,43 +0,0 @@
#ifndef __A_HEXENGLOBAL_H__
#define __A_HEXENGLOBAL_H__
#include "d_player.h"
void AdjustPlayerAngle(AActor *pmo, FTranslatedLineTarget *t);
class AHolySpirit : public AActor
{
DECLARE_CLASS (AHolySpirit, AActor)
public:
bool Slam (AActor *thing);
bool SpecialBlastHandling (AActor *source, double strength);
};
class AFighterWeapon : public AWeapon
{
DECLARE_CLASS (AFighterWeapon, AWeapon);
public:
};
class AClericWeapon : public AWeapon
{
DECLARE_CLASS (AClericWeapon, AWeapon);
public:
};
class AMageWeapon : public AWeapon
{
DECLARE_CLASS (AMageWeapon, AWeapon);
public:
};
class AArtiPoisonBag : public AInventory
{
DECLARE_CLASS (AArtiPoisonBag, AInventory)
public:
bool HandlePickup (AInventory *item);
AInventory *CreateCopy (AActor *other);
void BeginPlay ();
};
#endif //__A_HEXENGLOBAL_H__

View file

@ -1,57 +0,0 @@
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "a_action.h"
#include "m_random.h"
#include "a_sharedglobal.h"
#include "a_hexenglobal.h"
#include "i_system.h"
#include "vm.h"
#include "gi.h"
#include "g_level.h"
#include "p_enemy.h"
#include "a_weaponpiece.h"
#include "doomstat.h"
#include "p_lnspec.h"
#include "p_terrain.h"
#include "m_bbox.h"
#include "ravenshared.h"
#include "v_palette.h"
#include "g_game.h"
#include "p_blockmap.h"
#include "r_utility.h"
#include "p_maputl.h"
#include "p_spec.h"
#include "serializer.h"
#include "vm.h"
// Include all the Hexen stuff here to reduce compile time
#include "a_blastradius.cpp"
#include "a_boostarmor.cpp"
#include "a_clericflame.cpp"
#include "a_clericholy.cpp"
#include "a_clericmace.cpp"
#include "a_clericstaff.cpp"
#include "a_dragon.cpp"
#include "a_fighteraxe.cpp"
#include "a_fighterhammer.cpp"
#include "a_fighterplayer.cpp"
#include "a_fighterquietus.cpp"
#include "a_flechette.cpp"
#include "a_flies.cpp"
#include "a_fog.cpp"
#include "a_healingradius.cpp"
#include "a_heresiarch.cpp"
#include "a_hexenspecialdecs.cpp"
#include "a_iceguy.cpp"
#include "a_korax.cpp"
#include "a_magecone.cpp"
#include "a_magelightning.cpp"
#include "a_magestaff.cpp"
#include "a_pig.cpp"
#include "a_serpent.cpp"
#include "a_spike.cpp"
#include "a_summon.cpp"
#include "a_teleportother.cpp"
#include "a_wraith.cpp"

View file

@ -1,394 +0,0 @@
/*
** Decorations that do special things
*/
/*
#include "actor.h"
#include "info.h"
#include "a_action.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_local.h"
#include "p_lnspec.h"
#include "a_hexenglobal.h"
#include "vm.h"
#include "g_level.h"
#include "doomstat.h"
*/
static FRandom pr_pottery ("PotteryExplode");
static FRandom pr_bit ("PotteryChooseBit");
static FRandom pr_drip ("CorpseBloodDrip");
static FRandom pr_foo ("CorpseExplode");
static FRandom pr_leaf ("LeafSpawn");
static FRandom pr_leafthrust ("LeafThrust");
static FRandom pr_leafcheck ("LeafCheck");
static FRandom pr_shroom ("PoisonShroom");
static FRandom pr_soaexplode ("SoAExplode");
// Pottery1 ------------------------------------------------------------------
void A_PotteryExplode (AActor *);
void A_PotteryChooseBit (AActor *);
void A_PotteryCheck (AActor *);
class APottery1 : public AActor
{
DECLARE_CLASS (APottery1, AActor)
public:
void HitFloor ();
};
IMPLEMENT_CLASS(APottery1, false, false, false, false)
void APottery1::HitFloor ()
{
Super::HitFloor ();
P_DamageMobj (this, NULL, NULL, 25, NAME_None);
}
//============================================================================
//
// A_PotteryExplode
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PotteryExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo = NULL;
int i;
for(i = (pr_pottery()&3)+3; i; i--)
{
mo = Spawn ("PotteryBit", self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + (pr_pottery()%5));
mo->Vel.X = pr_pottery.Random2() / 64.;
mo->Vel.Y = pr_pottery.Random2() / 64.;
mo->Vel.Z = ((pr_pottery() & 7) + 5) * 0.75;
}
}
S_Sound (mo, CHAN_BODY, "PotteryExplode", 1, ATTN_NORM);
// Spawn an item?
PClassActor *type = P_GetSpawnableType(self->args[0]);
if (type != NULL)
{
if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS))
|| !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER))
{ // Only spawn monsters if not -nomonsters
Spawn (type, self->Pos(), ALLOW_REPLACE);
}
}
return 0;
}
//============================================================================
//
// A_PotteryChooseBit
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PotteryChooseBit)
{
PARAM_SELF_PROLOGUE(AActor);
self->SetState (self->FindState(NAME_Death) + 1 + 2*(pr_bit()%5));
self->tics = 256+(pr_bit()<<1);
return 0;
}
//============================================================================
//
// A_PotteryCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PotteryCheck)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
for(i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
AActor *pmo = players[i].mo;
if (P_CheckSight (self, pmo) && (absangle(pmo->AngleTo(self), pmo->Angles.Yaw) <= 45))
{ // Previous state (pottery bit waiting state)
self->SetState (self->state - 1);
return 0;
}
}
}
return 0;
}
// Lynched corpse (no heart) ------------------------------------------------
class AZCorpseLynchedNoHeart : public AActor
{
DECLARE_CLASS (AZCorpseLynchedNoHeart, AActor)
public:
void PostBeginPlay ();
};
IMPLEMENT_CLASS(AZCorpseLynchedNoHeart, false, false, false, false)
void AZCorpseLynchedNoHeart::PostBeginPlay ()
{
Super::PostBeginPlay ();
Spawn ("BloodPool", PosAtZ(floorz), ALLOW_REPLACE);
}
//============================================================================
//
// A_CorpseBloodDrip
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CorpseBloodDrip)
{
PARAM_SELF_PROLOGUE(AActor);
if (pr_drip() <= 128)
{
Spawn ("CorpseBloodDrip", self->PosPlusZ(self->Height / 2), ALLOW_REPLACE);
}
return 0;
}
//============================================================================
//
// A_CorpseExplode
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CorpseExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
for (i = (pr_foo()&3)+3; i; i--)
{
mo = Spawn ("CorpseBit", self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + (pr_foo()%3));
mo->Vel.X = pr_foo.Random2() / 64.;
mo->Vel.Y = pr_foo.Random2() / 64.;
mo->Vel.Z = ((pr_foo() & 7) + 5) * 0.75;
}
}
// Spawn a skull
mo = Spawn ("CorpseBit", self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + 3);
mo->Vel.X = pr_foo.Random2() / 64.;
mo->Vel.Y = pr_foo.Random2() / 64.;
mo->Vel.Z = ((pr_foo() & 7) + 5) * 0.75;
}
S_Sound (self, CHAN_BODY, self->DeathSound, 1, ATTN_IDLE);
self->Destroy ();
return 0;
}
//============================================================================
//
// A_LeafSpawn
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LeafSpawn)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
for (i = (pr_leaf()&3)+1; i; i--)
{
double xo = pr_leaf.Random2() / 4.;
double yo = pr_leaf.Random2() / 4.;
double zo = pr_leaf() / 4.;
mo = Spawn (pr_leaf()&1 ? PClass::FindActor ("Leaf1") : PClass::FindActor ("Leaf2"),
self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->Thrust(pr_leaf() / 128. + 3);
mo->target = self;
mo->special1 = 0;
}
}
return 0;
}
//============================================================================
//
// A_LeafThrust
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LeafThrust)
{
PARAM_SELF_PROLOGUE(AActor);
if (pr_leafthrust() <= 96)
{
self->Vel.Z += pr_leafthrust() / 128. + 1;
}
return 0;
}
//============================================================================
//
// A_LeafCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LeafCheck)
{
PARAM_SELF_PROLOGUE(AActor);
self->special1++;
if (self->special1 >= 20)
{
self->SetState (NULL);
return 0;
}
DAngle ang = self->target ? self->target->Angles.Yaw : self->Angles.Yaw;
if (pr_leafcheck() > 64)
{
if (self->Vel.X == 0 && self->Vel.Y == 0)
{
self->Thrust(ang, pr_leafcheck() / 128. + 1);
}
return 0;
}
self->SetState (self->SpawnState + 7);
self->Vel.Z = pr_leafcheck() / 128. + 1;
self->Thrust(ang, pr_leafcheck() / 128. + 2);
self->flags |= MF_MISSILE;
return 0;
}
//===========================================================================
//
// A_PoisonShroom
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PoisonShroom)
{
PARAM_SELF_PROLOGUE(AActor);
self->tics = 128 + (pr_shroom() << 1);
return 0;
}
//===========================================================================
//
// A_SoAExplode - Suit of Armor Explode
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SoAExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
for (i = 0; i < 10; i++)
{
double xo = (pr_soaexplode() - 128) / 16.;
double yo = (pr_soaexplode() - 128) / 16.;
double zo = pr_soaexplode()*self->Height / 256.;
mo = Spawn ("ZArmorChunk", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + i);
mo->Vel.X = pr_soaexplode.Random2() / 64.;
mo->Vel.Y = pr_soaexplode.Random2() / 64.;
mo->Vel.Z = (pr_soaexplode() & 7) + 5;
}
}
// Spawn an item?
PClassActor *type = P_GetSpawnableType(self->args[0]);
if (type != NULL)
{
if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS))
|| !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER))
{ // Only spawn monsters if not -nomonsters
Spawn (type, self->Pos(), ALLOW_REPLACE);
}
}
S_Sound (self, CHAN_BODY, self->DeathSound, 1, ATTN_NORM);
self->Destroy ();
return 0;
}
// Bell ---------------------------------------------------------------------
class AZBell : public AActor
{
DECLARE_CLASS (AZBell, AActor)
public:
void Activate (AActor *activator);
};
IMPLEMENT_CLASS(AZBell, false, false, false, false)
void AZBell::Activate (AActor *activator)
{
if (health > 0)
{
P_DamageMobj (this, activator, activator, 10, NAME_Melee, DMG_THRUSTLESS); // 'ring' the bell
}
}
//===========================================================================
//
// A_BellReset1
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_BellReset1)
{
PARAM_SELF_PROLOGUE(AActor);
self->flags |= MF_NOGRAVITY;
self->Height *= 4;
if (self->special)
{ // Initiate death action
P_ExecuteSpecial(self->special, NULL, NULL, false, self->args[0],
self->args[1], self->args[2], self->args[3], self->args[4]);
self->special = 0;
}
return 0;
}
//===========================================================================
//
// A_BellReset2
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_BellReset2)
{
PARAM_SELF_PROLOGUE(AActor);
self->flags |= MF_SHOOTABLE;
self->flags &= ~MF_CORPSE;
self->flags6 &= ~MF6_KILLED;
self->health = 5;
return 0;
}

View file

@ -1,132 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "p_enemy.h"
#include "a_action.h"
#include "m_random.h"
#include "vm.h"
*/
static FRandom pr_iceguylook ("IceGuyLook");
static FRandom pr_iceguychase ("IceGuyChase");
static const char *WispTypes[2] =
{
"IceGuyWisp1",
"IceGuyWisp2",
};
//============================================================================
//
// A_IceGuyLook
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_IceGuyLook)
{
PARAM_SELF_PROLOGUE(AActor);
double dist;
DAngle an;
CALL_ACTION(A_Look, self);
if (pr_iceguylook() < 64)
{
dist = (pr_iceguylook() - 128) * self->radius / 128.;
an = self->Angles.Yaw + 90;
Spawn(WispTypes[pr_iceguylook() & 1], self->Vec3Angle(dist, an, 60.), ALLOW_REPLACE);
}
return 0;
}
//============================================================================
//
// A_IceGuyChase
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_IceGuyChase)
{
PARAM_SELF_PROLOGUE(AActor);
double dist;
DAngle an;
AActor *mo;
A_Chase(stack, self);
if (pr_iceguychase() < 128)
{
dist = (pr_iceguychase() - 128) * self->radius / 128.;
an = self->Angles.Yaw + 90;
mo = Spawn(WispTypes[pr_iceguylook() & 1], self->Vec3Angle(dist, an, 60.), ALLOW_REPLACE);
if (mo)
{
mo->Vel = self->Vel;
mo->target = self;
}
}
return 0;
}
//============================================================================
//
// A_IceGuyAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_IceGuyAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if(!self->target)
{
return 0;
}
P_SpawnMissileXYZ(self->Vec3Angle(self->radius / 2, self->Angles.Yaw + 90, 40.), self, self->target, PClass::FindActor("IceGuyFX"));
P_SpawnMissileXYZ(self->Vec3Angle(self->radius / 2, self->Angles.Yaw - 90, 40.), self, self->target, PClass::FindActor("IceGuyFX"));
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
return 0;
}
//============================================================================
//
// A_IceGuyDie
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_IceGuyDie)
{
PARAM_SELF_PROLOGUE(AActor);
self->Vel.Zero();
self->Height = self->GetDefault()->Height;
CALL_ACTION(A_FreezeDeathChunks, self);
return 0;
}
//============================================================================
//
// A_IceGuyMissileExplode
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_IceGuyMissileExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
unsigned int i;
for (i = 0; i < 8; i++)
{
mo = P_SpawnMissileAngleZ (self, self->Z()+3, PClass::FindActor("IceGuyFX2"), DAngle(i*45.), -0.3);
if (mo)
{
mo->target = self->target;
}
}
return 0;
}

View file

@ -1,499 +0,0 @@
//===========================================================================
// Korax Variables
// tracer last teleport destination
// special2 set if "below half" script not yet run
//
// Korax Scripts (reserved)
// 249 Tell scripts that we are below half health
// 250-254 Control scripts (254 is only used when less than half health)
// 255 Death script
//
// Korax TIDs (reserved)
// 245 Reserved for Korax himself
// 248 Initial teleport destination
// 249 Teleport destination
// 250-254 For use in respective control scripts
// 255 For use in death script (spawn spots)
//===========================================================================
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "p_spec.h"
#include "s_sound.h"
#include "a_action.h"
#include "m_random.h"
#include "i_system.h"
#include "vm.h"
#include "g_level.h"
*/
const int KORAX_SPIRIT_LIFETIME = 5*TICRATE/5; // 5 seconds
const int KORAX_COMMAND_HEIGHT = 120;
const int KORAX_COMMAND_OFFSET = 27;
const int KORAX_TID = 245;
const int KORAX_FIRST_TELEPORT_TID = 248;
const int KORAX_TELEPORT_TID = 249;
const int KORAX_DELTAANGLE = 85;
const int KORAX_ARM_EXTENSION_SHORT = 40;
const int KORAX_ARM_EXTENSION_LONG = 55;
const int KORAX_ARM1_HEIGHT = 108;
const int KORAX_ARM2_HEIGHT = 82;
const int KORAX_ARM3_HEIGHT = 54;
const int KORAX_ARM4_HEIGHT = 104;
const int KORAX_ARM5_HEIGHT = 86;
const int KORAX_ARM6_HEIGHT = 53;
const double KORAX_BOLT_HEIGHT = 48.;
const int KORAX_BOLT_LIFETIME = 3;
static FRandom pr_koraxchase ("KoraxChase");
static FRandom pr_kspiritinit ("KSpiritInit");
static FRandom pr_koraxdecide ("KoraxDecide");
static FRandom pr_koraxmissile ("KoraxMissile");
static FRandom pr_koraxcommand ("KoraxCommand");
static FRandom pr_kspiritweave ("KSpiritWeave");
static FRandom pr_kspiritseek ("KSpiritSeek");
static FRandom pr_kspiritroam ("KSpiritRoam");
static FRandom pr_kmissile ("SKoraxMissile");
void A_KoraxChase (AActor *);
void A_KoraxStep (AActor *);
void A_KoraxStep2 (AActor *);
void A_KoraxDecide (AActor *);
void A_KoraxBonePop (AActor *);
void A_KoraxMissile (AActor *);
void A_KoraxCommand (AActor *);
void A_KSpiritRoam (AActor *);
void A_KBolt (AActor *);
void A_KBoltRaise (AActor *);
void KoraxFire (AActor *actor, PClassActor *type, int arm);
void KSpiritInit (AActor *spirit, AActor *korax);
AActor *P_SpawnKoraxMissile (const DVector3 &pos, AActor *source, AActor *dest, PClassActor *type);
extern void SpawnSpiritTail (AActor *spirit);
//============================================================================
//
// A_KoraxChase
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KoraxChase)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *spot;
if ((!self->special2) && (self->health <= (self->SpawnHealth()/2)))
{
FActorIterator iterator (KORAX_FIRST_TELEPORT_TID);
spot = iterator.Next ();
if (spot != NULL)
{
P_Teleport (self, spot->PosAtZ(ONFLOORZ), spot->Angles.Yaw, TELF_SOURCEFOG | TELF_DESTFOG);
}
P_StartScript (self, NULL, 249, NULL, NULL, 0, 0);
self->special2 = 1; // Don't run again
return 0;
}
if (self->target == NULL)
{
return 0;
}
if (pr_koraxchase()<30)
{
self->SetState (self->MissileState);
}
else if (pr_koraxchase()<30)
{
S_Sound (self, CHAN_VOICE, "KoraxActive", 1, ATTN_NONE);
}
// Teleport away
if (self->health < (self->SpawnHealth()>>1))
{
if (pr_koraxchase()<10)
{
FActorIterator iterator (KORAX_TELEPORT_TID);
if (self->tracer != NULL)
{ // Find the previous teleport destination
do
{
spot = iterator.Next ();
} while (spot != NULL && spot != self->tracer);
}
// Go to the next teleport destination
spot = iterator.Next ();
self->tracer = spot;
if (spot)
{
P_Teleport (self, spot->PosAtZ(ONFLOORZ), spot->Angles.Yaw, TELF_SOURCEFOG | TELF_DESTFOG);
}
}
}
return 0;
}
//============================================================================
//
// A_KoraxBonePop
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KoraxBonePop)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
// Spawn 6 spirits equalangularly
for (i = 0; i < 6; ++i)
{
mo = P_SpawnMissileAngle (self, PClass::FindActor("KoraxSpirit"), DAngle(60.*i), 5.);
if (mo)
{
KSpiritInit (mo, self);
}
}
P_StartScript (self, NULL, 255, NULL, NULL, 0, 0); // Death script
return 0;
}
//============================================================================
//
// KSpiritInit
//
//============================================================================
void KSpiritInit (AActor *spirit, AActor *korax)
{
spirit->health = KORAX_SPIRIT_LIFETIME;
spirit->tracer = korax; // Swarm around korax
spirit->WeaveIndexZ = 32 + (pr_kspiritinit() & 7); // Float bob index
spirit->args[0] = 10; // initial turn value
spirit->args[1] = 0; // initial look angle
// Spawn a tail for spirit
SpawnSpiritTail (spirit);
}
//============================================================================
//
// A_KoraxDecide
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KoraxDecide)
{
PARAM_SELF_PROLOGUE(AActor);
if (pr_koraxdecide()<220)
{
self->SetState (self->FindState("Attack"));
}
else
{
self->SetState (self->FindState("Command"));
}
return 0;
}
//============================================================================
//
// A_KoraxMissile
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KoraxMissile)
{
PARAM_SELF_PROLOGUE(AActor);
static const struct { const char *type, *sound; } choices[6] =
{
{ "WraithFX1", "WraithMissileFire" },
{ "Demon1FX1", "DemonMissileFire" },
{ "Demon2FX1", "DemonMissileFire" },
{ "FireDemonMissile", "FireDemonAttack" },
{ "CentaurFX", "CentaurLeaderAttack" },
{ "SerpentFX", "CentaurLeaderAttack" }
};
int type = pr_koraxmissile() % 6;
int i;
PClassActor *info;
S_Sound(self, CHAN_VOICE, "KoraxAttack", 1, ATTN_NORM);
info = PClass::FindActor(choices[type].type);
if (info == NULL)
{
I_Error("Unknown Korax missile: %s\n", choices[type].type);
}
// Fire all 6 missiles at once
S_Sound(self, CHAN_WEAPON, choices[type].sound, 1, ATTN_NONE);
for (i = 0; i < 6; ++i)
{
KoraxFire(self, info, i);
}
return 0;
}
//============================================================================
//
// A_KoraxCommand
//
// Call action code scripts (250-254)
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KoraxCommand)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle ang;
int numcommands;
S_Sound (self, CHAN_VOICE, "KoraxCommand", 1, ATTN_NORM);
// Shoot stream of lightning to ceiling
ang = self->Angles.Yaw - 90;
DVector3 pos = self->Vec3Angle(KORAX_COMMAND_OFFSET, ang, KORAX_COMMAND_HEIGHT);
Spawn("KoraxBolt", pos, ALLOW_REPLACE);
if (self->health <= (self->SpawnHealth() >> 1))
{
numcommands = 5;
}
else
{
numcommands = 4;
}
P_StartScript (self, NULL, 250+(pr_koraxcommand()%numcommands), NULL, NULL, 0, 0);
return 0;
}
//============================================================================
//
// KoraxFire
//
// Arm projectiles
// arm positions numbered:
// 1 top left
// 2 middle left
// 3 lower left
// 4 top right
// 5 middle right
// 6 lower right
//
//============================================================================
void KoraxFire (AActor *actor, PClassActor *type, int arm)
{
static const int extension[6] =
{
KORAX_ARM_EXTENSION_SHORT,
KORAX_ARM_EXTENSION_LONG,
KORAX_ARM_EXTENSION_LONG,
KORAX_ARM_EXTENSION_SHORT,
KORAX_ARM_EXTENSION_LONG,
KORAX_ARM_EXTENSION_LONG
};
static const int armheight[6] =
{
KORAX_ARM1_HEIGHT,
KORAX_ARM2_HEIGHT,
KORAX_ARM3_HEIGHT,
KORAX_ARM4_HEIGHT,
KORAX_ARM5_HEIGHT,
KORAX_ARM6_HEIGHT
};
DAngle ang = actor->Angles.Yaw + (arm < 3 ? -KORAX_DELTAANGLE : KORAX_DELTAANGLE);
DVector3 pos = actor->Vec3Angle(extension[arm], ang, armheight[arm] - actor->Floorclip);
P_SpawnKoraxMissile (pos, actor, actor->target, type);
}
//============================================================================
//
// A_KSpiritSeeker
//
//============================================================================
static void A_KSpiritSeeker (AActor *actor, DAngle thresh, DAngle turnMax)
{
int dir;
DAngle delta;
AActor *target;
double newZ;
double deltaZ;
target = actor->tracer;
if (target == NULL)
{
return;
}
dir = P_FaceMobj (actor, target, &delta);
if (delta > thresh)
{
delta /= 2;
if(delta > turnMax)
{
delta = turnMax;
}
}
if(dir)
{ // Turn clockwise
actor->Angles.Yaw += delta;
}
else
{ // Turn counter clockwise
actor->Angles.Yaw -= delta;
}
actor->VelFromAngle();
if (!(level.time&15)
|| actor->Z() > target->Z() + target->GetDefault()->Height
|| actor->Top() < target->Z())
{
newZ = target->Z() + pr_kspiritseek() * target->GetDefault()->Height / 256;
deltaZ = newZ-actor->Z();
if (fabs(deltaZ) > 15)
{
if(deltaZ > 0)
{
deltaZ = 15;
}
else
{
deltaZ = -15;
}
}
actor->Vel.Z = deltaZ + actor->DistanceBySpeed(target, actor->Speed);
}
return;
}
//============================================================================
//
// A_KSpiritRoam
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KSpiritRoam)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->health-- <= 0)
{
S_Sound (self, CHAN_VOICE, "SpiritDie", 1, ATTN_NORM);
self->SetState (self->FindState("Death"));
}
else
{
if (self->tracer)
{
A_KSpiritSeeker(self, (double)self->args[0], self->args[0] * 2.);
}
int xyspeed = (pr_kspiritweave() % 5);
int zspeed = (pr_kspiritweave() % 5);
A_Weave(self, xyspeed, zspeed, 4., 2.);
if (pr_kspiritroam()<50)
{
S_Sound (self, CHAN_VOICE, "SpiritActive", 1, ATTN_NONE);
}
}
return 0;
}
//============================================================================
//
// A_KBolt
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KBolt)
{
PARAM_SELF_PROLOGUE(AActor);
// Countdown lifetime
if (self->special1-- <= 0)
{
self->Destroy ();
}
return 0;
}
//============================================================================
//
// A_KBoltRaise
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_KBoltRaise)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
// Spawn a child upward
double z = self->Z() + KORAX_BOLT_HEIGHT;
if ((z + KORAX_BOLT_HEIGHT) < self->ceilingz)
{
mo = Spawn("KoraxBolt", self->PosAtZ(z), ALLOW_REPLACE);
if (mo)
{
mo->special1 = KORAX_BOLT_LIFETIME;
}
}
else
{
// Maybe cap it off here
}
return 0;
}
//============================================================================
//
// P_SpawnKoraxMissile
//
//============================================================================
AActor *P_SpawnKoraxMissile (const DVector3 &pos, AActor *source, AActor *dest, PClassActor *type)
{
AActor *th;
DAngle an;
double dist;
th = Spawn (type, pos, ALLOW_REPLACE);
th->target = source; // Originator
an = th->AngleTo(dest);
if (dest->flags & MF_SHADOW)
{ // Invisible target
an += pr_kmissile.Random2() * (45/256.);
}
th->Angles.Yaw = an;
th->VelFromAngle();
dist = dest->DistanceBySpeed(th, th->Speed);
th->Vel.Z = (dest->Z() - pos.Z + 30) / dist;
return (P_CheckMissileSpawn(th, source->radius) ? th : NULL);
}

View file

@ -1,185 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
*/
const int SHARDSPAWN_LEFT = 1;
const int SHARDSPAWN_RIGHT = 2;
const int SHARDSPAWN_UP = 4;
const int SHARDSPAWN_DOWN = 8;
static FRandom pr_cone ("FireConePL1");
void A_FireConePL1 (AActor *actor);
void A_ShedShard (AActor *);
// Frost Missile ------------------------------------------------------------
class AFrostMissile : public AActor
{
DECLARE_CLASS (AFrostMissile, AActor)
public:
int DoSpecialDamage (AActor *victim, int damage, FName damagetype);
};
IMPLEMENT_CLASS(AFrostMissile, false, false, false, false)
int AFrostMissile::DoSpecialDamage (AActor *victim, int damage, FName damagetype)
{
if (special2 > 0)
{
damage <<= special2;
}
return damage;
}
//============================================================================
//
// A_FireConePL1
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FireConePL1)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
int i;
AActor *mo;
bool conedone=false;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
S_Sound (self, CHAN_WEAPON, "MageShardsFire", 1, ATTN_NORM);
damage = 90+(pr_cone()&15);
for (i = 0; i < 16; i++)
{
angle = self->Angles.Yaw + i*(45./16);
slope = P_AimLineAttack (self, angle, MELEERANGE, &t, 0., ALF_CHECK3D);
if (t.linetarget)
{
P_DamageMobj (t.linetarget, self, self, damage, NAME_Ice, DMG_USEANGLE, t.angleFromSource.Degrees);
conedone = true;
break;
}
}
// didn't find any creatures, so fire projectiles
if (!conedone)
{
mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(AFrostMissile));
if (mo)
{
mo->special1 = SHARDSPAWN_LEFT|SHARDSPAWN_DOWN|SHARDSPAWN_UP
|SHARDSPAWN_RIGHT;
mo->special2 = 3; // Set sperm count (levels of reproductivity)
mo->target = self;
mo->args[0] = 3; // Mark Initial shard as super damage
}
}
return 0;
}
//============================================================================
//
// A_ShedShard
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ShedShard)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int spawndir = self->special1;
int spermcount = self->special2;
if (spermcount <= 0)
{
return 0; // No sperm left
}
self->special2 = 0;
spermcount--;
// every so many calls, spawn a new missile in its set directions
if (spawndir & SHARDSPAWN_LEFT)
{
mo = P_SpawnMissileAngleZSpeed(self, self->Z(), RUNTIME_CLASS(AFrostMissile),
self->Angles.Yaw + 5, 0, (20. + 2 * spermcount), self->target);
if (mo)
{
mo->special1 = SHARDSPAWN_LEFT;
mo->special2 = spermcount;
mo->Vel.Z = self->Vel.Z;
mo->args[0] = (spermcount==3)?2:0;
}
}
if (spawndir & SHARDSPAWN_RIGHT)
{
mo = P_SpawnMissileAngleZSpeed(self, self->Z(), RUNTIME_CLASS(AFrostMissile),
self->Angles.Yaw - 5, 0, (20. + 2 * spermcount), self->target);
if (mo)
{
mo->special1 = SHARDSPAWN_RIGHT;
mo->special2 = spermcount;
mo->Vel.Z = self->Vel.Z;
mo->args[0] = (spermcount==3)?2:0;
}
}
if (spawndir & SHARDSPAWN_UP)
{
mo = P_SpawnMissileAngleZSpeed(self, self->Z() + 8., RUNTIME_CLASS(AFrostMissile),
self->Angles.Yaw, 0, (15. + 2 * spermcount), self->target);
if (mo)
{
mo->Vel.Z = self->Vel.Z;
if (spermcount & 1) // Every other reproduction
mo->special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
else
mo->special1 = SHARDSPAWN_UP;
mo->special2 = spermcount;
mo->args[0] = (spermcount==3)?2:0;
}
}
if (spawndir & SHARDSPAWN_DOWN)
{
mo = P_SpawnMissileAngleZSpeed(self, self->Z() - 4., RUNTIME_CLASS(AFrostMissile),
self->Angles.Yaw, 0, (15. + 2 * spermcount), self->target);
if (mo)
{
mo->Vel.Z = self->Vel.Z;
if (spermcount & 1) // Every other reproduction
mo->special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
else
mo->special1 = SHARDSPAWN_DOWN;
mo->special2 = spermcount;
mo->target = self->target;
mo->args[0] = (spermcount==3)?2:0;
}
}
return 0;
}

View file

@ -1,367 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
#include "g_level.h"
*/
#define ZAGSPEED 1.
static FRandom pr_lightningready ("LightningReady");
static FRandom pr_lightningclip ("LightningClip");
static FRandom pr_zap ("LightningZap");
static FRandom pr_zapf ("LightningZapF");
static FRandom pr_hit ("LightningHit");
DECLARE_ACTION(A_LightningClip)
DECLARE_ACTION(A_LightningZap)
// Lightning ----------------------------------------------------------------
class ALightning : public AActor
{
DECLARE_CLASS (ALightning, AActor)
public:
int SpecialMissileHit (AActor *victim);
};
IMPLEMENT_CLASS(ALightning, false, false, false, false)
int ALightning::SpecialMissileHit (AActor *thing)
{
if (thing->flags&MF_SHOOTABLE && thing != target)
{
if (thing->Mass != INT_MAX)
{
thing->Vel.X += Vel.X / 16;
thing->Vel.Y += Vel.Y / 16;
}
if ((!thing->player && !(thing->flags2&MF2_BOSS))
|| !(level.time&1))
{
P_DamageMobj(thing, this, target, 3, NAME_Electric);
if (!(S_IsActorPlayingSomething (this, CHAN_WEAPON, -1)))
{
S_Sound (this, CHAN_WEAPON, this->AttackSound, 1, ATTN_NORM);
}
if (thing->flags3&MF3_ISMONSTER && pr_hit() < 64)
{
thing->Howl ();
}
}
health--;
if (health <= 0 || thing->health <= 0)
{
return 0;
}
if (flags3 & MF3_FLOORHUGGER)
{
if (lastenemy && ! lastenemy->tracer)
{
lastenemy->tracer = thing;
}
}
else if (!tracer)
{
tracer = thing;
}
}
return 1; // lightning zaps through all sprites
}
// Lightning Zap ------------------------------------------------------------
class ALightningZap : public AActor
{
DECLARE_CLASS (ALightningZap, AActor)
public:
int SpecialMissileHit (AActor *thing);
};
IMPLEMENT_CLASS(ALightningZap, false, false, false, false)
int ALightningZap::SpecialMissileHit (AActor *thing)
{
AActor *lmo;
if (thing->flags&MF_SHOOTABLE && thing != target)
{
lmo = lastenemy;
if (lmo)
{
if (lmo->flags3 & MF3_FLOORHUGGER)
{
if (lmo->lastenemy && !lmo->lastenemy->tracer)
{
lmo->lastenemy->tracer = thing;
}
}
else if (!lmo->tracer)
{
lmo->tracer = thing;
}
if (!(level.time&3))
{
lmo->health--;
}
}
}
return -1;
}
//============================================================================
//
// A_LightningReady
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningReady)
{
PARAM_ACTION_PROLOGUE(AActor);
DoReadyWeapon(self);
if (pr_lightningready() < 160)
{
S_Sound (self, CHAN_WEAPON, "MageLightningReady", 1, ATTN_NORM);
}
return 0;
}
//============================================================================
//
// A_LightningClip
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningClip)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *cMo;
AActor *target = NULL;
int zigZag;
if (self->flags3 & MF3_FLOORHUGGER)
{
if (self->lastenemy == NULL)
{
return 0;
}
self->SetZ(self->floorz);
target = self->lastenemy->tracer;
}
else if (self->flags3 & MF3_CEILINGHUGGER)
{
self->SetZ(self->ceilingz - self->Height);
target = self->tracer;
}
if (self->flags3 & MF3_FLOORHUGGER)
{ // floor lightning zig-zags, and forces the ceiling lightning to mimic
cMo = self->lastenemy;
zigZag = pr_lightningclip();
if((zigZag > 128 && self->special1 < 2) || self->special1 < -2)
{
self->Thrust(self->Angles.Yaw + 90, ZAGSPEED);
if(cMo)
{
cMo->Thrust(self->Angles.Yaw + 90, ZAGSPEED);
}
self->special1++;
}
else
{
self->Thrust(self->Angles.Yaw - 90, ZAGSPEED);
if(cMo)
{
cMo->Thrust(self->Angles.Yaw - 90, ZAGSPEED);
}
self->special1--;
}
}
if(target)
{
if(target->health <= 0)
{
P_ExplodeMissile(self, NULL, NULL);
}
else
{
self->Angles.Yaw = self->AngleTo(target);
self->VelFromAngle(self->Speed / 2);
}
}
return 0;
}
//============================================================================
//
// A_LightningZap
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningZap)
{
PARAM_SELF_PROLOGUE(AActor);
PClassActor *lightning = PClass::FindActor(self->GetClass()->MissileName);
AActor *mo;
if (lightning == NULL)
{
lightning = PClass::FindActor(NAME_LightningZap);
}
CALL_ACTION(A_LightningClip, self);
self->health -= 8;
if (self->health <= 0)
{
self->SetState (self->FindState(NAME_Death));
return 0;
}
double deltaX = (pr_zap() - 128) * self->radius / 256;
double deltaY = (pr_zap() - 128) * self->radius / 256;
double deltaZ = (self->flags3 & MF3_FLOORHUGGER) ? 10 : -10;
mo = Spawn(lightning, self->Vec3Offset(deltaX, deltaY, deltaZ), ALLOW_REPLACE);
if (mo)
{
mo->lastenemy = self;
mo->Vel.X = self->Vel.X;
mo->Vel.Y = self->Vel.Y;
mo->Vel.Z = (self->flags3 & MF3_FLOORHUGGER) ? 20 : -20;
mo->target = self->target;
}
if ((self->flags3 & MF3_FLOORHUGGER) && pr_zapf() < 160)
{
S_Sound (self, CHAN_BODY, self->ActiveSound, 1, ATTN_NORM);
}
return 0;
}
//============================================================================
//
// A_MLightningAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MLightningAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_CLASS_DEF(floor, AActor);
PARAM_CLASS_DEF(ceiling, AActor);
AActor *fmo, *cmo;
fmo = P_SpawnPlayerMissile (self, floor);
cmo = P_SpawnPlayerMissile (self, ceiling);
if (fmo)
{
fmo->special1 = 0;
fmo->lastenemy = cmo;
CALL_ACTION(A_LightningZap, fmo);
}
if (cmo)
{
cmo->tracer = NULL;
cmo->lastenemy = fmo;
CALL_ACTION(A_LightningZap, cmo);
}
S_Sound (self, CHAN_BODY, "MageLightningFire", 1, ATTN_NORM);
if (self->player != NULL)
{
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
{
weapon->DepleteAmmo (weapon->bAltFire);
}
}
return 0;
}
//============================================================================
//
// A_ZapMimic
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ZapMimic)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = self->lastenemy;
if (mo)
{
if (mo->state >= mo->FindState(NAME_Death))
{
P_ExplodeMissile (self, NULL, NULL);
}
else
{
self->Vel.X = mo->Vel.X;
self->Vel.Y = mo->Vel.Y;
}
}
return 0;
}
//============================================================================
//
// A_LastZap
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LastZap)
{
PARAM_SELF_PROLOGUE(AActor);
PClassActor *lightning = PClass::FindActor(self->GetClass()->MissileName);
AActor *mo;
if (lightning == NULL)
{
lightning = PClass::FindActor(NAME_LightningZap);
}
mo = Spawn(lightning, self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->FindState (NAME_Death));
mo->Vel.Z = 40;
mo->SetDamage(0);
}
return 0;
}
//============================================================================
//
// A_LightningRemove
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningRemove)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = self->lastenemy;
if (mo)
{
mo->lastenemy = NULL;
P_ExplodeMissile (mo, NULL, NULL);
}
return 0;
}

View file

@ -1,265 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "m_random.h"
#include "s_sound.h"
#include "a_hexenglobal.h"
#include "gstrings.h"
#include "a_weaponpiece.h"
#include "vm.h"
#include "doomstat.h"
*/
static FRandom pr_mstafftrack ("MStaffTrack");
static FRandom pr_bloodscourgedrop ("BloodScourgeDrop");
void A_MStaffTrack (AActor *);
void A_DropBloodscourgePieces (AActor *);
void A_MStaffAttack (AActor *actor);
void A_MStaffPalette (AActor *actor);
static AActor *FrontBlockCheck (AActor *mo, int index, void *);
static divline_t BlockCheckLine;
//==========================================================================
// The Mages's Staff (Bloodscourge) -----------------------------------------
class AMWeapBloodscourge : public AMageWeapon
{
DECLARE_CLASS (AMWeapBloodscourge, AMageWeapon)
public:
void Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("mstaffcount", MStaffCount);
}
PalEntry GetBlend ()
{
if (paletteflash & PF_HEXENWEAPONS)
{
if (MStaffCount == 3)
return PalEntry(128, 100, 73, 0);
else if (MStaffCount == 2)
return PalEntry(128, 125, 92, 0);
else if (MStaffCount == 1)
return PalEntry(128, 150, 110, 0);
else
return PalEntry(0, 0, 0, 0);
}
else
{
return PalEntry (MStaffCount * 128 / 3, 151, 110, 0);
}
}
BYTE MStaffCount;
};
IMPLEMENT_CLASS(AMWeapBloodscourge, false, false, false, false)
// Mage Staff FX2 (Bloodscourge) --------------------------------------------
class AMageStaffFX2 : public AActor
{
DECLARE_CLASS(AMageStaffFX2, AActor)
public:
int SpecialMissileHit (AActor *victim);
bool SpecialBlastHandling (AActor *source, double strength);
};
IMPLEMENT_CLASS(AMageStaffFX2, false, false, false, false)
int AMageStaffFX2::SpecialMissileHit (AActor *victim)
{
if (victim != target &&
!victim->player &&
!(victim->flags2 & MF2_BOSS))
{
P_DamageMobj (victim, this, target, 10, NAME_Fire);
return 1; // Keep going
}
return -1;
}
bool AMageStaffFX2::SpecialBlastHandling (AActor *source, double strength)
{
// Reflect to originator
tracer = target;
target = source;
return true;
}
//============================================================================
//============================================================================
//
// MStaffSpawn
//
//============================================================================
void MStaffSpawn (AActor *pmo, DAngle angle, AActor *alttarget)
{
AActor *mo;
FTranslatedLineTarget t;
mo = P_SpawnPlayerMissile (pmo, 0, 0, 8, RUNTIME_CLASS(AMageStaffFX2), angle, &t);
if (mo)
{
mo->target = pmo;
if (t.linetarget && !t.unlinked)
mo->tracer = t.linetarget;
else
mo->tracer = alttarget;
}
}
//============================================================================
//
// A_MStaffAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MStaffAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
AMWeapBloodscourge *weapon = static_cast<AMWeapBloodscourge *> (self->player->ReadyWeapon);
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
angle = self->Angles.Yaw;
// [RH] Let's try and actually track what the player aimed at
P_AimLineAttack (self, angle, PLAYERMISSILERANGE, &t, 32.);
if (t.linetarget == NULL)
{
BlockCheckLine.x = self->X();
BlockCheckLine.y = self->Y();
BlockCheckLine.dx = -angle.Sin();
BlockCheckLine.dy = -angle.Cos();
t.linetarget = P_BlockmapSearch (self, 10, FrontBlockCheck);
}
MStaffSpawn (self, angle, t.linetarget);
MStaffSpawn (self, angle-5, t.linetarget);
MStaffSpawn (self, angle+5, t.linetarget);
S_Sound (self, CHAN_WEAPON, "MageStaffFire", 1, ATTN_NORM);
weapon->MStaffCount = 3;
return 0;
}
//============================================================================
//
// A_MStaffPalette
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MStaffPalette)
{
PARAM_ACTION_PROLOGUE(AActor);
if (self->player != NULL)
{
AMWeapBloodscourge *weapon = static_cast<AMWeapBloodscourge *> (self->player->ReadyWeapon);
if (weapon != NULL && weapon->MStaffCount != 0)
{
weapon->MStaffCount--;
}
}
return 0;
}
//============================================================================
//
// A_MStaffTrack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MStaffTrack)
{
PARAM_SELF_PROLOGUE(AActor);
if ((self->tracer == 0) && (pr_mstafftrack()<50))
{
self->tracer = P_RoughMonsterSearch (self, 10, true);
}
P_SeekerMissile(self, 2, 10);
return 0;
}
//============================================================================
//
// FrontBlockCheck
//
// [RH] Like RoughBlockCheck, but it won't return anything behind a line.
//
//============================================================================
static AActor *FrontBlockCheck (AActor *mo, int index, void *)
{
FBlockNode *link;
for (link = blocklinks[index]; link != NULL; link = link->NextActor)
{
if (link->Me != mo)
{
if (P_PointOnDivlineSide(link->Me->X(), link->Me->Y(), &BlockCheckLine) == 0 &&
mo->IsOkayToAttack (link->Me))
{
return link->Me;
}
}
}
return NULL;
}
//============================================================================
//
// MStaffSpawn2 - for use by mage class boss
//
//============================================================================
void MStaffSpawn2 (AActor *actor, DAngle angle)
{
AActor *mo;
mo = P_SpawnMissileAngleZ (actor, actor->Z()+40, RUNTIME_CLASS(AMageStaffFX2), angle, 0.);
if (mo)
{
mo->target = actor;
mo->tracer = P_BlockmapSearch (mo, 10, FrontBlockCheck);
}
}
//============================================================================
//
// A_MStaffAttack2 - for use by mage class boss
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MageAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
{
return 0;
}
DAngle angle = self->Angles.Yaw;
MStaffSpawn2(self, angle);
MStaffSpawn2(self, angle - 5);
MStaffSpawn2(self, angle + 5);
S_Sound(self, CHAN_WEAPON, "MageStaffFire", 1, ATTN_NORM);
return 0;
}

View file

@ -1,107 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "a_pickups.h"
#include "p_local.h"
#include "a_sharedglobal.h"
#include "p_enemy.h"
#include "d_event.h"
#include "gstrings.h"
#include "vm.h"
*/
static FRandom pr_snoutattack ("SnoutAttack");
static FRandom pr_pigattack ("PigAttack");
static FRandom pr_pigplayerthink ("PigPlayerThink");
// Pig player ---------------------------------------------------------------
class APigPlayer : public APlayerPawn
{
DECLARE_CLASS (APigPlayer, APlayerPawn)
public:
void MorphPlayerThink ();
};
IMPLEMENT_CLASS(APigPlayer, false, false, false, false)
void APigPlayer::MorphPlayerThink ()
{
if (player->morphTics & 15)
{
return;
}
if(Vel.X == 0 && Vel.Y == 0 && pr_pigplayerthink() < 64)
{ // Snout sniff
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState("Grunt"));
}
S_Sound (this, CHAN_VOICE, "PigActive1", 1, ATTN_NORM); // snort
return;
}
if (pr_pigplayerthink() < 48)
{
S_Sound (this, CHAN_VOICE, "PigActive", 1, ATTN_NORM);
}
}
//============================================================================
//
// A_SnoutAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SnoutAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
player_t *player;
AActor *puff;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
damage = 3+(pr_snoutattack()&3);
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
puff = P_LineAttack(player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "SnoutPuff", true, &t);
S_Sound(player->mo, CHAN_VOICE, "PigActive", 1, ATTN_NORM);
if(t.linetarget)
{
AdjustPlayerAngle(player->mo, &t);
if(puff != NULL)
{ // Bit something
S_Sound(player->mo, CHAN_VOICE, "PigAttack", 1, ATTN_NORM);
}
}
return 0;
}
//============================================================================
//
// A_PigPain
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PigPain)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_Pain, self);
if (self->Z() <= self->floorz)
{
self->Vel.Z = 3.5;
}
return 0;
}

View file

@ -1,311 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "p_enemy.h"
#include "a_action.h"
#include "m_random.h"
#include "p_terrain.h"
#include "vm.h"
*/
static FRandom pr_serpentchase ("SerpentChase");
static FRandom pr_serpenthump ("SerpentHump");
static FRandom pr_serpentattack ("SerpentAttack");
static FRandom pr_serpentmeattack ("SerpentMeAttack");
static FRandom pr_serpentgibs ("SerpentGibs");
static FRandom pr_delaygib ("DelayGib");
//============================================================================
//
// A_SerpentUnHide
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentUnHide)
{
PARAM_SELF_PROLOGUE(AActor);
self->renderflags &= ~RF_INVISIBLE;
self->Floorclip = 24;
return 0;
}
//============================================================================
//
// A_SerpentHide
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentHide)
{
PARAM_SELF_PROLOGUE(AActor);
self->renderflags |= RF_INVISIBLE;
self->Floorclip = 0;
return 0;
}
//============================================================================
//
// A_SerpentRaiseHump
//
// Raises the hump above the surface by raising the floorclip level
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentRaiseHump)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip -= 4;
return 0;
}
//============================================================================
//
// A_SerpentLowerHump
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentLowerHump)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip += 4;
return 0;
}
//============================================================================
//
// A_SerpentHumpDecide
//
// Decided whether to hump up, or if the mobj is a serpent leader,
// to missile attack
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentHumpDecide)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->MissileState != NULL)
{
if (pr_serpenthump() > 30)
{
return 0;
}
else if (pr_serpenthump() < 40)
{ // Missile attack
self->SetState (self->MeleeState);
return 0;
}
}
else if (pr_serpenthump() > 3)
{
return 0;
}
if (!self->CheckMeleeRange ())
{ // The hump shouldn't occur when within melee range
if (self->MissileState != NULL && pr_serpenthump() < 128)
{
self->SetState (self->MeleeState);
}
else
{
self->SetState (self->FindState ("Hump"));
S_Sound (self, CHAN_BODY, "SerpentActive", 1, ATTN_NORM);
}
}
return 0;
}
//============================================================================
//
// A_SerpentCheckForAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentCheckForAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
{
return 0;
}
if (self->MissileState != NULL)
{
if (!self->CheckMeleeRange ())
{
self->SetState (self->FindState ("Attack"));
return 0;
}
}
if (P_CheckMeleeRange2 (self))
{
self->SetState (self->FindState ("Walk"));
}
else if (self->CheckMeleeRange ())
{
if (pr_serpentattack() < 32)
{
self->SetState (self->FindState ("Walk"));
}
else
{
self->SetState (self->FindState ("Attack"));
}
}
return 0;
}
//============================================================================
//
// A_SerpentChooseAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentChooseAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target || self->CheckMeleeRange())
{
return 0;
}
if (self->MissileState != NULL)
{
self->SetState (self->MissileState);
}
return 0;
}
//============================================================================
//
// A_SerpentMeleeAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentMeleeAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
{
return 0;
}
if (self->CheckMeleeRange ())
{
int damage = pr_serpentmeattack.HitDice (5);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
S_Sound (self, CHAN_BODY, "SerpentMeleeHit", 1, ATTN_NORM);
}
if (pr_serpentmeattack() < 96)
{
CALL_ACTION(A_SerpentCheckForAttack, self);
}
return 0;
}
//============================================================================
//
// A_SerpentSpawnGibs
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentSpawnGibs)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
static const char *GibTypes[] =
{
"SerpentGib3",
"SerpentGib2",
"SerpentGib1"
};
for (int i = countof(GibTypes)-1; i >= 0; --i)
{
double x = (pr_serpentgibs() - 128) / 16.;
double y = (pr_serpentgibs() - 128) / 16.;
mo = Spawn (GibTypes[i], self->Vec2OffsetZ(x, y, self->floorz + 1), ALLOW_REPLACE);
if (mo)
{
mo->Vel.X = (pr_serpentgibs() - 128) / 1024.f;
mo->Vel.Y = (pr_serpentgibs() - 128) / 1024.f;
mo->Floorclip = 6;
}
}
return 0;
}
//============================================================================
//
// A_FloatGib
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FloatGib)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip -= 1;
return 0;
}
//============================================================================
//
// A_SinkGib
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SinkGib)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip += 1;;
return 0;
}
//============================================================================
//
// A_DelayGib
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DelayGib)
{
PARAM_SELF_PROLOGUE(AActor);
self->tics -= pr_delaygib()>>2;
return 0;
}
//============================================================================
//
// A_SerpentHeadCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentHeadCheck)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->Z() <= self->floorz)
{
if (Terrains[P_GetThingFloorType(self)].IsLiquid)
{
P_HitFloor (self);
self->SetState (NULL);
}
else
{
self->SetState (self->FindState(NAME_Death));
}
}
return 0;
}

View file

@ -1,189 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "m_random.h"
#include "p_enemy.h"
#include "p_local.h"
#include "a_sharedglobal.h"
#include "s_sound.h"
#include "m_bbox.h"
#include "vm.h"
*/
static FRandom pr_thrustraise ("ThrustRaise");
// Spike (thrust floor) -----------------------------------------------------
// AThrustFloor is just a container for all the spike states.
// All the real spikes subclass it.
class AThrustFloor : public AActor
{
DECLARE_CLASS (AThrustFloor, AActor)
HAS_OBJECT_POINTERS
public:
void Serialize(FSerializer &arc);
void Activate (AActor *activator);
void Deactivate (AActor *activator);
TObjPtr<AActor> DirtClump;
};
IMPLEMENT_CLASS(AThrustFloor, false, true, false, false)
IMPLEMENT_POINTERS_START(AThrustFloor)
IMPLEMENT_POINTER(DirtClump)
IMPLEMENT_POINTERS_END
void AThrustFloor::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("dirtclump", DirtClump);
}
void AThrustFloor::Activate (AActor *activator)
{
if (args[0] == 0)
{
S_Sound (this, CHAN_BODY, "ThrustSpikeLower", 1, ATTN_NORM);
renderflags &= ~RF_INVISIBLE;
if (args[1])
SetState (FindState ("BloodThrustRaise"));
else
SetState (FindState ("ThrustRaise"));
}
}
void AThrustFloor::Deactivate (AActor *activator)
{
if (args[0] == 1)
{
S_Sound (this, CHAN_BODY, "ThrustSpikeRaise", 1, ATTN_NORM);
if (args[1])
SetState (FindState ("BloodThrustLower"));
else
SetState (FindState ("ThrustLower"));
}
}
//===========================================================================
//
// Thrust floor stuff
//
// Thrust Spike Variables
// DirtClump pointer to dirt clump actor
// special2 speed of raise
// args[0] 0 = lowered, 1 = raised
// args[1] 0 = normal, 1 = bloody
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ThrustInitUp)
{
PARAM_SELF_PROLOGUE(AActor);
self->special2 = 5; // Raise speed
self->args[0] = 1; // Mark as up
self->Floorclip = 0;
self->flags = MF_SOLID;
self->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP;
self->special1 = 0L;
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_ThrustInitDn)
{
PARAM_SELF_PROLOGUE(AActor);
self->special2 = 5; // Raise speed
self->args[0] = 0; // Mark as down
self->Floorclip = self->GetDefault()->Height;
self->flags = 0;
self->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP;
self->renderflags = RF_INVISIBLE;
static_cast<AThrustFloor *>(self)->DirtClump =
Spawn("DirtClump", self->Pos(), ALLOW_REPLACE);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_ThrustRaise)
{
PARAM_SELF_PROLOGUE(AActor);
AThrustFloor *actor = static_cast<AThrustFloor *>(self);
if (A_RaiseMobj (actor, self->special2))
{ // Reached it's target height
actor->args[0] = 1;
if (actor->args[1])
actor->SetState (actor->FindState ("BloodThrustInit2"), true);
else
actor->SetState (actor->FindState ("ThrustInit2"), true);
}
// Lose the dirt clump
if ((actor->Floorclip < actor->Height) && actor->DirtClump)
{
actor->DirtClump->Destroy ();
actor->DirtClump = NULL;
}
// Spawn some dirt
if (pr_thrustraise()<40)
P_SpawnDirt (actor, actor->radius);
actor->special2++; // Increase raise speed
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_ThrustLower)
{
PARAM_SELF_PROLOGUE(AActor);
if (A_SinkMobj (self, 6))
{
self->args[0] = 0;
if (self->args[1])
self->SetState (self->FindState ("BloodThrustInit1"), true);
else
self->SetState (self->FindState ("ThrustInit1"), true);
}
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_ThrustImpale)
{
PARAM_SELF_PROLOGUE(AActor);
// This doesn't need to iterate through portals.
FPortalGroupArray check;
FMultiBlockThingsIterator it(check, self);
FMultiBlockThingsIterator::CheckResult cres;
while (it.Next(&cres))
{
double blockdist = self->radius + cres.thing->radius;
if (fabs(cres.thing->X() - cres.Position.X) >= blockdist || fabs(cres.thing->Y() - cres.Position.Y) >= blockdist)
continue;
// Q: Make this z-aware for everything? It never was before.
if (cres.thing->Top() < self->Z() || cres.thing->Z() > self->Top())
{
if (self->Sector->PortalGroup != cres.thing->Sector->PortalGroup)
continue;
}
if (!(cres.thing->flags & MF_SHOOTABLE) )
continue;
if (cres.thing == self)
continue; // don't clip against self
int newdam = P_DamageMobj (cres.thing, self, self, 10001, NAME_Crush);
P_TraceBleed (newdam > 0 ? newdam : 10001, cres.thing);
self->args[1] = 1; // Mark thrust thing as bloody
}
return 0;
}

View file

@ -1,85 +0,0 @@
/*
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "ravenshared.h"
#include "vm.h"
#include "g_level.h"
*/
void A_Summon (AActor *);
// Dark Servant Artifact ----------------------------------------------------
class AArtiDarkServant : public AInventory
{
DECLARE_CLASS (AArtiDarkServant, AInventory)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiDarkServant, false, false, false, false)
//============================================================================
//
// Activate the summoning artifact
//
//============================================================================
bool AArtiDarkServant::Use (bool pickup)
{
AActor *mo = P_SpawnPlayerMissile (Owner, PClass::FindActor("SummoningDoll"));
if (mo)
{
mo->target = Owner;
mo->tracer = Owner;
mo->Vel.Z = 5;
}
return true;
}
//============================================================================
//
// A_Summon
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_Summon)
{
PARAM_SELF_PROLOGUE(AActor);
AMinotaurFriend *mo;
mo = Spawn<AMinotaurFriend>(self->Pos(), ALLOW_REPLACE);
if (mo)
{
if (P_TestMobjLocation(mo) == false || !self->tracer)
{ // Didn't fit - change back to artifact
mo->Destroy();
AActor *arti = Spawn<AArtiDarkServant>(self->Pos(), ALLOW_REPLACE);
if (arti) arti->flags |= MF_DROPPED;
return 0;
}
mo->StartTime = level.maptime;
if (self->tracer->flags & MF_CORPSE)
{ // Master dead
mo->tracer = NULL; // No master
}
else
{
mo->tracer = self->tracer; // Pointer to master
AInventory *power = Spawn<APowerMinotaur>();
power->CallTryPickup(self->tracer);
mo->SetFriendPlayer(self->tracer->player);
}
// Make smoke puff
Spawn("MinotaurSmoke", self->Pos(), ALLOW_REPLACE);
S_Sound(self, CHAN_VOICE, mo->ActiveSound, 1, ATTN_NORM);
}
return 0;
}

View file

@ -1,198 +0,0 @@
/*
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "p_lnspec.h"
#include "m_random.h"
#include "vm.h"
#include "g_level.h"
#include "doomstat.h"
*/
#define TELEPORT_LIFE 1
static FRandom pr_telestarts ("TeleStarts");
static FRandom pr_teledm ("TeleDM");
void A_TeloSpawnA (AActor *);
void A_TeloSpawnB (AActor *);
void A_TeloSpawnC (AActor *);
void A_TeloSpawnD (AActor *);
void A_CheckTeleRing (AActor *);
void P_TeleportToPlayerStarts (AActor *victim);
void P_TeleportToDeathmatchStarts (AActor *victim);
// Teleport Other Artifact --------------------------------------------------
class AArtiTeleportOther : public AInventory
{
DECLARE_CLASS (AArtiTeleportOther, AInventory)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiTeleportOther, false, false, false, false)
// Teleport Other FX --------------------------------------------------------
class ATelOtherFX1 : public AActor
{
DECLARE_CLASS (ATelOtherFX1, AActor)
public:
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
};
IMPLEMENT_CLASS(ATelOtherFX1, false, false, false, false)
static void TeloSpawn (AActor *source, const char *type)
{
AActor *fx;
fx = Spawn (type, source->Pos(), ALLOW_REPLACE);
if (fx)
{
fx->special1 = TELEPORT_LIFE; // Lifetime countdown
fx->Angles.Yaw = source->Angles.Yaw;
fx->target = source->target;
fx->Vel = source->Vel / 2;
}
}
DEFINE_ACTION_FUNCTION(AActor, A_TeloSpawnA)
{
PARAM_SELF_PROLOGUE(AActor);
TeloSpawn (self, "TelOtherFX2");
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_TeloSpawnB)
{
PARAM_SELF_PROLOGUE(AActor);
TeloSpawn (self, "TelOtherFX3");
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_TeloSpawnC)
{
PARAM_SELF_PROLOGUE(AActor);
TeloSpawn (self, "TelOtherFX4");
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_TeloSpawnD)
{
PARAM_SELF_PROLOGUE(AActor);
TeloSpawn (self, "TelOtherFX5");
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_CheckTeleRing)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->special1-- <= 0)
{
self->SetState (self->FindState(NAME_Death));
}
return 0;
}
//===========================================================================
//
// Activate Teleport Other
//
//===========================================================================
bool AArtiTeleportOther::Use (bool pickup)
{
AActor *mo;
mo = P_SpawnPlayerMissile (Owner, RUNTIME_CLASS(ATelOtherFX1));
if (mo)
{
mo->target = Owner;
}
return true;
}
//===========================================================================
//
// Perform Teleport Other
//
//===========================================================================
int ATelOtherFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
if ((target->flags3 & MF3_ISMONSTER || target->player != NULL) &&
!(target->flags2 & MF2_BOSS) &&
!(target->flags3 & MF3_NOTELEOTHER))
{
if (target->player)
{
if (deathmatch)
P_TeleportToDeathmatchStarts (target);
else
P_TeleportToPlayerStarts (target);
}
else
{
// If death action, run it upon teleport
if (target->flags3 & MF3_ISMONSTER && target->special)
{
target->RemoveFromHash ();
P_ExecuteSpecial(target->special, NULL, level.flags & LEVEL_ACTOWNSPECIAL
? target : (AActor *)(this->target), false, target->args[0], target->args[1],
target->args[2], target->args[3], target->args[4]);
target->special = 0;
}
// Send all monsters to deathmatch spots
P_TeleportToDeathmatchStarts (target);
}
}
return -1;
}
//===========================================================================
//
// P_TeleportToPlayerStarts
//
//===========================================================================
void P_TeleportToPlayerStarts (AActor *victim)
{
DVector3 dest;
FPlayerStart *start = G_PickPlayerStart(0, PPS_FORCERANDOM | PPS_NOBLOCKINGCHECK);
dest = start->pos;
dest.Z = ONFLOORZ;
P_Teleport (victim, dest, (double)start->angle, TELF_SOURCEFOG | TELF_DESTFOG);
}
//===========================================================================
//
// P_TeleportToDeathmatchStarts
//
//===========================================================================
void P_TeleportToDeathmatchStarts (AActor *victim)
{
unsigned int i, selections;
DVector3 dest;
selections = deathmatchstarts.Size ();
if (selections > 0)
{
i = pr_teledm() % selections;
dest = deathmatchstarts[i].pos;
dest.Z = ONFLOORZ;
P_Teleport (victim, dest, (double)deathmatchstarts[i].angle, TELF_SOURCEFOG | TELF_DESTFOG);
}
else
{
P_TeleportToPlayerStarts (victim);
}
}

View file

@ -1,258 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "p_enemy.h"
#include "a_action.h"
#include "m_random.h"
#include "a_sharedglobal.h"
#include "vm.h"
*/
static FRandom pr_stealhealth ("StealHealth");
static FRandom pr_wraithfx2 ("WraithFX2");
static FRandom pr_wraithfx3 ("WraithFX3");
static FRandom pr_wraithfx4 ("WraithFX4");
//============================================================================
//
// A_WraithInit
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithInit)
{
PARAM_SELF_PROLOGUE(AActor);
self->AddZ(48);
// [RH] Make sure the wraith didn't go into the ceiling
if (self->Top() > self->ceilingz)
{
self->SetZ(self->ceilingz - self->Height);
}
self->WeaveIndexZ = 0; // index into floatbob
return 0;
}
//============================================================================
//
// A_WraithRaiseInit
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithRaiseInit)
{
PARAM_SELF_PROLOGUE(AActor);
self->renderflags &= ~RF_INVISIBLE;
self->flags2 &= ~MF2_NONSHOOTABLE;
self->flags3 &= ~MF3_DONTBLAST;
self->flags |= MF_SHOOTABLE|MF_SOLID;
self->Floorclip = self->Height;
return 0;
}
//============================================================================
//
// A_WraithRaise
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithRaise)
{
PARAM_SELF_PROLOGUE(AActor);
if (A_RaiseMobj (self, 2))
{
// Reached it's target height
// [RH] Once a buried wraith is fully raised, it should be
// morphable, right?
self->flags3 &= ~(MF3_DONTMORPH|MF3_SPECIALFLOORCLIP);
self->SetState (self->FindState("Chase"));
// [RH] Reset PainChance to a normal wraith's.
self->PainChance = GetDefaultByName ("Wraith")->PainChance;
}
P_SpawnDirt (self, self->radius);
return 0;
}
//============================================================================
//
// A_WraithMelee
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithMelee)
{
PARAM_SELF_PROLOGUE(AActor);
int amount;
// Steal health from target and give to self
if (self->CheckMeleeRange() && (pr_stealhealth()<220))
{
amount = pr_stealhealth.HitDice (2);
P_DamageMobj (self->target, self, self, amount, NAME_Melee);
self->health += amount;
}
return 0;
}
//============================================================================
//
// A_WraithFX2 - spawns sparkle tail of missile
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithFX2)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
DAngle angle;
int i;
for (i = 2; i; --i)
{
mo = Spawn ("WraithFX2", self->Pos(), ALLOW_REPLACE);
if(mo)
{
angle = pr_wraithfx2() * (360 / 1024.f);
if (pr_wraithfx2() >= 128)
{
angle = -angle;
}
angle += self->Angles.Yaw;
mo->Vel.X = ((pr_wraithfx2() / 512.) + 1) * angle.Cos();
mo->Vel.Y = ((pr_wraithfx2() / 512.) + 1) * angle.Sin();
mo->Vel.Z = 0;
mo->target = self;
mo->Floorclip = 10;
}
}
return 0;
}
//============================================================================
//
// A_WraithFX3
//
// Spawn an FX3 around the self during attacks
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithFX3)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int numdropped = pr_wraithfx3() % 15;
while (numdropped-- > 0)
{
double xo = (pr_wraithfx3() - 128) / 32.;
double yo = (pr_wraithfx3() - 128) / 32.;
double zo = pr_wraithfx3() / 64.;
mo = Spawn("WraithFX3", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->floorz = self->floorz;
mo->ceilingz = self->ceilingz;
mo->target = self;
}
}
return 0;
}
//============================================================================
//
// A_WraithFX4
//
// Spawn an FX4 during movement
//
//============================================================================
void A_WraithFX4 (AActor *self)
{
AActor *mo;
int chance = pr_wraithfx4();
bool spawn4, spawn5;
if (chance < 10)
{
spawn4 = true;
spawn5 = false;
}
else if (chance < 20)
{
spawn4 = false;
spawn5 = true;
}
else if (chance < 25)
{
spawn4 = true;
spawn5 = true;
}
else
{
spawn4 = false;
spawn5 = false;
}
if (spawn4)
{
double xo = (pr_wraithfx4() - 128) / 16.;
double yo = (pr_wraithfx4() - 128) / 16.;
double zo = (pr_wraithfx4() / 64.);
mo = Spawn ("WraithFX4", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->floorz = self->floorz;
mo->ceilingz = self->ceilingz;
mo->target = self;
}
}
if (spawn5)
{
double xo = (pr_wraithfx4() - 128) / 32.;
double yo = (pr_wraithfx4() - 128) / 32.;
double zo = (pr_wraithfx4() / 64.);
mo = Spawn ("WraithFX5", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->floorz = self->floorz;
mo->ceilingz = self->ceilingz;
mo->target = self;
}
}
}
//============================================================================
//
// A_WraithChase
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_WraithChase)
{
PARAM_SELF_PROLOGUE(AActor);
int weaveindex = self->WeaveIndexZ;
self->AddZ(BobSin(weaveindex));
self->WeaveIndexZ = (weaveindex + 2) & 63;
// if (self->Floorclip > 0)
// {
// P_SetMobjState(self, S_WRAITH_RAISE2);
// return;
// }
A_Chase (stack, self);
A_WraithFX4 (self);
return 0;
}

381
src/g_inventory/a_ammo.cpp Normal file
View file

@ -0,0 +1,381 @@
/*
** a_ammo.cpp
** Implements ammo and backpack items.
**
**---------------------------------------------------------------------------
** Copyright 2000-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "c_dispatch.h"
#include "d_player.h"
#include "serializer.h"
IMPLEMENT_CLASS(PClassAmmo, false, false)
PClassAmmo::PClassAmmo()
{
DropAmount = 0;
}
void PClassAmmo::DeriveData(PClass *newclass)
{
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassAmmo)));
Super::DeriveData(newclass);
PClassAmmo *newc = static_cast<PClassAmmo *>(newclass);
newc->DropAmount = DropAmount;
}
IMPLEMENT_CLASS(AAmmo, false, false)
DEFINE_FIELD(AAmmo, BackpackAmount)
DEFINE_FIELD(AAmmo, BackpackMaxAmount)
//===========================================================================
//
// AAmmo :: Serialize
//
//===========================================================================
void AAmmo::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
auto def = (AAmmo*)GetDefault();
arc("backpackamount", BackpackAmount, def->BackpackAmount)
("backpackmaxamount", BackpackMaxAmount, def->BackpackMaxAmount);
}
//===========================================================================
//
// AAmmo :: GetParentAmmo
//
// Returns the least-derived ammo type that this ammo is a descendant of.
// That is, if this ammo is an immediate subclass of Ammo, then this ammo's
// type is returned. If this ammo's superclass is not Ammo, then this
// function travels up the inheritance chain until it finds a type that is
// an immediate subclass of Ammo and returns that.
//
// The intent of this is that all unique ammo types will be immediate
// subclasses of Ammo. To make different pickups with different ammo amounts,
// you subclass the type of ammo you want a different amount for and edit
// that.
//
//===========================================================================
PClassActor *AAmmo::GetParentAmmo () const
{
PClass *type = GetClass();
while (type->ParentClass != RUNTIME_CLASS(AAmmo) && type->ParentClass != NULL)
{
type = type->ParentClass;
}
return static_cast<PClassActor *>(type);
}
//===========================================================================
//
// AAmmo :: HandlePickup
//
//===========================================================================
EXTERN_CVAR(Bool, sv_unlimited_pickup)
bool AAmmo::HandlePickup (AInventory *item)
{
if (GetClass() == item->GetClass() ||
(item->IsKindOf (RUNTIME_CLASS(AAmmo)) && static_cast<AAmmo*>(item)->GetParentAmmo() == GetClass()))
{
if (Amount < MaxAmount || sv_unlimited_pickup)
{
int receiving = item->Amount;
if (!(item->ItemFlags & IF_IGNORESKILL))
{ // extra ammo in baby mode and nightmare mode
receiving = int(receiving * G_SkillProperty(SKILLP_AmmoFactor));
}
int oldamount = Amount;
if (Amount > 0 && Amount + receiving < 0)
{
Amount = 0x7fffffff;
}
else
{
Amount += receiving;
}
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item->ItemFlags |= IF_PICKUPGOOD;
// If the player previously had this ammo but ran out, possibly switch
// to a weapon that uses it, but only if the player doesn't already
// have a weapon pending.
assert (Owner != NULL);
if (oldamount == 0 && Owner != NULL && Owner->player != NULL)
{
barrier_cast<APlayerPawn *>(Owner)->CheckWeaponSwitch(GetClass());
}
}
return true;
}
return false;
}
//===========================================================================
//
// AAmmo :: CreateCopy
//
//===========================================================================
AInventory *AAmmo::CreateCopy (AActor *other)
{
AInventory *copy;
int amount = Amount;
// extra ammo in baby mode and nightmare mode
if (!(ItemFlags&IF_IGNORESKILL))
{
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
}
if (GetClass()->ParentClass != RUNTIME_CLASS(AAmmo) && GetClass() != RUNTIME_CLASS(AAmmo))
{
PClassActor *type = GetParentAmmo();
if (!GoAway ())
{
Destroy ();
}
copy = static_cast<AInventory *>(Spawn (type));
copy->Amount = amount;
copy->BecomeItem ();
}
else
{
copy = Super::CreateCopy (other);
copy->Amount = amount;
}
if (copy->Amount > copy->MaxAmount)
{ // Don't pick up more ammo than you're supposed to be able to carry.
copy->Amount = copy->MaxAmount;
}
return copy;
}
//===========================================================================
//
// AAmmo :: CreateTossable
//
//===========================================================================
AInventory *AAmmo::CreateTossable()
{
AInventory *copy = Super::CreateTossable();
if (copy != NULL)
{ // Do not increase ammo by dropping it and picking it back up at
// certain skill levels.
copy->ItemFlags |= IF_IGNORESKILL;
}
return copy;
}
// Backpack -----------------------------------------------------------------
IMPLEMENT_CLASS(ABackpackItem, false, false)
DEFINE_FIELD(ABackpackItem, bDepleted)
//===========================================================================
//
// ABackpackItem :: Serialize
//
//===========================================================================
void ABackpackItem::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
auto def = (ABackpackItem*)GetDefault();
arc("bdepleted", bDepleted, def->bDepleted);
}
//===========================================================================
//
// ABackpackItem :: CreateCopy
//
// A backpack is being added to a player who doesn't yet have one. Give them
// every kind of ammo, and increase their max amounts.
//
//===========================================================================
AInventory *ABackpackItem::CreateCopy (AActor *other)
{
// Find every unique type of ammo. Give it to the player if
// he doesn't have it already, and double its maximum capacity.
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
{
PClass *type = PClassActor::AllActorClasses[i];
if (type->ParentClass == RUNTIME_CLASS(AAmmo))
{
PClassActor *atype = static_cast<PClassActor *>(type);
AAmmo *ammo = static_cast<AAmmo *>(other->FindInventory(atype));
int amount = static_cast<AAmmo *>(GetDefaultByType(type))->BackpackAmount;
// extra ammo in baby mode and nightmare mode
if (!(ItemFlags&IF_IGNORESKILL))
{
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
}
if (amount < 0) amount = 0;
if (ammo == NULL)
{ // The player did not have the ammo. Add it.
ammo = static_cast<AAmmo *>(Spawn(atype));
ammo->Amount = bDepleted ? 0 : amount;
if (ammo->BackpackMaxAmount > ammo->MaxAmount)
{
ammo->MaxAmount = ammo->BackpackMaxAmount;
}
if (ammo->Amount > ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
ammo->AttachToOwner (other);
}
else
{ // The player had the ammo. Give some more.
if (ammo->MaxAmount < ammo->BackpackMaxAmount)
{
ammo->MaxAmount = ammo->BackpackMaxAmount;
}
if (!bDepleted && ammo->Amount < ammo->MaxAmount)
{
ammo->Amount += amount;
if (ammo->Amount > ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
}
}
}
}
return Super::CreateCopy (other);
}
//===========================================================================
//
// ABackpackItem :: HandlePickup
//
// When the player picks up another backpack, just give them more ammo.
//
//===========================================================================
bool ABackpackItem::HandlePickup (AInventory *item)
{
// Since you already have a backpack, that means you already have every
// kind of ammo in your inventory, so we don't need to look at the
// entire PClass list to discover what kinds of ammo exist, and we don't
// have to alter the MaxAmount either.
if (item->IsKindOf (RUNTIME_CLASS(ABackpackItem)))
{
for (AInventory *probe = Owner->Inventory; probe != NULL; probe = probe->Inventory)
{
if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo))
{
if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup)
{
int amount = static_cast<AAmmo*>(probe->GetDefault())->BackpackAmount;
// extra ammo in baby mode and nightmare mode
if (!(item->ItemFlags&IF_IGNORESKILL))
{
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
}
probe->Amount += amount;
if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup)
{
probe->Amount = probe->MaxAmount;
}
}
}
}
// The pickup always succeeds, even if you didn't get anything
item->ItemFlags |= IF_PICKUPGOOD;
return true;
}
return false;
}
//===========================================================================
//
// ABackpackItem :: CreateTossable
//
// The tossed backpack must not give out any more ammo, otherwise a player
// could cheat by dropping their backpack and picking it up for more ammo.
//
//===========================================================================
AInventory *ABackpackItem::CreateTossable ()
{
ABackpackItem *pack = static_cast<ABackpackItem *>(Super::CreateTossable());
if (pack != NULL)
{
pack->bDepleted = true;
}
return pack;
}
//===========================================================================
//
// ABackpackItem :: DetachFromOwner
//
//===========================================================================
void ABackpackItem::DetachFromOwner ()
{
// When removing a backpack, drop the player's ammo maximums to normal
AInventory *item;
for (item = Owner->Inventory; item != NULL; item = item->Inventory)
{
if (item->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo) &&
item->MaxAmount == static_cast<AAmmo*>(item)->BackpackMaxAmount)
{
item->MaxAmount = static_cast<AInventory*>(item->GetDefault())->MaxAmount;
if (item->Amount > item->MaxAmount)
{
item->Amount = item->MaxAmount;
}
}
}
}

47
src/g_inventory/a_ammo.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include "a_pickups.h"
// Ammo: Something a weapon needs to operate
class PClassAmmo : public PClassInventory
{
DECLARE_CLASS(PClassAmmo, PClassInventory)
protected:
virtual void DeriveData(PClass *newclass);
public:
PClassAmmo();
int DropAmount; // Specifies the amount for a dropped ammo item.
};
class AAmmo : public AInventory
{
DECLARE_CLASS_WITH_META(AAmmo, AInventory, PClassAmmo)
public:
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateTossable () override;
PClassActor *GetParentAmmo () const;
int BackpackAmount, BackpackMaxAmount;
};
// A backpack gives you one clip of each ammo and doubles your
// normal maximum ammo amounts.
class ABackpackItem : public AInventory
{
DECLARE_CLASS (ABackpackItem, AInventory)
public:
virtual void Serialize(FSerializer &arc) override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual void DetachFromOwner () override;
bool bDepleted;
};

View file

@ -1,18 +1,71 @@
/*
** a_armor.cpp
** Implements all variations of armor objects
**
**---------------------------------------------------------------------------
** Copyright 2002-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <assert.h>
#include "info.h"
#include "gi.h"
#include "a_pickups.h"
#include "a_armor.h"
#include "templates.h"
#include "g_level.h"
#include "d_player.h"
#include "serializer.h"
#include "cmdlib.h"
IMPLEMENT_CLASS(AArmor, false, false, false, false)
IMPLEMENT_CLASS(ABasicArmor, false, false, false, false)
IMPLEMENT_CLASS(ABasicArmorPickup, false, false, false, false)
IMPLEMENT_CLASS(ABasicArmorBonus, false, false, false, false)
IMPLEMENT_CLASS(AHexenArmor, false, false, false, false)
IMPLEMENT_CLASS(AArmor, false, false)
IMPLEMENT_CLASS(ABasicArmor, false, false)
IMPLEMENT_CLASS(ABasicArmorPickup, false, false)
IMPLEMENT_CLASS(ABasicArmorBonus, false, false)
IMPLEMENT_CLASS(AHexenArmor, false, false)
//===========================================================================
//
//
// BasicArmor
//
//
//===========================================================================
DEFINE_FIELD(ABasicArmor, AbsorbCount)
DEFINE_FIELD(ABasicArmor, SavePercent)
DEFINE_FIELD(ABasicArmor, MaxAbsorb)
DEFINE_FIELD(ABasicArmor, MaxFullAbsorb)
DEFINE_FIELD(ABasicArmor, BonusCount)
DEFINE_FIELD(ABasicArmor, ArmorType)
DEFINE_FIELD(ABasicArmor, ActualSaveAmount)
//===========================================================================
//
@ -105,10 +158,6 @@ bool ABasicArmor::HandlePickup (AInventory *item)
armor->SaveAmount = int(armor->SaveAmount * G_SkillProperty(SKILLP_ArmorFactor));
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
@ -192,6 +241,20 @@ void ABasicArmor::AbsorbDamage (int damage, FName damageType, int &newdamage)
}
}
//===========================================================================
//
//
// BasicArmorPickup
//
//
//===========================================================================
DEFINE_FIELD(ABasicArmorPickup, SavePercent)
DEFINE_FIELD(ABasicArmorPickup, MaxAbsorb)
DEFINE_FIELD(ABasicArmorPickup, MaxFullAbsorb)
DEFINE_FIELD(ABasicArmorPickup, SaveAmount)
//===========================================================================
//
// ABasicArmorPickup :: Serialize
@ -278,6 +341,23 @@ bool ABasicArmorPickup::Use (bool pickup)
return true;
}
//===========================================================================
//
//
// BasicArmorBonus
//
//
//===========================================================================
DEFINE_FIELD(ABasicArmorBonus, SavePercent)
DEFINE_FIELD(ABasicArmorBonus, MaxSaveAmount)
DEFINE_FIELD(ABasicArmorBonus, MaxAbsorb)
DEFINE_FIELD(ABasicArmorBonus, MaxFullAbsorb)
DEFINE_FIELD(ABasicArmorBonus, SaveAmount)
DEFINE_FIELD(ABasicArmorBonus, BonusCount)
DEFINE_FIELD(ABasicArmorBonus, BonusMax)
//===========================================================================
//
// ABasicArmorBonus :: Serialize
@ -381,6 +461,17 @@ bool ABasicArmorBonus::Use (bool pickup)
return true;
}
//===========================================================================
//
//
// HexenArmor
//
//
//===========================================================================
DEFINE_FIELD(AHexenArmor, Slots)
DEFINE_FIELD(AHexenArmor, SlotsIncrement)
//===========================================================================
//
// AHexenArmor :: Serialize
@ -441,10 +532,6 @@ bool AHexenArmor::HandlePickup (AInventory *item)
}
return true;
}
else if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
@ -548,6 +635,11 @@ void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage)
}
}
//===========================================================================
//
// AHexenArmor :: DepleteOrDestroy
//
//===========================================================================
void AHexenArmor::DepleteOrDestroy()
{

89
src/g_inventory/a_armor.h Normal file
View file

@ -0,0 +1,89 @@
#pragma once
#include "a_pickups.h"
// Armor absorbs some damage for the player.
class AArmor : public AInventory
{
DECLARE_CLASS (AArmor, AInventory)
};
// Basic armor absorbs a specific percent of the damage. You should
// never pickup a BasicArmor. Instead, you pickup a BasicArmorPickup
// or BasicArmorBonus and those gives you BasicArmor when it activates.
class ABasicArmor : public AArmor
{
DECLARE_CLASS (ABasicArmor, AArmor)
public:
virtual void Serialize(FSerializer &arc) override;
virtual void Tick () override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual bool HandlePickup (AInventory *item) override;
virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
int AbsorbCount;
double SavePercent;
int MaxAbsorb;
int MaxFullAbsorb;
int BonusCount;
FNameNoInit ArmorType;
int ActualSaveAmount;
};
// BasicArmorPickup replaces the armor you have.
class ABasicArmorPickup : public AArmor
{
DECLARE_CLASS (ABasicArmorPickup, AArmor)
public:
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual bool Use (bool pickup) override;
double SavePercent;
int MaxAbsorb;
int MaxFullAbsorb;
int SaveAmount;
};
// BasicArmorBonus adds to the armor you have.
class ABasicArmorBonus : public AArmor
{
DECLARE_CLASS (ABasicArmorBonus, AArmor)
public:
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual bool Use (bool pickup) override;
double SavePercent; // The default, for when you don't already have armor
int MaxSaveAmount;
int MaxAbsorb;
int MaxFullAbsorb;
int SaveAmount;
int BonusCount;
int BonusMax;
};
// Hexen armor consists of four separate armor types plus a conceptual armor
// type (the player himself) that work together as a single armor.
class AHexenArmor : public AArmor
{
DECLARE_CLASS (AHexenArmor, AArmor)
public:
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual bool HandlePickup (AInventory *item) override;
virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
virtual void DepleteOrDestroy() override;
double Slots[5];
double SlotsIncrement[4];
protected:
bool AddArmorToSlot (AActor *actor, int slot, int amount);
};

View file

@ -40,11 +40,12 @@ static FRandom pr_torch ("Torch");
#define TIMEFREEZE_TICS ( 12 * TICRATE )
*/
IMPLEMENT_CLASS(APowerup, false, false, false, false)
IMPLEMENT_CLASS(APowerup, false, false)
// Powerup-Giver -------------------------------------------------------------
IMPLEMENT_CLASS(PClassPowerupGiver, false, false, false, false)
IMPLEMENT_CLASS(PClassPowerupGiver, false, false)
void PClassPowerupGiver::ReplaceClassRef(PClass *oldclass, PClass *newclass)
{
@ -56,6 +57,14 @@ void PClassPowerupGiver::ReplaceClassRef(PClass *oldclass, PClass *newclass)
}
}
IMPLEMENT_CLASS(APowerupGiver, false, false)
DEFINE_FIELD(APowerupGiver, PowerupType)
DEFINE_FIELD(APowerupGiver, EffectTics)
DEFINE_FIELD(APowerupGiver, BlendColor)
DEFINE_FIELD(APowerupGiver, Mode)
DEFINE_FIELD(APowerupGiver, Strength)
//===========================================================================
//
// APowerupGiver :: Use
@ -65,6 +74,7 @@ void PClassPowerupGiver::ReplaceClassRef(PClass *oldclass, PClass *newclass)
bool APowerupGiver::Use (bool pickup)
{
if (PowerupType == NULL) return true; // item is useless
if (Owner == nullptr) return true;
APowerup *power = static_cast<APowerup *> (Spawn (PowerupType));
@ -114,6 +124,11 @@ void APowerupGiver::Serialize(FSerializer &arc)
// Powerup -------------------------------------------------------------------
DEFINE_FIELD(APowerup, EffectTics)
DEFINE_FIELD(APowerup, BlendColor)
DEFINE_FIELD(APowerup, Mode)
DEFINE_FIELD(APowerup, Strength)
//===========================================================================
//
// APowerup :: Tick
@ -297,10 +312,6 @@ bool APowerup::HandlePickup (AInventory *item)
power->ItemFlags |= IF_PICKUPGOOD;
return true;
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
@ -362,21 +373,9 @@ void APowerup::OwnerDied ()
Destroy ();
}
//===========================================================================
//
// AInventory :: GetNoTeleportFreeze
//
//===========================================================================
bool APowerup::GetNoTeleportFreeze ()
{
if (ItemFlags & IF_NOTELEPORTFREEZE) return true;
return Super::GetNoTeleportFreeze();
}
// Invulnerability Powerup ---------------------------------------------------
IMPLEMENT_CLASS(APowerInvulnerable, false, false, false, false)
IMPLEMENT_CLASS(APowerInvulnerable, false, false)
//===========================================================================
//
@ -513,7 +512,7 @@ int APowerInvulnerable::AlterWeaponSprite (visstyle_t *vis)
// Strength (aka Berserk) Powerup --------------------------------------------
IMPLEMENT_CLASS(APowerStrength, false, false, false, false)
IMPLEMENT_CLASS(APowerStrength, false, false)
//===========================================================================
//
@ -578,7 +577,7 @@ PalEntry APowerStrength::GetBlend ()
// Invisibility Powerup ------------------------------------------------------
IMPLEMENT_CLASS(APowerInvisibility, false, false, false, false)
IMPLEMENT_CLASS(APowerInvisibility, false, false)
// Invisibility flag combos
#define INVISIBILITY_FLAGS1 (MF_SHADOW)
@ -783,7 +782,7 @@ bool APowerInvisibility::HandlePickup (AInventory *item)
// Ironfeet Powerup ----------------------------------------------------------
IMPLEMENT_CLASS(APowerIronFeet, false, false, false, false)
IMPLEMENT_CLASS(APowerIronFeet, false, false)
//===========================================================================
//
@ -820,7 +819,7 @@ void APowerIronFeet::DoEffect ()
// Strife Environment Suit Powerup -------------------------------------------
IMPLEMENT_CLASS(APowerMask, false, false, false, false)
IMPLEMENT_CLASS(APowerMask, false, false)
//===========================================================================
//
@ -857,7 +856,7 @@ void APowerMask::DoEffect ()
// Light-Amp Powerup ---------------------------------------------------------
IMPLEMENT_CLASS(APowerLightAmp, false, false, false, false)
IMPLEMENT_CLASS(APowerLightAmp, false, false)
//===========================================================================
//
@ -899,7 +898,7 @@ void APowerLightAmp::EndEffect ()
// Torch Powerup -------------------------------------------------------------
IMPLEMENT_CLASS(APowerTorch, false, false, false, false)
IMPLEMENT_CLASS(APowerTorch, false, false)
//===========================================================================
//
@ -962,7 +961,7 @@ void APowerTorch::DoEffect ()
// Flight (aka Wings of Wrath) powerup ---------------------------------------
IMPLEMENT_CLASS(APowerFlight, false, false, false, false)
IMPLEMENT_CLASS(APowerFlight, false, false)
//===========================================================================
//
@ -1102,7 +1101,7 @@ bool APowerFlight::DrawPowerup (int x, int y)
// Weapon Level 2 (aka Tome of Power) Powerup --------------------------------
IMPLEMENT_CLASS(APowerWeaponLevel2, false, false, false, false)
IMPLEMENT_CLASS(APowerWeaponLevel2, false, false)
//===========================================================================
//
@ -1173,7 +1172,7 @@ void APowerWeaponLevel2::EndEffect ()
if (player->ReadyWeapon != NULL &&
player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP)
{
player->ReadyWeapon->EndPowerup ();
player->ReadyWeapon->CallEndPowerup ();
}
if (player->PendingWeapon != NULL && player->PendingWeapon != WP_NOCHANGE &&
player->PendingWeapon->WeaponFlags & WIF_POWERED_UP &&
@ -1184,39 +1183,11 @@ void APowerWeaponLevel2::EndEffect ()
}
}
// Player Speed Trail (used by the Speed Powerup) ----------------------------
class APlayerSpeedTrail : public AActor
{
DECLARE_CLASS (APlayerSpeedTrail, AActor)
public:
void Tick ();
};
IMPLEMENT_CLASS(APlayerSpeedTrail, false, false, false, false)
//===========================================================================
//
// APlayerSpeedTrail :: Tick
//
//===========================================================================
void APlayerSpeedTrail::Tick ()
{
const double fade = .6 / 8;
if (Alpha <= fade)
{
Destroy ();
}
else
{
Alpha -= fade;
}
}
// Speed Powerup -------------------------------------------------------------
IMPLEMENT_CLASS(APowerSpeed, false, false, false, false)
IMPLEMENT_CLASS(APowerSpeed, false, false)
DEFINE_FIELD(APowerSpeed, SpeedFlags)
//===========================================================================
//
@ -1230,20 +1201,6 @@ void APowerSpeed::Serialize(FSerializer &arc)
arc("speedflags", SpeedFlags);
}
//===========================================================================
//
// APowerSpeed :: GetSpeedFactor
//
//===========================================================================
double APowerSpeed ::GetSpeedFactor ()
{
if (Inventory != NULL)
return Speed * Inventory->GetSpeedFactor();
else
return Speed;
}
//===========================================================================
//
// APowerSpeed :: DoEffect
@ -1280,7 +1237,7 @@ void APowerSpeed::DoEffect ()
if (Owner->Vel.LengthSquared() <= 12*12)
return;
AActor *speedMo = Spawn<APlayerSpeedTrail> (Owner->Pos(), NO_REPLACE);
AActor *speedMo = Spawn("PlayerSpeedTrail", Owner->Pos(), NO_REPLACE);
if (speedMo)
{
speedMo->Angles.Yaw = Owner->Angles.Yaw;
@ -1303,11 +1260,11 @@ void APowerSpeed::DoEffect ()
// Minotaur (aka Dark Servant) powerup ---------------------------------------
IMPLEMENT_CLASS(APowerMinotaur, false, false, false, false)
IMPLEMENT_CLASS(APowerMinotaur, false, false)
// Targeter powerup ---------------------------------------------------------
IMPLEMENT_CLASS(APowerTargeter, false, false, false, false)
IMPLEMENT_CLASS(APowerTargeter, false, false)
void APowerTargeter::Travelled ()
{
@ -1422,7 +1379,7 @@ void APowerTargeter::PositionAccuracy ()
// Frightener Powerup --------------------------------
IMPLEMENT_CLASS(APowerFrightener, false, false, false, false)
IMPLEMENT_CLASS(APowerFrightener, false, false)
//===========================================================================
//
@ -1458,7 +1415,7 @@ void APowerFrightener::EndEffect ()
// Buddha Powerup --------------------------------
IMPLEMENT_CLASS(APowerBuddha, false, false, false, false)
IMPLEMENT_CLASS(APowerBuddha, false, false)
//===========================================================================
//
@ -1494,11 +1451,11 @@ void APowerBuddha::EndEffect ()
// Scanner powerup ----------------------------------------------------------
IMPLEMENT_CLASS(APowerScanner, false, false, false, false)
IMPLEMENT_CLASS(APowerScanner, false, false)
// Time freezer powerup -----------------------------------------------------
IMPLEMENT_CLASS( APowerTimeFreezer, false, false, false, false)
IMPLEMENT_CLASS( APowerTimeFreezer, false, false)
//===========================================================================
//
@ -1625,7 +1582,7 @@ void APowerTimeFreezer::EndEffect()
// Damage powerup ------------------------------------------------------
IMPLEMENT_CLASS(APowerDamage, false, false, false, false)
IMPLEMENT_CLASS(APowerDamage, false, false)
//===========================================================================
//
@ -1682,7 +1639,7 @@ void APowerDamage::ModifyDamage(int damage, FName damageType, int &newdamage, bo
// Quarter damage powerup ------------------------------------------------------
IMPLEMENT_CLASS(APowerProtection, false, false, false, false)
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)
@ -1760,7 +1717,7 @@ void APowerProtection::ModifyDamage(int damage, FName damageType, int &newdamage
// Drain rune -------------------------------------------------------
IMPLEMENT_CLASS(APowerDrain, false, false, false, false)
IMPLEMENT_CLASS(APowerDrain, false, false)
//===========================================================================
//
@ -1800,7 +1757,7 @@ void APowerDrain::EndEffect( )
// Regeneration rune -------------------------------------------------------
IMPLEMENT_CLASS(APowerRegeneration, false, false, false, false)
IMPLEMENT_CLASS(APowerRegeneration, false, false)
//===========================================================================
//
@ -1822,7 +1779,7 @@ void APowerRegeneration::DoEffect()
// High jump rune -------------------------------------------------------
IMPLEMENT_CLASS(APowerHighJump, false, false, false, false)
IMPLEMENT_CLASS(APowerHighJump, false, false)
//===========================================================================
//
@ -1860,7 +1817,7 @@ void APowerHighJump::EndEffect( )
// Double firing speed rune ---------------------------------------------
IMPLEMENT_CLASS(APowerDoubleFiringSpeed, false, false, false, false)
IMPLEMENT_CLASS(APowerDoubleFiringSpeed, false, false)
//===========================================================================
//
@ -1898,7 +1855,14 @@ void APowerDoubleFiringSpeed::EndEffect( )
// Morph powerup ------------------------------------------------------
IMPLEMENT_CLASS(APowerMorph, false, false, false, false)
IMPLEMENT_CLASS(APowerMorph, false, false)
DEFINE_FIELD(APowerMorph, PlayerClass)
DEFINE_FIELD(APowerMorph, MorphFlash)
DEFINE_FIELD(APowerMorph, UnMorphFlash)
DEFINE_FIELD(APowerMorph, MorphStyle)
DEFINE_FIELD(APowerMorph, MorphedPlayer)
DEFINE_FIELD(APowerMorph, bInUndoMorph)
//===========================================================================
//
@ -1913,7 +1877,7 @@ void APowerMorph::Serialize(FSerializer &arc)
("morphstyle", MorphStyle)
("morphflash", MorphFlash)
("unmorphflash", UnMorphFlash)
("player", Player);
("morphedplayer", MorphedPlayer);
}
//===========================================================================
@ -1926,17 +1890,14 @@ void APowerMorph::InitEffect( )
{
Super::InitEffect();
if (Owner != NULL && Owner->player != NULL && PlayerClass != NAME_None)
if (Owner != nullptr && Owner->player != nullptr && PlayerClass != nullptr)
{
player_t *realplayer = Owner->player; // Remember the identity of the player
PClassActor *morph_flash = PClass::FindActor(MorphFlash);
PClassActor *unmorph_flash = PClass::FindActor(UnMorphFlash);
PClassPlayerPawn *player_class = dyn_cast<PClassPlayerPawn>(PClass::FindClass (PlayerClass));
if (P_MorphPlayer(realplayer, realplayer, player_class, -1/*INDEFINITELY*/, MorphStyle, morph_flash, unmorph_flash))
if (P_MorphPlayer(realplayer, realplayer, PlayerClass, -1/*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)
Player = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
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
{
@ -1958,51 +1919,51 @@ void APowerMorph::EndEffect( )
// Abort if owner already destroyed
if (Owner == NULL)
{
assert(Player == NULL);
assert(MorphedPlayer == NULL);
return;
}
// Abort if owner already unmorphed
if (Player == NULL)
if (MorphedPlayer == NULL)
{
return;
}
// Abort if owner is dead; their Die() method will
// take care of any required unmorphing on death.
if (Player->health <= 0)
if (MorphedPlayer->health <= 0)
{
return;
}
// Unmorph if possible
if (!bNoCallUndoMorph)
if (!bInUndoMorph)
{
int savedMorphTics = Player->morphTics;
P_UndoPlayerMorph (Player, Player, 0, !!(Player->MorphStyle & MORPH_UNDOALWAYS));
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 (Player != NULL && Player->morphTics)
if (MorphedPlayer != NULL && MorphedPlayer->morphTics)
{
// Transfer retry timeout
// to the powerup's timer.
EffectTics = Player->morphTics;
EffectTics = MorphedPlayer->morphTics;
// Reload negative morph tics;
// use actual value; it may
// be in use for animation.
Player->morphTics = savedMorphTics;
MorphedPlayer->morphTics = savedMorphTics;
// Try again some time later
return;
}
}
// Unmorph suceeded
Player = NULL;
MorphedPlayer = NULL;
}
// Infinite Ammo Powerup -----------------------------------------------------
IMPLEMENT_CLASS(APowerInfiniteAmmo, false, false, false, false)
IMPLEMENT_CLASS(APowerInfiniteAmmo, false, false)
//===========================================================================
//

View file

@ -11,17 +11,15 @@ class APowerup : public AInventory
{
DECLARE_CLASS (APowerup, AInventory)
public:
virtual void Tick ();
virtual void Destroy ();
virtual bool HandlePickup (AInventory *item);
virtual AInventory *CreateCopy (AActor *other);
virtual AInventory *CreateTossable ();
virtual void Serialize(FSerializer &arc);
virtual void OwnerDied ();
virtual bool GetNoTeleportFreeze();
virtual PalEntry GetBlend ();
virtual bool DrawPowerup (int x, int y);
virtual void Tick () override;
virtual void Destroy () override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual void Serialize(FSerializer &arc) override;
virtual void OwnerDied () override;
virtual PalEntry GetBlend () override;
virtual bool DrawPowerup (int x, int y) override;
int EffectTics;
PalEntry BlendColor;
@ -30,7 +28,7 @@ public:
protected:
virtual void InitEffect ();
virtual void DoEffect ();
virtual void DoEffect () override;
virtual void EndEffect ();
friend void EndAllPowerupEffects(AInventory *item);
@ -51,9 +49,8 @@ class APowerupGiver : public AInventory
{
DECLARE_CLASS_WITH_META (APowerupGiver, AInventory, PClassPowerupGiver)
public:
virtual bool Use (bool pickup);
virtual void Serialize(FSerializer &arc);
virtual bool Use (bool pickup) override;
virtual void Serialize(FSerializer &arc) override;
PClassActor *PowerupType;
@ -67,10 +64,10 @@ class APowerInvulnerable : public APowerup
{
DECLARE_CLASS (APowerInvulnerable, APowerup)
protected:
void InitEffect ();
void DoEffect ();
void EndEffect ();
int AlterWeaponSprite (visstyle_t *vis);
virtual void InitEffect () override;
virtual void DoEffect () override;
virtual void EndEffect () override;
virtual int AlterWeaponSprite (visstyle_t *vis) override;
};
class APowerStrength : public APowerup
@ -79,44 +76,44 @@ class APowerStrength : public APowerup
public:
PalEntry GetBlend ();
protected:
void InitEffect ();
void Tick ();
bool HandlePickup (AInventory *item);
virtual void InitEffect () override;
virtual void Tick () override;
virtual bool HandlePickup (AInventory *item) override;
};
class APowerInvisibility : public APowerup
{
DECLARE_CLASS (APowerInvisibility, APowerup)
protected:
bool HandlePickup (AInventory *item);
void InitEffect ();
void DoEffect ();
void EndEffect ();
int AlterWeaponSprite (visstyle_t *vis);
virtual bool HandlePickup (AInventory *item) override;
virtual void InitEffect () override;
virtual void DoEffect () override;
virtual void EndEffect () override;
virtual int AlterWeaponSprite (visstyle_t *vis) override;
};
class APowerIronFeet : public APowerup
{
DECLARE_CLASS (APowerIronFeet, APowerup)
public:
void AbsorbDamage (int damage, FName damageType, int &newdamage);
void DoEffect ();
virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
virtual void DoEffect () override;
};
class APowerMask : public APowerIronFeet
{
DECLARE_CLASS (APowerMask, APowerIronFeet)
public:
void AbsorbDamage (int damage, FName damageType, int &newdamage);
void DoEffect ();
virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
virtual void DoEffect () override;
};
class APowerLightAmp : public APowerup
{
DECLARE_CLASS (APowerLightAmp, APowerup)
protected:
void DoEffect ();
void EndEffect ();
virtual void DoEffect () override;
virtual void EndEffect () override;
};
class APowerTorch : public APowerLightAmp
@ -124,9 +121,9 @@ class APowerTorch : public APowerLightAmp
DECLARE_CLASS (APowerTorch, APowerLightAmp)
public:
virtual void Serialize(FSerializer &arc);
virtual void Serialize(FSerializer &arc) override;
protected:
void DoEffect ();
virtual void DoEffect () override;
int NewTorch, NewTorchDelta;
};
@ -134,15 +131,15 @@ class APowerFlight : public APowerup
{
DECLARE_CLASS (APowerFlight, APowerup)
public:
bool DrawPowerup (int x, int y);
virtual void Serialize(FSerializer &arc);
virtual bool DrawPowerup (int x, int y) override;
virtual void Serialize(FSerializer &arc) override;
protected:
void InitEffect ();
void Tick ();
void EndEffect ();
virtual void InitEffect () override;
virtual void Tick () override;
virtual void EndEffect () override;
private:
bool HitCenterFrame;
};
@ -150,18 +147,17 @@ class APowerWeaponLevel2 : public APowerup
{
DECLARE_CLASS (APowerWeaponLevel2, APowerup)
protected:
void InitEffect ();
void EndEffect ();
virtual void InitEffect () override;
virtual void EndEffect () override;
};
class APowerSpeed : public APowerup
{
DECLARE_CLASS (APowerSpeed, APowerup)
protected:
void DoEffect ();
virtual void DoEffect () override;
virtual void Serialize(FSerializer &arc);
double GetSpeedFactor();
virtual void Serialize(FSerializer &arc) override;
public:
int SpeedFlags;
};
@ -182,95 +178,95 @@ class APowerTargeter : public APowerup
{
DECLARE_CLASS (APowerTargeter, APowerup)
protected:
void InitEffect ();
void DoEffect ();
void EndEffect ();
virtual void InitEffect () override;
virtual void DoEffect () override;
virtual void EndEffect () override;
void PositionAccuracy ();
void Travelled ();
void AttachToOwner(AActor *other);
bool HandlePickup(AInventory *item);
virtual void Travelled () override;
virtual void AttachToOwner(AActor *other) override;
virtual bool HandlePickup(AInventory *item) override;
};
class APowerFrightener : public APowerup
{
DECLARE_CLASS (APowerFrightener, APowerup)
protected:
void InitEffect ();
void EndEffect ();
virtual void InitEffect () override;
virtual void EndEffect () override;
};
class APowerBuddha : public APowerup
{
DECLARE_CLASS (APowerBuddha, APowerup)
protected:
void InitEffect ();
void EndEffect ();
virtual void InitEffect () override;
virtual void EndEffect () override;
};
class APowerTimeFreezer : public APowerup
{
DECLARE_CLASS( APowerTimeFreezer, APowerup )
protected:
void InitEffect( );
void DoEffect( );
void EndEffect( );
virtual void InitEffect() override;
virtual void DoEffect() override;
virtual void EndEffect() override;
};
class APowerDamage : public APowerup
{
DECLARE_CLASS( APowerDamage, APowerup )
protected:
void InitEffect ();
void EndEffect ();
virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive);
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:
void InitEffect ();
void EndEffect ();
virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive);
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:
void InitEffect( );
void EndEffect( );
virtual void InitEffect() override;
virtual void EndEffect() override;
};
class APowerRegeneration : public APowerup
{
DECLARE_CLASS( APowerRegeneration, APowerup )
protected:
void DoEffect();
virtual void DoEffect() override;
};
class APowerHighJump : public APowerup
{
DECLARE_CLASS( APowerHighJump, APowerup )
protected:
void InitEffect( );
void EndEffect( );
virtual void InitEffect() override;
virtual void EndEffect() override;
};
class APowerDoubleFiringSpeed : public APowerup
{
DECLARE_CLASS( APowerDoubleFiringSpeed, APowerup )
protected:
void InitEffect( );
void EndEffect( );
virtual void InitEffect() override;
virtual void EndEffect() override;
};
class APowerInfiniteAmmo : public APowerup
{
DECLARE_CLASS( APowerInfiniteAmmo, APowerup )
protected:
void InitEffect( );
void EndEffect( );
virtual void InitEffect() override;
virtual void EndEffect() override;
};
class APowerMorph : public APowerup
@ -278,18 +274,19 @@ class APowerMorph : public APowerup
DECLARE_CLASS( APowerMorph, APowerup )
public:
virtual void Serialize(FSerializer &arc);
void SetNoCallUndoMorph() { bNoCallUndoMorph = true; }
virtual void Serialize(FSerializer &arc) override;
void SetNoCallUndoMorph() { bInUndoMorph = true; }
FNameNoInit PlayerClass, MorphFlash, UnMorphFlash;
// Variables
PClassPlayerPawn *PlayerClass;
PClassActor *MorphFlash, *UnMorphFlash;
int MorphStyle;
player_t *MorphedPlayer;
bool bInUndoMorph; // Because P_UndoPlayerMorph() can call EndEffect recursively
protected:
void InitEffect ();
void EndEffect ();
// Variables
player_t *Player;
bool bNoCallUndoMorph; // Because P_UndoPlayerMorph() can call EndEffect recursively
virtual void InitEffect () override;
virtual void EndEffect () override;
};
#endif //__A_ARTIFACTS_H__

View file

@ -0,0 +1,301 @@
/*
** a_health.cpp
** All health items
**
**---------------------------------------------------------------------------
** Copyright 2000-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "d_player.h"
#include "a_morph.h"
#include "a_health.h"
#include "serializer.h"
//---------------------------------------------------------------------------
//
// FUNC P_GiveBody
//
// Returns false if the body isn't needed at all.
//
//---------------------------------------------------------------------------
bool P_GiveBody (AActor *actor, int num, int max)
{
if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD))
{ // Do not heal dead things.
return false;
}
player_t *player = actor->player;
num = clamp(num, -65536, 65536); // prevent overflows for bad values
if (player != NULL)
{
// Max is 0 by default, preserving default behavior for P_GiveBody()
// calls while supporting AHealth.
if (max <= 0)
{
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina;
// [MH] First step in predictable generic morph effects
if (player->morphTics)
{
if (player->MorphStyle & MORPH_FULLHEALTH)
{
if (!(player->MorphStyle & MORPH_ADDSTAMINA))
{
max -= player->mo->stamina;
}
}
else // old health behaviour
{
max = MAXMORPHHEALTH;
if (player->MorphStyle & MORPH_ADDSTAMINA)
{
max += player->mo->stamina;
}
}
}
}
// [RH] For Strife: A negative body sets you up with a percentage
// of your full health.
if (num < 0)
{
num = max * -num / 100;
if (player->health < num)
{
player->health = num;
actor->health = num;
return true;
}
}
else if (num > 0)
{
if (player->health < max)
{
num = int(num * G_SkillProperty(SKILLP_HealthFactor));
if (num < 1) num = 1;
player->health += num;
if (player->health > max)
{
player->health = max;
}
actor->health = player->health;
return true;
}
}
}
else
{
// Parameter value for max is ignored on monsters, preserving original
// behaviour on AHealth as well as on existing calls to P_GiveBody().
max = actor->SpawnHealth();
if (num < 0)
{
num = max * -num / 100;
if (actor->health < num)
{
actor->health = num;
return true;
}
}
else if (actor->health < max)
{
actor->health += num;
if (actor->health > max)
{
actor->health = max;
}
return true;
}
}
return false;
}
DEFINE_ACTION_FUNCTION(AActor, GiveBody)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(num);
PARAM_INT_DEF(max);
ACTION_RETURN_BOOL(P_GiveBody(self, num, max));
}
//===========================================================================
//
// Classes
//
//===========================================================================
IMPLEMENT_CLASS(PClassHealth, false, false)
IMPLEMENT_CLASS(AHealth, false, false)
DEFINE_FIELD(AHealth, PrevHealth)
//===========================================================================
//
// PClassHealth Constructor
//
//===========================================================================
PClassHealth::PClassHealth()
{
LowHealth = 0;
}
//===========================================================================
//
// PClassHealth :: DeriveData
//
//===========================================================================
void PClassHealth::DeriveData(PClass *newclass)
{
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassHealth)));
Super::DeriveData(newclass);
PClassHealth *newc = static_cast<PClassHealth *>(newclass);
newc->LowHealth = LowHealth;
newc->LowHealthMessage = LowHealthMessage;
}
//===========================================================================
//
// AHealth :: PickupMessage
//
//===========================================================================
FString AHealth::PickupMessage ()
{
int threshold = GetClass()->LowHealth;
if (PrevHealth < threshold)
{
FString message = GetClass()->LowHealthMessage;
if (message.IsNotEmpty())
{
return message;
}
}
return Super::PickupMessage();
}
//===========================================================================
//
// AHealth :: TryPickup
//
//===========================================================================
bool AHealth::TryPickup (AActor *&other)
{
PrevHealth = other->player != NULL ? other->player->health : other->health;
// P_GiveBody adds one new feature, applied only if it is possible to pick up negative health:
// Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount.
if (P_GiveBody(other, Amount, MaxAmount))
{
GoAwayAndDie();
return true;
}
return false;
}
IMPLEMENT_CLASS(AHealthPickup, false, false)
DEFINE_FIELD(AHealthPickup, autousemode)
//===========================================================================
//
// AHealthPickup :: CreateCopy
//
//===========================================================================
AInventory *AHealthPickup::CreateCopy (AActor *other)
{
AInventory *copy = Super::CreateCopy (other);
copy->health = health;
return copy;
}
//===========================================================================
//
// AHealthPickup :: CreateTossable
//
//===========================================================================
AInventory *AHealthPickup::CreateTossable ()
{
AInventory *copy = Super::CreateTossable ();
if (copy != NULL)
{
copy->health = health;
}
return copy;
}
//===========================================================================
//
// AHealthPickup :: HandlePickup
//
//===========================================================================
bool AHealthPickup::HandlePickup (AInventory *item)
{
// HealthPickups that are the same type but have different health amounts
// do not count as the same item.
if (item->health == health)
{
return Super::HandlePickup (item);
}
return false;
}
//===========================================================================
//
// AHealthPickup :: Use
//
//===========================================================================
bool AHealthPickup::Use (bool pickup)
{
return P_GiveBody (Owner, health, 0);
}
//===========================================================================
//
// AHealthPickup :: Serialize
//
//===========================================================================
void AHealthPickup::Serialize(FSerializer &arc)
{
Super::Serialize(arc);
auto def = (AHealthPickup*)GetDefault();
arc("autousemode", autousemode, def->autousemode);
}

View file

@ -0,0 +1,42 @@
#pragma once
#include "a_pickups.h"
// Health is some item that gives the player health when picked up.
class PClassHealth : public PClassInventory
{
DECLARE_CLASS(PClassHealth, PClassInventory)
protected:
public:
PClassHealth();
virtual void DeriveData(PClass *newclass);
FString LowHealthMessage;
int LowHealth;
};
class AHealth : public AInventory
{
DECLARE_CLASS_WITH_META(AHealth, AInventory, PClassHealth)
public:
int PrevHealth;
virtual bool TryPickup (AActor *&other) override;
virtual FString PickupMessage () override;
};
// HealthPickup is some item that gives the player health when used.
class AHealthPickup : public AInventory
{
DECLARE_CLASS (AHealthPickup, AInventory)
public:
int autousemode;
virtual void Serialize(FSerializer &arc) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual bool HandlePickup (AInventory *item) override;
virtual bool Use (bool pickup) override;
};

View file

@ -1,3 +1,36 @@
/*
** a_keys.cpp
** Implements all keys and associated data
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "a_keys.h"
#include "tarray.h"
#include "gi.h"
@ -12,6 +45,18 @@
#include "v_font.h"
//===========================================================================
//
// Data for the LOCKDEFS
//
//===========================================================================
//===========================================================================
//
//
//===========================================================================
struct OneKey
{
PClassActor *key;
@ -45,6 +90,11 @@ struct OneKey
}
};
//===========================================================================
//
//
//===========================================================================
struct Keygroup
{
TArray<OneKey> anykeylist;
@ -59,6 +109,11 @@ struct Keygroup
}
};
//===========================================================================
//
//
//===========================================================================
struct Lock
{
TArray<Keygroup *> keylist;
@ -100,6 +155,10 @@ struct Lock
}
};
//===========================================================================
//
//
//===========================================================================
static Lock *locks[256]; // all valid locks
static bool keysdone=false; // have the locks been initialized?
@ -470,7 +529,9 @@ bool P_CheckKeys (AActor *owner, int keynum, bool remote)
//
//==========================================================================
IMPLEMENT_CLASS(AKey, false, false, false, false)
IMPLEMENT_CLASS(AKey, false, false)
DEFINE_FIELD(AKey, KeyNumber)
bool AKey::HandlePickup (AInventory *item)
{
@ -485,13 +546,14 @@ bool AKey::HandlePickup (AInventory *item)
item->ItemFlags |= IF_PICKUPGOOD;
return true;
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
//===========================================================================
//
//
//===========================================================================
bool AKey::ShouldStay ()
{
return !!multiplayer;

48
src/g_inventory/a_keys.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef A_KEYS_H
#define A_KEYS_H
#include "a_pickups.h"
class AKey : public AInventory
{
DECLARE_CLASS (AKey, AInventory)
public:
virtual bool HandlePickup (AInventory *item) override;
BYTE KeyNumber;
protected:
virtual bool ShouldStay () override;
};
bool P_CheckKeys (AActor *owner, int keynum, bool remote);
void P_InitKeyMessages ();
void P_DeinitKeyMessages ();
int P_GetMapColorForLock (int lock);
int P_GetMapColorForKey (AInventory *key);
// PuzzleItems work in conjunction with the UsePuzzleItem special
class PClassPuzzleItem : public PClassInventory
{
DECLARE_CLASS(PClassPuzzleItem, PClassInventory);
protected:
public:
virtual void DeriveData(PClass *newclass);
FString PuzzFailMessage;
};
class APuzzleItem : public AInventory
{
DECLARE_CLASS_WITH_META(APuzzleItem, AInventory, PClassPuzzleItem)
public:
virtual bool ShouldStay () override;
virtual bool Use (bool pickup) override;
virtual bool HandlePickup (AInventory *item) override;
int PuzzleItemNumber;
};
#endif

File diff suppressed because it is too large Load diff

204
src/g_inventory/a_pickups.h Normal file
View file

@ -0,0 +1,204 @@
#ifndef __A_PICKUPS_H__
#define __A_PICKUPS_H__
#include "actor.h"
#include "info.h"
#include "s_sound.h"
#define NUM_WEAPON_SLOTS 10
class player_t;
class FConfigFile;
class PClassPlayerPawn;
struct visstyle_t;
/************************************************************************/
/* Class definitions */
/************************************************************************/
// A pickup is anything the player can pickup (i.e. weapons, ammo, powerups, etc)
enum
{
IF_ACTIVATABLE = 1<<0, // can be activated
IF_ACTIVATED = 1<<1, // is currently activated
IF_PICKUPGOOD = 1<<2, // HandlePickup wants normal pickup FX to happen
IF_QUIET = 1<<3, // Don't give feedback when picking up
IF_AUTOACTIVATE = 1<<4, // Automatically activate item on pickup
IF_UNDROPPABLE = 1<<5, // Item cannot be removed unless done explicitly with RemoveInventory
IF_INVBAR = 1<<6, // Item appears in the inventory bar
IF_HUBPOWER = 1<<7, // Powerup is kept when moving in a hub
IF_UNTOSSABLE = 1<<8, // The player cannot manually drop the item
IF_ADDITIVETIME = 1<<9, // when picked up while another item is active, time is added instead of replaced.
IF_ALWAYSPICKUP = 1<<10, // For IF_AUTOACTIVATE, MaxAmount=0 items: Always "pick up", even if it doesn't do anything
IF_FANCYPICKUPSOUND = 1<<11, // Play pickup sound in "surround" mode
IF_BIGPOWERUP = 1<<12, // Affected by RESPAWN_SUPER dmflag
IF_KEEPDEPLETED = 1<<13, // Items with this flag are retained even when they run out.
IF_IGNORESKILL = 1<<14, // Ignores any skill related multiplicators when giving this item.
IF_CREATECOPYMOVED = 1<<15, // CreateCopy changed the owner (copy's Owner field holds new owner).
IF_INITEFFECTFAILED = 1<<16, // CreateCopy tried to activate a powerup and activation failed (can happen with PowerMorph)
IF_NOATTENPICKUPSOUND = 1<<17, // Play pickup sound with ATTN_NONE
IF_PERSISTENTPOWER = 1<<18, // Powerup is kept when travelling between levels
IF_RESTRICTABSOLUTELY = 1<<19, // RestrictedTo and ForbiddenTo do not allow pickup in any form by other classes
IF_NEVERRESPAWN = 1<<20, // Never, ever respawns
IF_NOSCREENFLASH = 1<<21, // No pickup flash on the player's screen
IF_TOSSED = 1<<22, // Was spawned by P_DropItem (i.e. as a monster drop)
IF_ALWAYSRESPAWN = 1<<23, // Always respawn, regardless of dmflag
IF_TRANSFER = 1<<24, // All inventory items that the inventory item contains is also transfered to the pickuper
IF_NOTELEPORTFREEZE = 1<<25, // does not 'freeze' the player right after teleporting.
};
class PClassInventory : public PClassActor
{
DECLARE_CLASS(PClassInventory, PClassActor)
public:
PClassInventory();
virtual void DeriveData(PClass *newclass);
virtual void ReplaceClassRef(PClass *oldclass, PClass *newclass);
void Finalize(FStateDefinitions &statedef);
FString PickupMessage;
int GiveQuest; // Optionally give one of the quest items.
FTextureID AltHUDIcon;
TArray<PClassPlayerPawn *> RestrictedToPlayerClass;
TArray<PClassPlayerPawn *> ForbiddenToPlayerClass;
};
class AInventory : public AActor
{
DECLARE_CLASS_WITH_META(AInventory, AActor, PClassInventory)
HAS_OBJECT_POINTERS
public:
virtual void Touch (AActor *toucher) override;
virtual void Serialize(FSerializer &arc) override;
virtual void MarkPrecacheSounds() const override;
virtual void BeginPlay () override;
virtual void Destroy () override;
virtual void Tick() override;
virtual bool Grind(bool items) override;
// virtual methods that only get overridden by special internal classes, like DehackedPickup.
// There is no need to expose these to scripts.
virtual void DepleteOrDestroy ();
virtual bool ShouldRespawn ();
virtual void DoPickupSpecial (AActor *toucher);
// methods that can be overridden by scripts, plus their callers.
virtual bool SpecialDropAction (AActor *dropper);
bool CallSpecialDropAction(AActor *dropper);
virtual bool TryPickup(AActor *&toucher);
virtual bool TryPickupRestricted(AActor *&toucher);
bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // This wraps both virtual methods plus a few more checks.
virtual AInventory *CreateCopy(AActor *other);
AInventory *CallCreateCopy(AActor *other);
virtual AInventory *CreateTossable();
AInventory *CallCreateTossable();
virtual FString PickupMessage();
FString GetPickupMessage();
virtual bool HandlePickup(AInventory *item);
bool CallHandlePickup(AInventory *item);
virtual bool Use(bool pickup);
bool CallUse(bool pickup);
virtual PalEntry GetBlend();
PalEntry CallGetBlend();
virtual bool ShouldStay();
bool CallShouldStay();
virtual void DoEffect();
void CallDoEffect();
virtual void PlayPickupSound(AActor *toucher);
void CallPlayPickupSound(AActor *toucher);
virtual void AttachToOwner(AActor *other);
void CallAttachToOwner(AActor *other);
virtual void DetachFromOwner();
void CallDetachFromOwner();
// 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);
// visual stuff is for later. Right now the VM has not yet access to the needed functionality.
virtual bool DrawPowerup(int x, int y);
virtual int AlterWeaponSprite(visstyle_t *vis);
// virtual on the script side only.
double GetSpeedFactor();
bool GetNoTeleportFreeze();
// Stuff for later when more features are exported.
virtual void Travelled();
virtual void OwnerDied();
bool GoAway();
void GoAwayAndDie();
void Hide();
void BecomeItem ();
void BecomePickup ();
bool DoRespawn();
AInventory *PrevItem(); // Returns the item preceding this one in the list.
AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set.
AInventory *NextInv(); // Returns the next item with IF_INVBAR set.
TObjPtr<AActor> Owner; // Who owns this item? NULL if it's still a pickup.
int Amount; // Amount of item this instance has
int MaxAmount; // Max amount of item this instance can have
int InterHubAmount; // Amount of item that can be kept between hubs or levels
int RespawnTics; // Tics from pickup time to respawn time
FTextureID Icon; // Icon to show on status bar or HUD
int DropTime; // Countdown after dropping
PClassActor *SpawnPointClass; // For respawning like Heretic's mace
DWORD ItemFlags;
PClassActor *PickupFlash; // actor to spawn as pickup flash
FSoundIDNoInit PickupSound;
protected:
bool CanPickup(AActor * toucher);
void GiveQuest(AActor * toucher);
private:
static int StaticLastMessageTic;
static FString StaticLastMessage;
};
class AStateProvider : public AInventory
{
DECLARE_CLASS(AStateProvider, AInventory)
};
// CustomInventory: Supports the Use, Pickup, and Drop states from 96x
class ACustomInventory : public AStateProvider
{
DECLARE_CLASS (ACustomInventory, AStateProvider)
public:
// This is used when an inventory item's use state sequence is executed.
bool CallStateChain (AActor *actor, FState *state);
virtual bool TryPickup (AActor *&toucher) override;
virtual bool Use (bool pickup) override;
virtual bool SpecialDropAction (AActor *dropper) override;
};
extern PClassActor *QuestItemClasses[31];
#endif //__A_PICKUPS_H__

View file

@ -0,0 +1,117 @@
/*
** a_puzzleitems.cpp
** Implements Hexen's puzzle items.
**
**---------------------------------------------------------------------------
** Copyright 2002-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "s_sound.h"
#include "c_console.h"
#include "doomstat.h"
#include "v_font.h"
#include "a_keys.h"
IMPLEMENT_CLASS(PClassPuzzleItem, false, false)
IMPLEMENT_CLASS(APuzzleItem, false, false)
DEFINE_FIELD(APuzzleItem, PuzzleItemNumber)
//===========================================================================
//
//
//
//===========================================================================
void PClassPuzzleItem::DeriveData(PClass *newclass)
{
Super::DeriveData(newclass);
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassPuzzleItem)));
static_cast<PClassPuzzleItem *>(newclass)->PuzzFailMessage = PuzzFailMessage;
}
//===========================================================================
//
//
//
//===========================================================================
bool APuzzleItem::HandlePickup (AInventory *item)
{
// Can't carry more than 1 of each puzzle item in coop netplay
if (multiplayer && !deathmatch && item->GetClass() == GetClass())
{
return true;
}
return Super::HandlePickup (item);
}
//===========================================================================
//
//
//
//===========================================================================
bool APuzzleItem::Use (bool pickup)
{
if (P_UsePuzzleItem (Owner, PuzzleItemNumber))
{
return true;
}
// [RH] Always play the sound if the use fails.
S_Sound (Owner, CHAN_VOICE, "*puzzfail", 1, ATTN_IDLE);
if (Owner != NULL && Owner->CheckLocalView (consoleplayer))
{
FString message = GetClass()->PuzzFailMessage;
if (message.IsNotEmpty() && message[0] == '$') message = GStrings[&message[1]];
if (message.IsEmpty()) message = GStrings("TXT_USEPUZZLEFAILED");
C_MidPrintBold (SmallFont, message);
}
return false;
}
//===========================================================================
//
//
//
//===========================================================================
bool APuzzleItem::ShouldStay ()
{
return !!multiplayer;
}

View file

@ -1,10 +1,54 @@
/*
** a_weaponpieces.cpp
** Implements generic weapon pieces
**
**---------------------------------------------------------------------------
** Copyright 2006-2016 Cheistoph Oelckers
** Copyright 2006-2016 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "a_pickups.h"
#include "a_weaponpiece.h"
#include "doomstat.h"
#include "serializer.h"
IMPLEMENT_CLASS(PClassWeaponPiece, false, false, false, false)
IMPLEMENT_CLASS(AWeaponHolder, false, false, false, false)
IMPLEMENT_CLASS(PClassWeaponPiece, false, false)
IMPLEMENT_CLASS(AWeaponHolder, false, false)
DEFINE_FIELD(AWeaponHolder, PieceMask);
DEFINE_FIELD(AWeaponHolder, PieceWeapon);
//===========================================================================
//
//
//
//===========================================================================
void PClassWeaponPiece::ReplaceClassRef(PClass *oldclass, PClass *newclass)
{
@ -16,6 +60,11 @@ void PClassWeaponPiece::ReplaceClassRef(PClass *oldclass, PClass *newclass)
}
}
//===========================================================================
//
//
//
//===========================================================================
void AWeaponHolder::Serialize(FSerializer &arc)
{
@ -24,12 +73,17 @@ void AWeaponHolder::Serialize(FSerializer &arc)
("pieceweapon", PieceWeapon);
}
IMPLEMENT_CLASS(AWeaponPiece, false, true, false, false)
IMPLEMENT_CLASS(AWeaponPiece, false, true)
IMPLEMENT_POINTERS_START(AWeaponPiece)
IMPLEMENT_POINTER(FullWeapon)
IMPLEMENT_POINTERS_END
//===========================================================================
//
//
//
//===========================================================================
void AWeaponPiece::Serialize(FSerializer &arc)
{
@ -49,7 +103,7 @@ void AWeaponPiece::Serialize(FSerializer &arc)
bool AWeaponPiece::TryPickupRestricted (AActor *&toucher)
{
// Wrong class, but try to pick up for ammo
if (ShouldStay())
if (CallShouldStay())
{ // Can't pick up weapons for other classes in coop netplay
return false;
}
@ -148,11 +202,23 @@ bool AWeaponPiece::TryPickup (AActor *&toucher)
return true;
}
//===========================================================================
//
//
//
//===========================================================================
bool AWeaponPiece::ShouldStay ()
{
return PrivateShouldStay ();
}
//===========================================================================
//
//
//
//===========================================================================
bool AWeaponPiece::PrivateShouldStay ()
{
// We want a weapon piece to behave like a weapon, so follow the exact
@ -174,7 +240,7 @@ bool AWeaponPiece::PrivateShouldStay ()
//
//===========================================================================
const char *AWeaponPiece::PickupMessage ()
FString AWeaponPiece::PickupMessage ()
{
if (FullWeapon)
{

View file

@ -1,3 +1,6 @@
#pragma once
#include "a_pickups.h"
#include "a_weapons.h"
//
class PClassWeaponPiece : public PClassInventory
@ -16,12 +19,12 @@ protected:
bool PrivateShouldStay ();
public:
void Serialize(FSerializer &arc);
bool TryPickup (AActor *&toucher);
bool TryPickupRestricted (AActor *&toucher);
bool ShouldStay ();
virtual const char *PickupMessage ();
virtual void PlayPickupSound (AActor *toucher);
virtual void Serialize(FSerializer &arc) override;
virtual bool TryPickup (AActor *&toucher) override;
virtual bool TryPickupRestricted (AActor *&toucher) override;
virtual bool ShouldStay () override;
virtual FString PickupMessage () override;
virtual void PlayPickupSound (AActor *toucher) override;
int PieceValue;
PClassActor *WeaponClass;
@ -39,5 +42,5 @@ public:
PClassActor * PieceWeapon;
void Serialize(FSerializer &arc);
virtual void Serialize(FSerializer &arc) override;
};

View file

@ -1,3 +1,38 @@
/*
** a_weapons.cpp
** Implements weapon handling
**
**---------------------------------------------------------------------------
** Copyright 2000-2016 Randy Heit
** Copyright 2006-2016 Cheistoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <string.h>
#include "a_pickups.h"
@ -13,15 +48,19 @@
#include "cmdlib.h"
#include "templates.h"
#include "sbar.h"
#include "vm.h"
#include "doomstat.h"
#include "g_level.h"
#include "d_net.h"
#include "serializer.h"
#include "thingdef.h"
#include "virtual.h"
#include "a_ammo.h"
#define BONUSADD 6
IMPLEMENT_CLASS(AWeapon, false, true, false, false)
extern FFlagDef WeaponFlagDefs[];
IMPLEMENT_CLASS(AWeapon, false, true)
IMPLEMENT_POINTERS_START(AWeapon)
IMPLEMENT_POINTER(Ammo1)
@ -29,6 +68,46 @@ IMPLEMENT_POINTERS_START(AWeapon)
IMPLEMENT_POINTER(SisterWeapon)
IMPLEMENT_POINTERS_END
DEFINE_FIELD(AWeapon, WeaponFlags)
DEFINE_FIELD(AWeapon, AmmoType1)
DEFINE_FIELD(AWeapon, AmmoType2)
DEFINE_FIELD(AWeapon, AmmoGive1)
DEFINE_FIELD(AWeapon, AmmoGive2)
DEFINE_FIELD(AWeapon, MinAmmo1)
DEFINE_FIELD(AWeapon, MinAmmo2)
DEFINE_FIELD(AWeapon, AmmoUse1)
DEFINE_FIELD(AWeapon, AmmoUse2)
DEFINE_FIELD(AWeapon, Kickback)
DEFINE_FIELD(AWeapon, YAdjust)
DEFINE_FIELD(AWeapon, UpSound)
DEFINE_FIELD(AWeapon, ReadySound)
DEFINE_FIELD(AWeapon, SisterWeaponType)
DEFINE_FIELD(AWeapon, ProjectileType)
DEFINE_FIELD(AWeapon, AltProjectileType)
DEFINE_FIELD(AWeapon, SelectionOrder)
DEFINE_FIELD(AWeapon, MinSelAmmo1)
DEFINE_FIELD(AWeapon, MinSelAmmo2)
DEFINE_FIELD(AWeapon, MoveCombatDist)
DEFINE_FIELD(AWeapon, ReloadCounter)
DEFINE_FIELD(AWeapon, BobStyle)
DEFINE_FIELD(AWeapon, BobSpeed)
DEFINE_FIELD(AWeapon, BobRangeX)
DEFINE_FIELD(AWeapon, BobRangeY)
DEFINE_FIELD(AWeapon, Ammo1)
DEFINE_FIELD(AWeapon, Ammo2)
DEFINE_FIELD(AWeapon, SisterWeapon)
DEFINE_FIELD(AWeapon, FOVScale)
DEFINE_FIELD(AWeapon, Crosshair)
DEFINE_FIELD(AWeapon, GivenAsMorphWeapon)
DEFINE_FIELD(AWeapon, bAltFire)
DEFINE_FIELD_BIT(AWeapon, WeaponFlags, bDehAmmo, WIF_DEHAMMO)
//===========================================================================
//
//
//
//===========================================================================
FString WeaponSection;
TArray<FString> KeyConfWeapons;
FWeaponSlots *PlayingKeyConf;
@ -38,7 +117,13 @@ TMap<PClassWeapon *, int> Weapons_hton;
static int ntoh_cmp(const void *a, const void *b);
IMPLEMENT_CLASS(PClassWeapon, false, false, false, false)
IMPLEMENT_CLASS(PClassWeapon, false, false)
//===========================================================================
//
//
//
//===========================================================================
PClassWeapon::PClassWeapon()
{
@ -46,6 +131,12 @@ PClassWeapon::PClassWeapon()
SlotPriority = INT_MAX;
}
//===========================================================================
//
//
//
//===========================================================================
void PClassWeapon::DeriveData(PClass *newclass)
{
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassWeapon)));
@ -57,6 +148,12 @@ void PClassWeapon::DeriveData(PClass *newclass)
}
//===========================================================================
//
//
//
//===========================================================================
void PClassWeapon::ReplaceClassRef(PClass *oldclass, PClass *newclass)
{
Super::ReplaceClassRef(oldclass, newclass);
@ -69,6 +166,12 @@ void PClassWeapon::ReplaceClassRef(PClass *oldclass, PClass *newclass)
}
}
//===========================================================================
//
//
//
//===========================================================================
void PClassWeapon::Finalize(FStateDefinitions &statedef)
{
Super::Finalize(statedef);
@ -172,7 +275,7 @@ void AWeapon::MarkPrecacheSounds() const
bool AWeapon::TryPickupRestricted (AActor *&toucher)
{
// Wrong class, but try to pick up for ammo
if (ShouldStay())
if (CallShouldStay())
{ // Can't pick up weapons for other classes in coop netplay
return false;
}
@ -279,10 +382,6 @@ bool AWeapon::HandlePickup (AInventory *item)
}
return true;
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
@ -299,7 +398,7 @@ bool AWeapon::PickupForAmmo (AWeapon *ownedWeapon)
bool gotstuff = false;
// Don't take ammo if the weapon sticks around.
if (!ShouldStay ())
if (!CallShouldStay ())
{
int oldamount1 = 0;
int oldamount2 = 0;
@ -361,7 +460,7 @@ AInventory *AWeapon::CreateTossable ()
(((AWeapon*)SisterWeapon->GetDefault())->AmmoGive1 > 0 ||
((AWeapon*)SisterWeapon->GetDefault())->AmmoGive2 > 0))
{
return SisterWeapon->CreateTossable ();
return SisterWeapon->CallCreateTossable ();
}
AWeapon *copy = static_cast<AWeapon *> (Super::CreateTossable ());
@ -597,6 +696,16 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo, int am
return false;
}
DEFINE_ACTION_FUNCTION(AWeapon, CheckAmmo)
{
PARAM_SELF_PROLOGUE(AWeapon);
PARAM_INT(mode);
PARAM_BOOL(autoswitch);
PARAM_BOOL_DEF(require);
PARAM_INT_DEF(ammocnt);
ACTION_RETURN_BOOL(self->CheckAmmo(mode, autoswitch, require, ammocnt));
}
//===========================================================================
//
// AWeapon :: DepleteAmmo
@ -652,6 +761,15 @@ bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough, int ammouse)
return true;
}
DEFINE_ACTION_FUNCTION(AWeapon, DepleteAmmo)
{
PARAM_SELF_PROLOGUE(AWeapon);
PARAM_BOOL(altfire);
PARAM_BOOL_DEF(checkenough);
PARAM_INT_DEF(ammouse);
ACTION_RETURN_BOOL(self->DepleteAmmo(altfire, checkenough, ammouse));
}
//===========================================================================
//
@ -714,6 +832,25 @@ void AWeapon::EndPowerup ()
}
}
DEFINE_ACTION_FUNCTION(AWeapon, EndPowerup)
{
PARAM_SELF_PROLOGUE(AWeapon);
self->EndPowerup();
return 0;
}
void AWeapon::CallEndPowerup()
{
IFVIRTUAL(AWeapon, EndPowerup)
{
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else EndPowerup();
}
//===========================================================================
//
// AWeapon :: GetUpState
@ -722,7 +859,16 @@ void AWeapon::EndPowerup ()
FState *AWeapon::GetUpState ()
{
return FindState(NAME_Select);
IFVIRTUAL(AWeapon, GetUpState)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
return nullptr;
}
//===========================================================================
@ -733,7 +879,16 @@ FState *AWeapon::GetUpState ()
FState *AWeapon::GetDownState ()
{
return FindState(NAME_Deselect);
IFVIRTUAL(AWeapon, GetDownState)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
return nullptr;
}
//===========================================================================
@ -744,7 +899,16 @@ FState *AWeapon::GetDownState ()
FState *AWeapon::GetReadyState ()
{
return FindState(NAME_Ready);
IFVIRTUAL(AWeapon, GetReadyState)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
return nullptr;
}
//===========================================================================
@ -755,11 +919,16 @@ FState *AWeapon::GetReadyState ()
FState *AWeapon::GetAtkState (bool hold)
{
FState * state=NULL;
if (hold) state = FindState(NAME_Hold);
if (state == NULL) state = FindState(NAME_Fire);
return state;
IFVIRTUAL(AWeapon, GetAtkState)
{
VMValue params[2] = { (DObject*)this, hold };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return retval;
}
return nullptr;
}
//===========================================================================
@ -770,11 +939,16 @@ FState *AWeapon::GetAtkState (bool hold)
FState *AWeapon::GetAltAtkState (bool hold)
{
FState * state=NULL;
if (hold) state = FindState(NAME_AltHold);
if (state == NULL) state = FindState(NAME_AltFire);
return state;
IFVIRTUAL(AWeapon, GetAltAtkState)
{
VMValue params[2] = { (DObject*)this, hold };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return retval;
}
return nullptr;
}
//===========================================================================
@ -791,7 +965,9 @@ FState *AWeapon::GetStateForButtonName (FName button)
/* Weapon giver ***********************************************************/
IMPLEMENT_CLASS(AWeaponGiver, false, false, false, false)
IMPLEMENT_CLASS(AWeaponGiver, false, false)
DEFINE_FIELD(AWeaponGiver, DropAmmoFactor);
void AWeaponGiver::Serialize(FSerializer &arc)
{
@ -1923,9 +2099,9 @@ PClassWeapon *Net_ReadWeapon(BYTE **stream)
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AWeapon, A_ZoomFactor)
DEFINE_ACTION_FUNCTION(AWeapon, A_ZoomFactor)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ACTION_PROLOGUE(AActor);
PARAM_FLOAT_DEF(zoom);
PARAM_INT_DEF(flags);
@ -1951,9 +2127,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AWeapon, A_ZoomFactor)
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AWeapon, A_SetCrosshair)
DEFINE_ACTION_FUNCTION(AWeapon, A_SetCrosshair)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ACTION_PROLOGUE(AActor);
PARAM_INT(xhair);
if (self->player != NULL && self->player->ReadyWeapon != NULL)

233
src/g_inventory/a_weapons.h Normal file
View file

@ -0,0 +1,233 @@
#pragma once
#include "a_ammo.h"
class PClassWeapon;
class AWeapon;
class FWeaponSlot
{
public:
FWeaponSlot() { Clear(); }
FWeaponSlot(const FWeaponSlot &other) { Weapons = other.Weapons; }
FWeaponSlot &operator= (const FWeaponSlot &other) { Weapons = other.Weapons; return *this; }
void Clear() { Weapons.Clear(); }
bool AddWeapon (const char *type);
bool AddWeapon (PClassWeapon *type);
void AddWeaponList (const char *list, bool clear);
AWeapon *PickWeapon (player_t *player, bool checkammo = false);
int Size () const { return (int)Weapons.Size(); }
int LocateWeapon (PClassWeapon *type);
inline PClassWeapon *GetWeapon (int index) const
{
if ((unsigned)index < Weapons.Size())
{
return Weapons[index].Type;
}
else
{
return NULL;
}
}
friend struct FWeaponSlots;
private:
struct WeaponInfo
{
PClassWeapon *Type;
int Position;
};
void SetInitialPositions();
void Sort();
TArray<WeaponInfo> Weapons;
};
// FWeaponSlots::AddDefaultWeapon return codes
enum ESlotDef
{
SLOTDEF_Exists, // Weapon was already assigned a slot
SLOTDEF_Added, // Weapon was successfully added
SLOTDEF_Full // The specifed slot was full
};
struct FWeaponSlots
{
FWeaponSlots() { Clear(); }
FWeaponSlots(const FWeaponSlots &other);
FWeaponSlot Slots[NUM_WEAPON_SLOTS];
AWeapon *PickNextWeapon (player_t *player);
AWeapon *PickPrevWeapon (player_t *player);
void Clear ();
bool LocateWeapon (PClassWeapon *type, int *const slot, int *const index);
ESlotDef AddDefaultWeapon (int slot, PClassWeapon *type);
void AddExtraWeapons();
void SetFromGameInfo();
void SetFromPlayer(PClassPlayerPawn *type);
void StandardSetup(PClassPlayerPawn *type);
void LocalSetup(PClassActor *type);
void SendDifferences(int playernum, const FWeaponSlots &other);
int RestoreSlots (FConfigFile *config, const char *section);
void PrintSettings();
void AddSlot(int slot, PClassWeapon *type, bool feedback);
void AddSlotDefault(int slot, PClassWeapon *type, bool feedback);
};
void P_PlaybackKeyConfWeapons(FWeaponSlots *slots);
void Net_WriteWeapon(PClassWeapon *type);
PClassWeapon *Net_ReadWeapon(BYTE **stream);
void P_SetupWeapons_ntohton();
void P_WriteDemoWeaponsChunk(BYTE **demo);
void P_ReadDemoWeaponsChunk(BYTE **demo);
// A weapon is just that.
class PClassWeapon : public PClassInventory
{
DECLARE_CLASS(PClassWeapon, PClassInventory);
protected:
virtual void DeriveData(PClass *newclass);
public:
PClassWeapon();
virtual void ReplaceClassRef(PClass *oldclass, PClass *newclass);
void Finalize(FStateDefinitions &statedef);
int SlotNumber;
int SlotPriority;
};
class AWeapon : public AStateProvider
{
DECLARE_CLASS_WITH_META(AWeapon, AStateProvider, PClassWeapon)
HAS_OBJECT_POINTERS
public:
DWORD WeaponFlags;
PClassAmmo *AmmoType1, *AmmoType2; // Types of ammo used by this weapon
int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon
int MinAmmo1, MinAmmo2; // Minimum ammo needed to switch to this weapon
int AmmoUse1, AmmoUse2; // How much ammo to use with each shot
int Kickback;
float YAdjust; // For viewing the weapon fullscreen (visual only so no need to be a double)
FSoundIDNoInit UpSound, ReadySound; // Sounds when coming up and idle
PClassWeapon *SisterWeaponType; // Another weapon to pick up with this one
PClassActor *ProjectileType; // Projectile used by primary attack
PClassActor *AltProjectileType; // Projectile used by alternate attack
int SelectionOrder; // Lower-numbered weapons get picked first
int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo
double MoveCombatDist; // Used by bots, but do they *really* need it?
int ReloadCounter; // For A_CheckForReload
int BobStyle; // [XA] Bobbing style. Defines type of bobbing (e.g. Normal, Alpha) (visual only so no need to be a double)
float BobSpeed; // [XA] Bobbing speed. Defines how quickly a weapon bobs.
float BobRangeX, BobRangeY; // [XA] Bobbing range. Defines how far a weapon bobs in either direction.
// In-inventory instance variables
TObjPtr<AAmmo> Ammo1, Ammo2;
TObjPtr<AWeapon> SisterWeapon;
float FOVScale;
int Crosshair; // 0 to use player's crosshair
bool GivenAsMorphWeapon;
bool bAltFire; // Set when this weapon's alternate fire is used.
virtual void MarkPrecacheSounds() const;
virtual void Serialize(FSerializer &arc) override;
virtual bool ShouldStay () override;
virtual void AttachToOwner (AActor *other) override;
virtual bool HandlePickup (AInventory *item) override;
virtual AInventory *CreateCopy (AActor *other) override;
virtual AInventory *CreateTossable () override;
virtual bool TryPickup (AActor *&toucher) override;
virtual bool TryPickupRestricted (AActor *&toucher) override;
virtual bool Use (bool pickup) override;
virtual void Destroy() override;
bool PickupForAmmo(AWeapon *ownedWeapon);
void PostMorphWeapon();
// scripted virtuals.
FState *GetUpState ();
FState *GetDownState ();
FState *GetReadyState ();
FState *GetAtkState (bool hold);
FState *GetAltAtkState (bool hold);
FState *GetStateForButtonName (FName button);
virtual void EndPowerup ();
void CallEndPowerup();
enum
{
PrimaryFire,
AltFire,
EitherFire
};
bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1);
bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1);
enum
{
BobNormal,
BobInverse,
BobAlpha,
BobInverseAlpha,
BobSmooth,
BobInverseSmooth
};
protected:
AAmmo *AddAmmo (AActor *other, PClassActor *ammotype, int amount);
bool AddExistingAmmo (AAmmo *ammo, int amount);
AWeapon *AddWeapon (PClassWeapon *weapon);
};
enum
{
WIF_NOAUTOFIRE = 0x00000001, // weapon does not autofire
WIF_READYSNDHALF = 0x00000002, // ready sound is played ~1/2 the time
WIF_DONTBOB = 0x00000004, // don't bob the weapon
WIF_AXEBLOOD = 0x00000008, // weapon makes axe blood on impact (Hexen only)
WIF_NOALERT = 0x00000010, // weapon does not alert monsters
WIF_AMMO_OPTIONAL = 0x00000020, // weapon can use ammo but does not require it
WIF_ALT_AMMO_OPTIONAL = 0x00000040, // alternate fire can use ammo but does not require it
WIF_PRIMARY_USES_BOTH = 0x00000080, // primary fire uses both ammo
WIF_ALT_USES_BOTH = 0x00000100, // alternate fire uses both ammo
WIF_WIMPY_WEAPON = 0x00000200, // change away when ammo for another weapon is replenished
WIF_POWERED_UP = 0x00000400, // this is a tome-of-power'ed version of its sister
WIF_AMMO_CHECKBOTH = 0x00000800, // check for both primary and secondary fire before switching it off
WIF_NO_AUTO_SWITCH = 0x00001000, // never switch to this weapon when it's picked up
WIF_STAFF2_KICKBACK = 0x00002000, // the powered-up Heretic staff has special kickback
WIF_NOAUTOAIM = 0x00004000, // this weapon never uses autoaim (useful for ballistic projectiles)
WIF_MELEEWEAPON = 0x00008000, // melee weapon. Used by bots and monster AI.
WIF_DEHAMMO = 0x00010000, // Uses Doom's original amount of ammo for the respective attack functions so that old DEHACKED patches work as intended.
// AmmoUse1 will be set to the first attack's ammo use so that checking for empty weapons still works
WIF_NODEATHDESELECT = 0x00020000, // Don't jump to the Deselect state when the player dies
WIF_NODEATHINPUT = 0x00040000, // The weapon cannot be fired/reloaded/whatever when the player is dead
WIF_CHEATNOTWEAPON = 0x08000000, // Give cheat considers this not a weapon (used by Sigil)
// Flags used only by bot AI:
WIF_BOT_REACTION_SKILL_THING = 1<<31, // I don't understand this
WIF_BOT_EXPLOSIVE = 1<<30, // weapon fires an explosive
WIF_BOT_BFG = 1<<28, // this is a BFG
};
class AWeaponGiver : public AWeapon
{
DECLARE_CLASS(AWeaponGiver, AWeapon)
public:
virtual bool TryPickup(AActor *&toucher) override;
virtual void Serialize(FSerializer &arc) override;
double DropAmmoFactor;
};

View file

@ -79,7 +79,6 @@
#include "v_palette.h"
#include "menu/menu.h"
#include "a_sharedglobal.h"
#include "a_strifeglobal.h"
#include "r_data/colormaps.h"
#include "r_renderer.h"
#include "r_utility.h"
@ -478,7 +477,8 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
// Set the initial quest log text for Strife.
for (i = 0; i < MAXPLAYERS; ++i)
{
players[i].SetLogText ("Find help");
if (playeringame[i])
players[i].SetLogText ("Find help");
}
}
@ -903,7 +903,7 @@ public:
void Tick ();
};
IMPLEMENT_CLASS(DAutosaver, false, false, false, false)
IMPLEMENT_CLASS(DAutosaver, false, false)
void DAutosaver::Tick ()
{
@ -1092,7 +1092,7 @@ void G_WorldDone (void)
if (strncmp (nextlevel, "enDSeQ", 6) == 0)
{
FName endsequence = ENamedName(strtol(nextlevel.GetChars()+6, NULL, 16));
// Strife needs a special case here to choose between good and sad ending. Bad is handled elsewherw.
// Strife needs a special case here to choose between good and sad ending. Bad is handled elsewhere.
if (endsequence == NAME_Inter_Strife)
{
if (players[0].mo->FindInventory (QuestItemClasses[24]) ||
@ -1850,55 +1850,46 @@ void FLevelLocals::AddScroller (int secnum)
//==========================================================================
//
// sets up the script-side version of FLevelLocals
// Since this is a global variable and the script compiler does
// not allow defining them, it will be fully set up here.
//
//
//==========================================================================
void G_InitLevelLocalsForScript()
{
PStruct *lstruct = NewStruct("LevelLocals", nullptr);
PField *levelf = new PField("level", lstruct, VARF_Native | VARF_Static, (intptr_t)&level);
GlobalSymbols.AddSymbol(levelf);
// This only exports a selection of fields. Not everything here is useful to the playsim.
lstruct->AddNativeField("time", TypeSInt32, myoffsetof(FLevelLocals, time), VARF_ReadOnly);
lstruct->AddNativeField("maptime", TypeSInt32, myoffsetof(FLevelLocals, maptime), VARF_ReadOnly);
lstruct->AddNativeField("totaltime", TypeSInt32, myoffsetof(FLevelLocals, totaltime), VARF_ReadOnly);
lstruct->AddNativeField("starttime", TypeSInt32, myoffsetof(FLevelLocals, starttime), VARF_ReadOnly);
lstruct->AddNativeField("partime", TypeSInt32, myoffsetof(FLevelLocals, partime), VARF_ReadOnly);
lstruct->AddNativeField("sucktime", TypeSInt32, myoffsetof(FLevelLocals, sucktime), VARF_ReadOnly);
lstruct->AddNativeField("cluster", TypeSInt32, myoffsetof(FLevelLocals, cluster), VARF_ReadOnly);
lstruct->AddNativeField("clusterflags", TypeSInt32, myoffsetof(FLevelLocals, clusterflags), VARF_ReadOnly);
lstruct->AddNativeField("levelnum", TypeSInt32, myoffsetof(FLevelLocals, levelnum), VARF_ReadOnly);
//lstruct->AddNativeField("levelname", TypeString, myoffsetof(FLevelLocals, LevelName), VARF_ReadOnly); // must use an access function to resolve string table references.
lstruct->AddNativeField("mapname", TypeString, myoffsetof(FLevelLocals, MapName), VARF_ReadOnly);
lstruct->AddNativeField("nextmap", TypeString, myoffsetof(FLevelLocals, NextMap));
lstruct->AddNativeField("nextsecretmap", TypeString, myoffsetof(FLevelLocals, NextSecretMap));
lstruct->AddNativeField("maptype", TypeSInt32, myoffsetof(FLevelLocals, maptype), VARF_ReadOnly);
lstruct->AddNativeField("monsterstelefrag", TypeSInt32, myoffsetof(FLevelLocals, flags), VARF_ReadOnly, LEVEL_MONSTERSTELEFRAG);
lstruct->AddNativeField("actownspecial", TypeSInt32, myoffsetof(FLevelLocals, flags), VARF_ReadOnly, LEVEL_ACTOWNSPECIAL);
lstruct->AddNativeField("sndseqtotalctrl", TypeSInt32, myoffsetof(FLevelLocals, flags), VARF_ReadOnly, LEVEL_SNDSEQTOTALCTRL);
lstruct->AddNativeField("allmap", TypeSInt32, myoffsetof(FLevelLocals, flags2), 0, (LEVEL2_ALLMAP));
lstruct->AddNativeField("missilesactivateimpact", TypeSInt32, myoffsetof(FLevelLocals, flags2), 0, LEVEL2_MISSILESACTIVATEIMPACT);
lstruct->AddNativeField("monsterfallingdamage", TypeSInt32, myoffsetof(FLevelLocals, flags2), 0, LEVEL2_MONSTERFALLINGDAMAGE);
lstruct->AddNativeField("checkswitchrange", TypeSInt32, myoffsetof(FLevelLocals, flags2), 0, LEVEL2_CHECKSWITCHRANGE);
lstruct->AddNativeField("polygrind", TypeSInt32, myoffsetof(FLevelLocals, flags2), 0, LEVEL2_POLYGRIND);
lstruct->AddNativeField("music", TypeString, myoffsetof(FLevelLocals, Music), VARF_ReadOnly);
lstruct->AddNativeField("musicorder", TypeSInt32, myoffsetof(FLevelLocals, musicorder), VARF_ReadOnly);
lstruct->AddNativeField("total_secrets", TypeSInt32, myoffsetof(FLevelLocals, total_secrets), VARF_ReadOnly);
lstruct->AddNativeField("found_secrets", TypeSInt32, myoffsetof(FLevelLocals, found_secrets));
lstruct->AddNativeField("total_items", TypeSInt32, myoffsetof(FLevelLocals, total_items), VARF_ReadOnly);
lstruct->AddNativeField("found_items", TypeSInt32, myoffsetof(FLevelLocals, found_items));
lstruct->AddNativeField("total_monsters", TypeSInt32, myoffsetof(FLevelLocals, total_monsters), VARF_ReadOnly);
lstruct->AddNativeField("killed_monsters", TypeSInt32, myoffsetof(FLevelLocals, killed_monsters));
lstruct->AddNativeField("gravity", TypeFloat64, myoffsetof(FLevelLocals, gravity));
lstruct->AddNativeField("aircontrol", TypeFloat64, myoffsetof(FLevelLocals, aircontrol));
lstruct->AddNativeField("airfriction", TypeFloat64, myoffsetof(FLevelLocals, airfriction));
lstruct->AddNativeField("airsupply", TypeSInt32, myoffsetof(FLevelLocals, airsupply));
lstruct->AddNativeField("teamdamage", TypeFloat64, myoffsetof(FLevelLocals, teamdamage));
}
DEFINE_FIELD(FLevelLocals, time)
DEFINE_FIELD(FLevelLocals, maptime)
DEFINE_FIELD(FLevelLocals, totaltime)
DEFINE_FIELD(FLevelLocals, starttime)
DEFINE_FIELD(FLevelLocals, partime)
DEFINE_FIELD(FLevelLocals, sucktime)
DEFINE_FIELD(FLevelLocals, cluster)
DEFINE_FIELD(FLevelLocals, clusterflags)
DEFINE_FIELD(FLevelLocals, levelnum)
DEFINE_FIELD(FLevelLocals, LevelName)
DEFINE_FIELD(FLevelLocals, MapName)
DEFINE_FIELD(FLevelLocals, NextMap)
DEFINE_FIELD(FLevelLocals, NextSecretMap)
DEFINE_FIELD(FLevelLocals, maptype)
DEFINE_FIELD(FLevelLocals, Music)
DEFINE_FIELD(FLevelLocals, musicorder)
DEFINE_FIELD(FLevelLocals, total_secrets)
DEFINE_FIELD(FLevelLocals, found_secrets)
DEFINE_FIELD(FLevelLocals, total_items)
DEFINE_FIELD(FLevelLocals, found_items)
DEFINE_FIELD(FLevelLocals, total_monsters)
DEFINE_FIELD(FLevelLocals, killed_monsters)
DEFINE_FIELD(FLevelLocals, gravity)
DEFINE_FIELD(FLevelLocals, aircontrol)
DEFINE_FIELD(FLevelLocals, airfriction)
DEFINE_FIELD(FLevelLocals, airsupply)
DEFINE_FIELD(FLevelLocals, teamdamage)
DEFINE_FIELD_BIT(FLevelLocals, flags, monsterstelefrag, LEVEL_MONSTERSTELEFRAG)
DEFINE_FIELD_BIT(FLevelLocals, flags, actownspecial, LEVEL_ACTOWNSPECIAL)
DEFINE_FIELD_BIT(FLevelLocals, flags, sndseqtotalctrl, LEVEL_SNDSEQTOTALCTRL)
DEFINE_FIELD_BIT(FLevelLocals, flags2, allmap, LEVEL2_ALLMAP)
DEFINE_FIELD_BIT(FLevelLocals, flags2, missilesactivateimpact, LEVEL2_MISSILESACTIVATEIMPACT)
DEFINE_FIELD_BIT(FLevelLocals, flags2, monsterfallingdamage, LEVEL2_MONSTERFALLINGDAMAGE)
DEFINE_FIELD_BIT(FLevelLocals, flags2, checkswitchrange, LEVEL2_CHECKSWITCHRANGE)
DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND)
DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS)
//==========================================================================
//

View file

@ -1508,10 +1508,18 @@ level_info_t *FMapInfoParser::ParseMapHeader(level_info_t &defaultinfo)
if (sc.CheckNumber())
{ // MAPNAME is a number; assume a Hexen wad
char maptemp[8];
mysnprintf (maptemp, countof(maptemp), "MAP%02d", sc.Number);
mapname = maptemp;
HexenHack = true;
if (format_type == FMT_New)
{
mapname = sc.String;
}
else
{
char maptemp[8];
mysnprintf(maptemp, countof(maptemp), "MAP%02d", sc.Number);
mapname = maptemp;
HexenHack = true;
format_type = FMT_Old;
}
}
else
{

View file

@ -1,81 +0,0 @@
#include "info.h"
#include "a_pickups.h"
#include "a_artifacts.h"
#include "gstrings.h"
#include "p_local.h"
#include "p_spec.h"
#include "gi.h"
#include "s_sound.h"
#include "m_random.h"
#include "doomstat.h"
#include "g_game.h"
#include "d_player.h"
#include "a_morph.h"
static FRandom pr_tele ("TeleportSelf");
// Teleport (self) ----------------------------------------------------------
class AArtiTeleport : public AInventory
{
DECLARE_CLASS (AArtiTeleport, AInventory)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS(AArtiTeleport, false, false, false, false)
bool AArtiTeleport::Use (bool pickup)
{
DVector3 dest;
int destAngle;
if (deathmatch)
{
unsigned int selections = deathmatchstarts.Size ();
unsigned int i = pr_tele() % selections;
dest = deathmatchstarts[i].pos;
destAngle = deathmatchstarts[i].angle;
}
else
{
FPlayerStart *start = G_PickPlayerStart(int(Owner->player - players));
dest = start->pos;
destAngle = start->angle;
}
dest.Z = ONFLOORZ;
P_Teleport (Owner, dest, (double)destAngle, TELF_SOURCEFOG | TELF_DESTFOG);
bool canlaugh = true;
if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYCHAOSDEVICE))
{ // Teleporting away will undo any morph effects (pig)
if (!P_UndoPlayerMorph (Owner->player, Owner->player, MORPH_UNDOBYCHAOSDEVICE)
&& (Owner->player->MorphStyle & MORPH_FAILNOLAUGH))
{
canlaugh = false;
}
}
if (canlaugh)
{ // Full volume laugh
S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_NONE);
}
return true;
}
//---------------------------------------------------------------------------
//
// FUNC P_AutoUseChaosDevice
//
//---------------------------------------------------------------------------
bool P_AutoUseChaosDevice (player_t *player)
{
AInventory *arti = player->mo->FindInventory(PClass::FindActor("ArtiTeleport"));
if (arti != NULL)
{
player->mo->UseInventory (arti);
player->health = player->mo->health = (player->health+1)/2;
return true;
}
return false;
}

View file

@ -1,623 +0,0 @@
#include "actor.h"
#include "info.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_local.h"
#include "p_enemy.h"
#include "ravenshared.h"
#include "a_action.h"
#include "gi.h"
#include "w_wad.h"
#include "vm.h"
#include "g_level.h"
#include "doomstat.h"
#include "a_pickups.h"
#include "d_player.h"
#include "serializer.h"
#include "vm.h"
#define MAULATORTICS (25*35)
static FRandom pr_minotauratk1 ("MinotaurAtk1");
static FRandom pr_minotaurdecide ("MinotaurDecide");
static FRandom pr_atk ("MinotaurAtk2");
static FRandom pr_minotauratk3 ("MinotaurAtk3");
static FRandom pr_fire ("MntrFloorFire");
static FRandom pr_minotaurslam ("MinotaurSlam");
static FRandom pr_minotaurroam ("MinotaurRoam");
static FRandom pr_minotaurchase ("MinotaurChase");
void P_MinotaurSlam (AActor *source, AActor *target);
DECLARE_ACTION(A_MinotaurLook)
IMPLEMENT_CLASS(AMinotaur, false, false, false, false)
void AMinotaur::Tick ()
{
Super::Tick ();
// The unfriendly Minotaur (Heretic's) is invulnerable while charging
if (!(flags5 & MF5_SUMMONEDMONSTER))
{
// Get MF_SKULLFLY bit and shift it so it matches MF2_INVULNERABLE
DWORD flying = (flags & MF_SKULLFLY) << 3;
if ((flags2 & MF2_INVULNERABLE) != flying)
{
flags2 ^= MF2_INVULNERABLE;
}
}
}
bool AMinotaur::Slam (AActor *thing)
{
// Slamming minotaurs shouldn't move non-creatures
if (!(thing->flags3&MF3_ISMONSTER) && !thing->player)
{
return false;
}
return Super::Slam (thing);
}
int AMinotaur::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
damage = Super::DoSpecialDamage (target, damage, damagetype);
if ((damage != -1) && (flags & MF_SKULLFLY))
{ // Slam only when in charge mode
P_MinotaurSlam (this, target);
return -1;
}
return damage;
}
// Minotaur Friend ----------------------------------------------------------
IMPLEMENT_CLASS(AMinotaurFriend, false, false, false, false)
void AMinotaurFriend::BeginPlay ()
{
Super::BeginPlay ();
StartTime = -1;
}
void AMinotaurFriend::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("starttime", StartTime);
}
void AMinotaurFriend::Die (AActor *source, AActor *inflictor, int dmgflags)
{
Super::Die (source, inflictor, dmgflags);
if (tracer && tracer->health > 0 && tracer->player)
{
// Search thinker list for minotaur
TThinkerIterator<AMinotaurFriend> iterator;
AMinotaurFriend *mo;
while ((mo = iterator.Next()) != NULL)
{
if (mo->health <= 0) continue;
// [RH] Minotaurs can't be morphed, so this isn't needed
//if (!(mo->flags&MF_COUNTKILL)) continue; // for morphed minotaurs
if (mo->flags&MF_CORPSE) continue;
if (mo->StartTime >= 0 && (level.maptime - StartTime) >= MAULATORTICS) continue;
if (mo->tracer != NULL && mo->tracer->player == tracer->player) break;
}
if (mo == NULL)
{
AInventory *power = tracer->FindInventory(PClass::FindActor("PowerMinotaur"));
if (power != NULL)
{
power->Destroy ();
}
}
}
}
// Action functions for the minotaur ----------------------------------------
//----------------------------------------------------------------------------
//
// PROC A_MinotaurAtk1
//
// Melee attack.
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDeath)
{
PARAM_SELF_PROLOGUE(AActor);
if (Wads.CheckNumForName ("MNTRF1", ns_sprites) < 0 &&
Wads.CheckNumForName ("MNTRF0", ns_sprites) < 0)
self->SetState(self->FindState ("FadeOut"));
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk1)
{
PARAM_SELF_PROLOGUE(AActor);
player_t *player;
if (!self->target)
{
return 0;
}
S_Sound (self, CHAN_WEAPON, "minotaur/melee", 1, ATTN_NORM);
if (self->CheckMeleeRange())
{
int damage = pr_minotauratk1.HitDice (4);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
if ((player = self->target->player) != NULL &&
player->mo == self->target)
{ // Squish the player
player->deltaviewheight = -16;
}
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MinotaurDecide
//
// Choose a missile attack.
//
//----------------------------------------------------------------------------
#define MNTR_CHARGE_SPEED (13.)
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDecide)
{
PARAM_SELF_PROLOGUE(AActor);
bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER);
AActor *target;
double dist;
target = self->target;
if (!target)
{
return 0;
}
if (!friendly)
{
S_Sound (self, CHAN_WEAPON, "minotaur/sight", 1, ATTN_NORM);
}
dist = self->Distance2D(target);
if (target->Top() > self->Z()
&& target->Top() < self->Top()
&& dist < (friendly ? 16*64. : 8*64.)
&& dist > 1*64.
&& pr_minotaurdecide() < 150)
{ // Charge attack
// Don't call the state function right away
self->SetState (self->FindState ("Charge"), true);
self->flags |= MF_SKULLFLY;
if (!friendly)
{ // Heretic's Minotaur is invulnerable during charge attack
self->flags2 |= MF2_INVULNERABLE;
}
A_FaceTarget (self);
self->VelFromAngle(MNTR_CHARGE_SPEED);
self->special1 = TICRATE/2; // Charge duration
}
else if (target->Z() == target->floorz
&& dist < 9*64.
&& pr_minotaurdecide() < (friendly ? 100 : 220))
{ // Floor fire attack
self->SetState (self->FindState ("Hammer"));
self->special2 = 0;
}
else
{ // Swing attack
A_FaceTarget (self);
// Don't need to call P_SetMobjState because the current state
// falls through to the swing attack
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MinotaurCharge
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurCharge)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *puff;
if (self->target == NULL)
{
return 0;
}
if (self->special1 > 0)
{
PClassActor *type;
if (gameinfo.gametype == GAME_Heretic)
{
type = PClass::FindActor("PhoenixPuff");
}
else
{
type = PClass::FindActor("PunchPuff");
}
puff = Spawn (type, self->Pos(), ALLOW_REPLACE);
puff->Vel.Z = 2;
self->special1--;
}
else
{
self->flags &= ~MF_SKULLFLY;
self->flags2 &= ~MF2_INVULNERABLE;
self->SetState (self->SeeState);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MinotaurAtk2
//
// Swing attack.
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk2)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
DAngle angle;
double vz;
double z;
bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER);
if (self->target == NULL)
{
return 0;
}
S_Sound (self, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM);
if (self->CheckMeleeRange())
{
int damage;
damage = pr_atk.HitDice (friendly ? 3 : 5);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
z = self->Z() + 40;
PClassActor *fx = PClass::FindActor("MinotaurFX1");
if (fx)
{
mo = P_SpawnMissileZ (self, z, self->target, fx);
if (mo != NULL)
{
// S_Sound (mo, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM);
vz = mo->Vel.Z;
angle = mo->Angles.Yaw;
P_SpawnMissileAngleZ (self, z, fx, angle-(45./8), vz);
P_SpawnMissileAngleZ (self, z, fx, angle+(45./8), vz);
P_SpawnMissileAngleZ (self, z, fx, angle-(45./16), vz);
P_SpawnMissileAngleZ (self, z, fx, angle+(45./16), vz);
}
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MinotaurAtk3
//
// Floor fire attack.
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk3)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
player_t *player;
bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER);
if (!self->target)
{
return 0;
}
S_Sound (self, CHAN_VOICE, "minotaur/attack3", 1, ATTN_NORM);
if (self->CheckMeleeRange())
{
int damage;
damage = pr_minotauratk3.HitDice (friendly ? 3 : 5);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
if ((player = self->target->player) != NULL &&
player->mo == self->target)
{ // Squish the player
player->deltaviewheight = -16;
}
}
else
{
if (self->Floorclip > 0 && (i_compatflags & COMPATF_MINOTAUR))
{
// only play the sound.
S_Sound (self, CHAN_WEAPON, "minotaur/fx2hit", 1, ATTN_NORM);
}
else
{
mo = P_SpawnMissile (self, self->target, PClass::FindActor("MinotaurFX2"));
if (mo != NULL)
{
S_Sound (mo, CHAN_WEAPON, "minotaur/attack1", 1, ATTN_NORM);
}
}
}
if (pr_minotauratk3() < 192 && self->special2 == 0)
{
self->SetState (self->FindState ("HammerLoop"));
self->special2 = 1;
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MntrFloorFire
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MntrFloorFire)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
self->SetZ(self->floorz);
double x = pr_fire.Random2() / 64.;
double y = pr_fire.Random2() / 64.;
mo = Spawn("MinotaurFX3", self->Vec2OffsetZ(x, y, self->floorz), ALLOW_REPLACE);
mo->target = self->target;
mo->Vel.X = MinVel; // Force block checking
P_CheckMissileSpawn (mo, self->radius);
return 0;
}
//---------------------------------------------------------------------------
//
// FUNC P_MinotaurSlam
//
//---------------------------------------------------------------------------
void P_MinotaurSlam (AActor *source, AActor *target)
{
DAngle angle;
double thrust;
int damage;
angle = source->AngleTo(target);
thrust = 16 + pr_minotaurslam() / 64.;
target->VelFromAngle(thrust, angle);
damage = pr_minotaurslam.HitDice (static_cast<AMinotaur *>(source) ? 4 : 6);
int newdam = P_DamageMobj (target, NULL, NULL, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, target, angle, 0.);
if (target->player)
{
target->reactiontime = 14+(pr_minotaurslam()&7);
}
}
//----------------------------------------------------------------------------
//
// Minotaur variables
//
// special1 charge duration countdown
// special2 internal to minotaur AI
// StartTime minotaur start time
// tracer pointer to player that spawned it
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//
// A_MinotaurRoam
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurRoam)
{
PARAM_SELF_PROLOGUE(AActor);
// In case pain caused him to skip his fade in.
self->RenderStyle = STYLE_Normal;
if (self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend)))
{
AMinotaurFriend *self1 = static_cast<AMinotaurFriend *> (self);
if (self1->StartTime >= 0 && (level.maptime - self1->StartTime) >= MAULATORTICS)
{
P_DamageMobj (self1, NULL, NULL, TELEFRAG_DAMAGE, NAME_None);
return 0;
}
}
if (pr_minotaurroam() < 30)
CALL_ACTION(A_MinotaurLook, self); // adjust to closest target
if (pr_minotaurroam() < 6)
{
//Choose new direction
self->movedir = pr_minotaurroam() % 8;
FaceMovementDirection (self);
}
if (!P_Move(self))
{
// Turn
if (pr_minotaurroam() & 1)
self->movedir = (self->movedir + 1) % 8;
else
self->movedir = (self->movedir + 7) % 8;
FaceMovementDirection (self);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MinotaurLook
//
// Look for enemy of player
//----------------------------------------------------------------------------
#define MINOTAUR_LOOK_DIST (16*54.)
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurLook)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend)))
{
CALL_ACTION(A_Look, self);
return 0;
}
AActor *mo = NULL;
player_t *player;
double dist;
int i;
AActor *master = self->tracer;
self->target = NULL;
if (deathmatch) // Quick search for players
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i]) continue;
player = &players[i];
mo = player->mo;
if (mo == master) continue;
if (mo->health <= 0) continue;
dist = self->Distance2D(mo);
if (dist > MINOTAUR_LOOK_DIST) continue;
self->target = mo;
break;
}
}
if (!self->target) // Near player monster search
{
if (master && (master->health>0) && (master->player))
mo = P_RoughMonsterSearch(master, 20);
else
mo = P_RoughMonsterSearch(self, 20);
self->target = mo;
}
if (!self->target) // Normal monster search
{
FActorIterator iterator (0);
while ((mo = iterator.Next()) != NULL)
{
if (!(mo->flags3 & MF3_ISMONSTER)) continue;
if (mo->health <= 0) continue;
if (!(mo->flags & MF_SHOOTABLE)) continue;
dist = self->Distance2D(mo);
if (dist > MINOTAUR_LOOK_DIST) continue;
if ((mo == master) || (mo == self)) continue;
if ((mo->flags5 & MF5_SUMMONEDMONSTER) && (mo->tracer == master)) continue;
self->target = mo;
break; // Found actor to attack
}
}
if (self->target)
{
self->SetState (self->SeeState, true);
}
else
{
self->SetState (self->FindState ("Roam"), true);
}
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurChase)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend)))
{
A_Chase (stack, self);
return 0;
}
AMinotaurFriend *self1 = static_cast<AMinotaurFriend *> (self);
// In case pain caused him to skip his fade in.
self1->RenderStyle = STYLE_Normal;
if (self1->StartTime >= 0 && (level.maptime - self1->StartTime) >= MAULATORTICS)
{
P_DamageMobj (self1, NULL, NULL, TELEFRAG_DAMAGE, NAME_None);
return 0;
}
if (pr_minotaurchase() < 30)
CALL_ACTION(A_MinotaurLook, self1); // adjust to closest target
if (!self1->target || (self1->target->health <= 0) ||
!(self1->target->flags&MF_SHOOTABLE))
{ // look for a new target
self1->SetIdle();
return 0;
}
FaceMovementDirection (self1);
self1->reactiontime = 0;
// Melee attack
if (self1->MeleeState && self1->CheckMeleeRange ())
{
if (self1->AttackSound)
{
S_Sound (self1, CHAN_WEAPON, self1->AttackSound, 1, ATTN_NORM);
}
self1->SetState (self1->MeleeState);
return 0;
}
// Missile attack
if (self1->MissileState && P_CheckMissileRange(self1))
{
self1->SetState (self1->MissileState);
return 0;
}
// chase towards target
if (!P_Move (self1))
{
P_NewChaseDir (self1);
FaceMovementDirection (self1);
}
// Active sound
if (pr_minotaurchase() < 6)
{
self1->PlayActiveSound ();
}
return 0;
}

View file

@ -1,29 +0,0 @@
#ifndef __RAVENSHARED_H__
#define __RAVENSHARED_H__
class AActor;
class AMinotaur : public AActor
{
DECLARE_CLASS (AMinotaur, AActor)
public:
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
public:
bool Slam (AActor *);
void Tick ();
};
class AMinotaurFriend : public AMinotaur
{
DECLARE_CLASS (AMinotaurFriend, AMinotaur)
public:
int StartTime;
void Die (AActor *source, AActor *inflictor, int dmgflags);
void BeginPlay ();
void Serialize(FSerializer &arc);
};
#endif //__RAVENSHARED_H__

View file

@ -1,8 +1,6 @@
#include "actor.h"
#include "vm.h"
#include "p_conversation.h"
#include "p_lnspec.h"
#include "a_action.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
@ -19,39 +17,6 @@ static FRandom pr_icesettics ("IceSetTics");
static FRandom pr_freeze ("FreezeDeathChunks");
// SwitchableDecoration: Activate and Deactivate change state ---------------
class ASwitchableDecoration : public AActor
{
DECLARE_CLASS (ASwitchableDecoration, AActor)
public:
void Activate (AActor *activator);
void Deactivate (AActor *activator);
};
IMPLEMENT_CLASS(ASwitchableDecoration, false, false, false, false)
void ASwitchableDecoration::Activate (AActor *activator)
{
SetState (FindState(NAME_Active));
}
void ASwitchableDecoration::Deactivate (AActor *activator)
{
SetState (FindState(NAME_Inactive));
}
// SwitchingDecoration: Only Activate changes state -------------------------
class ASwitchingDecoration : public ASwitchableDecoration
{
DECLARE_CLASS (ASwitchingDecoration, ASwitchableDecoration)
public:
void Deactivate (AActor *activator) {}
};
IMPLEMENT_CLASS(ASwitchingDecoration, false, false, false, false)
//----------------------------------------------------------------------------
//
// PROC A_NoBlocking
@ -156,35 +121,19 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeath)
return 0;
}
//==========================================================================
//
// A_GenericFreezeDeath
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_GenericFreezeDeath)
{
PARAM_SELF_PROLOGUE(AActor);
self->Translation = TRANSLATION(TRANSLATION_Standard, 7);
CALL_ACTION(A_FreezeDeath, self);
return 0;
}
//============================================================================
//
// A_IceSetTics
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_IceSetTics)
void IceSetTics(AActor *self)
{
PARAM_SELF_PROLOGUE(AActor);
int floor;
self->tics = 70+(pr_icesettics()&63);
floor = P_GetThingFloorType (self);
self->tics = 70 + (pr_icesettics() & 63);
floor = P_GetThingFloorType(self);
if (Terrains[floor].DamageMOD == NAME_Fire)
{
self->tics >>= 2;
@ -193,6 +142,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_IceSetTics)
{
self->tics <<= 1;
}
}
DEFINE_ACTION_FUNCTION(AActor, A_IceSetTics)
{
PARAM_SELF_PROLOGUE(AActor);
IceSetTics(self);
return 0;
}
@ -238,7 +193,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeathChunks)
mo->Vel.X = pr_freeze.Random2() / 128.;
mo->Vel.Y = pr_freeze.Random2() / 128.;
mo->Vel.Z = (mo->Z() - self->Z()) / self->Height * 4;
CALL_ACTION(A_IceSetTics, mo); // set a random tic wait
IceSetTics(mo); // set a random tic wait
mo->RenderStyle = self->RenderStyle;
mo->Alpha = self->Alpha;
}
@ -296,7 +251,7 @@ class DCorpsePointer : public DThinker
HAS_OBJECT_POINTERS
public:
DCorpsePointer (AActor *ptr);
void Destroy ();
void Destroy() override;
void Serialize(FSerializer &arc);
TObjPtr<AActor> Corpse;
DWORD Count; // Only the first corpse pointer's count is valid.
@ -304,7 +259,7 @@ private:
DCorpsePointer () {}
};
IMPLEMENT_CLASS(DCorpsePointer, false, true, false, false)
IMPLEMENT_CLASS(DCorpsePointer, false, true)
IMPLEMENT_POINTERS_START(DCorpsePointer)
IMPLEMENT_POINTER(Corpse)
@ -413,46 +368,3 @@ DEFINE_ACTION_FUNCTION(AActor, A_DeQueueCorpse)
return 0;
}
//===========================================================================
//
// FaceMovementDirection
//
//===========================================================================
void FaceMovementDirection(AActor *actor)
{
switch (actor->movedir)
{
case DI_EAST:
actor->Angles.Yaw = 0.;
break;
case DI_NORTHEAST:
actor->Angles.Yaw = 45.;
break;
case DI_NORTH:
actor->Angles.Yaw = 90.;
break;
case DI_NORTHWEST:
actor->Angles.Yaw = 135.;
break;
case DI_WEST:
actor->Angles.Yaw = 180.;
break;
case DI_SOUTHWEST:
actor->Angles.Yaw = 225.;
break;
case DI_SOUTH:
actor->Angles.Yaw = 270.;
break;
case DI_SOUTHEAST:
actor->Angles.Yaw = 315.;
break;
}
}
DEFINE_ACTION_FUNCTION(AActor, FaceMovementDirection)
{
PARAM_SELF_PROLOGUE(AActor);
FaceMovementDirection(self);
return 0;
}

View file

@ -1,29 +0,0 @@
class AActor;
/*
void A_NoBlocking (AActor *);
void A_HideThing (AActor *);
void A_UnHideThing (AActor *);
void A_FreezeDeath (AActor *);
void A_FreezeDeathChunks (AActor *);
void A_GenericFreezeDeath (AActor *);
void A_IceSetTics (AActor *);
void A_IceCheckHeadDone (AActor *);
void A_QueueCorpse (AActor *);
void A_DeQueueCorpse (AActor *);
void A_SetInvulnerable (AActor *);
void A_UnSetInvulnerable (AActor *);
void A_SetReflective (AActor *);
void A_UnSetReflective (AActor *);
void A_SetReflectiveInvulnerable (AActor *);
void A_UnSetReflectiveInvulnerable (AActor *);
void A_SetShootable (AActor *);
void A_UnSetShootable (AActor *);
void A_SetFloorClip (AActor *);
void A_UnSetFloorClip (AActor *);
void A_NoGravity (AActor *);
void A_SkullPop (AActor *);
*/
void FaceMovementDirection (AActor *);

Some files were not shown because too many files have changed in this diff Show more