diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 154feea4c..ec8c1c09d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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/.+") diff --git a/src/__autostart.cpp b/src/__autostart.cpp index 280c3b13d..13e104d75 100644 --- a/src/__autostart.cpp +++ b/src/__autostart.cpp @@ -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; diff --git a/src/actor.h b/src/actor.h index b3bfada1c..03dce8d16 100644 --- a/src/actor.h +++ b/src/actor.h @@ -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; diff --git a/src/am_map.cpp b/src/am_map.cpp index d74927b68..99fda3bd6 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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 it (STAT_MAPMARKER); - AMapMarker *mark; + TThinkerIterator it ("MapMarker", STAT_MAPMARKER); + AActor *mark; while ((mark = it.Next()) != NULL) { diff --git a/src/autosegs.h b/src/autosegs.h index 08abe7cd3..f38e3628e 100644 --- a/src/autosegs.h +++ b/src/autosegs.h @@ -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; diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 93d71092d..3f4d0c592 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -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) diff --git a/src/b_think.cpp b/src/b_think.cpp index d097db864..e41433fa3 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -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"); diff --git a/src/c_console.cpp b/src/c_console.cpp index 3064abb0d..691c4a3ca 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -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 diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index c8f1ee4c6..fe859cb49 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -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 diff --git a/src/c_cvars.h b/src/c_cvars.h index be7676e89..cf6975b86 100644 --- a/src/c_cvars.h +++ b/src/c_cvars.h @@ -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); diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 71aeea0fa..d112634d6 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -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 () { diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 3c0994a3b..0a92a58b9 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -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 diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 880cd7dfe..6f9687aea 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -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 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(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; } diff --git a/src/d_dehacked.h b/src/d_dehacked.h index 564a6d499..434853bf2 100644 --- a/src/d_dehacked.h +++ b/src/d_dehacked.h @@ -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); diff --git a/src/d_main.cpp b/src/d_main.cpp index d7757ddf4..dad2b86d9 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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(); diff --git a/src/d_net.cpp b/src/d_net.cpp index 261133867..4bda25510 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -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) diff --git a/src/d_player.h b/src/d_player.h index 7f57837c7..4cdfb8b79 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 diff --git a/src/decallib.cpp b/src/decallib.cpp index 970bd7458..9fe2966eb 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -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) { diff --git a/src/dobject.cpp b/src/dobject.cpp index ad575bebc..b05ae69ac 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -61,15 +61,18 @@ ClassReg DObject::RegistrationInfo = nullptr, // MyClass "DObject", // Name nullptr, // ParentType - &DVMObject::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()); -} - //========================================================================== // // diff --git a/src/dobject.h b/src/dobject.h index d858e577f..7d27133dc 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -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::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, diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index fa8723e90..827516172 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -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 -------------------------------------------- diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 2c88da92a..dba554498 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -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 { } +//========================================================================== +// +// PType :: SetDefaultValue +// +//========================================================================== + +void PType::SetPointer(void *base, unsigned offset, TArray *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 *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(ptype); } +/* PStatePointer **********************************************************/ + +IMPLEMENT_CLASS(PStatePointer, false, false) + +//========================================================================== +// +// PStatePointer Default Constructor +// +//========================================================================== + +PStatePointer::PStatePointer() +{ + mDescriptiveName = "Pointer"; + 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 } } +//========================================================================== +// +// PArray :: SetDefaultValue +// +//========================================================================== + +void PArray::SetPointer(void *base, unsigned offset, TArray *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, TArrayFlags & 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 *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 TArrayFlags & 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(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(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 &rettypes, const TArray /* 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 &argflags, TArray &argnames, VMFunction *impl, int flags) +unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags, int useflags) { Variant variant; @@ -2497,6 +2649,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &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 &argflags, TArra assert(proto->ArgumentTypes.Size() > 0); auto selftypeptr = dyn_cast(proto->ArgumentTypes[0]); assert(selftypeptr != nullptr); - variant.SelfClass = dyn_cast(selftypeptr->PointedType); + variant.SelfClass = dyn_cast(selftypeptr->PointedType); assert(variant.SelfClass != nullptr); } else @@ -2524,7 +2677,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &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(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(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 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(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) diff --git a/src/dobjtype.h b/src/dobjtype.h index 1ca5df832..ad626ee8b 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -5,10 +5,10 @@ #error You must #include "dobject.h" to get dobjtype.h #endif -#include "vm.h" - typedef std::pair 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 *special=NULL) const; + virtual void SetPointer(void *base, unsigned offset, TArray *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 *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 *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 *special) const override; + void SetPointer(void *base, unsigned offset, TArray *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 *specials) const override; + void SetPointer(void *base, unsigned offset, TArray *specials) const override; static void WriteFields(FSerializer &ar, const void *addr, const TArray &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 ArgFlags; // Should be the same length as Proto->ArgumentTypes TArray 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 Variants; - PClass *OwningClass = nullptr; + PStruct *OwningClass = nullptr; - unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags); + unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &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 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 &rettypes, const TArray &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; diff --git a/src/doomtype.h b/src/doomtype.h index 129c5f122..a9818df78 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -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" diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index 700a407fd..3c33d1634 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -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 () { diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h index 0a5e9bd56..e9327ce70 100644 --- a/src/dsectoreffect.h +++ b/src/dsectoreffect.h @@ -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 diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 06cd5dbfa..fd20a4e79 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -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) { diff --git a/src/dthinker.h b/src/dthinker.h index 95985c2fb..3d580c3c2 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -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(); diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index ddc1a4bd4..6b0059981 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -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); } } diff --git a/src/fragglescript/t_prepro.cpp b/src/fragglescript/t_prepro.cpp index 2129df54f..4128e6661 100644 --- a/src/fragglescript/t_prepro.cpp +++ b/src/fragglescript/t_prepro.cpp @@ -71,7 +71,7 @@ // //========================================================================== -IMPLEMENT_CLASS(DFsSection, false, true, false, false) +IMPLEMENT_CLASS(DFsSection, false, true) IMPLEMENT_POINTERS_START(DFsSection) IMPLEMENT_POINTER(next) diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp index a40122623..b852607b3 100644 --- a/src/fragglescript/t_script.cpp +++ b/src/fragglescript/t_script.cpp @@ -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) diff --git a/src/fragglescript/t_script.h b/src/fragglescript/t_script.h index 3734e16b8..50829e000 100644 --- a/src/fragglescript/t_script.h +++ b/src/fragglescript/t_script.h @@ -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 script; @@ -687,7 +687,7 @@ public: bool nocheckposition; DFraggleThinker(); - void Destroy(); + void Destroy() override; void Serialize(FSerializer & arc); diff --git a/src/fragglescript/t_variable.cpp b/src/fragglescript/t_variable.cpp index 23d24b78a..9bdddda34 100644 --- a/src/fragglescript/t_variable.cpp +++ b/src/fragglescript/t_variable.cpp @@ -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) diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp deleted file mode 100644 index 5807ce175..000000000 --- a/src/g_doom/a_bossbrain.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_doom/a_doomglobal.h b/src/g_doom/a_doomglobal.h deleted file mode 100644 index 31d41f6c0..000000000 --- a/src/g_doom/a_doomglobal.h +++ /dev/null @@ -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__ diff --git a/src/g_doom/a_doommisc.cpp b/src/g_doom/a_doommisc.cpp deleted file mode 100644 index eabf887f0..000000000 --- a/src/g_doom/a_doommisc.cpp +++ /dev/null @@ -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" - diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp deleted file mode 100644 index bc962800f..000000000 --- a/src/g_doom/a_doomweaps.cpp +++ /dev/null @@ -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()) - 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(PClass::FindClass("ArmorBonus")); - } - if (armorbonustype != NULL) - { - assert(armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus))); - ABasicArmorBonus *armorbonus = static_cast(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(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; -} diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp deleted file mode 100644 index f2db5a240..000000000 --- a/src/g_doom/a_painelemental.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp deleted file mode 100644 index da2c7f791..000000000 --- a/src/g_doom/a_scriptedmarine.cpp +++ /dev/null @@ -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(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; - } -} diff --git a/src/g_game.cpp b/src/g_game.cpp index 810a29da1..b5729283e 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -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 // diff --git a/src/g_heretic/a_chicken.cpp b/src/g_heretic/a_chicken.cpp deleted file mode 100644 index 8a109d46d..000000000 --- a/src/g_heretic/a_chicken.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_heretic/a_dsparil.cpp b/src/g_heretic/a_dsparil.cpp deleted file mode 100644 index 5ab4067d3..000000000 --- a/src/g_heretic/a_dsparil.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_heretic/a_hereticartifacts.cpp b/src/g_heretic/a_hereticartifacts.cpp deleted file mode 100644 index 169d0e771..000000000 --- a/src/g_heretic/a_hereticartifacts.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_heretic/a_hereticmisc.cpp b/src/g_heretic/a_hereticmisc.cpp deleted file mode 100644 index 35b24eb93..000000000 --- a/src/g_heretic/a_hereticmisc.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp deleted file mode 100644 index a888be884..000000000 --- a/src/g_heretic/a_hereticweaps.cpp +++ /dev/null @@ -1,1394 +0,0 @@ -/* -#include "templates.h" -#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 "gi.h" -#include "r_data/r_translate.h" -#include "vm.h" -#include "doomstat.h" -*/ - -static FRandom pr_sap ("StaffAtkPL1"); -static FRandom pr_sap2 ("StaffAtkPL2"); -static FRandom pr_fgw ("FireWandPL1"); -static FRandom pr_fgw2 ("FireWandPL2"); -static FRandom pr_boltspark ("BoltSpark"); -static FRandom pr_macerespawn ("MaceRespawn"); -static FRandom pr_maceatk ("FireMacePL1"); -static FRandom pr_gatk ("GauntletAttack"); -static FRandom pr_bfx1 ("BlasterFX1"); -static FRandom pr_ripd ("RipperD"); -static FRandom pr_fb1 ("FireBlasterPL1"); -static FRandom pr_bfx1t ("BlasterFX1Tick"); -static FRandom pr_hrfx2 ("HornRodFX2"); -static FRandom pr_rp ("RainPillar"); -static FRandom pr_fsr1 ("FireSkullRodPL1"); -static FRandom pr_storm ("SkullRodStorm"); -static FRandom pr_impact ("RainImpact"); -static FRandom pr_pfx1 ("PhoenixFX1"); -static FRandom pr_pfx2 ("PhoenixFX2"); -static FRandom pr_fp2 ("FirePhoenixPL2"); - -#define FLAME_THROWER_TICS (10*TICRATE) - -void P_DSparilTeleport (AActor *actor); - -#define USE_BLSR_AMMO_1 1 -#define USE_BLSR_AMMO_2 5 -#define USE_SKRD_AMMO_1 1 -#define USE_SKRD_AMMO_2 5 -#define USE_PHRD_AMMO_1 1 -#define USE_PHRD_AMMO_2 1 -#define USE_MACE_AMMO_1 1 -#define USE_MACE_AMMO_2 5 - -extern bool P_AutoUseChaosDevice (player_t *player); - -// --- Staff ---------------------------------------------------------------- - -//---------------------------------------------------------------------------- -// -// PROC A_StaffAttackPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle angle; - DAngle slope; - player_t *player; - FTranslatedLineTarget t; - - if (NULL == (player = self->player)) - { - return 0; - } - - PARAM_INT (damage); - PARAM_CLASS (puff, AActor); - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - if (puff == NULL) - { - puff = PClass::FindActor(NAME_BulletPuff); // just to be sure - } - angle = self->Angles.Yaw + pr_sap.Random2() * (5.625 / 256); - slope = P_AimLineAttack (self, angle, MELEERANGE); - P_LineAttack (self, angle, MELEERANGE, slope, damage, NAME_Melee, puff, true, &t); - if (t.linetarget) - { - //S_StartSound(player->mo, sfx_stfhit); - // turn to face target - self->Angles.Yaw = t.angleFromSource; - } - return 0; -} - - -//---------------------------------------------------------------------------- -// -// PROC A_FireGoldWandPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireGoldWandPL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle angle; - int damage; - player_t *player; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo(weapon->bAltFire)) - return 0; - } - DAngle pitch = P_BulletSlope(self); - damage = 7 + (pr_fgw() & 7); - angle = self->Angles.Yaw; - if (player->refire) - { - angle += pr_fgw.Random2() * (5.625 / 256); - } - P_LineAttack(self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "GoldWandPuff1"); - S_Sound(self, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireGoldWandPL2 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireGoldWandPL2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - int i; - DAngle angle; - int damage; - double vz; - player_t *player; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - DAngle pitch = P_BulletSlope(self); - - vz = -GetDefaultByName("GoldWandFX2")->Speed * pitch.TanClamped(); - P_SpawnMissileAngle(self, PClass::FindActor("GoldWandFX2"), self->Angles.Yaw - (45. / 8), vz); - P_SpawnMissileAngle(self, PClass::FindActor("GoldWandFX2"), self->Angles.Yaw + (45. / 8), vz); - angle = self->Angles.Yaw - (45. / 8); - for(i = 0; i < 5; i++) - { - damage = 1+(pr_fgw2()&7); - P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "GoldWandPuff2"); - angle += ((45. / 8) * 2) / 4; - } - S_Sound (self, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireCrossbowPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireCrossbowPL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX1")); - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX3"), self->Angles.Yaw - 4.5); - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX3"), self->Angles.Yaw + 4.5); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireCrossbowPL2 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireCrossbowPL2) -{ - 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, PClass::FindActor("CrossbowFX2")); - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX2"), self->Angles.Yaw - 4.5); - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX2"), self->Angles.Yaw + 4.5); - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX3"), self->Angles.Yaw - 9.); - P_SpawnPlayerMissile (self, PClass::FindActor("CrossbowFX3"), self->Angles.Yaw + 9.); - return 0; -} - -//--------------------------------------------------------------------------- -// -// PROC A_GauntletAttack -// -//--------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GauntletAttack) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle Angle; - int damage; - DAngle slope; - int randVal; - double dist; - player_t *player; - PClassActor *pufftype; - FTranslatedLineTarget t; - int actualdamage = 0; - - if (nullptr == (player = self->player)) - { - return 0; - } - - PARAM_INT(power); - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != nullptr) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - - player->GetPSprite(PSP_WEAPON)->x = ((pr_gatk() & 3) - 2); - player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP + (pr_gatk() & 3); - } - Angle = self->Angles.Yaw; - if (power) - { - damage = pr_gatk.HitDice (2); - dist = 4*MELEERANGE; - Angle += pr_gatk.Random2() * (2.8125 / 256); - pufftype = PClass::FindActor("GauntletPuff2"); - } - else - { - damage = pr_gatk.HitDice (2); - dist = SAWRANGE; - Angle += pr_gatk.Random2() * (5.625 / 256); - pufftype = PClass::FindActor("GauntletPuff1"); - } - slope = P_AimLineAttack (self, Angle, dist); - P_LineAttack (self, Angle, dist, slope, damage, NAME_Melee, pufftype, false, &t, &actualdamage); - if (!t.linetarget) - { - if (pr_gatk() > 64) - { - player->extralight = !player->extralight; - } - S_Sound (self, CHAN_AUTO, "weapons/gauntletson", 1, ATTN_NORM); - return 0; - } - randVal = pr_gatk(); - if (randVal < 64) - { - player->extralight = 0; - } - else if (randVal < 160) - { - player->extralight = 1; - } - else - { - player->extralight = 2; - } - if (power) - { - if (!(t.linetarget->flags5 & MF5_DONTDRAIN)) P_GiveBody (self, actualdamage>>1); - S_Sound (self, CHAN_AUTO, "weapons/gauntletspowhit", 1, ATTN_NORM); - } - else - { - S_Sound (self, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM); - } - // turn to face target - DAngle 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; - } - self->flags |= MF_JUSTATTACKED; - return 0; -} - -// --- Mace ----------------------------------------------------------------- - -#define MAGIC_JUNK 1234 - -// Mace FX4 ----------------------------------------------------------------- - -class AMaceFX4 : public AActor -{ - DECLARE_CLASS (AMaceFX4, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(AMaceFX4, false, false, false, false) - -int AMaceFX4::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if ((target->flags2 & MF2_BOSS) || (target->flags3 & MF3_DONTSQUASH) || target->IsTeammate (this->target)) - { // Don't allow cheap boss kills and don't instagib teammates - return damage; - } - else if (target->player) - { // Player specific checks - if (target->player->mo->flags2 & MF2_INVULNERABLE) - { // Can't hurt invulnerable players - return -1; - } - if (P_AutoUseChaosDevice (target->player)) - { // Player was saved using chaos device - return -1; - } - } - return TELEFRAG_DAMAGE; // Something's gonna die -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireMacePL1B -// -//---------------------------------------------------------------------------- - -void FireMacePL1B (AActor *actor) -{ - AActor *ball; - player_t *player; - - if (NULL == (player = actor->player)) - { - return; - } - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return; - } - ball = Spawn("MaceFX2", actor->PosPlusZ(28 - actor->Floorclip), ALLOW_REPLACE); - ball->Vel.Z = 2 - player->mo->Angles.Pitch.TanClamped(); - ball->target = actor; - ball->Angles.Yaw = actor->Angles.Yaw; - ball->AddZ(ball->Vel.Z); - ball->VelFromAngle(); - ball->Vel += actor->Vel.XY()/2; - S_Sound (ball, CHAN_BODY, "weapons/maceshoot", 1, ATTN_NORM); - P_CheckMissileSpawn (ball, actor->radius); -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireMacePL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireMacePL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *ball; - player_t *player; - - if (nullptr == (player = self->player)) - { - return 0; - } - - if (pr_maceatk() < 28) - { - FireMacePL1B(self); - return 0; - } - AWeapon *weapon = player->ReadyWeapon; - if (weapon != nullptr) - { - if (!weapon->DepleteAmmo(weapon->bAltFire)) - return 0; - - player->GetPSprite(PSP_WEAPON)->x = ((pr_maceatk() & 3) - 2); - player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP + (pr_maceatk() & 3); - } - ball = P_SpawnPlayerMissile(self, PClass::FindActor("MaceFX1"), self->Angles.Yaw + (((pr_maceatk() & 7) - 4) * (360. / 256))); - if (ball) - { - ball->special1 = 16; // tics till dropoff - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MacePL1Check -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MacePL1Check) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->special1 == 0) - { - return 0; - } - self->special1 -= 4; - if (self->special1 > 0) - { - return 0; - } - self->special1 = 0; - self->flags &= ~MF_NOGRAVITY; - self->Gravity = 1. / 8;; - // [RH] Avoid some precision loss by scaling the velocity directly -#if 0 - // This is the original code, for reference. - a.ngle_t angle = self->angle>>ANGLETOF.INESHIFT; - self->velx = F.ixedMul(7*F.RACUNIT, f.inecosine[angle]); - self->vely = F.ixedMul(7*F.RACUNIT, f.inesine[angle]); -#else - double velscale = 7 / self->Vel.XY().Length(); - self->Vel.X *= velscale; - self->Vel.Y *= velscale; -#endif - self->Vel.Z *= 0.5; - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MaceBallImpact -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact) -{ - PARAM_SELF_PROLOGUE(AActor); - - if ((self->health != MAGIC_JUNK) && (self->flags & MF_INBOUNCE)) - { // Bounce - self->health = MAGIC_JUNK; - self->Vel.Z *= 0.75; - self->BounceFlags = BOUNCE_None; - self->SetState (self->SpawnState); - S_Sound (self, CHAN_BODY, "weapons/macebounce", 1, ATTN_NORM); - } - else - { // Explode - self->Vel.Zero(); - self->flags |= MF_NOGRAVITY; - self->Gravity = 1; - S_Sound (self, CHAN_BODY, "weapons/macehit", 1, ATTN_NORM); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MaceBallImpact2 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact2) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *tiny; - - if ((self->Z() <= self->floorz) && P_HitFloor (self)) - { // Landed in some sort of liquid - self->Destroy (); - return 0; - } - if (self->flags & MF_INBOUNCE) - { - if (self->Vel.Z < 2) - { - goto boom; - } - - // Bounce - self->Vel.Z *= 0.75; - self->SetState (self->SpawnState); - - tiny = Spawn("MaceFX3", self->Pos(), ALLOW_REPLACE); - tiny->target = self->target; - tiny->Angles.Yaw = self->Angles.Yaw + 90.; - tiny->VelFromAngle(self->Vel.Z - 1.); - tiny->Vel += { self->Vel.X * .5, self->Vel.Y * .5, self->Vel.Z }; - P_CheckMissileSpawn (tiny, self->radius); - - tiny = Spawn("MaceFX3", self->Pos(), ALLOW_REPLACE); - tiny->target = self->target; - tiny->Angles.Yaw = self->Angles.Yaw - 90.; - tiny->VelFromAngle(self->Vel.Z - 1.); - tiny->Vel += { self->Vel.X * .5, self->Vel.Y * .5, self->Vel.Z }; - P_CheckMissileSpawn (tiny, self->radius); - } - else - { // Explode -boom: - self->Vel.Zero(); - self->flags |= MF_NOGRAVITY; - self->BounceFlags = BOUNCE_None; - self->Gravity = 1; - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireMacePL2 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireMacePL2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *mo; - player_t *player; - FTranslatedLineTarget t; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - mo = P_SpawnPlayerMissile (self, 0,0,0, RUNTIME_CLASS(AMaceFX4), self->Angles.Yaw, &t); - if (mo) - { - mo->Vel += self->Vel.XY(); - mo->Vel.Z = 2 - player->mo->Angles.Pitch.TanClamped(); - if (t.linetarget && !t.unlinked) - { - mo->tracer = t.linetarget; - } - } - S_Sound (self, CHAN_WEAPON, "weapons/maceshoot", 1, ATTN_NORM); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_DeathBallImpact -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact) -{ - PARAM_SELF_PROLOGUE(AActor); - - int i; - AActor *target; - DAngle angle = 0.; - bool newAngle; - FTranslatedLineTarget t; - - if ((self->Z() <= self->floorz) && P_HitFloor (self)) - { // Landed in some sort of liquid - self->Destroy (); - return 0; - } - if (self->flags & MF_INBOUNCE) - { - if (self->Vel.Z < 2) - { - goto boom; - } - - // Bounce - newAngle = false; - target = self->tracer; - if (target) - { - if (!(target->flags&MF_SHOOTABLE)) - { // Target died - self->tracer = NULL; - } - else - { // Seek - angle = self->AngleTo(target); - newAngle = true; - } - } - else - { // Find new target - angle = 0.; - for (i = 0; i < 16; i++) - { - P_AimLineAttack (self, angle, 640., &t, 0., ALF_NOFRIENDS|ALF_PORTALRESTRICT, NULL, self->target); - if (t.linetarget && self->target != t.linetarget) - { - self->tracer = t.linetarget; - angle = t.angleFromSource; - newAngle = true; - break; - } - angle += 22.5; - } - } - if (newAngle) - { - self->Angles.Yaw = angle; - self->VelFromAngle(); - } - self->SetState (self->SpawnState); - S_Sound (self, CHAN_BODY, "weapons/macestop", 1, ATTN_NORM); - } - else - { // Explode -boom: - self->Vel.Zero(); - self->flags |= MF_NOGRAVITY; - self->Gravity = 1; - S_Sound (self, CHAN_BODY, "weapons/maceexplode", 1, ATTN_NORM); - } - return 0; -} - - -// Blaster FX 1 ------------------------------------------------------------- - -//---------------------------------------------------------------------------- -// -// Thinker for the ultra-fast blaster PL2 ripper-spawning missile. -// -//---------------------------------------------------------------------------- - -class ABlasterFX1 : public AFastProjectile -{ - DECLARE_CLASS(ABlasterFX1, AFastProjectile) -public: - void Effect (); - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -int ABlasterFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass ("Ironlich"))) - { // Less damage to Ironlich bosses - damage = pr_bfx1() & 1; - if (!damage) - { - return -1; - } - } - return damage; -} - -void ABlasterFX1::Effect () -{ - if (pr_bfx1t() < 64) - { - Spawn("BlasterSmoke", PosAtZ(MAX(Z() - 8., floorz)), ALLOW_REPLACE); - } -} - -IMPLEMENT_CLASS(ABlasterFX1, false, false, false, false) - -// Ripper ------------------------------------------------------------------- - - -class ARipper : public AActor -{ - DECLARE_CLASS (ARipper, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(ARipper, false, false, false, false) - -int ARipper::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass ("Ironlich"))) - { // Less damage to Ironlich bosses - damage = pr_ripd() & 1; - if (!damage) - { - return -1; - } - } - return damage; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireBlasterPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireBlasterPL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle angle; - int damage; - 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; - } - DAngle pitch = P_BulletSlope(self); - damage = pr_fb1.HitDice (4); - angle = self->Angles.Yaw; - if (player->refire) - { - angle += pr_fb1.Random2() * (5.625 / 256); - } - P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "BlasterPuff"); - S_Sound (self, CHAN_WEAPON, "weapons/blastershoot", 1, ATTN_NORM); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_SpawnRippers -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnRippers) -{ - PARAM_SELF_PROLOGUE(AActor); - - unsigned int i; - DAngle angle; - AActor *ripper; - - for(i = 0; i < 8; i++) - { - ripper = Spawn (self->Pos(), ALLOW_REPLACE); - angle = i*45.; - ripper->target = self->target; - ripper->Angles.Yaw = angle; - ripper->VelFromAngle(); - P_CheckMissileSpawn (ripper, self->radius); - } - return 0; -} - -// --- Skull rod ------------------------------------------------------------ - - -// Horn Rod FX 2 ------------------------------------------------------------ - -class AHornRodFX2 : public AActor -{ - DECLARE_CLASS (AHornRodFX2, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(AHornRodFX2, false, false, false, false) - -int AHornRodFX2::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass("Sorcerer2")) && pr_hrfx2() < 96) - { // D'Sparil teleports away - P_DSparilTeleport (target); - return -1; - } - return damage; -} - -// Rain pillar 1 ------------------------------------------------------------ - -class ARainPillar : public AActor -{ - DECLARE_CLASS (ARainPillar, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(ARainPillar, false, false, false, false) - -int ARainPillar::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->flags2 & MF2_BOSS) - { // Decrease damage for bosses - damage = (pr_rp() & 7) + 1; - } - return damage; -} - -// Rain tracker "inventory" item -------------------------------------------- - -class ARainTracker : public AInventory -{ - DECLARE_CLASS (ARainTracker, AInventory) -public: - - void Serialize(FSerializer &arc); - TObjPtr Rain1, Rain2; -}; - -IMPLEMENT_CLASS(ARainTracker, false, false, false, false) - -void ARainTracker::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("rain1", Rain1) - ("rain2", Rain2); -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireSkullRodPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *mo; - player_t *player; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - mo = P_SpawnPlayerMissile (self, PClass::FindActor("HornRodFX1")); - // Randomize the first frame - if (mo && pr_fsr1() > 128) - { - mo->SetState (mo->state->GetNextState()); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireSkullRodPL2 -// -// The special2 field holds the player number that shot the rain missile. -// The special1 field holds the id of the rain sound. -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player; - AActor *MissileActor; - FTranslatedLineTarget t; - - if (NULL == (player = self->player)) - { - return 0; - } - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - P_SpawnPlayerMissile (self, 0,0,0, RUNTIME_CLASS(AHornRodFX2), self->Angles.Yaw, &t, &MissileActor); - // Use MissileActor instead of the return value from - // P_SpawnPlayerMissile because we need to give info to the mobj - // even if it exploded immediately. - if (MissileActor != NULL) - { - MissileActor->special2 = (int)(player - players); - if (t.linetarget && !t.unlinked) - { - MissileActor->tracer = t.linetarget; - } - S_Sound (MissileActor, CHAN_WEAPON, "weapons/hornrodpowshoot", 1, ATTN_NORM); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_AddPlayerRain -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_AddPlayerRain) -{ - PARAM_SELF_PROLOGUE(AActor); - - ARainTracker *tracker; - - if (self->target == NULL || self->target->health <= 0) - { // Shooter is dead or nonexistant - return 0; - } - - tracker = self->target->FindInventory (); - - // They player is only allowed two rainstorms at a time. Shooting more - // than that will cause the oldest one to terminate. - if (tracker != NULL) - { - if (tracker->Rain1 && tracker->Rain2) - { // Terminate an active rain - if (tracker->Rain1->health < tracker->Rain2->health) - { - if (tracker->Rain1->health > 16) - { - tracker->Rain1->health = 16; - } - tracker->Rain1 = NULL; - } - else - { - if (tracker->Rain2->health > 16) - { - tracker->Rain2->health = 16; - } - tracker->Rain2 = NULL; - } - } - } - else - { - tracker = static_cast (self->target->GiveInventoryType (RUNTIME_CLASS(ARainTracker))); - } - // Add rain mobj to list - if (tracker->Rain1) - { - tracker->Rain2 = self; - } - else - { - tracker->Rain1 = self; - } - self->special1 = S_FindSound ("misc/rain"); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_SkullRodStorm -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_SkullRodStorm) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - ARainTracker *tracker; - - if (self->health-- == 0) - { - S_StopSound (self, CHAN_BODY); - if (self->target == NULL) - { // Player left the game - self->Destroy (); - return 0; - } - tracker = self->target->FindInventory (); - if (tracker != NULL) - { - if (tracker->Rain1 == self) - { - tracker->Rain1 = NULL; - } - else if (tracker->Rain2 == self) - { - tracker->Rain2 = NULL; - } - } - self->Destroy (); - return 0; - } - if (pr_storm() < 25) - { // Fudge rain frequency - return 0; - } - double xo = ((pr_storm() & 127) - 64); - double yo = ((pr_storm() & 127) - 64); - DVector3 pos = self->Vec2OffsetZ(xo, yo, ONCEILINGZ); - mo = Spawn (pos, ALLOW_REPLACE); - // We used bouncecount to store the 3D floor index in A_HideInCeiling - if (!mo) return 0; - if (mo->Sector->PortalGroup != self->Sector->PortalGroup) - { - // spawning this through a portal will never work right so abort right away. - mo->Destroy(); - return 0; - } - if (self->bouncecount >= 0 && (unsigned)self->bouncecount < self->Sector->e->XFloor.ffloors.Size()) - pos.Z = self->Sector->e->XFloor.ffloors[self->bouncecount]->bottom.plane->ZatPoint(mo); - else - pos.Z = self->Sector->ceilingplane.ZatPoint(mo); - int moceiling = P_Find3DFloor(NULL, pos, false, false, pos.Z); - if (moceiling >= 0) mo->SetZ(pos.Z - mo->Height); - mo->Translation = multiplayer ? TRANSLATION(TRANSLATION_RainPillar,self->special2) : 0; - mo->target = self->target; - mo->Vel.X = MinVel; // Force collision detection - mo->Vel.Z = -mo->Speed; - mo->special2 = self->special2; // Transfer player number - P_CheckMissileSpawn (mo, self->radius); - if (self->special1 != -1 && !S_IsActorPlayingSomething (self, CHAN_BODY, -1)) - { - S_Sound (self, CHAN_BODY|CHAN_LOOP, self->special1, 1, ATTN_NORM); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_RainImpact -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_RainImpact) -{ - PARAM_SELF_PROLOGUE(AActor); - if (self->Z() > self->floorz) - { - self->SetState (self->FindState("NotFloor")); - } - else if (pr_impact() < 40) - { - P_HitFloor (self); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_HideInCeiling -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_HideInCeiling) -{ - PARAM_SELF_PROLOGUE(AActor); - - // We use bouncecount to store the 3D floor index - double foo; - for (int i = self->Sector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) - { - F3DFloor * rover = self->Sector->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - - if ((foo = rover->bottom.plane->ZatPoint(self)) >= self->Top()) - { - self->SetZ(foo + 4, false); - self->bouncecount = i; - return 0; - } - } - self->bouncecount = -1; - self->SetZ(self->ceilingz + 4, false); - return 0; -} - -// --- Phoenix Rod ---------------------------------------------------------- - -class APhoenixRod : public AWeapon -{ - DECLARE_CLASS (APhoenixRod, AWeapon) -public: - - void Serialize(FSerializer &arc) - { - Super::Serialize (arc); - arc("flamecount", FlameCount); - } - int FlameCount; // for flamethrower duration -}; - -class APhoenixRodPowered : public APhoenixRod -{ - DECLARE_CLASS (APhoenixRodPowered, APhoenixRod) -public: - void EndPowerup (); -}; - -IMPLEMENT_CLASS(APhoenixRod, false, false, false, false) -IMPLEMENT_CLASS(APhoenixRodPowered, false, false, false, false) - -void APhoenixRodPowered::EndPowerup () -{ - DepleteAmmo (bAltFire); - Owner->player->refire = 0; - S_StopSound (Owner, CHAN_WEAPON); - Owner->player->ReadyWeapon = SisterWeapon; - P_SetPsprite(Owner->player, PSP_WEAPON, SisterWeapon->GetReadyState()); -} - -class APhoenixFX1 : public AActor -{ - DECLARE_CLASS (APhoenixFX1, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(APhoenixFX1, false, false, false, false) - -int APhoenixFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass("Sorcerer2")) && pr_hrfx2() < 96) - { // D'Sparil teleports away - P_DSparilTeleport (target); - return -1; - } - return damage; -} - -// Phoenix FX 2 ------------------------------------------------------------- - -class APhoenixFX2 : public AActor -{ - DECLARE_CLASS (APhoenixFX2, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(APhoenixFX2, false, false, false, false) - -int APhoenixFX2::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->player && pr_pfx2 () < 128) - { // Freeze player for a bit - target->reactiontime += 4; - } - return damage; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FirePhoenixPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FirePhoenixPL1) -{ - 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(APhoenixFX1)); - self->Thrust(self->Angles.Yaw + 180, 4); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_PhoenixPuff -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_PhoenixPuff) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *puff; - DAngle angle; - - //[RH] Heretic never sets the target for seeking - //P_SeekerMissile (self, 5, 10); - puff = Spawn("PhoenixPuff", self->Pos(), ALLOW_REPLACE); - angle = self->Angles.Yaw + 90; - puff->Vel = DVector3(angle.ToVector(1.3), 0); - - puff = Spawn("PhoenixPuff", self->Pos(), ALLOW_REPLACE); - angle = self->Angles.Yaw - 90; - puff->Vel = DVector3(angle.ToVector(1.3), 0); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_InitPhoenixPL2 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_InitPhoenixPL2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - if (self->player != NULL) - { - APhoenixRod *flamethrower = static_cast (self->player->ReadyWeapon); - if (flamethrower != NULL) - { - flamethrower->FlameCount = FLAME_THROWER_TICS; - } - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FirePhoenixPL2 -// -// Flame thrower effect. -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FirePhoenixPL2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *mo; - - double slope; - FSoundID soundid; - player_t *player; - APhoenixRod *flamethrower; - - if (nullptr == (player = self->player)) - { - return 0; - } - - soundid = "weapons/phoenixpowshoot"; - - flamethrower = static_cast (player->ReadyWeapon); - if (flamethrower == nullptr || --flamethrower->FlameCount == 0) - { // Out of flame - P_SetPsprite(player, PSP_WEAPON, flamethrower->FindState("Powerdown")); - player->refire = 0; - S_StopSound (self, CHAN_WEAPON); - return 0; - } - - slope = -self->Angles.Pitch.TanClamped(); - double xo = pr_fp2.Random2() / 128.; - double yo = pr_fp2.Random2() / 128.; - DVector3 pos = self->Vec3Offset(xo, yo, 26 + slope - self->Floorclip); - - slope += 0.1; - mo = Spawn("PhoenixFX2", pos, ALLOW_REPLACE); - mo->target = self; - mo->Angles.Yaw = self->Angles.Yaw; - mo->VelFromAngle(); - mo->Vel += self->Vel.XY(); - mo->Vel.Z = mo->Speed * slope; - if (!player->refire || !S_IsActorPlayingSomething (self, CHAN_WEAPON, -1)) - { - S_Sound (self, CHAN_WEAPON|CHAN_LOOP, soundid, 1, ATTN_NORM); - } - P_CheckMissileSpawn (mo, self->radius); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_ShutdownPhoenixPL2 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_ShutdownPhoenixPL2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player; - - if (NULL == (player = self->player)) - { - return 0; - } - S_StopSound (self, CHAN_WEAPON); - AWeapon *weapon = player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FlameEnd -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FlameEnd) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->Vel.Z += 1.5; - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FloatPuff -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FloatPuff) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->Vel.Z += 1.8; - return 0; -} - diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp deleted file mode 100644 index 8068c251b..000000000 --- a/src/g_heretic/a_ironlich.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_blastradius.cpp b/src/g_hexen/a_blastradius.cpp deleted file mode 100644 index f8bc60a7f..000000000 --- a/src/g_hexen/a_blastradius.cpp +++ /dev/null @@ -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 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; -} diff --git a/src/g_hexen/a_boostarmor.cpp b/src/g_hexen/a_boostarmor.cpp deleted file mode 100644 index 1323af8a5..000000000 --- a/src/g_hexen/a_boostarmor.cpp +++ /dev/null @@ -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(); - 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(); - armor->flags |= MF_DROPPED; - armor->SaveAmount = 50; - armor->MaxSaveAmount = 300; - if (!armor->CallTryPickup (Owner)) - { - armor->Destroy (); - return false; - } - else - { - return true; - } - } -} - diff --git a/src/g_hexen/a_clericflame.cpp b/src/g_hexen/a_clericflame.cpp deleted file mode 100644 index 2c66cb42d..000000000 --- a/src/g_hexen/a_clericflame.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp deleted file mode 100644 index 362434b02..000000000 --- a/src/g_hexen/a_clericholy.cpp +++ /dev/null @@ -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 (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 (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 (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; -} - diff --git a/src/g_hexen/a_clericmace.cpp b/src/g_hexen/a_clericmace.cpp deleted file mode 100644 index a15196cf8..000000000 --- a/src/g_hexen/a_clericmace.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_clericstaff.cpp b/src/g_hexen/a_clericstaff.cpp deleted file mode 100644 index a838357d6..000000000 --- a/src/g_hexen/a_clericstaff.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_dragon.cpp b/src/g_hexen/a_dragon.cpp deleted file mode 100644 index b79d92b36..000000000 --- a/src/g_hexen/a_dragon.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_fighteraxe.cpp b/src/g_hexen/a_fighteraxe.cpp deleted file mode 100644 index 1dc02d599..000000000 --- a/src/g_hexen/a_fighteraxe.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_fighterhammer.cpp b/src/g_hexen/a_fighterhammer.cpp deleted file mode 100644 index 84eca78b7..000000000 --- a/src/g_hexen/a_fighterhammer.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_fighterplayer.cpp b/src/g_hexen/a_fighterplayer.cpp deleted file mode 100644 index 7e1502a1c..000000000 --- a/src/g_hexen/a_fighterplayer.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_fighterquietus.cpp b/src/g_hexen/a_fighterquietus.cpp deleted file mode 100644 index 8f73ef48e..000000000 --- a/src/g_hexen/a_fighterquietus.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_flechette.cpp b/src/g_hexen/a_flechette.cpp deleted file mode 100644 index 335842887..000000000 --- a/src/g_hexen/a_flechette.cpp +++ /dev/null @@ -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(-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(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(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(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(); - 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 (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; -} diff --git a/src/g_hexen/a_flies.cpp b/src/g_hexen/a_flies.cpp deleted file mode 100644 index 7281dd8f2..000000000 --- a/src/g_hexen/a_flies.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_fog.cpp b/src/g_hexen/a_fog.cpp deleted file mode 100644 index 6b2bc9706..000000000 --- a/src/g_hexen/a_fog.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_healingradius.cpp b/src/g_hexen/a_healingradius.cpp deleted file mode 100644 index 25efbc3ca..000000000 --- a/src/g_hexen/a_healingradius.cpp +++ /dev/null @@ -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(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 (); - 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(PClass::FindClass(NAME_Mana1)), amount) || - players[i].mo->GiveAmmo (dyn_cast(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; - -} diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp deleted file mode 100644 index 32ad9ab69..000000000 --- a/src/g_hexen/a_heresiarch.cpp +++ /dev/null @@ -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(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 (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(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; -} diff --git a/src/g_hexen/a_hexenglobal.h b/src/g_hexen/a_hexenglobal.h deleted file mode 100644 index 3455b684d..000000000 --- a/src/g_hexen/a_hexenglobal.h +++ /dev/null @@ -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__ diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp deleted file mode 100644 index 1a635ff71..000000000 --- a/src/g_hexen/a_hexenmisc.cpp +++ /dev/null @@ -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" diff --git a/src/g_hexen/a_hexenspecialdecs.cpp b/src/g_hexen/a_hexenspecialdecs.cpp deleted file mode 100644 index 6d441ba57..000000000 --- a/src/g_hexen/a_hexenspecialdecs.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_iceguy.cpp b/src/g_hexen/a_iceguy.cpp deleted file mode 100644 index 1163a117a..000000000 --- a/src/g_hexen/a_iceguy.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_korax.cpp b/src/g_hexen/a_korax.cpp deleted file mode 100644 index 2824d8c0a..000000000 --- a/src/g_hexen/a_korax.cpp +++ /dev/null @@ -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); -} diff --git a/src/g_hexen/a_magecone.cpp b/src/g_hexen/a_magecone.cpp deleted file mode 100644 index da53a829c..000000000 --- a/src/g_hexen/a_magecone.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_magelightning.cpp b/src/g_hexen/a_magelightning.cpp deleted file mode 100644 index b06d22ce5..000000000 --- a/src/g_hexen/a_magelightning.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_magestaff.cpp b/src/g_hexen/a_magestaff.cpp deleted file mode 100644 index 0b78bd279..000000000 --- a/src/g_hexen/a_magestaff.cpp +++ /dev/null @@ -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 (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 (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; -} diff --git a/src/g_hexen/a_pig.cpp b/src/g_hexen/a_pig.cpp deleted file mode 100644 index 41054da00..000000000 --- a/src/g_hexen/a_pig.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_hexen/a_serpent.cpp b/src/g_hexen/a_serpent.cpp deleted file mode 100644 index 6f435747e..000000000 --- a/src/g_hexen/a_serpent.cpp +++ /dev/null @@ -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; -} - diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp deleted file mode 100644 index 9b2cb5d6e..000000000 --- a/src/g_hexen/a_spike.cpp +++ /dev/null @@ -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 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(self)->DirtClump = - Spawn("DirtClump", self->Pos(), ALLOW_REPLACE); - return 0; -} - - -DEFINE_ACTION_FUNCTION(AActor, A_ThrustRaise) -{ - PARAM_SELF_PROLOGUE(AActor); - - AThrustFloor *actor = static_cast(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; -} - diff --git a/src/g_hexen/a_summon.cpp b/src/g_hexen/a_summon.cpp deleted file mode 100644 index 7c0746c91..000000000 --- a/src/g_hexen/a_summon.cpp +++ /dev/null @@ -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(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(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(); - 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; -} diff --git a/src/g_hexen/a_teleportother.cpp b/src/g_hexen/a_teleportother.cpp deleted file mode 100644 index 4eeba53f9..000000000 --- a/src/g_hexen/a_teleportother.cpp +++ /dev/null @@ -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); - } -} diff --git a/src/g_hexen/a_wraith.cpp b/src/g_hexen/a_wraith.cpp deleted file mode 100644 index fcd0d75f3..000000000 --- a/src/g_hexen/a_wraith.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_inventory/a_ammo.cpp b/src/g_inventory/a_ammo.cpp new file mode 100644 index 000000000..23a922187 --- /dev/null +++ b/src/g_inventory/a_ammo.cpp @@ -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(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(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(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(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(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(type); + AAmmo *ammo = static_cast(other->FindInventory(atype)); + int amount = static_cast(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(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(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(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(item)->BackpackMaxAmount) + { + item->MaxAmount = static_cast(item->GetDefault())->MaxAmount; + if (item->Amount > item->MaxAmount) + { + item->Amount = item->MaxAmount; + } + } + } +} + diff --git a/src/g_inventory/a_ammo.h b/src/g_inventory/a_ammo.h new file mode 100644 index 000000000..643d4b924 --- /dev/null +++ b/src/g_inventory/a_ammo.h @@ -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; +}; + + diff --git a/src/g_shared/a_armor.cpp b/src/g_inventory/a_armor.cpp similarity index 79% rename from src/g_shared/a_armor.cpp rename to src/g_inventory/a_armor.cpp index 636441a9d..ebe8f49f6 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_inventory/a_armor.cpp @@ -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 #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() { diff --git a/src/g_inventory/a_armor.h b/src/g_inventory/a_armor.h new file mode 100644 index 000000000..63febda91 --- /dev/null +++ b/src/g_inventory/a_armor.h @@ -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); +}; + diff --git a/src/g_shared/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp similarity index 92% rename from src/g_shared/a_artifacts.cpp rename to src/g_inventory/a_artifacts.cpp index 7cf05613a..a75b9ecd4 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -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 (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 (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(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) //=========================================================================== // diff --git a/src/g_shared/a_artifacts.h b/src/g_inventory/a_artifacts.h similarity index 55% rename from src/g_shared/a_artifacts.h rename to src/g_inventory/a_artifacts.h index 85ebc2e81..7e9956d73 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -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__ diff --git a/src/g_inventory/a_health.cpp b/src/g_inventory/a_health.cpp new file mode 100644 index 000000000..58cd86e2a --- /dev/null +++ b/src/g_inventory/a_health.cpp @@ -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(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(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); +} + diff --git a/src/g_inventory/a_health.h b/src/g_inventory/a_health.h new file mode 100644 index 000000000..af282031c --- /dev/null +++ b/src/g_inventory/a_health.h @@ -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; +}; + diff --git a/src/g_shared/a_keys.cpp b/src/g_inventory/a_keys.cpp similarity index 79% rename from src/g_shared/a_keys.cpp rename to src/g_inventory/a_keys.cpp index 69fbe48af..a2b5b308a 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_inventory/a_keys.cpp @@ -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 anykeylist; @@ -59,6 +109,11 @@ struct Keygroup } }; +//=========================================================================== +// +// +//=========================================================================== + struct Lock { TArray 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; diff --git a/src/g_inventory/a_keys.h b/src/g_inventory/a_keys.h new file mode 100644 index 000000000..eda0473a3 --- /dev/null +++ b/src/g_inventory/a_keys.h @@ -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 diff --git a/src/g_shared/a_pickups.cpp b/src/g_inventory/a_pickups.cpp similarity index 61% rename from src/g_shared/a_pickups.cpp rename to src/g_inventory/a_pickups.cpp index 223a2f75f..91b88a053 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -11,20 +11,20 @@ #include "c_dispatch.h" #include "gstrings.h" #include "templates.h" -#include "a_strifeglobal.h" #include "a_morph.h" #include "a_specialspot.h" -#include "vm.h" #include "g_level.h" #include "g_game.h" #include "doomstat.h" #include "d_player.h" #include "p_spec.h" #include "serializer.h" +#include "virtual.h" +#include "a_ammo.h" -static FRandom pr_restore ("RestorePos"); +EXTERN_CVAR(Bool, sv_unlimited_pickup) -IMPLEMENT_CLASS(PClassInventory, false, false, false, false) +IMPLEMENT_CLASS(PClassInventory, false, false) PClassInventory::PClassInventory() { @@ -71,280 +71,6 @@ void PClassInventory::Finalize(FStateDefinitions &statedef) ((AActor*)Defaults)->flags |= MF_SPECIAL; } -IMPLEMENT_CLASS(PClassAmmo, false, false, false, false) - -PClassAmmo::PClassAmmo() -{ - DropAmount = 0; -} - -void PClassAmmo::DeriveData(PClass *newclass) -{ - assert(newclass->IsKindOf(RUNTIME_CLASS(PClassAmmo))); - Super::DeriveData(newclass); - PClassAmmo *newc = static_cast(newclass); - - newc->DropAmount = DropAmount; -} - -IMPLEMENT_CLASS(AAmmo, false, false, false, false) - -//=========================================================================== -// -// 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(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(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(Owner)->CheckWeaponSwitch(GetClass()); - } - } - return true; - } - if (Inventory != NULL) - { - return Inventory->HandlePickup (item); - } - 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(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; -} - -//--------------------------------------------------------------------------- -// -// 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(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; -} - //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing1 @@ -353,12 +79,12 @@ bool P_GiveBody (AActor *actor, int num, int max) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing1) +DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialThing1) { - PARAM_SELF_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AInventory); self->renderflags &= ~RF_INVISIBLE; - if (static_cast(self)->DoRespawn ()) + if (self->DoRespawn ()) { S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE); } @@ -371,9 +97,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing1) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing2) +DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialThing2) { - PARAM_SELF_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AInventory); self->flags |= MF_SPECIAL; if (!(self->GetDefault()->flags & MF_NOGRAVITY)) @@ -391,9 +117,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialThing2) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing) +DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialDoomThing) { - PARAM_SELF_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AInventory); self->renderflags &= ~RF_INVISIBLE; self->flags |= MF_SPECIAL; @@ -401,7 +127,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing) { self->flags &= ~MF_NOGRAVITY; } - if (static_cast(self)->DoRespawn ()) + if (self->DoRespawn ()) { self->SetState (self->SpawnState); S_Sound (self, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE); @@ -410,72 +136,29 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialDoomThing) return 0; } -//--------------------------------------------------------------------------- -// -// PROP A_RestoreSpecialPosition -// -//--------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) -{ - PARAM_SELF_PROLOGUE(AActor); - - // Move item back to its original location - DVector2 sp = self->SpawnPoint; - - self->UnlinkFromWorld(); - self->SetXY(sp); - self->LinkToWorld(true); - self->SetZ(self->Sector->floorplane.ZatPoint(sp)); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS); // no portal checks here so that things get spawned in this sector. - - if (self->flags & MF_SPAWNCEILING) - { - self->SetZ(self->ceilingz - self->Height - self->SpawnPoint.Z); - } - else if (self->flags2 & MF2_SPAWNFLOAT) - { - double space = self->ceilingz - self->Height - self->floorz; - if (space > 48) - { - space -= 40; - self->SetZ((space * pr_restore()) / 256. + self->floorz + 40); - } - else - { - self->SetZ(self->floorz); - } - } - else - { - self->SetZ(self->SpawnPoint.Z + self->floorz); - } - // Redo floor/ceiling check, in case of 3D floors and portals - P_FindFloorCeiling(self, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT); - if (self->Z() < self->floorz) - { // Do not reappear under the floor, even if that's where we were for the - // initial spawn. - self->SetZ(self->floorz); - } - if ((self->flags & MF_SOLID) && (self->Top() > self->ceilingz)) - { // Do the same for the ceiling. - self->SetZ(self->ceilingz - self->Height); - } - // Do not interpolate from the position the actor was at when it was - // picked up, in case that is different from where it is now. - self->ClearInterpolation(); - return 0; -} - int AInventory::StaticLastMessageTic; -const char *AInventory::StaticLastMessage; +FString AInventory::StaticLastMessage; -IMPLEMENT_CLASS(AInventory, false, true, false, false) +IMPLEMENT_CLASS(AInventory, false, true) IMPLEMENT_POINTERS_START(AInventory) IMPLEMENT_POINTER(Owner) IMPLEMENT_POINTERS_END +DEFINE_FIELD_BIT(AInventory, ItemFlags, bPickupGood, IF_PICKUPGOOD) +DEFINE_FIELD_BIT(AInventory, ItemFlags, bCreateCopyMoved, IF_CREATECOPYMOVED) +DEFINE_FIELD_BIT(AInventory, ItemFlags, bInitEffectFailed, IF_INITEFFECTFAILED) +DEFINE_FIELD(AInventory, Owner) +DEFINE_FIELD(AInventory, Amount) +DEFINE_FIELD(AInventory, MaxAmount) +DEFINE_FIELD(AInventory, InterHubAmount) +DEFINE_FIELD(AInventory, RespawnTics) +DEFINE_FIELD(AInventory, Icon) +DEFINE_FIELD(AInventory, DropTime) +DEFINE_FIELD(AInventory, SpawnPointClass) +DEFINE_FIELD(AInventory, PickupFlash) +DEFINE_FIELD(AInventory, PickupSound) + //=========================================================================== // // AInventory :: Tick @@ -568,6 +251,27 @@ bool AInventory::SpecialDropAction (AActor *dropper) return false; } +DEFINE_ACTION_FUNCTION(AInventory, SpecialDropAction) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(dropper, AActor); + ACTION_RETURN_BOOL(self->SpecialDropAction(dropper)); +} + +bool AInventory::CallSpecialDropAction(AActor *dropper) +{ + IFVIRTUAL(AInventory, SpecialDropAction) + { + VMValue params[2] = { (DObject*)this, (DObject*)dropper }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + } + return SpecialDropAction(dropper); +} + //=========================================================================== // // AInventory :: ShouldRespawn @@ -636,6 +340,25 @@ void AInventory::DoEffect () { } +DEFINE_ACTION_FUNCTION(AInventory, DoEffect) +{ + PARAM_SELF_PROLOGUE(AInventory); + self->DoEffect(); + return 0; +} + +void AInventory::CallDoEffect() +{ + IFVIRTUAL(AInventory, DoEffect) + { + VMValue params[1] = { (DObject*)this }; + VMFrameStack stack; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } + else DoEffect(); +} + + //=========================================================================== // // AInventory :: Travelled @@ -674,7 +397,7 @@ bool AInventory::HandlePickup (AInventory *item) { if (item->GetClass() == GetClass()) { - if (Amount < MaxAmount || (sv_unlimited_pickup && !item->ShouldStay())) + if (Amount < MaxAmount || (sv_unlimited_pickup && !item->CallShouldStay())) { if (Amount > 0 && Amount + item->Amount < 0) { @@ -693,13 +416,38 @@ bool AInventory::HandlePickup (AInventory *item) } return true; } - if (Inventory != NULL) + return false; +} + +DEFINE_ACTION_FUNCTION(AInventory, HandlePickup) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(item, AInventory); + ACTION_RETURN_BOOL(self->HandlePickup(item)); +} + +bool AInventory::CallHandlePickup(AInventory *item) +{ + auto self = this; + while (self != nullptr) { - return Inventory->HandlePickup (item); + IFVIRTUALPTR(self, AInventory, HandlePickup) + { + // Without the type cast this picks the 'void *' assignment... + VMValue params[2] = { (DObject*)self, (DObject*)item }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + if (retval) return true; + } + else if (self->HandlePickup(item)) return true; + self = self->Inventory; } return false; } + //=========================================================================== // // AInventory :: GoAway @@ -717,7 +465,7 @@ bool AInventory::GoAway () return false; } - if (!ShouldStay ()) + if (!CallShouldStay ()) { Hide (); if (ShouldRespawn ()) @@ -747,6 +495,14 @@ void AInventory::GoAwayAndDie () } } +DEFINE_ACTION_FUNCTION(AInventory, GoAwayAndDie) +{ + PARAM_SELF_PROLOGUE(AInventory); + self->GoAwayAndDie(); + return 0; +} + + //=========================================================================== // // AInventory :: CreateCopy @@ -775,6 +531,28 @@ AInventory *AInventory::CreateCopy (AActor *other) return copy; } +DEFINE_ACTION_FUNCTION(AInventory, CreateCopy) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(other, AActor); + ACTION_RETURN_OBJECT(self->CreateCopy(other)); +} + +AInventory *AInventory::CallCreateCopy(AActor *other) +{ + IFVIRTUAL(AInventory, CreateCopy) + { + VMValue params[2] = { (DObject*)this, (DObject*)other }; + VMReturn ret; + AInventory *retval; + ret.PointerAt((void**)&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return retval; + } + else return CreateCopy(other); +} + + //=========================================================================== // // AInventory::CreateTossable @@ -819,6 +597,26 @@ AInventory *AInventory::CreateTossable () return copy; } +DEFINE_ACTION_FUNCTION(AInventory, CreateTossable) +{ + PARAM_SELF_PROLOGUE(AInventory); + ACTION_RETURN_OBJECT(self->CreateTossable()); +} + +AInventory *AInventory::CallCreateTossable() +{ + IFVIRTUAL(AInventory, CreateTossable) + { + VMValue params[1] = { (DObject*)this }; + VMReturn ret; + AInventory *retval; + ret.PointerAt((void**)&retval); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + return retval; + } + else return CreateTossable(); +} + //=========================================================================== // // AInventory :: BecomeItem @@ -846,6 +644,13 @@ void AInventory::BecomeItem () SetState (FindState("Held")); } +DEFINE_ACTION_FUNCTION(AInventory, BecomeItem) +{ + PARAM_SELF_PROLOGUE(AInventory); + self->BecomeItem(); + return 0; +} + //=========================================================================== // // AInventory :: BecomePickup @@ -873,6 +678,13 @@ void AInventory::BecomePickup () SetState (SpawnState); } +DEFINE_ACTION_FUNCTION(AInventory, BecomePickup) +{ + PARAM_SELF_PROLOGUE(AInventory); + self->BecomePickup(); + return 0; +} + //=========================================================================== // // AInventory :: AbsorbDamage @@ -921,16 +733,24 @@ void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, boo // //=========================================================================== -double AInventory::GetSpeedFactor () +double AInventory::GetSpeedFactor() { - if (Inventory != NULL) + double factor = 1.; + auto self = this; + while (self != nullptr) { - return Inventory->GetSpeedFactor(); - } - else - { - return 1.; + IFVIRTUALPTR(self, AInventory, GetSpeedFactor) + { + VMValue params[2] = { (DObject*)self }; + VMReturn ret; + double retval; + ret.FloatAt(&retval); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + factor *= retval; + } + self = self->Inventory; } + return factor; } //=========================================================================== @@ -941,15 +761,21 @@ double AInventory::GetSpeedFactor () bool AInventory::GetNoTeleportFreeze () { - // do not check the flag here because it's only active when used on PowerUps, not on PowerupGivers. - if (Inventory != NULL) + auto self = this; + while (self != nullptr) { - return Inventory->GetNoTeleportFreeze(); - } - else - { - return false; + IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze) + { + VMValue params[2] = { (DObject*)self }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + if (retval) return true; + } + self = self->Inventory; } + return false; } //=========================================================================== @@ -981,6 +807,29 @@ bool AInventory::Use (bool pickup) return false; } +DEFINE_ACTION_FUNCTION(AInventory, Use) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_BOOL(pickup); + ACTION_RETURN_BOOL(self->Use(pickup)); +} + +bool AInventory::CallUse(bool pickup) +{ + IFVIRTUAL(AInventory, Use) + { + VMValue params[2] = { (DObject*)this, pickup }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + + } + else return Use(pickup); +} + + //=========================================================================== // // AInventory :: Hide @@ -1074,17 +923,17 @@ void AInventory::Touch (AActor *toucher) if (!CallTryPickup (toucher, &toucher)) return; // This is the only situation when a pickup flash should ever play. - if (PickupFlash != NULL && !ShouldStay()) + if (PickupFlash != NULL && !CallShouldStay()) { Spawn(PickupFlash, Pos(), ALLOW_REPLACE); } if (!(ItemFlags & IF_QUIET)) { - const char * message = PickupMessage (); + FString message = GetPickupMessage (); - if (message != NULL && *message != 0 && localview - && (StaticLastMessageTic != gametic || StaticLastMessage != message)) + if (message.IsNotEmpty() && localview + && (StaticLastMessageTic != gametic || StaticLastMessage.Compare(message))) { StaticLastMessageTic = gametic; StaticLastMessage = message; @@ -1159,11 +1008,31 @@ void AInventory::DoPickupSpecial (AActor *toucher) // //=========================================================================== -const char *AInventory::PickupMessage () +FString AInventory::PickupMessage () { return GetClass()->PickupMessage; } +DEFINE_ACTION_FUNCTION(AInventory, PickupMessage) +{ + PARAM_SELF_PROLOGUE(AInventory); + ACTION_RETURN_STRING(self->PickupMessage()); +} + +FString AInventory::GetPickupMessage() +{ + IFVIRTUAL(AInventory, PickupMessage) + { + VMValue params[1] = { (DObject*)this }; + VMReturn ret; + FString retval; + ret.StringAt(&retval); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + return retval; + } + else return PickupMessage(); +} + //=========================================================================== // // AInventory :: PlayPickupSound @@ -1202,6 +1071,25 @@ void AInventory::PlayPickupSound (AActor *toucher) S_Sound (toucher, chan, PickupSound, 1, atten); } +DEFINE_ACTION_FUNCTION(AInventory, PlayPickupSound) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(other, AActor); + self->PlayPickupSound(other); + return 0; +} + +void AInventory::CallPlayPickupSound(AActor *other) +{ + IFVIRTUAL(AInventory, PlayPickupSound) + { + VMValue params[2] = { (DObject*)this, (DObject*)other }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } + else PlayPickupSound(other); +} + + //=========================================================================== // // AInventory :: ShouldStay @@ -1215,6 +1103,27 @@ bool AInventory::ShouldStay () return false; } +DEFINE_ACTION_FUNCTION(AInventory, ShouldStay) +{ + PARAM_SELF_PROLOGUE(AInventory); + ACTION_RETURN_BOOL(self->ShouldStay()); +} + +bool AInventory::CallShouldStay() +{ + IFVIRTUAL(AInventory, ShouldStay) + { + VMValue params[1] = { (DObject*)this }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + return !!retval; + } + else return ShouldStay(); +} + + //=========================================================================== // // AInventory :: Destroy @@ -1275,6 +1184,26 @@ PalEntry AInventory::GetBlend () return 0; } +DEFINE_ACTION_FUNCTION(AInventory, GetBlend) +{ + PARAM_SELF_PROLOGUE(AInventory); + ACTION_RETURN_INT(self->GetBlend()); +} + +PalEntry AInventory::CallGetBlend() +{ + IFVIRTUAL(AInventory, GetBlend) + { + VMValue params[1] = { (DObject*)this }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + return retval; + } + else return GetBlend(); +} + //=========================================================================== // // AInventory :: PrevItem @@ -1350,12 +1279,6 @@ bool AInventory::DrawPowerup (int x, int y) return false; } -/***************************************************************************/ -/* AArtifact implementation */ -/***************************************************************************/ - -IMPLEMENT_CLASS(APowerupGiver, false, false, false, false) - //=========================================================================== // // AInventory :: DoRespawn @@ -1410,7 +1333,7 @@ bool AInventory::TryPickup (AActor *&toucher) // picked up, then it leaves the flag cleared. ItemFlags &= ~IF_PICKUPGOOD; - if (toucher->Inventory != NULL && toucher->Inventory->HandlePickup (this)) + if (toucher->Inventory != NULL && toucher->Inventory->CallHandlePickup (this)) { // Let something else the player is holding intercept the pickup. if (!(ItemFlags & IF_PICKUPGOOD)) @@ -1446,7 +1369,7 @@ bool AInventory::TryPickup (AActor *&toucher) { // Add the item to the inventory. It is not already there, or HandlePickup // would have already taken care of it. - AInventory *copy = CreateCopy (toucher); + AInventory *copy = CallCreateCopy (toucher); if (copy == NULL) { return false; @@ -1470,7 +1393,7 @@ bool AInventory::TryPickup (AActor *&toucher) copy->AttachToOwner (newtoucher); if (ItemFlags & IF_AUTOACTIVATE) { - if (copy->Use (true)) + if (copy->CallUse (true)) { if (--copy->Amount <= 0) { @@ -1483,6 +1406,13 @@ bool AInventory::TryPickup (AActor *&toucher) return true; } +DEFINE_ACTION_FUNCTION(AInventory, TryPickup) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_POINTER(toucher, AActor*); + ACTION_RETURN_BOOL(self->TryPickup(*toucher)); +} + //=========================================================================== // // AInventory :: TryPickupRestricted @@ -1494,6 +1424,14 @@ bool AInventory::TryPickupRestricted (AActor *&toucher) return false; } +DEFINE_ACTION_FUNCTION(AInventory, TryPickupRestricted) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_POINTER(toucher, AActor*); + ACTION_RETURN_BOOL(self->TryPickupRestricted(*toucher)); +} + + //=========================================================================== // // AInventory :: CallTryPickup @@ -1509,16 +1447,39 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return) bool res; if (CanPickup(toucher)) - res = TryPickup(toucher); + { + IFVIRTUAL(AInventory, TryPickup) + { + VMValue params[2] = { (DObject*)this, (void*)&toucher }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + res = !!retval; + } + else res = TryPickup(toucher); + } else if (!(ItemFlags & IF_RESTRICTABSOLUTELY)) - res = TryPickupRestricted(toucher); // let an item decide for itself how it will handle this + { + // let an item decide for itself how it will handle this + IFVIRTUAL(AInventory, TryPickupRestricted) + { + VMValue params[2] = { (DObject*)this, (void*)&toucher }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + res = !!retval; + } + else res = TryPickupRestricted(toucher); + } else return false; // Morph items can change the toucher so we need an option to return this info. if (toucher_return != NULL) *toucher_return = toucher; - if (!res && (ItemFlags & IF_ALWAYSPICKUP) && !ShouldStay()) + if (!res && (ItemFlags & IF_ALWAYSPICKUP) && !CallShouldStay()) { res = true; GoAwayAndDie(); @@ -1548,6 +1509,17 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return) return res; } +DEFINE_ACTION_FUNCTION(AInventory, CallTryPickup) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(toucher, AActor); + AActor *t_ret; + bool res = self->CallTryPickup(toucher, &t_ret); + if (numret > 0) ret[0].SetInt(res); + if (numret > 1) ret[1].SetPointer(t_ret, ATAG_OBJECT), numret = 2; + return numret; +} + //=========================================================================== // @@ -1631,6 +1603,25 @@ void AInventory::AttachToOwner (AActor *other) other->AddInventory (this); } +DEFINE_ACTION_FUNCTION(AInventory, AttachToOwner) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(other, AActor); + self->AttachToOwner(other); + return 0; +} + +void AInventory::CallAttachToOwner(AActor *other) +{ + IFVIRTUAL(AInventory, AttachToOwner) + { + VMValue params[2] = { (DObject*)this, (DObject*)other }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } + else AttachToOwner(other); +} + + //=========================================================================== // // AInventory :: DetachFromOwner @@ -1644,8 +1635,30 @@ void AInventory::DetachFromOwner () { } -IMPLEMENT_CLASS(AStateProvider, false, false, false, false) -IMPLEMENT_CLASS(ACustomInventory, false, false, false, false) +DEFINE_ACTION_FUNCTION(AInventory, DetachFromOwner) +{ + PARAM_SELF_PROLOGUE(AInventory); + self->DetachFromOwner(); + return 0; +} + +void AInventory::CallDetachFromOwner() +{ + IFVIRTUAL(AInventory, DetachFromOwner) + { + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } + else DetachFromOwner(); +} + +//=========================================================================== +//=========================================================================== + + + +IMPLEMENT_CLASS(AStateProvider, false, false) +IMPLEMENT_CLASS(ACustomInventory, false, false) //=========================================================================== // @@ -1689,372 +1702,3 @@ bool ACustomInventory::TryPickup (AActor *&toucher) } return useok; } - -IMPLEMENT_CLASS(PClassHealth, false, false, false, false) - -//=========================================================================== -// -// 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(newclass); - - newc->LowHealth = LowHealth; - newc->LowHealthMessage = LowHealthMessage; -} - -IMPLEMENT_CLASS(AHealth, false, false, false, false) - -//=========================================================================== -// -// AHealth :: PickupMessage -// -//=========================================================================== -const char *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, false, false) - -//=========================================================================== -// -// 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); - } - if (Inventory != NULL) - { - return Inventory->HandlePickup (item); - } - return false; -} - -//=========================================================================== -// -// AHealthPickup :: Use -// -//=========================================================================== - -bool AHealthPickup::Use (bool pickup) -{ - return P_GiveBody (Owner, health); -} - -//=========================================================================== -// -// AHealthPickup :: Serialize -// -//=========================================================================== - -void AHealthPickup::Serialize(FSerializer &arc) -{ - Super::Serialize(arc); - auto def = (AHealthPickup*)GetDefault(); - arc("autousemode", autousemode, def->autousemode); -} - -// Backpack ----------------------------------------------------------------- - -//=========================================================================== -// -// 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(type); - AAmmo *ammo = static_cast(other->FindInventory(atype)); - int amount = static_cast(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(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(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; - } - else if (Inventory != NULL) - { - return Inventory->HandlePickup (item); - } - else - { - 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(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(item)->BackpackMaxAmount) - { - item->MaxAmount = static_cast(item->GetDefault())->MaxAmount; - if (item->Amount > item->MaxAmount) - { - item->Amount = item->MaxAmount; - } - } - } -} - -//=========================================================================== -// -// ABackpack -// -//=========================================================================== - -IMPLEMENT_CLASS(ABackpackItem, false, false, false, false) -IMPLEMENT_CLASS(AMapRevealer, false, false, false, false) - -//=========================================================================== -// -// AMapRevealer :: TryPickup -// -// The MapRevealer doesn't actually go in your inventory. Instead, it sets -// a flag on the level. -// -//=========================================================================== - -bool AMapRevealer::TryPickup (AActor *&toucher) -{ - level.flags2 |= LEVEL2_ALLMAP; - GoAwayAndDie (); - return true; -} - - -//=========================================================================== -// -// AScoreItem -// -//=========================================================================== - -IMPLEMENT_CLASS(AScoreItem, false, false, false, false) - -//=========================================================================== -// -// AScoreItem :: TryPickup -// -// Adds the value (Amount) of the item to the toucher's Score property. -// -//=========================================================================== - -bool AScoreItem::TryPickup (AActor *&toucher) -{ - toucher->Score += Amount; - GoAwayAndDie(); - return true; -} - diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h new file mode 100644 index 000000000..99dad8713 --- /dev/null +++ b/src/g_inventory/a_pickups.h @@ -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 RestrictedToPlayerClass; + TArray 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 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__ diff --git a/src/g_inventory/a_puzzleitems.cpp b/src/g_inventory/a_puzzleitems.cpp new file mode 100644 index 000000000..7617f6383 --- /dev/null +++ b/src/g_inventory/a_puzzleitems.cpp @@ -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(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; +} + diff --git a/src/g_shared/a_weaponpiece.cpp b/src/g_inventory/a_weaponpiece.cpp similarity index 62% rename from src/g_shared/a_weaponpiece.cpp rename to src/g_inventory/a_weaponpiece.cpp index 7aa9591e9..e1a7fe33b 100644 --- a/src/g_shared/a_weaponpiece.cpp +++ b/src/g_inventory/a_weaponpiece.cpp @@ -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) { diff --git a/src/g_shared/a_weaponpiece.h b/src/g_inventory/a_weaponpiece.h similarity index 64% rename from src/g_shared/a_weaponpiece.h rename to src/g_inventory/a_weaponpiece.h index 88f3da02c..e0db15007 100644 --- a/src/g_shared/a_weaponpiece.h +++ b/src/g_inventory/a_weaponpiece.h @@ -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; }; diff --git a/src/g_shared/a_weapons.cpp b/src/g_inventory/a_weapons.cpp similarity index 88% rename from src/g_shared/a_weapons.cpp rename to src/g_inventory/a_weapons.cpp index 9063c6c2c..19a30dcd8 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -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 #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 KeyConfWeapons; FWeaponSlots *PlayingKeyConf; @@ -38,7 +117,13 @@ TMap 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 (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) diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h new file mode 100644 index 000000000..c6424075d --- /dev/null +++ b/src/g_inventory/a_weapons.h @@ -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 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 Ammo1, Ammo2; + TObjPtr 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; +}; + diff --git a/src/g_level.cpp b/src/g_level.cpp index 9a955a03e..4bb78461a 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -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) //========================================================================== // diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 814a36435..b83905901 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -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 { diff --git a/src/g_raven/a_artitele.cpp b/src/g_raven/a_artitele.cpp deleted file mode 100644 index 35387df45..000000000 --- a/src/g_raven/a_artitele.cpp +++ /dev/null @@ -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; -} diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp deleted file mode 100644 index dd4e4a8b8..000000000 --- a/src/g_raven/a_minotaur.cpp +++ /dev/null @@ -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 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(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 (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 (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; -} - diff --git a/src/g_raven/ravenshared.h b/src/g_raven/ravenshared.h deleted file mode 100644 index 3c6019ae7..000000000 --- a/src/g_raven/ravenshared.h +++ /dev/null @@ -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__ diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index edd97a6d4..2e4597617 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -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 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; -} diff --git a/src/g_shared/a_action.h b/src/g_shared/a_action.h deleted file mode 100644 index 7c5e00a79..000000000 --- a/src/g_shared/a_action.h +++ /dev/null @@ -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 *); diff --git a/src/g_shared/a_bridge.cpp b/src/g_shared/a_bridge.cpp index ab0df27e1..138210061 100644 --- a/src/g_shared/a_bridge.cpp +++ b/src/g_shared/a_bridge.cpp @@ -2,7 +2,6 @@ #include "info.h" #include "gi.h" #include "m_random.h" -#include "vm.h" static FRandom pr_orbit ("Orbit"); @@ -38,10 +37,10 @@ class ACustomBridge : public AActor DECLARE_CLASS (ACustomBridge, AActor) public: void BeginPlay (); - void Destroy(); + void Destroy() override; }; -IMPLEMENT_CLASS(ACustomBridge, false, false, false, false) +IMPLEMENT_CLASS(ACustomBridge, false, false) void ACustomBridge::BeginPlay () { @@ -90,24 +89,22 @@ void ACustomBridge::Destroy() // target pointer to center mobj // angle angle of ball -DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit) +static void BridgeOrbit(AActor *self) { - PARAM_SELF_PROLOGUE(AActor); - if (self->target == NULL) { // Don't crash if somebody spawned this into the world // independantly of a Bridge actor. - return 0; + return; } // Set default values // Every five tics, Hexen moved the ball 3/256th of a revolution. - DAngle rotationspeed = 45./32*3/5; + DAngle rotationspeed = 45. / 32 * 3 / 5; double rotationradius = ORBIT_RADIUS; // If the bridge is custom, set non-default values if any. // Set angular speed; 1--128: counterclockwise rotation ~=1--180°; 129--255: clockwise rotation ~= 180--1° - if (self->target->args[3] > 128) rotationspeed = 45./32 * (self->target->args[3]-256) / TICRATE; - else if (self->target->args[3] > 0) rotationspeed = 45./32 * (self->target->args[3]) / TICRATE; + if (self->target->args[3] > 128) rotationspeed = 45. / 32 * (self->target->args[3] - 256) / TICRATE; + else if (self->target->args[3] > 0) rotationspeed = 45. / 32 * (self->target->args[3]) / TICRATE; // Set rotation radius if (self->target->args[4]) rotationradius = ((self->target->args[4] * self->target->radius) / 100); @@ -115,11 +112,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit) self->SetOrigin(self->target->Vec3Angle(rotationradius, self->Angles.Yaw, 0), true); self->floorz = self->target->floorz; self->ceilingz = self->target->ceilingz; +} + +DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit) +{ + PARAM_SELF_PROLOGUE(AActor); + BridgeOrbit(self); return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BridgeInit) +DEFINE_ACTION_FUNCTION(AActor, A_BridgeInit) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS_DEF(balltype, AActor); @@ -141,7 +144,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BridgeInit) ball = Spawn(balltype, self->Pos(), ALLOW_REPLACE); ball->Angles.Yaw = startangle + (45./32) * (256/ballcount) * i; ball->target = self; - CALL_ACTION(A_BridgeOrbit, ball); + BridgeOrbit(ball); } return 0; } @@ -156,7 +159,7 @@ public: void BeginPlay (); }; -IMPLEMENT_CLASS(AInvisibleBridge, false, false, false, false) +IMPLEMENT_CLASS(AInvisibleBridge, false, false) void AInvisibleBridge::BeginPlay () { diff --git a/src/g_shared/a_camera.cpp b/src/g_shared/a_camera.cpp deleted file mode 100644 index ae34c3cbc..000000000 --- a/src/g_shared/a_camera.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* -** a_camera.cpp -** Implements the Duke Nukem 3D-ish security camera -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 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 "actor.h" -#include "info.h" -#include "a_sharedglobal.h" -#include "p_local.h" -#include "serializer.h" -#include "math/cmath.h" - -/* -== SecurityCamera -== -== args[0] = pitch -== args[1] = amount camera turns to either side of its initial position -== (in degrees) -== args[2] = octics to complete one cycle -*/ - -class ASecurityCamera : public AActor -{ - DECLARE_CLASS (ASecurityCamera, AActor) -public: - void PostBeginPlay (); - void Tick (); - - - void Serialize(FSerializer &arc); -protected: - DAngle Center; - DAngle Acc; - DAngle Delta; - DAngle Range; -}; - -IMPLEMENT_CLASS(ASecurityCamera, false, false, false, false) - -void ASecurityCamera::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("center", Center) - ("acc", Acc) - ("delta", Delta) - ("range", Range); -} - -void ASecurityCamera::PostBeginPlay () -{ - Super::PostBeginPlay (); - Center = Angles.Yaw; - if (args[2]) - Delta = 360. / (args[2] * TICRATE / 8); - else - Delta = 0.; - if (args[1]) - Delta /= 2; - Acc = 0.; - Angles.Pitch = (double)clamp((signed char)args[0], -89, 89); - Range = (double)args[1]; -} - -void ASecurityCamera::Tick () -{ - Acc += Delta; - if (Range != 0) - Angles.Yaw = Center + Range * Acc.Sin(); - else if (Delta != 0) - Angles.Yaw = Acc; -} - -/* -== AimingCamera -== -== args[0] = pitch -== args[1] = max turn (in degrees) -== args[2] = max pitch turn (in degrees) -== args[3] = tid of thing to look at -== -== Also uses: -== tracer: thing to look at -*/ - -class AAimingCamera : public ASecurityCamera -{ - DECLARE_CLASS (AAimingCamera, ASecurityCamera) -public: - void PostBeginPlay (); - void Tick (); - - - void Serialize(FSerializer &arc); -protected: - DAngle MaxPitchChange; -}; - -IMPLEMENT_CLASS(AAimingCamera, false, false, false, false) - -void AAimingCamera::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("maxpitchchange", MaxPitchChange); -} - -void AAimingCamera::PostBeginPlay () -{ - int changepitch = args[2]; - - args[2] = 0; - Super::PostBeginPlay (); - MaxPitchChange = double(changepitch / TICRATE); - Range /= TICRATE; - - TActorIterator iterator (args[3]); - tracer = iterator.Next (); - if (tracer == NULL) - { - //Printf ("AimingCamera %d: Can't find TID %d\n", tid, args[3]); - } - else - { // Don't try for a new target upon losing this one. - args[3] = 0; - } -} - -void AAimingCamera::Tick () -{ - if (tracer == NULL && args[3] != 0) - { // Recheck, in case something with this TID was created since the last time. - TActorIterator iterator (args[3]); - tracer = iterator.Next (); - } - if (tracer != NULL) - { - DAngle delta; - int dir = P_FaceMobj (this, tracer, &delta); - if (delta > Range) - { - delta = Range; - } - if (dir) - { - Angles.Yaw += delta; - } - else - { - Angles.Yaw -= delta; - } - if (MaxPitchChange != 0) - { // Aim camera's pitch; use floats for precision - DVector2 vect = tracer->Vec2To(this); - double dz = Z() - tracer->Z() - tracer->Height/2; - double dist = vect.Length(); - DAngle desiredPitch = dist != 0.f ? VecToAngle(dist, dz) : 0.; - DAngle diff = deltaangle(Angles.Pitch, desiredPitch); - if (fabs (diff) < MaxPitchChange) - { - Angles.Pitch = desiredPitch; - } - else if (diff < 0) - { - Angles.Pitch -= MaxPitchChange; - } - else - { - Angles.Pitch += MaxPitchChange; - } - } - } -} diff --git a/src/g_shared/a_debris.cpp b/src/g_shared/a_debris.cpp deleted file mode 100644 index 3be3c2240..000000000 --- a/src/g_shared/a_debris.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "actor.h" -#include "info.h" -#include "m_random.h" -#include "m_fixed.h" - -static FRandom pr_dirt ("SpawnDirt"); - -// Stained glass ------------------------------------------------------------ - -class AGlassShard : public AActor -{ - DECLARE_CLASS (AGlassShard, AActor) -public: - bool FloorBounceMissile (secplane_t &plane) - { - if (!Super::FloorBounceMissile (plane)) - { - if (fabs (Vel.Z) < 0.5) - { - Destroy (); - } - return false; - } - return true; - } -}; - -IMPLEMENT_CLASS(AGlassShard, false, false, false, false) - -// Dirt stuff - -void P_SpawnDirt (AActor *actor, double radius) -{ - PClassActor *dtype = NULL; - AActor *mo; - - double zo = pr_dirt() / 128. + 1; - DVector3 pos = actor->Vec3Angle(radius, pr_dirt() * (360./256), zo); - - char fmt[8]; - mysnprintf(fmt, countof(fmt), "Dirt%d", 1 + pr_dirt()%6); - dtype = PClass::FindActor(fmt); - if (dtype) - { - mo = Spawn (dtype, pos, ALLOW_REPLACE); - if (mo) - { - mo->Vel.Z = pr_dirt() / 64.; - } - } -} diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 06563f995..bfbe6d13d 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -58,14 +58,14 @@ static int ImpactCount; CVAR (Bool, cl_spreaddecals, true, CVAR_ARCHIVE) -IMPLEMENT_CLASS(DBaseDecal, false, true, false, false) +IMPLEMENT_CLASS(DBaseDecal, false, true) IMPLEMENT_POINTERS_START(DBaseDecal) IMPLEMENT_POINTER(WallPrev) IMPLEMENT_POINTER(WallNext) IMPLEMENT_POINTERS_END -IMPLEMENT_CLASS(DImpactDecal, false, false, false, false) +IMPLEMENT_CLASS(DImpactDecal, false, false) DBaseDecal::DBaseDecal () : DThinker(STAT_DECAL), @@ -746,7 +746,7 @@ public: void BeginPlay (); }; -IMPLEMENT_CLASS(ADecal, false, false, false, false) +IMPLEMENT_CLASS(ADecal, false, false) void ADecal::BeginPlay () { diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index ffca8a8fa..fb0faef0f 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -6,8 +6,9 @@ #include "p_lnspec.h" #include "b_bot.h" #include "p_checkposition.h" +#include "virtual.h" -IMPLEMENT_CLASS(AFastProjectile, false, false, false, false) +IMPLEMENT_CLASS(AFastProjectile, false, false) //---------------------------------------------------------------------------- @@ -132,7 +133,14 @@ void AFastProjectile::Tick () if (!frac.isZero() && ripcount <= 0) { ripcount = count >> 3; - Effect(); + + // call the scripted 'Effect' method. + IFVIRTUAL(AFastProjectile, Effect) + { + // Without the type cast this picks the 'void *' assignment... + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } } } @@ -153,35 +161,3 @@ void AFastProjectile::Tick () } -void AFastProjectile::Effect() -{ - FName name = GetClass()->MissileName; - if (name != NAME_None) - { - double hitz = Z()-8; - - if (hitz < floorz) - { - hitz = floorz; - } - // Do not clip this offset to the floor. - hitz += GetClass()->MissileHeight; - - PClassActor *trail = PClass::FindActor(name); - if (trail != NULL) - { - AActor *act = Spawn (trail, PosAtZ(hitz), ALLOW_REPLACE); - if (act != nullptr) - { - if ((flags5 & MF5_GETOWNER) && (target != nullptr)) - act->target = target; - else - act->target = this; - - act->Angles.Pitch = Angles.Pitch; - act->Angles.Yaw = Angles.Yaw; - } - } - } -} - diff --git a/src/g_shared/a_flashfader.cpp b/src/g_shared/a_flashfader.cpp index 8602f7a86..3e0b939e0 100644 --- a/src/g_shared/a_flashfader.cpp +++ b/src/g_shared/a_flashfader.cpp @@ -3,7 +3,7 @@ #include "d_player.h" #include "serializer.h" -IMPLEMENT_CLASS(DFlashFader, false, true, false, false) +IMPLEMENT_CLASS(DFlashFader, false, true) IMPLEMENT_POINTERS_START(DFlashFader) IMPLEMENT_POINTER(ForWho) diff --git a/src/g_shared/a_fountain.cpp b/src/g_shared/a_fountain.cpp index e89745fc7..7d7e2e412 100644 --- a/src/g_shared/a_fountain.cpp +++ b/src/g_shared/a_fountain.cpp @@ -46,13 +46,13 @@ public: void Deactivate (AActor *activator); }; -IMPLEMENT_CLASS(AParticleFountain, false, false, false, false) +IMPLEMENT_CLASS(AParticleFountain, false, false) void AParticleFountain::PostBeginPlay () { Super::PostBeginPlay (); if (!(SpawnFlags & MTF_DORMANT)) - Activate (NULL); + CallActivate (NULL); } void AParticleFountain::Activate (AActor *activator) diff --git a/src/g_shared/a_hatetarget.cpp b/src/g_shared/a_hatetarget.cpp deleted file mode 100644 index 7d25bdb31..000000000 --- a/src/g_shared/a_hatetarget.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* -** a_hatetarget.cpp -** Something for monsters to hate and shoot at -** -**--------------------------------------------------------------------------- -** Copyright 2003-2006 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 "actor.h" -#include "info.h" -#include "m_fixed.h" - -// Hate Target -------------------------------------------------------------- - -class AHateTarget : public AActor -{ - DECLARE_CLASS(AHateTarget, AActor) -public: - void BeginPlay(); - int TakeSpecialDamage(AActor *inflictor, AActor *source, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(AHateTarget, false, false, false, false) - -void AHateTarget::BeginPlay() -{ - Super::BeginPlay(); - if (SpawnAngle != 0) - { // Each degree translates into 10 units of health - health = SpawnAngle * 10; - } - else - { - special2 = 1; - health = 1000001; - } -} - -int AHateTarget::TakeSpecialDamage(AActor *inflictor, AActor *source, int damage, FName damagetype) -{ - if (special2 != 0) - { - return 0; - } - else - { - return damage; - } -} - diff --git a/src/g_shared/a_keys.h b/src/g_shared/a_keys.h deleted file mode 100644 index 8222aa694..000000000 --- a/src/g_shared/a_keys.h +++ /dev/null @@ -1,24 +0,0 @@ -#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); - - BYTE KeyNumber; - -protected: - virtual bool ShouldStay (); -}; - -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); - -#endif diff --git a/src/g_shared/a_lightning.cpp b/src/g_shared/a_lightning.cpp index f00560fc8..3f44f2fdd 100644 --- a/src/g_shared/a_lightning.cpp +++ b/src/g_shared/a_lightning.cpp @@ -13,7 +13,7 @@ static FRandom pr_lightning ("Lightning"); -IMPLEMENT_CLASS(DLightningThinker, false, false, false, false) +IMPLEMENT_CLASS(DLightningThinker, false, false) DLightningThinker::DLightningThinker () : DThinker (STAT_LIGHTNING) diff --git a/src/g_shared/a_mapmarker.cpp b/src/g_shared/a_mapmarker.cpp deleted file mode 100644 index d874042c5..000000000 --- a/src/g_shared/a_mapmarker.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* -** a_mapmarker.cpp -** An actor that appears on the automap instead of in the 3D view. -** -**--------------------------------------------------------------------------- -** Copyright 2006 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_sharedglobal.h" -#include "statnums.h" - -// Map Marker -------------------------------------------------------------- -// -// This class uses the following argument: -// args[0] == 0, shows the sprite at this actor -// != 0, shows the sprite for all actors whose TIDs match instead -// -// args[1] == 0, show the sprite always -// == 1, show the sprite only after its sector has been drawn -// -// To enable display of the sprite, activate it. To turn off the sprite, -// deactivate it. -// -// All the code to display it is in am_map.cpp. -// -//-------------------------------------------------------------------------- - -IMPLEMENT_CLASS(AMapMarker, false, false, false, false) - -void AMapMarker::BeginPlay () -{ - ChangeStatNum (STAT_MAPMARKER); -} - -void AMapMarker::Activate (AActor *activator) -{ - flags2 |= MF2_DORMANT; -} - -void AMapMarker::Deactivate (AActor *activator) -{ - flags2 &= ~MF2_DORMANT; -} diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 506f18b2f..f22318839 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -14,6 +14,7 @@ #include "serializer.h" #include "p_enemy.h" #include "d_player.h" +#include "a_armor.h" #include "r_data/sprites.h" static FRandom pr_morphmonst ("MorphMonster"); @@ -137,20 +138,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp AInventory *next = item->Inventory; if (item->IsKindOf (RUNTIME_CLASS(AArmor))) { - if (item->IsKindOf (RUNTIME_CLASS(AHexenArmor))) - { - // Set the HexenArmor slots to 0, except the class slot. - AHexenArmor *hxarmor = static_cast(item); - hxarmor->Slots[0] = 0; - hxarmor->Slots[1] = 0; - hxarmor->Slots[2] = 0; - hxarmor->Slots[3] = 0; - hxarmor->Slots[4] = spawntype->HexenArmor[0]; - } - else - { - item->DepleteOrDestroy(); - } + item->DepleteOrDestroy(); } item = next; } @@ -372,6 +360,15 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, return true; } +DEFINE_ACTION_FUNCTION(_PlayerInfo, UndoPlayerMorph) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_POINTER(player, player_t); + PARAM_INT_DEF(unmorphflag); + PARAM_BOOL_DEF(force); + ACTION_RETURN_BOOL(P_UndoPlayerMorph(self, player, unmorphflag, force)); +} + //--------------------------------------------------------------------------- // // FUNC P_MorphMonster @@ -616,21 +613,24 @@ void InitAllPowerupEffects(AInventory *item) // Base class for morphing projectiles -------------------------------------- -IMPLEMENT_CLASS(AMorphProjectile, false, false, false, false) +IMPLEMENT_CLASS(AMorphProjectile, false, false) + +DEFINE_FIELD(AMorphProjectile, PlayerClass) +DEFINE_FIELD(AMorphProjectile, MonsterClass) +DEFINE_FIELD(AMorphProjectile, MorphFlash) +DEFINE_FIELD(AMorphProjectile, UnMorphFlash) +DEFINE_FIELD(AMorphProjectile, Duration) +DEFINE_FIELD(AMorphProjectile, MorphStyle) int AMorphProjectile::DoSpecialDamage (AActor *target, int damage, FName damagetype) { - PClassActor *morph_flash = PClass::FindActor(MorphFlash); - PClassActor *unmorph_flash = PClass::FindActor(UnMorphFlash); if (target->player) { - PClassPlayerPawn *player_class = dyn_cast(PClass::FindClass(PlayerClass)); - P_MorphPlayer (NULL, target->player, player_class, Duration, MorphStyle, morph_flash, unmorph_flash); + P_MorphPlayer (NULL, target->player, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); } else { - PClassActor *monster_class = PClass::FindActor(MonsterClass); - P_MorphMonster (target, monster_class, Duration, MorphStyle, morph_flash, unmorph_flash); + P_MorphMonster (target, MonsterClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); } return -1; } @@ -650,12 +650,17 @@ void AMorphProjectile::Serialize(FSerializer &arc) // Morphed Monster (you must subclass this to do something useful) --------- -IMPLEMENT_CLASS(AMorphedMonster, false, true, false, false) +IMPLEMENT_CLASS(AMorphedMonster, false, true) IMPLEMENT_POINTERS_START(AMorphedMonster) IMPLEMENT_POINTER(UnmorphedMe) IMPLEMENT_POINTERS_END +DEFINE_FIELD(AMorphedMonster, UnmorphedMe) +DEFINE_FIELD(AMorphedMonster, UnmorphTime) +DEFINE_FIELD(AMorphedMonster, MorphStyle) +DEFINE_FIELD(AMorphedMonster, MorphExitFlash) + void AMorphedMonster::Serialize(FSerializer &arc) { Super::Serialize (arc); @@ -687,7 +692,7 @@ void AMorphedMonster::Die (AActor *source, AActor *inflictor, int dmgflags) if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED)) { UnmorphedMe->health = health; - UnmorphedMe->Die (source, inflictor, dmgflags); + UnmorphedMe->CallDie (source, inflictor, dmgflags); } } diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp index 030f5d368..a1ffe5dc6 100644 --- a/src/g_shared/a_movingcamera.cpp +++ b/src/g_shared/a_movingcamera.cpp @@ -66,7 +66,7 @@ public: TObjPtr Next; }; -IMPLEMENT_CLASS(AInterpolationPoint, false, true, false, false) +IMPLEMENT_CLASS(AInterpolationPoint, false, true) IMPLEMENT_POINTERS_START(AInterpolationPoint) IMPLEMENT_POINTER(Next) @@ -135,7 +135,7 @@ public: void Tick () {} // Does absolutely nothing itself }; -IMPLEMENT_CLASS(AInterpolationSpecial, false, false, false, false) +IMPLEMENT_CLASS(AInterpolationSpecial, false, false) /* == PathFollower: something that follows a camera path @@ -178,7 +178,7 @@ protected: int HoldTime; }; -IMPLEMENT_CLASS(APathFollower, false, true, false, false) +IMPLEMENT_CLASS(APathFollower, false, true) IMPLEMENT_POINTERS_START(APathFollower) IMPLEMENT_POINTER(PrevNode) @@ -342,9 +342,9 @@ void APathFollower::Tick () if (CurrNode != NULL) NewNode (); if (CurrNode == NULL || CurrNode->Next == NULL) - Deactivate (this); + CallDeactivate (this); if ((args[2] & 1) == 0 && CurrNode->Next->Next == NULL) - Deactivate (this); + CallDeactivate (this); } } } @@ -480,7 +480,7 @@ protected: bool Interpolate (); }; -IMPLEMENT_CLASS(AActorMover, false, false, false, false) +IMPLEMENT_CLASS(AActorMover, false, false) void AActorMover::BeginPlay() { @@ -596,7 +596,7 @@ protected: TObjPtr Activator; }; -IMPLEMENT_CLASS(AMovingCamera, false, true, false, false) +IMPLEMENT_CLASS(AMovingCamera, false, true) IMPLEMENT_POINTERS_START(AMovingCamera) IMPLEMENT_POINTER(Activator) diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h deleted file mode 100644 index 6dc2d0bc5..000000000 --- a/src/g_shared/a_pickups.h +++ /dev/null @@ -1,596 +0,0 @@ -#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 AWeapon; -class PClassWeapon; -class PClassPlayerPawn; -struct visstyle_t; - -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 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); - -/************************************************************************/ -/* 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 RestrictedToPlayerClass; - TArray ForbiddenToPlayerClass; -}; - -class AInventory : public AActor -{ - DECLARE_CLASS_WITH_META(AInventory, AActor, PClassInventory) - HAS_OBJECT_POINTERS -public: - virtual void Touch (AActor *toucher); - - virtual void Serialize(FSerializer &arc); - - virtual void MarkPrecacheSounds() const; - virtual void BeginPlay (); - virtual void Destroy (); - virtual void DepleteOrDestroy (); - virtual void Tick (); - virtual bool ShouldRespawn (); - virtual bool ShouldStay (); - virtual void Hide (); - bool CallTryPickup (AActor *toucher, AActor **toucher_return = NULL); - virtual void DoPickupSpecial (AActor *toucher); - virtual bool SpecialDropAction (AActor *dropper); - virtual bool DrawPowerup (int x, int y); - virtual void DoEffect (); - virtual bool Grind(bool items); - - virtual const char *PickupMessage (); - virtual void PlayPickupSound (AActor *toucher); - - 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 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; - - virtual void BecomeItem (); - virtual void BecomePickup (); - virtual void AttachToOwner (AActor *other); - virtual void DetachFromOwner (); - virtual AInventory *CreateCopy (AActor *other); - virtual AInventory *CreateTossable (); - virtual bool GoAway (); - virtual void GoAwayAndDie (); - virtual bool HandlePickup (AInventory *item); - virtual bool Use (bool pickup); - virtual void Travelled (); - virtual void OwnerDied (); - - virtual void AbsorbDamage (int damage, FName damageType, int &newdamage); - virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive); - virtual double GetSpeedFactor(); - virtual bool GetNoTeleportFreeze(); - virtual int AlterWeaponSprite (visstyle_t *vis); - - virtual PalEntry GetBlend (); - -protected: - virtual bool TryPickup (AActor *&toucher); - virtual bool TryPickupRestricted (AActor *&toucher); - bool CanPickup(AActor * toucher); - void GiveQuest(AActor * toucher); - -private: - static int StaticLastMessageTic; - static const char *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); - - bool TryPickup (AActor *&toucher); - bool Use (bool pickup); - bool SpecialDropAction (AActor *dropper); -}; - -// 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: - - void Serialize(FSerializer &arc); - AInventory *CreateCopy (AActor *other); - bool HandlePickup (AInventory *item); - PClassActor *GetParentAmmo () const; - AInventory *CreateTossable (); - - int BackpackAmount, BackpackMaxAmount; -}; - -// 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 Ammo1, Ammo2; - TObjPtr 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); - virtual bool ShouldStay (); - virtual void AttachToOwner (AActor *other); - virtual bool HandlePickup (AInventory *item); - virtual AInventory *CreateCopy (AActor *other); - virtual AInventory *CreateTossable (); - virtual bool TryPickup (AActor *&toucher); - virtual bool TryPickupRestricted (AActor *&toucher); - virtual bool PickupForAmmo (AWeapon *ownedWeapon); - virtual bool Use (bool pickup); - virtual void Destroy(); - - virtual FState *GetUpState (); - virtual FState *GetDownState (); - virtual FState *GetReadyState (); - virtual FState *GetAtkState (bool hold); - virtual FState *GetAltAtkState (bool hold); - virtual FState *GetStateForButtonName (FName button); - - virtual void PostMorphWeapon (); - virtual void EndPowerup (); - - 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: - bool TryPickup(AActor *&toucher); - - void Serialize(FSerializer &arc); - - double DropAmmoFactor; -}; - - -// 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) - - int PrevHealth; -public: - virtual bool TryPickup (AActor *&other); - virtual const char *PickupMessage (); -}; - -// 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); - virtual AInventory *CreateCopy (AActor *other); - virtual AInventory *CreateTossable (); - virtual bool HandlePickup (AInventory *item); - virtual bool Use (bool pickup); -}; - -// 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); - virtual void Tick (); - virtual AInventory *CreateCopy (AActor *other); - virtual bool HandlePickup (AInventory *item); - virtual void AbsorbDamage (int damage, FName damageType, int &newdamage); - - 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); - virtual AInventory *CreateCopy (AActor *other); - virtual bool Use (bool pickup); - - 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); - virtual AInventory *CreateCopy (AActor *other); - virtual bool Use (bool pickup); - - 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); - virtual AInventory *CreateCopy (AActor *other); - virtual AInventory *CreateTossable (); - virtual bool HandlePickup (AInventory *item); - virtual void AbsorbDamage (int damage, FName damageType, int &newdamage); - void DepleteOrDestroy(); - - double Slots[5]; - double SlotsIncrement[4]; - -protected: - bool AddArmorToSlot (AActor *actor, int slot, int amount); -}; - -// 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: - - bool ShouldStay (); - bool Use (bool pickup); - bool HandlePickup (AInventory *item); - - int PuzzleItemNumber; -}; - -// A MapRevealer reveals the whole map for the player who picks it up. -class AMapRevealer : public AInventory -{ - DECLARE_CLASS (AMapRevealer, AInventory) -public: - bool TryPickup (AActor *&toucher); -}; - -// 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: - - void Serialize(FSerializer &arc); - bool HandlePickup (AInventory *item); - AInventory *CreateCopy (AActor *other); - AInventory *CreateTossable (); - void DetachFromOwner (); - - bool bDepleted; -}; - - -// A score item is picked up without being added to the inventory. -// It differs from FakeInventory by doing nothing more than increasing the player's score. -class AScoreItem : public AInventory -{ - DECLARE_CLASS (AScoreItem, AInventory) - -public: - bool TryPickup(AActor *&toucher); -}; - - -#endif //__A_PICKUPS_H__ diff --git a/src/g_shared/a_puzzleitems.cpp b/src/g_shared/a_puzzleitems.cpp deleted file mode 100644 index d243dc73c..000000000 --- a/src/g_shared/a_puzzleitems.cpp +++ /dev/null @@ -1,54 +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 "c_console.h" -#include "doomstat.h" -#include "v_font.h" - -IMPLEMENT_CLASS(PClassPuzzleItem, false, false, false, false) - -void PClassPuzzleItem::DeriveData(PClass *newclass) -{ - Super::DeriveData(newclass); - assert(newclass->IsKindOf(RUNTIME_CLASS(PClassPuzzleItem))); - static_cast(newclass)->PuzzFailMessage = PuzzFailMessage; -} - -IMPLEMENT_CLASS(APuzzleItem, false, false, false, false) - -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; -} - diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 80a53ee08..96a932d4f 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -14,7 +14,7 @@ static FRandom pr_quake ("Quake"); -IMPLEMENT_CLASS(DEarthquake, false, true, false, false) +IMPLEMENT_CLASS(DEarthquake, false, true) IMPLEMENT_POINTERS_START(DEarthquake) IMPLEMENT_POINTER(m_Spot) diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index 2b2c82956..d5a396d0d 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -13,8 +13,6 @@ #include "s_sound.h" #include "statnums.h" #include "gstrings.h" -#include "a_action.h" -#include "vm.h" #include "v_text.h" #include "doomstat.h" #include "doomdata.h" @@ -231,4 +229,4 @@ class ARandomSpawner : public AActor }; -IMPLEMENT_CLASS(ARandomSpawner, false, false, false, false) +IMPLEMENT_CLASS(ARandomSpawner, false, false) diff --git a/src/g_shared/a_secrettrigger.cpp b/src/g_shared/a_secrettrigger.cpp deleted file mode 100644 index 372cef07d..000000000 --- a/src/g_shared/a_secrettrigger.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* -** a_secrettrigger.cpp -** A thing that counts toward the secret count when activated -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 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 "actor.h" -#include "g_level.h" -#include "c_console.h" -#include "info.h" -#include "s_sound.h" -#include "d_player.h" -#include "doomstat.h" -#include "v_font.h" -#include "p_spec.h" - -class ASecretTrigger : public AActor -{ - DECLARE_CLASS (ASecretTrigger, AActor) -public: - void PostBeginPlay (); - void Activate (AActor *activator); -}; - -IMPLEMENT_CLASS(ASecretTrigger, false, false, false, false) - -void ASecretTrigger::PostBeginPlay () -{ - Super::PostBeginPlay (); - level.total_secrets++; -} - -void ASecretTrigger::Activate (AActor *activator) -{ - P_GiveSecret(activator, args[0] <= 1, (args[0] == 0 || args[0] == 2), -1); - Destroy (); -} - diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp index 61a69b1b3..2d7037551 100644 --- a/src/g_shared/a_sectoraction.cpp +++ b/src/g_shared/a_sectoraction.cpp @@ -37,7 +37,7 @@ // The base class for sector actions ---------------------------------------- -IMPLEMENT_CLASS(ASectorAction, false, false, false, false) +IMPLEMENT_CLASS(ASectorAction, false, false) ASectorAction::ASectorAction (bool activatedByUse) : ActivatedByUse (activatedByUse) {} @@ -142,7 +142,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActEnter, false, false, false, false) +IMPLEMENT_CLASS(ASecActEnter, false, false) bool ASecActEnter::DoTriggerAction (AActor *triggerer, int activationType) @@ -160,7 +160,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActExit, false, false, false, false) +IMPLEMENT_CLASS(ASecActExit, false, false) bool ASecActExit::DoTriggerAction (AActor *triggerer, int activationType) @@ -181,7 +181,7 @@ public: // Skull Tag uses 9999 for a special that is triggered whenever // the player is on the sector's floor. I think this is more useful. -IMPLEMENT_CLASS(ASecActHitFloor, false, false, false, false) +IMPLEMENT_CLASS(ASecActHitFloor, false, false) bool ASecActHitFloor::DoTriggerAction (AActor *triggerer, int activationType) @@ -199,7 +199,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActHitCeil, false, false, false, false) +IMPLEMENT_CLASS(ASecActHitCeil, false, false) bool ASecActHitCeil::DoTriggerAction (AActor *triggerer, int activationType) @@ -218,7 +218,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActUse, false, false, false, false) +IMPLEMENT_CLASS(ASecActUse, false, false) bool ASecActUse::DoTriggerAction (AActor *triggerer, int activationType) @@ -237,7 +237,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActUseWall, false, false, false, false) +IMPLEMENT_CLASS(ASecActUseWall, false, false) bool ASecActUseWall::DoTriggerAction (AActor *triggerer, int activationType) @@ -255,7 +255,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActEyesDive, false, false, false, false) +IMPLEMENT_CLASS(ASecActEyesDive, false, false) bool ASecActEyesDive::DoTriggerAction (AActor *triggerer, int activationType) @@ -273,7 +273,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActEyesSurface, false, false, false, false) +IMPLEMENT_CLASS(ASecActEyesSurface, false, false) bool ASecActEyesSurface::DoTriggerAction (AActor *triggerer, int activationType) @@ -291,7 +291,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActEyesBelowC, false, false, false, false) +IMPLEMENT_CLASS(ASecActEyesBelowC, false, false) bool ASecActEyesBelowC::DoTriggerAction (AActor *triggerer, int activationType) @@ -309,7 +309,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActEyesAboveC, false, false, false, false) +IMPLEMENT_CLASS(ASecActEyesAboveC, false, false) bool ASecActEyesAboveC::DoTriggerAction (AActor *triggerer, int activationType) @@ -327,7 +327,7 @@ public: bool DoTriggerAction (AActor *triggerer, int activationType); }; -IMPLEMENT_CLASS(ASecActHitFakeFloor, false, false, false, false) +IMPLEMENT_CLASS(ASecActHitFakeFloor, false, false) bool ASecActHitFakeFloor::DoTriggerAction (AActor *triggerer, int activationType) diff --git a/src/g_shared/a_setcolor.cpp b/src/g_shared/a_setcolor.cpp deleted file mode 100644 index 585b9fcf6..000000000 --- a/src/g_shared/a_setcolor.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "r_defs.h" -#include "actor.h" -#include "info.h" - -class AColorSetter : public AActor -{ - DECLARE_CLASS(AColorSetter, AActor) - - void PostBeginPlay() - { - Super::PostBeginPlay(); - Sector->SetColor(args[0], args[1], args[2], args[3]); - Destroy(); - } - -}; - -IMPLEMENT_CLASS(AColorSetter, false, false, false, false) - -class AFadeSetter : public AActor -{ - DECLARE_CLASS(AFadeSetter, AActor) - - void PostBeginPlay() - { - Super::PostBeginPlay(); - Sector->SetFade(args[0], args[1], args[2]); - Destroy(); - } - -}; - -IMPLEMENT_CLASS(AFadeSetter, false, false, false, false) diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index c00cd8cf0..0087690f7 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -10,7 +10,6 @@ struct side_t; struct F3DFloor; class DBaseDecal; -void P_SpawnDirt (AActor *actor, double radius); class DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, double x, double y, double z, DAngle angle, double tracedist, bool permanent); class DBaseDecal : public DThinker @@ -25,7 +24,7 @@ public: DBaseDecal (const DBaseDecal *basis); void Serialize(FSerializer &arc); - void Destroy (); + void Destroy() override; FTextureID StickToWall(side_t *wall, double x, double y, F3DFloor * ffloor); double GetRealZ (const side_t *wall) const; void SetShade (DWORD rgb); @@ -67,7 +66,7 @@ public: static DImpactDecal *StaticCreate(const FDecalTemplate *tpl, const DVector3 &pos, side_t *wall, F3DFloor * ffloor, PalEntry color = 0); void BeginPlay (); - void Destroy (); + void Destroy() override; protected: DBaseDecal *CloneSelf(const FDecalTemplate *tpl, double x, double y, double z, side_t *wall, F3DFloor * ffloor) const; @@ -89,7 +88,7 @@ class ASkyViewpoint : public AActor DECLARE_CLASS (ASkyViewpoint, AActor) public: void BeginPlay (); - void Destroy (); + void Destroy() override; }; // For an EE compatible linedef based definition. @@ -117,7 +116,7 @@ public: DFlashFader (float r1, float g1, float b1, float a1, float r2, float g2, float b2, float a2, float time, AActor *who); - void Destroy (); + void Destroy() override; void Serialize(FSerializer &arc); void Tick (); AActor *WhoFor() { return ForWho; } @@ -193,7 +192,8 @@ public: void Serialize(FSerializer &arc); - FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash; + PClassPlayerPawn *PlayerClass; + PClassActor *MonsterClass, *MorphFlash, *UnMorphFlash; int Duration, MorphStyle; }; @@ -206,7 +206,7 @@ public: void Serialize(FSerializer &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); - void Destroy (); + void Destroy() override; TObjPtr UnmorphedMe; int UnmorphTime, MorphStyle; @@ -214,21 +214,11 @@ public: ActorFlags FlagsSave; }; -class AMapMarker : public AActor -{ - DECLARE_CLASS(AMapMarker, AActor) -public: - void BeginPlay (); - void Activate (AActor *activator); - void Deactivate (AActor *activator); -}; - class AFastProjectile : public AActor { DECLARE_CLASS(AFastProjectile, AActor) public: void Tick (); - virtual void Effect(); }; diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index 148a452e0..9e99d9368 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -42,7 +42,7 @@ // arg0 = Visibility*4 for this skybox -IMPLEMENT_CLASS(ASkyViewpoint, false, false, false, false) +IMPLEMENT_CLASS(ASkyViewpoint, false, false) // If this actor has no TID, make it the default sky box void ASkyViewpoint::BeginPlay () @@ -73,7 +73,7 @@ void ASkyViewpoint::Destroy () Super::Destroy(); } -IMPLEMENT_CLASS(ASkyCamCompat, false, false, false, false) +IMPLEMENT_CLASS(ASkyCamCompat, false, false) void ASkyCamCompat::BeginPlay() { @@ -98,7 +98,7 @@ public: void PostBeginPlay (); }; -IMPLEMENT_CLASS(ASkyPicker, false, false, false, false) +IMPLEMENT_CLASS(ASkyPicker, false, false) void ASkyPicker::PostBeginPlay () { @@ -142,7 +142,7 @@ void ASkyPicker::PostBeginPlay () // arg0 = opacity of plane; 0 = invisible, 255 = fully opaque -IMPLEMENT_CLASS(AStackPoint, false, false, false, false) +IMPLEMENT_CLASS(AStackPoint, false, false) void AStackPoint::BeginPlay () { @@ -157,10 +157,10 @@ class ASectorSilencer : public AActor DECLARE_CLASS (ASectorSilencer, AActor) public: void BeginPlay (); - void Destroy (); + void Destroy() override; }; -IMPLEMENT_CLASS(ASectorSilencer, false, false, false, false) +IMPLEMENT_CLASS(ASectorSilencer, false, false) void ASectorSilencer::BeginPlay () { @@ -177,18 +177,3 @@ void ASectorSilencer::Destroy () Super::Destroy (); } -class ASectorFlagSetter : public AActor -{ - DECLARE_CLASS (ASectorFlagSetter, AActor) -public: - void BeginPlay (); -}; - -IMPLEMENT_CLASS(ASectorFlagSetter, false, false, false, false) - -void ASectorFlagSetter::BeginPlay () -{ - Super::BeginPlay (); - Sector->Flags |= args[0]; -} - diff --git a/src/g_shared/a_soundenvironment.cpp b/src/g_shared/a_soundenvironment.cpp index 8e5cb133b..25088ae44 100644 --- a/src/g_shared/a_soundenvironment.cpp +++ b/src/g_shared/a_soundenvironment.cpp @@ -46,14 +46,14 @@ public: void Activate (AActor *deactivator); }; -IMPLEMENT_CLASS(ASoundEnvironment, false, false, false, false) +IMPLEMENT_CLASS(ASoundEnvironment, false, false) void ASoundEnvironment::PostBeginPlay () { Super::PostBeginPlay (); if (!(flags2 & MF2_DORMANT)) { - Activate (this); + CallActivate (this); } } diff --git a/src/g_shared/a_soundsequence.cpp b/src/g_shared/a_soundsequence.cpp index 25380a4c7..304017fba 100644 --- a/src/g_shared/a_soundsequence.cpp +++ b/src/g_shared/a_soundsequence.cpp @@ -80,7 +80,7 @@ public: TObjPtr Sequence; }; -IMPLEMENT_CLASS(ASoundSequenceSlot, false, true, false, false) +IMPLEMENT_CLASS(ASoundSequenceSlot, false, true) IMPLEMENT_POINTERS_START(ASoundSequenceSlot) IMPLEMENT_POINTER(Sequence) @@ -104,14 +104,14 @@ class ASoundSequence : public AActor { DECLARE_CLASS (ASoundSequence, AActor) public: - void Destroy (); + void Destroy() override; void PostBeginPlay (); void Activate (AActor *activator); void Deactivate (AActor *activator); void MarkPrecacheSounds () const; }; -IMPLEMENT_CLASS(ASoundSequence, false, false, false, false) +IMPLEMENT_CLASS(ASoundSequence, false, false) //========================================================================== // diff --git a/src/g_shared/a_spark.cpp b/src/g_shared/a_spark.cpp deleted file mode 100644 index 654ad710b..000000000 --- a/src/g_shared/a_spark.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* -** a_spark.cpp -** Actor that makes a particle spark when activated -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 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 "actor.h" -#include "info.h" -#include "m_random.h" -#include "p_effect.h" -#include "s_sound.h" - -class ASpark : public AActor -{ - DECLARE_CLASS (ASpark, AActor) -public: - void Activate (AActor *activator); -}; - -IMPLEMENT_CLASS(ASpark, false, false, false, false) - -void ASpark::Activate (AActor *activator) -{ - Super::Activate (activator); - P_DrawSplash (args[0] ? args[0] : 32, Pos(), Angles.Yaw, 1); - S_Sound (this, CHAN_AUTO, "world/spark", 1, ATTN_STATIC); -} diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp index 86d0b5e8a..026185d07 100644 --- a/src/g_shared/a_specialspot.cpp +++ b/src/g_shared/a_specialspot.cpp @@ -37,7 +37,6 @@ #include "p_local.h" #include "statnums.h" #include "i_system.h" -#include "vm.h" #include "doomstat.h" #include "serializer.h" #include "a_pickups.h" @@ -45,8 +44,8 @@ static FRandom pr_spot ("SpecialSpot"); static FRandom pr_spawnmace ("SpawnMace"); -IMPLEMENT_CLASS(DSpotState, false, false, false, false) -IMPLEMENT_CLASS(ASpecialSpot, false, false, false, false) +IMPLEMENT_CLASS(DSpotState, false, false) +IMPLEMENT_CLASS(ASpecialSpot, false, false) TObjPtr DSpotState::SpotState; //---------------------------------------------------------------------------- @@ -252,6 +251,12 @@ DSpotState *DSpotState::GetSpotState(bool create) return SpotState; } +DEFINE_ACTION_FUNCTION(DSpotState, GetSpotState) +{ + PARAM_PROLOGUE; + ACTION_RETURN_OBJECT(DSpotState::GetSpotState()); +} + //---------------------------------------------------------------------------- // // @@ -318,6 +323,14 @@ ASpecialSpot *DSpotState::GetNextInList(PClassActor *type, int skipcounter) return NULL; } +DEFINE_ACTION_FUNCTION(DSpotState, GetNextInList) +{ + PARAM_SELF_PROLOGUE(DSpotState); + PARAM_CLASS(type, AActor); + PARAM_INT(skipcounter); + ACTION_RETURN_OBJECT(self->GetNextInList(type, skipcounter)); +} + //---------------------------------------------------------------------------- // // @@ -331,6 +344,18 @@ ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(PClassActor *type, double x, return NULL; } +DEFINE_ACTION_FUNCTION(DSpotState, GetSpotWithMinMaxDistance) +{ + PARAM_SELF_PROLOGUE(DSpotState); + PARAM_CLASS(type, AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(mindist); + PARAM_FLOAT(maxdist); + ACTION_RETURN_OBJECT(self->GetSpotWithMinMaxDistance(type, x, y, mindist, maxdist)); +} + + //---------------------------------------------------------------------------- // // @@ -372,12 +397,11 @@ void ASpecialSpot::Destroy() // Mace spawn spot ---------------------------------------------------------- - // Every mace spawn spot will execute this action. The first one // will build a list of all mace spots in the level and spawn a // mace. The rest of the spots will do nothing. -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnSingleItem) +DEFINE_ACTION_FUNCTION(AActor, A_SpawnSingleItem) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (cls, AActor); diff --git a/src/g_shared/a_specialspot.h b/src/g_shared/a_specialspot.h index 8fe38608a..57a643bdd 100644 --- a/src/g_shared/a_specialspot.h +++ b/src/g_shared/a_specialspot.h @@ -11,7 +11,7 @@ class ASpecialSpot : public AActor public: void BeginPlay(); - void Destroy(); + void Destroy() override; }; @@ -28,7 +28,7 @@ public: DSpotState (); - void Destroy (); + void Destroy() override; void Tick (); static DSpotState *GetSpotState(bool create = true); FSpotList *FindSpotList(PClassActor *type); diff --git a/src/g_shared/a_waterzone.cpp b/src/g_shared/a_waterzone.cpp deleted file mode 100644 index 556d08d7a..000000000 --- a/src/g_shared/a_waterzone.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -** a_waterzone.cpp -** Actor that makes BOOM deep water swimmable -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 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 "info.h" -#include "r_defs.h" - -class AWaterZone : public AActor -{ - DECLARE_CLASS (AWaterZone, AActor) -public: - void PostBeginPlay (); -}; - -IMPLEMENT_CLASS(AWaterZone, false, false, false, false) - -void AWaterZone::PostBeginPlay () -{ - Super::PostBeginPlay (); - Sector->MoreFlags |= SECF_UNDERWATER; - Destroy (); -} - diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index e9370ae79..0db6d4743 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -44,15 +44,15 @@ EXTERN_CVAR(Int, con_scaletext) int active_con_scaletext(); -IMPLEMENT_CLASS(DHUDMessage, false, true, false, false) +IMPLEMENT_CLASS(DHUDMessage, false, true) IMPLEMENT_POINTERS_START(DHUDMessage) IMPLEMENT_POINTER(Next) IMPLEMENT_POINTERS_END -IMPLEMENT_CLASS(DHUDMessageFadeOut, false, false, false, false) -IMPLEMENT_CLASS(DHUDMessageFadeInOut, false, false, false, false) -IMPLEMENT_CLASS(DHUDMessageTypeOnFadeOut, false, false, false, false) +IMPLEMENT_CLASS(DHUDMessageFadeOut, false, false) +IMPLEMENT_CLASS(DHUDMessageFadeInOut, false, false) +IMPLEMENT_CLASS(DHUDMessageTypeOnFadeOut, false, false) /************************************************************************* * Basic HUD message. Appears and disappears without any special effects * diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index 6d5b10739..7b7a2849a 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -341,7 +341,7 @@ public: }; DBaseStatusBar (int reltop, int hres=320, int vres=200); - void Destroy (); + void Destroy() override; void SetScaled (bool scale, bool force=false); diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 304101311..8ca8d6816 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -45,19 +45,21 @@ #include "st_stuff.h" #include "m_swap.h" #include "a_keys.h" +#include "a_armor.h" #include "templates.h" #include "i_system.h" #include "sbarinfo.h" #include "gi.h" #include "r_data/r_translate.h" +#include "a_artifacts.h" #include "a_weaponpiece.h" -#include "a_strifeglobal.h" #include "g_level.h" #include "v_palette.h" #include "p_acs.h" #include "gstrings.h" #include "version.h" #include "cmdlib.h" +#include "a_ammo.h" #define ARTIFLASH_OFFSET (statusBar->invBarOffset+6) enum @@ -1531,7 +1533,7 @@ private: SBarInfoMainBlock *lastPopup; }; -IMPLEMENT_CLASS(DSBarInfo, false, true, false, false) +IMPLEMENT_CLASS(DSBarInfo, false, true) IMPLEMENT_POINTERS_START(DSBarInfo) IMPLEMENT_POINTER(ammo1) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index b3aecb0b5..f78c61040 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -268,7 +268,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl } else if(type == SIGIL) { - AInventory *item = statusBar->CPlayer->mo->FindInventory(); + AInventory *item = statusBar->CPlayer->mo->FindInventory(PClass::FindActor(NAME_Sigil)); if (item != NULL) texture = TexMan(item->Icon); } diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 4733c6e41..a061dbf59 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -43,6 +43,8 @@ #include "c_cvars.h" #include "w_wad.h" #include "a_keys.h" +#include "a_armor.h" +#include "a_ammo.h" #include "sbar.h" #include "sc_man.h" #include "templates.h" diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 80d221cfc..c288a05c8 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -62,7 +62,7 @@ #define XHAIRPICKUPSIZE (2+XHAIRSHRINKSIZE) #define POWERUPICONSIZE 32 -IMPLEMENT_CLASS(DBaseStatusBar, false, true, false, false) +IMPLEMENT_CLASS(DBaseStatusBar, false, true) IMPLEMENT_POINTERS_START(DBaseStatusBar) IMPLEMENT_POINTER(Messages[0]) diff --git a/src/g_skill.cpp b/src/g_skill.cpp index 44c2cef8c..70daadc23 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -388,6 +388,13 @@ int G_SkillProperty(ESkillProperty prop) return 0; } +DEFINE_ACTION_FUNCTION(DObject, G_SkillPropertyInt) +{ + PARAM_PROLOGUE; + PARAM_INT(which); + ACTION_RETURN_INT(G_SkillProperty((ESkillProperty)which)); +} + //========================================================================== // // @@ -433,6 +440,13 @@ double G_SkillProperty(EFSkillProperty prop) return 0; } +DEFINE_ACTION_FUNCTION(DObject, G_SkillPropertyFloat) +{ + PARAM_PROLOGUE; + PARAM_INT(which); + ACTION_RETURN_FLOAT(G_SkillProperty((EFSkillProperty)which)); +} + //========================================================================== diff --git a/src/g_strife/a_acolyte.cpp b/src/g_strife/a_acolyte.cpp deleted file mode 100644 index a21c06619..000000000 --- a/src/g_strife/a_acolyte.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "a_strifeglobal.h" -#include "doomdata.h" -#include "vm.h" -#include "doomstat.h" -*/ - -//============================================================================ -// -// A_HideDecepticon -// -// Hide the Acolyte-to-be -> -// Hide the guy transforming into an Acolyte -> -// Hide the transformer -> -// Transformers are Autobots and Decepticons, and -// Decepticons are the bad guys, so... -> -// -// Hide the Decepticon! -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_HideDecepticon) -{ - PARAM_SELF_PROLOGUE(AActor); - - EV_DoDoor (DDoor::doorClose, NULL, self, 999, 8., 0, 0, 0); - if (self->target != NULL && self->target->player != NULL) - { - P_NoiseAlert (self->target, self); - } - return 0; -} - -//============================================================================ -// -// A_AcolyteDie -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_AcolyteDie) -{ - PARAM_SELF_PROLOGUE(AActor); - - int i; - - // [RH] Disable translucency here. - self->RenderStyle = STYLE_Normal; - - // Only the Blue Acolyte does extra stuff on death. - if (self->GetClass()->TypeName != NAME_AcolyteBlue) - return 0; - - // Make sure somebody is still alive - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].health > 0) - break; - } - if (i == MAXPLAYERS) - return 0; - - // Make sure all the other blue acolytes are dead. - TThinkerIterator iterator(NAME_AcolyteBlue); - AActor *other; - - while ( (other = iterator.Next ()) ) - { - if (other != self && other->health > 0) - { // Found a living one - return 0; - } - } - - players[i].mo->GiveInventoryType (QuestItemClasses[6]); - players[i].SetLogNumber (14); - S_StopSound (CHAN_VOICE); - S_Sound (CHAN_VOICE, "svox/voc14", 1, ATTN_NORM); - return 0; -} - -//============================================================================ -// -// A_BeShadowyFoe -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_BeShadowyFoe) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->RenderStyle = STYLE_Translucent; - self->Alpha = HR_SHADOW; - self->flags &= ~MF_FRIENDLY; - return 0; -} - -//============================================================================ -// -// A_AcolyteBits -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_AcolyteBits) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->SpawnFlags & MTF_SHADOW) - { - CALL_ACTION(A_BeShadowyFoe, self); - } - if (self->SpawnFlags & MTF_ALTSHADOW) - { - //self->flags |= MF_STRIFEx8000000; - if (self->flags & MF_SHADOW) - { - // I dunno. - } - else - { - self->RenderStyle.BlendOp = STYLEOP_None; - } - } - return 0; -} diff --git a/src/g_strife/a_alienspectres.cpp b/src/g_strife/a_alienspectres.cpp deleted file mode 100644 index a1c20355f..000000000 --- a/src/g_strife/a_alienspectres.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "m_random.h" -#include "a_strifeglobal.h" -#include "c_console.h" -#include "gstrings.h" -#include "vm.h" -#include "doomstat.h" -*/ - -static FRandom pr_spectrespawn ("AlienSpectreSpawn"); -static FRandom pr_spectrechunk ("212e4"); - -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SpectreChunkSmall) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *foo = Spawn("AlienChunkSmall", self->PosPlusZ(10.), ALLOW_REPLACE); - - if (foo != NULL) - { - int t; - - t = pr_spectrechunk() & 15; - foo->Vel.X = (t - (pr_spectrechunk() & 7)); - - t = pr_spectrechunk() & 15; - foo->Vel.Y = (t - (pr_spectrechunk() & 7)); - - foo->Vel.Z = (pr_spectrechunk() & 15); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SpectreChunkLarge) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *foo = Spawn("AlienChunkLarge", self->PosPlusZ(10.), ALLOW_REPLACE); - - if (foo != NULL) - { - int t; - - t = pr_spectrechunk() & 7; - foo->Vel.X = (t - (pr_spectrechunk() & 15)); - - t = pr_spectrechunk() & 7; - foo->Vel.Y = (t - (pr_spectrechunk() & 15)); - - foo->Vel.Z = (pr_spectrechunk() & 7); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_Spectre3Attack) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->target == NULL) - return 0; - - AActor *foo = Spawn("SpectralLightningV2", self->PosPlusZ(32.), ALLOW_REPLACE); - - foo->Vel.Z = -12; - foo->target = self; - foo->FriendPlayer = 0; - foo->tracer = self->target; - - self->Angles.Yaw -= 90.; - for (int i = 0; i < 20; ++i) - { - self->Angles.Yaw += 9.; - P_SpawnSubMissile (self, PClass::FindActor("SpectralLightningBall2"), self); - } - self->Angles.Yaw -= 90.; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *player; - char voc[32]; - int log; - int i; - - A_Unblock(self, true); // [RH] Need this for Sigil rewarding - if (!CheckBossDeath (self)) - { - return 0; - } - for (i = 0, player = NULL; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].health > 0) - { - player = players[i].mo; - break; - } - } - if (player == NULL) - { - return 0; - } - - switch (self->GetClass()->TypeName) - { - case NAME_AlienSpectre1: - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, 1., 0., -1, 0, false); - log = 95; - break; - - case NAME_AlienSpectre2: - C_MidPrint(SmallFont, GStrings("TXT_KILLED_BISHOP")); - log = 74; - player->GiveInventoryType (QuestItemClasses[20]); - break; - - case NAME_AlienSpectre3: - { - C_MidPrint(SmallFont, GStrings("TXT_KILLED_ORACLE")); - // If there are any Oracles still alive, kill them. - TThinkerIterator it(NAME_Oracle); - AActor *oracle; - - while ( (oracle = it.Next()) != NULL) - { - if (oracle->health > 0) - { - oracle->health = 0; - oracle->Die (self, self); - } - } - player->GiveInventoryType (QuestItemClasses[22]); - if (player->FindInventory (QuestItemClasses[20])) - { // If the Bishop is dead, set quest item 22 - player->GiveInventoryType (QuestItemClasses[21]); - } - if (player->FindInventory (QuestItemClasses[23]) == NULL) - { // Macil is calling us back... - log = 87; - } - else - { // You wield the power of the complete Sigil. - log = 85; - } - EV_DoDoor (DDoor::doorOpen, NULL, NULL, 222, 8., 0, 0, 0); - break; - } - - case NAME_AlienSpectre4: - C_MidPrint(SmallFont, GStrings("TXT_KILLED_MACIL")); - player->GiveInventoryType (QuestItemClasses[23]); - if (player->FindInventory (QuestItemClasses[24]) == NULL) - { // Richter has taken over. Macil is a snake. - log = 79; - } - else - { // Back to the factory for another Sigil! - log = 106; - } - break; - - case NAME_AlienSpectre5: - C_MidPrint(SmallFont, GStrings("TXT_KILLED_LOREMASTER")); - ASigil *sigil; - - player->GiveInventoryType (QuestItemClasses[25]); - if (!multiplayer) - { - player->GiveInventoryType (RUNTIME_CLASS(AUpgradeStamina)); - player->GiveInventoryType (RUNTIME_CLASS(AUpgradeAccuracy)); - } - sigil = player->FindInventory(); - if (sigil != NULL && sigil->NumPieces == 5) - { // You wield the power of the complete Sigil. - log = 85; - } - else - { // Another Sigil piece. Woohoo! - log = 83; - } - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, 1., 0., -1, 0, false); - break; - - default: - return 0; - } - mysnprintf (voc, countof(voc), "svox/voc%d", log); - S_Sound (CHAN_VOICE, voc, 1, ATTN_NORM); - player->player->SetLogNumber (log); - return 0; -} diff --git a/src/g_strife/a_coin.cpp b/src/g_strife/a_coin.cpp deleted file mode 100644 index 87c4619d3..000000000 --- a/src/g_strife/a_coin.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* -#include "a_pickups.h" -#include "a_strifeglobal.h" -#include "gstrings.h" -*/ - -// Coin --------------------------------------------------------------------- - -IMPLEMENT_CLASS(ACoin, false, false, false, false) - -const char *ACoin::PickupMessage () -{ - if (Amount == 1) - { - return Super::PickupMessage(); - } - else - { - static char msg[64]; - - mysnprintf (msg, countof(msg), GStrings("TXT_XGOLD"), Amount); - return msg; - } -} - -bool ACoin::HandlePickup (AInventory *item) -{ - if (item->IsKindOf (RUNTIME_CLASS(ACoin))) - { - if (Amount < MaxAmount) - { - if (MaxAmount - Amount < item->Amount) - { - Amount = MaxAmount; - } - else - { - Amount += item->Amount; - } - item->ItemFlags |= IF_PICKUPGOOD; - } - return true; - } - if (Inventory != NULL) - { - return Inventory->HandlePickup (item); - } - return false; -} - -AInventory *ACoin::CreateCopy (AActor *other) -{ - if (GetClass() == RUNTIME_CLASS(ACoin)) - { - return Super::CreateCopy (other); - } - AInventory *copy = Spawn (); - copy->Amount = Amount; - copy->BecomeItem (); - GoAwayAndDie (); - return copy; -} - -//=========================================================================== -// -// ACoin :: CreateTossable -// -// Gold drops in increments of 50 if you have that much, less if you don't. -// -//=========================================================================== - -AInventory *ACoin::CreateTossable () -{ - ACoin *tossed; - - if ((ItemFlags & IF_UNDROPPABLE) || Owner == NULL || Amount <= 0) - { - return NULL; - } - if (Amount >= 50) - { - Amount -= 50; - tossed = static_cast(Spawn("Gold50", Owner->Pos(), NO_REPLACE)); - } - else if (Amount >= 25) - { - Amount -= 25; - tossed = static_cast(Spawn("Gold25", Owner->Pos(), NO_REPLACE)); - } - else if (Amount >= 10) - { - Amount -= 10; - tossed = static_cast(Spawn("Gold10", Owner->Pos(), NO_REPLACE)); - } - else if (Amount > 1 || (ItemFlags & IF_KEEPDEPLETED)) - { - Amount -= 1; - tossed = static_cast(Spawn("Coin", Owner->Pos(), NO_REPLACE)); - } - else // Amount == 1 && !(ItemFlags & IF_KEEPDEPLETED) - { - BecomePickup (); - tossed = this; - } - tossed->flags &= ~(MF_SPECIAL|MF_SOLID); - tossed->DropTime = 30; - if (tossed != this && Amount <= 0) - { - Destroy (); - } - return tossed; -} diff --git a/src/g_strife/a_crusader.cpp b/src/g_strife/a_crusader.cpp deleted file mode 100644 index 05835a5f9..000000000 --- a/src/g_strife/a_crusader.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "a_strifeglobal.h" -#include "vm.h" -*/ - -static bool CrusaderCheckRange (AActor *self) -{ - if (self->reactiontime == 0 && P_CheckSight (self, self->target)) - { - return self->Distance2D (self->target) < 264.; - } - return false; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CrusaderChoose) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->target == NULL) - return 0; - - if (CrusaderCheckRange (self)) - { - A_FaceTarget (self); - self->Angles.Yaw -= 180./16; - P_SpawnMissileZAimed (self, self->Z() + 40, self->target, PClass::FindActor("FastFlameMissile")); - } - else - { - if (P_CheckMissileRange (self)) - { - A_FaceTarget (self); - P_SpawnMissileZAimed (self, self->Z() + 56, self->target, PClass::FindActor("CrusaderMissile")); - self->Angles.Yaw -= 45./32; - P_SpawnMissileZAimed (self, self->Z() + 40, self->target, PClass::FindActor("CrusaderMissile")); - self->Angles.Yaw += 45./16; - P_SpawnMissileZAimed (self, self->Z() + 40, self->target, PClass::FindActor("CrusaderMissile")); - self->Angles.Yaw -= 45./16; - self->reactiontime += 15; - } - self->SetState (self->SeeState); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CrusaderSweepLeft) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->Angles.Yaw += 90./16; - AActor *misl = P_SpawnMissileZAimed (self, self->Z() + 48, self->target, PClass::FindActor("FastFlameMissile")); - if (misl != NULL) - { - misl->Vel.Z += 1; - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CrusaderSweepRight) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->Angles.Yaw -= 90./16; - AActor *misl = P_SpawnMissileZAimed (self, self->Z() + 48, self->target, PClass::FindActor("FastFlameMissile")); - if (misl != NULL) - { - misl->Vel.Z += 1; - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CrusaderRefire) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->target == NULL || - self->target->health <= 0 || - !P_CheckSight (self, self->target)) - { - self->SetState (self->SeeState); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CrusaderDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (CheckBossDeath (self)) - { - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, 1., 0., -1, 0, false); - } - return 0; -} diff --git a/src/g_strife/a_entityboss.cpp b/src/g_strife/a_entityboss.cpp deleted file mode 100644 index 535022901..000000000 --- a/src/g_strife/a_entityboss.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "a_strifeglobal.h" -#include "vm.h" -#include "g_level.h" -*/ - -static FRandom pr_entity ("Entity"); - -DEFINE_ACTION_FUNCTION(AActor, A_SubEntityDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (CheckBossDeath (self)) - { - G_ExitLevel (0, false); - } - return 0; -} - -void A_SpectralMissile (AActor *self, const char *missilename) -{ - if (self->target != NULL) - { - AActor *missile = P_SpawnMissileXYZ (self->PosPlusZ(32.), self, self->target, PClass::FindActor(missilename), false); - if (missile != NULL) - { - missile->tracer = self->target; - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -DECLARE_ACTION(A_SpotLightning) -DECLARE_ACTION(A_Spectre3Attack) - - -DEFINE_ACTION_FUNCTION(AActor, A_EntityAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - // Apparent Strife bug: Case 5 was unreachable because they used % 5 instead of % 6. - // I've fixed that by making case 1 duplicate it, since case 1 did nothing. - switch (pr_entity() % 5) - { - case 0: - CALL_ACTION(A_SpotLightning, self); - break; - - case 2: - A_SpectralMissile (self, "SpectralLightningH3"); - break; - - case 3: - CALL_ACTION(A_Spectre3Attack, self); - break; - - case 4: - A_SpectralMissile (self, "SpectralLightningBigV2"); - break; - - case 1: - case 5: - A_SpectralMissile (self, "SpectralLightningBigBall2"); - break; - } - return 0; -} - - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnEntity) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *entity = Spawn("EntityBoss", self->PosPlusZ(70.), ALLOW_REPLACE); - if (entity != NULL) - { - entity->Angles.Yaw = self->Angles.Yaw; - entity->CopyFriendliness(self, true); - entity->Vel.Z = 5; - entity->tracer = self; - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_EntityDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *second; - double secondRadius = GetDefaultByName("EntitySecond")->radius * 2; - - static const double turns[3] = { 0, 90, -90 }; - const double velmul[3] = { 4.8828125f, secondRadius*4, secondRadius*4 }; - - AActor *spot = self->tracer; - if (spot == NULL) spot = self; - - for (int i = 0; i < 3; i++) - { - DAngle an = self->Angles.Yaw + turns[i]; - DVector3 pos = spot->Vec3Angle(secondRadius, an, self->tracer ? 70. : 0.); - - second = Spawn("EntitySecond", pos, ALLOW_REPLACE); - second->CopyFriendliness(self, true); - A_FaceTarget(second); - second->VelFromAngle(velmul[i], an); - } - return 0; -} diff --git a/src/g_strife/a_inquisitor.cpp b/src/g_strife/a_inquisitor.cpp deleted file mode 100644 index a4c2d72f4..000000000 --- a/src/g_strife/a_inquisitor.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "vm.h" -*/ - -static FRandom pr_inq ("Inquisitor"); - -DEFINE_ACTION_FUNCTION(AActor, A_InquisitorWalk) -{ - PARAM_SELF_PROLOGUE(AActor); - - S_Sound (self, CHAN_BODY, "inquisitor/walk", 1, ATTN_NORM); - A_Chase (stack, self); - return 0; -} - -bool InquisitorCheckDistance (AActor *self) -{ - if (self->reactiontime == 0 && P_CheckSight (self, self->target)) - { - return self->Distance2D (self->target) < 264.; - } - return false; -} - -DEFINE_ACTION_FUNCTION(AActor, A_InquisitorDecide) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->target == NULL) - return 0; - - A_FaceTarget (self); - if (!InquisitorCheckDistance (self)) - { - self->SetState (self->FindState("Grenade")); - } - if (self->target->Z() != self->Z()) - { - if (self->Top() + 54 < self->ceilingz) - { - self->SetState (self->FindState("Jump")); - } - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_InquisitorAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *proj; - - if (self->target == NULL) - return 0; - - A_FaceTarget (self); - - self->AddZ(32); - self->Angles.Yaw -= 45./32; - proj = P_SpawnMissileZAimed (self, self->Z(), self->target, PClass::FindActor("InquisitorShot")); - if (proj != NULL) - { - proj->Vel.Z += 9; - } - self->Angles.Yaw += 45./16; - proj = P_SpawnMissileZAimed (self, self->Z(), self->target, PClass::FindActor("InquisitorShot")); - if (proj != NULL) - { - proj->Vel.Z += 16; - } - self->AddZ(-32); - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_InquisitorJump) -{ - PARAM_SELF_PROLOGUE(AActor); - - double dist; - double speed; - - if (self->target == NULL) - return 0; - - S_Sound (self, CHAN_ITEM|CHAN_LOOP, "inquisitor/jump", 1, ATTN_NORM); - self->AddZ(64); - A_FaceTarget (self); - speed = self->Speed * (2./3); - self->VelFromAngle(speed); - dist = self->DistanceBySpeed(self->target, speed); - self->Vel.Z = (self->target->Z() - self->Z()) / dist; - self->reactiontime = 60; - self->flags |= MF_NOGRAVITY; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_InquisitorCheckLand) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->reactiontime--; - if (self->reactiontime < 0 || - self->Vel.X == 0 || - self->Vel.Y == 0 || - self->Z() <= self->floorz) - { - self->SetState (self->SeeState); - self->reactiontime = 0; - self->flags &= ~MF_NOGRAVITY; - S_StopSound (self, CHAN_ITEM); - return 0; - } - if (!S_IsActorPlayingSomething (self, CHAN_ITEM, -1)) - { - S_Sound (self, CHAN_ITEM|CHAN_LOOP, "inquisitor/jump", 1, ATTN_NORM); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_TossArm) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *foo = Spawn("InquisitorArm", self->PosPlusZ(24.), ALLOW_REPLACE); - foo->Angles.Yaw = self->Angles.Yaw - 90. + pr_inq.Random2() * (360./1024.); - foo->VelFromAngle(foo->Speed / 8); - foo->Vel.Z = pr_inq() / 64.; - return 0; -} - diff --git a/src/g_strife/a_loremaster.cpp b/src/g_strife/a_loremaster.cpp deleted file mode 100644 index 0f1bef7cc..000000000 --- a/src/g_strife/a_loremaster.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* -#include "actor.h" -#include "a_action.h" -#include "a_strifeglobal.h" -#include "m_random.h" -#include "p_local.h" -#include "s_sound.h" -#include "vm.h" -*/ - -// Loremaster (aka Priest) -------------------------------------------------- - -class ALoreShot : public AActor -{ - DECLARE_CLASS (ALoreShot, AActor) -public: - int DoSpecialDamage (AActor *victim, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(ALoreShot, false, false, false, false) - -int ALoreShot::DoSpecialDamage (AActor *victim, int damage, FName damagetype) -{ - - if (victim != NULL && target != NULL && !(victim->flags7 & MF7_DONTTHRUST)) - { - DVector3 thrust = victim->Vec3To(target); - thrust.MakeResize(255. * 50 / MAX(victim->Mass, 1)); - victim->Vel += thrust; - } - return damage; -} - -DEFINE_ACTION_FUNCTION(AActor, A_LoremasterChain) -{ - PARAM_SELF_PROLOGUE(AActor); - - S_Sound (self, CHAN_BODY, "loremaster/active", 1, ATTN_NORM); - Spawn("LoreShot2", self->Pos(), ALLOW_REPLACE); - Spawn("LoreShot2", self->Vec3Offset(-self->Vel/2.), ALLOW_REPLACE); - Spawn("LoreShot2", self->Vec3Offset(-self->Vel), ALLOW_REPLACE); - return 0; -} diff --git a/src/g_strife/a_oracle.cpp b/src/g_strife/a_oracle.cpp deleted file mode 100644 index f49849922..000000000 --- a/src/g_strife/a_oracle.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* -#include "actor.h" -#include "a_action.h" -#include "a_strifeglobal.h" -#include "p_enemy.h" -#include "r_defs.h" -#include "vm.h" -*/ - - -DEFINE_ACTION_FUNCTION(AActor, A_WakeOracleSpectre) -{ - PARAM_SELF_PROLOGUE(AActor); - - TThinkerIterator it(NAME_AlienSpectre3); - AActor *spectre = it.Next(); - - if (spectre != NULL && spectre->health > 0 && self->target != spectre) - { - spectre->Sector->SoundTarget = spectre->LastHeard = self->LastHeard; - spectre->target = self->target; - spectre->SetState (spectre->SeeState); - } - return 0; -} - diff --git a/src/g_strife/a_programmer.cpp b/src/g_strife/a_programmer.cpp deleted file mode 100644 index c00e7fb01..000000000 --- a/src/g_strife/a_programmer.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "a_strifeglobal.h" -#include "vm.h" -#include "g_level.h" -#include "doomstat.h" -*/ -static FRandom pr_prog ("Programmer"); - -// The Programmer level ending thing ---------------------------------------- -// [RH] I took some liberties to make this "cooler" than it was in Strife. - -class AProgLevelEnder : public AInventory -{ - DECLARE_CLASS (AProgLevelEnder, AInventory) -public: - void Tick (); - PalEntry GetBlend (); -}; - -IMPLEMENT_CLASS(AProgLevelEnder, false, false, false, false) - -//============================================================================ -// -// AProgLevelEnder :: Tick -// -// Fade to black, end the level, then unfade. -// -//============================================================================ - -void AProgLevelEnder::Tick () -{ - if (special2 == 0) - { // fade out over .66 second - special1 += 255 / (TICRATE*2/3); - if (++special1 >= 255) - { - special1 = 255; - special2 = 1; - G_ExitLevel (0, false); - } - } - else - { // fade in over two seconds - special1 -= 255 / (TICRATE*2); - if (special1 <= 0) - { - Destroy (); - } - } -} - -//============================================================================ -// -// AProgLevelEnder :: GetBlend -// -//============================================================================ - -PalEntry AProgLevelEnder::GetBlend () -{ - return PalEntry ((BYTE)special1, 0, 0, 0); -} - -//============================================================================ -// -// A_ProgrammerMelee -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ProgrammerMelee) -{ - PARAM_SELF_PROLOGUE(AActor); - - int damage; - - if (self->target == NULL) - return 0; - - A_FaceTarget (self); - - if (!self->CheckMeleeRange ()) - return 0; - - S_Sound (self, CHAN_WEAPON, "programmer/clank", 1, ATTN_NORM); - - damage = ((pr_prog() % 10) + 1) * 6; - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - return 0; -} - -//============================================================================ -// -// A_SpotLightning -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SpotLightning) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *spot; - - if (self->target == NULL) - return 0; - - spot = Spawn("SpectralLightningSpot", self->target->PosAtZ(self->target->floorz), ALLOW_REPLACE); - if (spot != NULL) - { - spot->threshold = 25; - spot->target = self; - spot->FriendPlayer = 0; - spot->tracer = self->target; - } - return 0; -} - -//============================================================================ -// -// A_SpawnProgrammerBase -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnProgrammerBase) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *foo = Spawn("ProgrammerBase", self->PosPlusZ(24.), ALLOW_REPLACE); - if (foo != NULL) - { - foo->Angles.Yaw = self->Angles.Yaw + 180. + pr_prog.Random2() * (360. / 1024.); - foo->VelFromAngle(); - foo->Vel.Z = pr_prog() / 128.; - } - return 0; -} - -//============================================================================ -// -// A_ProgrammerDeath -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ProgrammerDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (!CheckBossDeath (self)) - return 0; - - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].health > 0) - { - players[i].mo->GiveInventoryType (RUNTIME_CLASS(AProgLevelEnder)); - break; - } - } - // the sky change scripts are now done as special actions in MAPINFO - A_BossDeath(self); - return 0; -} diff --git a/src/g_strife/a_reaver.cpp b/src/g_strife/a_reaver.cpp deleted file mode 100644 index 9c6e9f6b9..000000000 --- a/src/g_strife/a_reaver.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* -#include "actor.h" -#include "p_enemy.h" -#include "a_action.h" -#include "s_sound.h" -#include "m_random.h" -#include "p_local.h" -#include "a_strifeglobal.h" -#include "vm.h" -*/ - -static FRandom pr_reaverattack ("ReaverAttack"); - -DEFINE_ACTION_FUNCTION(AActor, A_ReaverRanged) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->target != NULL) - { - DAngle bangle; - DAngle pitch; - - A_FaceTarget (self); - S_Sound (self, CHAN_WEAPON, "reaver/attack", 1, ATTN_NORM); - bangle = self->Angles.Yaw; - pitch = P_AimLineAttack (self, bangle, MISSILERANGE); - - for (int i = 0; i < 3; ++i) - { - DAngle angle = bangle + pr_reaverattack.Random2() * (22.5 / 256); - int damage = ((pr_reaverattack() & 7) + 1) * 3; - P_LineAttack (self, angle, MISSILERANGE, pitch, damage, NAME_Hitscan, NAME_StrifePuff); - } - } - return 0; -} diff --git a/src/g_strife/a_rebels.cpp b/src/g_strife/a_rebels.cpp deleted file mode 100644 index 7f21886bf..000000000 --- a/src/g_strife/a_rebels.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "gi.h" -#include "a_sharedglobal.h" -#include "a_strifeglobal.h" -#include "vm.h" -#include "doomstat.h" -*/ - -static FRandom pr_shootgun ("ShootGun"); - -//============================================================================ -// -// A_ShootGun -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ShootGun) -{ - PARAM_SELF_PROLOGUE(AActor); - - DAngle pitch; - - if (self->target == NULL) - return 0; - - S_Sound (self, CHAN_WEAPON, "monsters/rifle", 1, ATTN_NORM); - A_FaceTarget (self); - pitch = P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE); - P_LineAttack (self, self->Angles.Yaw + pr_shootgun.Random2() * (11.25 / 256), - MISSILERANGE, pitch, - 3*(pr_shootgun() % 5 + 1), NAME_Hitscan, NAME_StrifePuff); - return 0; -} - -// Teleporter Beacon -------------------------------------------------------- - -class ATeleporterBeacon : public AInventory -{ - DECLARE_CLASS (ATeleporterBeacon, AInventory) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(ATeleporterBeacon, false, false, false, false) - -bool ATeleporterBeacon::Use (bool pickup) -{ - AInventory *drop; - - // Increase the amount by one so that when DropInventory decrements it, - // the actor will have the same number of beacons that he started with. - // When we return to UseInventory, it will take care of decrementing - // Amount again and disposing of this item if there are no more. - Amount++; - drop = Owner->DropInventory (this); - if (drop == NULL) - { - Amount--; - return false; - } - else - { - drop->SetState(drop->FindState(NAME_Drop)); - drop->target = Owner; - return true; - } -} - -DEFINE_ACTION_FUNCTION(AActor, A_Beacon) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *owner = self->target; - AActor *rebel; - - rebel = Spawn("Rebel1", self->PosAtZ(self->floorz), ALLOW_REPLACE); - if (!P_TryMove (rebel, rebel->Pos(), true)) - { - rebel->Destroy (); - return 0; - } - // Once the rebels start teleporting in, you can't pick up the beacon anymore. - self->flags &= ~MF_SPECIAL; - static_cast(self)->DropTime = 0; - // Set up the new rebel. - rebel->threshold = rebel->DefThreshold; - rebel->target = NULL; - rebel->flags4 |= MF4_INCOMBAT; - rebel->LastHeard = owner; // Make sure the rebels look for targets - if (deathmatch) - { - rebel->health *= 2; - } - if (owner != NULL) - { - // Rebels are the same color as their owner (but only in multiplayer) - if (multiplayer) - { - rebel->Translation = owner->Translation; - } - rebel->SetFriendPlayer(owner->player); - // Set the rebel's target to whatever last hurt the player, so long as it's not - // one of the player's other rebels. - if (owner->target != NULL && !rebel->IsFriend (owner->target)) - { - rebel->target = owner->target; - } - } - - rebel->SetState (rebel->SeeState); - rebel->Angles.Yaw = self->Angles.Yaw; - P_SpawnTeleportFog(rebel, rebel->Vec3Angle(20., self->Angles.Yaw, 0), false, true); - if (--self->health < 0) - { - self->SetState(self->FindState(NAME_Death)); - } - return 0; -} diff --git a/src/g_strife/a_sentinel.cpp b/src/g_strife/a_sentinel.cpp deleted file mode 100644 index 44946c84a..000000000 --- a/src/g_strife/a_sentinel.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* -#include "actor.h" -#include "p_enemy.h" -#include "a_action.h" -#include "p_local.h" -#include "m_random.h" -#include "vm.h" -*/ - -static FRandom pr_sentinelrefire ("SentinelRefire"); - -DEFINE_ACTION_FUNCTION(AActor, A_SentinelBob) -{ - PARAM_SELF_PROLOGUE(AActor); - - double minz, maxz; - - if (self->flags & MF_INFLOAT) - { - self->Vel.Z = 0; - return 0; - } - if (self->threshold != 0) - return 0; - - maxz = self->ceilingz - self->Height - 16; - minz = self->floorz + 96; - if (minz > maxz) - { - minz = maxz; - } - if (minz < self->Z()) - { - self->Vel.Z -= 1; - } - else - { - self->Vel.Z += 1; - } - self->reactiontime = (minz >= self->Z()) ? 4 : 0; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SentinelAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *missile, *trail; - - // [BB] Without a target the P_SpawnMissileZAimed call will crash. - if (!self->target) - { - return 0; - } - - missile = P_SpawnMissileZAimed (self, self->Z() + 32, self->target, PClass::FindActor("SentinelFX2")); - - if (missile != NULL && (missile->Vel.X != 0 || missile->Vel.Y != 0)) - { - for (int i = 8; i > 1; --i) - { - trail = Spawn("SentinelFX1", - self->Vec3Angle(missile->radius*i, missile->Angles.Yaw, 32 + missile->Vel.Z / 4 * i), ALLOW_REPLACE); - if (trail != NULL) - { - trail->target = self; - trail->Vel = missile->Vel; - P_CheckMissileSpawn (trail, self->radius); - } - } - missile->AddZ(missile->Vel.Z / 4); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SentinelRefire) -{ - PARAM_SELF_PROLOGUE(AActor); - - A_FaceTarget (self); - - if (pr_sentinelrefire() >= 30) - { - if (self->target == NULL || - self->target->health <= 0 || - !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) || - P_HitFriend(self) || - (self->MissileState == NULL && !self->CheckMeleeRange()) || - pr_sentinelrefire() < 40) - { - self->SetState (self->SeeState); - } - } - return 0; -} diff --git a/src/g_strife/a_spectral.cpp b/src/g_strife/a_spectral.cpp deleted file mode 100644 index c7012accf..000000000 --- a/src/g_strife/a_spectral.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "s_sound.h" -#include "m_random.h" -#include "a_strifeglobal.h" -#include "vm.h" -*/ - -class ASpectralMonster : public AActor -{ - DECLARE_CLASS (ASpectralMonster, AActor) -public: - void Touch (AActor *toucher); -}; - -IMPLEMENT_CLASS(ASpectralMonster, false, false, false, false) - -void ASpectralMonster::Touch (AActor *toucher) -{ - P_DamageMobj (toucher, this, this, 5, NAME_Melee); -} - - -DEFINE_ACTION_FUNCTION(AActor, A_SpectralLightningTail) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *foo = Spawn("SpectralLightningHTail", self->Vec3Offset(-self->Vel.X, -self->Vel.Y, 0.), ALLOW_REPLACE); - - foo->Angles.Yaw = self->Angles.Yaw; - foo->FriendPlayer = self->FriendPlayer; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SpectralBigBallLightning) -{ - PARAM_SELF_PROLOGUE(AActor); - - PClassActor *cls = PClass::FindActor("SpectralLightningH3"); - if (cls) - { - self->Angles.Yaw += 90.; - P_SpawnSubMissile (self, cls, self->target); - self->Angles.Yaw += 180.; - P_SpawnSubMissile (self, cls, self->target); - self->Angles.Yaw -= 270.; - P_SpawnSubMissile (self, cls, self->target); - } - return 0; -} - -static FRandom pr_zap5 ("Zap5"); - -DEFINE_ACTION_FUNCTION(AActor, A_SpectralLightning) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *flash; - - if (self->threshold != 0) - --self->threshold; - - self->Vel.X += pr_zap5.Random2(3); - self->Vel.Y += pr_zap5.Random2(3); - - double xo = pr_zap5.Random2(3) * 50.; - double yo = pr_zap5.Random2(3) * 50.; - - flash = Spawn (self->threshold > 25 ? PClass::FindActor(NAME_SpectralLightningV2) : - PClass::FindActor(NAME_SpectralLightningV1), self->Vec2OffsetZ(xo, yo, ONCEILINGZ), ALLOW_REPLACE); - - flash->target = self->target; - flash->Vel.Z = -18; - flash->FriendPlayer = self->FriendPlayer; - - flash = Spawn(NAME_SpectralLightningV2, self->PosAtZ(ONCEILINGZ), ALLOW_REPLACE); - - flash->target = self->target; - flash->Vel.Z = -18; - flash->FriendPlayer = self->FriendPlayer; - return 0; -} - -// In Strife, this number is stored in the data segment, but it doesn't seem to be -// altered anywhere. -#define TRACEANGLE (19.6875) - -DEFINE_ACTION_FUNCTION(AActor, A_Tracer2) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *dest; - double dist; - double slope; - - dest = self->tracer; - - if (!dest || dest->health <= 0 || self->Speed == 0 || !self->CanSeek(dest)) - return 0; - - DAngle exact = self->AngleTo(dest); - DAngle diff = deltaangle(self->Angles.Yaw, exact); - - if (diff < 0) - { - self->Angles.Yaw -= TRACEANGLE; - if (deltaangle(self->Angles.Yaw, exact) > 0) - self->Angles.Yaw = exact; - } - else if (diff > 0) - { - self->Angles.Yaw += TRACEANGLE; - if (deltaangle(self->Angles.Yaw, exact) < 0.) - self->Angles.Yaw = exact; - } - - self->VelFromAngle(); - - if (!(self->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))) - { - // change slope - dist = self->DistanceBySpeed (dest, self->Speed); - if (dest->Height >= 56) - { - slope = (dest->Z()+40 - self->Z()) / dist; - } - else - { - slope = (dest->Z() + self->Height*(2./3) - self->Z()) / dist; - } - if (slope < self->Vel.Z) - { - self->Vel.Z -= 1 / 8.; - } - else - { - self->Vel.Z += 1 / 8.; - } - } - return 0; -} diff --git a/src/g_strife/a_stalker.cpp b/src/g_strife/a_stalker.cpp deleted file mode 100644 index 9189ef8de..000000000 --- a/src/g_strife/a_stalker.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "vm.h" -*/ - -static FRandom pr_stalker ("Stalker"); - - -DEFINE_ACTION_FUNCTION(AActor, A_StalkerChaseDecide) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (!(self->flags & MF_NOGRAVITY)) - { - self->SetState (self->FindState("SeeFloor")); - } - else if (self->ceilingz > self->Top()) - { - self->SetState (self->FindState("Drop")); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_StalkerLookInit) -{ - PARAM_SELF_PROLOGUE(AActor); - - FState *state; - if (self->flags & MF_NOGRAVITY) - { - state = self->FindState("LookCeiling"); - } - else - { - state = self->FindState("LookFloor"); - } - if (self->state->NextState != state) - { - self->SetState (state); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_StalkerDrop) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->flags5 &= ~MF5_NOVERTICALMELEERANGE; - self->flags &= ~MF_NOGRAVITY; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_StalkerAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->flags & MF_NOGRAVITY) - { - self->SetState (self->FindState("Drop")); - } - else if (self->target != NULL) - { - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - int damage = (pr_stalker() & 7) * 2 + 2; - - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_StalkerWalk) -{ - PARAM_SELF_PROLOGUE(AActor); - - S_Sound (self, CHAN_BODY, "stalker/walk", 1, ATTN_NORM); - A_Chase (stack, self); - return 0; -} - diff --git a/src/g_strife/a_strifeglobal.h b/src/g_strife/a_strifeglobal.h deleted file mode 100644 index 9e7f9148e..000000000 --- a/src/g_strife/a_strifeglobal.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef __A_STRIFEGLOBAL_H__ -#define __A_STRIFEGLOBAL_H__ - -#include "info.h" -#include "a_pickups.h" - -// Base class for every humanoid in Strife that can go into -// a fire or electric death. -class ADegninOre : public AInventory -{ - DECLARE_CLASS (ADegninOre, AInventory) -public: - bool Use (bool pickup); -}; - -class ACoin : public AInventory -{ - DECLARE_CLASS (ACoin, AInventory) -public: - const char *PickupMessage (); - bool HandlePickup (AInventory *item); - AInventory *CreateTossable (); - AInventory *CreateCopy (AActor *other); -}; - -class ADummyStrifeItem : public AInventory -{ - DECLARE_CLASS (ADummyStrifeItem, AInventory) -}; - -class AUpgradeStamina : public ADummyStrifeItem -{ - DECLARE_CLASS (AUpgradeStamina, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); -}; - -class AUpgradeAccuracy : public ADummyStrifeItem -{ - DECLARE_CLASS (AUpgradeAccuracy, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); -}; - -class ASlideshowStarter : public ADummyStrifeItem -{ - DECLARE_CLASS (ASlideshowStarter, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); -}; - -class ASigil : public AWeapon -{ - DECLARE_CLASS (ASigil, AWeapon) -public: - bool HandlePickup (AInventory *item); - AInventory *CreateCopy (AActor *other); - - void Serialize(FSerializer &arc); - bool SpecialDropAction (AActor *dropper); - static int GiveSigilPiece (AActor *daPlayer); - void BeginPlay(); - - int NumPieces, DownPieces; -}; - -extern PClassActor *QuestItemClasses[31]; - -#endif diff --git a/src/g_strife/a_strifeitems.cpp b/src/g_strife/a_strifeitems.cpp deleted file mode 100644 index c1b9d75d6..000000000 --- a/src/g_strife/a_strifeitems.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/* -#include "info.h" -#include "a_pickups.h" -#include "d_player.h" -#include "gstrings.h" -#include "p_local.h" -#include "p_spec.h" -#include "a_strifeglobal.h" -#include "p_lnspec.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "d_event.h" -#include "a_keys.h" -#include "c_console.h" -#include "templates.h" -#include "vm.h" -#include "g_level.h" -#include "doomstat.h" -*/ -// Degnin Ore --------------------------------------------------------------- - -IMPLEMENT_CLASS(ADegninOre, false, false, false, false) - -DEFINE_ACTION_FUNCTION(AActor, A_RemoveForceField) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->flags &= ~MF_SPECIAL; - - for (int i = 0; i < self->Sector->linecount; ++i) - { - line_t *line = self->Sector->lines[i]; - if (line->backsector != NULL && line->special == ForceField) - { - line->flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING); - line->special = 0; - line->sidedef[0]->SetTexture(side_t::mid, FNullTextureID()); - line->sidedef[1]->SetTexture(side_t::mid, FNullTextureID()); - } - } - return 0; -} - -bool ADegninOre::Use (bool pickup) -{ - if (pickup) - { - return false; - } - else - { - AInventory *drop; - - // Increase the amount by one so that when DropInventory decrements it, - // the actor will have the same number of beacons that he started with. - // When we return to UseInventory, it will take care of decrementing - // Amount again and disposing of this item if there are no more. - Amount++; - drop = Owner->DropInventory (this); - if (drop == NULL) - { - Amount--; - return false; - } - return true; - } -} - -// Health Training ---------------------------------------------------------- - -class AHealthTraining : public AInventory -{ - DECLARE_CLASS (AHealthTraining, AInventory) -public: - bool TryPickup (AActor *&toucher); -}; - -IMPLEMENT_CLASS(AHealthTraining, false, false, false, false) - -bool AHealthTraining::TryPickup (AActor *&toucher) -{ - if (Super::TryPickup (toucher)) - { - toucher->GiveInventoryType (PClass::FindActor("GunTraining")); - AInventory *coin = Spawn (); - if (coin != NULL) - { - coin->Amount = toucher->player->mo->accuracy*5 + 300; - if (!coin->CallTryPickup (toucher)) - { - coin->Destroy (); - } - } - return true; - } - return false; -} - -// Scanner ------------------------------------------------------------------ - -class AScanner : public APowerupGiver -{ - DECLARE_CLASS (AScanner, APowerupGiver) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AScanner, false, false, false, false) - -bool AScanner::Use (bool pickup) -{ - if (!(level.flags2 & LEVEL2_ALLMAP)) - { - if (Owner->CheckLocalView (consoleplayer)) - { - C_MidPrint(SmallFont, GStrings("TXT_NEEDMAP")); - } - return false; - } - return Super::Use (pickup); -} - -// Prison Pass -------------------------------------------------------------- - -class APrisonPass : public AKey -{ - DECLARE_CLASS (APrisonPass, AKey) -public: - bool TryPickup (AActor *&toucher); - bool SpecialDropAction (AActor *dropper); -}; - -IMPLEMENT_CLASS(APrisonPass, false, false, false, false) - -bool APrisonPass::TryPickup (AActor *&toucher) -{ - Super::TryPickup (toucher); - EV_DoDoor (DDoor::doorOpen, NULL, toucher, 223, 2., 0, 0, 0); - toucher->GiveInventoryType (QuestItemClasses[9]); - return true; -} - -//============================================================================ -// -// APrisonPass :: SpecialDropAction -// -// Trying to make a monster that drops a prison pass turns it into an -// OpenDoor223 item instead. That means the only way to get it in Strife -// is through dialog, which is why it doesn't have its own sprite. -// -//============================================================================ - -bool APrisonPass::SpecialDropAction (AActor *dropper) -{ - EV_DoDoor (DDoor::doorOpen, NULL, dropper, 223, 2., 0, 0, 0); - Destroy (); - return true; -} - - -//--------------------------------------------------------------------------- -// Dummy items. They are just used by Strife to perform --------------------- -// actions and cannot be held. ---------------------------------------------- -//--------------------------------------------------------------------------- - -IMPLEMENT_CLASS(ADummyStrifeItem, false, false, false, false) - -// Sound the alarm! --------------------------------------------------------- - -class ARaiseAlarm : public ADummyStrifeItem -{ - DECLARE_CLASS (ARaiseAlarm, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); - bool SpecialDropAction (AActor *dropper); -}; - -IMPLEMENT_CLASS(ARaiseAlarm, false, false, false, false) - -bool ARaiseAlarm::TryPickup (AActor *&toucher) -{ - P_NoiseAlert (toucher, toucher); - VMFrameStack stack1, *stack = &stack1; - CALL_ACTION(A_WakeOracleSpectre, toucher); - GoAwayAndDie (); - return true; -} - -bool ARaiseAlarm::SpecialDropAction (AActor *dropper) -{ - if (dropper->target != nullptr) - { - P_NoiseAlert(dropper->target, dropper->target); - if (dropper->target->CheckLocalView(consoleplayer)) - { - Printf("You Fool! You've set off the alarm.\n"); - } - } - Destroy (); - return true; -} - -// Open door tag 222 -------------------------------------------------------- - -class AOpenDoor222 : public ADummyStrifeItem -{ - DECLARE_CLASS (AOpenDoor222, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); -}; - -IMPLEMENT_CLASS(AOpenDoor222, false, false, false, false) - -bool AOpenDoor222::TryPickup (AActor *&toucher) -{ - EV_DoDoor (DDoor::doorOpen, NULL, toucher, 222, 2., 0, 0, 0); - GoAwayAndDie (); - return true; -} - -// Close door tag 222 ------------------------------------------------------- - -class ACloseDoor222 : public ADummyStrifeItem -{ - DECLARE_CLASS (ACloseDoor222, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); - bool SpecialDropAction (AActor *dropper); -}; - -IMPLEMENT_CLASS(ACloseDoor222, false, false, false, false) - -bool ACloseDoor222::TryPickup (AActor *&toucher) -{ - EV_DoDoor (DDoor::doorClose, NULL, toucher, 222, 2., 0, 0, 0); - GoAwayAndDie (); - return true; -} - -bool ACloseDoor222::SpecialDropAction (AActor *dropper) -{ - EV_DoDoor (DDoor::doorClose, NULL, dropper, 222, 2., 0, 0, 0); - if (dropper->target != nullptr) - { - if (dropper->target->CheckLocalView(consoleplayer)) - { - Printf("You're dead! You set off the alarm.\n"); - } - P_NoiseAlert(dropper->target, dropper->target); - } - Destroy (); - return true; -} - -// Open door tag 224 -------------------------------------------------------- - -class AOpenDoor224 : public ADummyStrifeItem -{ - DECLARE_CLASS (AOpenDoor224, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); - bool SpecialDropAction (AActor *dropper); -}; - -IMPLEMENT_CLASS(AOpenDoor224, false, false, false, false) - -bool AOpenDoor224::TryPickup (AActor *&toucher) -{ - EV_DoDoor (DDoor::doorOpen, NULL, toucher, 224, 2., 0, 0, 0); - GoAwayAndDie (); - return true; -} - -bool AOpenDoor224::SpecialDropAction (AActor *dropper) -{ - EV_DoDoor (DDoor::doorOpen, NULL, dropper, 224, 2., 0, 0, 0); - Destroy (); - return true; -} - -// Ammo --------------------------------------------------------------------- - -class AAmmoFillup : public ADummyStrifeItem -{ - DECLARE_CLASS (AAmmoFillup, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); -}; - -IMPLEMENT_CLASS(AAmmoFillup, false, false, false, false) - -bool AAmmoFillup::TryPickup (AActor *&toucher) -{ - PClassActor *clip = PClass::FindActor(NAME_ClipOfBullets); - if (clip != NULL) - { - AInventory *item = toucher->FindInventory(clip); - if (item == NULL) - { - item = toucher->GiveInventoryType (clip); - if (item != NULL) - { - item->Amount = 50; - } - } - else if (item->Amount < 50) - { - item->Amount = 50; - } - else - { - return false; - } - GoAwayAndDie (); - } - return true; -} - -// Health ------------------------------------------------------------------- - -class AHealthFillup : public ADummyStrifeItem -{ - DECLARE_CLASS (AHealthFillup, ADummyStrifeItem) -public: - bool TryPickup (AActor *&toucher); -}; - -IMPLEMENT_CLASS(AHealthFillup, false, false, false, false) - -bool AHealthFillup::TryPickup (AActor *&toucher) -{ - static const int skillhealths[5] = { -100, -75, -50, -50, -100 }; - - int index = clamp(gameskill, 0,4); - if (!P_GiveBody (toucher, skillhealths[index])) - { - return false; - } - GoAwayAndDie (); - return true; -} - -// Upgrade Stamina ---------------------------------------------------------- - -IMPLEMENT_CLASS(AUpgradeStamina, false, false, false, false) - -bool AUpgradeStamina::TryPickup (AActor *&toucher) -{ - if (toucher->player == NULL) - return false; - - toucher->player->mo->stamina += Amount; - if (toucher->player->mo->stamina >= MaxAmount) - toucher->player->mo->stamina = MaxAmount; - - P_GiveBody (toucher, -100); - GoAwayAndDie (); - return true; -} - -// Upgrade Accuracy --------------------------------------------------------- - -IMPLEMENT_CLASS(AUpgradeAccuracy, false, false, false, false) - -bool AUpgradeAccuracy::TryPickup (AActor *&toucher) -{ - if (toucher->player == NULL || toucher->player->mo->accuracy >= 100) - return false; - toucher->player->mo->accuracy += 10; - GoAwayAndDie (); - return true; -} - -// Start a slideshow -------------------------------------------------------- - -IMPLEMENT_CLASS(ASlideshowStarter, false, false, false, false) - -bool ASlideshowStarter::TryPickup (AActor *&toucher) -{ - gameaction = ga_slideshow; - if (level.levelnum == 10) - { - toucher->GiveInventoryType (QuestItemClasses[16]); - } - GoAwayAndDie (); - return true; -} diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp deleted file mode 100644 index 1580f127f..000000000 --- a/src/g_strife/a_strifestuff.cpp +++ /dev/null @@ -1,425 +0,0 @@ -#include "actor.h" -#include "g_level.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_strifeglobal.h" -#include "p_enemy.h" -#include "p_lnspec.h" -#include "c_console.h" -#include "vm.h" -#include "doomstat.h" -#include "gstrings.h" -#include "a_keys.h" -#include "a_sharedglobal.h" -#include "templates.h" -#include "d_event.h" -#include "v_font.h" -#include "serializer.h" -#include "p_spec.h" -#include "portal.h" -#include "vm.h" - -// Include all the other Strife stuff here to reduce compile time -#include "a_acolyte.cpp" -#include "a_spectral.cpp" -#include "a_alienspectres.cpp" -#include "a_coin.cpp" -#include "a_crusader.cpp" -#include "a_entityboss.cpp" -#include "a_inquisitor.cpp" -#include "a_loremaster.cpp" -//#include "a_macil.cpp" -#include "a_oracle.cpp" -#include "a_programmer.cpp" -#include "a_reaver.cpp" -#include "a_rebels.cpp" -#include "a_sentinel.cpp" -#include "a_stalker.cpp" -#include "a_strifeitems.cpp" -#include "a_strifeweapons.cpp" -#include "a_templar.cpp" -#include "a_thingstoblowup.cpp" - -// Notes so I don't forget them: -// Strife does some extra stuff in A_Explode if a player caused the explosion. (probably NoiseAlert) -// See the instructions @ 21249. -// -// Strife's FLOATSPEED is 5 and not 4. -// -// In P_CheckMissileRange, mobjtypes 53,54,55,56,57,58 shift the distance right 4 bits (some, but not all the acolytes) -// mobjtypes 61,63,91 shift it right 1 bit -// -// When shooting missiles at something, if MF_SHADOW is set, the angle is adjusted with the formula: -// angle += pr_spawnmissile.Random2() << 21 -// When MF_STRIFEx4000000 is set, the angle is adjusted similarly: -// angle += pr_spawnmissile.Random2() << 22 -// Note that these numbers are different from those used by all the other Doom engine games. - -static FRandom pr_gibtosser ("GibTosser"); - -// Force Field Guard -------------------------------------------------------- - -void A_RemoveForceField (AActor *); - -class AForceFieldGuard : public AActor -{ - DECLARE_CLASS (AForceFieldGuard, AActor) -public: - int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(AForceFieldGuard, false, false, false, false) - -int AForceFieldGuard::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype) -{ - if (inflictor == NULL || !inflictor->IsKindOf (RUNTIME_CLASS(ADegninOre))) - { - return -1; - } - return health; -} - -// Kneeling Guy ------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_SetShadow) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->flags |= MF_STRIFEx8000000|MF_SHADOW; - self->RenderStyle = STYLE_Translucent; - self->Alpha = HR_SHADOW; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_ClearShadow) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->flags &= ~(MF_STRIFEx8000000|MF_SHADOW); - self->RenderStyle = STYLE_Normal; - self->Alpha = 1.; - return 0; -} - -static FRandom pr_gethurt ("HurtMe!"); - -DEFINE_ACTION_FUNCTION(AActor, A_GetHurt) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->flags4 |= MF4_INCOMBAT; - if ((pr_gethurt() % 5) == 0) - { - S_Sound (self, CHAN_VOICE, self->PainSound, 1, ATTN_NORM); - self->health--; - } - if (self->health <= 0) - { - self->Die (self->target, self->target); - } - return 0; -} - -// Klaxon Warning Light ----------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_TurretLook) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *target; - - if (self->flags5 & MF5_INCONVERSATION) - return 0; - - self->threshold = 0; - target = self->LastHeard; - if (target != NULL && - target->health > 0 && - target->flags & MF_SHOOTABLE && - (self->flags & MF_FRIENDLY) != (target->flags & MF_FRIENDLY)) - { - self->target = target; - if ((self->flags & MF_AMBUSH) && !P_CheckSight (self, target)) - { - return 0; - } - if (self->SeeSound != 0) - { - S_Sound (self, CHAN_VOICE, self->SeeSound, 1, ATTN_NORM); - } - self->LastHeard = NULL; - self->threshold = 10; - self->SetState (self->SeeState); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_KlaxonBlare) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (--self->reactiontime < 0) - { - self->target = NULL; - self->reactiontime = self->GetDefault()->reactiontime; - CALL_ACTION(A_TurretLook, self); - if (self->target == NULL) - { - self->SetIdle(); - } - else - { - self->reactiontime = 50; - } - } - if (self->reactiontime == 2) - { - // [RH] Unalert monsters near the alarm and not just those in the same sector as it. - P_NoiseAlert (NULL, self, false); - } - else if (self->reactiontime > 50) - { - S_Sound (self, CHAN_VOICE, "misc/alarm", 1, ATTN_NORM); - } - return 0; -} - -// Power Coupling ----------------------------------------------------------- - -class APowerCoupling : public AActor -{ - DECLARE_CLASS (APowerCoupling, AActor) -public: - void Die (AActor *source, AActor *inflictor, int dmgflags); -}; - -IMPLEMENT_CLASS(APowerCoupling, false, false, false, false) - -void APowerCoupling::Die (AActor *source, AActor *inflictor, int dmgflags) -{ - Super::Die (source, inflictor, dmgflags); - - int i; - - for (i = 0; i < MAXPLAYERS; ++i) - if (playeringame[i] && players[i].health > 0) - break; - - if (i == MAXPLAYERS) - return; - - // [RH] In case the player broke it with the dagger, alert the guards now. - if (LastHeard != source) - { - P_NoiseAlert (source, this); - } - EV_DoDoor (DDoor::doorClose, NULL, players[i].mo, 225, 2., 0, 0, 0); - EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, 1., 0., -1, 0, false); - players[i].mo->GiveInventoryType (QuestItemClasses[5]); - S_Sound (CHAN_VOICE, "svox/voc13", 1, ATTN_NORM); - players[i].SetLogNumber (13); - P_DropItem (this, PClass::FindActor("BrokenPowerCoupling"), -1, 256); - Destroy (); -} - -// Gibs for things that bleed ----------------------------------------------- - -class AMeat : public AActor -{ - DECLARE_CLASS (AMeat, AActor) -public: - void BeginPlay () - { - // Strife used mod 19, but there are 20 states. Hmm. - SetState (SpawnState + pr_gibtosser() % 20); - } -}; - -IMPLEMENT_CLASS(AMeat, false, false, false, false) - -//========================================================================== -// -// A_TossGib -// -//========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_TossGib) -{ - PARAM_SELF_PROLOGUE(AActor); - - const char *gibtype = (self->flags & MF_NOBLOOD) ? "Junk" : "Meat"; - AActor *gib = Spawn (gibtype, self->PosPlusZ(24.), ALLOW_REPLACE); - - if (gib == NULL) - { - return 0; - } - - gib->Angles.Yaw = pr_gibtosser() * (360 / 256.f); - gib->VelFromAngle(pr_gibtosser() & 15); - gib->Vel.Z = pr_gibtosser() & 15; - return 0; -} - -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FLoopActiveSound) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->ActiveSound != 0 && !(level.time & 7)) - { - S_Sound (self, CHAN_VOICE, self->ActiveSound, 1, ATTN_NORM); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_Countdown) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (--self->reactiontime <= 0) - { - P_ExplodeMissile (self, NULL, NULL); - self->flags &= ~MF_SKULLFLY; - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_LoopActiveSound) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->ActiveSound != 0 && !S_IsActorPlayingSomething (self, CHAN_VOICE, -1)) - { - S_Sound (self, CHAN_VOICE|CHAN_LOOP, self->ActiveSound, 1, ATTN_NORM); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain) -{ - PARAM_SELF_PROLOGUE(AActor); - - sector_t *sec = self->Sector; - - if (self->Z() == sec->floorplane.ZatPoint(self) && sec->PortalBlocksMovement(sector_t::floor)) - { - if (sec->special == Damage_InstantDeath) - { - P_DamageMobj (self, NULL, NULL, 999, NAME_InstantDeath); - } - else if (sec->special == Scroll_StrifeCurrent) - { - int anglespeed = tagManager.GetFirstSectorTag(sec) - 100; - double speed = (anglespeed % 10) / 16.; - DAngle an = (anglespeed / 10) * (360 / 8.); - self->Thrust(an, speed); - } - } - return 0; -} - -//============================================================================ -// -// A_ClearSoundTarget -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ClearSoundTarget) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *actor; - - self->Sector->SoundTarget = NULL; - for (actor = self->Sector->thinglist; actor != NULL; actor = actor->snext) - { - actor->LastHeard = NULL; - } - return 0; -} - - -DEFINE_ACTION_FUNCTION(AActor, A_ItBurnsItBurns) -{ - PARAM_SELF_PROLOGUE(AActor); - - S_Sound (self, CHAN_VOICE, "human/imonfire", 1, ATTN_NORM); - - if (self->player != nullptr && self->player->mo == self) - { - P_SetPsprite(self->player, PSP_STRIFEHANDS, self->FindState("FireHands")); - - self->player->ReadyWeapon = nullptr; - self->player->PendingWeapon = WP_NOCHANGE; - self->player->playerstate = PST_LIVE; - self->player->extralight = 3; - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_DropFire) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *drop = Spawn("FireDroplet", self->PosPlusZ(24.), ALLOW_REPLACE); - drop->Vel.Z = -1.; - P_RadiusAttack (self, self, 64, 64, NAME_Fire, 0); - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_CrispyPlayer) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->player != nullptr && self->player->mo == self) - { - DPSprite *psp; - psp = self->player->GetPSprite(PSP_STRIFEHANDS); - - FState *firehandslower = self->FindState("FireHandsLower"); - FState *firehands = self->FindState("FireHands"); - FState *state = psp->GetState(); - - if (state != nullptr && firehandslower != nullptr && firehands != nullptr && firehands < firehandslower) - { - self->player->playerstate = PST_DEAD; - psp->SetState(state + (firehandslower - firehands)); - } - else if (state == nullptr) - { - psp->SetState(nullptr); - } - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_HandLower) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->player != nullptr) - { - DPSprite *psp = self->player->GetPSprite(PSP_STRIFEHANDS); - - if (psp->GetState() == nullptr) - { - psp->SetState(nullptr); - return 0; - } - - psp->y += 9; - if (psp->y > WEAPONBOTTOM*2) - { - psp->SetState(nullptr); - } - - if (self->player->extralight > 0) self->player->extralight--; - } - return 0; -} diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp deleted file mode 100644 index 4dad52de4..000000000 --- a/src/g_strife/a_strifeweapons.cpp +++ /dev/null @@ -1,1170 +0,0 @@ -/* -#include "a_pickups.h" -#include "p_local.h" -#include "m_random.h" -#include "a_strifeglobal.h" -#include "s_sound.h" -#include "p_enemy.h" -#include "templates.h" -#include "vm.h" -#include "doomstat.h" -*/ - -// Note: Strife missiles do 1-4 times their damage amount. -// Doom missiles do 1-8 times their damage amount, so to -// make the strife missiles do proper damage without -// hacking more stuff in the executable, be sure to give -// all Strife missiles the MF4_STRIFEDAMAGE flag. - -static FRandom pr_jabdagger ("JabDagger"); -static FRandom pr_electric ("FireElectric"); -static FRandom pr_sgunshot ("StrifeGunShot"); -static FRandom pr_minimissile ("MiniMissile"); -static FRandom pr_flamethrower ("FlameThrower"); -static FRandom pr_flamedie ("FlameDie"); -static FRandom pr_mauler1 ("Mauler1"); -static FRandom pr_mauler2 ("Mauler2"); -static FRandom pr_phburn ("PhBurn"); - -void A_LoopActiveSound (AActor *); -void A_Countdown (AActor *); - -// Punch Dagger ------------------------------------------------------------- - -//============================================================================ -// -// P_DaggerAlert -// -//============================================================================ - -void P_DaggerAlert (AActor *target, AActor *emitter) -{ - AActor *looker; - sector_t *sec = emitter->Sector; - - if (emitter->LastHeard != NULL) - return; - if (emitter->health <= 0) - return; - if (!(emitter->flags3 & MF3_ISMONSTER)) - return; - if (emitter->flags4 & MF4_INCOMBAT) - return; - emitter->flags4 |= MF4_INCOMBAT; - - emitter->target = target; - FState *painstate = emitter->FindState(NAME_Pain, NAME_Dagger); - if (painstate != NULL) - { - emitter->SetState (painstate); - } - - for (looker = sec->thinglist; looker != NULL; looker = looker->snext) - { - if (looker == emitter || looker == target) - continue; - - if (looker->health <= 0) - continue; - - if (!(looker->flags4 & MF4_SEESDAGGERS)) - continue; - - if (!(looker->flags4 & MF4_INCOMBAT)) - { - if (!P_CheckSight (looker, target) && !P_CheckSight (looker, emitter)) - continue; - - looker->target = target; - if (looker->SeeSound) - { - S_Sound (looker, CHAN_VOICE, looker->SeeSound, 1, ATTN_NORM); - } - looker->SetState (looker->SeeState); - looker->flags4 |= MF4_INCOMBAT; - } - } -} - -//============================================================================ -// -// A_JabDagger -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_JabDagger) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle angle; - int damage; - DAngle pitch; - int power; - FTranslatedLineTarget t; - - power = MIN(10, self->player->mo->stamina / 10); - damage = (pr_jabdagger() % (power + 8)) * (power + 2); - - if (self->FindInventory()) - { - damage *= 10; - } - - angle = self->Angles.Yaw + pr_jabdagger.Random2() * (5.625 / 256); - pitch = P_AimLineAttack (self, angle, 80.); - P_LineAttack (self, angle, 80., pitch, damage, NAME_Melee, "StrifeSpark", true, &t); - - // turn to face target - if (t.linetarget) - { - S_Sound (self, CHAN_WEAPON, - t.linetarget->flags & MF_NOBLOOD ? "misc/metalhit" : "misc/meathit", - 1, ATTN_NORM); - self->Angles.Yaw = t.angleFromSource; - self->flags |= MF_JUSTATTACKED; - P_DaggerAlert (self, t.linetarget); - } - else - { - S_Sound (self, CHAN_WEAPON, "misc/swish", 1, ATTN_NORM); - } - return 0; -} - -//============================================================================ -// -// A_AlertMonsters -// -//============================================================================ - -enum -{ - AMF_TARGETEMITTER = 1, - AMF_TARGETNONPLAYER = 2, - AMF_EMITFROMTARGET = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_AlertMonsters) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_FLOAT_DEF(maxdist); - PARAM_INT_DEF(Flags); - - AActor * target = NULL; - AActor * emitter = self; - - if (self->player != NULL || (Flags & AMF_TARGETEMITTER)) - { - target = self; - } - else if (self->target != NULL && (Flags & AMF_TARGETNONPLAYER)) - { - target = self->target; - } - else if (self->target != NULL && self->target->player != NULL) - { - target = self->target; - } - - if (Flags & AMF_EMITFROMTARGET) emitter = target; - - if (target != NULL && emitter != NULL) - { - P_NoiseAlert(target, emitter, false, maxdist); - } - return 0; -} - -// Poison Bolt -------------------------------------------------------------- - -class APoisonBolt : public AActor -{ - DECLARE_CLASS (APoisonBolt, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(APoisonBolt, false, false, false, false) - -int APoisonBolt::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->flags & MF_NOBLOOD) - { - return -1; - } - if (target->health < 1000000) - { - if (!(target->flags2 & MF2_BOSS)) - return target->health + 10; - else - return 50; - } - return 1; -} - -// Strife's Crossbow -------------------------------------------------------- - -//============================================================================ -// -// A_ClearFlash -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ClearFlash) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player = self->player; - - if (player == nullptr) - return 0; - - P_SetPsprite (player, PSP_FLASH, nullptr); - return 0; -} - -//============================================================================ -// -// A_ShowElectricFlash -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ShowElectricFlash) -{ - PARAM_ACTION_PROLOGUE(AActor); - - if (self->player != nullptr) - { - P_SetPsprite (self->player, PSP_FLASH, self->player->ReadyWeapon->FindState(NAME_Flash)); - } - return 0; -} - -//============================================================================ -// -// A_FireElectric -// -//============================================================================ - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireArrow) -{ - PARAM_ACTION_PROLOGUE(AActor); - PARAM_CLASS(ti, AActor); - - DAngle savedangle; - - if (self->player == NULL) - return 0; - - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - - if (ti) - { - savedangle = self->Angles.Yaw; - self->Angles.Yaw += pr_electric.Random2() * (5.625/256) * self->player->mo->AccuracyFactor(); - self->player->mo->PlayAttacking2 (); - P_SpawnPlayerMissile (self, ti); - self->Angles.Yaw = savedangle; - S_Sound (self, CHAN_WEAPON, "weapons/xbowshoot", 1, ATTN_NORM); - } - return 0; -} - -// Assault Gun -------------------------------------------------------------- - -//============================================================================ -// -// P_StrifeGunShot -// -//============================================================================ - -void P_StrifeGunShot (AActor *mo, bool accurate, DAngle pitch) -{ - DAngle angle; - int damage; - - damage = 4*(pr_sgunshot()%3+1); - angle = mo->Angles.Yaw; - - if (mo->player != NULL && !accurate) - { - angle += pr_sgunshot.Random2() * (22.5 / 256) * mo->player->mo->AccuracyFactor(); - } - - P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, NAME_StrifePuff); -} - -//============================================================================ -// -// A_FireAssaultGun -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireAssaultGun) -{ - PARAM_ACTION_PROLOGUE(AActor); - - bool accurate; - - S_Sound (self, CHAN_WEAPON, "weapons/assaultgun", 1, ATTN_NORM); - - if (self->player != NULL) - { - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - self->player->mo->PlayAttacking2 (); - accurate = !self->player->refire; - } - else - { - accurate = true; - } - - P_StrifeGunShot (self, accurate, P_BulletSlope (self)); - return 0; -} - -// Mini-Missile Launcher ---------------------------------------------------- - -//============================================================================ -// -// A_FireMiniMissile -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireMiniMissile) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player = self->player; - DAngle savedangle; - - if (self->player == NULL) - return 0; - - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - - savedangle = self->Angles.Yaw; - self->Angles.Yaw += pr_minimissile.Random2() * (11.25 / 256) * player->mo->AccuracyFactor(); - player->mo->PlayAttacking2 (); - P_SpawnPlayerMissile (self, PClass::FindActor("MiniMissile")); - self->Angles.Yaw = savedangle; - return 0; -} - -//============================================================================ -// -// A_RocketInFlight -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_RocketInFlight) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *trail; - - S_Sound (self, CHAN_VOICE, "misc/missileinflight", 1, ATTN_NORM); - P_SpawnPuff (self, PClass::FindActor("MiniMissilePuff"), self->Pos(), self->Angles.Yaw - 180, self->Angles.Yaw - 180, 2, PF_HITTHING); - trail = Spawn("RocketTrail", self->Vec3Offset(-self->Vel.X, -self->Vel.Y, 0.), ALLOW_REPLACE); - if (trail != NULL) - { - trail->Vel.Z = 1; - } - return 0; -} - -// Flame Thrower ------------------------------------------------------------ - -//============================================================================ -// -// A_FlameDie -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FlameDie) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->flags |= MF_NOGRAVITY; - self->Vel.Z = pr_flamedie() & 3; - return 0; -} - -//============================================================================ -// -// A_FireFlamer -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireFlamer) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player = self->player; - - if (player != NULL) - { - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - player->mo->PlayAttacking2 (); - } - - self->Angles.Yaw += pr_flamethrower.Random2() * (5.625/256.); - self = P_SpawnPlayerMissile (self, PClass::FindActor("FlameMissile")); - if (self != NULL) - { - self->Vel.Z += 5; - } - return 0; -} - -// Mauler ------------------------------------------------------------------- - -//============================================================================ -// -// A_FireMauler1 -// -// Hey! This is exactly the same as a super shotgun except for the sound -// and the bullet puffs and the disintegration death. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireMauler1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - if (self->player != NULL) - { - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - // Strife apparently didn't show the player shooting. Let's fix that. - self->player->mo->PlayAttacking2 (); - } - - S_Sound (self, CHAN_WEAPON, "weapons/mauler1", 1, ATTN_NORM); - - - DAngle bpitch = P_BulletSlope (self); - - for (int i = 0; i < 20; ++i) - { - int damage = 5 * (pr_mauler1() % 3 + 1); - DAngle angle = self->Angles.Yaw + pr_mauler1.Random2() * (11.25 / 256); - DAngle pitch = bpitch + pr_mauler1.Random2() * (7.097 / 256); - - // Strife used a range of 2112 units for the mauler to signal that - // it should use a different puff. ZDoom's default range is longer - // than this, so let's not handicap it by being too faithful to the - // original. - P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, NAME_MaulerPuff); - } - return 0; -} - -//============================================================================ -// -// A_FireMauler2Pre -// -// Makes some noise and moves the psprite. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireMauler2Pre) -{ - PARAM_ACTION_PROLOGUE(AActor); - - S_Sound (self, CHAN_WEAPON, "weapons/mauler2charge", 1, ATTN_NORM); - - if (self->player != nullptr) - { - self->player->GetPSprite(PSP_WEAPON)->x += pr_mauler2.Random2() / 64.; - self->player->GetPSprite(PSP_WEAPON)->y += pr_mauler2.Random2() / 64.; - } - return 0; -} - -//============================================================================ -// -// A_FireMauler2Pre -// -// Fires the torpedo. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireMauler2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - if (self->player != NULL) - { - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - self->player->mo->PlayAttacking2 (); - } - P_SpawnPlayerMissile (self, PClass::FindActor("MaulerTorpedo")); - P_DamageMobj (self, self, NULL, 20, self->DamageType); - self->Thrust(self->Angles.Yaw+180., 7.8125); - return 0; -} - -//============================================================================ -// -// A_MaulerTorpedoWave -// -// Launches lots of balls when the torpedo hits something. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_MaulerTorpedoWave) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *wavedef = GetDefaultByName("MaulerTorpedoWave"); - double savedz; - self->Angles.Yaw += 180.; - - // If the torpedo hit the ceiling, it should still spawn the wave - savedz = self->Z(); - if (wavedef && self->ceilingz < self->Z() + wavedef->Height) - { - self->SetZ(self->ceilingz - wavedef->Height); - } - - for (int i = 0; i < 80; ++i) - { - self->Angles.Yaw += 4.5; - P_SpawnSubMissile (self, PClass::FindActor("MaulerTorpedoWave"), self->target); - } - self->SetZ(savedz); - return 0; -} - -AActor *P_SpawnSubMissile (AActor *source, PClassActor *type, AActor *target) -{ - AActor *other = Spawn (type, source->Pos(), ALLOW_REPLACE); - - if (other == NULL) - { - return NULL; - } - - other->target = target; - other->Angles.Yaw = source->Angles.Yaw; - other->VelFromAngle(); - - if (other->flags4 & MF4_SPECTRAL) - { - if (source->flags & MF_MISSILE && source->flags4 & MF4_SPECTRAL) - { - other->FriendPlayer = source->FriendPlayer; - } - else - { - other->SetFriendPlayer(target->player); - } - } - - if (P_CheckMissileSpawn (other, source->radius)) - { - DAngle pitch = P_AimLineAttack (source, source->Angles.Yaw, 1024.); - other->Vel.Z = -other->Speed * pitch.Sin(); - return other; - } - return NULL; -} - -class APhosphorousFire : public AActor -{ - DECLARE_CLASS (APhosphorousFire, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(APhosphorousFire, false, false, false, false) - -int APhosphorousFire::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->flags & MF_NOBLOOD) - { - return damage / 2; - } - return Super::DoSpecialDamage (target, damage, damagetype); -} - -DEFINE_ACTION_FUNCTION(AActor, A_BurnArea) -{ - PARAM_SELF_PROLOGUE(AActor); - - P_RadiusAttack (self, self->target, 128, 128, self->DamageType, RADF_HURTSOURCE); - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_Burnination) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->Vel.Z -= 8; - self->Vel.X += (pr_phburn.Random2 (3)); - self->Vel.Y += (pr_phburn.Random2 (3)); - S_Sound (self, CHAN_VOICE, "world/largefire", 1, ATTN_NORM); - - // Only the main fire spawns more. - if (!(self->flags & MF_DROPPED)) - { - // Original x and y offsets seemed to be like this: - // x + (((pr_phburn() + 12) & 31) << F.RACBITS); - // - // But that creates a lop-sided burn because it won't use negative offsets. - int xofs, xrand = pr_phburn(); - int yofs, yrand = pr_phburn(); - - // Adding 12 is pointless if you're going to mask it afterward. - xofs = xrand & 31; - if (xrand & 128) - { - xofs = -xofs; - } - - yofs = yrand & 31; - if (yrand & 128) - { - yofs = -yofs; - } - - DVector2 pos = self->Vec2Offset((double)xofs, (double)yofs); - sector_t * sector = P_PointInSector(pos); - - // The sector's floor is too high so spawn the flame elsewhere. - if (sector->floorplane.ZatPoint(pos) > self->Z() + self->MaxStepHeight) - { - pos = self->Pos(); - } - - AActor *drop = Spawn (DVector3(pos, self->Z() + 4.), ALLOW_REPLACE); - if (drop != NULL) - { - drop->Vel.X = self->Vel.X + pr_phburn.Random2 (7); - drop->Vel.Y = self->Vel.Y + pr_phburn.Random2 (7); - drop->Vel.Z = self->Vel.Z - 1; - drop->reactiontime = (pr_phburn() & 3) + 2; - drop->flags |= MF_DROPPED; - } - } - return 0; -} - -//============================================================================ -// -// A_FireGrenade -// -//============================================================================ - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade) -{ - PARAM_ACTION_PROLOGUE(AActor); - PARAM_CLASS(grenadetype, AActor); - PARAM_ANGLE(angleofs); - PARAM_STATE(flash) - - player_t *player = self->player; - AActor *grenade; - DAngle an; - AWeapon *weapon; - - if (player == nullptr || grenadetype == nullptr) - return 0; - - if ((weapon = player->ReadyWeapon) == nullptr) - return 0; - - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - - P_SetPsprite (player, PSP_FLASH, flash, true); - - if (grenadetype != nullptr) - { - self->AddZ(32); - grenade = P_SpawnSubMissile (self, grenadetype, self); - self->AddZ(-32); - if (grenade == nullptr) - return 0; - - if (grenade->SeeSound != 0) - { - S_Sound (grenade, CHAN_VOICE, grenade->SeeSound, 1, ATTN_NORM); - } - - grenade->Vel.Z = (-self->Angles.Pitch.TanClamped()) * grenade->Speed + 8; - - DVector2 offset = self->Angles.Yaw.ToVector(self->radius + grenade->radius); - DAngle an = self->Angles.Yaw + angleofs; - offset += an.ToVector(15); - grenade->SetOrigin(grenade->Vec3Offset(offset.X, offset.Y, 0.), false); - } - return 0; -} - -// The Almighty Sigil! ------------------------------------------------------ - -IMPLEMENT_CLASS(ASigil, false, false, false, false) - -//============================================================================ -// -// ASigil :: Serialize -// -//============================================================================ - -void ASigil::BeginPlay() -{ - NumPieces = health; -} - -//============================================================================ -// -// ASigil :: Serialize -// -//============================================================================ - -void ASigil::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("numpieces", NumPieces) - ("downpieces", DownPieces); -} - -//============================================================================ -// -// ASigil :: HandlePickup -// -//============================================================================ - -bool ASigil::HandlePickup (AInventory *item) -{ - if (item->IsKindOf (RUNTIME_CLASS(ASigil))) - { - int otherPieces = static_cast(item)->NumPieces; - if (otherPieces > NumPieces) - { - item->ItemFlags |= IF_PICKUPGOOD; - Icon = item->Icon; - // If the player is holding the Sigil right now, drop it and bring - // it back with the new piece(s) in view. - if (Owner->player != NULL && Owner->player->ReadyWeapon == this) - { - DownPieces = NumPieces; - Owner->player->PendingWeapon = this; - } - NumPieces = otherPieces; - } - return true; - } - if (Inventory != NULL) - { - return Inventory->HandlePickup (item); - } - return false; -} - -//============================================================================ -// -// ASigil :: CreateCopy -// -//============================================================================ - -AInventory *ASigil::CreateCopy (AActor *other) -{ - ASigil *copy = Spawn (); - copy->Amount = Amount; - copy->MaxAmount = MaxAmount; - copy->NumPieces = NumPieces; - copy->Icon = Icon; - GoAwayAndDie (); - return copy; -} - -//============================================================================ -// -// A_SelectPiece -// -// Decide which sprite frame this Sigil should use as an item, based on how -// many pieces it represents. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SelectPiece) -{ - PARAM_ACTION_PROLOGUE(AActor); - - int pieces = MIN (static_cast(self)->NumPieces, 5); - - if (pieces > 1) - { - self->SetState (self->FindState("Spawn")+pieces); - } - return 0; -} - -//============================================================================ -// -// A_SelectSigilView -// -// Decide which first-person frame this Sigil should show, based on how many -// pieces it represents. Strife did this by selecting a flash that looked like -// the Sigil whenever you switched to it and at the end of an attack. I have -// chosen to make the weapon sprite choose the correct frame and let the flash -// be a regular flash. It means I need to use more states, but I think it's -// worth it. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SelectSigilView) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DPSprite *pspr; - int pieces; - - if (self->player == nullptr) - { - return 0; - } - pieces = static_cast(self->player->ReadyWeapon)->NumPieces; - pspr = self->player->GetPSprite(PSP_WEAPON); - pspr->SetState(pspr->GetState() + pieces); - return 0; -} - -//============================================================================ -// -// A_SelectSigilDown -// -// Same as A_SelectSigilView, except it uses DownPieces. This is so that when -// you pick up a Sigil, the old one will drop and *then* change to the new -// one. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SelectSigilDown) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DPSprite *pspr; - int pieces; - - if (self->player == nullptr) - { - return 0; - } - pieces = static_cast(self->player->ReadyWeapon)->DownPieces; - static_cast(self->player->ReadyWeapon)->DownPieces = 0; - if (pieces == 0) - { - pieces = static_cast(self->player->ReadyWeapon)->NumPieces; - } - pspr = self->player->GetPSprite(PSP_WEAPON); - pspr->SetState(pspr->GetState() + pieces); - return 0; -} - -//============================================================================ -// -// A_SelectSigilAttack -// -// Same as A_SelectSigilView, but used just before attacking. -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SelectSigilAttack) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DPSprite *pspr; - int pieces; - - if (self->player == nullptr) - { - return 0; - } - pieces = static_cast(self->player->ReadyWeapon)->NumPieces; - pspr = self->player->GetPSprite(PSP_WEAPON); - pspr->SetState(pspr->GetState() + 4*pieces - 3); - return 0; -} - -//============================================================================ -// -// A_SigilCharge -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SigilCharge) -{ - PARAM_ACTION_PROLOGUE(AActor); - - S_Sound (self, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM); - if (self->player != NULL) - { - self->player->extralight = 2; - } - return 0; -} - -//============================================================================ -// -// A_FireSigil1 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireSigil1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *spot; - player_t *player = self->player; - FTranslatedLineTarget t; - - if (player == NULL || player->ReadyWeapon == NULL) - return 0; - - P_DamageMobj (self, self, NULL, 1*4, 0, DMG_NO_ARMOR); - S_Sound (self, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM); - - P_BulletSlope (self, &t, ALF_PORTALRESTRICT); - if (t.linetarget != NULL) - { - spot = Spawn("SpectralLightningSpot", t.linetarget->PosAtZ(t.linetarget->floorz), ALLOW_REPLACE); - if (spot != NULL) - { - spot->tracer = t.linetarget; - } - } - else - { - spot = Spawn("SpectralLightningSpot", self->Pos(), ALLOW_REPLACE); - if (spot != NULL) - { - spot->VelFromAngle(28., self->Angles.Yaw); - } - } - if (spot != NULL) - { - spot->SetFriendPlayer(player); - spot->target = self; - } - return 0; -} - -//============================================================================ -// -// A_FireSigil2 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireSigil2) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player = self->player; - - if (player == NULL || player->ReadyWeapon == NULL) - return 0; - - P_DamageMobj (self, self, NULL, 2*4, 0, DMG_NO_ARMOR); - S_Sound (self, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM); - - P_SpawnPlayerMissile (self, PClass::FindActor("SpectralLightningH1")); - return 0; -} - -//============================================================================ -// -// A_FireSigil3 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireSigil3) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *spot; - player_t *player = self->player; - int i; - - if (player == NULL || player->ReadyWeapon == NULL) - return 0; - - P_DamageMobj (self, self, NULL, 3*4, 0, DMG_NO_ARMOR); - S_Sound (self, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM); - - self->Angles.Yaw -= 90.; - for (i = 0; i < 20; ++i) - { - self->Angles.Yaw += 9.; - spot = P_SpawnSubMissile (self, PClass::FindActor("SpectralLightningBall1"), self); - if (spot != NULL) - { - spot->SetZ(self->Z() + 32); - } - } - self->Angles.Yaw -= 90.; - return 0; -} - -//============================================================================ -// -// A_FireSigil4 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireSigil4) -{ - PARAM_ACTION_PROLOGUE(AActor); - - AActor *spot; - player_t *player = self->player; - FTranslatedLineTarget t; - - if (player == NULL || player->ReadyWeapon == NULL) - return 0; - - P_DamageMobj (self, self, NULL, 4*4, 0, DMG_NO_ARMOR); - S_Sound (self, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM); - - P_BulletSlope (self, &t, ALF_PORTALRESTRICT); - if (t.linetarget != NULL) - { - spot = P_SpawnPlayerMissile (self, 0,0,0, PClass::FindActor("SpectralLightningBigV1"), self->Angles.Yaw, &t, NULL, false, false, ALF_PORTALRESTRICT); - if (spot != NULL) - { - spot->tracer = t.linetarget; - } - } - else - { - spot = P_SpawnPlayerMissile (self, PClass::FindActor("SpectralLightningBigV1")); - if (spot != NULL) - { - spot->VelFromAngle(spot->Speed, self->Angles.Yaw); - } - } - return 0; -} - -//============================================================================ -// -// A_FireSigil5 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireSigil5) -{ - PARAM_ACTION_PROLOGUE(AActor); - - player_t *player = self->player; - - if (player == NULL || player->ReadyWeapon == NULL) - return 0; - - P_DamageMobj (self, self, NULL, 5*4, 0, DMG_NO_ARMOR); - S_Sound (self, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM); - - P_SpawnPlayerMissile (self, PClass::FindActor("SpectralLightningBigBall1")); - return 0; -} - -//============================================================================ -// -// ASigil :: SpecialDropAction -// -// Monsters don't drop Sigil pieces. The Sigil pieces grab hold of the person -// who killed the dropper and automatically enter their inventory. That's the -// way it works if you believe Macil, anyway... -// -//============================================================================ - -bool ASigil::SpecialDropAction (AActor *dropper) -{ - // Give a Sigil piece to every player in the game - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].mo != NULL) - { - GiveSigilPiece (players[i].mo); - Destroy (); - } - } - return true; -} - -//============================================================================ -// -// ASigil :: GiveSigilPiece -// -// Gives the actor another Sigil piece, up to 5. Returns the number of Sigil -// pieces the actor previously held. -// -//============================================================================ - -int ASigil::GiveSigilPiece (AActor *receiver) -{ - ASigil *sigil; - - sigil = receiver->FindInventory (); - if (sigil == NULL) - { - sigil = static_cast(Spawn("Sigil1")); - if (!sigil->CallTryPickup (receiver)) - { - sigil->Destroy (); - } - return 0; - } - else if (sigil->NumPieces < 5) - { - ++sigil->NumPieces; - static const char* sigils[5] = - { - "Sigil1", "Sigil2", "Sigil3", "Sigil4", "Sigil5" - }; - sigil->Icon = ((AInventory*)GetDefaultByName (sigils[MAX(0,sigil->NumPieces-1)]))->Icon; - // If the player has the Sigil out, drop it and bring it back up. - if (sigil->Owner->player != NULL && sigil->Owner->player->ReadyWeapon == sigil) - { - sigil->Owner->player->PendingWeapon = sigil; - sigil->DownPieces = sigil->NumPieces - 1; - } - return sigil->NumPieces - 1; - } - else - { - return 5; - } -} diff --git a/src/g_strife/a_templar.cpp b/src/g_strife/a_templar.cpp deleted file mode 100644 index 0bfa00b24..000000000 --- a/src/g_strife/a_templar.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "p_enemy.h" -#include "s_sound.h" -#include "a_strifeglobal.h" -#include "vm.h" -*/ - -static FRandom pr_templar ("Templar"); - -DEFINE_ACTION_FUNCTION(AActor, A_TemplarAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - int damage; - DAngle angle; - DAngle pitch; - - if (self->target == NULL) - return 0; - - S_Sound (self, CHAN_WEAPON, "templar/shoot", 1, ATTN_NORM); - A_FaceTarget (self); - pitch = P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE); - - for (int i = 0; i < 10; ++i) - { - damage = (pr_templar() & 4) * 2; - angle = self->Angles.Yaw + pr_templar.Random2() * (11.25 / 256); - P_LineAttack (self, angle, MISSILERANGE+64., pitch + pr_templar.Random2() * (7.097 / 256), damage, NAME_Hitscan, NAME_MaulerPuff); - } - return 0; -} diff --git a/src/g_strife/a_thingstoblowup.cpp b/src/g_strife/a_thingstoblowup.cpp deleted file mode 100644 index 5e1bc42d2..000000000 --- a/src/g_strife/a_thingstoblowup.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "p_local.h" -#include "c_console.h" -#include "p_enemy.h" -#include "a_action.h" -#include "gstrings.h" -#include "vm.h" -#include "vm.h" -#include "doomstat.h" -*/ - -static FRandom pr_bang4cloud ("Bang4Cloud"); -static FRandom pr_lightout ("LightOut"); - -DEFINE_ACTION_FUNCTION(AActor, A_Bang4Cloud) -{ - PARAM_SELF_PROLOGUE(AActor); - - double xo = (pr_bang4cloud.Random2() & 3) * (10. / 64); - double yo = (pr_bang4cloud.Random2() & 3) * (10. / 64); - Spawn("Bang4Cloud", self->Vec3Offset(xo, yo, 0.), ALLOW_REPLACE); - return 0; -} - -// ------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveQuestItem) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT(questitem); - - // Give one of these quest items to every player in the game - if (questitem >= 0 && questitem < (int)countof(QuestItemClasses)) - { - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - AInventory *item = static_cast(Spawn (QuestItemClasses[questitem - 1])); - if (!item->CallTryPickup (players[i].mo)) - { - item->Destroy (); - } - } - } - } - - char messageid[64]; - - mysnprintf(messageid, countof(messageid), "TXT_QUEST_%d", questitem); - const char * name = GStrings[messageid]; - - if (name != NULL) - { - C_MidPrint (SmallFont, name); - } - return 0; -} - -// PowerCrystal ------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_ExtraLightOff) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->target != NULL && self->target->player != NULL) - { - self->target->player->extralight = 0; - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_Explode512) -{ - PARAM_SELF_PROLOGUE(AActor); - - P_RadiusAttack (self, self->target, 512, 512, NAME_None, RADF_HURTSOURCE); - if (self->target != NULL && self->target->player != NULL) - { - self->target->player->extralight = 5; - } - P_CheckSplash(self, 512); - - // Strife didn't do this next part, but it looks good - self->RenderStyle = STYLE_Add; - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_LightGoesOut) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *foo; - sector_t *sec = self->Sector; - vertex_t *spot; - double newheight; - - sec->SetLightLevel(0); - - double oldtheight = sec->floorplane.fD(); - newheight = sec->FindLowestFloorSurrounding(&spot); - sec->floorplane.setD(sec->floorplane.PointToDist (spot, newheight)); - double newtheight = sec->floorplane.fD(); - sec->ChangePlaneTexZ(sector_t::floor, newtheight - oldtheight); - sec->CheckPortalPlane(sector_t::floor); - - for (int i = 0; i < 8; ++i) - { - foo = Spawn("Rubble1", self->Pos(), ALLOW_REPLACE); - if (foo != NULL) - { - int t = pr_lightout() & 15; - foo->Vel.X = t - (pr_lightout() & 7); - foo->Vel.Y = pr_lightout.Random2() & 7; - foo->Vel.Z = 7 + (pr_lightout() & 3); - } - } - return 0; -} diff --git a/src/g_strife/strife_sbar.cpp b/src/g_strife/strife_sbar.cpp index 73e31e676..973364cf7 100644 --- a/src/g_strife/strife_sbar.cpp +++ b/src/g_strife/strife_sbar.cpp @@ -12,7 +12,8 @@ #include "m_swap.h" #include "templates.h" #include "a_keys.h" -#include "a_strifeglobal.h" +#include "a_armor.h" +#include "a_ammo.h" #include "gi.h" #include "g_level.h" #include "colormatcher.h" @@ -439,7 +440,7 @@ private: } // Sigil - item = CPlayer->mo->FindInventory(); + item = CPlayer->mo->FindInventory(PClass::FindActor(NAME_Sigil)); if (item != NULL) { DrawImage (TexMan(item->Icon), 253, 7); @@ -851,7 +852,7 @@ private: double ItemFlash; }; -IMPLEMENT_CLASS(DStrifeStatusBar, false, false, false, false); +IMPLEMENT_CLASS(DStrifeStatusBar, false, false); DBaseStatusBar *CreateStrifeStatusBar () { diff --git a/src/i_module.cpp b/src/i_module.cpp index 1ed40310f..a82963ec5 100644 --- a/src/i_module.cpp +++ b/src/i_module.cpp @@ -97,5 +97,5 @@ bool FModule::Open(const char* lib) void *FModule::GetSym(const char* name) { - return GetProcAddress((HMODULE)handle, name); + return (void *)GetProcAddress((HMODULE)handle, name); } diff --git a/src/info.cpp b/src/info.cpp index e771108c3..b8bf3acde 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -43,7 +43,6 @@ #include "v_text.h" #include "gi.h" -#include "vm.h" #include "actor.h" #include "r_state.h" #include "i_system.h" @@ -52,7 +51,6 @@ #include "cmdlib.h" #include "g_level.h" #include "stats.h" -#include "vm.h" #include "thingdef.h" #include "d_player.h" #include "doomerrors.h" @@ -77,7 +75,6 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, { ActionCycles.Clock(); - VMFrameStack stack; VMValue params[3] = { self, stateowner, VMValue(info, ATAG_GENERIC) }; // If the function returns a state, store it at *stateret. // If it doesn't return a state but stateret is non-NULL, we need @@ -94,13 +91,13 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, } if (stateret == NULL) { - stack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); + GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); } else { VMReturn ret; ret.PointerAt((void **)stateret); - stack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); + GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); } ActionCycles.Unclock(); return true; @@ -153,7 +150,14 @@ int GetSpriteIndex(const char * spritename, bool add) return (lastindex = (int)sprites.Push (temp)); } -IMPLEMENT_CLASS(PClassActor, false, true, false, false) +DEFINE_ACTION_FUNCTION(AActor, GetSpriteIndex) +{ + PARAM_PROLOGUE; + PARAM_NAME(sprt); + ACTION_RETURN_INT(GetSpriteIndex(sprt.GetChars(), false)); +} + +IMPLEMENT_CLASS(PClassActor, false, true) IMPLEMENT_POINTERS_START(PClassActor) IMPLEMENT_POINTER(DropItems) @@ -232,7 +236,6 @@ PClassActor::PClassActor() BurnHeight = -1; GibHealth = INT_MIN; WoundHealth = 6; - PoisonDamage = 0; FastSpeed = -1.; RDFactor = 1.; CameraHeight = INT_MIN; @@ -293,7 +296,6 @@ void PClassActor::DeriveData(PClass *newclass) newa->BloodColor = BloodColor; newa->GibHealth = GibHealth; newa->WoundHealth = WoundHealth; - newa->PoisonDamage = PoisonDamage; newa->FastSpeed = FastSpeed; newa->RDFactor = RDFactor; newa->CameraHeight = CameraHeight; diff --git a/src/info.h b/src/info.h index 0b7d4dfc3..bb21524a5 100644 --- a/src/info.h +++ b/src/info.h @@ -43,7 +43,6 @@ #include "dobject.h" #include "doomdef.h" -#include "vm.h" #include "s_sound.h" #include "m_fixed.h" @@ -64,7 +63,17 @@ enum EStateDefineFlags SDF_LABEL = 4, SDF_INDEX = 5, SDF_MASK = 7, - SDF_DEHACKED = 8, // Identify a state as having been modified by a dehacked lump +}; + +enum EStateFlags +{ + STF_SLOW = 1, // State duration is extended when slow monsters is on. + STF_FAST = 2, // State duration is shortened when fast monsters is on. + STF_FULLBRIGHT = 4, // State is fullbright + STF_NODELAY = 8, // Spawn states executes its action normally + STF_SAMEFRAME = 16, // Ignore Frame (except when spawning actor) + STF_CANRAISE = 32, // Allows a monster to be resurrected without waiting for an infinate frame + STF_DEHACKED = 64, // Modified by Dehacked }; enum EStateUseFlags @@ -102,33 +111,44 @@ struct FState { FState *NextState; VMFunction *ActionFunc; - WORD sprite; - SWORD Tics; - WORD TicRange; - short Light; - BYTE Frame; - BYTE UseFlags; - BYTE DefineFlags; // Unused byte so let's use it during state creation. - int Misc1; // Was changed to SBYTE, reverted to long for MBF compat - int Misc2; // Was changed to BYTE, reverted to long for MBF compat - BYTE Fullbright:1; // State is fullbright - BYTE SameFrame:1; // Ignore Frame (except when spawning actor) - BYTE Fast:1; - BYTE NoDelay:1; // Spawn states executes its action normally - BYTE CanRaise:1; // Allows a monster to be resurrected without waiting for an infinate frame - BYTE Slow:1; // Inverse of fast - + int32_t sprite; + int16_t Tics; + uint16_t TicRange; + int16_t Light; + uint16_t StateFlags; + uint8_t Frame; + uint8_t UseFlags; + uint8_t DefineFlags; // Unused byte so let's use it during state creation. + int32_t Misc1; // Was changed to SBYTE, reverted to long for MBF compat + int32_t Misc2; // Was changed to BYTE, reverted to long for MBF compat +public: inline int GetFrame() const { return Frame; } inline bool GetSameFrame() const { - return SameFrame; + return !!(StateFlags & STF_SAMEFRAME); } inline int GetFullbright() const { - return Fullbright ? 0x10 /*RF_FULLBRIGHT*/ : 0; + return (StateFlags & STF_FULLBRIGHT)? 0x10 /*RF_FULLBRIGHT*/ : 0; + } + inline bool GetFast() const + { + return !!(StateFlags & STF_FAST); + } + inline bool GetSlow() const + { + return !!(StateFlags & STF_SLOW); + } + inline bool GetNoDelay() const + { + return !!(StateFlags & STF_NODELAY); + } + inline bool GetCanRaise() const + { + return !!(StateFlags & STF_CANRAISE); } inline int GetTics() const { @@ -150,22 +170,10 @@ struct FState { return NextState; } - inline bool GetNoDelay() const - { - return NoDelay; - } - inline bool GetCanRaise() const - { - return CanRaise; - } inline void SetFrame(BYTE frame) { Frame = frame - 'A'; } - inline bool CheckUse(int usetype) - { - return !!(UseFlags & usetype); - } void SetAction(VMFunction *func) { ActionFunc = func; } void ClearAction() { ActionFunc = NULL; } void SetAction(const char *name); @@ -288,7 +296,6 @@ public: PalEntry BloodColor; // Colorized blood int GibHealth; // Negative health below which this monster dies an extreme death int WoundHealth; // Health needed to enter wound state - int PoisonDamage; // Amount of poison damage double FastSpeed; // speed in fast mode double RDFactor; // Radius damage factor double CameraHeight; // Height of camera when used as such @@ -360,7 +367,7 @@ struct FStateLabelStorage } } - FState *GetState(int pos, PClassActor *cls); + FState *GetState(int pos, PClassActor *cls, bool exact = false); }; extern FStateLabelStorage StateLabels; diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index be8f58e19..2aacd215b 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -54,12 +54,12 @@ FIntermissionDescriptorList IntermissionDescriptors; -IMPLEMENT_CLASS(DIntermissionScreen, false, false, false, false) -IMPLEMENT_CLASS(DIntermissionScreenFader, false, false, false, false) -IMPLEMENT_CLASS(DIntermissionScreenText, false, false, false, false) -IMPLEMENT_CLASS(DIntermissionScreenCast, false, false, false, false) -IMPLEMENT_CLASS(DIntermissionScreenScroller, false, false, false, false) -IMPLEMENT_CLASS(DIntermissionController, false, true, false, false) +IMPLEMENT_CLASS(DIntermissionScreen, false, false) +IMPLEMENT_CLASS(DIntermissionScreenFader, false, false) +IMPLEMENT_CLASS(DIntermissionScreenText, false, false) +IMPLEMENT_CLASS(DIntermissionScreenCast, false, false) +IMPLEMENT_CLASS(DIntermissionScreenScroller, false, false) +IMPLEMENT_CLASS(DIntermissionController, false, true) IMPLEMENT_POINTERS_START(DIntermissionController) IMPLEMENT_POINTER(mScreen) diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h index 45779f895..25b369a3a 100644 --- a/src/intermission/intermission.h +++ b/src/intermission/intermission.h @@ -176,7 +176,7 @@ public: virtual int Responder (event_t *ev); virtual int Ticker (); virtual void Drawer (); - void Destroy(); + void Destroy() override; FTextureID GetBackground(bool *fill) { *fill = mFlatfill; @@ -301,7 +301,7 @@ public: bool Responder (event_t *ev); void Ticker (); void Drawer (); - void Destroy(); + void Destroy() override; friend void F_AdvanceIntermission(); }; diff --git a/src/m_argv.cpp b/src/m_argv.cpp index 7d82ed834..395885986 100644 --- a/src/m_argv.cpp +++ b/src/m_argv.cpp @@ -37,7 +37,7 @@ #include "cmdlib.h" #include "i_system.h" -IMPLEMENT_CLASS(DArgs, false, false, false, false) +IMPLEMENT_CLASS(DArgs, false, false) //=========================================================================== // diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index c53192ee7..53ae789c6 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -30,7 +30,6 @@ #include "doomstat.h" #include "gstrings.h" #include "p_local.h" -#include "a_strifeglobal.h" #include "gi.h" #include "p_enemy.h" #include "sbar.h" @@ -48,6 +47,8 @@ #include "serializer.h" #include "r_utility.h" #include "a_morph.h" +#include "a_armor.h" +#include "a_ammo.h" // [RH] Actually handle the cheat. The cheat code in st_stuff.c now just // writes some bytes to the network data stream, and the network code @@ -483,18 +484,27 @@ void cht_DoCheat (player_t *player, int cheat) case CHT_LEGO: if (player->mo != NULL && player->health >= 0) { - int oldpieces = ASigil::GiveSigilPiece (player->mo); - item = player->mo->FindInventory (RUNTIME_CLASS(ASigil)); - - if (item != NULL) + static VMFunction *gsp = nullptr; + if (gsp == nullptr) gsp = PClass::FindFunction(NAME_Sigil, NAME_GiveSigilPiece); + if (gsp) { - if (oldpieces == 5) + VMValue params[1] = { player->mo }; + VMReturn ret; + int oldpieces = 1; + ret.IntAt(&oldpieces); + GlobalVMStack.Call(gsp, params, 1, &ret, 1, nullptr); + item = player->mo->FindInventory(PClass::FindActor(NAME_Sigil)); + + if (item != NULL) { - item->Destroy (); - } - else - { - player->PendingWeapon = static_cast (item); + if (oldpieces == 5) + { + item->Destroy(); + } + else + { + player->PendingWeapon = static_cast (item); + } } } } @@ -1033,7 +1043,7 @@ public: } }; -IMPLEMENT_CLASS(DSuicider, false, true, false, false) +IMPLEMENT_CLASS(DSuicider, false, true) IMPLEMENT_POINTERS_START(DSuicider) IMPLEMENT_POINTER(Pawn) diff --git a/src/memarena.cpp b/src/memarena.cpp index e6e9edd6e..8ea8b5806 100644 --- a/src/memarena.cpp +++ b/src/memarena.cpp @@ -72,11 +72,11 @@ static inline void *RoundPointer(void *ptr) // //========================================================================== -FMemArena::FMemArena(int bs) +FMemArena::FMemArena(size_t blocksize) { TopBlock = NULL; FreeBlocks = NULL; - BlockSize = bs; + BlockSize = blocksize; } //========================================================================== diff --git a/src/memarena.h b/src/memarena.h index cc0c8f148..3601469bf 100644 --- a/src/memarena.h +++ b/src/memarena.h @@ -40,7 +40,7 @@ class FMemArena { public: - FMemArena(int blocksize = 10*1024); + FMemArena(size_t blocksize = 10*1024); ~FMemArena(); void *Alloc(size_t size); @@ -55,7 +55,7 @@ protected: Block *TopBlock; Block *FreeBlocks; - int BlockSize; + size_t BlockSize; }; // An arena specializing in storage of FStrings. It knows how to free them, @@ -87,4 +87,4 @@ private: }; -#endif \ No newline at end of file +#endif diff --git a/src/menu/colorpickermenu.cpp b/src/menu/colorpickermenu.cpp index 639c6bbef..e1ee3566d 100644 --- a/src/menu/colorpickermenu.cpp +++ b/src/menu/colorpickermenu.cpp @@ -95,7 +95,7 @@ public: desc->CalcIndent(); } - void Destroy() + void Destroy() override { if (mStartItem >= 0) { @@ -331,7 +331,7 @@ public: } }; -IMPLEMENT_CLASS(DColorPickerMenu, true, false, false, false) +IMPLEMENT_CLASS(DColorPickerMenu, true, false) CCMD(undocolorpic) { diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index cca13ad44..f715dce4a 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -236,7 +236,7 @@ class DJoystickConfigMenu : public DOptionMenu DECLARE_CLASS(DJoystickConfigMenu, DOptionMenu) }; -IMPLEMENT_CLASS(DJoystickConfigMenu, false, false, false, false) +IMPLEMENT_CLASS(DJoystickConfigMenu, false, false) //============================================================================= // diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 7fdd0c5bc..455ea6c4b 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -42,7 +42,7 @@ #include "d_event.h" #include "menu/menu.h" -IMPLEMENT_CLASS(DListMenu, false, false, false, false) +IMPLEMENT_CLASS(DListMenu, false, false) //============================================================================= // diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 6d543228c..73183ca22 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -103,7 +103,7 @@ protected: char savegamestring[SAVESTRINGSIZE]; DLoadSaveMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); - void Destroy(); + void Destroy() override; int RemoveSaveSlot (int index); void UnloadSaveData (); @@ -119,7 +119,7 @@ public: }; -IMPLEMENT_CLASS(DLoadSaveMenu, false, false, false, false) +IMPLEMENT_CLASS(DLoadSaveMenu, false, false) TArray DLoadSaveMenu::SaveGames; int DLoadSaveMenu::LastSaved = -1; @@ -466,6 +466,7 @@ void DLoadSaveMenu::Destroy() if (currentSavePic != nullptr) delete currentSavePic; currentSavePic = nullptr; ClearSaveStuff (); + Super::Destroy(); } //============================================================================= @@ -927,14 +928,14 @@ class DSaveMenu : public DLoadSaveMenu public: DSaveMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); - void Destroy(); + void Destroy() override; void DoSave (FSaveGameNode *node); bool Responder (event_t *ev); bool MenuEvent (int mkey, bool fromcontroller); }; -IMPLEMENT_CLASS(DSaveMenu, false, false, false, false) +IMPLEMENT_CLASS(DSaveMenu, false, false) //============================================================================= @@ -975,6 +976,7 @@ void DSaveMenu::Destroy() if (Selected == 0) Selected = -1; else Selected--; } + Super::Destroy(); } //============================================================================= @@ -1102,7 +1104,7 @@ public: bool MenuEvent (int mkey, bool fromcontroller); }; -IMPLEMENT_CLASS(DLoadMenu, false, false, false, false) +IMPLEMENT_CLASS(DLoadMenu, false, false) //============================================================================= diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 6648c32b4..0c6019a5f 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -91,7 +91,7 @@ static bool MenuEnabled = true; // //============================================================================ -IMPLEMENT_CLASS(DMenu, false, true, false, false) +IMPLEMENT_CLASS(DMenu, false, true) IMPLEMENT_POINTERS_START(DMenu) IMPLEMENT_POINTER(mParentMenu) diff --git a/src/menu/menuinput.cpp b/src/menu/menuinput.cpp index c33e9fed2..cca4756f5 100644 --- a/src/menu/menuinput.cpp +++ b/src/menu/menuinput.cpp @@ -44,7 +44,7 @@ // [TP] New #includes #include "v_text.h" -IMPLEMENT_CLASS(DTextEnterMenu, true, false, false, false) +IMPLEMENT_CLASS(DTextEnterMenu, true, false) #define INPUTGRID_WIDTH 13 #define INPUTGRID_HEIGHT 5 diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp index de8fa1027..c406fc701 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -63,7 +63,7 @@ class DMessageBoxMenu : public DMenu public: DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None); - void Destroy(); + void Destroy() override; void Init(DMenu *parent, const char *message, int messagemode, bool playsound = false); void Drawer(); bool Responder(event_t *ev); @@ -73,7 +73,7 @@ public: virtual void HandleResult(bool res); }; -IMPLEMENT_CLASS(DMessageBoxMenu, false, false, false, false) +IMPLEMENT_CLASS(DMessageBoxMenu, false, false) //============================================================================= // @@ -128,6 +128,7 @@ void DMessageBoxMenu::Destroy() { if (mMessage != NULL) V_FreeBrokenLines(mMessage); mMessage = NULL; + Super::Destroy(); } //============================================================================= @@ -365,7 +366,7 @@ public: virtual void HandleResult(bool res); }; -IMPLEMENT_CLASS(DQuitMenu, false, false, false, false) +IMPLEMENT_CLASS(DQuitMenu, false, false) //============================================================================= // @@ -458,7 +459,7 @@ public: virtual void HandleResult(bool res); }; -IMPLEMENT_CLASS(DEndGameMenu, false, false, false, false) +IMPLEMENT_CLASS(DEndGameMenu, false, false) //============================================================================= // @@ -536,7 +537,7 @@ public: virtual void HandleResult(bool res); }; -IMPLEMENT_CLASS(DQuickSaveMenu, false, false, false, false) +IMPLEMENT_CLASS(DQuickSaveMenu, false, false) //============================================================================= // @@ -632,7 +633,7 @@ public: virtual void HandleResult(bool res); }; -IMPLEMENT_CLASS(DQuickLoadMenu, false, false, false, false) +IMPLEMENT_CLASS(DQuickLoadMenu, false, false) //============================================================================= // diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 1631c3d4b..f9a29b6f5 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -64,7 +64,7 @@ void M_DrawConText (int color, int x, int y, const char *str) TAG_DONE); } -IMPLEMENT_CLASS(DOptionMenu, false, false, false, false) +IMPLEMENT_CLASS(DOptionMenu, false, false) //============================================================================= // @@ -578,7 +578,7 @@ public: } }; -IMPLEMENT_CLASS(DGameplayMenu, false, false, false, false) +IMPLEMENT_CLASS(DGameplayMenu, false, false) class DCompatibilityMenu : public DOptionMenu { @@ -600,4 +600,4 @@ public: } }; -IMPLEMENT_CLASS(DCompatibilityMenu, false, false, false, false) +IMPLEMENT_CLASS(DCompatibilityMenu, false, false) diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h index e83f4ada4..c3cb7297a 100644 --- a/src/menu/optionmenuitems.h +++ b/src/menu/optionmenuitems.h @@ -399,7 +399,7 @@ public: }; #ifndef NO_IMP -IMPLEMENT_CLASS(DEnterKey, true, false, false, false) +IMPLEMENT_CLASS(DEnterKey, true, false) #endif //============================================================================= diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index e32c7dd61..9f5196367 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -526,7 +526,7 @@ public: void Drawer (); }; -IMPLEMENT_CLASS(DPlayerMenu, false, false, false, false) +IMPLEMENT_CLASS(DPlayerMenu, false, false) //============================================================================= // diff --git a/src/menu/readthis.cpp b/src/menu/readthis.cpp index 0dfc47e73..b8a3837ee 100644 --- a/src/menu/readthis.cpp +++ b/src/menu/readthis.cpp @@ -54,7 +54,7 @@ public: bool MouseEvent(int type, int x, int y); }; -IMPLEMENT_CLASS(DReadThisMenu, false, false, false, false) +IMPLEMENT_CLASS(DReadThisMenu, false, false) //============================================================================= // diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp index 37f59149e..d8edee8f1 100644 --- a/src/menu/videomenu.cpp +++ b/src/menu/videomenu.cpp @@ -183,7 +183,7 @@ public: } }; -IMPLEMENT_CLASS(DVideoModeMenu, false, false, false, false) +IMPLEMENT_CLASS(DVideoModeMenu, false, false) //============================================================================= diff --git a/src/namedef.h b/src/namedef.h index e9324d44b..9cb4e8d23 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -120,6 +120,11 @@ xx(ArtiPoisonBag3) // Strife quests xx(QuestItem) +xx(Sigil) +xx(ScriptedMarine) +xx(GiveSigilPiece) +xx(SetWeapon) +xx(SetSprite) // Armor xx(BasicArmor) @@ -209,6 +214,7 @@ xx(XDeath) xx(Burn) //xx(Ice) // already defined above xx(Disintegrate) +xx(Smash) // Weapon animator names. xx(Select) @@ -284,6 +290,8 @@ xx(FRandom) xx(Random2) xx(RandomPick) xx(FRandomPick) +xx(GetClass) +xx(GetDefaultByType) xx(Exp) xx(Log10) xx(Ceil) @@ -683,6 +691,7 @@ xx(BuiltinFindMultiNameState) xx(BuiltinFindSingleNameState) xx(BuiltinHandleRuntimeState) xx(BuiltinGetDefault) +xx(BuiltinClassCast) xx(Damage) // basic type names @@ -694,6 +703,10 @@ xx(uShort) xx(Int) xx(uInt) xx(Bool) +xx(uint8) +xx(int8) +xx(uint16) +xx(int16) xx(Float) xx(Float32) xx(Float64) @@ -736,4 +749,48 @@ xx(DamageFunction) xx(Length) xx(Unit) xx(StateLabel) +xx(SpriteID) +xx(TextureID) xx(Overlay) +xx(IsValid) +xx(IsNull) +xx(Exists) +xx(SetInvalid) +xx(SetNull) + +xx(A_Punch) +xx(A_FirePistol) +xx(A_FireShotgun) +xx(A_FireShotgun2) +xx(A_FireCGun) +xx(A_FireMissile) +xx(A_Saw) +xx(A_FirePlasma) +xx(A_FireBFG) +xx(A_FireOldBFG) +xx(A_FireRailgun) + +// color channels +xx(a) +xx(r) +xx(g) +xx(b) + +// Special translation names +xx(RainPillar1) +xx(RainPillar2) +xx(RainPillar3) +xx(RainPillar4) +xx(RainPillar5) +xx(RainPillar6) +xx(RainPillar7) +xx(RainPillar8) + +xx(Player1) +xx(Player2) +xx(Player3) +xx(Player4) +xx(Player5) +xx(Player6) +xx(Player7) +xx(Player8) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index da8f76d7f..6a7c9d10c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -58,8 +58,6 @@ #include "sbar.h" #include "m_swap.h" #include "a_sharedglobal.h" -#include "a_doomglobal.h" -#include "a_strifeglobal.h" #include "v_video.h" #include "w_wad.h" #include "r_sky.h" @@ -84,8 +82,9 @@ #include "i_music.h" #include "serializer.h" #include "thingdef.h" - -#include "g_shared/a_pickups.h" +#include "a_pickups.h" +#include "a_armor.h" +#include "a_ammo.h" extern FILE *Logfile; @@ -1369,7 +1368,7 @@ private: DPlaneWatcher() {} }; -IMPLEMENT_CLASS(DPlaneWatcher, false, true, false, false) +IMPLEMENT_CLASS(DPlaneWatcher, false, true) IMPLEMENT_POINTERS_START(DPlaneWatcher) IMPLEMENT_POINTER(Activator) @@ -2860,7 +2859,7 @@ void FBehavior::StaticStopMyScripts (AActor *actor) //---- The ACS Interpreter ----// -IMPLEMENT_CLASS(DACSThinker, false, true, false, false) +IMPLEMENT_CLASS(DACSThinker, false, true) IMPLEMENT_POINTERS_START(DACSThinker) IMPLEMENT_POINTER(LastScript) @@ -2991,7 +2990,7 @@ void DACSThinker::StopScriptsFor (AActor *actor) } } -IMPLEMENT_CLASS(DLevelScript, false, true, false, false) +IMPLEMENT_CLASS(DLevelScript, false, true) IMPLEMENT_POINTERS_START(DLevelScript) IMPLEMENT_POINTER(next) @@ -6110,6 +6109,28 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, int *Stack, int & return true; } +static void SetMarineWeapon(AActor *marine, int weapon) +{ + static VMFunction *smw = nullptr; + if (smw == nullptr) smw = PClass::FindFunction(NAME_ScriptedMarine, NAME_SetWeapon); + if (smw) + { + VMValue params[2] = { marine, weapon }; + GlobalVMStack.Call(smw, params, 2, nullptr, 0, nullptr); + } +} + +static void SetMarineSprite(AActor *marine, PClassActor *source) +{ + static VMFunction *sms = nullptr; + if (sms == nullptr) sms = PClass::FindFunction(NAME_ScriptedMarine, NAME_SetSprite); + if (sms) + { + VMValue params[2] = { marine, source }; + GlobalVMStack.Call(sms, params, 2, nullptr, 0, nullptr); + } +} + int DLevelScript::RunScript () { DACSThinker *controller = DACSThinker::ActiveThinker; @@ -8550,15 +8571,15 @@ scriptwait: case PCD_GETSIGILPIECES: { - ASigil *sigil; + AInventory *sigil; - if (activator == NULL || (sigil = activator->FindInventory()) == NULL) + if (activator == NULL || (sigil = activator->FindInventory(PClass::FindActor(NAME_Sigil))) == NULL) { PushToStack (0); } else { - PushToStack (sigil->NumPieces); + PushToStack (sigil->health); } } break; @@ -8946,20 +8967,19 @@ scriptwait: case PCD_SETMARINEWEAPON: if (STACK(2) != 0) { - AScriptedMarine *marine; - TActorIterator iterator (STACK(2)); + AActor *marine; + NActorIterator iterator("ScriptedMarine", STACK(2)); while ((marine = iterator.Next()) != NULL) { - marine->SetWeapon ((AScriptedMarine::EMarineWeapon)STACK(1)); + SetMarineWeapon(marine, STACK(1)); } } else { - if (activator != NULL && activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine))) + if (activator != nullptr && activator->IsKindOf (PClass::FindClass("ScriptedMarine"))) { - barrier_cast(activator)->SetWeapon ( - (AScriptedMarine::EMarineWeapon)STACK(1)); + SetMarineWeapon(activator, STACK(1)); } } sp -= 2; @@ -8973,19 +8993,19 @@ scriptwait: { if (STACK(2) != 0) { - AScriptedMarine *marine; - TActorIterator iterator (STACK(2)); + AActor *marine; + NActorIterator iterator("ScriptedMarine", STACK(2)); while ((marine = iterator.Next()) != NULL) { - marine->SetSprite (type); + SetMarineSprite(marine, type); } } else { - if (activator != NULL && activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine))) + if (activator != nullptr && activator->IsKindOf(PClass::FindClass("ScriptedMarine"))) { - barrier_cast(activator)->SetSprite (type); + SetMarineSprite(activator, type); } } } diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 967d5a9e5..477ed37f2 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -53,7 +53,6 @@ #include "p_lnspec.h" #include "p_effect.h" #include "p_enemy.h" -#include "a_action.h" #include "decallib.h" #include "m_random.h" #include "i_system.h" @@ -61,7 +60,6 @@ #include "c_console.h" #include "doomerrors.h" #include "a_sharedglobal.h" -#include "vm.h" #include "v_video.h" #include "v_font.h" #include "doomstat.h" @@ -77,10 +75,11 @@ #include "p_maputl.h" #include "p_spec.h" #include "templates.h" -#include "vm.h" #include "v_text.h" #include "thingdef.h" #include "math/cmath.h" +#include "a_armor.h" +#include "a_health.h" AActor *SingleActorFromTID(int tid, AActor *defactor); @@ -145,11 +144,10 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state) // If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash. auto owner = FState::StaticFindStateOwner(state); Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s which accesses user variables. The action function has been removed from this state\n", - owner->TypeName.GetChars(), state - owner->OwnedStates, static_cast(state->ActionFunc)->PrintableName.GetChars()); + owner->TypeName.GetChars(), int(state - owner->OwnedStates), state->ActionFunc->PrintableName.GetChars()); state->ActionFunc = nullptr; } - VMFrameStack stack; PPrototype *proto = state->ActionFunc->Proto; VMReturn *wantret; FStateParamInfo stp = { state, STATE_StateChain, PSP_WEAPON }; @@ -185,7 +183,7 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state) numret = 2; } } - stack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); + GlobalVMStack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); // As long as even one state succeeds, the whole chain succeeds unless aborted below. // A state that wants to jump does not count as "succeeded". if (nextstate == NULL) @@ -306,7 +304,7 @@ DEFINE_ACTION_FUNCTION(AActor, GetMissileDamage) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CountInv) +DEFINE_ACTION_FUNCTION(AActor, CountInv) { if (numret > 0) { @@ -337,7 +335,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CountInv) // NON-ACTION function to get the distance in double. // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetDistance) +DEFINE_ACTION_FUNCTION(AActor, GetDistance) { if (numret > 0) { @@ -380,7 +378,7 @@ enum GAFlags GAF_SWITCH = 1 << 1, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetAngle) +DEFINE_ACTION_FUNCTION(AActor, GetAngle) { if (numret > 0) { @@ -413,7 +411,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetAngle) // GetSpawnHealth // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetSpawnHealth) +DEFINE_ACTION_FUNCTION(AActor, GetSpawnHealth) { if (numret > 0) { @@ -429,7 +427,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetSpawnHealth) // GetGibHealth // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetGibHealth) +DEFINE_ACTION_FUNCTION(AActor, GetGibHealth) { if (numret > 0) { @@ -446,7 +444,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetGibHealth) // // NON-ACTION function returns the sprite angle of a pointer. //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetSpriteAngle) +DEFINE_ACTION_FUNCTION(AActor, GetSpriteAngle) { if (numret > 0) { @@ -475,7 +473,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetSpriteAngle) // // NON-ACTION function returns the sprite rotation of a pointer. //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetSpriteRotation) +DEFINE_ACTION_FUNCTION(AActor, GetSpriteRotation) { if (numret > 0) { @@ -515,7 +513,7 @@ enum GZFlags GZF_NO3DFLOOR = 1 << 5, // Pass all 3D floors. }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetZAt) +DEFINE_ACTION_FUNCTION(AActor, GetZAt) { if (numret > 0) { @@ -603,7 +601,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetZAt) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetCrouchFactor) +DEFINE_ACTION_FUNCTION(AActor, GetCrouchFactor) { if (numret > 0) { @@ -633,7 +631,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetCrouchFactor) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetCVar) +DEFINE_ACTION_FUNCTION(AActor, GetCVar) { if (numret > 0) { @@ -663,7 +661,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetCVar) // Takes a pointer as anyone may or may not be a player. //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetPlayerInput) +DEFINE_ACTION_FUNCTION(AActor, GetPlayerInput) { if (numret > 0) { @@ -696,7 +694,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetPlayerInput) // Takes a pointer as anyone may or may not be a player. //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CountProximity) +DEFINE_ACTION_FUNCTION(AActor, CountProximity) { if (numret > 0) { @@ -731,21 +729,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CountProximity) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_int__) +DEFINE_ACTION_FUNCTION(AActor, __decorate_internal_int__) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(returnme); ACTION_RETURN_INT(returnme); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_bool__) +DEFINE_ACTION_FUNCTION(AActor, __decorate_internal_bool__) { PARAM_SELF_PROLOGUE(AActor); PARAM_BOOL(returnme); ACTION_RETURN_BOOL(returnme); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_float__) +DEFINE_ACTION_FUNCTION(AActor, __decorate_internal_float__) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(returnme); @@ -768,7 +766,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_float__) //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) +DEFINE_ACTION_FUNCTION(AActor, A_RearrangePointers) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (ptr_target); @@ -848,7 +846,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) +DEFINE_ACTION_FUNCTION(AActor, A_TransferPointer) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (ptr_source); @@ -891,7 +889,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) +DEFINE_ACTION_FUNCTION(AActor, A_CopyFriendliness) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF (ptr_source); @@ -974,7 +972,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) +DEFINE_ACTION_FUNCTION(AActor, A_BasicAttack) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (melee_damage); @@ -995,7 +993,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) +DEFINE_ACTION_FUNCTION(AActor, A_PlaySound) { PARAM_SELF_PROLOGUE(AActor); PARAM_SOUND_DEF (soundid); @@ -1019,7 +1017,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) +DEFINE_ACTION_FUNCTION(AActor, A_StopSound) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(slot); @@ -1037,7 +1035,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) +DEFINE_ACTION_FUNCTION(AActor, A_PlaySoundEx) { PARAM_SELF_PROLOGUE(AActor); PARAM_SOUND (soundid); @@ -1074,7 +1072,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) +DEFINE_ACTION_FUNCTION(AActor, A_StopSoundEx) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME(channel); @@ -1098,7 +1096,7 @@ enum SMF_PRECISE = 2, SMF_CURSPEED = 4, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) +DEFINE_ACTION_FUNCTION(AActor, A_SeekerMissile) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(ang1); @@ -1155,7 +1153,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) // State jump function // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) +DEFINE_ACTION_FUNCTION(AActor, A_Jump) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT(maxchance); @@ -1219,7 +1217,7 @@ DEFINE_ACTION_FUNCTION(AActor, CheckInventory) // State jump function // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckArmorType) +DEFINE_ACTION_FUNCTION(AActor, CheckArmorType) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME (type); @@ -1244,7 +1242,7 @@ enum XF_NOSPLASH = 16, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) +DEFINE_ACTION_FUNCTION(AActor, A_Explode) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF (damage); @@ -1313,7 +1311,7 @@ enum RTF_NOTMISSILE = 4, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) +DEFINE_ACTION_FUNCTION(AActor, A_RadiusThrust) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF (force); @@ -1353,7 +1351,7 @@ enum RDSF_BFGDAMAGE = 1, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusDamageSelf) +DEFINE_ACTION_FUNCTION(AActor, A_RadiusDamageSelf) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(damage); @@ -1414,7 +1412,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusDamageSelf) // Execute a line special / script // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) +DEFINE_ACTION_FUNCTION(AActor, A_CallSpecial) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (special); @@ -1447,7 +1445,7 @@ enum CM_Flags CMF_ABSOLUTEANGLE = 128 }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) +DEFINE_ACTION_FUNCTION(AActor, A_CustomMissile) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (ti, AActor); @@ -1596,7 +1594,7 @@ enum CBA_Flags static void AimBulletMissile(AActor *proj, AActor *puff, int flags, bool temp, bool cba); -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) +DEFINE_ACTION_FUNCTION(AActor, A_CustomBulletAttack) { PARAM_SELF_PROLOGUE(AActor); PARAM_ANGLE (spread_xy); @@ -1688,7 +1686,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) // A fully customizable melee attack // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMeleeAttack) +DEFINE_ACTION_FUNCTION(AActor, A_CustomMeleeAttack) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF (damage); @@ -1725,7 +1723,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMeleeAttack) // A fully customizable combo attack // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) +DEFINE_ACTION_FUNCTION(AActor, A_CustomComboAttack) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (ti, AActor); @@ -1775,7 +1773,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) // State jump function // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) +DEFINE_ACTION_FUNCTION(AActor, A_JumpIfNoAmmo) { PARAM_ACTION_PROLOGUE(AActor); PARAM_STATE_ACTION(jump); @@ -1846,7 +1844,7 @@ static void AimBulletMissile(AActor *proj, AActor *puff, int flags, bool temp, b } } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) +DEFINE_ACTION_FUNCTION(AActor, A_FireBullets) { PARAM_ACTION_PROLOGUE(AActor); PARAM_ANGLE (spread_xy); @@ -1976,7 +1974,7 @@ enum FP_Flags FPF_TRANSFERTRANSLATION = 2, FPF_NOAUTOAIM = 4, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireCustomMissile) +DEFINE_ACTION_FUNCTION(AActor, A_FireCustomMissile) { PARAM_ACTION_PROLOGUE(AActor); PARAM_CLASS (ti, AActor); @@ -2053,7 +2051,7 @@ enum CPF_STEALARMOR = 32, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) +DEFINE_ACTION_FUNCTION(AActor, A_CustomPunch) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT (damage); @@ -2157,7 +2155,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) // customizable railgun attack function // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) +DEFINE_ACTION_FUNCTION(AActor, A_RailAttack) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT (damage); @@ -2236,7 +2234,7 @@ enum CRF_EXPLICITANGLE = 4, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) +DEFINE_ACTION_FUNCTION(AActor, A_CustomRailgun) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (damage); @@ -2409,19 +2407,19 @@ static bool DoGiveInventory(AActor *receiver, bool orresult, VM_ARGS) return false; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) +DEFINE_ACTION_FUNCTION(AActor, A_GiveInventory) { PARAM_SELF_PROLOGUE(AActor); ACTION_RETURN_BOOL(DoGiveInventory(self, false, VM_ARGS_NAMES)); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) +DEFINE_ACTION_FUNCTION(AActor, A_GiveToTarget) { PARAM_SELF_PROLOGUE(AActor); ACTION_RETURN_BOOL(DoGiveInventory(self->target, false, VM_ARGS_NAMES)); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) +DEFINE_ACTION_FUNCTION(AActor, A_GiveToChildren) { PARAM_SELF_PROLOGUE(AActor); @@ -2439,7 +2437,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren) ACTION_RETURN_INT(count); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) +DEFINE_ACTION_FUNCTION(AActor, A_GiveToSiblings) { PARAM_SELF_PROLOGUE(AActor); @@ -2466,7 +2464,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetInventory) +DEFINE_ACTION_FUNCTION(AActor, A_SetInventory) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS(itemtype, AInventory); @@ -2581,19 +2579,19 @@ bool DoTakeInventory(AActor *receiver, bool orresult, VM_ARGS) return receiver->TakeInventory(itemtype, amount, true, (flags & TIF_NOTAKEINFINITE) != 0); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) +DEFINE_ACTION_FUNCTION(AActor, A_TakeInventory) { PARAM_SELF_PROLOGUE(AActor); ACTION_RETURN_BOOL(DoTakeInventory(self, false, VM_ARGS_NAMES)); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) +DEFINE_ACTION_FUNCTION(AActor, A_TakeFromTarget) { PARAM_SELF_PROLOGUE(AActor); ACTION_RETURN_BOOL(DoTakeInventory(self->target, false, VM_ARGS_NAMES)); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) +DEFINE_ACTION_FUNCTION(AActor, A_TakeFromChildren) { PARAM_SELF_PROLOGUE(AActor); TThinkerIterator it; @@ -2610,7 +2608,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren) ACTION_RETURN_INT(count); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings) +DEFINE_ACTION_FUNCTION(AActor, A_TakeFromSiblings) { PARAM_SELF_PROLOGUE(AActor); TThinkerIterator it; @@ -2846,7 +2844,7 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) +DEFINE_ACTION_FUNCTION(AActor, A_SpawnItem) { PARAM_ACTION_PROLOGUE(AActor); PARAM_CLASS_DEF (missile, AActor) @@ -2894,7 +2892,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) // Enhanced spawning function // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) +DEFINE_ACTION_FUNCTION(AActor, A_SpawnItemEx) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (missile, AActor); @@ -2978,7 +2976,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) // Throws a grenade (like Hexen's fighter flechette) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) +DEFINE_ACTION_FUNCTION(AActor, A_ThrowGrenade) { PARAM_ACTION_PROLOGUE(AActor); PARAM_CLASS (missile, AActor); @@ -3056,7 +3054,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) // A_Recoil // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) +DEFINE_ACTION_FUNCTION(AActor, A_Recoil) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(xyvel); @@ -3075,7 +3073,7 @@ enum SW_Flags { SWF_SELECTPRIORITY = 1, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) +DEFINE_ACTION_FUNCTION(AActor, A_SelectWeapon) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS(cls, AWeapon); @@ -3120,7 +3118,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) //=========================================================================== EXTERN_CVAR(Float, con_midtime) -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) +DEFINE_ACTION_FUNCTION(AActor, A_Print) { PARAM_SELF_PROLOGUE(AActor); PARAM_STRING (text); @@ -3155,7 +3153,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) +DEFINE_ACTION_FUNCTION(AActor, A_PrintBold) { PARAM_SELF_PROLOGUE(AActor); PARAM_STRING (text); @@ -3186,7 +3184,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) +DEFINE_ACTION_FUNCTION(AActor, A_Log) { PARAM_SELF_PROLOGUE(AActor); PARAM_STRING(text); @@ -3203,7 +3201,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) +DEFINE_ACTION_FUNCTION(AActor, A_LogInt) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(num); @@ -3217,7 +3215,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogFloat) +DEFINE_ACTION_FUNCTION(AActor, A_LogFloat) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(num); @@ -3232,7 +3230,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogFloat) // A_SetTranslucent // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) +DEFINE_ACTION_FUNCTION(AActor, A_SetTranslucent) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT (alpha); @@ -3251,7 +3249,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) // A_SetRenderStyle // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRenderStyle) +DEFINE_ACTION_FUNCTION(AActor, A_SetRenderStyle) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(alpha); @@ -3276,7 +3274,7 @@ enum FadeFlags FTF_CLAMP = 1 << 1, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) +DEFINE_ACTION_FUNCTION(AActor, A_FadeIn) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT_DEF(reduce); @@ -3311,7 +3309,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) // fades the actor out and destroys it when done // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) +DEFINE_ACTION_FUNCTION(AActor, A_FadeOut) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT_DEF(reduce); @@ -3345,7 +3343,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) // destroys it if so desired // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) +DEFINE_ACTION_FUNCTION(AActor, A_FadeTo) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT (target); @@ -3388,7 +3386,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) // A_SpawnDebris // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) +DEFINE_ACTION_FUNCTION(AActor, A_SpawnDebris) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (debris, AActor); @@ -3444,7 +3442,7 @@ enum SPFflag SPF_NOTIMEFREEZE = 1 << 5, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnParticle) +DEFINE_ACTION_FUNCTION(AActor, A_SpawnParticle) { PARAM_SELF_PROLOGUE(AActor); PARAM_COLOR (color); @@ -3505,7 +3503,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnParticle) // jumps if no player can see this actor // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfSeen) +DEFINE_ACTION_FUNCTION(AActor, CheckIfSeen) { PARAM_SELF_PROLOGUE(AActor); @@ -3573,7 +3571,7 @@ static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool return false; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckSightOrRange) +DEFINE_ACTION_FUNCTION(AActor, CheckSightOrRange) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(range); @@ -3601,7 +3599,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckSightOrRange) } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckRange) +DEFINE_ACTION_FUNCTION(AActor, CheckRange) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(range); @@ -3634,7 +3632,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckRange) // Inventory drop // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) +DEFINE_ACTION_FUNCTION(AActor, A_DropInventory) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS(drop, AInventory); @@ -3656,7 +3654,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) // A_SetBlend // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) +DEFINE_ACTION_FUNCTION(AActor, A_SetBlend) { PARAM_SELF_PROLOGUE(AActor); PARAM_COLOR (color); @@ -3684,7 +3682,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) +DEFINE_ACTION_FUNCTION(AActor, A_CountdownArg) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(cnt); @@ -3716,7 +3714,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) // //============================================================================ -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) +DEFINE_ACTION_FUNCTION(AActor, A_Burst) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS(chunk, AActor); @@ -3803,8 +3801,6 @@ static void CheckStopped(AActor *self) // //=========================================================================== -DECLARE_ACTION(A_RestoreSpecialPosition) - enum RS_Flags { RSF_FOG=1, @@ -3812,7 +3808,7 @@ enum RS_Flags RSF_TELEFRAG=4, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) +DEFINE_ACTION_FUNCTION(AActor, A_Respawn) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); @@ -3823,7 +3819,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) self->flags |= MF_SOLID; self->Height = self->GetDefault()->Height; self->radius = self->GetDefault()->radius; - CALL_ACTION(A_RestoreSpecialPosition, self); + self->RestoreSpecialPosition(); if (flags & RSF_TELEFRAG) { @@ -4029,7 +4025,7 @@ ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) return TRACE_Abort; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckLOF) +DEFINE_ACTION_FUNCTION(AActor, CheckLOF) { // Check line of fire @@ -4230,7 +4226,7 @@ enum JLOS_flags JLOSF_CHECKTRACER = 1 << 12, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfTargetInLOS) +DEFINE_ACTION_FUNCTION(AActor, CheckIfTargetInLOS) { PARAM_SELF_PROLOGUE(AActor); PARAM_ANGLE_DEF (fov) @@ -4367,7 +4363,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfTargetInLOS) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfInTargetLOS) +DEFINE_ACTION_FUNCTION(AActor, CheckIfInTargetLOS) { PARAM_SELF_PROLOGUE(AActor); PARAM_ANGLE_DEF (fov) @@ -4447,7 +4443,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfInTargetLOS) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) +DEFINE_ACTION_FUNCTION(AActor, A_CheckForReload) { PARAM_ACTION_PROLOGUE(AActor); @@ -4517,7 +4513,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) // A_ChangeFlag // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) +DEFINE_ACTION_FUNCTION(AActor, A_ChangeFlag) { PARAM_SELF_PROLOGUE(AActor); PARAM_STRING (flagname); @@ -4533,7 +4529,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckFlag) +DEFINE_ACTION_FUNCTION(AActor, CheckFlag) { PARAM_SELF_PROLOGUE(AActor); PARAM_STRING (flagname); @@ -4544,7 +4540,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckFlag) } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeCountFlags) +DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(kill); @@ -4651,7 +4647,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) // Keep firing unless target got out of sight // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) +DEFINE_ACTION_FUNCTION(AActor, A_MonsterRefire) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (prob); @@ -4687,7 +4683,7 @@ enum }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) +DEFINE_ACTION_FUNCTION(AActor, A_SetAngle) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT_DEF(angle); @@ -4710,7 +4706,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) +DEFINE_ACTION_FUNCTION(AActor, A_SetPitch) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(pitch); @@ -4734,7 +4730,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) +DEFINE_ACTION_FUNCTION(AActor, A_SetRoll) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT (roll); @@ -4757,7 +4753,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRoll) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) +DEFINE_ACTION_FUNCTION(AActor, A_ScaleVelocity) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(scale); @@ -4789,7 +4785,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) +DEFINE_ACTION_FUNCTION(AActor, A_ChangeVelocity) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT_DEF (x) @@ -4851,7 +4847,7 @@ static PField *GetVar(DObject *self, FName varname) return var; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) +DEFINE_ACTION_FUNCTION(AActor, A_SetUserVar) { PARAM_SELF_PROLOGUE(DObject); PARAM_NAME (varname); @@ -4866,7 +4862,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVarFloat) +DEFINE_ACTION_FUNCTION(AActor, A_SetUserVarFloat) { PARAM_SELF_PROLOGUE(DObject); PARAM_NAME (varname); @@ -4908,7 +4904,7 @@ static PField *GetArrayVar(DObject *self, FName varname, int pos) return var; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +DEFINE_ACTION_FUNCTION(AActor, A_SetUserArray) { PARAM_SELF_PROLOGUE(DObject); PARAM_NAME (varname); @@ -4925,7 +4921,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArrayFloat) +DEFINE_ACTION_FUNCTION(AActor, A_SetUserArrayFloat) { PARAM_SELF_PROLOGUE(DObject); PARAM_NAME (varname); @@ -4969,7 +4965,7 @@ enum T_Flags TF_SENSITIVEZ = 0x00000800, // Fail if the actor wouldn't fit in the position (for Z). }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) +DEFINE_ACTION_FUNCTION(AActor, A_Teleport) { PARAM_ACTION_PROLOGUE(AActor); PARAM_STATE_DEF (teleport_state) @@ -5145,7 +5141,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) +DEFINE_ACTION_FUNCTION(AActor, A_Quake) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (intensity); @@ -5166,7 +5162,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) // take flags. //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_QuakeEx) +DEFINE_ACTION_FUNCTION(AActor, A_QuakeEx) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(intensityX); @@ -5237,7 +5233,7 @@ void A_Weave(AActor *self, int xyspeed, int zspeed, double xydist, double zdist) } } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) +DEFINE_ACTION_FUNCTION(AActor, A_Weave) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (xspeed); @@ -5259,7 +5255,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) +DEFINE_ACTION_FUNCTION(AActor, A_LineEffect) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(special); @@ -5294,7 +5290,7 @@ enum WolfAttackFlags WAF_USEPUFF = 2, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) +DEFINE_ACTION_FUNCTION(AActor, A_WolfAttack) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF (flags) @@ -5399,7 +5395,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) +DEFINE_ACTION_FUNCTION(AActor, A_Warp) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT(destination_selector); @@ -5651,7 +5647,7 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo return false; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) +DEFINE_ACTION_FUNCTION(AActor, A_RadiusGive) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (item, AInventory); @@ -5704,7 +5700,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) +DEFINE_ACTION_FUNCTION(AActor, A_SetTics) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT(tics_to_set); @@ -5733,7 +5729,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) +DEFINE_ACTION_FUNCTION(AActor, A_DropItem) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS (spawntype, AActor); @@ -5814,7 +5810,7 @@ static void DoDamage(AActor *dmgtarget, AActor *inflictor, AActor *source, int a // // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +DEFINE_ACTION_FUNCTION(AActor, A_DamageSelf) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (amount); @@ -5837,7 +5833,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) // // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +DEFINE_ACTION_FUNCTION(AActor, A_DamageTarget) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (amount); @@ -5861,7 +5857,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) // // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +DEFINE_ACTION_FUNCTION(AActor, A_DamageTracer) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (amount); @@ -5885,7 +5881,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) // // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +DEFINE_ACTION_FUNCTION(AActor, A_DamageMaster) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (amount); @@ -5909,7 +5905,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) // // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +DEFINE_ACTION_FUNCTION(AActor, A_DamageChildren) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (amount); @@ -5939,7 +5935,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) // // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +DEFINE_ACTION_FUNCTION(AActor, A_DamageSiblings) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (amount); @@ -6022,7 +6018,7 @@ static void DoKill(AActor *killtarget, AActor *inflictor, AActor *source, FName // A_KillTarget(damagetype, int flags) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +DEFINE_ACTION_FUNCTION(AActor, A_KillTarget) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME_DEF (damagetype) @@ -6045,7 +6041,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) // A_KillTracer(damagetype, int flags) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +DEFINE_ACTION_FUNCTION(AActor, A_KillTracer) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME_DEF (damagetype) @@ -6068,7 +6064,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) // A_KillMaster(damagetype, int flags) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +DEFINE_ACTION_FUNCTION(AActor, A_KillMaster) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME_DEF (damagetype) @@ -6091,7 +6087,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) // A_KillChildren(damagetype, int flags) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +DEFINE_ACTION_FUNCTION(AActor, A_KillChildren) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME_DEF (damagetype) @@ -6122,7 +6118,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) // A_KillSiblings(damagetype, int flags) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +DEFINE_ACTION_FUNCTION(AActor, A_KillSiblings) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME_DEF (damagetype) @@ -6198,7 +6194,7 @@ static void DoRemove(AActor *removetarget, int flags, PClassActor *filter, FName // A_RemoveTarget // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); @@ -6217,7 +6213,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) // A_RemoveTracer // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); @@ -6236,7 +6232,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) // A_RemoveMaster // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) +DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); @@ -6255,7 +6251,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) // A_RemoveChildren // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +DEFINE_ACTION_FUNCTION(AActor, A_RemoveChildren) { PARAM_SELF_PROLOGUE(AActor); PARAM_BOOL_DEF(removeall); @@ -6281,7 +6277,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) // A_RemoveSiblings // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +DEFINE_ACTION_FUNCTION(AActor, A_RemoveSiblings) { PARAM_SELF_PROLOGUE(AActor); PARAM_BOOL_DEF(removeall); @@ -6310,7 +6306,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) // A_Remove // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) +DEFINE_ACTION_FUNCTION(AActor, A_Remove) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(removee); @@ -6334,7 +6330,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) // Takes a name of the classes for the source and destination. //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTeleFog) +DEFINE_ACTION_FUNCTION(AActor, A_SetTeleFog) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS(oldpos, AActor); @@ -6379,7 +6375,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog) // Takes a pointer as well. //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) +DEFINE_ACTION_FUNCTION(AActor, A_SetHealth) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT (health); @@ -6417,7 +6413,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetHealth) // Takes a pointer. //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) +DEFINE_ACTION_FUNCTION(AActor, A_ResetHealth) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(ptr); @@ -6446,7 +6442,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth) // // Sets the species of the calling actor('s pointer). //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecies) +DEFINE_ACTION_FUNCTION(AActor, A_SetSpecies) { PARAM_SELF_PROLOGUE(AActor); PARAM_NAME(species); @@ -6470,7 +6466,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecies) // changes the default threshold which the actor resets to once it switches // targets and doesn't have the +QUICKTORETALIATE flag. //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetChaseThreshold) +DEFINE_ACTION_FUNCTION(AActor, A_SetChaseThreshold) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(threshold); @@ -6497,7 +6493,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetChaseThreshold) // Checks to see if a certain actor class is close to the // actor/pointer within distance, in numbers. //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckProximity) +DEFINE_ACTION_FUNCTION(AActor, CheckProximity) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS(classname, AActor); @@ -6530,7 +6526,7 @@ enum CBF CBF_ABSOLUTEANGLE = 1 << 8, //Absolute angle for offsets. }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckBlock) +DEFINE_ACTION_FUNCTION(AActor, CheckBlock) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags) @@ -6629,7 +6625,7 @@ enum FMDFlags FMDF_INTERPOLATE = 1 << 1, FMDF_NOANGLE = 1 << 2, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FaceMovementDirection) +DEFINE_ACTION_FUNCTION(AActor, A_FaceMovementDirection) { PARAM_SELF_PROLOGUE(AActor); PARAM_ANGLE_DEF(offset) @@ -6723,7 +6719,7 @@ enum CPSFFlags CPSF_NOFRAME = 1 << 1, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopySpriteFrame) +DEFINE_ACTION_FUNCTION(AActor, A_CopySpriteFrame) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(from); @@ -6758,7 +6754,7 @@ enum VRFFlags VRF_NOPITCHEND = 1 << 3, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetVisibleRotation) +DEFINE_ACTION_FUNCTION(AActor, A_SetVisibleRotation) { PARAM_SELF_PROLOGUE(AActor); PARAM_ANGLE_DEF(anglestart) @@ -6801,11 +6797,40 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetVisibleRotation) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslation) +DEFINE_ACTION_FUNCTION(AActor, A_SetTranslation) { PARAM_SELF_PROLOGUE(AActor); - PARAM_STRING(trname); + PARAM_NAME(trname); self->SetTranslation(trname); return 0; } + +//========================================================================== +// +// +// +//========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain) +{ + PARAM_SELF_PROLOGUE(AActor); + + sector_t *sec = self->Sector; + + if (self->Z() == sec->floorplane.ZatPoint(self) && sec->PortalBlocksMovement(sector_t::floor)) + { + if (sec->special == Damage_InstantDeath) + { + P_DamageMobj(self, NULL, NULL, 999, NAME_InstantDeath); + } + else if (sec->special == Scroll_StrifeCurrent) + { + int anglespeed = tagManager.GetFirstSectorTag(sec) - 100; + double speed = (anglespeed % 10) / 16.; + DAngle an = (anglespeed / 10) * (360 / 8.); + self->Thrust(an, speed); + } + } + return 0; +} diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index b3526c60d..4d1909da5 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -865,7 +865,7 @@ public: void BeginPlay (); }; -IMPLEMENT_CLASS(ACustomSprite, false, false, false, false) +IMPLEMENT_CLASS(ACustomSprite, false, false) void ACustomSprite::BeginPlay () { diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index d22dd6371..932b4c649 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -38,7 +38,7 @@ // //============================================================================ -IMPLEMENT_CLASS(DCeiling, false, false, false, false) +IMPLEMENT_CLASS(DCeiling, false, false) DCeiling::DCeiling () { @@ -458,6 +458,22 @@ bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int t return ceiling != NULL; } +DEFINE_ACTION_FUNCTION(DCeiling, CreateCeiling) +{ + PARAM_PROLOGUE; + PARAM_POINTER(sec, sector_t); + PARAM_INT(type); + PARAM_POINTER(ln, line_t); + PARAM_FLOAT(speed); + PARAM_FLOAT(speed2); + PARAM_FLOAT_DEF(height); + PARAM_INT_DEF(crush); + PARAM_INT_DEF(silent); + PARAM_INT_DEF(change); + PARAM_INT_DEF(crushmode); + ACTION_RETURN_BOOL(P_CreateCeiling(sec, (DCeiling::ECeiling)type, ln, 0, speed, speed2, height, crush, silent, change, (DCeiling::ECrushMode)crushmode)); +} + //============================================================================ // // EV_DoCeiling diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 5b37268b8..5fd21e73b 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -44,7 +44,6 @@ #include "m_random.h" #include "gi.h" #include "templates.h" -#include "a_strifeglobal.h" #include "a_keys.h" #include "p_enemy.h" #include "gstrings.h" @@ -252,7 +251,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc if ((type == 1 && !isbinary) || (type == 2 && isbinary)) { - DPrintf(DMSG_ERROR, "Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); + DPrintf(DMSG_ERROR, "Incorrect data format for conversation script in %s.\n", Wads.GetLumpFullName(lumpnum)); return false; } @@ -272,7 +271,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc // is exactly 1516 bytes long. if (numnodes % 1516 != 0) { - DPrintf(DMSG_ERROR, "Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); + DPrintf(DMSG_ERROR, "Incorrect data format for conversation script in %s.\n", Wads.GetLumpFullName(lumpnum)); return false; } numnodes /= 1516; @@ -282,7 +281,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc // And the teaser version has 1488-byte entries. if (numnodes % 1488 != 0) { - DPrintf(DMSG_ERROR, "Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); + DPrintf(DMSG_ERROR, "Incorrect data format for conversation script in %s.\n", Wads.GetLumpFullName(lumpnum)); return false; } numnodes /= 1488; @@ -646,7 +645,7 @@ static void TakeStrifeItem (player_t *player, PClassActor *itemtype, int amount) return; // Don't take the sigil. - if (itemtype == RUNTIME_CLASS(ASigil)) + if (itemtype->GetClass()->TypeName == NAME_Sigil) return; player->mo->TakeInventory(itemtype, amount); @@ -834,6 +833,7 @@ public: V_FreeBrokenLines(mDialogueLines); mDialogueLines = NULL; I_SetMusicVolume (1.f); + Super::Destroy(); } bool DimAllowed() @@ -1052,18 +1052,22 @@ public: if (ShowGold) { - AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); - char goldstr[32]; + auto cointype = PClass::FindActor("Coin"); + if (cointype) + { + AInventory *coin = cp->ConversationPC->FindInventory(cointype); + char goldstr[32]; - mysnprintf (goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0); - screen->DrawText (SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true, - DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE); - screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), - 3, 190, DTA_320x200, true, - DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE); - screen->DrawText (SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE); - screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), - 2, 189, DTA_320x200, true, TAG_DONE); + mysnprintf(goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0); + screen->DrawText(SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true, + DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE); + screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon), + 3, 190, DTA_320x200, true, + DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE); + screen->DrawText(SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE); + screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon), + 2, 189, DTA_320x200, true, TAG_DONE); + } } y = mYpos; @@ -1103,7 +1107,7 @@ public: }; -IMPLEMENT_CLASS(DConversationMenu, true, false, false, false) +IMPLEMENT_CLASS(DConversationMenu, true, false) int DConversationMenu::mSelection; // needs to be preserved if the same dialogue is restarted @@ -1352,7 +1356,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply } } - if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(ASlideshowStarter))) + if (reply->GiveType->IsDescendantOf(PClass::FindActor("SlideshowStarter"))) gameaction = ga_slideshow; } else diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 542308fa6..aa59e5463 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -45,7 +45,7 @@ // //============================================================================ -IMPLEMENT_CLASS(DDoor, false, false, false, false) +IMPLEMENT_CLASS(DDoor, false, false) DDoor::DDoor () { @@ -513,7 +513,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, // //============================================================================ -IMPLEMENT_CLASS(DAnimatedDoor, false, false, false, false) +IMPLEMENT_CLASS(DAnimatedDoor, false, false) DAnimatedDoor::DAnimatedDoor () { diff --git a/src/p_effect.cpp b/src/p_effect.cpp index ec051c299..42e82d740 100644 --- a/src/p_effect.cpp +++ b/src/p_effect.cpp @@ -574,6 +574,16 @@ void P_DrawSplash (int count, const DVector3 &pos, DAngle angle, int kind) } } +DEFINE_ACTION_FUNCTION(AActor, DrawSplash) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(count); + PARAM_FLOAT(angle); + PARAM_INT(kind); + P_DrawSplash(count, self->Pos(), angle, kind); + return 0; +} + void P_DrawSplash2 (int count, const DVector3 &pos, DAngle angle, int updown, int kind) { int color1, color2, zadd; diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index cbddb4840..751f92a30 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -42,8 +42,6 @@ #include "c_cvars.h" #include "p_enemy.h" #include "a_sharedglobal.h" -#include "a_action.h" -#include "vm.h" #include "d_dehacked.h" #include "g_level.h" #include "r_utility.h" @@ -53,7 +51,7 @@ #include "p_spec.h" #include "p_checkposition.h" #include "math/cmath.h" -#include "vm.h" +#include "a_ammo.h" #include "gi.h" @@ -264,7 +262,80 @@ void P_NoiseAlert (AActor *target, AActor *emitter, bool splash, double maxdist) } } +DEFINE_ACTION_FUNCTION(AActor, NoiseAlert) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(target, AActor); + PARAM_BOOL_DEF(splash); + PARAM_FLOAT_DEF(maxdist); + // Note that the emitter is self, not the target of the alert! Target can be NULL. + P_NoiseAlert(target, self, splash, maxdist); + return 0; +} +//============================================================================ +// +// P_DaggerAlert +// +//============================================================================ + +void P_DaggerAlert(AActor *target, AActor *emitter) +{ + AActor *looker; + sector_t *sec = emitter->Sector; + + if (emitter->LastHeard != NULL) + return; + if (emitter->health <= 0) + return; + if (!(emitter->flags3 & MF3_ISMONSTER)) + return; + if (emitter->flags4 & MF4_INCOMBAT) + return; + emitter->flags4 |= MF4_INCOMBAT; + + emitter->target = target; + FState *painstate = emitter->FindState(NAME_Pain, NAME_Dagger); + if (painstate != NULL) + { + emitter->SetState(painstate); + } + + for (looker = sec->thinglist; looker != NULL; looker = looker->snext) + { + if (looker == emitter || looker == target) + continue; + + if (looker->health <= 0) + continue; + + if (!(looker->flags4 & MF4_SEESDAGGERS)) + continue; + + if (!(looker->flags4 & MF4_INCOMBAT)) + { + if (!P_CheckSight(looker, target) && !P_CheckSight(looker, emitter)) + continue; + + looker->target = target; + if (looker->SeeSound) + { + S_Sound(looker, CHAN_VOICE, looker->SeeSound, 1, ATTN_NORM); + } + looker->SetState(looker->SeeState); + looker->flags4 |= MF4_INCOMBAT; + } + } +} + +DEFINE_ACTION_FUNCTION(AActor, DaggerAlert) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(target, AActor); + // Note that the emitter is self, not the target of the alert! Target can be NULL. + P_DaggerAlert(target, self); + return 0; +} //---------------------------------------------------------------------------- @@ -359,6 +430,12 @@ bool P_CheckMeleeRange2 (AActor *actor) return true; } +DEFINE_ACTION_FUNCTION(AActor, CheckMeleeRange2) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_INT(P_CheckMeleeRange2(self)); +} + //============================================================================= // @@ -1207,6 +1284,14 @@ void P_RandomChaseDir (AActor *actor) actor->movedir = DI_NODIR; // cannot move } +DEFINE_ACTION_FUNCTION(AActor, RandomChaseDir) +{ + PARAM_SELF_PROLOGUE(AActor); + P_RandomChaseDir(self); + return 0; +} + + //--------------------------------------------------------------------------- // // P_IsVisible @@ -1259,6 +1344,15 @@ bool P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams return P_CheckSight(lookee, other, SF_SEEPASTSHOOTABLELINES); } +DEFINE_ACTION_FUNCTION(AActor, IsVisible) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(other, AActor); + PARAM_BOOL(allaround); + PARAM_POINTER_DEF(params, FLookExParams); + ACTION_RETURN_BOOL(P_IsVisible(self, other, allaround, params)); +} + //--------------------------------------------------------------------------- // // FUNC P_LookForMonsters @@ -1312,6 +1406,12 @@ bool P_LookForMonsters (AActor *actor) return false; } +DEFINE_ACTION_FUNCTION(AActor, LookForMonsters) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(P_LookForMonsters(self)); +} + //============================================================================ // // LookForTIDinBlock @@ -1484,6 +1584,14 @@ bool P_LookForTID (AActor *actor, INTBOOL allaround, FLookExParams *params) return false; } +DEFINE_ACTION_FUNCTION(AActor, LookForTID) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL(allaround); + PARAM_POINTER_DEF(params, FLookExParams); + ACTION_RETURN_BOOL(P_LookForTID(self, allaround, params)); +} + //============================================================================ // // LookForEnemiesinBlock @@ -1623,6 +1731,15 @@ bool P_LookForEnemies (AActor *actor, INTBOOL allaround, FLookExParams *params) return false; } +DEFINE_ACTION_FUNCTION(AActor, LookForEnemies) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL(allaround); + PARAM_POINTER_DEF(params, FLookExParams); + ACTION_RETURN_BOOL(P_LookForEnemies(self, allaround, params)); +} + + /* ================ = @@ -1810,7 +1927,8 @@ DEFINE_ACTION_FUNCTION(AActor, LookForPlayers) { PARAM_SELF_PROLOGUE(AActor); PARAM_BOOL(allaround); - ACTION_RETURN_BOOL(P_LookForPlayers(self, allaround, nullptr)); + PARAM_POINTER_DEF(params, FLookExParams); + ACTION_RETURN_BOOL(P_LookForPlayers(self, allaround, params)); } // @@ -1942,7 +2060,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Look) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LookEx) +DEFINE_ACTION_FUNCTION(AActor, A_LookEx) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF (flags) @@ -2159,7 +2277,7 @@ enum ChaseFlags CHF_STOPIFBLOCKED = 256, }; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Wander) +DEFINE_ACTION_FUNCTION(AActor, A_Wander) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); @@ -2167,12 +2285,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Wander) return 0; } -// [MC] I had to move this out from within A_Wander in order to allow flags to -// pass into it. That meant replacing the CALL_ACTION(A_Wander) functions with -// just straight up defining A_Wander in order to compile. Looking around though, -// actors from the games themselves just do a straight A_Chase call itself so -// I saw no harm in it. - void A_Wander(AActor *self, int flags) { // [RH] Strife probably clears this flag somewhere, but I couldn't find where. @@ -2285,7 +2397,7 @@ nosee: //============================================================================= #define CLASS_BOSS_STRAFE_RANGE 64*10 -void A_DoChase (VMFrameStack *stack, AActor *actor, bool fastchase, FState *meleestate, FState *missilestate, bool playactive, bool nightmarefast, bool dontmove, int flags) +void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missilestate, bool playactive, bool nightmarefast, bool dontmove, int flags) { if (actor->flags5 & MF5_INCONVERSATION) @@ -2415,7 +2527,7 @@ void A_DoChase (VMFrameStack *stack, AActor *actor, bool fastchase, FState *mele { if (actor->flags & MF_FRIENDLY) { - //CALL_ACTION(A_Look, actor); + //A_Look(actor); if (actor->target == NULL) { if (!dontmove) A_Wander(actor); @@ -2801,7 +2913,7 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) // //========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase) +DEFINE_ACTION_FUNCTION(AActor, A_Chase) { PARAM_SELF_PROLOGUE(AActor); PARAM_STATE_DEF (melee) @@ -2813,12 +2925,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase) if ((flags & CHF_RESURRECT) && P_CheckForResurrection(self, false)) return 0; - A_DoChase(stack, self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), + A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), !!(flags&CHF_NIGHTMAREFAST), !!(flags&CHF_DONTMOVE), flags); } else // this is the old default A_Chase { - A_DoChase(stack, self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, flags); + A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, flags); } return 0; } @@ -2826,7 +2938,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase) DEFINE_ACTION_FUNCTION(AActor, A_FastChase) { PARAM_SELF_PROLOGUE(AActor); - A_DoChase(stack, self, true, self->MeleeState, self->MissileState, true, true, false, 0); + A_DoChase(self, true, self->MeleeState, self->MissileState, true, true, false, 0); return 0; } @@ -2835,12 +2947,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_VileChase) PARAM_SELF_PROLOGUE(AActor); if (!P_CheckForResurrection(self, true)) { - A_DoChase(stack, self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); + A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); } return 0; } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ExtChase) +DEFINE_ACTION_FUNCTION(AActor, A_ExtChase) { PARAM_SELF_PROLOGUE(AActor); PARAM_BOOL (domelee); @@ -2849,16 +2961,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ExtChase) PARAM_BOOL_DEF (nightmarefast); // Now that A_Chase can handle state label parameters, this function has become rather useless... - A_DoChase(stack, self, false, + A_DoChase(self, false, domelee ? self->MeleeState : NULL, domissile ? self->MissileState : NULL, playactive, nightmarefast, false, 0); return 0; } // for internal use -void A_Chase(VMFrameStack *stack, AActor *self) +void A_Chase(AActor *self) { - A_DoChase(stack, self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); + A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); } //============================================================================= @@ -2980,7 +3092,7 @@ void A_FaceTarget(AActor *self) A_Face(self, self->target); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Face) +DEFINE_ACTION_FUNCTION(AActor, A_Face) { PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT(faceto, AActor) @@ -3197,7 +3309,7 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c AInventory *inv = static_cast(mo); ModifyDropAmount(inv, dropamount); inv->ItemFlags |= IF_TOSSED; - if (inv->SpecialDropAction (source)) + if (inv->CallSpecialDropAction (source)) { // The special action indicates that the item should not spawn inv->Destroy(); @@ -3211,6 +3323,15 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c return NULL; } +DEFINE_ACTION_FUNCTION(AActor, DoDropItem) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(cls, AActor); + PARAM_INT(amt); + PARAM_INT(chance); + ACTION_RETURN_OBJECT(P_DropItem(self, cls, amt, chance)); +} + //============================================================================ // // P_TossItem @@ -3327,6 +3448,12 @@ bool CheckBossDeath (AActor *actor) return true; } +DEFINE_ACTION_FUNCTION(AActor, CheckBossDeath) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(CheckBossDeath(self)); +} + // // A_BossDeath // Possibly trigger special effects if on a boss level @@ -3468,47 +3595,6 @@ int P_Massacre () return killcount; } -// -// A_SinkMobj -// Sink a mobj incrementally into the floor -// - -bool A_SinkMobj (AActor *actor, double speed) -{ - if (actor->Floorclip < actor->Height) - { - actor->Floorclip += speed; - return false; - } - return true; -} - -// -// A_RaiseMobj -// Raise a mobj incrementally from the floor to -// - -bool A_RaiseMobj (AActor *actor, double speed) -{ - bool done = true; - - // Raise a mobj from the ground - if (actor->Floorclip > 0) - { - actor->Floorclip -= speed; - if (actor->Floorclip <= 0) - { - actor->Floorclip = 0; - done = true; - } - else - { - done = false; - } - } - return done; // Reached target height -} - DEFINE_ACTION_FUNCTION(AActor, A_ClassBossHealth) { PARAM_SELF_PROLOGUE(AActor); diff --git a/src/p_enemy.h b/src/p_enemy.h index 7f4424fd6..a0e829c71 100644 --- a/src/p_enemy.h +++ b/src/p_enemy.h @@ -1,7 +1,7 @@ #ifndef __P_ENEMY_H__ #define __P_ENEMY_H__ -#include "vm.h" +#include "dobject.h" #include "vectors.h" struct sector_t; @@ -60,24 +60,13 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params); void A_Weave(AActor *self, int xyspeed, int zspeed, double xydist, double zdist); void A_Unblock(AActor *self, bool drop); -DECLARE_ACTION(A_Look) -DECLARE_ACTION(A_BossDeath) -DECLARE_ACTION(A_Pain) -DECLARE_ACTION(A_MonsterRail) -DECLARE_ACTION(A_NoBlocking) -DECLARE_ACTION(A_Scream) -DECLARE_ACTION(A_FreezeDeath) -DECLARE_ACTION(A_FreezeDeathChunks) void A_BossDeath(AActor *self); void A_Wander(AActor *self, int flags = 0); -void A_Chase(VMFrameStack *stack, AActor *self); +void A_Chase(AActor *self); void A_FaceTarget(AActor *actor); void A_Face(AActor *self, AActor *other, DAngle max_turn = 0., DAngle max_pitch = 270., DAngle ang_offset = 0., DAngle pitch_offset = 0., int flags = 0, double z_add = 0); -bool A_RaiseMobj (AActor *, double speed); -bool A_SinkMobj (AActor *, double speed); - bool CheckBossDeath (AActor *); int P_Massacre (); bool P_CheckMissileRange (AActor *actor); diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 7b5ff5cc9..8fd5ad038 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -64,7 +64,7 @@ static void StartFloorSound (sector_t *sec) // //========================================================================== -IMPLEMENT_CLASS(DFloor, false, false, false, false) +IMPLEMENT_CLASS(DFloor, false, false) DFloor::DFloor () { @@ -492,6 +492,21 @@ bool P_CreateFloor(sector_t *sec, DFloor::EFloor floortype, line_t *line, return true; } +DEFINE_ACTION_FUNCTION(DFloor, CreateFloor) +{ + PARAM_PROLOGUE; + PARAM_POINTER(sec, sector_t); + PARAM_INT(floortype); + PARAM_POINTER(ln, line_t); + PARAM_FLOAT(speed); + PARAM_FLOAT_DEF(height); + PARAM_INT_DEF(crush); + PARAM_INT_DEF(change); + PARAM_BOOL_DEF(hereticlower); + PARAM_BOOL_DEF(hexencrush); + ACTION_RETURN_BOOL(P_CreateFloor(sec, (DFloor::EFloor)floortype, ln, speed, height, crush, change, hexencrush, hereticlower)); +} + //========================================================================== // // HANDLE FLOOR TYPES @@ -815,7 +830,7 @@ bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed) // //========================================================================== -IMPLEMENT_CLASS(DElevator, false, true, false, false) +IMPLEMENT_CLASS(DElevator, false, true) IMPLEMENT_POINTERS_START(DElevator) IMPLEMENT_POINTER(m_Interp_Floor) @@ -1105,9 +1120,9 @@ bool EV_DoChange (line_t *line, EChange changetype, int tag) // //========================================================================== -IMPLEMENT_CLASS(DWaggleBase, false, false, false, false) -IMPLEMENT_CLASS(DFloorWaggle, false, false, false, false) -IMPLEMENT_CLASS(DCeilingWaggle, false, false, false, false) +IMPLEMENT_CLASS(DWaggleBase, false, false) +IMPLEMENT_CLASS(DFloorWaggle, false, false) +IMPLEMENT_CLASS(DCeilingWaggle, false, false) DWaggleBase::DWaggleBase () { @@ -1141,11 +1156,6 @@ DWaggleBase::DWaggleBase (sector_t *sec) { } -void DWaggleBase::Destroy() -{ - Super::Destroy(); -} - //========================================================================== // // diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index 39c8f8e0d..26e341aab 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -1356,7 +1356,6 @@ subsector_t *P_PointInSubsector (double x, double y) return (subsector_t *)((BYTE *)node - 1); } - //========================================================================== // // PointOnLine diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 48d13bebf..e6729096c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -58,6 +58,8 @@ #include "d_net.h" #include "d_netinf.h" #include "a_morph.h" +#include "virtual.h" +#include "a_health.h" static FRandom pr_obituary ("Obituary"); static FRandom pr_botrespawn ("BotRespawn"); @@ -104,8 +106,7 @@ void P_TouchSpecialThing (AActor *special, AActor *toucher) toucher->player->Bot->prev = toucher->player->Bot->dest; toucher->player->Bot->dest = NULL; } - - special->Touch (toucher); + special->CallTouch (toucher); } @@ -342,7 +343,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l } } - realthis->Die(source, inflictor, dmgflags); + realthis->CallDie(source, inflictor, dmgflags); } return; } @@ -761,7 +762,25 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) } } +DEFINE_ACTION_FUNCTION(AActor, Die) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(source, AActor); + PARAM_OBJECT(inflictor, AActor); + PARAM_INT_DEF(dmgflags); + self->Die(source, inflictor, dmgflags); + return 0; +} +void AActor::CallDie(AActor *source, AActor *inflictor, int dmgflags) +{ + IFVIRTUAL(AActor, Die) + { + VMValue params[4] = { (DObject*)this, source, inflictor, dmgflags }; + GlobalVMStack.Call(func, params, 4, nullptr, 0, nullptr); + } + else return Die(source, inflictor, dmgflags); +} //--------------------------------------------------------------------------- @@ -1071,7 +1090,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - damage = inflictor->DoSpecialDamage(target, damage, mod); + damage = inflictor->CallDoSpecialDamage(target, damage, mod); if (damage < 0) { return -1; @@ -1085,15 +1104,15 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, damage = int(damage * source->DamageMultiply); // Handle active damage modifiers (e.g. PowerDamage) - if (damage > 0 && source->Inventory != NULL) + if (damage > 0) { - source->Inventory->ModifyDamage(damage, mod, damage, false); + damage = source->GetModifiedDamage(mod, damage, false); } } // Handle passive damage modifiers (e.g. PowerProtection), provided they are not afflicted with protection penetrating powers. - if (damage > 0 && (target->Inventory != NULL) && !(flags & DMG_NO_PROTECT)) + if (damage > 0 && !(flags & DMG_NO_PROTECT)) { - target->Inventory->ModifyDamage(damage, mod, damage, true); + damage = target->GetModifiedDamage(mod, damage, true); } if (damage > 0 && !(flags & DMG_NO_FACTOR)) { @@ -1102,7 +1121,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (damage >= 0) { - damage = target->TakeSpecialDamage(inflictor, source, damage, mod); + damage = target->CallTakeSpecialDamage(inflictor, source, damage, mod); } // '<0' is handled below. This only handles the case where damage gets reduced to 0. @@ -1443,7 +1462,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, source = source->tracer; } } - target->Die (source, inflictor, flags); + target->CallDie (source, inflictor, flags); return damage; } } @@ -1617,6 +1636,20 @@ void P_PoisonMobj (AActor *target, AActor *inflictor, AActor *source, int damage } +DEFINE_ACTION_FUNCTION(AActor, PoisonMobj) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(inflictor, AActor); + PARAM_OBJECT(source, AActor); + PARAM_INT(damage); + PARAM_INT(duration); + PARAM_INT(period); + PARAM_NAME(mod); + P_PoisonMobj(self, inflictor, source, damage, duration, period, mod); + return 0; +} + + bool AActor::OkayToSwitchTarget (AActor *other) { if (other == this) @@ -1714,14 +1747,22 @@ bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poi return true; } +DEFINE_ACTION_FUNCTION(_PlayerInfo, PoisonPlayer) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_OBJECT(poisoner, AActor); + PARAM_OBJECT(source, AActor); + PARAM_INT(poison); + ACTION_RETURN_BOOL(P_PoisonPlayer(self, poisoner, source, poison)); +} + //========================================================================== // // P_PoisonDamage - Similar to P_DamageMobj // //========================================================================== -void P_PoisonDamage (player_t *player, AActor *source, int damage, - bool playPainSound) +void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPainSound) { AActor *target; @@ -1742,10 +1783,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, // Take half damage in trainer mode damage = int(damage * G_SkillProperty(SKILLP_DamageFactor)); // Handle passive damage modifiers (e.g. PowerProtection) - if (target->Inventory != NULL) - { - target->Inventory->ModifyDamage(damage, player->poisontype, damage, true); - } + damage = target->GetModifiedDamage(player->poisontype, damage, true); // Modify with damage factors damage = target->ApplyDamageFactor(player->poisontype, damage); @@ -1794,7 +1832,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, target->DamageType = player->poisontype; } } - target->Die(source, source); + target->CallDie(source, source); return; } } @@ -1816,6 +1854,15 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, */ } +DEFINE_ACTION_FUNCTION(_PlayerInfo, PoisonDamage) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_OBJECT(source, AActor); + PARAM_INT(damage); + PARAM_BOOL(playsound); + P_PoisonDamage(self, source, damage, playsound); + return 0; +} CCMD (kill) { diff --git a/src/p_lights.cpp b/src/p_lights.cpp index c44a22d4b..910c52a64 100644 --- a/src/p_lights.cpp +++ b/src/p_lights.cpp @@ -173,7 +173,7 @@ private: // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DLighting, false, false, false, false) +IMPLEMENT_CLASS(DLighting, false, false) DLighting::DLighting () { @@ -191,7 +191,7 @@ DLighting::DLighting (sector_t *sector) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DFireFlicker, false, false, false, false) +IMPLEMENT_CLASS(DFireFlicker, false, false) DFireFlicker::DFireFlicker () { @@ -258,7 +258,7 @@ DFireFlicker::DFireFlicker (sector_t *sector, int upper, int lower) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DFlicker, false, false, false, false) +IMPLEMENT_CLASS(DFlicker, false, false) DFlicker::DFlicker () { @@ -334,7 +334,7 @@ void EV_StartLightFlickering (int tag, int upper, int lower) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DLightFlash, false, false, false, false) +IMPLEMENT_CLASS(DLightFlash, false, false) DLightFlash::DLightFlash () { @@ -409,7 +409,7 @@ DLightFlash::DLightFlash (sector_t *sector, int min, int max) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DStrobe, false, false, false, false) +IMPLEMENT_CLASS(DStrobe, false, false) DStrobe::DStrobe () { @@ -667,7 +667,7 @@ void EV_LightChange (int tag, int value) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DGlow, false, false, false, false) +IMPLEMENT_CLASS(DGlow, false, false) DGlow::DGlow () { @@ -736,7 +736,7 @@ DGlow::DGlow (sector_t *sector) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DGlow2, false, false, false, false) +IMPLEMENT_CLASS(DGlow2, false, false) DGlow2::DGlow2 () { @@ -869,7 +869,7 @@ void EV_StartLightFading (int tag, int value, int tics) // //----------------------------------------------------------------------------- -IMPLEMENT_CLASS(DPhased, false, false, false, false) +IMPLEMENT_CLASS(DPhased, false, false) DPhased::DPhased () { diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index cf50a1dfa..d368739e1 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -50,7 +50,6 @@ #include "gi.h" #include "m_random.h" #include "p_conversation.h" -#include "a_strifeglobal.h" #include "r_data/r_translate.h" #include "p_3dmidtex.h" #include "d_net.h" @@ -350,7 +349,7 @@ FUNC(LS_Floor_LowerToHighest) } FUNC(LS_Floor_LowerToHighestEE) -// Floor_LowerToHighest (tag, speed, change) +// Floor_LowerToHighestEE (tag, speed, change) { return EV_DoFloor (DFloor::floorLowerToHighest, ln, arg0, SPEED(arg1), 0, -1, CHANGE(arg2), false); } @@ -1371,7 +1370,7 @@ void DoActivateThing(AActor * thing, AActor * activator) if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching thing->activationtype |= THINGSPEC_Deactivate; } - thing->Activate (activator); + thing->CallActivate (activator); } void DoDeactivateThing(AActor * thing, AActor * activator) @@ -1382,7 +1381,7 @@ void DoDeactivateThing(AActor * thing, AActor * activator) if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching thing->activationtype |= THINGSPEC_Activate; } - thing->Deactivate (activator); + thing->CallDeactivate (activator); } FUNC(LS_Thing_Activate) @@ -3155,17 +3154,7 @@ FUNC(LS_ClearForceField) sector_t *sec = §ors[secnum]; rtn = true; - for (int i = 0; i < sec->linecount; ++i) - { - line_t *line = sec->lines[i]; - if (line->backsector != NULL && line->special == ForceField) - { - line->flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING); - line->special = 0; - line->sidedef[0]->SetTexture(side_t::mid, FNullTextureID()); - line->sidedef[1]->SetTexture(side_t::mid, FNullTextureID()); - } - } + sec->RemoveForceField(); } return rtn; } @@ -3233,8 +3222,8 @@ FUNC(LS_GlassBreak) if (it != NULL) { it->GiveInventoryType (QuestItemClasses[28]); - it->GiveInventoryType (RUNTIME_CLASS(AUpgradeAccuracy)); - it->GiveInventoryType (RUNTIME_CLASS(AUpgradeStamina)); + it->GiveInventoryType (PClass::FindActor("UpgradeAccuracy")); + it->GiveInventoryType (PClass::FindActor("UpgradeStamina")); } } } diff --git a/src/p_local.h b/src/p_local.h index d759828dc..12b4a40a3 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -238,7 +238,7 @@ enum PCM AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params = NULL); -AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false); +AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false, bool frontonly = false); // // P_MAP diff --git a/src/p_map.cpp b/src/p_map.cpp index d7bffa589..11036d171 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -297,6 +297,14 @@ void P_FindFloorCeiling(AActor *actor, int flags) } } +DEFINE_ACTION_FUNCTION(AActor, FindFloorCeiling) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT_DEF(flags); + P_FindFloorCeiling(self, flags); + return 0; +} + // Debug CCMD for checking errors in the MultiBlockLinesIterator (needs to be removed when this code is complete) CCMD(ffcf) { @@ -661,6 +669,21 @@ double P_GetMoveFactor(const AActor *mo, double *frictionp) return movefactor; } +DEFINE_ACTION_FUNCTION(AActor, GetFriction) +{ + PARAM_SELF_PROLOGUE(AActor); + double friction, movefactor = P_GetMoveFactor(self, &friction); + if (numret > 1) + { + numret = 2; + ret[1].SetFloat(movefactor); + } + if (numret > 0) + { + ret[0].SetFloat(friction); + } + return numret; +} //========================================================================== // @@ -850,9 +873,11 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec // If the floor planes on both sides match we should recalculate open.bottom at the actual position we are checking // This is to avoid bumpy movement when crossing a linedef with the same slope on both sides. + // This should never narrow down the opening, though, only widen it. if (open.frontfloorplane == open.backfloorplane && open.bottom > LINEOPEN_MIN) { - open.bottom = open.frontfloorplane.ZatPoint(cres.Position); + auto newopen = open.frontfloorplane.ZatPoint(cres.Position); + if (newopen < open.bottom) open.bottom = newopen; } if (rail && @@ -1243,7 +1268,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch // Check for skulls slamming into things if (tm.thing->flags & MF_SKULLFLY) { - bool res = tm.thing->Slam(tm.thing->BlockingMobj); + bool res = tm.thing->CallSlam(tm.thing->BlockingMobj); tm.thing->BlockingMobj = NULL; return res; } @@ -1265,7 +1290,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch { // ideally this should take the mass factor into account thing->Vel += tm.thing->Vel.XY(); - if ((thing->Vel.X + thing->Vel.Y) > 3.) + if (fabs(thing->Vel.X) + fabs(thing->Vel.Y) > 3.) { int newdam; damage = (tm.thing->Mass / 100) + 1; @@ -1738,6 +1763,15 @@ bool P_CheckPosition(AActor *thing, const DVector2 &pos, bool actorsonly) return P_CheckPosition(thing, pos, tm, actorsonly); } +DEFINE_ACTION_FUNCTION(AActor, CheckPosition) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_BOOL_DEF(actorsonly); + ACTION_RETURN_BOOL(P_CheckPosition(self, DVector2(x, y), actorsonly)); +} + //---------------------------------------------------------------------------- // // FUNC P_TestMobjLocation @@ -1766,6 +1800,11 @@ bool P_TestMobjLocation(AActor *mobj) return false; } +DEFINE_ACTION_FUNCTION(AActor, TestMobjLocation) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(P_TestMobjLocation(self)); +} //============================================================================= // @@ -1869,6 +1908,26 @@ bool P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj) return onmobj == NULL; } +DEFINE_ACTION_FUNCTION(AActor, TestMobjZ) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL_DEF(quick); + + AActor *on = nullptr;; + bool retv = P_TestMobjZ(self, quick, &on); + if (numret > 1) + { + numret = 2; + ret[1].SetPointer(on, ATAG_OBJECT); + } + if (numret > 0) + { + ret[0].SetInt(retv); + } + return numret; +} + + //============================================================================= // // P_FakeZMovement @@ -2498,6 +2557,14 @@ bool P_TryMove(AActor *thing, const DVector2 &pos, return P_TryMove(thing, pos, dropoff, onfloor, tm); } +DEFINE_ACTION_FUNCTION(AActor, TryMove) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_INT(dropoff); + ACTION_RETURN_BOOL(P_TryMove(self, DVector2(x, y), dropoff)); +} //========================================================================== @@ -3254,7 +3321,7 @@ bool FSlide::BounceWall(AActor *mo) if (mo->flags & MF_MISSILE) P_ExplodeMissile(mo, line, NULL); else - mo->Die(NULL, NULL); + mo->CallDie(NULL, NULL); return true; } @@ -4587,6 +4654,18 @@ void P_TraceBleed(int damage, AActor *target, DAngle angle, DAngle pitch) P_TraceBleed(damage, target->PosPlusZ(target->Height/2), target, angle, pitch); } +DEFINE_ACTION_FUNCTION(AActor, TraceBleedAngle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(damage); + PARAM_FLOAT(angle); + PARAM_FLOAT(pitch); + + P_TraceBleed(damage, self, angle, pitch); + return 0; +} + + //========================================================================== // // @@ -4616,16 +4695,6 @@ void P_TraceBleed(int damage, AActor *target, AActor *missile) P_TraceBleed(damage, target->PosPlusZ(target->Height/2), target, missile->AngleTo(target), pitch); } -DEFINE_ACTION_FUNCTION(AActor, TraceBleed) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT(damage); - PARAM_OBJECT(missile, AActor); - - P_TraceBleed(damage, self, missile); - return 0; -} - //========================================================================== // // @@ -4643,6 +4712,17 @@ void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff) P_TraceBleed(damage, t->linetarget->PosPlusZ(t->linetarget->Height/2), t->linetarget, t->angleFromSource, pitch); } +DEFINE_ACTION_FUNCTION(_FTranslatedLineTarget, TraceBleed) +{ + PARAM_SELF_STRUCT_PROLOGUE(FTranslatedLineTarget); + PARAM_INT(damage); + PARAM_OBJECT(missile, AActor); + + P_TraceBleed(damage, self, missile); + return 0; +} + + //========================================================================== // // @@ -4659,6 +4739,18 @@ void P_TraceBleed(int damage, AActor *target) } } +DEFINE_ACTION_FUNCTION(AActor, TraceBleed) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(damage); + PARAM_OBJECT(missile, AActor); + + if (missile) P_TraceBleed(damage, self, missile); + else P_TraceBleed(damage, self); + return 0; +} + + //========================================================================== // // [RH] Rail gun stuffage @@ -6723,7 +6815,7 @@ bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death) thing->activationtype &= ~THINGSPEC_Activate; // Clear flag if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching thing->activationtype |= THINGSPEC_Deactivate; - thing->Activate(trigger); + thing->CallActivate(trigger); res = true; } // If not, can it be deactivated? @@ -6732,7 +6824,7 @@ bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death) thing->activationtype &= ~THINGSPEC_Deactivate; // Clear flag if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching thing->activationtype |= THINGSPEC_Activate; - thing->Deactivate(trigger); + thing->CallDeactivate(trigger); res = true; } } diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 0a1d831d8..aa29a1c5e 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -45,7 +45,6 @@ #include "templates.h" #include "po_man.h" -static AActor *RoughBlockCheck (AActor *mo, int index, void *); sector_t *P_PointInSectorBuggy(double x, double y); int P_VanillaPointOnDivlineSide(double x, double y, const divline_t* line); @@ -1153,6 +1152,75 @@ void FMultiBlockThingsIterator::Reset() startIteratorForGroup(basegroup); } +//=========================================================================== +// +// and the scriptable version +// +//=========================================================================== + +class DBlockThingsIterator : public DObject, public FMultiBlockThingsIterator +{ + DECLARE_CLASS(DBlockThingsIterator, DObject); + FPortalGroupArray check; +public: + FMultiBlockThingsIterator::CheckResult cres; + +public: + bool Next() + { + return FMultiBlockThingsIterator::Next(&cres); + } + + DBlockThingsIterator(AActor *origin = nullptr, double checkradius = -1, bool ignorerestricted = false) + : FMultiBlockThingsIterator(check, origin, checkradius, ignorerestricted) + { + cres.thing = nullptr; + cres.Position.Zero(); + cres.portalflags = 0; + } + + DBlockThingsIterator(double checkx, double checky, double checkz, double checkh, double checkradius, bool ignorerestricted, sector_t *newsec) + : FMultiBlockThingsIterator(check, checkx, checky, checkz, checkh, checkradius, ignorerestricted, newsec) + { + cres.thing = nullptr; + cres.Position.Zero(); + cres.portalflags = 0; + } +}; + +IMPLEMENT_CLASS(DBlockThingsIterator, false, false); + +DEFINE_ACTION_FUNCTION(DBlockThingsIterator, Create) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(origin, AActor); + PARAM_FLOAT_DEF(radius); + PARAM_BOOL_DEF(ignore); + ACTION_RETURN_OBJECT(new DBlockThingsIterator(origin, radius, ignore)); +} + +DEFINE_ACTION_FUNCTION(DBlockThingsIterator, CreateFromPos) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_FLOAT(h); + PARAM_FLOAT(radius); + PARAM_BOOL(ignore); + ACTION_RETURN_OBJECT(new DBlockThingsIterator(x, y, z, h, radius, ignore, nullptr)); +} + +DEFINE_ACTION_FUNCTION(DBlockThingsIterator, Next) +{ + PARAM_SELF_PROLOGUE(DBlockThingsIterator); + ACTION_RETURN_BOOL(self->Next()); +} + +DEFINE_FIELD_NAMED(DBlockThingsIterator, cres.thing, thing); +DEFINE_FIELD_NAMED(DBlockThingsIterator, cres.Position, position); +DEFINE_FIELD_NAMED(DBlockThingsIterator, cres.portalflags, portalflags); + //=========================================================================== // // FPathTraverse :: Intercepts @@ -1685,11 +1753,6 @@ FPathTraverse::~FPathTraverse() // distance is in MAPBLOCKUNITS //=========================================================================== -AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable) -{ - return P_BlockmapSearch (mo, distance, RoughBlockCheck, (void *)onlyseekable); -} - AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params) { int blockX; @@ -1779,6 +1842,13 @@ AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, in return NULL; } +struct BlockCheckInfo +{ + bool onlyseekable; + bool frontonly; + divline_t frontline; +}; + //=========================================================================== // // RoughBlockCheck @@ -1787,14 +1857,19 @@ AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, in static AActor *RoughBlockCheck (AActor *mo, int index, void *param) { - bool onlyseekable = param != NULL; + BlockCheckInfo *info = (BlockCheckInfo *)param; + FBlockNode *link; for (link = blocklinks[index]; link != NULL; link = link->NextActor) { if (link->Me != mo) { - if (onlyseekable && !mo->CanSeek(link->Me)) + if (info->onlyseekable && !mo->CanSeek(link->Me)) + { + continue; + } + if (info->frontonly && P_PointOnDivlineSide(link->Me->X(), link->Me->Y(), &info->frontline) != 0) { continue; } @@ -1807,6 +1882,30 @@ static AActor *RoughBlockCheck (AActor *mo, int index, void *param) return NULL; } +AActor *P_RoughMonsterSearch(AActor *mo, int distance, bool onlyseekable, bool frontonly) +{ + BlockCheckInfo info; + info.onlyseekable = onlyseekable; + if ((info.frontonly = frontonly)) + { + info.frontline.x = mo->X(); + info.frontline.y = mo->Y(); + info.frontline.dx = -mo->Angles.Yaw.Sin(); + info.frontline.dy = -mo->Angles.Yaw.Cos(); + } + + return P_BlockmapSearch(mo, distance, RoughBlockCheck, (void *)&info); +} + +DEFINE_ACTION_FUNCTION(AActor, RoughMonsterSearch) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(distance); + PARAM_BOOL_DEF(onlyseekable); + PARAM_BOOL_DEF(frontonly); + ACTION_RETURN_OBJECT(P_RoughMonsterSearch(self, distance, onlyseekable, frontonly)); +} + //========================================================================== // // [RH] LinkToWorldForMapThing @@ -1941,4 +2040,3 @@ sector_t *P_PointInSectorBuggy(double x, double y) subsector_t *ssec = (subsector_t *)((BYTE *)node - 1); return ssec->sector; } - diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index ce74684cc..f1f33425a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -22,7 +22,6 @@ //----------------------------------------------------------------------------- // HEADER FILES ------------------------------------------------------------ - #include #include "templates.h" #include "i_system.h" @@ -42,18 +41,14 @@ #include "c_dispatch.h" #include "b_bot.h" //Added by MC: #include "stats.h" -#include "a_hexenglobal.h" #include "a_sharedglobal.h" #include "gi.h" #include "sbar.h" #include "p_acs.h" #include "cmdlib.h" #include "decallib.h" -#include "ravenshared.h" -#include "a_action.h" #include "a_keys.h" #include "p_conversation.h" -#include "vm.h" #include "g_game.h" #include "teaminfo.h" #include "r_data/r_translate.h" @@ -72,7 +67,11 @@ #include "serializer.h" #include "r_utility.h" #include "thingdef.h" +#include "d_player.h" #include "virtual.h" +#include "a_armor.h" +#include "a_ammo.h" +#include "a_health.h" // MACROS ------------------------------------------------------------------ @@ -132,7 +131,7 @@ CVAR (Int, cl_bloodtype, 0, CVAR_ARCHIVE); // CODE -------------------------------------------------------------------- -IMPLEMENT_CLASS(AActor, false, true, true, true) +IMPLEMENT_CLASS(AActor, false, true) IMPLEMENT_POINTERS_START(AActor) IMPLEMENT_POINTER(target) @@ -157,182 +156,182 @@ AActor::~AActor () extern FFlagDef InternalActorFlagDefs[]; extern FFlagDef ActorFlagDefs[]; -void AActor::InitNativeFields() -{ - PType *TypePlayer = NewPointer(TypeVoid); // placeholder - PType *TypeActor = NewPointer(RUNTIME_CLASS(AActor)); - PType *TypeActorClass = NewClassPointer(RUNTIME_CLASS(AActor)); - PType *TypeInventory = NewPointer(RUNTIME_CLASS(AInventory)); - PStruct *sstruct = NewStruct("Sector", nullptr); - auto TypeSector = NewPointer(sstruct); - PType *array5 = NewArray(TypeSInt32, 5); +DEFINE_FIELD(AActor, snext) +DEFINE_FIELD(AActor, player) +DEFINE_FIELD_NAMED(AActor, __Pos, pos) +DEFINE_FIELD_NAMED(AActor, __Pos.X, x) +DEFINE_FIELD_NAMED(AActor, __Pos.Y, y) +DEFINE_FIELD_NAMED(AActor, __Pos.Z, z) +DEFINE_FIELD(AActor, Prev) +DEFINE_FIELD(AActor, SpriteAngle) +DEFINE_FIELD(AActor, SpriteRotation) +DEFINE_FIELD(AActor, VisibleStartAngle) +DEFINE_FIELD(AActor, VisibleStartPitch) +DEFINE_FIELD(AActor, VisibleEndAngle) +DEFINE_FIELD(AActor, VisibleEndPitch) +DEFINE_FIELD_NAMED(AActor, Angles.Yaw, angle) +DEFINE_FIELD_NAMED(AActor, Angles.Pitch, pitch) +DEFINE_FIELD_NAMED(AActor, Angles.Roll, roll) +DEFINE_FIELD(AActor, Vel) +DEFINE_FIELD_NAMED(AActor, Vel.X, velx) +DEFINE_FIELD_NAMED(AActor, Vel.Y, vely) +DEFINE_FIELD_NAMED(AActor, Vel.Z, velz) +DEFINE_FIELD_NAMED(AActor, Vel.X, momx) +DEFINE_FIELD_NAMED(AActor, Vel.Y, momy) +DEFINE_FIELD_NAMED(AActor, Vel.Z, momz) +DEFINE_FIELD(AActor, Speed) +DEFINE_FIELD(AActor, FloatSpeed) +DEFINE_FIELD(AActor, sprite) +DEFINE_FIELD(AActor, frame) +DEFINE_FIELD(AActor, Scale) +DEFINE_FIELD_NAMED(AActor, Scale.X, scalex) +DEFINE_FIELD_NAMED(AActor, Scale.Y, scaley) +DEFINE_FIELD(AActor, RenderStyle) +DEFINE_FIELD(AActor, picnum) +DEFINE_FIELD(AActor, Alpha) +DEFINE_FIELD(AActor, fillcolor) +DEFINE_FIELD_NAMED(AActor, Sector, CurSector) // clashes with type 'sector'. +DEFINE_FIELD(AActor, subsector) +DEFINE_FIELD(AActor, ceilingz) +DEFINE_FIELD(AActor, floorz) +DEFINE_FIELD(AActor, dropoffz) +DEFINE_FIELD(AActor, floorsector) +DEFINE_FIELD(AActor, floorpic) +DEFINE_FIELD(AActor, floorterrain) +DEFINE_FIELD(AActor, ceilingsector) +DEFINE_FIELD(AActor, ceilingpic) +DEFINE_FIELD(AActor, Height) +DEFINE_FIELD(AActor, radius) +DEFINE_FIELD(AActor, projectilepassheight) +DEFINE_FIELD(AActor, tics) +DEFINE_FIELD_NAMED(AActor, state, curstate) // clashes with type 'state'. +DEFINE_FIELD_NAMED(AActor, DamageVal, Damage) // name differs for historic reasons +DEFINE_FIELD(AActor, projectileKickback) +DEFINE_FIELD(AActor, VisibleToTeam) +DEFINE_FIELD(AActor, special1) +DEFINE_FIELD(AActor, special2) +DEFINE_FIELD(AActor, specialf1) +DEFINE_FIELD(AActor, specialf2) +DEFINE_FIELD(AActor, weaponspecial) +DEFINE_FIELD(AActor, health) +DEFINE_FIELD(AActor, movedir) +DEFINE_FIELD(AActor, visdir) +DEFINE_FIELD(AActor, movecount) +DEFINE_FIELD(AActor, strafecount) +DEFINE_FIELD(AActor, target) +DEFINE_FIELD(AActor, master) +DEFINE_FIELD(AActor, tracer) +DEFINE_FIELD(AActor, LastHeard) +DEFINE_FIELD(AActor, lastenemy) +DEFINE_FIELD(AActor, LastLookActor) +DEFINE_FIELD(AActor, reactiontime) +DEFINE_FIELD(AActor, threshold) +DEFINE_FIELD(AActor, DefThreshold) +DEFINE_FIELD(AActor, SpawnPoint) +DEFINE_FIELD(AActor, SpawnAngle) +DEFINE_FIELD(AActor, StartHealth) +DEFINE_FIELD(AActor, WeaveIndexXY) +DEFINE_FIELD(AActor, WeaveIndexZ) +DEFINE_FIELD(AActor, skillrespawncount) +DEFINE_FIELD(AActor, args) +DEFINE_FIELD(AActor, Mass) +DEFINE_FIELD(AActor, special) +DEFINE_FIELD(AActor, tid) +DEFINE_FIELD(AActor, TIDtoHate) +DEFINE_FIELD(AActor, waterlevel) +DEFINE_FIELD(AActor, Score) +DEFINE_FIELD(AActor, accuracy) +DEFINE_FIELD(AActor, stamina) +DEFINE_FIELD(AActor, meleerange) +DEFINE_FIELD(AActor, PainThreshold) +DEFINE_FIELD(AActor, Gravity) +DEFINE_FIELD(AActor, Floorclip) +DEFINE_FIELD(AActor, DamageType) +DEFINE_FIELD(AActor, DamageTypeReceived) +DEFINE_FIELD(AActor, FloatBobPhase) +DEFINE_FIELD(AActor, RipperLevel) +DEFINE_FIELD(AActor, RipLevelMin) +DEFINE_FIELD(AActor, RipLevelMax) +DEFINE_FIELD(AActor, Species) +DEFINE_FIELD(AActor, alternative) +DEFINE_FIELD(AActor, goal) +DEFINE_FIELD(AActor, MinMissileChance) +DEFINE_FIELD(AActor, LastLookPlayerNumber) +DEFINE_FIELD(AActor, SpawnFlags) +DEFINE_FIELD(AActor, meleethreshold) +DEFINE_FIELD(AActor, maxtargetrange) +DEFINE_FIELD(AActor, bouncefactor) +DEFINE_FIELD(AActor, wallbouncefactor) +DEFINE_FIELD(AActor, bouncecount) +DEFINE_FIELD(AActor, Friction) +DEFINE_FIELD(AActor, FastChaseStrafeCount) +DEFINE_FIELD(AActor, pushfactor) +DEFINE_FIELD(AActor, lastpush) +DEFINE_FIELD(AActor, activationtype) +DEFINE_FIELD(AActor, lastbump) +DEFINE_FIELD(AActor, DesignatedTeam) +DEFINE_FIELD(AActor, BlockingMobj) +DEFINE_FIELD(AActor, BlockingLine) +DEFINE_FIELD(AActor, PoisonDamage) +DEFINE_FIELD(AActor, PoisonDamageType) +DEFINE_FIELD(AActor, PoisonDuration) +DEFINE_FIELD(AActor, PoisonPeriod) +DEFINE_FIELD(AActor, PoisonDamageReceived) +DEFINE_FIELD(AActor, PoisonDamageTypeReceived) +DEFINE_FIELD(AActor, PoisonDurationReceived) +DEFINE_FIELD(AActor, PoisonPeriodReceived) +DEFINE_FIELD(AActor, Poisoner) +DEFINE_FIELD_NAMED(AActor, Inventory, Inv) // clashes with type 'Inventory'. +DEFINE_FIELD(AActor, smokecounter) +DEFINE_FIELD(AActor, FriendPlayer) +DEFINE_FIELD(AActor, Translation) +DEFINE_FIELD(AActor, AttackSound) +DEFINE_FIELD(AActor, DeathSound) +DEFINE_FIELD(AActor, SeeSound) +DEFINE_FIELD(AActor, PainSound) +DEFINE_FIELD(AActor, ActiveSound) +DEFINE_FIELD(AActor, UseSound) +DEFINE_FIELD(AActor, BounceSound) +DEFINE_FIELD(AActor, WallBounceSound) +DEFINE_FIELD(AActor, CrushPainSound) +DEFINE_FIELD(AActor, MaxDropOffHeight) +DEFINE_FIELD(AActor, MaxStepHeight) +DEFINE_FIELD(AActor, PainChance) +DEFINE_FIELD(AActor, PainType) +DEFINE_FIELD(AActor, DeathType) +DEFINE_FIELD(AActor, DamageFactor) +DEFINE_FIELD(AActor, DamageMultiply) +DEFINE_FIELD(AActor, TeleFogSourceType) +DEFINE_FIELD(AActor, TeleFogDestType) +DEFINE_FIELD(AActor, SpawnState) +DEFINE_FIELD(AActor, SeeState) +DEFINE_FIELD(AActor, MeleeState) +DEFINE_FIELD(AActor, MissileState) +DEFINE_FIELD(AActor, ConversationRoot) +DEFINE_FIELD(AActor, Conversation) +DEFINE_FIELD(AActor, DecalGenerator) - auto meta = RUNTIME_CLASS(AActor); - - meta->AddNativeField("Player", TypePlayer, myoffsetof(AActor, player)); - meta->AddNativeField("Pos", TypeVector3, myoffsetof(AActor, __Pos), VARF_ReadOnly); - meta->AddNativeField(NAME_X, TypeFloat64, myoffsetof(AActor, __Pos.X), VARF_ReadOnly | VARF_Deprecated); // must remain read-only! - meta->AddNativeField(NAME_Y, TypeFloat64, myoffsetof(AActor, __Pos.Y), VARF_ReadOnly | VARF_Deprecated); // must remain read-only! - meta->AddNativeField(NAME_Z, TypeFloat64, myoffsetof(AActor, __Pos.Z), VARF_ReadOnly | VARF_Deprecated); // must remain read-only! - meta->AddNativeField("Prev", TypeVector3, myoffsetof(AActor, Prev)); - meta->AddNativeField("spriteAngle", TypeFloat64, myoffsetof(AActor, SpriteAngle)); - meta->AddNativeField("spriteRotation", TypeFloat64, myoffsetof(AActor, SpriteRotation)); - meta->AddNativeField(NAME_VisibleStartAngle, TypeFloat64, myoffsetof(AActor, VisibleStartAngle)); - meta->AddNativeField(NAME_VisibleStartPitch, TypeFloat64, myoffsetof(AActor, VisibleStartPitch)); - meta->AddNativeField(NAME_VisibleEndAngle, TypeFloat64, myoffsetof(AActor, VisibleEndAngle)); - meta->AddNativeField(NAME_VisibleEndPitch, TypeFloat64, myoffsetof(AActor, VisibleEndPitch)); - meta->AddNativeField(NAME_Angle, TypeFloat64, myoffsetof(AActor, Angles.Yaw)); - meta->AddNativeField(NAME_Pitch, TypeFloat64, myoffsetof(AActor, Angles.Pitch)); - meta->AddNativeField(NAME_Roll, TypeFloat64, myoffsetof(AActor, Angles.Roll)); - meta->AddNativeField("Vel", TypeVector3, myoffsetof(AActor, Vel)); - meta->AddNativeField(NAME_VelX, TypeFloat64, myoffsetof(AActor, Vel.X), VARF_ReadOnly | VARF_Deprecated); - meta->AddNativeField(NAME_VelY, TypeFloat64, myoffsetof(AActor, Vel.Y), VARF_ReadOnly | VARF_Deprecated); - meta->AddNativeField(NAME_VelZ, TypeFloat64, myoffsetof(AActor, Vel.Z), VARF_ReadOnly | VARF_Deprecated); - meta->AddNativeField(NAME_MomX, TypeFloat64, myoffsetof(AActor, Vel.X), VARF_ReadOnly | VARF_Deprecated); - meta->AddNativeField(NAME_MomY, TypeFloat64, myoffsetof(AActor, Vel.Y), VARF_ReadOnly | VARF_Deprecated); - meta->AddNativeField(NAME_MomZ, TypeFloat64, myoffsetof(AActor, Vel.Z), VARF_ReadOnly | VARF_Deprecated); - meta->AddNativeField(NAME_Speed, TypeFloat64, myoffsetof(AActor, Speed)); - meta->AddNativeField("FloatSpeed", TypeFloat64, myoffsetof(AActor, FloatSpeed)); - meta->AddNativeField("sprite", TypeSInt32, myoffsetof(AActor, sprite)); // this is an index, not a name! - meta->AddNativeField("frame", TypeUInt8, myoffsetof(AActor, frame)); - meta->AddNativeField("Scale", TypeVector2, myoffsetof(AActor, Scale)); - meta->AddNativeField(NAME_ScaleX, TypeFloat64, myoffsetof(AActor, Scale.X), VARF_Deprecated); - meta->AddNativeField(NAME_ScaleY, TypeFloat64, myoffsetof(AActor, Scale.Y), VARF_Deprecated); - //FRenderStyle RenderStyle; // how do we expose this? - meta->AddNativeField("picnum", TypeSInt32, myoffsetof(AActor, picnum)); // Do we need a variable type 'texture' to do this? - meta->AddNativeField(NAME_Alpha, TypeFloat64, myoffsetof(AActor, Alpha)); - meta->AddNativeField("fillcolor", TypeColor, myoffsetof(AActor, fillcolor)); - meta->AddNativeField("Sector", TypeSector, myoffsetof(AActor, Sector)); - //meta->AddNativeField("Subsector", TypeSubsector, myoffsetof(AActor, subsector)); - meta->AddNativeField(NAME_CeilingZ, TypeFloat64, myoffsetof(AActor, ceilingz), VARF_ReadOnly); - meta->AddNativeField(NAME_FloorZ, TypeFloat64, myoffsetof(AActor, floorz), VARF_ReadOnly); - meta->AddNativeField("DropoffZ", TypeFloat64, myoffsetof(AActor, dropoffz), VARF_ReadOnly); - meta->AddNativeField("floorsector", TypeSector, myoffsetof(AActor, floorsector)); - meta->AddNativeField("floorpic", TypeSInt32, myoffsetof(AActor, floorpic)); // Do we need a variable type 'texture' to do this? - meta->AddNativeField("floorterrain", TypeSInt32, myoffsetof(AActor, floorterrain)); - meta->AddNativeField("ceilingsector", TypeSector, myoffsetof(AActor, ceilingsector)); - meta->AddNativeField("ceilingpic", TypeSInt32, myoffsetof(AActor, ceilingpic)); // Do we need a variable type 'texture' to do this? - meta->AddNativeField(NAME_Height, TypeFloat64, myoffsetof(AActor, Height)); - meta->AddNativeField(NAME_Radius, TypeFloat64, myoffsetof(AActor, radius), VARF_ReadOnly); - meta->AddNativeField("projectilepassheight",TypeFloat64, myoffsetof(AActor, projectilepassheight)); - meta->AddNativeField("tics", TypeSInt32, myoffsetof(AActor, tics)); - meta->AddNativeField("CurState", TypeState, myoffsetof(AActor, state), VARF_ReadOnly); // has to be renamed on the script side because it clashes with the same named type. - meta->AddNativeField(NAME_Damage, TypeSInt32, myoffsetof(AActor, DamageVal), VARF_ReadOnly); - meta->AddNativeField("projectilekickback", TypeSInt32, myoffsetof(AActor, projectileKickback)); - //DWORD VisibleToTeam; - meta->AddNativeField("special1", TypeSInt32, myoffsetof(AActor, special1)); - meta->AddNativeField("special2", TypeSInt32, myoffsetof(AActor, special2)); - meta->AddNativeField("specialf1", TypeFloat64, myoffsetof(AActor, specialf1)); - meta->AddNativeField("specialf2", TypeFloat64, myoffsetof(AActor, specialf2)); - meta->AddNativeField("weaponspecial", TypeSInt32, myoffsetof(AActor, weaponspecial)); - meta->AddNativeField(NAME_Health, TypeSInt32, myoffsetof(AActor, health), VARF_ReadOnly); - meta->AddNativeField("movedir", TypeUInt8, myoffsetof(AActor, movedir)); - meta->AddNativeField("visdir", TypeSInt8, myoffsetof(AActor, visdir)); - meta->AddNativeField("movecount", TypeSInt16, myoffsetof(AActor, movecount)); - meta->AddNativeField("strafecount", TypeSInt16, myoffsetof(AActor, strafecount)); - meta->AddNativeField(NAME_Target, TypeActor, myoffsetof(AActor, target)); - meta->AddNativeField(NAME_Master, TypeActor, myoffsetof(AActor, master)); - meta->AddNativeField(NAME_Tracer, TypeActor, myoffsetof(AActor, tracer)); - meta->AddNativeField("LastHeard", TypeActor, myoffsetof(AActor, LastHeard)); - meta->AddNativeField("LastEnemy", TypeActor, myoffsetof(AActor, lastenemy)); - meta->AddNativeField("LastLookActor", TypeActor, myoffsetof(AActor, LastLookActor)); - meta->AddNativeField(NAME_ReactionTime, TypeSInt32, myoffsetof(AActor, reactiontime)); - meta->AddNativeField(NAME_Threshold, TypeSInt32, myoffsetof(AActor, threshold)); - meta->AddNativeField(NAME_DefThreshold, TypeSInt32, myoffsetof(AActor, DefThreshold), VARF_ReadOnly); - meta->AddNativeField("SpawnPoint", TypeVector3, myoffsetof(AActor, SpawnPoint), VARF_ReadOnly); - meta->AddNativeField("SpawnAngle", TypeUInt16, myoffsetof(AActor, SpawnAngle), VARF_ReadOnly); - meta->AddNativeField("StartHealth", TypeSInt32, myoffsetof(AActor, StartHealth)); - meta->AddNativeField("WeaveIndexXY", TypeUInt16, myoffsetof(AActor, WeaveIndexXY)); - meta->AddNativeField("WeaveIndexZ", TypeUInt16, myoffsetof(AActor, WeaveIndexZ)); - meta->AddNativeField("skillrespawncount", TypeSInt32, myoffsetof(AActor, skillrespawncount)); - meta->AddNativeField(NAME_Args, array5, myoffsetof(AActor, args)); - meta->AddNativeField(NAME_Mass, TypeSInt32, myoffsetof(AActor, Mass)); - meta->AddNativeField(NAME_Special, TypeSInt32, myoffsetof(AActor, special)); - meta->AddNativeField(NAME_TID, TypeSInt32, myoffsetof(AActor, tid), VARF_ReadOnly); - meta->AddNativeField(NAME_TIDtoHate, TypeSInt32, myoffsetof(AActor, TIDtoHate), VARF_ReadOnly); - meta->AddNativeField(NAME_WaterLevel, TypeSInt32, myoffsetof(AActor, waterlevel), VARF_ReadOnly); - meta->AddNativeField(NAME_Score, TypeSInt32, myoffsetof(AActor, Score)); - meta->AddNativeField(NAME_Accuracy, TypeSInt32, myoffsetof(AActor, accuracy)); - meta->AddNativeField(NAME_Stamina, TypeSInt32, myoffsetof(AActor, stamina)); - meta->AddNativeField(NAME_MeleeRange, TypeFloat64, myoffsetof(AActor, meleerange)); - meta->AddNativeField("PainThreshold", TypeSInt32, myoffsetof(AActor, PainThreshold)); - meta->AddNativeField("Gravity", TypeFloat64, myoffsetof(AActor, Gravity)); - meta->AddNativeField("FloorClip", TypeFloat64, myoffsetof(AActor, Floorclip)); - meta->AddNativeField("DamageType", TypeName, myoffsetof(AActor, DamageType)); - meta->AddNativeField("DamageTypeReceived", TypeName, myoffsetof(AActor, DamageTypeReceived)); - meta->AddNativeField("FloatBobPhase", TypeUInt8, myoffsetof(AActor, FloatBobPhase)); - meta->AddNativeField("RipperLevel", TypeSInt32, myoffsetof(AActor, RipperLevel)); - meta->AddNativeField("RipLevelMin", TypeSInt32, myoffsetof(AActor, RipLevelMin)); - meta->AddNativeField("RipLevelMax", TypeSInt32, myoffsetof(AActor, RipLevelMax)); - meta->AddNativeField("Species", TypeName, myoffsetof(AActor, Species)); - meta->AddNativeField("Alternative", TypeActor, myoffsetof(AActor, alternative)); - meta->AddNativeField("goal", TypeActor, myoffsetof(AActor, goal)); - meta->AddNativeField("MinMissileChance", TypeUInt8, myoffsetof(AActor, MinMissileChance)); - meta->AddNativeField("LastLookPlayerNumber",TypeSInt8, myoffsetof(AActor, LastLookPlayerNumber)); - meta->AddNativeField("SpawnFlags", TypeUInt32, myoffsetof(AActor, SpawnFlags)); - meta->AddNativeField("meleethreshold", TypeFloat64, myoffsetof(AActor, meleethreshold)); - meta->AddNativeField("maxtargetrange", TypeFloat64, myoffsetof(AActor, maxtargetrange)); - meta->AddNativeField("bouncefactor", TypeFloat64, myoffsetof(AActor, bouncefactor)); - meta->AddNativeField("wallbouncefactor", TypeFloat64, myoffsetof(AActor, wallbouncefactor)); - meta->AddNativeField("bouncecount", TypeSInt32, myoffsetof(AActor, bouncecount)); - meta->AddNativeField("friction", TypeFloat64, myoffsetof(AActor, Friction)); - meta->AddNativeField("FastChaseStrafeCount",TypeSInt32, myoffsetof(AActor, FastChaseStrafeCount)); - meta->AddNativeField("pushfactor", TypeFloat64, myoffsetof(AActor, pushfactor)); - meta->AddNativeField("lastpush", TypeSInt32, myoffsetof(AActor, lastpush)); - meta->AddNativeField("activationtype", TypeSInt32, myoffsetof(AActor, activationtype)); - meta->AddNativeField("lastbump", TypeSInt32, myoffsetof(AActor, lastbump)); - meta->AddNativeField("DesignatedTeam", TypeSInt32, myoffsetof(AActor, DesignatedTeam)); - meta->AddNativeField("BlockingMobj", TypeActor, myoffsetof(AActor, BlockingMobj)); - //line_t *BlockingLine; // Line that blocked the last move - meta->AddNativeField("PoisonDamage", TypeSInt32, myoffsetof(AActor, PoisonDamage)); - meta->AddNativeField("PoisonDamageType", TypeName, myoffsetof(AActor, PoisonDamageType)); - meta->AddNativeField("PoisonDuration", TypeSInt32, myoffsetof(AActor, PoisonDuration)); - meta->AddNativeField("PoisonPeriod", TypeSInt32, myoffsetof(AActor, PoisonPeriod)); - meta->AddNativeField("PoisonDamageReceived",TypeSInt32, myoffsetof(AActor, PoisonDamageReceived)); - meta->AddNativeField("PoisonDamageTypeReceived", TypeName, myoffsetof(AActor, PoisonDamageTypeReceived)); - meta->AddNativeField("PoisonDurationReceived", TypeSInt32, myoffsetof(AActor, PoisonDurationReceived)); - meta->AddNativeField("PoisonPeriodReceived", TypeSInt32, myoffsetof(AActor, PoisonPeriodReceived)); - meta->AddNativeField("Poisoner", TypeActor, myoffsetof(AActor, Poisoner)); - meta->AddNativeField("Inv", TypeInventory, myoffsetof(AActor, Inventory)); // Needs to be renamed because it hides the actual type. - meta->AddNativeField("smokecounter", TypeUInt8, myoffsetof(AActor, smokecounter)); - meta->AddNativeField("FriendPlayer", TypeUInt8, myoffsetof(AActor, FriendPlayer)); - meta->AddNativeField("Translation", TypeUInt32, myoffsetof(AActor, Translation)); - meta->AddNativeField("AttackSound", TypeSound, myoffsetof(AActor, AttackSound)); - meta->AddNativeField("DeathSound", TypeSound, myoffsetof(AActor, DeathSound)); - meta->AddNativeField("SeeSound", TypeSound, myoffsetof(AActor, SeeSound)); - meta->AddNativeField("PainSound", TypeSound, myoffsetof(AActor, PainSound)); - meta->AddNativeField("ActiveSound", TypeSound, myoffsetof(AActor, ActiveSound)); - meta->AddNativeField("UseSound", TypeSound, myoffsetof(AActor, UseSound)); - meta->AddNativeField("BounceSound", TypeSound, myoffsetof(AActor, BounceSound)); - meta->AddNativeField("WallBounceSound", TypeSound, myoffsetof(AActor, WallBounceSound)); - meta->AddNativeField("CrushPainSound", TypeSound, myoffsetof(AActor, CrushPainSound)); - meta->AddNativeField("MaxDropoffHeight", TypeFloat64, myoffsetof(AActor, MaxDropOffHeight)); - meta->AddNativeField("MaxStepHeight", TypeFloat64, myoffsetof(AActor, MaxStepHeight)); - meta->AddNativeField("PainChance", TypeSInt16, myoffsetof(AActor, PainChance)); - meta->AddNativeField("PainType", TypeName, myoffsetof(AActor, PainType)); - meta->AddNativeField("DeathType", TypeName, myoffsetof(AActor, DeathType)); - meta->AddNativeField("DamageFactor", TypeFloat64, myoffsetof(AActor, DamageFactor)); - meta->AddNativeField("DamageMultiply", TypeFloat64, myoffsetof(AActor, DamageMultiply)); - meta->AddNativeField("TelefogSourceType", TypeActorClass, myoffsetof(AActor, TeleFogSourceType)); - meta->AddNativeField("TelefogDestType", TypeActorClass, myoffsetof(AActor, TeleFogDestType)); - meta->AddNativeField("SpawnState", TypeState, myoffsetof(AActor, SpawnState), VARF_ReadOnly); - meta->AddNativeField("SeeState", TypeState, myoffsetof(AActor, SeeState), VARF_ReadOnly); - meta->AddNativeField("MeleeState", TypeState, myoffsetof(AActor, MeleeState), VARF_ReadOnly); - meta->AddNativeField("MissileState", TypeState, myoffsetof(AActor, MissileState), VARF_ReadOnly); - //int ConversationRoot; // THe root of the current dialogue - //FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is "used." - //FDecalBase *DecalGenerator; - - // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. - for (size_t i = 0; ActorFlagDefs[i].flagbit != 0xffffffff; i++) - { - meta->AddNativeField(FStringf("b%s", ActorFlagDefs[i].name), (ActorFlagDefs[i].fieldsize == 4? TypeSInt32 : TypeSInt16), ActorFlagDefs[i].structoffset, ActorFlagDefs[i].varflags, ActorFlagDefs[i].flagbit); - } - for (size_t i = 0; InternalActorFlagDefs[i].flagbit != 0xffffffff; i++) - { - meta->AddNativeField(FStringf("b%s", InternalActorFlagDefs[i].name), (InternalActorFlagDefs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), InternalActorFlagDefs[i].structoffset, InternalActorFlagDefs[i].varflags, InternalActorFlagDefs[i].flagbit); - } -} +DEFINE_FIELD(PClassActor, Obituary) +DEFINE_FIELD(PClassActor, HitObituary) +DEFINE_FIELD(PClassActor, DeathHeight) +DEFINE_FIELD(PClassActor, BurnHeight) +DEFINE_FIELD(PClassActor, BloodColor) +DEFINE_FIELD(PClassActor, GibHealth) +DEFINE_FIELD(PClassActor, WoundHealth) +DEFINE_FIELD(PClassActor, FastSpeed) +DEFINE_FIELD(PClassActor, RDFactor) +DEFINE_FIELD(PClassActor, CameraHeight) +DEFINE_FIELD(PClassActor, HowlSound) +DEFINE_FIELD(PClassActor, BloodType) +DEFINE_FIELD(PClassActor, BloodType2) +DEFINE_FIELD(PClassActor, BloodType3) +DEFINE_FIELD(PClassActor, DontHurtShooter) +DEFINE_FIELD(PClassActor, ExplosionRadius) +DEFINE_FIELD(PClassActor, ExplosionDamage) +DEFINE_FIELD(PClassActor, MeleeDamage) +DEFINE_FIELD(PClassActor, MeleeSound) +DEFINE_FIELD(PClassActor, MissileName) +DEFINE_FIELD(PClassActor, MissileHeight) //========================================================================== // @@ -572,6 +571,13 @@ bool AActor::InStateSequence(FState * newstate, FState * basestate) return false; } +DEFINE_ACTION_FUNCTION(AActor, InStateSequence) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER(newstate, FState); + PARAM_POINTER(basestate, FState); + ACTION_RETURN_BOOL(self->InStateSequence(newstate, basestate)); +} //========================================================================== // // AActor::GetTics @@ -585,11 +591,11 @@ bool AActor::InStateSequence(FState * newstate, FState * basestate) int AActor::GetTics(FState * newstate) { int tics = newstate->GetTics(); - if (isFast() && newstate->Fast) + if (isFast() && newstate->GetFast()) { return tics - (tics>>1); } - else if (isSlow() && newstate->Slow) + else if (isSlow() && newstate->GetSlow()) { return tics<<1; } @@ -740,6 +746,15 @@ void AActor::AddInventory (AInventory *item) Inventory->InventoryID = InventoryID++; } +DEFINE_ACTION_FUNCTION(AActor, AddInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(item, AInventory); + self->AddInventory(item); + return 0; +} + + //============================================================================ // // AActor :: GiveInventory @@ -799,6 +814,14 @@ bool AActor::GiveInventory(PClassInventory *type, int amount, bool givecheat) return result; } +DEFINE_ACTION_FUNCTION(AActor, Inventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(item, AInventory); + ACTION_RETURN_BOOL(self->UseInventory(item)); +} + + //============================================================================ // // AActor :: RemoveInventory @@ -826,6 +849,15 @@ void AActor::RemoveInventory(AInventory *item) } } +DEFINE_ACTION_FUNCTION(AActor, RemoveInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(item, AInventory); + self->RemoveInventory(item); + return 0; +} + + //============================================================================ // // AActor :: TakeInventory @@ -957,7 +989,7 @@ bool AActor::UseInventory (AInventory *item) { return false; } - if (!item->Use (false)) + if (!item->CallUse (false)) { return false; } @@ -972,6 +1004,13 @@ bool AActor::UseInventory (AInventory *item) return true; } +DEFINE_ACTION_FUNCTION(AActor, UseInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(item, AInventory); + ACTION_RETURN_BOOL(self->UseInventory(item)); +} + //=========================================================================== // // AActor :: DropInventory @@ -982,7 +1021,7 @@ bool AActor::UseInventory (AInventory *item) AInventory *AActor::DropInventory (AInventory *item) { - AInventory *drop = item->CreateTossable (); + AInventory *drop = item->CallCreateTossable (); if (drop == NULL) { @@ -998,6 +1037,13 @@ AInventory *AActor::DropInventory (AInventory *item) return drop; } +DEFINE_ACTION_FUNCTION(AActor, DropInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(item, AInventory); + ACTION_RETURN_OBJECT(self->DropInventory(item)); +} + //============================================================================ // // AActor :: FindInventory @@ -1037,6 +1083,14 @@ AInventory *AActor::FindInventory (FName type) return FindInventory(PClass::FindActor(type)); } +DEFINE_ACTION_FUNCTION(AActor, FindInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(type, AInventory); + PARAM_BOOL_DEF(subclass); + ACTION_RETURN_OBJECT(self->FindInventory(type, subclass)); +} + //============================================================================ // // AActor :: GiveInventoryType @@ -1059,6 +1113,13 @@ AInventory *AActor::GiveInventoryType (PClassActor *type) return item; } +DEFINE_ACTION_FUNCTION(AActor, GiveInventoryType) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(type, AInventory); + ACTION_RETURN_OBJECT(self->GiveInventoryType(type)); +} + //============================================================================ // // AActor :: GiveAmmo @@ -1087,6 +1148,14 @@ bool AActor::GiveAmmo (PClassAmmo *type, int amount) return false; } +DEFINE_ACTION_FUNCTION(AActor, GiveAmmo) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(type, AAmmo); + PARAM_INT(amount); + ACTION_RETURN_BOOL(self->GiveAmmo(type, amount)); +} + //============================================================================ // // AActor :: ClearInventory @@ -1153,6 +1222,14 @@ void AActor::ClearInventory() } } +DEFINE_ACTION_FUNCTION(AActor, ClearInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + self->ClearInventory(); + return 0; +} + + //============================================================================ // // AActor :: CopyFriendliness @@ -1254,6 +1331,13 @@ bool AActor::CheckLocalView (int playernum) const return false; } +DEFINE_ACTION_FUNCTION(AActor, CheckLocalView) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(cp); + ACTION_RETURN_BOOL(self->CheckLocalView(cp)); +} + //============================================================================ // // AActor :: IsInsideVisibleAngles @@ -1397,6 +1481,24 @@ void AActor::Touch (AActor *toucher) { } +DEFINE_ACTION_FUNCTION(AActor, Touch) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(toucher, AActor); + self->Touch(toucher); + return 0; +} + +void AActor::CallTouch(AActor *toucher) +{ + IFVIRTUAL(AActor, Touch) + { + VMValue params[2] = { (DObject*)this, toucher }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } + else Touch(toucher); +} + //============================================================================ // // AActor :: Grind @@ -1708,6 +1810,15 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) } } +DEFINE_ACTION_FUNCTION(AActor, ExplodeMissile) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER_DEF(line, line_t); + PARAM_OBJECT_DEF(target, AActor); + P_ExplodeMissile(self, line, target); + return 0; +} + void AActor::PlayBounceSound(bool onfloor) { @@ -1750,7 +1861,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane) if (flags & MF_MISSILE) P_ExplodeMissile(this, NULL, NULL); else - Die(NULL, NULL); + CallDie(NULL, NULL); return true; } if (!(BounceFlags & BOUNCE_CanBounceWater)) @@ -1777,7 +1888,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane) if (flags & MF_MISSILE) P_ExplodeMissile(this, NULL, NULL); else - Die(NULL, NULL); + CallDie(NULL, NULL); return true; } @@ -2757,7 +2868,10 @@ void P_ZMovement (AActor *mo, double oldfloorz) return; } // Let the actor do something special for hitting the floor - mo->HitFloor (); + if (mo->flags7 & MF7_SMASHABLE) + { + P_DamageMobj(mo, nullptr, nullptr, mo->health, NAME_Smash); + } if (mo->player) { if (mo->player->jumpTics < 0 || mo->Vel.Z < minvel) @@ -3098,6 +3212,25 @@ void AActor::RemoveFromHash () tid = 0; } +DEFINE_ACTION_FUNCTION(AActor, RemoveFromHash) +{ + PARAM_SELF_PROLOGUE(AActor); + self->RemoveFromHash(); + return 0; +} + +DEFINE_ACTION_FUNCTION(AActor, ChangeTid) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(tid); + self->RemoveFromHash(); + self->tid = tid; + self->AddToHash(); + return 0; +} + + + //========================================================================== // // P_IsTIDUsed @@ -3180,6 +3313,17 @@ int P_FindUniqueTID(int start_tid, int limit) return 0; } +DEFINE_ACTION_FUNCTION(AActor, FindUniqueTid) +{ + PARAM_PROLOGUE; + PARAM_INT_DEF(start); + PARAM_INT_DEF(limit); + ACTION_RETURN_INT(P_FindUniqueTID(start, limit)); +} + + + + CCMD(utid) { Printf("%d\n", @@ -3215,7 +3359,6 @@ int AActor::GetMissileDamage (int mask, int add) assert(false && "No damage function found"); return 0; } - VMFrameStack stack; VMValue param = this; VMReturn result; @@ -3223,7 +3366,7 @@ int AActor::GetMissileDamage (int mask, int add) result.IntAt(&amount); - if (stack.Call(DamageFunc, ¶m, 1, &result, 1) < 1) + if (GlobalVMStack.Call(DamageFunc, ¶m, 1, &result, 1) < 1) { // No results return 0; } @@ -3239,8 +3382,11 @@ void AActor::Howl () } } -void AActor::HitFloor () +DEFINE_ACTION_FUNCTION(AActor, Howl) { + PARAM_SELF_PROLOGUE(AActor); + self->Howl(); + return 0; } bool AActor::Slam (AActor *thing) @@ -3270,14 +3416,43 @@ bool AActor::Slam (AActor *thing) return false; // stop moving } -bool AActor::SpecialBlastHandling (AActor *source, double strength) +DEFINE_ACTION_FUNCTION(AActor, Slam) { - return true; + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(thing, AActor); + ACTION_RETURN_BOOL(self->Slam(thing)); } +bool AActor::CallSlam(AActor *thing) +{ + IFVIRTUAL(AActor, Slam) + { + VMValue params[2] = { (DObject*)this, thing }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + + } + else return Slam(thing); +} + + + +// This virtual method only exists on the script side. int AActor::SpecialMissileHit (AActor *victim) { - return -1; + IFVIRTUAL(AActor, SpecialMissileHit) + { + VMValue params[2] = { (DObject*)this, victim }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return retval; + } + else return -1; } bool AActor::AdjustReflectionAngle (AActor *thing, DAngle &angle) @@ -3291,8 +3466,7 @@ bool AActor::AdjustReflectionAngle (AActor *thing, DAngle &angle) if (absangle(angle, thing->Angles.Yaw) > 45) return true; // Let missile explode - if (thing->IsKindOf (RUNTIME_CLASS(AHolySpirit))) // shouldn't this be handled by another flag??? - return true; + if (thing->flags7 & MF7_NOSHIELDREFLECT) return true; if (pr_reflect () < 128) angle += 45; @@ -3411,6 +3585,14 @@ void AActor::SetShade (int r, int g, int b) fillcolor = MAKEARGB(ColorMatcher.Pick (r, g, b), r, g, b); } +DEFINE_ACTION_FUNCTION(AActor, SetShade) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(color); + self->SetShade(color); + return 0; +} + void AActor::SetPitch(DAngle p, bool interpolate, bool forceclamp) { if (player != NULL || forceclamp) @@ -3595,11 +3777,14 @@ void AActor::Tick () } } - UnlinkFromWorld (); - flags |= MF_NOBLOCKMAP; - SetXYZ(Vec3Offset(Vel)); - CheckPortalTransition(false); - LinkToWorld (); + if (!Vel.isZero() || !(flags & MF_NOBLOCKMAP)) + { + UnlinkFromWorld(); + flags |= MF_NOBLOCKMAP; + SetXYZ(Vec3Offset(Vel)); + CheckPortalTransition(false); + LinkToWorld(); + } } else { @@ -4099,6 +4284,14 @@ void AActor::Tick () } } +DEFINE_ACTION_FUNCTION(AActor, Tick) +{ + PARAM_SELF_PROLOGUE(AActor); + self->Tick(); + return 0; +} + + //========================================================================== // // AActor :: CheckNoDelay @@ -4443,7 +4636,7 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a actor->UpdateWaterLevel (false); if (!SpawningMapThing) { - actor->BeginPlay (); + actor->CallBeginPlay (); if (actor->ObjectFlags & OF_EuthanizeMe) { return NULL; @@ -4519,7 +4712,7 @@ void AActor::HandleSpawnFlags () } if (SpawnFlags & MTF_DORMANT) { - Deactivate (NULL); + CallDeactivate (NULL); } if (SpawnFlags & MTF_STANDSTILL) { @@ -4563,10 +4756,29 @@ void AActor::BeginPlay () if (flags2 & MF2_DORMANT) { flags2 &= ~MF2_DORMANT; - Deactivate (NULL); + CallDeactivate (NULL); } } +DEFINE_ACTION_FUNCTION(AActor, BeginPlay) +{ + PARAM_SELF_PROLOGUE(AActor); + self->BeginPlay(); + return 0; +} + +void AActor::CallBeginPlay() +{ + IFVIRTUAL(AActor, BeginPlay) + { + // Without the type cast this picks the 'void *' assignment... + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } + else BeginPlay(); +} + + void AActor::PostBeginPlay () { if (Renderer != NULL) @@ -4602,6 +4814,12 @@ bool AActor::isSlow() return !!G_SkillProperty(SKILLP_SlowMonsters); } +//=========================================================================== +// +// Activate +// +//=========================================================================== + void AActor::Activate (AActor *activator) { if ((flags3 & MF3_ISMONSTER) && (health > 0 || (flags & MF_ICECORPSE))) @@ -4622,6 +4840,32 @@ void AActor::Activate (AActor *activator) } } +DEFINE_ACTION_FUNCTION(AActor, Activate) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(activator, AActor); + self->Activate(activator); + return 0; +} + +void AActor::CallActivate(AActor *activator) +{ + IFVIRTUAL(AActor, Activate) + { + // Without the type cast this picks the 'void *' assignment... + VMValue params[2] = { (DObject*)this, (DObject*)activator }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } + else Activate(activator); +} + + +//=========================================================================== +// +// Deactivate +// +//=========================================================================== + void AActor::Deactivate (AActor *activator) { if ((flags3 & MF3_ISMONSTER) && (health > 0 || (flags & MF_ICECORPSE))) @@ -4642,10 +4886,30 @@ void AActor::Deactivate (AActor *activator) } } +DEFINE_ACTION_FUNCTION(AActor, Deactivate) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(activator, AActor); + self->Deactivate(activator); + return 0; +} +void AActor::CallDeactivate(AActor *activator) +{ + IFVIRTUAL(AActor, Deactivate) + { + // Without the type cast this picks the 'void *' assignment... + VMValue params[2] = { (DObject*)this, (DObject*)activator }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } + else Deactivate(activator); +} + +//=========================================================================== // -// P_RemoveMobj +// Destroy // +//=========================================================================== void AActor::Destroy () { @@ -5300,7 +5564,11 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) if (mthing->FloatbobPhase >= 0 && mthing->FloatbobPhase < 64) mobj->FloatBobPhase = mthing->FloatbobPhase; if (mthing->Gravity < 0) mobj->Gravity = -mthing->Gravity; else if (mthing->Gravity > 0) mobj->Gravity *= mthing->Gravity; - else mobj->flags &= ~MF_NOGRAVITY; + else + { + mobj->flags |= MF_NOGRAVITY; + mobj->Gravity = 0; + } // For Hexen floatbob 'compatibility' we do not really want to alter the floorz. if (mobj->specialf1 == 0 || !(mobj->flags2 & MF2_FLOATBOB) || !(ib_compatflags & BCOMPATF_FLOATBOB)) @@ -5352,7 +5620,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) if (mthing->fillcolor) mobj->fillcolor = mthing->fillcolor; - mobj->BeginPlay (); + mobj->CallBeginPlay (); if (!(mobj->ObjectFlags & OF_EuthanizeMe)) { mobj->LevelSpawned (); @@ -5363,7 +5631,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) else mobj->health = -mthing->health; if (mthing->health == 0) - mobj->Die(NULL, NULL); + mobj->CallDie(NULL, NULL); else if (mthing->health != 1) mobj->StartHealth = mobj->health; @@ -5562,6 +5830,19 @@ void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *origina P_DrawSplash2 (40, pos, dir, 2, bloodcolor); } +DEFINE_ACTION_FUNCTION(AActor, SpawnBlood) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_ANGLE(dir); + PARAM_INT(damage); + P_SpawnBlood(DVector3(x, y, z), dir, damage, self); + return 0; +} + + //--------------------------------------------------------------------------- // // PROC P_BloodSplatter @@ -5644,6 +5925,20 @@ void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle) } } +DEFINE_ACTION_FUNCTION(AActor, BloodSplatter) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_ANGLE(dir); + PARAM_BOOL_DEF(axe); + if (axe) P_BloodSplatter2(DVector3(x, y, z), self, dir); + else P_BloodSplatter(DVector3(x, y, z), self, dir); + return 0; +} + + //--------------------------------------------------------------------------- // // PROC P_RipperBlood @@ -5709,6 +6004,13 @@ int P_GetThingFloorType (AActor *thing) } } +DEFINE_ACTION_FUNCTION(AActor, GetFloorTerrain) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_POINTER(&Terrains[P_GetThingFloorType(self)]); +} + + //--------------------------------------------------------------------------- // // FUNC P_HitWater @@ -5856,6 +6158,20 @@ foundone: return plane == &sec->floorplane ? Terrains[terrainnum].IsLiquid : false; } +DEFINE_ACTION_FUNCTION(AActor, HitWater) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER(sec, sector_t); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_BOOL_DEF(checkabove); + PARAM_BOOL_DEF(alert); + PARAM_BOOL_DEF(force); + ACTION_RETURN_BOOL(P_HitWater(self, sec, DVector3(x, y, z), checkabove, alert, force)); +} + + //--------------------------------------------------------------------------- // // FUNC P_HitFloor @@ -5911,6 +6227,12 @@ bool P_HitFloor (AActor *thing) return P_HitWater (thing, m->m_sector, pos); } +DEFINE_ACTION_FUNCTION(AActor, HitFloor) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(P_HitFloor(self)); +} + //--------------------------------------------------------------------------- // // P_CheckSplash @@ -6017,6 +6339,13 @@ bool P_CheckMissileSpawn (AActor* th, double maxdist) return true; } +DEFINE_ACTION_FUNCTION(AActor, CheckMissileSpawn) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(add); + ACTION_RETURN_BOOL(P_CheckMissileSpawn(self, add)); +} + //--------------------------------------------------------------------------- // @@ -6049,6 +6378,15 @@ void P_PlaySpawnSound(AActor *missile, AActor *spawner) } } +DEFINE_ACTION_FUNCTION(AActor, PlaySpawnSound) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(missile, AActor); + P_PlaySpawnSound(missile, self); + return 0; +} + + static double GetDefaultSpeed(PClassActor *type) { if (type == NULL) @@ -6184,6 +6522,20 @@ AActor *P_SpawnMissileXYZ (DVector3 pos, AActor *source, AActor *dest, PClassAct return (!checkspawn || P_CheckMissileSpawn (th, source->radius)) ? th : NULL; } +DEFINE_ACTION_FUNCTION(AActor, SpawnMissileXYZ) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_OBJECT(dest, AActor); + PARAM_CLASS(type, AActor); + PARAM_BOOL_DEF(check); + PARAM_OBJECT_DEF(owner, AActor); + ACTION_RETURN_OBJECT(P_SpawnMissileXYZ(DVector3(x,y,z), self, dest, type, check, owner)); +} + + AActor *P_OldSpawnMissile(AActor *source, AActor *owner, AActor *dest, PClassActor *type) { if (source == NULL) @@ -6268,6 +6620,15 @@ AActor *P_SpawnMissileZAimed (AActor *source, double z, AActor *dest, PClassActo return P_SpawnMissileAngleZSpeed (source, z, type, an, vz, speed); } +DEFINE_ACTION_FUNCTION(AActor, SpawnMissileZAimed) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(z); + PARAM_OBJECT(dest, AActor); + PARAM_CLASS(type, AActor); + ACTION_RETURN_OBJECT(P_SpawnMissileZAimed(self, z, dest, type)); +} + //--------------------------------------------------------------------------- // // FUNC P_SpawnMissileAngleZSpeed @@ -6321,6 +6682,48 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnMissileAngleZSpeed) ACTION_RETURN_OBJECT(P_SpawnMissileAngleZSpeed(self, z, type, angle, vz, speed, owner, checkspawn)); } + +AActor *P_SpawnSubMissile(AActor *source, PClassActor *type, AActor *target) +{ + AActor *other = Spawn(type, source->Pos(), ALLOW_REPLACE); + + if (other == NULL) + { + return NULL; + } + + other->target = target; + other->Angles.Yaw = source->Angles.Yaw; + other->VelFromAngle(); + + if (other->flags4 & MF4_SPECTRAL) + { + if (source->flags & MF_MISSILE && source->flags4 & MF4_SPECTRAL) + { + other->FriendPlayer = source->FriendPlayer; + } + else + { + other->SetFriendPlayer(target->player); + } + } + + if (P_CheckMissileSpawn(other, source->radius)) + { + DAngle pitch = P_AimLineAttack(source, source->Angles.Yaw, 1024.); + other->Vel.Z = -other->Speed * pitch.Sin(); + return other; + } + return NULL; +} + +DEFINE_ACTION_FUNCTION(AActor, SpawnSubMissile) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(cls, AActor); + PARAM_OBJECT(target, AActor); + ACTION_RETURN_OBJECT(P_SpawnSubMissile(self, cls, target)); +} /* ================ = @@ -6443,6 +6846,27 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, return NULL; } +DEFINE_ACTION_FUNCTION(AActor, SpawnPlayerMissile) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(type, AActor); + PARAM_ANGLE_DEF(angle); + PARAM_FLOAT_DEF(x); + PARAM_FLOAT_DEF(y); + PARAM_FLOAT_DEF(z); + PARAM_POINTER_DEF(lt, FTranslatedLineTarget); + PARAM_BOOL_DEF(nofreeaim); + PARAM_BOOL_DEF(noautoaim); + PARAM_INT_DEF(aimflags); + AActor *missileactor; + if (numparam == 2) angle = self->Angles.Yaw; + AActor *misl = P_SpawnPlayerMissile(self, x, y, z, type, angle, lt, &missileactor, nofreeaim, noautoaim, aimflags); + if (numret > 0) ret[0].SetPointer(misl, ATAG_OBJECT); + if (numret > 1) ret[1].SetPointer(missileactor, ATAG_OBJECT), numret = 2; + return numret; +} + + int AActor::GetTeam() { if (player) @@ -6481,6 +6905,13 @@ bool AActor::IsTeammate (AActor *other) return false; } +DEFINE_ACTION_FUNCTION(AActor, isTeammate) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(other, AActor); + ACTION_RETURN_BOOL(self->IsTeammate(other)); +} + //========================================================================== // // AActor :: GetSpecies @@ -6583,6 +7014,22 @@ bool AActor::IsHostile (AActor *other) return true; } +DEFINE_ACTION_FUNCTION(AActor, isHostile) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(other, AActor); + ACTION_RETURN_BOOL(self->IsHostile(other)); +} + + +//========================================================================== +// +// AActor :: DoSpecialDamage +// +// override this for special damage effects. +// +//========================================================================== + int AActor::DoSpecialDamage (AActor *target, int damage, FName damagetype) { if (target->player && target->player->mo == target && damage < 1000 && @@ -6606,6 +7053,37 @@ int AActor::DoSpecialDamage (AActor *target, int damage, FName damagetype) } } +DEFINE_ACTION_FUNCTION(AActor, DoSpecialDamage) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(target, AActor); + PARAM_INT(damage); + PARAM_NAME(damagetype); + ACTION_RETURN_INT(self->DoSpecialDamage(target, damage, damagetype)); +} + +int AActor::CallDoSpecialDamage(AActor *target, int damage, FName damagetype) +{ + IFVIRTUAL(AActor, DoSpecialDamage) + { + // Without the type cast this picks the 'void *' assignment... + VMValue params[4] = { (DObject*)this, (DObject*)target, damage, damagetype.GetIndex() }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 4, &ret, 1, nullptr); + return retval; + } + else return DoSpecialDamage(target, damage, damagetype); + +} + +//========================================================================== +// +// AActor :: TakeSpecialDamage +// +//========================================================================== + int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype) { FState *death; @@ -6640,6 +7118,31 @@ int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FN return (death == NULL) ? -1 : damage; } +DEFINE_ACTION_FUNCTION(AActor, TakeSpecialDamage) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(inflictor, AActor); + PARAM_OBJECT(source, AActor); + PARAM_INT(damage); + PARAM_NAME(damagetype); + ACTION_RETURN_INT(self->TakeSpecialDamage(inflictor, source, damage, damagetype)); +} + +int AActor::CallTakeSpecialDamage(AActor *inflictor, AActor *source, int damage, FName damagetype) +{ + IFVIRTUAL(AActor, TakeSpecialDamage) + { + VMValue params[5] = { (DObject*)this, inflictor, source, damage, damagetype.GetIndex() }; + VMReturn ret; + int retval; + ret.IntAt(&retval); + GlobalVMStack.Call(func, params, 5, &ret, 1, nullptr); + return retval; + } + else return TakeSpecialDamage(inflictor, source, damage, damagetype); + +} + void AActor::Crash() { // [RC] Weird that this forces the Crash state regardless of flag. @@ -6689,6 +7192,14 @@ void AActor::SetIdle(bool nofunction) SetState(idle, nofunction); } +DEFINE_ACTION_FUNCTION(AActor, SetIdle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL_DEF(nofunction); + self->SetIdle(nofunction); + return 0; +} + int AActor::SpawnHealth() const { int defhealth = StartHealth ? StartHealth : GetDefault()->health; @@ -6708,6 +7219,12 @@ int AActor::SpawnHealth() const } } +DEFINE_ACTION_FUNCTION(AActor, SpawnHealth) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_INT(self->SpawnHealth()); +} + FState *AActor::GetRaiseState() { if (!(flags & MF_CORPSE)) @@ -6771,6 +7288,13 @@ double AActor::GetCameraHeight() const return GetClass()->CameraHeight == INT_MIN ? Height / 2 : GetClass()->CameraHeight; } +DEFINE_ACTION_FUNCTION(AActor, GetCameraHeight) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_FLOAT(self->GetCameraHeight()); +} + + DDropItem *AActor::GetDropItems() const { return GetClass()->DropItems; @@ -6788,6 +7312,13 @@ double AActor::GetGravity() const return level.gravity * Sector->gravity * Gravity * 0.00125; } +DEFINE_ACTION_FUNCTION(AActor, GetGravity) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_FLOAT(self->GetGravity()); +} + + // killough 11/98: // Whether an object is "sentient" or not. Used for environmental influences. // (left precisely the same as MBF even though it doesn't make much sense.) @@ -6823,16 +7354,26 @@ const char *AActor::GetTag(const char *def) const } } +DEFINE_ACTION_FUNCTION(AActor, GetTag) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_STRING(def); + ACTION_RETURN_STRING(self->GetTag(def.Len() == 0? nullptr : def.GetChars())); +} + void AActor::SetTag(const char *def) { - if (def == NULL || *def == 0) - { - Tag = NULL; - } - else - { - Tag = mStringPropertyData.Alloc(def); - } + if (def == NULL || *def == 0) Tag = nullptr; + else Tag = mStringPropertyData.Alloc(def); +} + +DEFINE_ACTION_FUNCTION(AActor, SetTag) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_STRING(def); + if (def.IsEmpty()) self->Tag = nullptr; + else self->Tag = self->mStringPropertyData.Alloc(def); + return 0; } @@ -6857,6 +7398,29 @@ void AActor::ClearCounters() } } +DEFINE_ACTION_FUNCTION(AActor, ClearCounters) +{ + PARAM_SELF_PROLOGUE(AActor); + self->ClearCounters(); + return 0; +} + +int AActor::GetModifiedDamage(FName damagetype, int damage, bool passive) +{ + if (Inventory != nullptr) + Inventory->ModifyDamage(damage, damagetype, damage, false); + + return damage; +} + +DEFINE_ACTION_FUNCTION(AActor, GetModifiedDamage) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_NAME(type); + PARAM_INT(damage); + PARAM_BOOL(passive); + ACTION_RETURN_INT(self->GetModifiedDamage(type, damage, passive)); +} int AActor::ApplyDamageFactor(FName damagetype, int damage) const { @@ -6868,10 +7432,19 @@ int AActor::ApplyDamageFactor(FName damagetype, int damage) const return damage; } - -void AActor::SetTranslation(const char *trname) +DEFINE_ACTION_FUNCTION(AActor, ApplyDamageFactor) { - if (*trname == 0) + PARAM_SELF_PROLOGUE(AActor); + PARAM_NAME(type); + PARAM_INT(damage); + ACTION_RETURN_INT(self->ApplyDamageFactor(type, damage)); +} + + +void AActor::SetTranslation(FName trname) +{ + // There is no constant for the empty name... + if (trname.GetChars()[0] == 0) { // an empty string resets to the default Translation = GetDefault()->Translation; @@ -6886,6 +7459,106 @@ void AActor::SetTranslation(const char *trname) // silently ignore if the name does not exist, this would create some insane message spam otherwise. } +//--------------------------------------------------------------------------- +// +// PROP A_RestoreSpecialPosition +// +//--------------------------------------------------------------------------- +static FRandom pr_restore("RestorePos"); + +void AActor::RestoreSpecialPosition() +{ + // Move item back to its original location + DVector2 sp = SpawnPoint; + + UnlinkFromWorld(); + SetXY(sp); + LinkToWorld(true); + SetZ(Sector->floorplane.ZatPoint(sp)); + P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS); // no portal checks here so that things get spawned in this sector. + + if (flags & MF_SPAWNCEILING) + { + SetZ(ceilingz - Height - SpawnPoint.Z); + } + else if (flags2 & MF2_SPAWNFLOAT) + { + double space = ceilingz - Height - floorz; + if (space > 48) + { + space -= 40; + SetZ((space * pr_restore()) / 256. + floorz + 40); + } + else + { + SetZ(floorz); + } + } + else + { + SetZ(SpawnPoint.Z + floorz); + } + // Redo floor/ceiling check, in case of 3D floors and portals + P_FindFloorCeiling(this, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT); + if (Z() < floorz) + { // Do not reappear under the floor, even if that's where we were for the + // initial spawn. + SetZ(floorz); + } + if ((flags & MF_SOLID) && (Top() > ceilingz)) + { // Do the same for the ceiling. + SetZ(ceilingz - Height); + } + // Do not interpolate from the position the actor was at when it was + // picked up, in case that is different from where it is now. + ClearInterpolation(); +} + +DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) +{ + PARAM_SELF_PROLOGUE(AActor); + self->RestoreSpecialPosition(); + return 0; +} + + + + +class DActorIterator : public DObject, public NActorIterator +{ + DECLARE_CLASS(DActorIterator, DObject) + +public: + DActorIterator(PClassActor *cls= nullptr, int tid = 0) + : NActorIterator(cls, tid) + { + } +}; + +IMPLEMENT_CLASS(DActorIterator, false, false); +DEFINE_ACTION_FUNCTION(DActorIterator, Create) +{ + PARAM_PROLOGUE; + PARAM_INT(tid); + PARAM_CLASS_DEF(type, AActor); + ACTION_RETURN_OBJECT(new DActorIterator(type, tid)); +} + +DEFINE_ACTION_FUNCTION(DActorIterator, Next) +{ + PARAM_SELF_PROLOGUE(DActorIterator); + ACTION_RETURN_OBJECT(self->Next()); +} + +DEFINE_ACTION_FUNCTION(DActorIterator, Reinit) +{ + PARAM_SELF_PROLOGUE(DActorIterator); + self->Reinit(); + return 0; +} + + + DEFINE_ACTION_FUNCTION(AActor, deltaangle) // should this be global? { PARAM_PROLOGUE; @@ -6894,6 +7567,14 @@ DEFINE_ACTION_FUNCTION(AActor, deltaangle) // should this be global? ACTION_RETURN_FLOAT(deltaangle(DAngle(a1), DAngle(a2)).Degrees); } +DEFINE_ACTION_FUNCTION(AActor, absangle) // should this be global? +{ + PARAM_PROLOGUE; + PARAM_FLOAT(a1); + PARAM_FLOAT(a2); + ACTION_RETURN_FLOAT(absangle(DAngle(a1), DAngle(a2)).Degrees); +} + DEFINE_ACTION_FUNCTION(AActor, Distance2D) { PARAM_SELF_PROLOGUE(AActor); @@ -6901,11 +7582,19 @@ DEFINE_ACTION_FUNCTION(AActor, Distance2D) ACTION_RETURN_FLOAT(self->Distance2D(other)); } +DEFINE_ACTION_FUNCTION(AActor, Distance3D) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(other, AActor); + ACTION_RETURN_FLOAT(self->Distance3D(other)); +} + DEFINE_ACTION_FUNCTION(AActor, AddZ) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(addz); - self->AddZ(addz); + PARAM_BOOL_DEF(moving); + self->AddZ(addz, moving); return 0; } @@ -6963,6 +7652,17 @@ DEFINE_ACTION_FUNCTION(AActor, VelFromAngle) return 0; } +// This combines all 3 variations of the internal function +DEFINE_ACTION_FUNCTION(AActor, Vel3DFromAngle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(speed); + PARAM_ANGLE(angle); + PARAM_ANGLE(pitch); + self->Vel3DFromAngle(pitch, angle, speed); + return 0; +} + // This combines all 3 variations of the internal function DEFINE_ACTION_FUNCTION(AActor, Thrust) { @@ -6995,6 +7695,23 @@ DEFINE_ACTION_FUNCTION(AActor, AngleTo) ACTION_RETURN_FLOAT(self->AngleTo(targ, absolute).Degrees); } +DEFINE_ACTION_FUNCTION(AActor, AngleToVector) +{ + PARAM_PROLOGUE; + PARAM_ANGLE(angle); + PARAM_FLOAT_DEF(length); + ACTION_RETURN_VEC2(angle.ToVector(length)); +} + +DEFINE_ACTION_FUNCTION(AActor, RotateVector) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_ANGLE(angle); + ACTION_RETURN_VEC2(DVector2(x, y).Rotated(angle)); +} + DEFINE_ACTION_FUNCTION(AActor, DistanceBySpeed) { PARAM_SELF_PROLOGUE(AActor); @@ -7013,13 +7730,37 @@ DEFINE_ACTION_FUNCTION(AActor, SetXYZ) return 0; } +DEFINE_ACTION_FUNCTION(AActor, Vec2Angle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(length); + PARAM_ANGLE(angle); + PARAM_BOOL_DEF(absolute); + ACTION_RETURN_VEC2(self->Vec2Angle(length, angle, absolute)); +} + + +DEFINE_ACTION_FUNCTION(AActor, Vec3To) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(t, AActor) + ACTION_RETURN_VEC3(self->Vec3To(t)); +} + +DEFINE_ACTION_FUNCTION(AActor, Vec2To) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(t, AActor) + ACTION_RETURN_VEC2(self->Vec2To(t)); +} + DEFINE_ACTION_FUNCTION(AActor, Vec3Angle) { PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(length) - PARAM_ANGLE(angle); + PARAM_ANGLE(angle); PARAM_FLOAT(z); - PARAM_BOOL_DEF(absolute); + PARAM_BOOL_DEF(absolute); ACTION_RETURN_VEC3(self->Vec3Angle(length, angle, z, absolute)); } @@ -7033,6 +7774,15 @@ DEFINE_ACTION_FUNCTION(AActor, Vec2OffsetZ) ACTION_RETURN_VEC3(self->Vec2OffsetZ(x, y, z, absolute)); } +DEFINE_ACTION_FUNCTION(AActor, Vec2Offset) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_BOOL_DEF(absolute); + ACTION_RETURN_VEC2(self->Vec2Offset(x, y, absolute)); +} + DEFINE_ACTION_FUNCTION(AActor, Vec3Offset) { PARAM_SELF_PROLOGUE(AActor); @@ -7043,27 +7793,62 @@ DEFINE_ACTION_FUNCTION(AActor, Vec3Offset) ACTION_RETURN_VEC3(self->Vec3Offset(x, y, z, absolute)); } +DEFINE_ACTION_FUNCTION(AActor, RestoreDamage) +{ + PARAM_SELF_PROLOGUE(AActor); + self->RestoreDamage(); + return 0; +} + +DEFINE_ACTION_FUNCTION(AActor, PlayerNumber) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_INT(self->player ? int(self->player - players) : 0); +} + +DEFINE_ACTION_FUNCTION(AActor, SetFriendPlayer) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER(player, player_t); + self->SetFriendPlayer(player); + return 0; +} + +DEFINE_ACTION_FUNCTION(AActor, ClearBounce) +{ + PARAM_SELF_PROLOGUE(AActor); + self->BounceFlags = 0; + return 0; +} + +DEFINE_ACTION_FUNCTION(AActor, AccuracyFactor) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_FLOAT(self->AccuracyFactor()); +} + +DEFINE_ACTION_FUNCTION(AActor, CountsAsKill) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_FLOAT(self->CountsAsKill()); +} + //---------------------------------------------------------------------------- // // DropItem handling // //---------------------------------------------------------------------------- -IMPLEMENT_CLASS(DDropItem, false, true, true, false) +IMPLEMENT_CLASS(DDropItem, false, true) IMPLEMENT_POINTERS_START(DDropItem) - IMPLEMENT_POINTER(Next) +IMPLEMENT_POINTER(Next) IMPLEMENT_POINTERS_END -void DDropItem::InitNativeFields() -{ - auto meta = RUNTIME_CLASS(DDropItem); - PType *TypeDropItem = NewPointer(RUNTIME_CLASS(DDropItem)); - meta->AddNativeField("Next", TypeDropItem, myoffsetof(DDropItem, Next), VARF_ReadOnly); - meta->AddNativeField("Name", TypeName, myoffsetof(DDropItem, Name), VARF_ReadOnly); - meta->AddNativeField("Probability", TypeSInt32, myoffsetof(DDropItem, Probability), VARF_ReadOnly); - meta->AddNativeField("Amount", TypeSInt32, myoffsetof(DDropItem, Amount)); -} +DEFINE_FIELD(DDropItem, Next) +DEFINE_FIELD(DDropItem, Name) +DEFINE_FIELD(DDropItem, Probability) +DEFINE_FIELD(DDropItem, Amount) void PrintMiscActorInfo(AActor *query) { diff --git a/src/p_pillar.cpp b/src/p_pillar.cpp index 27eb13ec5..b0ea0e4a3 100644 --- a/src/p_pillar.cpp +++ b/src/p_pillar.cpp @@ -40,7 +40,7 @@ #include "serializer.h" #include "r_data/r_interpolate.h" -IMPLEMENT_CLASS(DPillar, false, true, false, false) +IMPLEMENT_CLASS(DPillar, false, true) IMPLEMENT_POINTERS_START(DPillar) IMPLEMENT_POINTER(m_Interp_Floor) diff --git a/src/p_plats.cpp b/src/p_plats.cpp index 9924a0f9c..43e8eefb4 100644 --- a/src/p_plats.cpp +++ b/src/p_plats.cpp @@ -35,7 +35,7 @@ static FRandom pr_doplat ("DoPlat"); -IMPLEMENT_CLASS(DPlat, false, false, false, false) +IMPLEMENT_CLASS(DPlat, false, false) DPlat::DPlat () { diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 1bb0db909..610ade35a 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -25,11 +25,11 @@ #include "gi.h" #include "p_pspr.h" #include "templates.h" -#include "vm.h" #include "g_level.h" #include "d_player.h" #include "serializer.h" #include "v_text.h" +#include "cmdlib.h" // MACROS ------------------------------------------------------------------ @@ -80,7 +80,6 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO); // PRIVATE DATA DEFINITIONS ------------------------------------------------ static FRandom pr_wpnreadysnd ("WpnReadySnd"); -static FRandom pr_gunshot ("GunShot"); static const FGenericButtons ButtonChecks[] = { @@ -100,13 +99,33 @@ static const FGenericButtons ButtonChecks[] = // //------------------------------------------------------------------------ -IMPLEMENT_CLASS(DPSprite, false, true, false, false) +IMPLEMENT_CLASS(DPSprite, false, true) IMPLEMENT_POINTERS_START(DPSprite) IMPLEMENT_POINTER(Caller) IMPLEMENT_POINTER(Next) IMPLEMENT_POINTERS_END +DEFINE_FIELD_NAMED(DPSprite, State, CurState) // deconflict with same named type +DEFINE_FIELD(DPSprite, Caller) +DEFINE_FIELD(DPSprite, Next) +DEFINE_FIELD(DPSprite, Owner) +DEFINE_FIELD(DPSprite, Sprite) +DEFINE_FIELD(DPSprite, Frame) +DEFINE_FIELD(DPSprite, ID) +DEFINE_FIELD(DPSprite, processPending) +DEFINE_FIELD(DPSprite, x) +DEFINE_FIELD(DPSprite, y) +DEFINE_FIELD(DPSprite, oldx) +DEFINE_FIELD(DPSprite, oldy) +DEFINE_FIELD(DPSprite, firstTic) +DEFINE_FIELD(DPSprite, Tics) +DEFINE_FIELD_BIT(DPSprite, Flags, bAddWeapon, PSPF_ADDWEAPON) +DEFINE_FIELD_BIT(DPSprite, Flags, bAddBob, PSPF_ADDBOB) +DEFINE_FIELD_BIT(DPSprite, Flags, bPowDouble, PSPF_POWDOUBLE) +DEFINE_FIELD_BIT(DPSprite, Flags, bCVarFast, PSPF_CVARFAST) +DEFINE_FIELD_BIT(DPSprite, Flags, bFlip, PSPF_FLIP) + //------------------------------------------------------------------------ // // @@ -173,6 +192,14 @@ DPSprite *player_t::FindPSprite(int layer) return pspr; } +DEFINE_ACTION_FUNCTION(_PlayerInfo, FindPSprite) // the underscore is needed to get past the name mangler which removes the first clas name character to match the class representation (needs to be fixed in a later commit) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_INT(id); + ACTION_RETURN_OBJECT(self->FindPSprite((PSPLayers)id)); +} + + //------------------------------------------------------------------------ // // @@ -185,6 +212,16 @@ void P_SetPsprite(player_t *player, PSPLayers id, FState *state, bool pending) player->GetPSprite(id)->SetState(state, pending); } +DEFINE_ACTION_FUNCTION(_PlayerInfo, SetPSprite) // the underscore is needed to get past the name mangler which removes the first clas name character to match the class representation (needs to be fixed in a later commit) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_INT(id); + PARAM_POINTER(state, FState); + PARAM_BOOL_DEF(pending); + P_SetPsprite(self, (PSPLayers)id, state, pending); + return 0; +} + DPSprite *player_t::GetPSprite(PSPLayers layer) { AActor *oldcaller = nullptr; @@ -246,6 +283,14 @@ DPSprite *player_t::GetPSprite(PSPLayers layer) return pspr; } +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetPSprite) // the underscore is needed to get past the name mangler which removes the first clas name character to match the class representation (needs to be fixed in a later commit) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_INT(id); + ACTION_RETURN_OBJECT(self->GetPSprite((PSPLayers)id)); +} + + //--------------------------------------------------------------------------- // // PROC P_NewPspriteTick @@ -364,7 +409,7 @@ void DPSprite::SetState(FState *newstate, bool pending) // If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash. auto owner = FState::StaticFindStateOwner(newstate); Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s which accesses user variables. The action function has been removed from this state\n", - owner->TypeName.GetChars(), newstate - owner->OwnedStates, static_cast(newstate->ActionFunc)->PrintableName.GetChars()); + owner->TypeName.GetChars(), int(newstate - owner->OwnedStates), newstate->ActionFunc->PrintableName.GetChars()); newstate->ActionFunc = nullptr; } if (newstate->CallAction(Owner->mo, Caller, &stp, &nextstate)) @@ -394,6 +439,15 @@ void DPSprite::SetState(FState *newstate, bool pending) return; } +DEFINE_ACTION_FUNCTION(DPSprite, SetState) +{ + PARAM_SELF_PROLOGUE(DPSprite); + PARAM_POINTER(state, FState); + PARAM_BOOL_DEF(pending); + self->SetState(state, pending); + return 0; +} + //--------------------------------------------------------------------------- // // PROC P_BringUpWeapon @@ -772,7 +826,7 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToGeneric(self, ~0); } -DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) +DEFINE_ACTION_FUNCTION(AStateProvider, A_WeaponReady) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT_DEF(flags); @@ -904,7 +958,7 @@ static void P_CheckWeaponButtons (player_t *player) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_ReFire) +DEFINE_ACTION_FUNCTION(AStateProvider, A_ReFire) { PARAM_ACTION_PROLOGUE(AActor); PARAM_STATE_ACTION_DEF(state); @@ -942,7 +996,7 @@ void A_ReFire(AActor *self, FState *state) } } -DEFINE_ACTION_FUNCTION(AInventory, A_ClearReFire) +DEFINE_ACTION_FUNCTION(AStateProvider, A_ClearReFire) { PARAM_ACTION_PROLOGUE(AActor); player_t *player = self->player; @@ -964,7 +1018,7 @@ DEFINE_ACTION_FUNCTION(AInventory, A_ClearReFire) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AInventory, A_CheckReload) +DEFINE_ACTION_FUNCTION(AStateProvider, A_CheckReload) { PARAM_ACTION_PROLOGUE(AActor); @@ -1152,7 +1206,7 @@ DEFINE_ACTION_FUNCTION(AActor, OverlayID) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AInventory, A_Lower) +DEFINE_ACTION_FUNCTION(AStateProvider, A_Lower) { PARAM_ACTION_PROLOGUE(AActor); @@ -1200,7 +1254,7 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Lower) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AInventory, A_Raise) +DEFINE_ACTION_FUNCTION(AActor, A_Raise) { PARAM_ACTION_PROLOGUE(AActor); @@ -1241,7 +1295,7 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Raise) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Overlay) +DEFINE_ACTION_FUNCTION(AActor, A_Overlay) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT (layer); @@ -1261,7 +1315,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Overlay) ACTION_RETURN_BOOL(true); } -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ClearOverlays) +DEFINE_ACTION_FUNCTION(AActor, A_ClearOverlays) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(start); @@ -1312,7 +1366,7 @@ enum GF_Flags GFF_NOEXTCHANGE = 1, }; -DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash) +DEFINE_ACTION_FUNCTION(AStateProvider, A_GunFlash) { PARAM_ACTION_PROLOGUE(AActor); PARAM_STATE_ACTION_DEF(flash); @@ -1382,6 +1436,15 @@ DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget, int aimfla return pitch; } +DEFINE_ACTION_FUNCTION(AActor, BulletSlope) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER_DEF(t, FTranslatedLineTarget); + PARAM_INT_DEF(aimflags); + ACTION_RETURN_FLOAT(P_BulletSlope(self, t, aimflags).Degrees); +} + + AActor *P_AimTarget(AActor *mo) { FTranslatedLineTarget t; @@ -1395,38 +1458,6 @@ DEFINE_ACTION_FUNCTION(AActor, AimTarget) ACTION_RETURN_OBJECT(P_AimTarget(self)); } - -// -// P_GunShot -// -void P_GunShot (AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch) -{ - DAngle angle; - int damage; - - damage = 5*(pr_gunshot()%3+1); - angle = mo->Angles.Yaw; - - if (!accurate) - { - angle += pr_gunshot.Random2 () * (5.625 / 256); - } - - P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, pufftype); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Light) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT(light); - - if (self->player != NULL) - { - self->player->extralight = clamp(light, -20, 20); - } - return 0; -} - //------------------------------------------------------------------------ // // PROC P_SetupPsprites @@ -1563,6 +1594,63 @@ void player_t::DestroyPSprites() } } +//------------------------------------------------------------------------------------ +// +// 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(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); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, SetSafeFlash) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_OBJECT(weapon, AWeapon); + PARAM_POINTER(state, FState); + PARAM_INT(index); + P_SetSafeFlash(weapon, self, state, index); + return 0; +} + //------------------------------------------------------------------------ // // diff --git a/src/p_pspr.h b/src/p_pspr.h index bd1b32cd5..9c535e7d0 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -25,7 +25,6 @@ // Basic data types. // Needs fixed point, and BAM angles. -//#include "vm.h" #define WEAPONBOTTOM 128. @@ -88,8 +87,9 @@ private: void Serialize(FSerializer &arc); void Tick(); - void Destroy(); + void Destroy() override; +public: // must be public to be able to generate the field export tables. Grrr... TObjPtr Caller; TObjPtr Next; player_t *Owner; @@ -113,8 +113,6 @@ void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac); DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget = NULL, int aimflags = 0); AActor *P_AimTarget(AActor *mo); -void P_GunShot (AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch); - void DoReadyWeapon(AActor *self); void DoReadyWeaponToBob(AActor *self); void DoReadyWeaponToFire(AActor *self, bool primary = true, bool secondary = true); diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp index f2f982a7f..1cf004120 100644 --- a/src/p_pusher.cpp +++ b/src/p_pusher.cpp @@ -74,7 +74,7 @@ protected: friend bool PIT_PushThing (AActor *thing); }; -IMPLEMENT_CLASS(DPusher, false, true, false, false) +IMPLEMENT_CLASS(DPusher, false, true) IMPLEMENT_POINTERS_START(DPusher) IMPLEMENT_POINTER(m_Source) diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index f44eac25a..25789d944 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -44,7 +44,7 @@ public: DScroller (EScroll type, double dx, double dy, int control, int affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all); DScroller (double dx, double dy, const line_t *l, int control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); - void Destroy(); + void Destroy() override; void Serialize(FSerializer &arc); void Tick (); @@ -73,7 +73,7 @@ private: } }; -IMPLEMENT_CLASS(DScroller, false, true, false, false) +IMPLEMENT_CLASS(DScroller, false, true) IMPLEMENT_POINTERS_START(DScroller) IMPLEMENT_POINTER(m_Interpolations[0]) diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 5ed4cd144..22ef88358 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -22,6 +22,7 @@ //----------------------------------------------------------------------------- #include "p_spec.h" +#include "p_lnspec.h" #include "c_cvars.h" #include "doomstat.h" #include "g_level.h" @@ -699,6 +700,16 @@ void sector_t::SetColor(int r, int g, int b, int desat) P_RecalculateAttachedLights(this); } +DEFINE_ACTION_FUNCTION(_Sector, SetColor) +{ + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + PARAM_COLOR(color); + PARAM_INT(desat); + self->ColorMap = GetSpecialLights(color, self->ColorMap->Fade, desat); + P_RecalculateAttachedLights(self); + return 0; +} + void sector_t::SetFade(int r, int g, int b) { PalEntry fade = PalEntry (r,g,b); @@ -706,6 +717,16 @@ void sector_t::SetFade(int r, int g, int b) P_RecalculateAttachedLights(this); } +DEFINE_ACTION_FUNCTION(_Sector, SetFade) +{ + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + PARAM_COLOR(fade); + self->ColorMap = GetSpecialLights(self->ColorMap->Color, fade, self->ColorMap->Desaturate); + P_RecalculateAttachedLights(self); + return 0; +} + + //=========================================================================== // // sector_t :: ClosestPoint @@ -960,7 +981,7 @@ double sector_t::NextHighestCeilingAt(double x, double y, double bottomz, double } } if ((flags & FFCF_NOPORTALS) || sec->PortalBlocksMovement(ceiling) || planeheight >= sec->GetPortalPlaneZ(ceiling)) - { // Use sector's floor + { // Use sector's ceiling if (resultffloor) *resultffloor = NULL; if (resultsec) *resultsec = sec; return realceil; @@ -976,6 +997,34 @@ double sector_t::NextHighestCeilingAt(double x, double y, double bottomz, double } } +DEFINE_ACTION_FUNCTION(_Sector, NextHighestCeilingAt) +{ + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(bottomz); + PARAM_FLOAT(topz); + PARAM_INT_DEF(flags); + sector_t *resultsec; + F3DFloor *resultff; + double resultheight = self->NextHighestCeilingAt(x, y, bottomz, topz, flags, &resultsec, &resultff); + + if (numret > 2) + { + ret[2].SetPointer(resultff, ATAG_GENERIC); + numret = 3; + } + if (numret > 1) + { + ret[1].SetPointer(resultsec, ATAG_GENERIC); + } + if (numret > 0) + { + ret[0].SetFloat(resultheight); + } + return numret; +} + double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, double steph, sector_t **resultsec, F3DFloor **resultffloor) { sector_t *sec = this; @@ -1021,6 +1070,35 @@ double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, doub } } +DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt) +{ + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_INT_DEF(flags); + PARAM_FLOAT_DEF(steph); + sector_t *resultsec; + F3DFloor *resultff; + double resultheight = self->NextLowestFloorAt(x, y, z, flags, steph, &resultsec, &resultff); + + if (numret > 2) + { + ret[2].SetPointer(resultff, ATAG_GENERIC); + numret = 3; + } + if (numret > 1) + { + ret[1].SetPointer(resultsec, ATAG_GENERIC); + } + if (numret > 0) + { + ret[0].SetFloat(resultheight); + } + return numret; +} + + //=========================================================================== // // @@ -1047,6 +1125,43 @@ double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, doub } } + //=========================================================================== + // + // + // + //=========================================================================== + + void sector_t::RemoveForceField() + { + for (int i = 0; i < linecount; ++i) + { + line_t *line = lines[i]; + if (line->backsector != NULL && line->special == ForceField) + { + line->flags &= ~(ML_BLOCKING | ML_BLOCKEVERYTHING); + line->special = 0; + line->sidedef[0]->SetTexture(side_t::mid, FNullTextureID()); + line->sidedef[1]->SetTexture(side_t::mid, FNullTextureID()); + } + } + } + + DEFINE_ACTION_FUNCTION(_Sector, RemoveForceField) + { + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + self->RemoveForceField(); + return 0; + } + + + DEFINE_ACTION_FUNCTION(_Sector, PointInSector) + { + PARAM_PROLOGUE; + PARAM_FLOAT(x); + PARAM_FLOAT(y); + ACTION_RETURN_POINTER(P_PointInSector(x, y)); + } + //=========================================================================== // // @@ -1237,3 +1352,64 @@ int side_t::GetLightLevel (bool foggy, int baselight, bool is3dlight, int *pfake } return baselight; } + + +DEFINE_FIELD_X(Sector, sector_t, floorplane) +DEFINE_FIELD_X(Sector, sector_t, ceilingplane) +DEFINE_FIELD_X(Sector, sector_t, ColorMap) +DEFINE_FIELD_X(Sector, sector_t, SoundTarget) +DEFINE_FIELD_X(Sector, sector_t, special) +DEFINE_FIELD_X(Sector, sector_t, lightlevel) +DEFINE_FIELD_X(Sector, sector_t, seqType) +DEFINE_FIELD_X(Sector, sector_t, sky) +DEFINE_FIELD_X(Sector, sector_t, SeqName) +DEFINE_FIELD_X(Sector, sector_t, centerspot) +DEFINE_FIELD_X(Sector, sector_t, validcount) +DEFINE_FIELD_X(Sector, sector_t, thinglist) +DEFINE_FIELD_X(Sector, sector_t, friction) +DEFINE_FIELD_X(Sector, sector_t, movefactor) +DEFINE_FIELD_X(Sector, sector_t, terrainnum) +DEFINE_FIELD_X(Sector, sector_t, floordata) +DEFINE_FIELD_X(Sector, sector_t, ceilingdata) +DEFINE_FIELD_X(Sector, sector_t, lightingdata) +DEFINE_FIELD_X(Sector, sector_t, interpolations) +DEFINE_FIELD_X(Sector, sector_t, soundtraversed) +DEFINE_FIELD_X(Sector, sector_t, stairlock) +DEFINE_FIELD_X(Sector, sector_t, prevsec) +DEFINE_FIELD_X(Sector, sector_t, nextsec) +DEFINE_FIELD_X(Sector, sector_t, linecount) +DEFINE_FIELD_X(Sector, sector_t, lines) +DEFINE_FIELD_X(Sector, sector_t, heightsec) +DEFINE_FIELD_X(Sector, sector_t, bottommap) +DEFINE_FIELD_X(Sector, sector_t, midmap) +DEFINE_FIELD_X(Sector, sector_t, topmap) +DEFINE_FIELD_X(Sector, sector_t, touching_thinglist) +DEFINE_FIELD_X(Sector, sector_t, render_thinglist) +DEFINE_FIELD_X(Sector, sector_t, gravity) +DEFINE_FIELD_X(Sector, sector_t, damagetype) +DEFINE_FIELD_X(Sector, sector_t, damageamount) +DEFINE_FIELD_X(Sector, sector_t, damageinterval) +DEFINE_FIELD_X(Sector, sector_t, leakydamage) +DEFINE_FIELD_X(Sector, sector_t, ZoneNumber) +DEFINE_FIELD_X(Sector, sector_t, MoreFlags) +DEFINE_FIELD_X(Sector, sector_t, Flags) +DEFINE_FIELD_X(Sector, sector_t, SecActTarget) +DEFINE_FIELD_X(Sector, sector_t, Portals) +DEFINE_FIELD_X(Sector, sector_t, PortalGroup) +DEFINE_FIELD_X(Sector, sector_t, sectornum) + +DEFINE_FIELD_X(Line, line_t, v1) +DEFINE_FIELD_X(Line, line_t, v2) +DEFINE_FIELD_X(Line, line_t, delta) +DEFINE_FIELD_X(Line, line_t, flags) +DEFINE_FIELD_X(Line, line_t, activation) +DEFINE_FIELD_X(Line, line_t, special) +DEFINE_FIELD_X(Line, line_t, args) +DEFINE_FIELD_X(Line, line_t, alpha) +DEFINE_FIELD_X(Line, line_t, sidedef) +DEFINE_FIELD_X(Line, line_t, bbox) +DEFINE_FIELD_X(Line, line_t, frontsector) +DEFINE_FIELD_X(Line, line_t, backsector) +DEFINE_FIELD_X(Line, line_t, validcount) +DEFINE_FIELD_X(Line, line_t, locknumber) +DEFINE_FIELD_X(Line, line_t, portalindex) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index fa7a55aba..ccfafedac 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -552,6 +552,15 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornu level.found_secrets++; } +DEFINE_ACTION_FUNCTION(AActor, GiveSecret) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL(printmessage); + PARAM_BOOL(playsound); + P_GiveSecret(self, printmessage, playsound, -1); + return 0; +} + //============================================================================ // // P_PlayerOnSpecialFlat @@ -660,7 +669,7 @@ protected: short LastLight; }; -IMPLEMENT_CLASS(DLightTransfer, false, false, false, false) +IMPLEMENT_CLASS(DLightTransfer, false, false) void DLightTransfer::Serialize(FSerializer &arc) { @@ -750,7 +759,7 @@ protected: BYTE Flags; }; -IMPLEMENT_CLASS(DWallLightTransfer, false, false, false, false) +IMPLEMENT_CLASS(DWallLightTransfer, false, false) void DWallLightTransfer::Serialize(FSerializer &arc) { @@ -1609,3 +1618,4 @@ void sector_t::AdjustFloorClip () const } } } + diff --git a/src/p_spec.h b/src/p_spec.h index 41d47c721..8a0729d6a 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -243,7 +243,7 @@ public: void Serialize(FSerializer &arc); void Tick (); - void Destroy(); + void Destroy() override; protected: EPillar m_Type; @@ -572,7 +572,7 @@ public: DElevator (sector_t *sec); - void Destroy(); + void Destroy() override; void Serialize(FSerializer &arc); void Tick (); @@ -617,7 +617,6 @@ protected: int offset, int timer, bool ceiling); void DoWaggle (bool ceiling); - void Destroy(); DWaggleBase (); }; diff --git a/src/p_states.cpp b/src/p_states.cpp index f7f237c77..7daaa5e41 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -39,7 +39,6 @@ #include "i_system.h" #include "c_dispatch.h" #include "v_text.h" -#include "vm.h" #include "thingdef.h" // stores indices for symbolic state labels for some old-style DECORATE functions. @@ -290,7 +289,7 @@ static bool VerifyJumpTarget(PClassActor *cls, FState *CallingState, int index) // //========================================================================== -FState *FStateLabelStorage::GetState(int pos, PClassActor *cls) +FState *FStateLabelStorage::GetState(int pos, PClassActor *cls, bool exact) { if (pos > 0x10000000) { @@ -323,7 +322,7 @@ FState *FStateLabelStorage::GetState(int pos, PClassActor *cls) else if (cls != nullptr) { FName *labels = (FName*)&Storage[pos + sizeof(int)]; - return cls->FindState(val, labels, false); + return cls->FindState(val, labels, exact); } } return nullptr; @@ -338,12 +337,13 @@ FState *FStateLabelStorage::GetState(int pos, PClassActor *cls) DEFINE_ACTION_FUNCTION(AActor, FindState) { PARAM_SELF_PROLOGUE(AActor); - PARAM_STATE(newstate); - ACTION_RETURN_STATE(newstate); + PARAM_INT(newstate); + PARAM_BOOL_DEF(exact) + ACTION_RETURN_STATE(StateLabels.GetState(newstate, self->GetClass(), exact)); } // same as above but context aware. -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ResolveState) +DEFINE_ACTION_FUNCTION(AActor, ResolveState) { PARAM_ACTION_PROLOGUE(AActor); PARAM_STATE_ACTION(newstate); @@ -924,9 +924,9 @@ int FStateDefinitions::AddStates(FState *state, const char *framechars, const FS if (*framechars == '#') noframe = true; - else if (*framechars == '^') + else if (*framechars == '^') frame = '\\' - 'A'; - else + else frame = (*framechars & 223) - 'A'; framechars++; @@ -937,13 +937,14 @@ int FStateDefinitions::AddStates(FState *state, const char *framechars, const FS } state->Frame = frame; - state->SameFrame = noframe; + if (noframe) state->StateFlags |= STF_SAMEFRAME; + else state->StateFlags &= ~STF_SAMEFRAME; StateArray.Push(*state); SourceLines.Push(sc); ++count; // NODELAY flag is not carried past the first state - state->NoDelay = false; + state->StateFlags &= ~STF_NODELAY; } laststate = &StateArray[StateArray.Size() - 1]; laststatebeforelabel = laststate; @@ -1055,3 +1056,38 @@ CCMD(dumpstates) Printf(PRINT_LOG, "----------------------------\n"); } } + +//========================================================================== +// +// sets up the script-side version of states +// +//========================================================================== + +DEFINE_FIELD(FState, NextState) +DEFINE_FIELD(FState, sprite) +DEFINE_FIELD(FState, Tics) +DEFINE_FIELD(FState, TicRange) +DEFINE_FIELD(FState, Frame) +DEFINE_FIELD(FState, UseFlags) +DEFINE_FIELD(FState, Misc1) +DEFINE_FIELD(FState, Misc2) +DEFINE_FIELD_BIT(FState, StateFlags, bSlow, STF_SLOW) +DEFINE_FIELD_BIT(FState, StateFlags, bFast, STF_FAST) +DEFINE_FIELD_BIT(FState, StateFlags, bFullbright, STF_FULLBRIGHT) +DEFINE_FIELD_BIT(FState, StateFlags, bNoDelay, STF_NODELAY) +DEFINE_FIELD_BIT(FState, StateFlags, bSameFrame, STF_SAMEFRAME) +DEFINE_FIELD_BIT(FState, StateFlags, bCanRaise, STF_CANRAISE) +DEFINE_FIELD_BIT(FState, StateFlags, bDehacked, STF_DEHACKED) + +DEFINE_ACTION_FUNCTION(FState, DistanceTo) +{ + PARAM_SELF_STRUCT_PROLOGUE(FState); + PARAM_POINTER(other, FState); + + // Safely calculate the distance between two states. + auto o1 = FState::StaticFindStateOwner(self); + int retv; + if (other < o1->OwnedStates || other >= o1->OwnedStates + o1->NumOwnedStates) retv = INT_MIN; + else retv = int(other - self); + ACTION_RETURN_INT(retv); +} diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 2d5423f08..ae7cf2143 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -320,7 +320,7 @@ bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *ques // //========================================================================== -IMPLEMENT_CLASS(DActiveButton, false, false, false, false) +IMPLEMENT_CLASS(DActiveButton, false, false) DActiveButton::DActiveButton () { diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 01595ff65..e9752ee53 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -47,7 +47,7 @@ extern void P_CalcHeight (player_t *player); CVAR (Bool, telezoom, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -IMPLEMENT_CLASS(ATeleportFog, false, false, false, false) +IMPLEMENT_CLASS(ATeleportFog, false, false) void ATeleportFog::PostBeginPlay () { @@ -96,6 +96,18 @@ void P_SpawnTeleportFog(AActor *mobj, const DVector3 &pos, bool beforeTele, bool mo->target = mobj; } +DEFINE_ACTION_FUNCTION(AActor, SpawnTeleportFog) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_BOOL(before); + PARAM_BOOL(settarget); + P_SpawnTeleportFog(self, DVector3(x, y, z), before, settarget); + return 0; +} + // // TELEPORTATION // @@ -226,6 +238,17 @@ bool P_Teleport (AActor *thing, DVector3 pos, DAngle angle, int flags) return true; } +DEFINE_ACTION_FUNCTION(AActor, Teleport) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_ANGLE(an); + PARAM_INT(flags); + ACTION_RETURN_BOOL(P_Teleport(self, DVector3(x, y, z), an, flags)); +} + static AActor *SelectTeleDest (int tid, int tag, bool norandom) { AActor *searcher; diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index 2504bad94..3adc94876 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -724,3 +724,18 @@ FName P_GetTerrainName(int terrainnum) } } +DEFINE_FIELD_NAMED(FTerrainDef, Name, TerrainName) +DEFINE_FIELD(FTerrainDef, Splash) +DEFINE_FIELD(FTerrainDef, DamageAmount) +DEFINE_FIELD(FTerrainDef, DamageMOD) +DEFINE_FIELD(FTerrainDef, DamageTimeMask) +DEFINE_FIELD(FTerrainDef, FootClip) +DEFINE_FIELD(FTerrainDef, StepVolume) +DEFINE_FIELD(FTerrainDef, WalkStepTics) +DEFINE_FIELD(FTerrainDef, RunStepTics) +DEFINE_FIELD(FTerrainDef, LeftStepSound) +DEFINE_FIELD(FTerrainDef, RightStepSound) +DEFINE_FIELD(FTerrainDef, IsLiquid) +DEFINE_FIELD(FTerrainDef, AllowProtection) +DEFINE_FIELD(FTerrainDef, Friction) +DEFINE_FIELD(FTerrainDef, MoveFactor) diff --git a/src/p_things.cpp b/src/p_things.cpp index d94ba6a01..ea01d38ed 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -520,6 +520,13 @@ PClassActor *P_GetSpawnableType(int spawnnum) return NULL; } +DEFINE_ACTION_FUNCTION(AActor, GetSpawnableType) +{ + PARAM_PROLOGUE; + PARAM_INT(num); + ACTION_RETURN_OBJECT(P_GetSpawnableType(num)); +} + struct MapinfoSpawnItem { FName classname; // DECORATE is read after MAPINFO so we do not have the actual classes available here yet. diff --git a/src/p_user.cpp b/src/p_user.cpp index 6b0b2cfe6..74b07e0ca 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -48,7 +48,6 @@ #include "doomdef.h" #include "c_dispatch.h" #include "tarray.h" -#include "vm.h" #include "g_level.h" #include "d_net.h" #include "gstrings.h" @@ -60,6 +59,8 @@ #include "a_morph.h" #include "p_spec.h" #include "virtual.h" +#include "a_armor.h" +#include "a_ammo.h" static FRandom pr_skullpop ("SkullPop"); @@ -487,14 +488,33 @@ void player_t::SetLogNumber (int num) } } +DEFINE_ACTION_FUNCTION(_PlayerInfo, SetLogNumber) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_INT(log); + self->SetLogNumber(log); + return 0; +} + void player_t::SetLogText (const char *text) { LogText = text; - // Print log text to console - AddToConsole(-1, TEXTCOLOR_GOLD); - AddToConsole(-1, LogText); - AddToConsole(-1, "\n"); + if (mo->CheckLocalView(consoleplayer)) + { + // Print log text to console + AddToConsole(-1, TEXTCOLOR_GOLD); + AddToConsole(-1, LogText); + AddToConsole(-1, "\n"); + } +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, SetLogText) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_STRING(log); + self->SetLogText(log); + return 0; } int player_t::GetSpawnClass() @@ -509,7 +529,7 @@ int player_t::GetSpawnClass() // //=========================================================================== -IMPLEMENT_CLASS(PClassPlayerPawn, false, false, false, false) +IMPLEMENT_CLASS(PClassPlayerPawn, false, false) PClassPlayerPawn::PClassPlayerPawn() { @@ -623,14 +643,14 @@ void player_t::SendPitchLimits() const // //=========================================================================== -IMPLEMENT_CLASS(APlayerPawn, false, true, false, true) +IMPLEMENT_CLASS(APlayerPawn, false, true) IMPLEMENT_POINTERS_START(APlayerPawn) IMPLEMENT_POINTER(InvFirst) IMPLEMENT_POINTER(InvSel) IMPLEMENT_POINTERS_END -IMPLEMENT_CLASS(APlayerChunk, false, false, false, false) +IMPLEMENT_CLASS(APlayerChunk, false, false) void APlayerPawn::Serialize(FSerializer &arc) { @@ -1208,6 +1228,12 @@ int APlayerPawn::GetMaxHealth() const return MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth); } +DEFINE_ACTION_FUNCTION(APlayerPawn, GetMaxHealth) +{ + PARAM_SELF_PROLOGUE(APlayerPawn); + ACTION_RETURN_INT(self->GetMaxHealth()); +} + //=========================================================================== // // APlayerPawn :: UpdateWaterLevel @@ -1261,6 +1287,13 @@ bool APlayerPawn::ResetAirSupply (bool playgasp) return wasdrowning; } +DEFINE_ACTION_FUNCTION(APlayerPawn, ResetAirSupply) +{ + PARAM_SELF_PROLOGUE(APlayerPawn); + PARAM_BOOL_DEF(playgasp); + ACTION_RETURN_BOOL(self->ResetAirSupply(playgasp)); +} + //=========================================================================== // // Animations @@ -1269,28 +1302,38 @@ bool APlayerPawn::ResetAirSupply (bool playgasp) void APlayerPawn::PlayIdle () { - if (InStateSequence(state, SeeState)) - SetState (SpawnState); + IFVIRTUAL(APlayerPawn, PlayIdle) + { + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } void APlayerPawn::PlayRunning () { - if (InStateSequence(state, SpawnState) && SeeState != NULL) - SetState (SeeState); + IFVIRTUAL(APlayerPawn, PlayRunning) + { + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } void APlayerPawn::PlayAttacking () { - if (MissileState != NULL) SetState (MissileState); + IFVIRTUAL(APlayerPawn, PlayAttacking) + { + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } void APlayerPawn::PlayAttacking2 () { - if (MeleeState != NULL) SetState (MeleeState); -} - -void APlayerPawn::ThrowPoisonBag () -{ + IFVIRTUAL(APlayerPawn, PlayAttacking2) + { + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } //=========================================================================== @@ -1376,6 +1419,11 @@ void APlayerPawn::GiveDefaultInventory () void APlayerPawn::MorphPlayerThink () { + IFVIRTUAL(APlayerPawn, MorphPlayerThink) + { + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } void APlayerPawn::ActivateMorphWeapon () @@ -1431,7 +1479,7 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags) if (player != NULL && player->mo != this) { // Make the real player die, too - player->mo->Die (source, inflictor, dmgflags); + player->mo->CallDie (source, inflictor, dmgflags); } else { @@ -1626,7 +1674,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_PlayerScream) // //---------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SkullPop) +DEFINE_ACTION_FUNCTION(AActor, A_SkullPop) { PARAM_SELF_PROLOGUE(AActor); PARAM_CLASS_DEF(spawntype, APlayerChunk); @@ -3120,3 +3168,135 @@ bool P_IsPlayerTotallyFrozen(const player_t *player) player->cheats & CF_TOTALLYFROZEN || ((level.flags2 & LEVEL2_FROZEN) && player->timefreezer == 0); } + + +//========================================================================== +// +// native members +// +//========================================================================== + +DEFINE_FIELD(APlayerPawn, crouchsprite) +DEFINE_FIELD(APlayerPawn, MaxHealth) +DEFINE_FIELD(APlayerPawn, MugShotMaxHealth) +DEFINE_FIELD(APlayerPawn, RunHealth) +DEFINE_FIELD(APlayerPawn, PlayerFlags) +DEFINE_FIELD(APlayerPawn, InvFirst) +DEFINE_FIELD(APlayerPawn, InvSel) +DEFINE_FIELD(APlayerPawn, JumpZ) +DEFINE_FIELD(APlayerPawn, GruntSpeed) +DEFINE_FIELD(APlayerPawn, FallingScreamMinSpeed) +DEFINE_FIELD(APlayerPawn, FallingScreamMaxSpeed) +DEFINE_FIELD(APlayerPawn, ViewHeight) +DEFINE_FIELD(APlayerPawn, ForwardMove1) +DEFINE_FIELD(APlayerPawn, ForwardMove2) +DEFINE_FIELD(APlayerPawn, SideMove1) +DEFINE_FIELD(APlayerPawn, SideMove2) +DEFINE_FIELD(APlayerPawn, ScoreIcon) +DEFINE_FIELD(APlayerPawn, SpawnMask) +DEFINE_FIELD(APlayerPawn, MorphWeapon) +DEFINE_FIELD(APlayerPawn, AttackZOffset) +DEFINE_FIELD(APlayerPawn, UseRange) +DEFINE_FIELD(APlayerPawn, AirCapacity) +DEFINE_FIELD(APlayerPawn, FlechetteType) +DEFINE_FIELD(APlayerPawn, DamageFade) +DEFINE_FIELD(APlayerPawn, ViewBob) + +DEFINE_FIELD(PClassPlayerPawn, HealingRadiusType) +DEFINE_FIELD(PClassPlayerPawn, DisplayName) +DEFINE_FIELD(PClassPlayerPawn, SoundClass) +DEFINE_FIELD(PClassPlayerPawn, Face) +DEFINE_FIELD(PClassPlayerPawn, Portrait) +DEFINE_FIELD(PClassPlayerPawn, Slot) +DEFINE_FIELD(PClassPlayerPawn, InvulMode) +DEFINE_FIELD(PClassPlayerPawn, HexenArmor) +DEFINE_FIELD(PClassPlayerPawn, ColorRangeStart) +DEFINE_FIELD(PClassPlayerPawn, ColorRangeEnd) +DEFINE_FIELD(PClassPlayerPawn, ColorSets) +DEFINE_FIELD(PClassPlayerPawn, PainFlashes) + +DEFINE_FIELD_X(PlayerInfo, player_t, mo) +DEFINE_FIELD_X(PlayerInfo, player_t, playerstate) +DEFINE_FIELD_X(PlayerInfo, player_t, original_oldbuttons) +DEFINE_FIELD_X(PlayerInfo, player_t, cls) +DEFINE_FIELD_X(PlayerInfo, player_t, DesiredFOV) +DEFINE_FIELD_X(PlayerInfo, player_t, FOV) +DEFINE_FIELD_X(PlayerInfo, player_t, viewz) +DEFINE_FIELD_X(PlayerInfo, player_t, viewheight) +DEFINE_FIELD_X(PlayerInfo, player_t, deltaviewheight) +DEFINE_FIELD_X(PlayerInfo, player_t, bob) +DEFINE_FIELD_X(PlayerInfo, player_t, Vel) +DEFINE_FIELD_X(PlayerInfo, player_t, centering) +DEFINE_FIELD_X(PlayerInfo, player_t, turnticks) +DEFINE_FIELD_X(PlayerInfo, player_t, attackdown) +DEFINE_FIELD_X(PlayerInfo, player_t, usedown) +DEFINE_FIELD_X(PlayerInfo, player_t, oldbuttons) +DEFINE_FIELD_X(PlayerInfo, player_t, health) +DEFINE_FIELD_X(PlayerInfo, player_t, inventorytics) +DEFINE_FIELD_X(PlayerInfo, player_t, CurrentPlayerClass) +DEFINE_FIELD_X(PlayerInfo, player_t, frags) +DEFINE_FIELD_X(PlayerInfo, player_t, fragcount) +DEFINE_FIELD_X(PlayerInfo, player_t, lastkilltime) +DEFINE_FIELD_X(PlayerInfo, player_t, multicount) +DEFINE_FIELD_X(PlayerInfo, player_t, spreecount) +DEFINE_FIELD_X(PlayerInfo, player_t, WeaponState) +DEFINE_FIELD_X(PlayerInfo, player_t, ReadyWeapon) +DEFINE_FIELD_X(PlayerInfo, player_t, PendingWeapon) +DEFINE_FIELD_X(PlayerInfo, player_t, psprites) +DEFINE_FIELD_X(PlayerInfo, player_t, cheats) +DEFINE_FIELD_X(PlayerInfo, player_t, timefreezer) +DEFINE_FIELD_X(PlayerInfo, player_t, refire) +DEFINE_FIELD_NAMED_X(PlayerInfo, player_t, inconsistant, inconsistent) +DEFINE_FIELD_X(PlayerInfo, player_t, waiting) +DEFINE_FIELD_X(PlayerInfo, player_t, killcount) +DEFINE_FIELD_X(PlayerInfo, player_t, itemcount) +DEFINE_FIELD_X(PlayerInfo, player_t, secretcount) +DEFINE_FIELD_X(PlayerInfo, player_t, damagecount) +DEFINE_FIELD_X(PlayerInfo, player_t, bonuscount) +DEFINE_FIELD_X(PlayerInfo, player_t, hazardcount) +DEFINE_FIELD_X(PlayerInfo, player_t, hazardinterval) +DEFINE_FIELD_X(PlayerInfo, player_t, hazardtype) +DEFINE_FIELD_X(PlayerInfo, player_t, poisoncount) +DEFINE_FIELD_X(PlayerInfo, player_t, poisontype) +DEFINE_FIELD_X(PlayerInfo, player_t, poisonpaintype) +DEFINE_FIELD_X(PlayerInfo, player_t, poisoner) +DEFINE_FIELD_X(PlayerInfo, player_t, attacker) +DEFINE_FIELD_X(PlayerInfo, player_t, extralight) +DEFINE_FIELD_X(PlayerInfo, player_t, fixedcolormap) +DEFINE_FIELD_X(PlayerInfo, player_t, fixedlightlevel) +DEFINE_FIELD_X(PlayerInfo, player_t, morphTics) +DEFINE_FIELD_X(PlayerInfo, player_t, MorphedPlayerClass) +DEFINE_FIELD_X(PlayerInfo, player_t, MorphStyle) +DEFINE_FIELD_X(PlayerInfo, player_t, MorphExitFlash) +DEFINE_FIELD_X(PlayerInfo, player_t, PremorphWeapon) +DEFINE_FIELD_X(PlayerInfo, player_t, chickenPeck) +DEFINE_FIELD_X(PlayerInfo, player_t, jumpTics) +DEFINE_FIELD_X(PlayerInfo, player_t, onground) +DEFINE_FIELD_X(PlayerInfo, player_t, respawn_time) +DEFINE_FIELD_X(PlayerInfo, player_t, camera) +DEFINE_FIELD_X(PlayerInfo, player_t, air_finished) +DEFINE_FIELD_X(PlayerInfo, player_t, LastDamageType) +DEFINE_FIELD_X(PlayerInfo, player_t, MUSINFOactor) +DEFINE_FIELD_X(PlayerInfo, player_t, MUSINFOtics) +DEFINE_FIELD_X(PlayerInfo, player_t, settings_controller) +DEFINE_FIELD_X(PlayerInfo, player_t, crouching) +DEFINE_FIELD_X(PlayerInfo, player_t, crouchdir) +DEFINE_FIELD_X(PlayerInfo, player_t, Bot) +DEFINE_FIELD_X(PlayerInfo, player_t, BlendR) +DEFINE_FIELD_X(PlayerInfo, player_t, BlendG) +DEFINE_FIELD_X(PlayerInfo, player_t, BlendB) +DEFINE_FIELD_X(PlayerInfo, player_t, BlendA) +DEFINE_FIELD_X(PlayerInfo, player_t, LogText) +DEFINE_FIELD_X(PlayerInfo, player_t, MinPitch) +DEFINE_FIELD_X(PlayerInfo, player_t, MaxPitch) +DEFINE_FIELD_X(PlayerInfo, player_t, crouchfactor) +DEFINE_FIELD_X(PlayerInfo, player_t, crouchoffset) +DEFINE_FIELD_X(PlayerInfo, player_t, crouchviewdelta) +DEFINE_FIELD_X(PlayerInfo, player_t, ConversationNPC) +DEFINE_FIELD_X(PlayerInfo, player_t, ConversationPC) +DEFINE_FIELD_X(PlayerInfo, player_t, ConversationNPCAngle) +DEFINE_FIELD_X(PlayerInfo, player_t, ConversationFaceTalker) +DEFINE_FIELD_X(PlayerInfo, player_t, cmd) +DEFINE_FIELD_X(PlayerInfo, player_t, original_cmd) +DEFINE_FIELD_X(PlayerInfo, player_t, userinfo) +DEFINE_FIELD_X(PlayerInfo, player_t, weapons) diff --git a/src/po_man.cpp b/src/po_man.cpp index 4e304daa8..fafa227ee 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -172,7 +172,7 @@ static FPolyNode *FreePolyNodes; // //========================================================================== -IMPLEMENT_CLASS(DPolyAction, false, true, false, false) +IMPLEMENT_CLASS(DPolyAction, false, true) IMPLEMENT_POINTERS_START(DPolyAction) IMPLEMENT_POINTER(m_Interpolation) @@ -240,7 +240,7 @@ void DPolyAction::StopInterpolation () // //========================================================================== -IMPLEMENT_CLASS(DRotatePoly, false, false, false, false) +IMPLEMENT_CLASS(DRotatePoly, false, false) DRotatePoly::DRotatePoly () { @@ -257,7 +257,7 @@ DRotatePoly::DRotatePoly (int polyNum) // //========================================================================== -IMPLEMENT_CLASS(DMovePoly, false, false, false, false) +IMPLEMENT_CLASS(DMovePoly, false, false) DMovePoly::DMovePoly () { @@ -284,7 +284,7 @@ DMovePoly::DMovePoly (int polyNum) // //========================================================================== -IMPLEMENT_CLASS(DMovePolyTo, false, false, false, false) +IMPLEMENT_CLASS(DMovePolyTo, false, false) DMovePolyTo::DMovePolyTo() { @@ -309,7 +309,7 @@ DMovePolyTo::DMovePolyTo(int polyNum) // //========================================================================== -IMPLEMENT_CLASS(DPolyDoor, false, false, false, false) +IMPLEMENT_CLASS(DPolyDoor, false, false) DPolyDoor::DPolyDoor () { diff --git a/src/po_man.h b/src/po_man.h index ec6a3901b..4d78fa179 100644 --- a/src/po_man.h +++ b/src/po_man.h @@ -12,7 +12,7 @@ class DPolyAction : public DThinker public: DPolyAction(int polyNum); void Serialize(FSerializer &arc); - void Destroy(); + void Destroy() override; void Stop(); double GetSpeed() const { return m_Speed; } diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index 3bbf42a9a..703280615 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -526,7 +526,7 @@ void ProcessMouseMoveInGame(NSEvent* theEvent) lastX = x; lastY = y; - if (0 != event.x | 0 != event.y) + if (0 != event.x || 0 != event.y) { event.type = EV_Mouse; diff --git a/src/posix/cocoa/i_joystick.cpp b/src/posix/cocoa/i_joystick.cpp index c4bcc85c3..747cc1422 100644 --- a/src/posix/cocoa/i_joystick.cpp +++ b/src/posix/cocoa/i_joystick.cpp @@ -1018,7 +1018,7 @@ IOKitJoystickManager::~IOKitJoystickManager() if (0 != notification) { IOObjectRelease(notification); - notification = NULL; + notification = 0; } } } diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index d8f0e74cf..851e72e48 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -156,7 +156,7 @@ static void I_DetectOS() case 12: name = "macOS Sierra"; break; } - char release[16] = {}; + char release[16] = "unknown"; size_t size = sizeof release - 1; sysctlbyname("kern.osversion", release, &size, nullptr, 0); diff --git a/src/posix/cocoa/i_timer.cpp b/src/posix/cocoa/i_timer.cpp index 901657eb9..5141f8750 100644 --- a/src/posix/cocoa/i_timer.cpp +++ b/src/posix/cocoa/i_timer.cpp @@ -37,8 +37,6 @@ #include #include -#include "basictypes.h" -#include "basicinlines.h" #include "doomdef.h" #include "i_system.h" #include "templates.h" diff --git a/src/posix/sdl/i_timer.cpp b/src/posix/sdl/i_timer.cpp index cad3000ba..84108f3b7 100644 --- a/src/posix/sdl/i_timer.cpp +++ b/src/posix/sdl/i_timer.cpp @@ -7,8 +7,7 @@ #include -#include "basictypes.h" -#include "basicinlines.h" +#include "m_fixed.h" #include "hardware.h" #include "i_system.h" #include "templates.h" diff --git a/src/posix/sdl/sdlvideo.cpp b/src/posix/sdl/sdlvideo.cpp index a1e6056ca..4a6833026 100644 --- a/src/posix/sdl/sdlvideo.cpp +++ b/src/posix/sdl/sdlvideo.cpp @@ -79,7 +79,7 @@ private: SDLFB () {} }; -IMPLEMENT_CLASS(SDLFB, false, false, false, false) +IMPLEMENT_CLASS(SDLFB, false, false) struct MiniModeInfo { diff --git a/src/r_data/r_interpolate.cpp b/src/r_data/r_interpolate.cpp index 871640f47..92dc683a8 100644 --- a/src/r_data/r_interpolate.cpp +++ b/src/r_data/r_interpolate.cpp @@ -62,7 +62,7 @@ public: DSectorPlaneInterpolation() {} DSectorPlaneInterpolation(sector_t *sector, bool plane, bool attach); - void Destroy(); + void Destroy() override; void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); @@ -91,7 +91,7 @@ public: DSectorScrollInterpolation() {} DSectorScrollInterpolation(sector_t *sector, bool plane); - void Destroy(); + void Destroy() override; void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); @@ -119,7 +119,7 @@ public: DWallScrollInterpolation() {} DWallScrollInterpolation(side_t *side, int part); - void Destroy(); + void Destroy() override; void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); @@ -146,7 +146,7 @@ public: DPolyobjInterpolation() {} DPolyobjInterpolation(FPolyObj *poly); - void Destroy(); + void Destroy() override; void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); @@ -161,17 +161,17 @@ public: // //========================================================================== -IMPLEMENT_CLASS(DInterpolation, true, true, false, false) +IMPLEMENT_CLASS(DInterpolation, true, true) IMPLEMENT_POINTERS_START(DInterpolation) IMPLEMENT_POINTER(Next) IMPLEMENT_POINTER(Prev) IMPLEMENT_POINTERS_END -IMPLEMENT_CLASS(DSectorPlaneInterpolation, false, false, false, false) -IMPLEMENT_CLASS(DSectorScrollInterpolation, false, false, false, false) -IMPLEMENT_CLASS(DWallScrollInterpolation, false, false, false, false) -IMPLEMENT_CLASS(DPolyobjInterpolation, false, false, false, false) +IMPLEMENT_CLASS(DSectorPlaneInterpolation, false, false) +IMPLEMENT_CLASS(DSectorScrollInterpolation, false, false) +IMPLEMENT_CLASS(DWallScrollInterpolation, false, false) +IMPLEMENT_CLASS(DPolyobjInterpolation, false, false) //========================================================================== // diff --git a/src/r_data/r_interpolate.h b/src/r_data/r_interpolate.h index 092cbe5ee..44ff9bed0 100644 --- a/src/r_data/r_interpolate.h +++ b/src/r_data/r_interpolate.h @@ -27,7 +27,7 @@ public: int AddRef(); int DelRef(bool force = false); - virtual void Destroy(); + void Destroy() override; virtual void UpdateInterpolation() = 0; virtual void Restore() = 0; virtual void Interpolate(double smoothratio) = 0; diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index fe8ff890a..78d6e22c9 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -1205,20 +1205,37 @@ void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayer //---------------------------------------------------------------------------- static TMap customTranslationMap; -int R_FindCustomTranslation(const char *name) +int R_FindCustomTranslation(FName name) { - if (name == nullptr) - { - return -1; - } - // Ice is a special case which will remain in its original slot. - if (!stricmp(name, "Ice")) + switch (name) { + case NAME_Ice: + // Ice is a special case which will remain in its original slot. return TRANSLATION(TRANSLATION_Standard, 7); - } - else if (!stricmp(name, "None")) - { + + case NAME_None: return 0; + + case NAME_RainPillar1: + case NAME_RainPillar2: + case NAME_RainPillar3: + case NAME_RainPillar4: + case NAME_RainPillar5: + case NAME_RainPillar6: + case NAME_RainPillar7: + case NAME_RainPillar8: + return TRANSLATION(TRANSLATION_RainPillar, name.GetIndex() - NAME_RainPillar1); + + case NAME_Player1: + case NAME_Player2: + case NAME_Player3: + case NAME_Player4: + case NAME_Player5: + case NAME_Player6: + case NAME_Player7: + case NAME_Player8: + return TRANSLATION(TRANSLATION_Players, name.GetIndex() - NAME_Player1); + } int *t = customTranslationMap.CheckKey(FName(name, true)); return (t != nullptr)? *t : -1; diff --git a/src/r_data/r_translate.h b/src/r_data/r_translate.h index 4ca2f203f..1680f4df0 100644 --- a/src/r_data/r_translate.h +++ b/src/r_data/r_translate.h @@ -110,7 +110,7 @@ extern TArray BloodTranslationColors; int CreateBloodTranslation(PalEntry color); -int R_FindCustomTranslation(const char *name); +int R_FindCustomTranslation(FName name); void R_ParseTrnslate(); diff --git a/src/r_defs.h b/src/r_defs.h index f172a84a6..0f5ed0017 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -278,7 +278,7 @@ class ASectorAction : public AActor DECLARE_CLASS (ASectorAction, AActor) public: ASectorAction (bool activatedByUse = false); - void Destroy (); + void Destroy () override; void BeginPlay (); void Activate (AActor *source); void Deactivate (AActor *source); @@ -658,6 +658,7 @@ public: sector_t *NextSpecialSector (int type, sector_t *prev) const; // [RH] double FindLowestCeilingPoint(vertex_t **v) const; double FindHighestFloorPoint(vertex_t **v) const; + void RemoveForceField(); void AdjustFloorClip () const; void SetColor(int r, int g, int b, int desat); @@ -1030,8 +1031,8 @@ public: BYTE soundtraversed; // 0 = untraversed, 1,2 = sndlines -1 // jff 2/26/98 lockout machinery for stairbuilding SBYTE stairlock; // -2 on first locked -1 after thinker done 0 normally - SWORD prevsec; // -1 or number of sector for previous step - SWORD nextsec; // -1 or number of next step sector + int prevsec; // -1 or number of sector for previous step + int nextsec; // -1 or number of next step sector short linecount; struct line_t **lines; // [linecount] size @@ -1276,9 +1277,7 @@ struct side_t struct line_t { vertex_t *v1, *v2; // vertices, from v1 to v2 -private: DVector2 delta; // precalculated v2 - v1 for side checking -public: uint32_t flags; uint32_t activation; // activation type int special; diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 369722242..ac5683b9b 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -2508,326 +2508,196 @@ void R_StoreWallRange (int start, int stop) ds_p++; } -int OWallMost (short *mostbuf, double z, const FWallCoords *wallc) +int WallMostAny(short *mostbuf, double z1, double z2, const FWallCoords *wallc) { - int bad, ix1, ix2; - double y, iy1, iy2; - double s1, s2, s3, s4; + float y1 = (float)(CenterY - z1 * InvZtoScale / wallc->sz1); + float y2 = (float)(CenterY - z2 * InvZtoScale / wallc->sz2); - z = -z; - s1 = globaluclip * wallc->sz1; s2 = globaluclip * wallc->sz2; - s3 = globaldclip * wallc->sz1; s4 = globaldclip * wallc->sz2; - bad = (zs3)<<2)+((z>s4)<<3); - - if ((bad&3) == 3) - { // entire line is above the screen - memset (&mostbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1)*sizeof(mostbuf[0])); - return bad; - } - - if ((bad&12) == 12) - { // entire line is below the screen - clearbufshort (&mostbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); - return bad; - } - ix1 = wallc->sx1; iy1 = wallc->sz1; - ix2 = wallc->sx2; iy2 = wallc->sz2; - if (bad & 3) - { // the line intersects the top of the screen - double t = (z-s1) / (s2-s1); - double inty = wallc->sz1 + t * (wallc->sz2 - wallc->sz1); - int xcross = xs_RoundToInt(wallc->sx1 + (t * wallc->sz2 * (wallc->sx2 - wallc->sx1)) / inty); - - if ((bad & 3) == 2) - { // the right side is above the screen - if (wallc->sx1 <= xcross) { iy2 = inty; ix2 = xcross; } - if (wallc->sx2 > xcross) memset (&mostbuf[xcross], 0, (wallc->sx2-xcross)*sizeof(mostbuf[0])); - } - else - { // the left side is above the screen - if (xcross <= wallc->sx2) { iy1 = inty; ix1 = xcross; } - if (xcross > wallc->sx1) memset (&mostbuf[wallc->sx1], 0, (xcross-wallc->sx1)*sizeof(mostbuf[0])); - } - } - - if (bad & 12) - { // the line intersects the bottom of the screen - double t = (z-s3) / (s4-s3); - double inty = wallc->sz1 + t * (wallc->sz2 - wallc->sz1); - int xcross = xs_RoundToInt(wallc->sx1 + (t * wallc->sz2 * (wallc->sx2 - wallc->sx1)) / inty); - - if ((bad & 12) == 8) - { // the right side is below the screen - if (wallc->sx1 <= xcross) { iy2 = inty; ix2 = xcross; } - if (wallc->sx2 > xcross) clearbufshort (&mostbuf[xcross], wallc->sx2 - xcross, viewheight); - } - else - { // the left side is below the screen - if (xcross <= wallc->sx2) { iy1 = inty; ix1 = xcross; } - if (xcross > wallc->sx1) clearbufshort (&mostbuf[wallc->sx1], xcross - wallc->sx1, viewheight); - } - } - - y = z * InvZtoScale / iy1; - if (ix2 == ix1) + if (y1 < 0 && y2 < 0) // entire line is above screen { - mostbuf[ix1] = (short)xs_RoundToInt(y + CenterY); + memset(&mostbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(mostbuf[0])); + return 3; + } + else if (y1 > viewheight && y2 > viewheight) // entire line is below screen + { + clearbufshort(&mostbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); + return 12; + } + + if (wallc->sx2 <= wallc->sx1) + return 0; + + float rcp_delta = 1.0f / (wallc->sx2 - wallc->sx1); + if (y1 >= 0.0f && y2 >= 0.0f && xs_RoundToInt(y1) <= viewheight && xs_RoundToInt(y2) <= viewheight) + { + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + float t = (x - wallc->sx1) * rcp_delta; + float y = y1 * (1.0f - t) + y2 * t; + mostbuf[x] = (short)xs_RoundToInt(y); + } } else { - fixed_t yinc = FLOAT2FIXED(((z * InvZtoScale / iy2) - y) / (ix2 - ix1)); - qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, FLOAT2FIXED(y + CenterY) + FRACUNIT/2, yinc); + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + float t = (x - wallc->sx1) * rcp_delta; + float y = y1 * (1.0f - t) + y2 * t; + mostbuf[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); + } } - return bad; + + return 0; } -int WallMost (short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) +int OWallMost(short *mostbuf, double z, const FWallCoords *wallc) +{ + return WallMostAny(mostbuf, z, z, wallc); +} + +int WallMost(short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) { if (!plane.isSlope()) { return OWallMost(mostbuf, plane.Zat0() - ViewPos.Z, wallc); } - - double x, y, den, z1, z2, oz1, oz2; - double s1, s2, s3, s4; - int bad, ix1, ix2; - double iy1, iy2; - - // Get Z coordinates at both ends of the line - if (MirrorFlags & RF_XFLIP) - { - x = curline->v2->fX(); - y = curline->v2->fY(); - if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) - { - double frac = (wallc->tleft.Y + wallc->tleft.X) / den; - x -= frac * (x - curline->v1->fX()); - y -= frac * (y - curline->v1->fY()); - } - z1 = ViewPos.Z - plane.ZatPoint(x, y); - - if (wallc->sx2 > wallc->sx1 + 1) - { - x = curline->v1->fX(); - y = curline->v1->fY(); - if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) - { - double frac = (wallc->tright.Y - wallc->tright.X) / den; - x += frac * (curline->v2->fX() - x); - y += frac * (curline->v2->fY() - y); - } - z2 = ViewPos.Z - plane.ZatPoint(x, y); - } - else - { - z2 = z1; - } - } else { - x = curline->v1->fX(); - y = curline->v1->fY(); - if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) - { - double frac = (wallc->tleft.Y + wallc->tleft.X) / den; - x += frac * (curline->v2->fX() - x); - y += frac * (curline->v2->fY() - y); - } - z1 = ViewPos.Z - plane.ZatPoint(x, y); - - if (wallc->sx2 > wallc->sx1 + 1) + // Get Z coordinates at both ends of the line + double x, y, den, z1, z2; + if (MirrorFlags & RF_XFLIP) { x = curline->v2->fX(); y = curline->v2->fY(); - if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) { - double frac = (wallc->tright.Y - wallc->tright.X) / den; + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; x -= frac * (x - curline->v1->fX()); y -= frac * (y - curline->v1->fY()); } - z2 = ViewPos.Z - plane.ZatPoint(x, y); - } - else - { - z2 = z1; - } - } + z1 = plane.ZatPoint(x, y) - ViewPos.Z; - s1 = globaluclip * wallc->sz1; s2 = globaluclip * wallc->sz2; - s3 = globaldclip * wallc->sz1; s4 = globaldclip * wallc->sz2; - bad = (z1s3)<<2)+((z2>s4)<<3); - - ix1 = wallc->sx1; ix2 = wallc->sx2; - iy1 = wallc->sz1; iy2 = wallc->sz2; - oz1 = z1; oz2 = z2; - - if ((bad&3) == 3) - { // The entire line is above the screen - memset (&mostbuf[ix1], 0, (ix2-ix1)*sizeof(mostbuf[0])); - return bad; - } - - if ((bad&12) == 12) - { // The entire line is below the screen - clearbufshort (&mostbuf[ix1], ix2-ix1, viewheight); - return bad; - - } - - if (bad&3) - { // The line intersects the top of the screen - //inty = intz / (globaluclip>>16) - double t = (oz1-s1) / (s2-s1+oz1-oz2); - double inty = wallc->sz1 + t * (wallc->sz2-wallc->sz1); - double intz = oz1 + t * (oz2-oz1); - int xcross = wallc->sx1 + xs_RoundToInt((t * wallc->sz2 * (wallc->sx2-wallc->sx1)) / inty); - - //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4)); - //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t); - //intz = z1 + mulscale30(z2-z1,t); - - if ((bad&3) == 2) - { // The right side of the line is above the screen - if (wallc->sx1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; } - memset (&mostbuf[xcross], 0, (wallc->sx2-xcross)*sizeof(mostbuf[0])); - } - else - { // The left side of the line is above the screen - if (xcross <= wallc->sx2) { z1 = intz; iy1 = inty; ix1 = xcross; } - memset (&mostbuf[wallc->sx1], 0, (xcross-wallc->sx1)*sizeof(mostbuf[0])); - } - } - - if (bad&12) - { // The line intersects the bottom of the screen - //inty = intz / (globaldclip>>16) - double t = (oz1-s3) / (s4-s3+oz1-oz2); - double inty = wallc->sz1 + t * (wallc->sz2-wallc->sz1); - double intz = oz1 + t * (oz2-oz1); - int xcross = wallc->sx1 + xs_RoundToInt((t * wallc->sz2 * (wallc->sx2-wallc->sx1)) / inty); - - //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4)); - //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t); - //intz = z1 + mulscale30(z2-z1,t); - - if ((bad&12) == 8) - { // The right side of the line is below the screen - if (wallc->sx1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; } - if (wallc->sx2 > xcross) clearbufshort (&mostbuf[xcross], wallc->sx2-xcross, viewheight); - } - else - { // The left side of the line is below the screen - if (xcross <= wallc->sx2) { z1 = intz; iy1 = inty; ix1 = xcross; } - if (xcross > wallc->sx1) clearbufshort (&mostbuf[wallc->sx1], xcross-wallc->sx1, viewheight); - } - } - - y = z1 * InvZtoScale / iy1; - if (ix2 == ix1) - { - mostbuf[ix1] = (short)xs_RoundToInt(y + CenterY); - } - else - { - fixed_t yinc = FLOAT2FIXED(((z2 * InvZtoScale / iy2) - y) / (ix2-ix1)); - qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, FLOAT2FIXED(y + CenterY) + FRACUNIT/2, yinc); - } - - return bad; -} - -static void PrepWallRoundFix(fixed_t *lwall, fixed_t walxrepeat, int x1, int x2) -{ - // fix for rounding errors - walxrepeat = abs(walxrepeat); - fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; - int x; - - if (x1 > 0) - { - for (x = x1; x < x2; x++) - { - if ((unsigned)lwall[x] >= (unsigned)walxrepeat) + if (wallc->sx2 > wallc->sx1 + 1) { - lwall[x] = fix; + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z2 = plane.ZatPoint(x, y) - ViewPos.Z; } else { - break; + z2 = z1; } } - } - fix = walxrepeat - 1 - fix; - for (x = x2-1; x >= x1; x--) - { - if ((unsigned)lwall[x] >= (unsigned)walxrepeat) - { - lwall[x] = fix; - } else { - break; + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z1 = plane.ZatPoint(x, y) - ViewPos.Z; + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z2 = plane.ZatPoint(x, y) - ViewPos.Z; + } + else + { + z2 = z1; + } + } + + return WallMostAny(mostbuf, z1, z2, wallc); + } +} + +void PrepWall(float *vstep, fixed_t *upos, double walxrepeat, int x1, int x2) +{ + float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX); + float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX); + float uGradient = WallT.UoverZstep; + float zGradient = WallT.InvZstep; + float xrepeat = (float)walxrepeat; + float depthScale = (float)(WallT.InvZstep * WallTMapScale2); + float depthOrg = (float)(-WallT.UoverZstep * WallTMapScale2); + + if (xrepeat < 0.0f) + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ; + + upos[x] = (fixed_t)((xrepeat - u * xrepeat) * FRACUNIT); + vstep[x] = depthOrg + u * depthScale; + + uOverZ += uGradient; + invZ += zGradient; + } + } + else + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ; + + upos[x] = (fixed_t)(u * xrepeat * FRACUNIT); + vstep[x] = depthOrg + u * depthScale; + + uOverZ += uGradient; + invZ += zGradient; } } } -void PrepWall (float *swall, fixed_t *lwall, double walxrepeat, int x1, int x2) -{ // swall = scale, lwall = texturecolumn - double top, bot, i; - double xrepeat = fabs(walxrepeat * 65536); - double depth_scale = WallT.InvZstep * WallTMapScale2; - double depth_org = -WallT.UoverZstep * WallTMapScale2; +void PrepLWall(fixed_t *upos, double walxrepeat, int x1, int x2) +{ + float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX); + float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX); + float uGradient = WallT.UoverZstep; + float zGradient = WallT.InvZstep; + float xrepeat = (float)walxrepeat; - i = x1 - centerx; - top = WallT.UoverZorg + WallT.UoverZstep * i; - bot = WallT.InvZorg + WallT.InvZstep * i; - - for (int x = x1; x < x2; x++) + if (xrepeat < 0.0f) { - double frac = top / bot; - if (walxrepeat < 0) + for (int x = x1; x < x2; x++) { - lwall[x] = xs_RoundToInt(xrepeat - frac * xrepeat); + float u = uOverZ / invZ * xrepeat - xrepeat; + + upos[x] = (fixed_t)(u * FRACUNIT); + + uOverZ += uGradient; + invZ += zGradient; } - else - { - lwall[x] = xs_RoundToInt(frac * xrepeat); - } - swall[x] = float(frac * depth_scale + depth_org); - top += WallT.UoverZstep; - bot += WallT.InvZstep; } - PrepWallRoundFix(lwall, FLOAT2FIXED(walxrepeat), x1, x2); -} - -void PrepLWall (fixed_t *lwall, double walxrepeat, int x1, int x2) -{ // lwall = texturecolumn - double top, bot, i; - double xrepeat = fabs(walxrepeat * 65536); - double topstep, botstep; - - i = x1 - centerx; - top = WallT.UoverZorg + WallT.UoverZstep * i; - bot = WallT.InvZorg + WallT.InvZstep * i; - - top *= xrepeat; - topstep = WallT.UoverZstep * xrepeat; - botstep = WallT.InvZstep; - - for (int x = x1; x < x2; x++) + else { - if (walxrepeat < 0) + for (int x = x1; x < x2; x++) { - lwall[x] = xs_RoundToInt(xrepeat - top / bot); + float u = uOverZ / invZ * xrepeat; + + upos[x] = (fixed_t)(u * FRACUNIT); + + uOverZ += uGradient; + invZ += zGradient; } - else - { - lwall[x] = xs_RoundToInt(top / bot); - } - top += topstep; - bot += botstep; } - PrepWallRoundFix(lwall, FLOAT2FIXED(walxrepeat), x1, x2); } // pass = 0: when seg is first drawn diff --git a/src/resourcefiles/file_zip.cpp b/src/resourcefiles/file_zip.cpp index 3f4548040..3ae708823 100644 --- a/src/resourcefiles/file_zip.cpp +++ b/src/resourcefiles/file_zip.cpp @@ -585,7 +585,7 @@ bool WriteZip(const char *filename, TArray &filenames, TArraySeqName; } -IMPLEMENT_CLASS(DSeqActorNode, false, true, false, false) +IMPLEMENT_CLASS(DSeqActorNode, false, true) IMPLEMENT_POINTERS_START(DSeqActorNode) IMPLEMENT_POINTER(m_Actor) @@ -441,7 +441,7 @@ void DSeqActorNode::Serialize(FSerializer &arc) arc("actor", m_Actor); } -IMPLEMENT_CLASS(DSeqPolyNode, false, false, false, false) +IMPLEMENT_CLASS(DSeqPolyNode, false, false) void DSeqPolyNode::Serialize(FSerializer &arc) { @@ -449,7 +449,7 @@ void DSeqPolyNode::Serialize(FSerializer &arc) arc("poly", m_Poly); } -IMPLEMENT_CLASS(DSeqSectorNode, false, false, false, false) +IMPLEMENT_CLASS(DSeqSectorNode, false, false) void DSeqSectorNode::Serialize(FSerializer &arc) { diff --git a/src/s_sndseq.h b/src/s_sndseq.h index 74ded5af4..13444f862 100644 --- a/src/s_sndseq.h +++ b/src/s_sndseq.h @@ -22,7 +22,7 @@ class DSeqNode : public DObject public: void Serialize(FSerializer &arc); void StopAndDestroy (); - void Destroy (); + void Destroy() override; void Tick (); void ChangeData (int seqOffset, int delayTics, float volume, FSoundID currentSoundID); void AddChoice (int seqnum, seqtype_t type); diff --git a/src/s_sound.cpp b/src/s_sound.cpp index a51a7101b..fd15b9491 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1232,6 +1232,16 @@ void S_Sound (int channel, FSoundID sound_id, float volume, float attenuation) S_StartSound (NULL, NULL, NULL, NULL, channel, sound_id, volume, attenuation); } +DEFINE_ACTION_FUNCTION(DObject, S_Sound) +{ + PARAM_PROLOGUE; + PARAM_SOUND(id); + PARAM_INT(channel); + PARAM_FLOAT_DEF(volume); + PARAM_FLOAT_DEF(attn); + return 0; +} + //========================================================================== // // S_Sound - An actor is source @@ -2597,6 +2607,17 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) return false; } +DEFINE_ACTION_FUNCTION(DObject, S_ChangeMusic) +{ + PARAM_PROLOGUE; + PARAM_STRING(music); + PARAM_INT_DEF(order); + PARAM_BOOL(looping); + PARAM_BOOL(force); + ACTION_RETURN_BOOL(S_ChangeMusic(music, order, looping, force)); +} + + //========================================================================== // // S_RestartMusic diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 194582d73..a57cc34ad 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -111,6 +111,10 @@ std2: 'sbyte' { RET(TK_SByte); } 'short' { RET(TK_Short); } 'ushort' { RET(TK_UShort); } + 'int8' { RET(TK_Int8); } + 'uint8' { RET(TK_UInt8); } + 'int16' { RET(TK_Int16); } + 'uint16' { RET(TK_UInt16); } 'int' { RET(TK_Int); } 'uint' { RET(TK_UInt); } 'long' { RET(TK_Long); } @@ -158,13 +162,11 @@ std2: 'private' { RET(TK_Private); } 'dot' { RET(TK_Dot); } 'cross' { RET(TK_Cross); } - 'ignores' { RET(TK_Ignores); } 'localized' { RET(TK_Localized); } 'latent' { RET(TK_Latent); } 'singular' { RET(TK_Singular); } 'config' { RET(TK_Config); } 'coerce' { RET(TK_Coerce); } - 'iterator' { RET(TK_Iterator); } 'optional' { RET(TK_Optional); } 'export' { RET(TK_Export); } 'virtual' { RET(TK_Virtual); } @@ -236,6 +238,7 @@ std2: "<>=" { RET(TK_LtGtEq); } "**" { RET(TK_MulMul); } "::" { RET(TK_ColonColon); } + "->" { RET(TK_Arrow); } ";" { StateOptions = false; RET(';'); } "{" { StateOptions = false; RET('{'); } "}" { RET('}'); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 801d02be3..c1ba10044 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -33,6 +33,7 @@ xx(TK_Neq, "'!='") xx(TK_ApproxEq, "'~=='") xx(TK_LtGtEq, "'<>='") xx(TK_MulMul, "'**'") +xx(TK_Arrow, "'->'") xx(TK_Action, "'action'") xx(TK_Break, "'break'") xx(TK_Case, "'case'") @@ -49,12 +50,17 @@ xx(TK_Until, "'until'") xx(TK_While, "'while'") xx(TK_Bool, "'bool'") xx(TK_Float, "'float'") +xx(TK_Float32, "'float32'") xx(TK_Double, "'double'") xx(TK_Char, "'char'") xx(TK_Byte, "'byte'") xx(TK_SByte, "'sbyte'") xx(TK_Short, "'short'") xx(TK_UShort, "'ushort'") +xx(TK_Int8, "'int8'") +xx(TK_UInt8, "'uint8'") +xx(TK_Int16, "'int16'") +xx(TK_UInt16, "'uint16'") xx(TK_Int, "'int'") xx(TK_UInt, "'uint'") xx(TK_Long, "'long'") diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index f45bea5e3..28b20e289 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -101,7 +101,7 @@ FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret, bool fromdecor if (fnc != nullptr) Class = fnc->OwningClass; } -FCompileContext::FCompileContext(PClass *cls, bool fromdecorate) +FCompileContext::FCompileContext(PStruct *cls, bool fromdecorate) : ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1) { } @@ -185,6 +185,16 @@ FxLocalVariableDeclaration *FCompileContext::FindLocalVariable(FName name) } } +static PStruct *FindStructType(FName name) +{ + PStruct *ccls = PClass::FindClass(name); + if (ccls == nullptr) + { + ccls = dyn_cast(TypeTable.FindType(RUNTIME_CLASS(PStruct), 0, (intptr_t)name, nullptr)); + if (ccls == nullptr) ccls = dyn_cast(TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), 0, (intptr_t)name, nullptr)); + } + return ccls; +} //========================================================================== // // ExpEmit @@ -230,6 +240,7 @@ static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCall { PSymbolVMFunction *symfunc = new PSymbolVMFunction(funcname); VMNativeFunction *calldec = new VMNativeFunction(func, funcname); + calldec->PrintableName = funcname.GetChars(); symfunc->Function = calldec; sym = symfunc; GlobalSymbols.AddSymbol(sym); @@ -243,7 +254,7 @@ static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCall // //========================================================================== -static bool AreCompatiblePointerTypes(PType *dest, PType *source) +static bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompare = false) { if (dest->IsKindOf(RUNTIME_CLASS(PPointer)) && source->IsKindOf(RUNTIME_CLASS(PPointer))) { @@ -251,12 +262,13 @@ static bool AreCompatiblePointerTypes(PType *dest, PType *source) auto fromtype = static_cast(source); auto totype = static_cast(dest); if (fromtype == nullptr) return true; - if (totype->IsConst && !fromtype->IsConst) return false; + if (!forcompare && totype->IsConst != fromtype->IsConst) return false; if (fromtype == totype) return true; if (fromtype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)) && totype->PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { auto fromcls = static_cast(fromtype->PointedType); auto tocls = static_cast(totype->PointedType); + if (forcompare && tocls->IsDescendantOf(fromcls)) return true; return (fromcls->IsDescendantOf(tocls)); } } @@ -838,7 +850,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx) { ExpVal constval = static_cast(basex)->GetValue(); FxExpression *x = new FxConstant(constval.GetInt(), ScriptPosition); - if (!NoWarn && constval.GetInt() != constval.GetFloat()) + if (constval.GetInt() != constval.GetFloat()) { ScriptPosition.Message(MSG_WARNING, "Truncation of floating point constant %f", constval.GetFloat()); } @@ -848,7 +860,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx) } else if (!NoWarn) { - ScriptPosition.Message(MSG_WARNING, "Truncation of floating point value"); + ScriptPosition.Message(MSG_DEBUGWARN, "Truncation of floating point value"); } return this; @@ -871,7 +883,7 @@ ExpEmit FxIntCast::Emit(VMFunctionBuilder *build) assert(basex->ValueType->GetRegType() == REGT_FLOAT); from.Free(build); ExpEmit to(build, REGT_INT); - build->Emit(OP_CAST, to.RegNum, from.RegNum, CAST_F2I); + build->Emit(OP_CAST, to.RegNum, from.RegNum, ValueType == TypeUInt32? CAST_F2U : CAST_F2I); return to; } @@ -962,7 +974,7 @@ ExpEmit FxFloatCast::Emit(VMFunctionBuilder *build) assert(basex->ValueType->GetRegType() == REGT_INT); from.Free(build); ExpEmit to(build, REGT_FLOAT); - build->Emit(OP_CAST, to.RegNum, from.RegNum, CAST_I2F); + build->Emit(OP_CAST, to.RegNum, from.RegNum, basex->ValueType == TypeUInt32? CAST_U2F : CAST_I2F); return to; } @@ -1428,6 +1440,14 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) delete this; return x; } + else if (ValueType == TypeSpriteID && basex->IsInteger()) + { + basex->ValueType = TypeSpriteID; + auto x = basex; + basex = nullptr; + delete this; + return x; + } else if (ValueType == TypeStateLabel) { if (basex->ValueType == TypeNullPtr) @@ -1440,8 +1460,8 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) // Right now this only supports string constants. There should be an option to pass a string variable, too. if (basex->isConstant() && (basex->ValueType == TypeString || basex->ValueType == TypeName)) { - const char *s = static_cast(basex)->GetValue().GetString(); - if (*s == 0 && !ctx.FromDecorate) // DECORATE should never get here at all, but let's better be safe. + FString s= static_cast(basex)->GetValue().GetString(); + if (s.Len() == 0 && !ctx.FromDecorate) // DECORATE should never get here at all, but let's better be safe. { ScriptPosition.Message(MSG_ERROR, "State jump to empty label."); delete this; @@ -1670,19 +1690,23 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build) { assert(ValueType == Operand->ValueType); ExpEmit from = Operand->Emit(build); + ExpEmit to; assert(from.Konst == 0); assert(ValueType->GetRegCount() == from.RegCount); // Do it in-place, unless a local variable if (from.Fixed) { - ExpEmit to = ExpEmit(build, from.RegType, from.RegCount); - build->Emit(Operand->ValueType->GetMoveOp(), to.RegNum, from.RegNum); - from = to; + to = ExpEmit(build, from.RegType, from.RegCount); + from.Free(build); + } + else + { + to = from; } if (ValueType->GetRegType() == REGT_INT) { - build->Emit(OP_NEG, from.RegNum, from.RegNum, 0); + build->Emit(OP_NEG, to.RegNum, from.RegNum, 0); } else { @@ -1690,20 +1714,20 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build) switch (from.RegCount) { case 1: - build->Emit(OP_FLOP, from.RegNum, from.RegNum, FLOP_NEG); + build->Emit(OP_FLOP, to.RegNum, from.RegNum, FLOP_NEG); break; case 2: - build->Emit(OP_NEGV2, from.RegNum, from.RegNum); + build->Emit(OP_NEGV2, to.RegNum, from.RegNum); break; case 3: - build->Emit(OP_NEGV3, from.RegNum, from.RegNum); + build->Emit(OP_NEGV3, to.RegNum, from.RegNum); break; } } - return from; + return to; } //========================================================================== @@ -1850,10 +1874,13 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build) assert(Operand->ValueType == TypeBool); assert(ValueType == TypeBool || IsInteger()); // this may have been changed by an int cast. ExpEmit from = Operand->Emit(build); + from.Free(build); + ExpEmit to(build, REGT_INT); assert(!from.Konst); // boolean not is the same as XOR-ing the lowest bit - build->Emit(OP_XOR_RK, from.RegNum, from.RegNum, build->GetConstantInt(1)); - return from; + + build->Emit(OP_XOR_RK, to.RegNum, from.RegNum, build->GetConstantInt(1)); + return to; } //========================================================================== @@ -1989,7 +2016,7 @@ ExpEmit FxPreIncrDecr::Emit(VMFunctionBuilder *build) if (regtype == REGT_INT) { - build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, value.RegNum, value.RegNum, build->GetConstantInt(1)); + build->Emit(OP_ADDI, value.RegNum, value.RegNum, uint8_t((Token == TK_Incr) ? 1 : -1)); } else { @@ -2073,7 +2100,7 @@ ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build) ExpEmit assign(build, regtype); if (regtype == REGT_INT) { - build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, assign.RegNum, out.RegNum, build->GetConstantInt(1)); + build->Emit(OP_ADDI, assign.RegNum, out.RegNum, uint8_t((Token == TK_Incr) ? 1 : -1)); } else { @@ -2090,7 +2117,7 @@ ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build) if (regtype == REGT_INT) { build->Emit(OP_MOVE, out.RegNum, pointer.RegNum); - build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, pointer.RegNum, pointer.RegNum, build->GetConstantInt(1)); + build->Emit(OP_ADDI, pointer.RegNum, pointer.RegNum, uint8_t((Token == TK_Incr) ? 1 : -1)); } else { @@ -2104,7 +2131,7 @@ ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build) { if (regtype == REGT_INT) { - build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, pointer.RegNum, pointer.RegNum, build->GetConstantInt(1)); + build->Emit(OP_ADDI, pointer.RegNum, pointer.RegNum, uint8_t((Token == TK_Incr) ? 1 : -1)); } else { @@ -2198,6 +2225,14 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) } // Both types are the same so this is ok. } + else if (Right->ValueType->IsA(RUNTIME_CLASS(PNativeStruct)) && Base->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(Base->ValueType)->PointedType == Right->ValueType) + { + // allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers. + // For all other types this is not needed. Structs are not assignable and classes can only exist as references. + bool writable; + Right->RequestAddress(ctx, &writable); + Right->ValueType = Base->ValueType; + } else { // pass it to FxTypeCast for complete handling. @@ -2213,16 +2248,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) } // Special case: Assignment to a bitfield. - if (Base->ExprType == EFX_StructMember || Base->ExprType == EFX_ClassMember) - { - auto f = static_cast(Base)->membervar; - if (f->BitValue != -1 && !ctx.CheckReadOnly(f->Flags)) - { - IsBitWrite = f->BitValue; - return this; - } - } - + IsBitWrite = Base->GetBitValue(); return this; } @@ -2310,7 +2336,7 @@ ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build) ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it if (!pointer.Target) { - ExpEmit out(build, ValueType->GetRegType()); + ExpEmit out(build, ValueType->GetRegType(), ValueType->GetRegCount()); if (Assignment->IsBitWrite != -1) { build->Emit(OP_LBIT, out.RegNum, pointer.RegNum, 1 << Assignment->IsBitWrite); @@ -2327,6 +2353,95 @@ ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build) } } + +//========================================================================== +// +// +// +//========================================================================== + +FxMultiAssign::FxMultiAssign(FArgumentList &base, FxExpression *right, const FScriptPosition &pos) + :FxExpression(EFX_MultiAssign, pos) +{ + Base = std::move(base); + Right = right; + LocalVarContainer = new FxCompoundStatement(ScriptPosition); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxMultiAssign::~FxMultiAssign() +{ + SAFE_DELETE(Right); + SAFE_DELETE(LocalVarContainer); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxMultiAssign::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Right, ctx); + if (Right->ExprType != EFX_VMFunctionCall) + { + Right->ScriptPosition.Message(MSG_ERROR, "Function call expected on right side of multi-assigment"); + delete this; + return nullptr; + } + auto VMRight = static_cast(Right); + auto rets = VMRight->GetReturnTypes(); + if (rets.Size() < Base.Size()) + { + Right->ScriptPosition.Message(MSG_ERROR, "Insufficient returns in function %s", VMRight->Function->SymbolName.GetChars()); + delete this; + return nullptr; + } + // Pack the generated data (temp local variables for the results and necessary type casts and single assignments) into a compound statement for easier management. + for (unsigned i = 0; i < Base.Size(); i++) + { + auto singlevar = new FxLocalVariableDeclaration(rets[i], NAME_None, nullptr, 0, ScriptPosition); + LocalVarContainer->Add(singlevar); + Base[i] = Base[i]->Resolve(ctx); + ABORT(Base[i]); + auto varaccess = new FxLocalVariable(singlevar, ScriptPosition); + auto assignee = new FxTypeCast(varaccess, Base[i]->ValueType, false); + LocalVarContainer->Add(new FxAssign(Base[i], assignee, false)); + } + auto x = LocalVarContainer->Resolve(ctx); + LocalVarContainer = nullptr; + ABORT(x); + LocalVarContainer = static_cast(x); + static_cast(Right)->AssignCount = Base.Size(); + ValueType = TypeVoid; + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpEmit FxMultiAssign::Emit(VMFunctionBuilder *build) +{ + Right->Emit(build); + for (unsigned i = 0; i < Base.Size(); i++) + { + LocalVarContainer->LocalVars[i]->SetReg(static_cast(Right)->ReturnRegs[i]); + } + static_cast(Right)->ReturnRegs.Clear(); + static_cast(Right)->ReturnRegs.ShrinkToFit(); + return LocalVarContainer->Emit(build); +} + //========================================================================== // // @@ -2359,215 +2474,59 @@ FxBinary::~FxBinary() // //========================================================================== -bool FxBinary::ResolveLR(FCompileContext& ctx, bool castnumeric) +bool FxBinary::Promote(FCompileContext &ctx, bool forceint) { - RESOLVE(left, ctx); - RESOLVE(right, ctx); - if (!left || !right) + // math operations of unsigned ints results in an unsigned int. (16 and 8 bit values never get here, they get promoted to regular ints elsewhere already.) + if (left->ValueType == TypeUInt32 && right->ValueType == TypeUInt32) { - delete this; - return false; + ValueType = TypeUInt32; } - - if (left->ValueType == TypeString || right->ValueType == TypeString) + else if (left->IsInteger() && right->IsInteger()) { - switch (Operator) + ValueType = TypeSInt32; // Addition and subtraction forces all integer-derived types to signed int. + } + else if (!forceint) + { + ValueType = TypeFloat64; + if (left->IsFloat() && right->IsInteger()) + { + right = (new FxFloatCast(right))->Resolve(ctx); + } + else if (left->IsInteger() && right->IsFloat()) + { + left = (new FxFloatCast(left))->Resolve(ctx); + } + } + else if (ctx.FromDecorate) + { + // For DECORATE which allows floats here. ZScript does not. + if (left->IsFloat()) + { + left = new FxIntCast(left, ctx.FromDecorate); + left = left->Resolve(ctx); + } + if (right->IsFloat()) + { + right = new FxIntCast(right, ctx.FromDecorate); + right = right->Resolve(ctx); + } + if (left == nullptr || right == nullptr) { - case '+': - // later - break; - - case '<': - case '>': - case TK_Geq: - case TK_Leq: - case TK_Eq: - case TK_Neq: - case TK_ApproxEq: - if (left->ValueType != TypeString) - { - left = new FxStringCast(left); - left = left->Resolve(ctx); - if (left == nullptr) - { - delete this; - return false; - } - } - if (right->ValueType != TypeString) - { - right = new FxStringCast(right); - right = right->Resolve(ctx); - if (right == nullptr) - { - delete this; - return false; - } - } - ValueType = TypeBool; - break; - - default: - ScriptPosition.Message(MSG_ERROR, "Incompatible operands for comparison"); delete this; return false; } - } - else if (left->IsVector() || right->IsVector()) - { - switch (Operator) - { - case '+': - case '-': - // a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand. - if (left->ValueType == right->ValueType || (left->ValueType == TypeVector3 && right->ValueType == TypeVector2)) - { - ValueType = left->ValueType; - return true; - } - else - { - ScriptPosition.Message(MSG_ERROR, "Incompatible operands for operator %c", Operator); - delete this; - return false; - } - break; + ValueType = TypeSInt32; - case '/': - if (right->IsVector()) - { - // For division, the vector must be the first operand. - ScriptPosition.Message(MSG_ERROR, "Incompatible operands for division"); - delete this; - return false; - } - case '*': - if (left->IsVector()) - { - right = new FxFloatCast(right); - right = right->Resolve(ctx); - if (right == nullptr) - { - delete this; - return false; - } - ValueType = left->ValueType; - } - else - { - left = new FxFloatCast(left); - left = left->Resolve(ctx); - if (left == nullptr) - { - delete this; - return false; - } - ValueType = right->ValueType; - } - break; - - case TK_Eq: - case TK_Neq: - if (left->ValueType != right->ValueType) - { - ScriptPosition.Message(MSG_ERROR, "Incompatible operands for comparison"); - delete this; - return false; - } - ValueType = TypeBool; - break; - - default: - ScriptPosition.Message(MSG_ERROR, "Incompatible operation for vector type"); - delete this; - return false; - - } - } - else if (left->ValueType == TypeBool && right->ValueType == TypeBool) - { - if (Operator == '&' || Operator == '|' || Operator == '^' || ctx.FromDecorate) - { - ValueType = TypeBool; - } - else - { - ValueType = TypeSInt32; // math operations on bools result in integers. - } - } - else if (left->ValueType == TypeName && right->ValueType == TypeName) - { - // pointers can only be compared for equality. - if (Operator == TK_Eq || Operator == TK_Neq) - { - ValueType = TypeBool; - return true; - } - else - { - ScriptPosition.Message(MSG_ERROR, "Invalid operation for names"); - delete this; - return false; - } - } - else if (left->IsNumeric() && right->IsNumeric()) - { - if (left->ValueType->GetRegType() == REGT_INT && right->ValueType->GetRegType() == REGT_INT) - { - ValueType = TypeSInt32; - } - else - { - ValueType = TypeFloat64; - } - } - else if (left->ValueType->GetRegType() == REGT_POINTER) - { - if (left->ValueType == right->ValueType || right->ValueType == TypeNullPtr || left->ValueType == TypeNullPtr || - AreCompatiblePointerTypes(left->ValueType, right->ValueType)) - { - // pointers can only be compared for equality. - if (Operator == TK_Eq || Operator == TK_Neq) - { - ValueType = TypeBool; - return true; - } - else - { - ScriptPosition.Message(MSG_ERROR, "Invalid operation for pointers"); - delete this; - return false; - } - } } else { - // To check: It may be that this could pass in DECORATE, although setting TypeVoid here would pretty much prevent that. - ScriptPosition.Message(MSG_ERROR, "Incompatible operator"); + ScriptPosition.Message(MSG_ERROR, "Integer operand expected"); delete this; return false; } - assert(ValueType != nullptr && ValueType < (PType*)0xfffffffffffffff); - - if (castnumeric) - { - // later! - } return true; } -void FxBinary::Promote(FCompileContext &ctx) -{ - if (left->ValueType->GetRegType() == REGT_FLOAT && right->ValueType->GetRegType() == REGT_INT) - { - right = (new FxFloatCast(right))->Resolve(ctx); - } - else if (left->ValueType->GetRegType() == REGT_INT && right->ValueType->GetRegType() == REGT_FLOAT) - { - left = (new FxFloatCast(left))->Resolve(ctx); - } -} - //========================================================================== // // @@ -2588,16 +2547,46 @@ FxAddSub::FxAddSub(int o, FxExpression *l, FxExpression *r) FxExpression *FxAddSub::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, true)) - return nullptr; - if (!IsNumeric() && !IsVector()) + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) { - ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return nullptr; } - else if (left->isConstant() && right->isConstant()) + + if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant()) + { + // This is the only special case of pointer addition that will be accepted - because it is used quite often in the existing game code. + ValueType = TypeState; + right = new FxMulDiv('*', right, new FxConstant((int)sizeof(FState), ScriptPosition)); // multiply by size here, so that constants can be better optimized. + right = right->Resolve(ctx); + ABORT(right); + } + else if (left->IsVector() && right->IsVector()) + { + // a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand. + if (left->ValueType == right->ValueType || (left->ValueType == TypeVector3 && right->ValueType == TypeVector2)) + { + ValueType = left->ValueType; + } + else + { + goto error; + } + } + else if (left->IsNumeric() && right->IsNumeric()) + { + Promote(ctx); + } + else + { + // To check: It may be that this could pass in DECORATE, although setting TypeVoid here would pretty much prevent that. + goto error; + } + + if (left->isConstant() && right->isConstant()) { if (IsFloat()) { @@ -2627,8 +2616,12 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx) } } - Promote(ctx); return this; + +error: + ScriptPosition.Message(MSG_ERROR, "Incompatible operands for %s", Operator == '+' ? "addition" : "subtraction"); + delete this; + return nullptr; } //========================================================================== @@ -2644,6 +2637,16 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) ExpEmit op2 = right->Emit(build); if (Operator == '+') { + if (op1.RegType == REGT_POINTER) + { + assert(!op1.Konst); + assert(op2.RegType == REGT_INT); + op1.Free(build); + op2.Free(build); + ExpEmit opout(build, REGT_POINTER); + build->Emit(op2.Konst? OP_ADDA_RK : OP_ADDA_RR, opout.RegNum, op1.RegNum, op2.RegNum); + return opout; + } // Since addition is commutative, only the second operand may be a constant. if (op1.Konst) { @@ -2728,16 +2731,70 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, true)) - return nullptr; - - if (!IsNumeric() && !IsVector()) + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) { - ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return nullptr; } - else if (left->isConstant() && right->isConstant()) + + if (left->IsVector() || right->IsVector()) + { + switch (Operator) + { + case '/': + // For division, the vector must be the first operand. + if (right->IsVector()) goto error; + + case '*': + if (left->IsVector() && right->IsNumeric()) + { + if (right->IsInteger()) + { + right = new FxFloatCast(right); + right = right->Resolve(ctx); + if (right == nullptr) + { + delete this; + return nullptr; + } + } + ValueType = left->ValueType; + } + else if (right->IsVector() && left->IsNumeric()) + { + if (left->IsInteger()) + { + left = new FxFloatCast(left); + left = left->Resolve(ctx); + if (left == nullptr) + { + delete this; + return nullptr; + } + } + ValueType = right->ValueType; + } + break; + + default: + // Vector modulus is not permitted + goto error; + + } + } + else if (left->IsNumeric() && right->IsNumeric()) + { + Promote(ctx); + } + else + { + // To check: It may be that this could pass in DECORATE, although setting TypeVoid here would pretty much prevent that. + goto error; + } + + if (left->isConstant() && right->isConstant()) { if (IsFloat()) { @@ -2783,8 +2840,13 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) } } - Promote(ctx); return this; + +error: + ScriptPosition.Message(MSG_ERROR, "Incompatible operands for %s", Operator == '*' ? "multiplication" : Operator == '%' ? "modulus" : "division"); + delete this; + return nullptr; + } @@ -2796,7 +2858,6 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build) { - // allocate the result first so that the operation does not leave gaps in the register set. ExpEmit op1 = left->Emit(build); ExpEmit op2 = right->Emit(build); @@ -2868,9 +2929,18 @@ ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build) { assert(ValueType->GetRegType() == REGT_INT); assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); - build->Emit(Operator == '/' ? (op1.Konst ? OP_DIV_KR : op2.Konst ? OP_DIV_RK : OP_DIV_RR) - : (op1.Konst ? OP_MOD_KR : op2.Konst ? OP_MOD_RK : OP_MOD_RR), - to.RegNum, op1.RegNum, op2.RegNum); + if (ValueType == TypeUInt32) + { + build->Emit(Operator == '/' ? (op1.Konst ? OP_DIVU_KR : op2.Konst ? OP_DIVU_RK : OP_DIVU_RR) + : (op1.Konst ? OP_MODU_KR : op2.Konst ? OP_MODU_RK : OP_MODU_RR), + to.RegNum, op1.RegNum, op2.RegNum); + } + else + { + build->Emit(Operator == '/' ? (op1.Konst ? OP_DIV_KR : op2.Konst ? OP_DIV_RK : OP_DIV_RR) + : (op1.Konst ? OP_MOD_KR : op2.Konst ? OP_MOD_RK : OP_MOD_RR), + to.RegNum, op1.RegNum, op2.RegNum); + } return to; } } @@ -2897,15 +2967,29 @@ FxExpression *FxPow::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, true)) - return nullptr; - - if (!IsNumeric()) + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) { - ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return nullptr; } + if (!left->IsNumeric() || !right->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected for '**'"); + delete this; + return nullptr; + } + if (!left->IsFloat()) + { + left = (new FxFloatCast(left))->Resolve(ctx); + ABORT(left); + } + if (!right->IsFloat()) + { + right = (new FxFloatCast(right))->Resolve(ctx); + ABORT(right); + } if (left->isConstant() && right->isConstant()) { double v1 = static_cast(left)->GetValue().GetFloat(); @@ -2915,7 +2999,6 @@ FxExpression *FxPow::Resolve(FCompileContext& ctx) return this; } - //========================================================================== // // @@ -2957,16 +3040,51 @@ FxCompareRel::FxCompareRel(int o, FxExpression *l, FxExpression *r) FxExpression *FxCompareRel::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, true)) - return nullptr; - if (!IsNumeric()) + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) { - ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return nullptr; } - else if (left->isConstant() && right->isConstant()) + + if (left->ValueType == TypeString || right->ValueType == TypeString) + { + if (left->ValueType != TypeString) + { + left = new FxStringCast(left); + left = left->Resolve(ctx); + if (left == nullptr) + { + delete this; + return nullptr; + } + } + if (right->ValueType != TypeString) + { + right = new FxStringCast(right); + right = right->Resolve(ctx); + if (right == nullptr) + { + delete this; + return nullptr; + } + } + ValueType = TypeString; + } + else if (left->IsNumeric() && right->IsNumeric()) + { + Promote(ctx); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Incompatible operands for relative comparison"); + delete this; + return nullptr; + } + + if (left->isConstant() && right->isConstant()) { int v; @@ -2989,20 +3107,29 @@ FxExpression *FxCompareRel::Resolve(FCompileContext& ctx) Operator == TK_Geq? v1 >= v2 : Operator == TK_Leq? v1 <= v2 : 0; } - else + else if (ValueType == TypeUInt32) { - int v1 = static_cast(left)->GetValue().GetInt(); - int v2 = static_cast(right)->GetValue().GetInt(); + int v1 = static_cast(left)->GetValue().GetUInt(); + int v2 = static_cast(right)->GetValue().GetUInt(); v = Operator == '<'? v1 < v2 : Operator == '>'? v1 > v2 : Operator == TK_Geq? v1 >= v2 : Operator == TK_Leq? v1 <= v2 : 0; } + else + { + int v1 = static_cast(left)->GetValue().GetInt(); + int v2 = static_cast(right)->GetValue().GetInt(); + v = Operator == '<' ? v1 < v2 : + Operator == '>' ? v1 > v2 : + Operator == TK_Geq ? v1 >= v2 : + Operator == TK_Leq ? v1 <= v2 : 0; + } FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } - Promote(ctx); + CompareType = ValueType; // needs to be preserved for detection of unsigned compare. ValueType = TypeBool; return this; } @@ -3057,10 +3184,10 @@ ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build) assert(Operator == '<' || Operator == '>' || Operator == TK_Geq || Operator == TK_Leq); static const VM_UBYTE InstrMap[][4] = { - { OP_LT_RR, OP_LTF_RR, 0 }, // < - { OP_LE_RR, OP_LEF_RR, 1 }, // > - { OP_LT_RR, OP_LTF_RR, 1 }, // >= - { OP_LE_RR, OP_LEF_RR, 0 } // <= + { OP_LT_RR, OP_LTF_RR, OP_LTU_RR, 0 }, // < + { OP_LE_RR, OP_LEF_RR, OP_LEU_RR, 1 }, // > + { OP_LT_RR, OP_LTF_RR, OP_LTU_RR, 1 }, // >= + { OP_LE_RR, OP_LEF_RR, OP_LEU_RR, 0 } // <= }; int instr, check; ExpEmit to(build, REGT_INT); @@ -3068,8 +3195,9 @@ ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build) Operator == '>' ? 1 : Operator == TK_Geq ? 2 : 3; - instr = InstrMap[index][op1.RegType == REGT_INT ? 0 : 1]; - check = InstrMap[index][2]; + int mode = op1.RegType == REGT_FLOAT ? 1 : CompareType == TypeUInt32 ? 2 : 0; + instr = InstrMap[index][mode]; + check = InstrMap[index][3]; if (op2.Konst) { instr += 1; @@ -3117,24 +3245,66 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, true)) - return nullptr; - + RESOLVE(left, ctx); + RESOLVE(right, ctx); if (!left || !right) { delete this; return nullptr; } - if (!IsNumeric() && !IsPointer() && !IsVector() && ValueType != TypeName) + if (left->ValueType != right->ValueType) // identical types are always comparable, if they can be placed in a register, so we can save most checks if this is the case. { - ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); - delete this; - return nullptr; + // Special cases: Compare strings and names with names, sounds, colors, state labels and class types. + // These are all types a string can be implicitly cast into, so for convenience, so they should when doing a comparison. + if ((left->ValueType == TypeString || left->ValueType == TypeName) && + (right->ValueType == TypeName || right->ValueType == TypeSound || right->ValueType == TypeColor || right->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) || right->ValueType == TypeStateLabel)) + { + left = new FxTypeCast(left, right->ValueType, false, true); + left = left->Resolve(ctx); + ABORT(left); + ValueType = right->ValueType; + } + else if ((right->ValueType == TypeString || right->ValueType == TypeName) && + (left->ValueType == TypeName || left->ValueType == TypeSound || left->ValueType == TypeColor || left->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) || left->ValueType == TypeStateLabel)) + { + right = new FxTypeCast(right, left->ValueType, false, true); + right = right->Resolve(ctx); + ABORT(right); + ValueType = left->ValueType; + } + else if (left->IsNumeric() && right->IsNumeric()) + { + Promote(ctx); + } + else if (left->ValueType->GetRegType() == REGT_POINTER && right->ValueType->GetRegType() == REGT_POINTER) + { + if (left->ValueType != right->ValueType && right->ValueType != TypeNullPtr && left->ValueType != TypeNullPtr && + !AreCompatiblePointerTypes(left->ValueType, right->ValueType, true)) + { + goto error; + } + } + else + { + goto error; + } + } + else if (left->ValueType->GetRegType() == REGT_NIL) + { + goto error; + } + else + { + ValueType = left->ValueType; + } + + if (Operator == TK_ApproxEq && ValueType->GetRegType() != REGT_FLOAT && ValueType->GetRegType() != REGT_STRING) + { + // Only floats, vectors and strings have handling for '~==', for all other types this is an error. + goto error; } - if (Operator == TK_ApproxEq && left->ValueType->GetRegType() != REGT_FLOAT && left->ValueType->GetRegType() != REGT_STRING) - Operator = TK_Eq; if (left->isConstant() && right->isConstant()) { int v; @@ -3236,9 +3406,13 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) } } } - Promote(ctx); ValueType = TypeBool; return this; + +error: + ScriptPosition.Message(MSG_ERROR, "Incompatible operands for %s comparison", Operator == TK_Eq ? "==" : Operator == TK_Neq ? "!=" : "~=="); + delete this; + return nullptr; } //========================================================================== @@ -3314,7 +3488,7 @@ ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build) // //========================================================================== -FxBinaryInt::FxBinaryInt(int o, FxExpression *l, FxExpression *r) +FxBitOp::FxBitOp(int o, FxExpression *l, FxExpression *r) : FxBinary(o, l, r) { ValueType = TypeSInt32; @@ -3326,48 +3500,39 @@ FxBinaryInt::FxBinaryInt(int o, FxExpression *l, FxExpression *r) // //========================================================================== -FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx) +FxExpression *FxBitOp::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, false)) - return nullptr; - if (IsFloat() && ctx.FromDecorate) + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) { - // For DECORATE which allows floats here. ZScript does not. - if (left->ValueType->GetRegType() != REGT_INT) - { - left = new FxIntCast(left, ctx.FromDecorate); - left = left->Resolve(ctx); - } - if (right->ValueType->GetRegType() != REGT_INT) - { - right = new FxIntCast(right, ctx.FromDecorate); - right = right->Resolve(ctx); - } - if (left == nullptr || right == nullptr) - { - delete this; - return nullptr; - } - ValueType = TypeSInt32; - } - - if (ValueType->GetRegType() != REGT_INT) - { - ScriptPosition.Message(MSG_ERROR, "Integer type expected"); delete this; return nullptr; } - else if (left->isConstant() && right->isConstant()) + + if (left->ValueType == TypeBool && right->ValueType == TypeBool) + { + ValueType = TypeBool; + } + else if (left->IsNumeric() && right->IsNumeric()) + { + if (!Promote(ctx, true)) return nullptr; + } + else + { + ScriptPosition.Message(MSG_ERROR, "Incompatible operands for bit operation"); + delete this; + return nullptr; + } + + if (left->isConstant() && right->isConstant()) { int v1 = static_cast(left)->GetValue().GetInt(); int v2 = static_cast(right)->GetValue().GetInt(); FxExpression *e = new FxConstant( - Operator == TK_LShift? v1 << v2 : - Operator == TK_RShift? v1 >> v2 : - Operator == TK_URShift? int((unsigned int)(v1) >> v2) : Operator == '&'? v1 & v2 : Operator == '|'? v1 | v2 : Operator == '^'? v1 ^ v2 : 0, ScriptPosition); @@ -3384,7 +3549,98 @@ FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx) // //========================================================================== -ExpEmit FxBinaryInt::Emit(VMFunctionBuilder *build) +ExpEmit FxBitOp::Emit(VMFunctionBuilder *build) +{ + assert(left->ValueType->GetRegType() == REGT_INT); + assert(right->ValueType->GetRegType() == REGT_INT); + int instr, rop; + ExpEmit op1, op2; + + op1 = left->Emit(build); + op2 = right->Emit(build); + if (op1.Konst) + { + swapvalues(op1, op2); + } + assert(!op1.Konst); + rop = op2.RegNum; + op2.Free(build); + op1.Free(build); + + instr = Operator == '&' ? OP_AND_RR : + Operator == '|' ? OP_OR_RR : + Operator == '^' ? OP_XOR_RR : -1; + + assert(instr > 0); + ExpEmit to(build, REGT_INT); + build->Emit(instr + op2.Konst, to.RegNum, op1.RegNum, rop); + return to; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxShift::FxShift(int o, FxExpression *l, FxExpression *r) + : FxBinary(o, l, r) +{ + ValueType = TypeSInt32; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxShift::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) + { + delete this; + return nullptr; + } + + if (left->IsNumeric() && right->IsNumeric()) + { + if (!Promote(ctx, true)) return nullptr; + if (ValueType == TypeUInt32 && Operator == TK_RShift) Operator = TK_URShift; + } + else + { + ScriptPosition.Message(MSG_ERROR, "Incompatible operands for shift operation"); + delete this; + return nullptr; + } + + if (left->isConstant() && right->isConstant()) + { + int v1 = static_cast(left)->GetValue().GetInt(); + int v2 = static_cast(right)->GetValue().GetInt(); + + FxExpression *e = new FxConstant( + Operator == TK_LShift ? v1 << v2 : + Operator == TK_RShift ? v1 >> v2 : + Operator == TK_URShift ? int((unsigned int)(v1) >> v2) : 0, ScriptPosition); + + delete this; + return e; + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpEmit FxShift::Emit(VMFunctionBuilder *build) { assert(left->ValueType->GetRegType() == REGT_INT); assert(right->ValueType->GetRegType() == REGT_INT); @@ -3393,58 +3649,34 @@ ExpEmit FxBinaryInt::Emit(VMFunctionBuilder *build) { OP_SLL_RR, OP_SLL_KR, OP_SLL_RI }, // TK_LShift { OP_SRA_RR, OP_SRA_KR, OP_SRA_RI }, // TK_RShift { OP_SRL_RR, OP_SRL_KR, OP_SRL_RI }, // TK_URShift - { OP_AND_RR, 0, OP_AND_RK }, // '&' - { OP_OR_RR, 0, OP_OR_RK }, // '|' - { OP_XOR_RR, 0, OP_XOR_RK }, // '^' }; int index, instr, rop; ExpEmit op1, op2; index = Operator == TK_LShift ? 0 : Operator == TK_RShift ? 1 : - Operator == TK_URShift ? 2 : - Operator == '&' ? 3 : - Operator == '|' ? 4 : - Operator == '^' ? 5 : -1; + Operator == TK_URShift ? 2 : -1; assert(index >= 0); op1 = left->Emit(build); - if (index < 3) - { // Shift instructions use right-hand immediates instead of constant registers. - if (right->isConstant()) - { - rop = static_cast(right)->GetValue().GetInt(); - op2.Konst = true; - } - else - { - op2 = right->Emit(build); - assert(!op2.Konst); - op2.Free(build); - rop = op2.RegNum; - } + + // Shift instructions use right-hand immediates instead of constant registers. + if (right->isConstant()) + { + rop = static_cast(right)->GetValue().GetInt(); + op2.Konst = true; } else - { // The other operators only take a constant on the right-hand side. + { op2 = right->Emit(build); - if (op1.Konst) - { - swapvalues(op1, op2); - } - assert(!op1.Konst); - rop = op2.RegNum; + assert(!op2.Konst); op2.Free(build); + rop = op2.RegNum; } + if (!op1.Konst) { op1.Free(build); - if (!op2.Konst) - { - instr = InstrMap[index][0]; - } - else - { - instr = InstrMap[index][2]; - } + instr = InstrMap[index][op2.Konst? 2:0]; } else { @@ -3478,30 +3710,27 @@ FxLtGtEq::FxLtGtEq(FxExpression *l, FxExpression *r) FxExpression *FxLtGtEq::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ResolveLR(ctx, true)) - return nullptr; - if (!left->IsNumeric() || !right->IsNumeric()) + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) + { + delete this; + return nullptr; + } + + if (left->IsNumeric() && right->IsNumeric()) + { + Promote(ctx); + } + else { ScriptPosition.Message(MSG_ERROR, "<>= expects two numeric operands"); delete this; return nullptr; } - if (left->ValueType->GetRegType() != right->ValueType->GetRegType()) - { - if (left->ValueType->GetRegType() == REGT_INT) - { - left = new FxFloatCast(left); - SAFE_RESOLVE(left, ctx); - } - if (right->ValueType->GetRegType() == REGT_INT) - { - right = new FxFloatCast(left); - SAFE_RESOLVE(left, ctx); - } - } - else if (left->isConstant() && right->isConstant()) + if (left->isConstant() && right->isConstant()) { // let's cut this short and always compare doubles. For integers the result will be exactly the same as with an integer comparison, either signed or unsigned. auto v1 = static_cast(left)->GetValue().GetFloat(); @@ -3556,6 +3785,141 @@ ExpEmit FxLtGtEq::Emit(VMFunctionBuilder *build) // //========================================================================== +FxConcat::FxConcat(FxExpression *l, FxExpression *r) + : FxBinary(TK_DotDot, l, r) +{ + ValueType = TypeString; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxConcat::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) + { + delete this; + return nullptr; + } + + // To concatenate two operands the only requirement is that they are integral types, i.e. can occupy a register + if (left->ValueType->GetRegType() == REGT_NIL || right->ValueType->GetRegType() == REGT_NIL) + { + ScriptPosition.Message(MSG_ERROR, "Invalid operand for string concatenation"); + delete this; + return nullptr; + } + + if (left->isConstant() && right->isConstant() && (left->ValueType == TypeString || left->ValueType == TypeName) && (right->ValueType == TypeString || right->ValueType == TypeName)) + { + // for now this is only types which have a constant string representation. + auto v1 = static_cast(left)->GetValue().GetString(); + auto v2 = static_cast(right)->GetValue().GetString(); + auto e = new FxConstant(v1 + v2, ScriptPosition); + delete this; + return e; + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpEmit FxConcat::Emit(VMFunctionBuilder *build) +{ + ExpEmit op1 = left->Emit(build); + ExpEmit op2 = right->Emit(build); + ExpEmit strng, strng2; + + if (op1.RegType == REGT_STRING && op1.Konst) + { + strng = ExpEmit(build, REGT_STRING); + build->Emit(OP_LKS, strng.RegNum, op1.RegNum); + } + else if (op1.RegType == REGT_STRING) + { + strng = op1; + } + else + { + int cast; + strng = ExpEmit(build, REGT_STRING); + if (op1.Konst) + { + ExpEmit nonconst(build, op1.RegType); + build->Emit(op1.RegType == REGT_INT ? OP_LK : op1.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op1.RegNum); + op1 = nonconst; + } + if (op1.RegType == REGT_FLOAT) cast = op1.RegCount == 1 ? CAST_F2S : op1.RegCount == 2 ? CAST_V22S : CAST_V32S; + else if (left->ValueType == TypeUInt32) cast = CAST_U2S; + else if (left->ValueType == TypeName) cast = CAST_N2S; + else if (left->ValueType == TypeSound) cast = CAST_So2S; + else if (left->ValueType == TypeColor) cast = CAST_Co2S; + else if (left->ValueType == TypeSpriteID) cast = CAST_SID2S; + else if (left->ValueType == TypeTextureID) cast = CAST_TID2S; + else if (op1.RegType == REGT_POINTER) cast = CAST_P2S; + else if (op1.RegType == REGT_INT) cast = CAST_I2S; + else assert(false && "Bad type for string concatenation"); + build->Emit(OP_CAST, strng.RegNum, op1.RegNum, cast); + op1.Free(build); + } + + if (op2.RegType == REGT_STRING && op2.Konst) + { + strng2 = ExpEmit(build, REGT_STRING); + build->Emit(OP_LKS, strng2.RegNum, op2.RegNum); + } + else if (op2.RegType == REGT_STRING) + { + strng2 = op2; + } + else + { + int cast; + strng2 = ExpEmit(build, REGT_STRING); + if (op2.Konst) + { + ExpEmit nonconst(build, op2.RegType); + build->Emit(op2.RegType == REGT_INT ? OP_LK : op2.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op2.RegNum); + op2 = nonconst; + } + if (op2.RegType == REGT_FLOAT) cast = op2.RegCount == 1 ? CAST_F2S : op2.RegCount == 2 ? CAST_V22S : CAST_V32S; + else if (right->ValueType == TypeUInt32) cast = CAST_U2S; + else if (right->ValueType == TypeName) cast = CAST_N2S; + else if (right->ValueType == TypeSound) cast = CAST_So2S; + else if (right->ValueType == TypeColor) cast = CAST_Co2S; + else if (right->ValueType == TypeSpriteID) cast = CAST_SID2S; + else if (right->ValueType == TypeTextureID) cast = CAST_TID2S; + else if (op2.RegType == REGT_POINTER) cast = CAST_P2S; + else if (op2.RegType == REGT_INT) cast = CAST_I2S; + else assert(false && "Bad type for string concatenation"); + build->Emit(OP_CAST, strng2.RegNum, op2.RegNum, cast); + op2.Free(build); + } + strng.Free(build); + strng2.Free(build); + ExpEmit dest(build, REGT_STRING); + assert(strng.RegType == strng2.RegType && strng.RegType == REGT_STRING); + build->Emit(OP_CONCAT, dest.RegNum, strng.RegNum, strng2.RegNum); + return dest; +} + +//========================================================================== +// +// +// +//========================================================================== + FxBinaryLogical::FxBinaryLogical(int o, FxExpression *l, FxExpression *r) : FxExpression(EFX_BinaryLogical, l->ScriptPosition) { @@ -3731,7 +4095,7 @@ ExpEmit FxBinaryLogical::Emit(VMFunctionBuilder *build) build->Emit(OP_JMP, 1); auto ctarget = build->Emit(OP_LI, to.RegNum, (Operator == TK_AndAnd) ? 0 : 1); for (auto addr : patchspots) build->Backpatch(addr, ctarget); - list.Clear(); + list.DeleteAndClear(); list.ShrinkToFit(); return to; } @@ -3864,7 +4228,7 @@ PPrototype *FxTypeCheck::ReturnProto() // //========================================================================== -int BuiltinTypeCheck(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinTypeCheck(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam == 2); PARAM_POINTER_AT(0, obj, DObject); @@ -3880,7 +4244,6 @@ int BuiltinTypeCheck(VMFrameStack *stack, VMValue *param, TArray &defau ExpEmit FxTypeCheck::Emit(VMFunctionBuilder *build) { - ExpEmit out(build, REGT_INT); EmitParameter(build, left, ScriptPosition); EmitParameter(build, right, ScriptPosition); @@ -3901,6 +4264,7 @@ ExpEmit FxTypeCheck::Emit(VMFunctionBuilder *build) return call; } + ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); return out; } @@ -3939,7 +4303,18 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); SAFE_RESOLVE(expr, ctx); + if (expr->ExprType == EFX_GetDefaultByType) + { + int a = 0; + } bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(expr->ValueType)->IsConst; + if (constflag) + { + // readonly pointers are normally only used for class defaults which lack type information to be cast properly, so we have to error out here. + ScriptPosition.Message(MSG_ERROR, "Cannot cast a readonly pointer"); + delete this; + return nullptr; + } expr = new FxTypeCast(expr, NewPointer(RUNTIME_CLASS(DObject), constflag), true, true); expr = expr->Resolve(ctx); if (expr == nullptr) @@ -3959,11 +4334,13 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx) ExpEmit FxDynamicCast::Emit(VMFunctionBuilder *build) { - ExpEmit out = expr->Emit(build); + ExpEmit in = expr->Emit(build); + ExpEmit out = in.Fixed ? ExpEmit(build, in.RegType) : in; ExpEmit check(build, REGT_INT); assert(out.RegType == REGT_POINTER); - build->Emit(OP_PARAM, 0, REGT_POINTER, out.RegNum); + if (in.Fixed) build->Emit(OP_MOVEA, out.RegNum, in.RegNum); + build->Emit(OP_PARAM, 0, REGT_POINTER, in.RegNum); build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CastType, ATAG_OBJECT)); PSymbol *sym = FindBuiltinFunction(NAME_BuiltinTypeCheck, BuiltinTypeCheck); @@ -4255,19 +4632,31 @@ FxExpression *FxAbs::Resolve(FCompileContext &ctx) ExpEmit FxAbs::Emit(VMFunctionBuilder *build) { - ExpEmit absofsteal = val->Emit(build); - assert(!absofsteal.Konst); - ExpEmit out(build, absofsteal.RegType); - if (absofsteal.RegType == REGT_INT) + assert(ValueType == val->ValueType); + ExpEmit from = val->Emit(build); + ExpEmit to; + assert(from.Konst == 0); + assert(ValueType->GetRegCount() == 1); + // Do it in-place, unless a local variable + if (from.Fixed) { - build->Emit(OP_ABS, out.RegNum, absofsteal.RegNum, 0); + to = ExpEmit(build, from.RegType); + from.Free(build); } else { - assert(absofsteal.RegType == REGT_FLOAT); - build->Emit(OP_FLOP, out.RegNum, absofsteal.RegNum, FLOP_ABS); + to = from; } - return out; + + if (ValueType->GetRegType() == REGT_INT) + { + build->Emit(OP_ABS, to.RegNum, from.RegNum, 0); + } + else + { + build->Emit(OP_FLOP, to.RegNum, from.RegNum, FLOP_ABS); + } + return to; } //========================================================================== @@ -4641,7 +5030,7 @@ FxExpression *FxRandom::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinRandom(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinRandom(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam >= 1 && numparam <= 3); FRandom *rng = reinterpret_cast(param[0].a); @@ -4867,6 +5256,8 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) // The result register needs to be in-use when we return. // It should have been freed earlier, so restore its in-use flag. resultreg.Reuse(build); + choices.DeleteAndClear(); + choices.ShrinkToFit(); return resultreg; } @@ -4893,7 +5284,7 @@ FxFRandom::FxFRandom(FRandom *r, FxExpression *mi, FxExpression *ma, const FScri // //========================================================================== -int BuiltinFRandom(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinFRandom(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam == 1 || numparam == 3); FRandom *rng = reinterpret_cast(param[0].a); @@ -5068,9 +5459,24 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) FxLocalVariableDeclaration *local = ctx.FindLocalVariable(Identifier); if (local != nullptr) { - auto x = new FxLocalVariable(local, ScriptPosition); - delete this; - return x->Resolve(ctx); + if (local->ExprType == EFX_StaticArray) + { + auto x = new FxStaticArrayVariable(local, ScriptPosition); + delete this; + return x->Resolve(ctx); + } + else if (local->ValueType->GetRegType() != REGT_NIL) + { + auto x = new FxLocalVariable(local, ScriptPosition); + delete this; + return x->Resolve(ctx); + } + else + { + auto x = new FxStackVariable(local->ValueType, local->StackOffset, ScriptPosition); + delete this; + return x->Resolve(ctx); + } } if (Identifier == NAME_Default) @@ -5081,7 +5487,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) delete this; return nullptr; } - if (!ctx.Function->Variants[0].SelfClass->IsDescendantOf(RUNTIME_CLASS(AActor))) + if (!ctx.Function->Variants[0].SelfClass->IsKindOf(RUNTIME_CLASS(PClassActor))) { ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); delete this; @@ -5106,6 +5512,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) self = self->Resolve(ctx); newex = ResolveMember(ctx, ctx.Function->Variants[0].SelfClass, self, ctx.Function->Variants[0].SelfClass); ABORT(newex); + goto foundit; } } @@ -5116,8 +5523,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) { ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as class constant\n", Identifier.GetChars()); newex = FxConstant::MakeConstant(sym, ScriptPosition); - delete this; - return newex->Resolve(ctx); + goto foundit; } // Do this check for ZScript as well, so that a clearer error message can be printed. MSG_OPTERROR will default to MSG_ERROR there. else if (ctx.Function->Variants[0].SelfClass != ctx.Class && sym->IsKindOf(RUNTIME_CLASS(PField))) @@ -5128,6 +5534,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) ABORT(newex); ScriptPosition.Message(MSG_OPTERROR, "Self pointer used in ambiguous context; VM execution may abort!"); ctx.Unsafe = true; + goto foundit; } else { @@ -5144,6 +5551,13 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) } } + if (noglobal) + { + // This is needed to properly resolve class names on the left side of the member access operator + ValueType = TypeError; + return this; + } + // now check the global identifiers. if (newex == nullptr && (sym = ctx.FindGlobal(Identifier)) != nullptr) { @@ -5151,16 +5565,20 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) { ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as global constant\n", Identifier.GetChars()); newex = FxConstant::MakeConstant(sym, ScriptPosition); + goto foundit; } else if (sym->IsKindOf(RUNTIME_CLASS(PField))) { // internally defined global variable ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as global variable\n", Identifier.GetChars()); newex = new FxGlobalVariable(static_cast(sym), ScriptPosition); + goto foundit; } else { ScriptPosition.Message(MSG_ERROR, "Invalid global identifier '%s'\n", Identifier.GetChars()); + delete this; + return nullptr; } } @@ -5169,14 +5587,27 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) { ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as line special %d\n", Identifier.GetChars(), num); newex = new FxConstant(num, ScriptPosition); + goto foundit; + } + + auto cvar = FindCVar(Identifier.GetChars(), nullptr); + if (cvar != nullptr) + { + if (cvar->GetFlags() & CVAR_USERINFO) + { + ScriptPosition.Message(MSG_ERROR, "Cannot access userinfo CVARs directly. Use GetCVar() instead."); + delete this; + return nullptr; + } + newex = new FxCVar(cvar, ScriptPosition); + goto foundit; } - if (newex == nullptr) - { - ScriptPosition.Message(MSG_ERROR, "Unknown identifier '%s'", Identifier.GetChars()); - delete this; - return nullptr; - } + ScriptPosition.Message(MSG_ERROR, "Unknown identifier '%s'", Identifier.GetChars()); + delete this; + return nullptr; + +foundit: delete this; return newex? newex->Resolve(ctx) : nullptr; } @@ -5187,11 +5618,27 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) // //========================================================================== -FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PClass *classctx, FxExpression *&object, PStruct *objtype) +FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classctx, FxExpression *&object, PStruct *objtype) { PSymbol *sym; PSymbolTable *symtbl; bool isclass = objtype->IsKindOf(RUNTIME_CLASS(PClass)); + + if (Identifier == NAME_Default) + { + if (!objtype->IsKindOf(RUNTIME_CLASS(PClassActor))) + { + ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); + delete this; + return nullptr; + } + + FxExpression * x = new FxClassDefaults(object, ScriptPosition); + object = nullptr; + delete this; + return x->Resolve(ctx); + } + if ((sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr) { if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) @@ -5220,12 +5667,6 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PClass *classctx return nullptr; } - if (vsym->Flags & VARF_Static) - { - // todo. For now these cannot be defined so let's just exit. - ScriptPosition.Message(MSG_ERROR, "Static members not implemented yet."); - return nullptr; - } auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition) : new FxStructMember(object, vsym, ScriptPosition); object = nullptr; return x->Resolve(ctx); @@ -5280,11 +5721,58 @@ FxMemberIdentifier::~FxMemberIdentifier() FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) { + PStruct *ccls = nullptr; CHECKRESOLVED(); + if (Object->ExprType == EFX_Identifier) + { + // If the left side is a class name for a static member function call it needs to be resolved manually + // because the resulting value type would cause problems in nearly every other place where identifiers are being used. + ccls = FindStructType(static_cast(Object)->Identifier); + if (ccls != nullptr) static_cast(Object)->noglobal = true; + } + SAFE_RESOLVE(Object, ctx); - if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) + if (Identifier == FName("allmap")) + { + int a = 2; + } + + // check for class or struct constants if the left side is a type name. + if (Object->ValueType == TypeError) + { + if (ccls != nullptr) + { + if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) + { + PSymbol *sym; + if ((sym = ccls->Symbols.FindSymbol(Identifier, true)) != nullptr) + { + if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) + { + ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s.%s' as constant\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + delete this; + return FxConstant::MakeConstant(sym, ScriptPosition); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Unable to access '%s.%s' in a static context\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + delete this; + return nullptr; + } + } + } + } + } + + // allow accessing the color channels by mapping the type to a matching struct which defines them. + if (Object->ValueType == TypeColor) + { + Object->ValueType = TypeColorStruct; + } + + else if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { auto ptype = static_cast(Object->ValueType)->PointedType; if (ptype->IsKindOf(RUNTIME_CLASS(PStruct))) @@ -5294,7 +5782,7 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) return ret; } } - else if (Object->ValueType->IsA(RUNTIME_CLASS(PStruct))) + else if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PStruct))) { auto ret = ResolveMember(ctx, ctx.Class, Object, static_cast(Object->ValueType)); delete this; @@ -5336,13 +5824,65 @@ bool FxLocalVariable::RequestAddress(FCompileContext &ctx, bool *writable) ExpEmit FxLocalVariable::Emit(VMFunctionBuilder *build) { - ExpEmit ret(Variable->RegNum + RegOffset, Variable->ValueType->GetRegType(), false, true); - ret.RegCount = ValueType->GetRegCount(); - if (AddressRequested) ret.Target = true; - return ret; + // 'Out' variables are actually pointers but this fact must be hidden to the script. + if (Variable->VarFlags & VARF_Out) + { + if (!AddressRequested) + { + ExpEmit reg(build, ValueType->GetRegType(), ValueType->GetRegCount()); + build->Emit(ValueType->GetLoadOp(), reg.RegNum, Variable->RegNum, build->GetConstantInt(RegOffset)); + return reg; + } + else + { + if (RegOffset == 0) return ExpEmit(Variable->RegNum, REGT_POINTER, false, true); + ExpEmit reg(build, REGT_POINTER); + build->Emit(OP_ADDA_RK, reg.RegNum, Variable->RegNum, build->GetConstantInt(RegOffset)); + return reg; + } + } + else + { + ExpEmit ret(Variable->RegNum + RegOffset, Variable->ValueType->GetRegType(), false, true); + ret.RegCount = ValueType->GetRegCount(); + if (AddressRequested) ret.Target = true; + return ret; + } } +//========================================================================== +// +// +// +//========================================================================== + +FxStaticArrayVariable::FxStaticArrayVariable(FxLocalVariableDeclaration *var, const FScriptPosition &sc) + : FxExpression(EFX_StaticArrayVariable, sc) +{ + Variable = static_cast(var); + ValueType = Variable->ValueType; +} + +FxExpression *FxStaticArrayVariable::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + return this; +} + +bool FxStaticArrayVariable::RequestAddress(FCompileContext &ctx, bool *writable) +{ + AddressRequested = true; + if (writable != nullptr) *writable = false; + return true; +} + +ExpEmit FxStaticArrayVariable::Emit(VMFunctionBuilder *build) +{ + // returns the first const register for this array + return ExpEmit(Variable->StackOffset, Variable->ElementType->GetRegType(), true, false); +} + //========================================================================== // @@ -5465,40 +6005,14 @@ FxExpression *FxClassDefaults::Resolve(FCompileContext& ctx) // //========================================================================== -int BuiltinGetDefault(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) -{ - assert(numparam == 1); - PARAM_POINTER_AT(0, obj, DObject); - ACTION_RETURN_OBJECT(obj->GetClass()->Defaults); -} - -//========================================================================== -// -// -// -//========================================================================== - ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build) { - EmitParameter(build, obj, ScriptPosition); - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinGetDefault, BuiltinGetDefault); - - assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); - assert(((PSymbolVMFunction *)sym)->Function != nullptr); - auto callfunc = ((PSymbolVMFunction *)sym)->Function; - int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); - build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); - - if (EmitTail) - { - ExpEmit call; - call.Final = true; - return call; - } - - ExpEmit out(build, REGT_POINTER); - build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum); - return out; + ExpEmit ob = obj->Emit(build); + ob.Free(build); + ExpEmit meta(build, REGT_POINTER); + build->Emit(OP_META, meta.RegNum, ob.RegNum); + build->Emit(OP_LO, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + return meta; } @@ -5568,6 +6082,223 @@ ExpEmit FxGlobalVariable::Emit(VMFunctionBuilder *build) } +//========================================================================== +// +// +// +//========================================================================== + +FxCVar::FxCVar(FBaseCVar *cvar, const FScriptPosition &pos) + : FxExpression(EFX_CVar, pos) +{ + CVar = cvar; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxCVar::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + switch (CVar->GetRealType()) + { + case CVAR_Bool: + case CVAR_DummyBool: + ValueType = TypeBool; + break; + + case CVAR_Int: + case CVAR_DummyInt: + ValueType = TypeSInt32; + break; + + case CVAR_Color: + ValueType = TypeColor; + break; + + case CVAR_Float: + ValueType = TypeFloat64; + break; + + case CVAR_String: + ValueType = TypeString; + break; + + default: + ScriptPosition.Message(MSG_ERROR, "Unknown CVar type for %s", CVar->GetName()); + delete this; + return nullptr; + } + return this; +} + +ExpEmit FxCVar::Emit(VMFunctionBuilder *build) +{ + ExpEmit dest(build, ValueType->GetRegType()); + ExpEmit addr(build, REGT_POINTER); + int nul = build->GetConstantInt(0); + switch (CVar->GetRealType()) + { + case CVAR_Int: + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->Value, ATAG_GENERIC)); + build->Emit(OP_LW, dest.RegNum, addr.RegNum, nul); + break; + + case CVAR_Color: + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->Value, ATAG_GENERIC)); + build->Emit(OP_LW, dest.RegNum, addr.RegNum, nul); + break; + + case CVAR_Float: + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->Value, ATAG_GENERIC)); + build->Emit(OP_LSP, dest.RegNum, addr.RegNum, nul); + break; + + case CVAR_Bool: + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->Value, ATAG_GENERIC)); + build->Emit(OP_LBU, dest.RegNum, addr.RegNum, nul); + break; + + case CVAR_String: + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->Value, ATAG_GENERIC)); + build->Emit(OP_LS, dest.RegNum, addr.RegNum, nul); + break; + + case CVAR_DummyBool: + { + auto cv = static_cast(CVar); + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&cv->ValueVar.Value, ATAG_GENERIC)); + build->Emit(OP_LW, dest.RegNum, addr.RegNum, nul); + build->Emit(OP_SRL_RI, dest.RegNum, dest.RegNum, cv->BitNum); + build->Emit(OP_AND_RK, dest.RegNum, dest.RegNum, build->GetConstantInt(1)); + break; + } + + case CVAR_DummyInt: + { + auto cv = static_cast(CVar); + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&cv->ValueVar.Value, ATAG_GENERIC)); + build->Emit(OP_LW, dest.RegNum, addr.RegNum, nul); + build->Emit(OP_AND_RK, dest.RegNum, dest.RegNum, build->GetConstantInt(cv->BitVal)); + build->Emit(OP_SRL_RI, dest.RegNum, dest.RegNum, cv->BitNum); + break; + } + + default: + assert(false && "Unsupported CVar type"); + break; + } + addr.Free(build); + return dest; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxStackVariable::FxStackVariable(PType *type, int offset, const FScriptPosition &pos) + : FxExpression(EFX_StackVariable, pos) +{ + membervar = new PField(NAME_None, type, 0, offset); + AddressRequested = false; + AddressWritable = true; // must be true unless classx tells us otherwise if requested. +} + +//========================================================================== +// +// force delete the PField because we know we won't need it anymore +// and it won't get GC'd until the compiler finishes. +// +//========================================================================== + +FxStackVariable::~FxStackVariable() +{ + // Q: Is this good or bad? Needs testing if this is fine or better left to the GC anyway. DObject's destructor is anything but cheap. + membervar->ObjectFlags |= OF_YesReallyDelete; + delete membervar; +} + +//========================================================================== +// +// +//========================================================================== + +void FxStackVariable::ReplaceField(PField *newfield) +{ + membervar->ObjectFlags |= OF_YesReallyDelete; + delete membervar; + membervar = newfield; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FxStackVariable::RequestAddress(FCompileContext &ctx, bool *writable) +{ + AddressRequested = true; + if (writable != nullptr) *writable = AddressWritable && !ctx.CheckReadOnly(membervar->Flags); + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxStackVariable::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + ValueType = membervar->Type; + return this; +} + +ExpEmit FxStackVariable::Emit(VMFunctionBuilder *build) +{ + int offsetreg = -1; + + if (membervar->Offset != 0) offsetreg = build->GetConstantInt((int)membervar->Offset); + + if (AddressRequested) + { + if (offsetreg >= 0) + { + ExpEmit obj(build, REGT_POINTER); + build->Emit(OP_ADDA_RK, obj.RegNum, build->FramePointer.RegNum, offsetreg); + return obj; + } + else + { + return build->FramePointer; + } + } + ExpEmit loc(build, membervar->Type->GetRegType(), membervar->Type->GetRegCount()); + + if (membervar->BitValue == -1) + { + if (offsetreg == -1) offsetreg = build->GetConstantInt(0); + build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, build->FramePointer.RegNum, offsetreg); + } + else + { + ExpEmit obj(build, REGT_POINTER); + if (offsetreg >= 0) build->Emit(OP_ADDA_RK, obj.RegNum, build->FramePointer.RegNum, offsetreg); + obj.Free(build); + build->Emit(OP_LBIT, loc.RegNum, obj.RegNum, 1 << membervar->BitValue); + } + return loc; +} + + //========================================================================== // // @@ -5602,6 +6333,11 @@ FxStructMember::~FxStructMember() bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) { + // Cannot take the address of metadata variables. + if (membervar->Flags & VARF_Static) + { + return false; + } AddressRequested = true; if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckReadOnly(membervar->Flags) && (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast(classx->ValueType)->IsConst)); @@ -5644,14 +6380,15 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) return nullptr; } } - else if (classx->ValueType->IsA(RUNTIME_CLASS(PStruct))) // Classes can never be used as value types so we do not have to consider that case. + else if (classx->ValueType->IsKindOf(RUNTIME_CLASS(PStruct))) { // if this is a struct within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset. if (classx->ExprType == EFX_ClassMember || classx->ExprType == EFX_StructMember) { auto parentfield = static_cast(classx)->membervar; // PFields are garbage collected so this will be automatically taken care of later. - auto newfield = new PField(membervar->SymbolName, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset, membervar->BitValue); + auto newfield = new PField(membervar->SymbolName, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset); + newfield->BitValue = membervar->BitValue; static_cast(classx)->membervar = newfield; classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. auto x = classx->Resolve(ctx); @@ -5661,13 +6398,25 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) else if (classx->ExprType == EFX_GlobalVariable) { auto parentfield = static_cast(classx)->membervar; - auto newfield = new PField(membervar->SymbolName, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset, membervar->BitValue); + auto newfield = new PField(membervar->SymbolName, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset); + newfield->BitValue = membervar->BitValue; static_cast(classx)->membervar = newfield; classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. auto x = classx->Resolve(ctx); classx = nullptr; return x; } + else if (classx->ExprType == EFX_StackVariable) + { + auto parentfield = static_cast(classx)->membervar; + auto newfield = new PField(membervar->SymbolName, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset); + newfield->BitValue = membervar->BitValue; + static_cast(classx)->ReplaceField(newfield); + classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. + auto x = classx->Resolve(ctx); + classx = nullptr; + return x; + } else if (classx->ExprType == EFX_LocalVariable && classx->IsVector()) // vectors are a special case because they are held in registers { // since this is a vector, all potential things that may get here are single float or an xy-vector. @@ -5678,6 +6427,26 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) delete this; return locvar; } + else if (classx->ExprType == EFX_LocalVariable && classx->ValueType == TypeColorStruct) + { + // This needs special treatment because it'd require accessing the register via address. + // Fortunately this is the only place where this kind of access is ever needed so an explicit handling is acceptable. + int bits; + switch (membervar->SymbolName.GetIndex()) + { + case NAME_a: bits = 24; break; + case NAME_r: bits = 16; break; + case NAME_g: bits = 8; break; + case NAME_b: default: bits = 0; break; + } + classx->ValueType = TypeColor; // need to set it back. + FxExpression *x = classx; + if (bits > 0) x = new FxShift(TK_URShift, x, new FxConstant(bits, ScriptPosition)); + x = new FxBitOp('&', x, new FxConstant(255, ScriptPosition)); + classx = nullptr; + delete this; + return x->Resolve(ctx); + } else { if (!(classx->RequestAddress(ctx, &AddressWritable))) @@ -5708,6 +6477,14 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build) obj = newobj; } + if (membervar->Flags & VARF_Static) + { + obj.Free(build); + ExpEmit meta(build, REGT_POINTER); + build->Emit(OP_META, meta.RegNum, obj.RegNum); + obj = meta; + } + if (AddressRequested) { if (membervar->Offset == 0) @@ -5815,7 +6592,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) return nullptr; } } - if (index->ValueType->GetRegType() != REGT_INT && index->ValueType != TypeName) + if (!index->IsInteger()) { ScriptPosition.Message(MSG_ERROR, "Array index must be integer"); delete this; @@ -5825,9 +6602,16 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) PArray *arraytype = dyn_cast(Array->ValueType); if (arraytype == nullptr) { - ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays."); - delete this; - return nullptr; + // Check if we got a pointer to an array. Some native data structures (like the line list in sectors) use this. + PPointer *ptype = dyn_cast(Array->ValueType); + if (ptype == nullptr || !ptype->PointedType->IsKindOf(RUNTIME_CLASS(PArray))) + { + ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays."); + delete this; + return nullptr; + } + arraytype = static_cast(ptype->PointedType); + arrayispointer = true; } if (index->isConstant()) { @@ -5838,16 +6622,45 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) delete this; return nullptr; } + + if (!arrayispointer) + { + // if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset. + if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember) + { + auto parentfield = static_cast(Array)->membervar; + // PFields are garbage collected so this will be automatically taken care of later. + auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset); + static_cast(Array)->membervar = newfield; + Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. + auto x = Array->Resolve(ctx); + Array = nullptr; + return x; + } + else if (Array->ExprType == EFX_GlobalVariable) + { + auto parentfield = static_cast(Array)->membervar; + auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset); + static_cast(Array)->membervar = newfield; + Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. + auto x = Array->Resolve(ctx); + Array = nullptr; + return x; + } + else if (Array->ExprType == EFX_StackVariable) + { + auto parentfield = static_cast(Array)->membervar; + auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset); + static_cast(Array)->ReplaceField(newfield); + Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. + auto x = Array->Resolve(ctx); + Array = nullptr; + return x; + } + } } ValueType = arraytype->ElementType; - if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT) - { - // int arrays only for now - ScriptPosition.Message(MSG_ERROR, "Only numeric arrays are supported."); - delete this; - return nullptr; - } if (!Array->RequestAddress(ctx, &AddressWritable)) { ScriptPosition.Message(MSG_ERROR, "Unable to dereference array."); @@ -5865,10 +6678,19 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { + PArray *arraytype; + + if (arrayispointer) + { + arraytype = static_cast(static_cast(Array->ValueType)->PointedType); + } + else + { + arraytype = static_cast(Array->ValueType); + } ExpEmit start = Array->Emit(build); - PArray *const arraytype = static_cast(Array->ValueType); - ExpEmit dest(build, arraytype->ElementType->GetRegType()); + /* what was this for? if (start.Konst) { ExpEmit tmpstart(build, REGT_POINTER); @@ -5876,59 +6698,172 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) start.Free(build); start = tmpstart; } + */ if (index->isConstant()) { unsigned indexval = static_cast(index)->GetValue().GetInt(); assert(indexval < arraytype->ElementCount && "Array index out of bounds"); - indexval *= arraytype->ElementSize; if (AddressRequested) { if (indexval != 0) { - build->Emit(OP_ADDA_RK, start.RegNum, start.RegNum, build->GetConstantInt(indexval)); + indexval *= arraytype->ElementSize; + if (!start.Fixed) + { + build->Emit(OP_ADDA_RK, start.RegNum, start.RegNum, build->GetConstantInt(indexval)); + } + else + { + // do not clobber local variables. + ExpEmit temp(build, start.RegType); + build->Emit(OP_ADDA_RK, temp.RegNum, start.RegNum, build->GetConstantInt(indexval)); + start.Free(build); + start = temp; + } } + return start; + } + else if (!start.Konst) + { + start.Free(build); + ExpEmit dest(build, ValueType->GetRegType()); + build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, start.RegNum, build->GetConstantInt(indexval* arraytype->ElementSize)); + return dest; } else { - build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, - start.RegNum, build->GetConstantInt(indexval)); + static int LK_Ops[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP }; + assert(start.RegType == ValueType->GetRegType()); + ExpEmit dest(build, start.RegType); + build->Emit(LK_Ops[start.RegType], dest.RegNum, start.RegNum + indexval); + return dest; } } else { ExpEmit indexv(index->Emit(build)); - int shiftbits = 0; - while (1u << shiftbits < arraytype->ElementSize) - { - shiftbits++; - } - assert(1u << shiftbits == arraytype->ElementSize && "Element sizes other than power of 2 are not implemented"); - build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount); - if (shiftbits > 0) + // Todo: For dynamically allocated arrays (like global sector and linedef tables) we need to get the bound value in here somehow. + // Right now their bounds are not properly checked for. + if (arraytype->ElementCount > 65535) { - build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits); - } - - if (AddressRequested) - { - build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexv.RegNum); + build->Emit(OP_BOUND_K, indexv.RegNum, build->GetConstantInt(arraytype->ElementCount)); } else { - build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that - dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register + build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount); + } + + if (!start.Konst) + { + int shiftbits = 0; + while (1u << shiftbits < arraytype->ElementSize) + { + shiftbits++; + } + ExpEmit indexwork = indexv.Fixed && arraytype->ElementSize > 1 ? ExpEmit(build, indexv.RegType) : indexv; + if (1u << shiftbits == arraytype->ElementSize) + { + if (shiftbits > 0) + { + build->Emit(OP_SLL_RI, indexwork.RegNum, indexv.RegNum, shiftbits); + } + } + else + { + // A shift won't do, so use a multiplication + build->Emit(OP_MUL_RK, indexwork.RegNum, indexv.RegNum, build->GetConstantInt(arraytype->ElementSize)); + } + indexwork.Free(build); + + if (AddressRequested) + { + if (!start.Fixed) + { + build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexwork.RegNum); + } + else + { + start.Free(build); + // do not clobber local variables. + ExpEmit temp(build, start.RegType); + build->Emit(OP_ADDA_RR, temp.RegNum, start.RegNum, indexwork.RegNum); + start = temp; + } + return start; + } + else + { + start.Free(build); + ExpEmit dest(build, ValueType->GetRegType()); + // added 1 to use the *_R version that takes the offset from a register + build->Emit(arraytype->ElementType->GetLoadOp() + 1, dest.RegNum, start.RegNum, indexwork.RegNum); + return dest; + } + } + else + { + static int LKR_Ops[] = { OP_LK_R, OP_LKF_R, OP_LKS_R, OP_LKP_R }; + assert(start.RegType == ValueType->GetRegType()); + ExpEmit dest(build, start.RegType); + if (start.RegNum <= 255) + { + // Since large constant tables are the exception, the constant component in C is an immediate value here. + build->Emit(LKR_Ops[start.RegType], dest.RegNum, indexv.RegNum, start.RegNum); + } + else + { + build->Emit(OP_ADD_RK, indexv.RegNum, indexv.RegNum, build->GetConstantInt(start.RegNum)); + build->Emit(LKR_Ops[start.RegType], dest.RegNum, indexv.RegNum, 0); + } + indexv.Free(build); + return dest; } - indexv.Free(build); } - if (AddressRequested) +} + +//========================================================================== +// +// Checks if a function may be called from the current context. +// +//========================================================================== + +static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction *caller, PFunction *callee) +{ + if (callee->Variants[0].Flags & VARF_Method) { - dest.Free(build); - return start; + // The called function must support all usage modes of the current function. It may support more, but must not support less. + if ((callee->Variants[0].UseFlags & caller->Variants[0].UseFlags) != caller->Variants[0].UseFlags) + { + ScriptPosition.Message(MSG_ERROR, "Function %s incompatible with current context\n", callee->SymbolName.GetChars()); + return false; + } + + if (!(caller->Variants[0].Flags & VARF_Method)) + { + ScriptPosition.Message(MSG_ERROR, "Call to non-static function %s from a static context", callee->SymbolName.GetChars()); + return false; + } + else + { + auto callingself = caller->Variants[0].SelfClass; + auto calledself = callee->Variants[0].SelfClass; + bool match = (callingself == calledself); + if (!match) + { + auto callingselfcls = dyn_cast(caller->Variants[0].SelfClass); + auto calledselfcls = dyn_cast(callee->Variants[0].SelfClass); + match = callingselfcls != nullptr && calledselfcls != nullptr && callingselfcls->IsDescendantOf(calledselfcls); + } + + if (!match) + { + ScriptPosition.Message(MSG_ERROR, "Call to member function %s with incompatible self pointer.", callee->SymbolName.GetChars()); + return false; + } + } } - - start.Free(build); - return dest; + return true; } //========================================================================== @@ -6006,41 +6941,27 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) ABORT(ctx.Class); bool error = false; - PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error); - - // Action functions in state providers need special treatment because self is of type Actor here. - if (afd != nullptr && ctx.Class->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) && (ctx.Function->Variants[0].Flags & VARF_Action)) + for (auto a : ArgList) { - // Only accept static and action functions from the current class. Calling a member function will require explicit use of 'invoker'. - if ((afd->Variants[0].Flags & (VARF_Method|VARF_Action)) == VARF_Method) + if (a == nullptr) { - // Everything else that may be used here must pass the selfclass check, i.e. it must be reachable from Actor. - // Note that FuncClass is still the current item because for symbol privacy checks this is relevant. - afd = FindClassMemberFunction(ctx.Function->Variants[0].SelfClass, ctx.Class, MethodName, ScriptPosition, &error); - if (afd == nullptr) - { - ScriptPosition.Message(MSG_ERROR, "Unable to call non-action function %s from here. Please use 'invoker.%s' to call it.", MethodName.GetChars(), MethodName.GetChars()); - delete this; - return nullptr; - } - } - } - - if (error) - { - delete this; - return nullptr; - } - - if (afd != nullptr) - { - if (ctx.Function->Variants[0].Flags & VARF_Static && !(afd->Variants[0].Flags & VARF_Static)) - { - ScriptPosition.Message(MSG_ERROR, "Call to non-static function %s from a static context", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Empty function argument."); delete this; return nullptr; } - auto self = !(afd->Variants[0].Flags & VARF_Static)? new FxSelf(ScriptPosition) : nullptr; + } + + PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error); + + if (afd != nullptr) + { + if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd)) + { + delete this; + return nullptr; + } + + auto self = (afd->Variants[0].Flags & VARF_Method)? new FxSelf(ScriptPosition) : nullptr; auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, false); delete this; return x->Resolve(ctx); @@ -6114,15 +7035,23 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) switch (MethodName) { + case NAME_Color: + if (ArgList.Size() == 3 || ArgList.Size() == 4) + { + func = new FxColorLiteral(ArgList, ScriptPosition); + break; + } + // fall through case NAME_Bool: case NAME_Int: case NAME_uInt: case NAME_Float: case NAME_Double: case NAME_Name: - case NAME_Color: case NAME_Sound: case NAME_State: + case NAME_SpriteID: + case NAME_TextureID: if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition)) { PType *type = @@ -6132,6 +7061,8 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) MethodName == NAME_Float ? TypeFloat64 : MethodName == NAME_Double ? TypeFloat64 : MethodName == NAME_Name ? TypeName : + MethodName == NAME_SpriteID ? TypeSpriteID : + MethodName == NAME_TextureID ? TypeTextureID : MethodName == NAME_State ? TypeState : MethodName == NAME_Color ? TypeColor : (PType*)TypeSound; @@ -6140,6 +7071,21 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) } break; + case NAME_GetClass: + if (CheckArgSize(NAME_GetClass, ArgList, 0, 0, ScriptPosition)) + { + func = new FxGetClass(new FxSelf(ScriptPosition)); + } + break; + + case NAME_GetDefaultByType: + if (CheckArgSize(NAME_GetDefaultByType, ArgList, 1, 1, ScriptPosition)) + { + func = new FxGetDefaultByType(ArgList[0]); + ArgList[0] = nullptr; + } + break; + case NAME_Random: // allow calling Random without arguments to default to (0, 255) if (ArgList.Size() == 0) @@ -6264,30 +7210,106 @@ FxMemberFunctionCall::~FxMemberFunctionCall() FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { ABORT(ctx.Class); - PClass *cls; + PStruct *cls; bool staticonly = false; bool novirtual = false; + PStruct *ccls = nullptr; + + for (auto a : ArgList) + { + if (a == nullptr) + { + ScriptPosition.Message(MSG_ERROR, "Empty function argument."); + delete this; + return nullptr; + } + } + if (Self->ExprType == EFX_Identifier) { // If the left side is a class name for a static member function call it needs to be resolved manually // because the resulting value type would cause problems in nearly every other place where identifiers are being used. - cls = PClass::FindClass(static_cast(Self)->Identifier); - if (cls != nullptr && cls->bExported) + ccls = FindStructType(static_cast(Self)->Identifier); + if (ccls != nullptr) static_cast(Self)->noglobal = true; + } + + SAFE_RESOLVE(Self, ctx); + + if (Self->ValueType == TypeError) + { + if (ccls != nullptr) { - staticonly = true; - goto isresolved; + if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) + { + cls = ccls; + staticonly = true; + goto isresolved; + } } } - SAFE_RESOLVE(Self, ctx); if (Self->ExprType == EFX_Super) { - // give the node the proper value type now that we know it's properly used. - cls = ctx.Function->Variants[0].SelfClass->ParentClass; - Self->ValueType = NewPointer(cls); - Self->ExprType = EFX_Self; - novirtual = true; // super calls are always non-virtual + auto clstype = dyn_cast(ctx.Function->Variants[0].SelfClass); + if (clstype != nullptr) + { + // give the node the proper value type now that we know it's properly used. + cls = clstype->ParentClass; + Self->ValueType = NewPointer(cls); + Self->ExprType = EFX_Self; + novirtual = true; // super calls are always non-virtual + } + else + { + ScriptPosition.Message(MSG_ERROR, "Super requires a class type"); + } + } + + // Note: These builtins would better be relegated to the actual type objects, instead of polluting this file, but that's a task for later. + + // Texture builtins. + if (Self->ValueType == TypeTextureID) + { + if (MethodName == NAME_IsValid || MethodName == NAME_IsNull || MethodName == NAME_Exists || MethodName == NAME_SetInvalid || MethodName == NAME_SetNull) + { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } + // No need to create a dedicated node here, all builtins map directly to trivial operations. + Self->ValueType = TypeSInt32; // all builtins treat the texture index as integer. + FxExpression *x; + switch (MethodName) + { + case NAME_IsValid: + x = new FxCompareRel('>', Self, new FxConstant(0, ScriptPosition)); + break; + + case NAME_IsNull: + x = new FxCompareEq(TK_Eq, Self, new FxConstant(0, ScriptPosition)); + break; + + case NAME_Exists: + x = new FxCompareRel(TK_Geq, Self, new FxConstant(0, ScriptPosition)); + break; + + case NAME_SetInvalid: + x = new FxAssign(Self, new FxConstant(-1, ScriptPosition)); + break; + + case NAME_SetNull: + x = new FxAssign(Self, new FxConstant(0, ScriptPosition)); + break; + } + Self = nullptr; + SAFE_RESOLVE(x, ctx); + if (MethodName == NAME_SetInvalid || MethodName == NAME_SetNull) x->ValueType = TypeVoid; // override the default type of the assignment operator. + delete this; + return x; + } } if (Self->IsVector()) @@ -6295,6 +7317,12 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) // handle builtins: Vectors got 2: Length and Unit. if (MethodName == NAME_Length || MethodName == NAME_Unit) { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } auto x = new FxVectorBuiltin(Self, MethodName); Self = nullptr; delete this; @@ -6302,12 +7330,29 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } } + if (Self->ValueType == TypeString) + { + // same for String methods. It also uses a hidden struct type to define them. + Self->ValueType = TypeStringStruct; + } + if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { auto ptype = static_cast(Self->ValueType)->PointedType; - if (ptype->IsKindOf(RUNTIME_CLASS(PClass))) + if (ptype->IsKindOf(RUNTIME_CLASS(PStruct))) { - cls = static_cast(ptype); + if (ptype->IsKindOf(RUNTIME_CLASS(PClass)) && MethodName == NAME_GetClass) + { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } + auto x = new FxGetClass(Self); + return x->Resolve(ctx); + } + cls = static_cast(ptype); } else { @@ -6316,6 +7361,22 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) return nullptr; } } + else if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PStruct))) + { + bool writable; + if (Self->RequestAddress(ctx, &writable) && writable) + { + cls = static_cast(Self->ValueType); + Self->ValueType = NewPointer(Self->ValueType); + } + else + { + // Cannot be made writable so we cannot use its methods. + ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s\n", MethodName.GetChars()); + delete this; + return nullptr; + } + } else { ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s\n", MethodName.GetChars()); @@ -6323,6 +7384,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) return nullptr; } + // Todo: handle member calls from instantiated structs. + isresolved: bool error = false; PFunction *afd = FindClassMemberFunction(cls, ctx.Class, MethodName, ScriptPosition, &error); @@ -6338,9 +7401,12 @@ isresolved: delete this; return nullptr; } + if (staticonly && (afd->Variants[0].Flags & VARF_Method)) { - if (!ctx.Class->IsDescendantOf(cls)) + auto clstype = dyn_cast(ctx.Class); + auto ccls = dyn_cast(cls); + if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls)) { ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars()); delete this; @@ -6355,6 +7421,28 @@ isresolved: } } + if (afd->Variants[0].Flags & VARF_Method) + { + if (Self->ExprType == EFX_Self) + { + if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd)) + { + delete this; + return nullptr; + } + } + else + { + // Functions with no Actor usage may not be called through a pointer because they will lose their context. + if (!(afd->Variants[0].UseFlags & SUF_ACTOR)) + { + ScriptPosition.Message(MSG_ERROR, "Function %s cannot be used with a non-self object\n", afd->SymbolName.GetChars()); + delete this; + return nullptr; + } + } + } + // do not pass the self pointer to static functions. auto self = (afd->Variants[0].Flags & VARF_Method) ? Self : nullptr; auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, staticonly|novirtual); @@ -6470,7 +7558,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) // //========================================================================== -int BuiltinCallLineSpecial(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinCallLineSpecial(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam > 2 && numparam < 8); assert(param[0].Type == REGT_INT); @@ -6523,7 +7611,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - ArgList.Clear(); + ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); if (EmitTail) @@ -6592,6 +7680,8 @@ VMFunction *FxVMFunctionCall::GetDirectFunction() // it inside VM code. if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual)) { + unsigned imp = Function->GetImplicitArgs(); + if (Function->Variants[0].ArgFlags.Size() > imp && !(Function->Variants[0].ArgFlags[imp] & VARF_Optional)) return nullptr; return Function->Variants[0].Implementation; } @@ -6610,7 +7700,10 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) SAFE_RESOLVE_OPT(Self, ctx); bool failed = false; auto proto = Function->Variants[0].Proto; - auto argtypes = proto->ArgumentTypes; + auto &argtypes = proto->ArgumentTypes; + auto &argnames = Function->Variants[0].ArgNames; + auto &argflags = Function->Variants[0].ArgFlags; + auto &defaults = Function->Variants[0].Implementation->DefaultArgs; int implicit = Function->GetImplicitArgs(); @@ -6626,6 +7719,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) { bool foundvarargs = false; PType * type = nullptr; + int flag = 0; if (argtypes.Last() != nullptr && ArgList.Size() + implicit > argtypes.Size()) { ScriptPosition.Message(MSG_ERROR, "Too many arguments in call to %s", Function->SymbolName.GetChars()); @@ -6639,12 +7733,102 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) if (!foundvarargs) { if (argtypes[i + implicit] == nullptr) foundvarargs = true; - else type = argtypes[i + implicit]; + else + { + type = argtypes[i + implicit]; + flag = argflags[i + implicit]; + } } assert(type != nullptr); - FxExpression *x = new FxTypeCast(ArgList[i], type, false); - x = x->Resolve(ctx); + if (ArgList[i]->ExprType == EFX_NamedNode) + { + if (!(flag & VARF_Optional)) + { + ScriptPosition.Message(MSG_ERROR, "Cannot use a named argument here - not all required arguments have been passed."); + delete this; + return nullptr; + } + if (foundvarargs) + { + ScriptPosition.Message(MSG_ERROR, "Cannot use a named argument in the varargs part of the parameter list."); + delete this; + return nullptr; + } + unsigned j; + bool done = false; + FName name = static_cast(ArgList[i])->name; + for (j = 0; j < argnames.Size() - implicit; j++) + { + if (argnames[j + implicit] == name) + { + if (j < i) + { + ScriptPosition.Message(MSG_ERROR, "Named argument %s comes before current position in argument list.", name.GetChars()); + delete this; + return nullptr; + } + // copy the original argument into the list + auto old = static_cast(ArgList[i]); + ArgList[i] = old->value; + old->value = nullptr; + delete old; + // now fill the gap with constants created from the default list so that we got a full list of arguments. + int insert = j - i; + for (int k = 0; k < insert; k++) + { + auto ntype = argtypes[i + k + implicit]; + // If this is a reference argument, the pointer type must be undone because the code below expects the pointed type as value type. + if (argflags[i + k + implicit] & VARF_Ref) + { + assert(ntype->IsKindOf(RUNTIME_CLASS(PPointer))); + ntype = TypeNullPtr; // the default of a reference type can only be a null pointer + } + auto x = new FxConstant(ntype, defaults[i + k + implicit], ScriptPosition); + ArgList.Insert(i + k, x); + } + done = true; + break; + } + } + if (!done) + { + ScriptPosition.Message(MSG_ERROR, "Named argument %s not found.", name.GetChars()); + delete this; + return nullptr; + } + // re-get the proper info for the inserted node. + type = argtypes[i + implicit]; + flag = argflags[i + implicit]; + } + + FxExpression *x; + if (!(flag & (VARF_Ref|VARF_Out))) + { + x = new FxTypeCast(ArgList[i], type, false); + x = x->Resolve(ctx); + } + else + { + bool writable; + ArgList[i] = ArgList[i]->Resolve(ctx); // nust be resolved before the address is requested. + if (ArgList[i] != nullptr && ArgList[i]->ValueType != TypeNullPtr) + { + ArgList[i]->RequestAddress(ctx, &writable); + if (flag & VARF_Ref) ArgList[i]->ValueType = NewPointer(ArgList[i]->ValueType); + // For a reference argument the types must match 100%. + if (type != ArgList[i]->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in reference argument", Function->SymbolName.GetChars()); + x = nullptr; + } + else + { + x = ArgList[i]; + } + } + else x = ArgList[i]; + } failed |= (x == nullptr); ArgList[i] = x; } @@ -6687,7 +7871,6 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) { ValueType = TypeVoid; } - return this; } @@ -6707,14 +7890,14 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) ExpEmit reg; if (CheckEmitCast(build, EmitTail, reg)) { - ArgList.Clear(); + ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); return reg; } } VMFunction *vmfunc = Function->Variants[0].Implementation; - bool staticcall = (vmfunc->Final || vmfunc->VirtualIndex == -1 || NoVirtual); + bool staticcall = (vmfunc->Final || vmfunc->VirtualIndex == ~0u || NoVirtual); count = 0; // Emit code to pass implied parameters @@ -6723,8 +7906,16 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { assert(Self != nullptr); selfemit = Self->Emit(build); - assert(selfemit.RegType == REGT_POINTER); - build->Emit(OP_PARAM, 0, selfemit.RegType, selfemit.RegNum); + assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target)); + if (selfemit.Fixed && selfemit.Target) + { + // Address of a local variable. + build->Emit(OP_PARAM, 0, selfemit.RegType | REGT_ADDROF, selfemit.RegNum); + } + else + { + build->Emit(OP_PARAM, 0, selfemit.RegType, selfemit.RegNum); + } count += 1; if (Function->Variants[0].Flags & VARF_Action) { @@ -6750,7 +7941,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { count += EmitParameter(build, ArgList[i], ScriptPosition); } - ArgList.Clear(); + ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); // Get a constant register for this function @@ -6767,10 +7958,8 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) } else if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result - ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount()); - build->Emit(OP_CALL_K, funcaddr, count, 1); - build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); - return reg; + build->Emit(OP_CALL_K, funcaddr, count, MAX(1, AssignCount)); + goto handlereturns; } else { // Call, expecting no results @@ -6792,18 +7981,34 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) } else if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result - ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount()); - build->Emit(OP_CALL, funcreg.RegNum, count, 1); - build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); - return reg; + build->Emit(OP_CALL, funcreg.RegNum, count, MAX(1, AssignCount)); + goto handlereturns; } else { // Call, expecting no results build->Emit(OP_CALL, funcreg.RegNum, count, 0); return ExpEmit(); } - - + } +handlereturns: + if (AssignCount == 0) + { + // Regular call, will not write to ReturnRegs + ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount()); + build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); + return reg; + } + else + { + // Multi-Assignment call, this must fill in the ReturnRegs array so that the multi-assignment operator can dispatch the return values. + assert((unsigned)AssignCount <= vmfunc->Proto->ReturnTypes.Size()); + for (int i = 0; i < AssignCount; i++) + { + ExpEmit reg(build, vmfunc->Proto->ReturnTypes[i]->GetRegType(), vmfunc->Proto->ReturnTypes[i]->GetRegCount()); + build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); + ReturnRegs.Push(reg); + } + return ExpEmit(); } } @@ -6920,13 +8125,26 @@ FxExpression *FxFlopFunctionCall::Resolve(FCompileContext& ctx) ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build) { - ExpEmit v = ArgList[0]->Emit(build); - assert(!v.Konst && v.RegType == REGT_FLOAT); + assert(ValueType == ArgList[0]->ValueType); + ExpEmit from = ArgList[0]->Emit(build); + ExpEmit to; + assert(from.Konst == 0); + assert(ValueType->GetRegCount() == 1); + // Do it in-place, unless a local variable + if (from.Fixed) + { + to = ExpEmit(build, from.RegType); + from.Free(build); + } + else + { + to = from; + } - build->Emit(OP_FLOP, v.RegNum, v.RegNum, FxFlops[Index].Flop); - ArgList.Clear(); + build->Emit(OP_FLOP, to.RegNum, from.RegNum, FxFlops[Index].Flop); + ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); - return v; + return to; } @@ -6974,6 +8192,186 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build) return to; } +//========================================================================== +// +// +//========================================================================== + +FxGetClass::FxGetClass(FxExpression *self) + :FxExpression(EFX_GetClass, self->ScriptPosition) +{ + Self = self; +} + +FxGetClass::~FxGetClass() +{ + SAFE_DELETE(Self); +} + +FxExpression *FxGetClass::Resolve(FCompileContext &ctx) +{ + SAFE_RESOLVE(Self, ctx); + if (!Self->IsObject()) + { + ScriptPosition.Message(MSG_ERROR, "GetClass() requires an object"); + delete this; + return nullptr; + } + ValueType = NewClassPointer(static_cast(static_cast(Self->ValueType)->PointedType)); + return this; +} + +ExpEmit FxGetClass::Emit(VMFunctionBuilder *build) +{ + ExpEmit op = Self->Emit(build); + op.Free(build); + ExpEmit to(build, REGT_POINTER); + build->Emit(OP_META, to.RegNum, op.RegNum); + return to; +} + +//========================================================================== +// +// +//========================================================================== + +FxGetDefaultByType::FxGetDefaultByType(FxExpression *self) + :FxExpression(EFX_GetDefaultByType, self->ScriptPosition) +{ + Self = self; +} + +FxGetDefaultByType::~FxGetDefaultByType() +{ + SAFE_DELETE(Self); +} + +FxExpression *FxGetDefaultByType::Resolve(FCompileContext &ctx) +{ + SAFE_RESOLVE(Self, ctx); + PClass *cls = nullptr; + + if (Self->ValueType == TypeString || Self->ValueType == TypeName) + { + if (Self->isConstant()) + { + cls = PClass::FindActor(static_cast(Self)->GetValue().GetName()); + if (cls == nullptr) + { + ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast(Self)->GetValue().GetString().GetChars()); + delete this; + return nullptr; + } + Self = new FxConstant(cls, NewClassPointer(cls), ScriptPosition); + } + else + { + // this is the ugly case. We do not know what we have and cannot do proper type casting. + // For now error out and let this case require explicit handling on the user side. + ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type", static_cast(Self)->GetValue().GetString().GetChars()); + delete this; + return nullptr; + } + } + else + { + auto cp = dyn_cast(Self->ValueType); + if (cp == nullptr || !cp->ClassRestriction->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type"); + delete this; + return nullptr; + } + cls = cp->ClassRestriction; + } + ValueType = NewPointer(cls, true); + return this; +} + +ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build) +{ + ExpEmit op = Self->Emit(build); + op.Free(build); + ExpEmit to(build, REGT_POINTER); + if (op.Konst) + { + build->Emit(OP_LKP, to.RegNum, op.RegNum); + op = to; + } + build->Emit(OP_LO, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + return to; +} + +//========================================================================== +// +// +//========================================================================== + +FxColorLiteral::FxColorLiteral(FArgumentList &args, FScriptPosition &sc) + :FxExpression(EFX_ColorLiteral, sc) +{ + ArgList = std::move(args); +} + +FxExpression *FxColorLiteral::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + unsigned constelements = 0; + assert(ArgList.Size() == 3 || ArgList.Size() == 4); + if (ArgList.Size() == 3) ArgList.Insert(0, nullptr); + for (int i = 0; i < 4; i++) + { + if (ArgList[i] != nullptr) + { + SAFE_RESOLVE(ArgList[i], ctx); + if (!ArgList[i]->IsInteger()) + { + ScriptPosition.Message(MSG_ERROR, "Integer expected for color component"); + delete this; + return nullptr; + } + if (ArgList[i]->isConstant()) + { + constval += clamp(static_cast(ArgList[i])->GetValue().GetInt(), 0, 255) << (24 - i * 8); + delete ArgList[i]; + ArgList[i] = nullptr; + constelements++; + } + } + else constelements++; + } + if (constelements == 4) + { + auto x = new FxConstant(constval, ScriptPosition); + x->ValueType = TypeColor; + delete this; + return x; + } + ValueType = TypeColor; + return this; +} + +ExpEmit FxColorLiteral::Emit(VMFunctionBuilder *build) +{ + ExpEmit out(build, REGT_INT); + build->Emit(OP_LK, out.RegNum, build->GetConstantInt(constval)); + for (int i = 0; i < 4; i++) + { + if (ArgList[i] != nullptr) + { + assert(!ArgList[i]->isConstant()); + ExpEmit in = ArgList[i]->Emit(build); + in.Free(build); + ExpEmit work(build, REGT_INT); + build->Emit(OP_MAX_RK, work.RegNum, in.RegNum, build->GetConstantInt(0)); + build->Emit(OP_MIN_RK, work.RegNum, work.RegNum, build->GetConstantInt(255)); + if (i != 3) build->Emit(OP_SLL_RI, work.RegNum, work.RegNum, 24 - (i * 8)); + build->Emit(OP_OR_RR, out.RegNum, out.RegNum, work.RegNum); + } + } + return out; +} + //========================================================================== // // FxSequence :: Resolve @@ -7146,7 +8544,7 @@ bool FxCompoundStatement::CheckLocalVariable(FName name) FxSwitchStatement::FxSwitchStatement(FxExpression *cond, FArgumentList &content, const FScriptPosition &pos) : FxExpression(EFX_SwitchStatement, pos) { - Condition = new FxIntCast(cond, false); + Condition = cond; Content = std::move(content); } @@ -7160,6 +8558,12 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx) CHECKRESOLVED(); SAFE_RESOLVE(Condition, ctx); + if (Condition->ValueType != TypeName) + { + Condition = new FxIntCast(Condition, false); + SAFE_RESOLVE(Condition, ctx); + } + if (Content.Size() == 0) { ScriptPosition.Message(MSG_WARNING, "Empty switch statement"); @@ -7178,15 +8582,15 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx) } } + auto outerctrl = ctx.ControlStmt; + ctx.ControlStmt = this; + for (auto &line : Content) { - // Do not resolve breaks, they need special treatment inside switch blocks. - if (line->ExprType != EFX_JumpStatement || static_cast(line)->Token != TK_Break) - { - SAFE_RESOLVE(line, ctx); - line->NeedResult = false; - } + SAFE_RESOLVE(line, ctx); + line->NeedResult = false; } + ctx.ControlStmt = outerctrl; if (Condition->isConstant()) { @@ -7206,6 +8610,12 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx) auto casestmt = static_cast(content[i]); if (casestmt->Condition == nullptr) defaultindex = i; else if (casestmt->CaseValue == static_cast(Condition)->GetValue().GetInt()) caseindex = i; + if (casestmt->Condition && casestmt->Condition->ValueType != Condition->ValueType) + { + casestmt->Condition->ScriptPosition.Message(MSG_ERROR, "Type mismatch in case statement"); + delete this; + return nullptr; + } } if (content[i]->ExprType == EFX_JumpStatement && static_cast(content[i])->Token == TK_Break) { @@ -7286,7 +8696,7 @@ ExpEmit FxSwitchStatement::Emit(VMFunctionBuilder *build) ca.jumpaddress = build->Emit(OP_JMP, 0); } size_t DefaultAddress = build->Emit(OP_JMP, 0); - TArray BreakAddresses; + bool defaultset = false; for (auto line : Content) { @@ -7307,27 +8717,21 @@ ExpEmit FxSwitchStatement::Emit(VMFunctionBuilder *build) else { build->BackpatchToHere(DefaultAddress); + defaultset = true; } break; - case EFX_JumpStatement: - if (static_cast(line)->Token == TK_Break) - { - BreakAddresses.Push(build->Emit(OP_JMP, 0)); - break; - } - // fall through for continue. - default: line->Emit(build); break; } } - for (auto addr : BreakAddresses) + for (auto addr : Breaks) { - build->BackpatchToHere(addr); + build->BackpatchToHere(addr->Address); } - Content.Clear(); + if (!defaultset) build->BackpatchToHere(DefaultAddress); + Content.DeleteAndClear(); Content.ShrinkToFit(); return ExpEmit(); } @@ -7360,7 +8764,7 @@ bool FxSwitchStatement::CheckReturn() FxCaseStatement::FxCaseStatement(FxExpression *cond, const FScriptPosition &pos) : FxExpression(EFX_CaseStatement, pos) { - Condition = cond? new FxIntCast(cond, false) : nullptr; + Condition = cond; } FxCaseStatement::~FxCaseStatement() @@ -7381,7 +8785,17 @@ FxExpression *FxCaseStatement::Resolve(FCompileContext &ctx) delete this; return nullptr; } - CaseValue = static_cast(Condition)->GetValue().GetInt(); + // Case labels can be ints or names. + if (Condition->ValueType != TypeName) + { + Condition = new FxIntCast(Condition, false); + SAFE_RESOLVE(Condition, ctx); + CaseValue = static_cast(Condition)->GetValue().GetInt(); + } + else + { + CaseValue = static_cast(Condition)->GetValue().GetName(); + } } return this; } @@ -7418,7 +8832,8 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx) if (WhenTrue == nullptr && WhenFalse == nullptr) { // We don't do anything either way, so disappear delete this; - return nullptr; + ScriptPosition.Message(MSG_WARNING, "empty if statement"); + return new FxNop(ScriptPosition); } SAFE_RESOLVE(Condition, ctx); @@ -7551,10 +8966,13 @@ bool FxIfStatement::CheckReturn() FxExpression *FxLoopStatement::Resolve(FCompileContext &ctx) { + auto outerctrl = ctx.ControlStmt; auto outer = ctx.Loop; + ctx.ControlStmt = this; ctx.Loop = this; auto x = DoResolve(ctx); ctx.Loop = outer; + ctx.ControlStmt = outerctrl; return x; } @@ -7878,9 +9296,17 @@ FxExpression *FxJumpStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); - if (ctx.Loop != nullptr) + if (ctx.ControlStmt != nullptr) { - ctx.Loop->Jumps.Push(this); + if (ctx.ControlStmt == ctx.Loop || Token == TK_Continue) + { + ctx.Loop->Jumps.Push(this); + } + else + { + // break in switch. + static_cast(ctx.ControlStmt)->Breaks.Push(this); + } return this; } else @@ -7927,7 +9353,7 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) else { // If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.) - if (ctx.ReturnProto != nullptr && ctx.Function->SymbolName != NAME_None) + if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() > 0 && ctx.Function->SymbolName != NAME_None) { Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[0], false, false); Value = Value->Resolve(ctx); @@ -8092,7 +9518,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinNameToClass(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinNameToClass(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam == 2); assert(numret == 1); @@ -8101,15 +9527,20 @@ int BuiltinNameToClass(VMFrameStack *stack, VMValue *param, TArray &def assert(ret->RegType == REGT_POINTER); FName clsname = ENamedName(param[0].i); - const PClass *cls = PClass::FindClass(clsname); - const PClass *desttype = reinterpret_cast(param[1].a); - - if (!cls->IsDescendantOf(desttype)) + if (clsname != NAME_None) { - Printf("class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); - cls = nullptr; + const PClass *cls = PClass::FindClass(clsname); + const PClass *desttype = reinterpret_cast(param[1].a); + + if (!cls->IsDescendantOf(desttype)) + { + // Let the caller check this. The message can be enabled for diagnostic purposes. + DPrintf(DMSG_SPAMMY, "class '%s' is not compatible with '%s'\n", clsname.GetChars(), desttype->TypeName.GetChars()); + cls = nullptr; + } + ret->SetPointer(const_cast(cls), ATAG_OBJECT); } - ret->SetPointer(const_cast(cls), ATAG_OBJECT); + else ret->SetPointer(nullptr, ATAG_OBJECT); return 1; } @@ -8139,6 +9570,114 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) return dest; } +//========================================================================== +// +//========================================================================== + +FxClassPtrCast::FxClassPtrCast(PClass *dtype, FxExpression *x) + : FxExpression(EFX_ClassPtrCast, x->ScriptPosition) +{ + ValueType = NewClassPointer(dtype); + desttype = dtype; + basex = x; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxClassPtrCast::~FxClassPtrCast() +{ + SAFE_DELETE(basex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxClassPtrCast::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(basex, ctx); + + if (basex->ValueType == TypeNullPtr) + { + basex->ValueType = ValueType; + auto x = basex; + basex = nullptr; + delete this; + return x; + } + auto to = static_cast(ValueType); + if (basex->ValueType->GetClass() == RUNTIME_CLASS(PClassPointer)) + { + auto from = static_cast(basex->ValueType); + // Downcast is always ok. + if (from->ClassRestriction->IsDescendantOf(to->ClassRestriction)) + { + basex->ValueType = to; + auto x = basex; + basex = nullptr; + delete this; + return x; + } + // Upcast needs a runtime check. + else if (to->ClassRestriction->IsDescendantOf(from->ClassRestriction)) + { + return this; + } + } + else if (basex->ValueType == TypeString || basex->ValueType == TypeName) + { + FxExpression *x = new FxClassTypeCast(to, basex); + basex = nullptr; + delete this; + return x->Resolve(ctx); + } + // Everything else is an error. + ScriptPosition.Message(MSG_ERROR, "Cannot cast %s to %s. The types are incompatible.", basex->ValueType->DescriptiveName(), to->DescriptiveName()); + delete this; + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +int BuiltinClassCast(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +{ + PARAM_PROLOGUE; + PARAM_CLASS(from, DObject); + PARAM_CLASS(to, DObject); + ACTION_RETURN_OBJECT(from->IsDescendantOf(to) ? from : nullptr); +} + +ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) +{ + ExpEmit clsname = basex->Emit(build); + + build->Emit(OP_PARAM, 0, clsname.RegType, clsname.RegNum); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(desttype, ATAG_OBJECT)); + + VMFunction *callfunc; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinClassCast, BuiltinClassCast); + + assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); + assert(((PSymbolVMFunction *)sym)->Function != nullptr); + callfunc = ((PSymbolVMFunction *)sym)->Function; + clsname.Free(build); + ExpEmit dest(build, REGT_POINTER); + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum); + return dest; +} + //========================================================================== // // Symbolic state labels. @@ -8283,14 +9822,22 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) CHECKRESOLVED(); ABORT(ctx.Class); int symlabel; + auto clstype = dyn_cast(ctx.Class); if (names[0] == NAME_None) { scope = nullptr; } + else if (clstype == nullptr) + { + // not in an actor, so any further checks are pointless. + ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars()); + delete this; + return nullptr; + } else if (names[0] == NAME_Super) { - scope = dyn_cast(ctx.Class->ParentClass); + scope = dyn_cast(clstype->ParentClass); } else { @@ -8301,7 +9848,7 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) delete this; return nullptr; } - else if (!scope->IsAncestorOf(ctx.Class)) + else if (!scope->IsAncestorOf(clstype)) { ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars()); delete this; @@ -8348,7 +9895,7 @@ FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name, VarFlags = varflags; Name = name; RegCount = type == TypeVector2 ? 2 : type == TypeVector3 ? 3 : 1; - Init = initval == nullptr? nullptr : new FxTypeCast(initval, type, false); + Init = initval; } FxLocalVariableDeclaration::~FxLocalVariableDeclaration() @@ -8359,75 +9906,188 @@ FxLocalVariableDeclaration::~FxLocalVariableDeclaration() FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); - SAFE_RESOLVE_OPT(Init, ctx); if (ctx.Block == nullptr) { ScriptPosition.Message(MSG_ERROR, "Variable declaration outside compound statement"); delete this; return nullptr; } + if (ValueType->RegType == REGT_NIL) + { + auto sfunc = static_cast(ctx.Function->Variants[0].Implementation); + StackOffset = sfunc->AllocExtraStack(ValueType); + // Todo: Process the compound initializer once implemented. + } + else + { + if (Init) Init = new FxTypeCast(Init, ValueType, false); + SAFE_RESOLVE_OPT(Init, ctx); + } ctx.Block->LocalVars.Push(this); return this; } +void FxLocalVariableDeclaration::SetReg(ExpEmit emit) +{ + assert(ValueType->GetRegType() == emit.RegType && ValueType->GetRegCount() == emit.RegCount); + RegNum = emit.RegNum; +} + ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) { - if (Init == nullptr) + if (ValueType->RegType != REGT_NIL) { - RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount); - } - else - { - ExpEmit emitval = Init->Emit(build); - - int regtype = emitval.RegType; - if (regtype < REGT_INT || regtype > REGT_TYPE) + if (Init == nullptr) { - ScriptPosition.Message(MSG_ERROR, "Attempted to assign a non-value"); - return ExpEmit(); - } - if (emitval.Konst) - { - auto constval = static_cast(Init); - RegNum = build->Registers[regtype].Get(1); - switch (regtype) + if (RegNum == -1) { - default: - case REGT_INT: - build->Emit(OP_LK, RegNum, build->GetConstantInt(constval->GetValue().GetInt())); - break; - - case REGT_FLOAT: - build->Emit(OP_LKF, RegNum, build->GetConstantFloat(constval->GetValue().GetFloat())); - break; - - case REGT_POINTER: - build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC)); - break; - - case REGT_STRING: - build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString())); + if (!(VarFlags & VARF_Out)) RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount); + else RegNum = build->Registers[REGT_POINTER].Get(1); } - emitval.Free(build); - } - else if (Init->ExprType != EFX_LocalVariable) - { - // take over the register that got allocated while emitting the Init expression. - RegNum = emitval.RegNum; } else { - ExpEmit out(build, emitval.RegType, emitval.RegCount); - build->Emit(ValueType->GetMoveOp(), out.RegNum, emitval.RegNum); - RegNum = out.RegNum; + assert(!(VarFlags & VARF_Out)); // 'out' variables should never be initialized, they can only exist as function parameters. + ExpEmit emitval = Init->Emit(build); + + int regtype = emitval.RegType; + if (regtype < REGT_INT || regtype > REGT_TYPE) + { + ScriptPosition.Message(MSG_ERROR, "Attempted to assign a non-value"); + return ExpEmit(); + } + if (emitval.Konst) + { + auto constval = static_cast(Init); + RegNum = build->Registers[regtype].Get(1); + switch (regtype) + { + default: + case REGT_INT: + build->Emit(OP_LK, RegNum, build->GetConstantInt(constval->GetValue().GetInt())); + break; + + case REGT_FLOAT: + build->Emit(OP_LKF, RegNum, build->GetConstantFloat(constval->GetValue().GetFloat())); + break; + + case REGT_POINTER: + { + bool isobject = ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass))); + build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), isobject ? ATAG_OBJECT : ATAG_GENERIC)); + break; + } + case REGT_STRING: + build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString())); + } + emitval.Free(build); + } + else if (Init->ExprType != EFX_LocalVariable) + { + // take over the register that got allocated while emitting the Init expression. + RegNum = emitval.RegNum; + } + else + { + ExpEmit out(build, emitval.RegType, emitval.RegCount); + build->Emit(ValueType->GetMoveOp(), out.RegNum, emitval.RegNum); + RegNum = out.RegNum; + } } } + else + { + // Init arrays and structs. + } return ExpEmit(); } void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build) { // Release the register after the containing block gets closed - assert(RegNum != -1); - build->Registers[ValueType->GetRegType()].Return(RegNum, RegCount); + if(RegNum != -1) + { + build->Registers[ValueType->GetRegType()].Return(RegNum, RegCount); + } + // Stack space will not be released because that would make controlled destruction impossible. + // For that all local stack variables need to live for the entire execution of a function. +} + + +FxStaticArray::FxStaticArray(PType *type, FName name, FArgumentList &args, const FScriptPosition &pos) + : FxLocalVariableDeclaration(NewArray(type, args.Size()), name, nullptr, VARF_Static|VARF_ReadOnly, pos) +{ + ElementType = type; + ExprType = EFX_StaticArray; + values = std::move(args); +} + +FxExpression *FxStaticArray::Resolve(FCompileContext &ctx) +{ + bool fail = false; + for (unsigned i = 0; i < values.Size(); i++) + { + values[i] = new FxTypeCast(values[i], ElementType, false); + values[i] = values[i]->Resolve(ctx); + if (values[i] == nullptr) fail = true; + else if (!values[i]->isConstant()) + { + ScriptPosition.Message(MSG_ERROR, "Initializer must be constant"); + fail = true; + } + } + if (fail) + { + delete this; + return nullptr; + } + if (ElementType->GetRegType() == REGT_NIL) + { + ScriptPosition.Message(MSG_ERROR, "Invalid type for constant array"); + delete this; + return nullptr; + } + + ctx.Block->LocalVars.Push(this); + return this; +} + +ExpEmit FxStaticArray::Emit(VMFunctionBuilder *build) +{ + switch (ElementType->GetRegType()) + { + default: + assert(false && "Invalid register type"); + break; + + case REGT_INT: + { + TArray cvalues; + for (auto v : values) cvalues.Push(static_cast(v)->GetValue().GetInt()); + StackOffset = build->AllocConstantsInt(cvalues.Size(), &cvalues[0]); + break; + } + case REGT_FLOAT: + { + TArray cvalues; + for (auto v : values) cvalues.Push(static_cast(v)->GetValue().GetFloat()); + StackOffset = build->AllocConstantsFloat(cvalues.Size(), &cvalues[0]); + break; + } + case REGT_STRING: + { + TArray cvalues; + for (auto v : values) cvalues.Push(static_cast(v)->GetValue().GetString()); + StackOffset = build->AllocConstantsString(cvalues.Size(), &cvalues[0]); + break; + } + case REGT_POINTER: + { + TArray cvalues; + for (auto v : values) cvalues.Push(static_cast(v)->GetValue().GetPointer()); + StackOffset = build->AllocConstantsAddress(cvalues.Size(), &cvalues[0], ElementType->GetLoadOp() == OP_LO ? ATAG_OBJECT : ATAG_GENERIC); + break; + } + } + return ExpEmit(); } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index f1778512f..c54c8dc62 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -44,6 +44,7 @@ #include "sc_man.h" #include "s_sound.h" #include "actor.h" +#include "vmbuilder.h" #define CHECKRESOLVED() if (isresolved) return this; isresolved=true; @@ -67,14 +68,16 @@ struct FScriptPosition; class FxLoopStatement; class FxCompoundStatement; class FxLocalVariableDeclaration; +typedef TDeletingArray FArgumentList; struct FCompileContext { + FxExpression *ControlStmt = nullptr; FxLoopStatement *Loop = nullptr; FxCompoundStatement *Block = nullptr; PPrototype *ReturnProto; PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.) - PClass *Class; // The type of the owning class. + PStruct *Class; // The type of the owning class. bool FromDecorate; // DECORATE must silence some warnings and demote some errors. int StateIndex; // index in actor's state table for anonymous functions, otherwise -1 (not used by DECORATE which pre-resolves state indices) int StateCount; // amount of states an anoymous function is being used on (must be 1 for state indices to be allowed.) @@ -83,7 +86,7 @@ struct FCompileContext TDeletingArray FunctionArgs; FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump); - FCompileContext(PClass *cls, bool fromdecorate); // only to be used to resolve constants! + FCompileContext(PStruct *cls, bool fromdecorate); // only to be used to resolve constants! PSymbol *FindInClass(FName identifier, PSymbolTable *&symt); PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt); @@ -168,10 +171,16 @@ struct ExpVal return regtype == REGT_INT ? Int : regtype == REGT_FLOAT ? int(Float) : 0; } + unsigned GetUInt() const + { + int regtype = Type->GetRegType(); + return regtype == REGT_INT ? unsigned(Int) : regtype == REGT_FLOAT ? unsigned(Float) : 0; + } + double GetFloat() const { int regtype = Type->GetRegType(); - return regtype == REGT_INT ? double(Int) : regtype == REGT_FLOAT ? Float : 0; + return regtype == REGT_INT ? (Type == TypeUInt32? double(unsigned(Int)) : double(Int)) : regtype == REGT_FLOAT ? Float : 0; } void *GetPointer() const @@ -202,17 +211,6 @@ struct ExpVal } }; -struct ExpEmit -{ - ExpEmit() : RegNum(0), RegType(REGT_NIL), RegCount(1), Konst(false), Fixed(false), Final(false), Target(false) {} - ExpEmit(int reg, int type, bool konst = false, bool fixed = false) : RegNum(reg), RegType(type), RegCount(1), Konst(konst), Fixed(fixed), Final(false), Target(false) {} - ExpEmit(VMFunctionBuilder *build, int type, int count = 1); - void Free(VMFunctionBuilder *build); - void Reuse(VMFunctionBuilder *build); - - BYTE RegNum, RegType, RegCount, Konst:1, Fixed:1, Final:1, Target:1; -}; - enum EFxType { EFX_Expression, @@ -268,6 +266,7 @@ enum EFxType EFX_JumpStatement, EFX_ReturnStatement, EFX_ClassTypeCast, + EFX_ClassPtrCast, EFX_StateByIndex, EFX_RuntimeStateIndex, EFX_MultiNameState, @@ -282,6 +281,15 @@ enum EFxType EFX_DynamicCast, EFX_GlobalVariable, EFX_Super, + EFX_StackVariable, + EFX_MultiAssign, + EFX_StaticArray, + EFX_StaticArrayVariable, + EFX_CVar, + EFX_NamedNode, + EFX_GetClass, + EFX_ColorLiteral, + EFX_GetDefaultByType, EFX_COUNT }; @@ -310,12 +318,14 @@ public: virtual PPrototype *ReturnProto(); virtual VMFunction *GetDirectFunction(); virtual bool CheckReturn() { return false; } + virtual int GetBitValue() { return -1; } bool IsNumeric() const { return ValueType->isNumeric(); } bool IsFloat() const { return ValueType->GetRegType() == REGT_FLOAT && ValueType->GetRegCount() == 1; } bool IsInteger() const { return ValueType->isNumeric() && (ValueType->GetRegType() == REGT_INT); } bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; } bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; }; bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); } + bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); } virtual ExpEmit Emit(VMFunctionBuilder *build); @@ -345,10 +355,11 @@ class FxIdentifier : public FxExpression { public: FName Identifier; + bool noglobal = false; FxIdentifier(FName i, const FScriptPosition &p); FxExpression *Resolve(FCompileContext&); - FxExpression *ResolveMember(FCompileContext&, PClass*, FxExpression*&, PStruct*); + FxExpression *ResolveMember(FCompileContext&, PStruct*, FxExpression*&, PStruct*); }; @@ -468,6 +479,31 @@ public: ValueType = value.Type = TypeNullPtr; isresolved = true; } + + FxConstant(PType *type, VMValue &vmval, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos) + { + ValueType = value.Type = type; + isresolved = true; + switch (vmval.Type) + { + default: + case REGT_INT: + value.Int = vmval.i; + break; + + case REGT_FLOAT: + value.Float = vmval.f; + break; + + case REGT_STRING: + value = ExpVal(vmval.s()); + break; + + case REGT_POINTER: + value.pointer = vmval.a; + break; + } + } static FxExpression *MakeConstant(PSymbol *sym, const FScriptPosition &pos); @@ -784,6 +820,28 @@ public: ExpEmit Address; }; +//========================================================================== +// +// FxAssign +// +//========================================================================== +class FxCompoundStatement; + +class FxMultiAssign : public FxExpression +{ + FxCompoundStatement *LocalVarContainer; // for handling the temporary variables of the results, which may need type casts. + FArgumentList Base; + FxExpression *Right; + bool AddressRequested = false; + bool AddressWritable = false; + +public: + FxMultiAssign(FArgumentList &base, FxExpression *right, const FScriptPosition &pos); + ~FxMultiAssign(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxAssignSelf @@ -815,8 +873,7 @@ public: FxBinary(int, FxExpression*, FxExpression*); ~FxBinary(); - bool ResolveLR(FCompileContext& ctx, bool castnumeric); - void Promote(FCompileContext &ctx); + bool Promote(FCompileContext &ctx, bool forceint = false); }; //========================================================================== @@ -872,6 +929,7 @@ public: class FxCompareRel : public FxBinary { + PType *CompareType; public: FxCompareRel(int, FxExpression*, FxExpression*); @@ -900,11 +958,26 @@ public: // //========================================================================== -class FxBinaryInt : public FxBinary +class FxBitOp : public FxBinary { public: - FxBinaryInt(int, FxExpression*, FxExpression*); + FxBitOp(int, FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxShift : public FxBinary +{ +public: + + FxShift(int, FxExpression*, FxExpression*); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -924,6 +997,21 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// +// +//========================================================================== + +class FxConcat : public FxBinary +{ +public: + FxConcat(FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxBinaryLogical @@ -1179,6 +1267,16 @@ public: FxExpression *Resolve(FCompileContext&); bool RequestAddress(FCompileContext &ctx, bool *writable); ExpEmit Emit(VMFunctionBuilder *build); + virtual int GetBitValue() { return membervar->BitValue; } +}; + +class FxCVar : public FxExpression +{ + FBaseCVar *CVar; +public: + FxCVar(FBaseCVar*, const FScriptPosition&); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -1200,6 +1298,7 @@ public: FxExpression *Resolve(FCompileContext&); bool RequestAddress(FCompileContext &ctx, bool *writable); ExpEmit Emit(VMFunctionBuilder *build); + virtual int GetBitValue() { return membervar->BitValue; } }; //========================================================================== @@ -1234,6 +1333,47 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxLocalVariable +// +//========================================================================== + +class FxStackVariable : public FxExpression +{ +public: + PField *membervar; + bool AddressRequested; + bool AddressWritable; + + FxStackVariable(PType *type, int offset, const FScriptPosition&); + ~FxStackVariable(); + void ReplaceField(PField *newfield); + FxExpression *Resolve(FCompileContext&); + bool RequestAddress(FCompileContext &ctx, bool *writable); + ExpEmit Emit(VMFunctionBuilder *build); + virtual int GetBitValue() { return membervar->BitValue; } +}; + +//========================================================================== +// +// FxLocalVariable +// +//========================================================================== +class FxStaticArray; + +class FxStaticArrayVariable : public FxExpression +{ +public: + FxStaticArray *Variable; + bool AddressRequested; + + FxStaticArrayVariable(FxLocalVariableDeclaration*, const FScriptPosition&); + FxExpression *Resolve(FCompileContext&); + bool RequestAddress(FCompileContext &ctx, bool *writable); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxSelf @@ -1280,6 +1420,7 @@ public: FxExpression *index; bool AddressRequested; bool AddressWritable; + bool arrayispointer = false; FxArrayElement(FxExpression*, FxExpression*); ~FxArrayElement(); @@ -1295,8 +1436,6 @@ public: // //========================================================================== -typedef TDeletingArray FArgumentList; - class FxFunctionCall : public FxExpression { FName MethodName; @@ -1391,6 +1530,60 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxFlopFunctionCall +// +//========================================================================== + +class FxGetClass : public FxExpression +{ + FxExpression *Self; + +public: + + FxGetClass(FxExpression *self); + ~FxGetClass(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// FxFlopFunctionCall +// +//========================================================================== + +class FxGetDefaultByType : public FxExpression +{ + FxExpression *Self; + +public: + + FxGetDefaultByType(FxExpression *self); + ~FxGetDefaultByType(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// FxColorLiteral +// +//========================================================================== + +class FxColorLiteral : public FxExpression +{ + FArgumentList ArgList; + int constval = 0; + +public: + + FxColorLiteral(FArgumentList &args, FScriptPosition &sc); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxVMFunctionCall @@ -1399,11 +1592,16 @@ public: class FxVMFunctionCall : public FxExpression { + friend class FxMultiAssign; + bool EmitTail; bool NoVirtual; FxExpression *Self; PFunction *Function; FArgumentList ArgList; + // for multi assignment + int AssignCount = 0; + TArray ReturnRegs; public: FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual); @@ -1413,6 +1611,10 @@ public: VMFunction *GetDirectFunction(); ExpEmit Emit(VMFunctionBuilder *build); bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit ®); + TArray &GetReturnTypes() const + { + return Function->Variants[0].Proto->ReturnTypes; + } }; //========================================================================== @@ -1448,6 +1650,8 @@ class FxCompoundStatement : public FxSequence FxCompoundStatement *Outer = nullptr; friend class FxLocalVariableDeclaration; + friend class FxStaticArray; + friend class FxMultiAssign; public: FxCompoundStatement(const FScriptPosition &pos) : FxSequence(pos) {} @@ -1477,6 +1681,8 @@ class FxSwitchStatement : public FxExpression TArray CaseAddresses; public: + TArray Breaks; + FxSwitchStatement(FxExpression *cond, FArgumentList &content, const FScriptPosition &pos); ~FxSwitchStatement(); FxExpression *Resolve(FCompileContext&); @@ -1656,6 +1862,25 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// +// +//========================================================================== + +class FxClassPtrCast : public FxExpression +{ + PClass *desttype; + FxExpression *basex; + +public: + + FxClassPtrCast(PClass *dtype, FxExpression *x); + ~FxClassPtrCast(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // Only used to resolve the old jump by index feature of DECORATE @@ -1740,12 +1965,14 @@ class FxLocalVariableDeclaration : public FxExpression { friend class FxCompoundStatement; friend class FxLocalVariable; + friend class FxStaticArrayVariable; FName Name; FxExpression *Init; int VarFlags; int RegCount; public: + int StackOffset = -1; int RegNum = -1; FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, int varflags, const FScriptPosition &p); @@ -1753,7 +1980,48 @@ public: FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); void Release(VMFunctionBuilder *build); + void SetReg(ExpEmit reginfo); }; +//========================================================================== +// +// +// +//========================================================================== + +class FxStaticArray : public FxLocalVariableDeclaration +{ + friend class FxStaticArrayVariable; + + PType *ElementType; + FArgumentList values; + +public: + + FxStaticArray(PType *type, FName name, FArgumentList &args, const FScriptPosition &pos); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +class FxNamedNode : public FxExpression +{ +public: + FName name; + FxExpression *value; + FxNamedNode(FName n, FxExpression *x, const FScriptPosition &pos) + : FxExpression(EFX_NamedNode, pos), name(n), value(x) + { + } + + FxExpression *Resolve(FCompileContext&) + { + // This should never reach the backend in a supported context, + // it's just needed to extend argument lists with the skipped parameters and needs to be resolved by the parent node. + ScriptPosition.Message(MSG_ERROR, "Named arguments not supported here"); + delete this; + return nullptr; + } +}; + #endif diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index a4cf20de4..400444340 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -44,7 +44,6 @@ #include "s_sound.h" #include "cmdlib.h" #include "p_lnspec.h" -#include "a_action.h" #include "decallib.h" #include "i_system.h" #include "thingdef.h" @@ -96,7 +95,9 @@ public: } }; -IMPLEMENT_CLASS(AFakeInventory, false, false, false, false) +IMPLEMENT_CLASS(AFakeInventory, false, false) + +DEFINE_FIELD(AFakeInventory, Respawnable) // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -699,7 +700,7 @@ static void ParseSpriteFrames (PClassActor *info, TArray &states, TArray { sc.ScriptError ("* must come after a frame"); } - state.Fullbright = true; + state.StateFlags |= STF_FULLBRIGHT; } else if (*token < 'A' || *token > ']') { diff --git a/src/scripting/decorate/thingdef_exp.cpp b/src/scripting/decorate/thingdef_exp.cpp index 2d69be340..a0ab09510 100644 --- a/src/scripting/decorate/thingdef_exp.cpp +++ b/src/scripting/decorate/thingdef_exp.cpp @@ -5,6 +5,7 @@ ** **--------------------------------------------------------------------------- ** Copyright 2005 Jan Cholasta +** Copyright 2005-2016 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -139,27 +140,27 @@ static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls) break; case TK_LShiftEq: - exp = new FxBinaryInt(TK_LShift, left, nullptr); + exp = new FxShift(TK_LShift, left, nullptr); break; case TK_RShiftEq: - exp = new FxBinaryInt(TK_RShift, left, nullptr); + exp = new FxShift(TK_RShift, left, nullptr); break; case TK_URShiftEq: - exp = new FxBinaryInt(TK_URShift, left, nullptr); + exp = new FxShift(TK_URShift, left, nullptr); break; case TK_AndEq: - exp = new FxBinaryInt('&', left, nullptr); + exp = new FxBitOp('&', left, nullptr); break; case TK_XorEq: - exp = new FxBinaryInt('^', left, nullptr); + exp = new FxBitOp('^', left, nullptr); break; case TK_OrEq: - exp = new FxBinaryInt('|', left, nullptr); + exp = new FxBitOp('|', left, nullptr); break; default: @@ -207,7 +208,7 @@ static FxExpression *ParseExpressionJ (FScanner &sc, PClassActor *cls) while (sc.CheckToken('|')) { FxExpression *right = ParseExpressionI (sc, cls); - tmp = new FxBinaryInt('|', tmp, right); + tmp = new FxBitOp('|', tmp, right); } return tmp; } @@ -219,7 +220,7 @@ static FxExpression *ParseExpressionI (FScanner &sc, PClassActor *cls) while (sc.CheckToken('^')) { FxExpression *right = ParseExpressionH (sc, cls); - tmp = new FxBinaryInt('^', tmp, right); + tmp = new FxBitOp('^', tmp, right); } return tmp; } @@ -231,7 +232,7 @@ static FxExpression *ParseExpressionH (FScanner &sc, PClassActor *cls) while (sc.CheckToken('&')) { FxExpression *right = ParseExpressionG (sc, cls); - tmp = new FxBinaryInt('&', tmp, right); + tmp = new FxBitOp('&', tmp, right); } return tmp; } @@ -272,7 +273,7 @@ static FxExpression *ParseExpressionE (FScanner &sc, PClassActor *cls) { int token = sc.TokenType; FxExpression *right = ParseExpressionD (sc, cls); - tmp = new FxBinaryInt(token, tmp, right); + tmp = new FxShift(token, tmp, right); } if (!sc.End) sc.UnGet(); return tmp; diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 490d9cf79..442e52884 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -4,8 +4,8 @@ ** Actor definitions - all parser related code ** **--------------------------------------------------------------------------- -** Copyright 2002-2007 Christoph Oelckers -** Copyright 2004-2007 Randy Heit +** Copyright 2002-2016 Christoph Oelckers +** Copyright 2004-2016 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index cad542d1b..1cb0a91af 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -4,8 +4,8 @@ ** Actor definitions - the state parser ** **--------------------------------------------------------------------------- -** Copyright 2002-2007 Christoph Oelckers -** Copyright 2004-2007 Randy Heit +** Copyright 2002-2016 Christoph Oelckers +** Copyright 2004-2016 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -45,7 +45,6 @@ #include "templates.h" #include "cmdlib.h" #include "p_lnspec.h" -#include "a_action.h" #include "p_local.h" #include "v_palette.h" #include "doomerrors.h" @@ -132,7 +131,7 @@ static FString ParseStateString(FScanner &sc) } //========================================================================== -//*** +// // ParseStates // parses a state block // @@ -274,24 +273,24 @@ do_stop: { if (sc.Compare("BRIGHT")) { - state.Fullbright = true; + state.StateFlags |= STF_FULLBRIGHT; continue; } if (sc.Compare("FAST")) { - state.Fast = true; + state.StateFlags |= STF_FAST; continue; } if (sc.Compare("SLOW")) { - state.Slow = true; + state.StateFlags |= STF_SLOW; continue; } if (sc.Compare("NODELAY")) { if (bag.statedef.GetStateLabelIndex(NAME_Spawn) == bag.statedef.GetStateCount()) { - state.NoDelay = true; + state.StateFlags |= STF_NODELAY; } else { @@ -327,7 +326,7 @@ do_stop: } if (sc.Compare("CANRAISE")) { - state.CanRaise = true; + state.StateFlags |= STF_CANRAISE; continue; } diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index bf6b08ea7..77120c947 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -50,13 +50,13 @@ #include "s_sound.h" #include "cmdlib.h" #include "p_lnspec.h" -#include "a_action.h" #include "decallib.h" #include "m_random.h" #include "i_system.h" #include "m_argv.h" #include "p_local.h" #include "doomerrors.h" +#include "a_artifacts.h" #include "a_weaponpiece.h" #include "p_conversation.h" #include "v_text.h" @@ -68,6 +68,7 @@ // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void InitThingdef(); +TArray OptionalClassPtrs; // STATIC FUNCTION PROTOTYPES -------------------------------------------- PClassActor *QuestItemClasses[31]; @@ -104,7 +105,7 @@ FScriptPosition & GetStateSource(FState *state) // //========================================================================== -void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags, int useflags) +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PStruct *cls, DWORD funcflags, int useflags) { // Must be called before adding any other arguments. assert(args == nullptr || args->Size() == 0); @@ -131,7 +132,7 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArrayPush(NewPointer(cls)); } - args->Push(TypeState/*Info*/); // fixme: TypeState is not the correct type here!!! + args->Push(NewPointer(NewStruct("FStateParamInfo", nullptr))); } if (argflags != nullptr) { @@ -170,7 +171,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i SetImplicitArgs(&args, &argflags, &argnames, containingclass, fflags, flags); PFunction *sym = new PFunction(containingclass, NAME_None); // anonymous functions do not have names. - sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, fflags); + sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, fflags, flags); return sym; } @@ -182,7 +183,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i // //========================================================================== -PFunction *FindClassMemberFunction(PClass *selfcls, PClass *funccls, FName name, FScriptPosition &sc, bool *error) +PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error) { // Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead. if (name == NAME_ACS_NamedExecuteWithResult) return nullptr; @@ -390,8 +391,21 @@ void LoadActors () { if (ti->Size == TentativeClass) { - Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars()); - FScriptPosition::ErrorCounter++; + if (ti->ObjectFlags & OF_Transient) + { + Printf(TEXTCOLOR_ORANGE "Class %s referenced but not defined\n", ti->TypeName.GetChars()); + FScriptPosition::WarnCounter++; + DObject::StaticPointerSubstitution(ti, nullptr); + for (auto op : OptionalClassPtrs) + { + if (*op == ti) *op = nullptr; + } + } + else + { + Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars()); + FScriptPosition::ErrorCounter++; + } continue; } @@ -431,4 +445,6 @@ void LoadActors () QuestItemClasses[i] = PClass::FindActor(fmt); } StateSourceLines.Clear(); + OptionalClassPtrs.Clear(); + OptionalClassPtrs.ShrinkToFit(); } diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 7218a84e6..dd8529252 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -6,7 +6,6 @@ #include "s_sound.h" #include "sc_man.h" #include "cmdlib.h" -#include "vm.h" class FScanner; @@ -147,7 +146,8 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass) // //========================================================================== -AFuncDesc *FindFunction(const char * string); +AFuncDesc *FindFunction(PStruct *cls, const char * string); +FieldDesc *FindField(PStruct *cls, const char * string); FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false); @@ -157,9 +157,9 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags, int useflags); +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PStruct *cls, DWORD funcflags, int useflags); PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags); -PFunction *FindClassMemberFunction(PClass *cls, PClass *funccls, FName name, FScriptPosition &sc, bool *error); +PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error); void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate); //========================================================================== diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 4ccc72748..b34157b36 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -43,9 +43,16 @@ #include "d_player.h" #include "p_effect.h" #include "autosegs.h" +#include "p_maputl.h" +#include "gi.h" +#include "p_terrain.h" +#include "gstrings.h" +#include "zstring.h" +#include "d_event.h" static TArray properties; static TArray AFTable; +static TArray FieldTable; //========================================================================== // @@ -62,7 +69,7 @@ static TArray AFTable; #define DEFINE_DUMMY_FLAG(name, deprec) { DEPF_UNUSED, #name, -1, 0, deprec? VARF_Deprecated:0 } // internal flags. These do not get exposed to actor definitions but scripts need to be able to access them as variables. -FFlagDef InternalActorFlagDefs[]= +static FFlagDef InternalActorFlagDefs[]= { DEFINE_FLAG(MF, INCHASE, AActor, flags), DEFINE_FLAG(MF, UNMORPHED, AActor, flags), @@ -89,11 +96,10 @@ FFlagDef InternalActorFlagDefs[]= DEFINE_FLAG(MF6, INTRYMOVE, AActor, flags6), DEFINE_FLAG(MF7, HANDLENODELAY, AActor, flags7), DEFINE_FLAG(MF7, FLYCHEAT, AActor, flags7), - { 0xffffffff } }; -FFlagDef ActorFlagDefs[]= +static FFlagDef ActorFlagDefs[]= { DEFINE_FLAG(MF, PICKUP, APlayerPawn, flags), DEFINE_FLAG(MF, SPECIAL, APlayerPawn, flags), @@ -294,6 +300,8 @@ FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF7, USEKILLSCRIPTS, AActor, flags7), DEFINE_FLAG(MF7, NOKILLSCRIPTS, AActor, flags7), DEFINE_FLAG(MF7, SPRITEANGLE, AActor, flags7), + DEFINE_FLAG(MF7, SMASHABLE, AActor, flags7), + DEFINE_FLAG(MF7, NOSHIELDREFLECT, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), @@ -327,7 +335,6 @@ FFlagDef ActorFlagDefs[]= DEFINE_FLAG2(BOUNCE_MBF, MBFBOUNCER, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_AutoOffFloorOnly, BOUNCEAUTOOFFFLOORONLY, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_UseBounceState, USEBOUNCESTATE, AActor, BounceFlags), - { 0xffffffff } }; // These won't be accessible through bitfield variables @@ -411,12 +418,14 @@ static FFlagDef WeaponFlagDefs[] = DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags), - - DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags), + + DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), }; + + static FFlagDef PlayerPawnFlagDefs[] = { // PlayerPawn flags @@ -431,14 +440,15 @@ static FFlagDef PowerSpeedFlagDefs[] = DEFINE_FLAG(PSF, NOTRAIL, APowerSpeed, SpeedFlags), }; -static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; } FlagLists[] = +static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; int Use; } FlagLists[] = { - { &RUNTIME_CLASS_CASTLESS(AActor), ActorFlagDefs, countof(ActorFlagDefs)-1 }, // -1 to account for the terminator - { &RUNTIME_CLASS_CASTLESS(AActor), MoreFlagDefs, countof(MoreFlagDefs) }, - { &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs) }, - { &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs) }, - { &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs) }, - { &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs) }, + { &RUNTIME_CLASS_CASTLESS(AActor), ActorFlagDefs, countof(ActorFlagDefs), 3 }, // -1 to account for the terminator + { &RUNTIME_CLASS_CASTLESS(AActor), MoreFlagDefs, countof(MoreFlagDefs), 1 }, + { &RUNTIME_CLASS_CASTLESS(AActor), InternalActorFlagDefs, countof(InternalActorFlagDefs), 2 }, + { &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs), 3 }, + { &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs), 3 }, + { &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 }, + { &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs), 3 }, }; #define NUM_FLAG_LISTS (countof(FlagLists)) @@ -486,7 +496,7 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo int max = strict ? 2 : NUM_FLAG_LISTS; for (int i = 0; i < max; ++i) { - if (type->IsDescendantOf (*FlagLists[i].Type)) + if ((FlagLists[i].Use & 1) && type->IsDescendantOf (*FlagLists[i].Type)) { def = FindFlag (FlagLists[i].Defs, FlagLists[i].NumDefs, part1); if (def != NULL) @@ -571,17 +581,60 @@ FPropertyInfo *FindProperty(const char * string) // //========================================================================== -AFuncDesc *FindFunction(const char * string) +AFuncDesc *FindFunction(PStruct *cls, const char * string) { - int min = 0, max = AFTable.Size()-1; + for (int i = 0; i < 2; i++) + { + // Since many functions have been declared with Actor as owning class, despite being members of something else, let's hack around this until they have been fixed or exported. + // Since most of these are expected to be scriptified anyway, there's no point fixing them all before they get exported. + if (i == 1) + { + if (!cls->IsKindOf(RUNTIME_CLASS(PClassActor))) break; + cls = RUNTIME_CLASS(AActor); + } + + int min = 0, max = AFTable.Size() - 1; + + while (min <= max) + { + int mid = (min + max) / 2; + int lexval = stricmp(cls->TypeName.GetChars(), AFTable[mid].ClassName + 1); + if (lexval == 0) lexval = stricmp(string, AFTable[mid].FuncName); + if (lexval == 0) + { + return &AFTable[mid]; + } + else if (lexval > 0) + { + min = mid + 1; + } + else + { + max = mid - 1; + } + } + } + return nullptr; +} + +//========================================================================== +// +// Find a function by name using a binary search +// +//========================================================================== + +FieldDesc *FindField(PStruct *cls, const char * string) +{ + int min = 0, max = FieldTable.Size() - 1; while (min <= max) { int mid = (min + max) / 2; - int lexval = stricmp (string, AFTable[mid].Name); + int lexval = stricmp(cls->TypeName.GetChars(), FieldTable[mid].ClassName + 1); + if (lexval == 0) lexval = stricmp(string, FieldTable[mid].FieldName); if (lexval == 0) { - return &AFTable[mid]; + return &FieldTable[mid]; } else if (lexval > 0) { @@ -592,7 +645,7 @@ AFuncDesc *FindFunction(const char * string) max = mid - 1; } } - return NULL; + return nullptr; } @@ -627,7 +680,18 @@ static int propcmp(const void * a, const void * b) static int funccmp(const void * a, const void * b) { - return stricmp( ((AFuncDesc*)a)->Name, ((AFuncDesc*)b)->Name); + // +1 to get past the prefix letter of the native class name, which gets omitted by the FName for the class. + int res = stricmp(((AFuncDesc*)a)->ClassName + 1, ((AFuncDesc*)b)->ClassName + 1); + if (res == 0) res = stricmp(((AFuncDesc*)a)->FuncName, ((AFuncDesc*)b)->FuncName); + return res; +} + +static int fieldcmp(const void * a, const void * b) +{ + // +1 to get past the prefix letter of the native class name, which gets omitted by the FName for the class. + int res = stricmp(((FieldDesc*)a)->ClassName + 1, ((FieldDesc*)b)->ClassName + 1); + if (res == 0) res = stricmp(((FieldDesc*)a)->FieldName, ((FieldDesc*)b)->FieldName); + return res; } //========================================================================== @@ -635,10 +699,90 @@ static int funccmp(const void * a, const void * b) // Initialization // //========================================================================== -void G_InitLevelLocalsForScript(); void InitThingdef() { + PType *TypeActor = NewPointer(RUNTIME_CLASS(AActor)); + + PStruct *sstruct = NewNativeStruct("Sector", nullptr); + auto sptr = NewPointer(sstruct); + sstruct->AddNativeField("soundtarget", TypeActor, myoffsetof(sector_t, SoundTarget)); + + // expose the global validcount variable. + PField *vcf = new PField("validcount", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&validcount); + GlobalSymbols.AddSymbol(vcf); + + // expose the global Multiplayer variable. + PField *multif = new PField("multiplayer", TypeBool, VARF_Native | VARF_ReadOnly | VARF_Static, (intptr_t)&multiplayer); + GlobalSymbols.AddSymbol(multif); + + // set up a variable for the global level data structure + PStruct *lstruct = NewNativeStruct("LevelLocals", nullptr); + PField *levelf = new PField("level", lstruct, VARF_Native | VARF_Static, (intptr_t)&level); + GlobalSymbols.AddSymbol(levelf); + + // set up a variable for the DEH data + PStruct *dstruct = NewNativeStruct("DehInfo", nullptr); + PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh); + + GlobalSymbols.AddSymbol(dehf); + + // set up a variable for the global players array. + PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); + pstruct->Size = sizeof(player_t); + pstruct->Align = alignof(player_t); + PArray *parray = NewArray(pstruct, MAXPLAYERS); + PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players); + GlobalSymbols.AddSymbol(playerf); + + // set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well... + // As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up. + pstruct = NewNativeStruct("Sector", nullptr); + pstruct->AddNativeField("lines", NewPointer(NewArray(NewPointer(NewNativeStruct("line", nullptr), false), 0x40000), false), myoffsetof(sector_t, lines), VARF_Native); + + parray = NewArray(TypeBool, MAXPLAYERS); + playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame); + GlobalSymbols.AddSymbol(playerf); + + playerf = new PField("gameaction", TypeUInt8, VARF_Native | VARF_Static, (intptr_t)&gameaction); + GlobalSymbols.AddSymbol(playerf); + + playerf = new PField("consoleplayer", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&consoleplayer); + GlobalSymbols.AddSymbol(playerf); + + // Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag. + // It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution' + // is to create a static variable from it and reference that in the script. Yuck!!! + static AWeapon *wpnochg = WP_NOCHANGE; + playerf = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg); + GlobalSymbols.AddSymbol(playerf); + + // this needs to be done manually until it can be given a proper type. + RUNTIME_CLASS(AActor)->AddNativeField("DecalGenerator", NewPointer(TypeVoid), myoffsetof(AActor, DecalGenerator)); + + // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. + for (auto &fl : FlagLists) + { + if (fl.Use & 2) + { + for(int i=0;i 0) // skip the deprecated entries in this list + { + const_cast(*fl.Type)->AddNativeField(FStringf("b%s", fl.Defs[i].name), (fl.Defs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), fl.Defs[i].structoffset, fl.Defs[i].varflags, fl.Defs[i].flagbit); + } + } + } + } + + FAutoSegIterator probe(CRegHead, CRegTail); + + while (*++probe != NULL) + { + if (((ClassReg *)*probe)->InitNatives) + ((ClassReg *)*probe)->InitNatives(); + } + // Sort the flag lists for (size_t i = 0; i < NUM_FLAG_LISTS; ++i) { @@ -668,27 +812,56 @@ void InitThingdef() { AFuncDesc *afunc = (AFuncDesc *)*probe; assert(afunc->VMPointer != NULL); - *(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->Name); + *(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->FuncName); + (*(afunc->VMPointer))->PrintableName.Format("%s.%s [Native]", afunc->ClassName+1, afunc->FuncName); AFTable.Push(*afunc); } AFTable.ShrinkToFit(); qsort(&AFTable[0], AFTable.Size(), sizeof(AFTable[0]), funccmp); } - PType *TypeActor = NewPointer(RUNTIME_CLASS(AActor)); - - PStruct *sstruct = NewStruct("Sector", nullptr); - auto sptr = NewPointer(sstruct); - sstruct->AddNativeField("soundtarget", TypeActor, myoffsetof(sector_t, SoundTarget)); - - G_InitLevelLocalsForScript(); - - FAutoSegIterator probe(CRegHead, CRegTail); - - while (*++probe != NULL) + FieldTable.Clear(); + if (FieldTable.Size() == 0) { - if (((ClassReg *)*probe)->InitNatives) - ((ClassReg *)*probe)->InitNatives(); + FAutoSegIterator probe(FRegHead, FRegTail); + + while (*++probe != NULL) + { + FieldDesc *afield = (FieldDesc *)*probe; + FieldTable.Push(*afield); + } + FieldTable.ShrinkToFit(); + qsort(&FieldTable[0], FieldTable.Size(), sizeof(FieldTable[0]), fieldcmp); } } + +DEFINE_ACTION_FUNCTION(DObject, GameType) +{ + PARAM_PROLOGUE; + ACTION_RETURN_INT(gameinfo.gametype); +} + +DEFINE_ACTION_FUNCTION(DObject, BAM) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(ang); + ACTION_RETURN_INT(DAngle(ang).BAMs()); +} + +DEFINE_ACTION_FUNCTION(FStringTable, Localize) +{ + PARAM_PROLOGUE; + PARAM_STRING(label); + ACTION_RETURN_STRING(GStrings(label)); +} + +DEFINE_ACTION_FUNCTION(FString, Replace) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + PARAM_STRING(s1); + PARAM_STRING(s2); + self->Substitute(*s1, *s2); + return 0; +} + diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 7faab937b..9bff91a80 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -39,17 +39,17 @@ */ #include "gi.h" -#include "actor.h" +#include "d_player.h" #include "info.h" #include "tarray.h" #include "w_wad.h" #include "templates.h" #include "r_defs.h" #include "a_pickups.h" +#include "a_armor.h" #include "s_sound.h" #include "cmdlib.h" #include "p_lnspec.h" -#include "a_action.h" #include "decallib.h" #include "m_random.h" #include "i_system.h" @@ -57,8 +57,7 @@ #include "p_effect.h" #include "v_palette.h" #include "doomerrors.h" -#include "a_hexenglobal.h" -#include "a_weaponpiece.h" +#include "a_artifacts.h" #include "p_conversation.h" #include "v_text.h" #include "thingdef.h" @@ -69,15 +68,20 @@ #include "teaminfo.h" #include "v_video.h" #include "r_data/colormaps.h" +#include "a_weaponpiece.h" #include "vmbuilder.h" +#include "a_ammo.h" +#include "a_health.h" +#include "a_keys.h" +extern TArray OptionalClassPtrs; //========================================================================== // // Gets a class pointer and performs an error check for correct type // //========================================================================== -static PClassActor *FindClassTentative(const char *name, PClass *ancestor) +static PClassActor *FindClassTentative(const char *name, PClass *ancestor, bool optional = false) { // "" and "none" mean 'no class' if (name == NULL || *name == 0 || !stricmp(name, "none")) @@ -91,23 +95,27 @@ static PClassActor *FindClassTentative(const char *name, PClass *ancestor) { I_Error("%s does not inherit from %s\n", name, ancestor->TypeName.GetChars()); } + if (cls->Size == TentativeClass && optional) + { + cls->ObjectFlags |= OF_Transient; // since this flag has no meaning in class types, let's use it for marking the type optional. + } return static_cast(cls); } -static AAmmo::MetaClass *FindClassTentativeAmmo(const char *name) +static AAmmo::MetaClass *FindClassTentativeAmmo(const char *name, bool optional = false) { - return static_cast(FindClassTentative(name, RUNTIME_CLASS(AAmmo))); + return static_cast(FindClassTentative(name, RUNTIME_CLASS(AAmmo), optional)); } -static AWeapon::MetaClass *FindClassTentativeWeapon(const char *name) +static AWeapon::MetaClass *FindClassTentativeWeapon(const char *name, bool optional = false) { - return static_cast(FindClassTentative(name, RUNTIME_CLASS(AWeapon))); + return static_cast(FindClassTentative(name, RUNTIME_CLASS(AWeapon), optional)); } -static APowerup::MetaClass *FindClassTentativePowerup(const char *name) +static APowerup::MetaClass *FindClassTentativePowerup(const char *name, bool optional = false) { - return static_cast(FindClassTentative(name, RUNTIME_CLASS(APowerup))); + return static_cast(FindClassTentative(name, RUNTIME_CLASS(APowerup), optional)); } -static APlayerPawn::MetaClass *FindClassTentativePlayerPawn(const char *name) +static APlayerPawn::MetaClass *FindClassTentativePlayerPawn(const char *name, bool optional = false) { - return static_cast(FindClassTentative(name, RUNTIME_CLASS(APlayerPawn))); + return static_cast(FindClassTentative(name, RUNTIME_CLASS(APlayerPawn), optional)); } //========================================================================== @@ -2795,7 +2803,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, morphweapon, S, PlayerPawn) DEFINE_CLASS_PROPERTY_PREFIX(player, flechettetype, S, PlayerPawn) { PROP_STRING_PARM(str, 0); - defaults->FlechetteType = FindClassTentative(str, RUNTIME_CLASS(AArtiPoisonBag)); + defaults->FlechetteType = FindClassTentative(str, PClass::FindActor("ArtiPoisonBag")); } //========================================================================== @@ -2984,21 +2992,23 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, viewbob, F, PlayerPawn) } //========================================================================== -// +// (non-fatal with non-existent types only in DECORATE) //========================================================================== DEFINE_CLASS_PROPERTY(playerclass, S, MorphProjectile) { PROP_STRING_PARM(str, 0); - defaults->PlayerClass = FName(str); + defaults->PlayerClass = FindClassTentativePlayerPawn(str, bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push((PClassActor**)&defaults->PlayerClass); } //========================================================================== -// +// (non-fatal with non-existent types only in DECORATE) //========================================================================== DEFINE_CLASS_PROPERTY(monsterclass, S, MorphProjectile) { PROP_STRING_PARM(str, 0); - defaults->MonsterClass = FName(str); + defaults->MonsterClass = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->MonsterClass); } //========================================================================== @@ -3020,12 +3030,13 @@ DEFINE_CLASS_PROPERTY(morphstyle, M, MorphProjectile) } //========================================================================== -// +// (non-fatal with non-existent types only in DECORATE) //========================================================================== DEFINE_CLASS_PROPERTY(morphflash, S, MorphProjectile) { PROP_STRING_PARM(str, 0); - defaults->MorphFlash = FName(str); + defaults->MorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->MorphFlash); } //========================================================================== @@ -3034,16 +3045,18 @@ DEFINE_CLASS_PROPERTY(morphflash, S, MorphProjectile) DEFINE_CLASS_PROPERTY(unmorphflash, S, MorphProjectile) { PROP_STRING_PARM(str, 0); - defaults->UnMorphFlash = FName(str); + defaults->UnMorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->UnMorphFlash); } //========================================================================== -// +// (non-fatal with non-existent types only in DECORATE) //========================================================================== DEFINE_CLASS_PROPERTY(playerclass, S, PowerMorph) { PROP_STRING_PARM(str, 0); - defaults->PlayerClass = FName(str); + defaults->PlayerClass = FindClassTentativePlayerPawn(str, bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push((PClassActor**)&defaults->PlayerClass); } //========================================================================== @@ -3056,21 +3069,23 @@ DEFINE_CLASS_PROPERTY(morphstyle, M, PowerMorph) } //========================================================================== -// +// (non-fatal with non-existent types only in DECORATE) //========================================================================== DEFINE_CLASS_PROPERTY(morphflash, S, PowerMorph) { PROP_STRING_PARM(str, 0); - defaults->MorphFlash = FName(str); + defaults->MorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->MorphFlash); } //========================================================================== -// +// (non-fatal with non-existent types only in DECORATE) //========================================================================== DEFINE_CLASS_PROPERTY(unmorphflash, S, PowerMorph) { PROP_STRING_PARM(str, 0); - defaults->UnMorphFlash = FName(str); + defaults->UnMorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + if (bag.fromDecorate) OptionalClassPtrs.Push(&defaults->UnMorphFlash); } diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 2f98c8d81..9bd0a4bc9 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -2,9 +2,9 @@ #define VM_H #include "zstring.h" -#include "dobject.h" #include "autosegs.h" #include "vectors.h" +#include "cmdlib.h" #define MAX_RETURNS 8 // Maximum number of results a function called by script code can return #define MAX_TRY_DEPTH 8 // Maximum number of nested TRYs in a single function @@ -112,7 +112,10 @@ enum { CAST_I2F, CAST_I2S, + CAST_U2F, + CAST_U2S, CAST_F2I, + CAST_F2U, CAST_F2S, CAST_P2S, CAST_S2I, @@ -123,6 +126,10 @@ enum CAST_S2So, CAST_Co2S, CAST_So2S, + CAST_V22S, + CAST_V32S, + CAST_SID2S, + CAST_TID2S, }; // Register types for VMParam @@ -153,13 +160,15 @@ enum ATAG_OBJECT, // pointer to an object; will be followed by GC // The following are all for documentation during debugging and are - // functionally no different than ATAG_GENERIC. + // functionally no different than ATAG_GENERIC (meaning they are useless because they trigger asserts all over the place.) + /* ATAG_FRAMEPOINTER, // pointer to extra stack frame space for this function ATAG_DREGISTER, // pointer to a data register ATAG_FREGISTER, // pointer to a float register ATAG_SREGISTER, // pointer to a string register ATAG_AREGISTER, // pointer to an address register + */ ATAG_RNG, // pointer to FRandom ATAG_STATE = ATAG_GENERIC, // pointer to FState (cannot have its own type because there's no means to track inside the VM.) @@ -658,9 +667,10 @@ public: bool Final = false; // cannot be overridden bool Unsafe = false; // Contains references to class fields that are unsafe for psp and item state calls. BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action - int VirtualIndex = -1; + unsigned VirtualIndex = ~0u; FName Name; TArray DefaultArgs; + FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong. class PPrototype *Proto; @@ -808,13 +818,17 @@ public: VM_UBYTE NumRegF; VM_UBYTE NumRegS; VM_UBYTE NumRegA; - VM_UBYTE NumKonstD; - VM_UBYTE NumKonstF; - VM_UBYTE NumKonstS; - VM_UBYTE NumKonstA; + VM_UHALF NumKonstD; + VM_UHALF NumKonstF; + VM_UHALF NumKonstS; + VM_UHALF NumKonstA; VM_UHALF MaxParam; // Maximum number of parameters this function has on the stack at once VM_UBYTE NumArgs; // Number of arguments this function takes - FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong. + TArray SpecialInits; // list of all contents on the extra stack which require construction and destruction + + void InitExtra(void *addr); + void DestroyExtra(void *addr); + int AllocExtraStack(PType *type); }; class VMFrameStack @@ -822,7 +836,6 @@ class VMFrameStack public: VMFrameStack(); ~VMFrameStack(); - VMFrame *AllocFrame(int numregd, int numregf, int numregs, int numrega); VMFrame *AllocFrame(VMScriptFunction *func); VMFrame *PopFrame(); VMFrame *TopFrame() @@ -854,7 +867,7 @@ class VMNativeFunction : public VMFunction { DECLARE_CLASS(VMNativeFunction, VMFunction); public: - typedef int (*NativeCallType)(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret); + typedef int (*NativeCallType)(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret); VMNativeFunction() : NativeCall(NULL) { Native = true; } VMNativeFunction(NativeCallType call) : NativeCall(call) { Native = true; } @@ -917,6 +930,9 @@ enum EVMEngine VMEngine_Checked }; +extern thread_local VMFrameStack GlobalVMStack; + + void VMSelectEngine(EVMEngine engine); extern int (*VMExec)(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret); void VMFillParams(VMValue *params, VMFrame *callee, int numparam); @@ -925,8 +941,8 @@ void VMDumpConstants(FILE *out, const VMScriptFunction *func); void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func); // Use this in the prototype for a native function. -#define VM_ARGS VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret -#define VM_ARGS_NAMES stack, param, defaultparam, numparam, ret, numret +#define VM_ARGS VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret +#define VM_ARGS_NAMES param, defaultparam, numparam, ret, numret // Use these to collect the parameters in a native function. // variable name at position

@@ -946,7 +962,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction #define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); #define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); -#define PARAM_EXISTS(p) ((p) < numparam && param[p].Type != REGT_NIL) +#define PARAM_EXISTS(p) ((p) < numparam) #define ASSERTINT(p) assert((p).Type == REGT_INT) #define ASSERTFLOAT(p) assert((p).Type == REGT_FLOAT) #define ASSERTSTRING(p) assert((p).Type == REGT_STRING) @@ -999,55 +1015,94 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction #define PARAM_OBJECT_DEF(x,type) ++paramnum; PARAM_OBJECT_DEF_AT(paramnum,x,type) #define PARAM_CLASS_DEF(x,base) ++paramnum; PARAM_CLASS_DEF_AT(paramnum,x,base) -typedef int(*actionf_p)(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/ +typedef int(*actionf_p)(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/ + +struct FieldDesc +{ + const char *ClassName; + const char *FieldName; + unsigned FieldOffset; + unsigned FieldSize; + int BitValue; +}; struct AFuncDesc { - const char *Name; + const char *ClassName; + const char *FuncName; actionf_p Function; VMNativeFunction **VMPointer; }; #if defined(_MSC_VER) #pragma section(".areg$u",read) +#pragma section(".freg$u",read) #define MSVC_ASEG __declspec(allocate(".areg$u")) +#define MSVC_FSEG __declspec(allocate(".freg$u")) #define GCC_ASEG +#define GCC_FSEG #else #define MSVC_ASEG +#define MSVC_FSEG #define GCC_ASEG __attribute__((section(SECTION_AREG))) __attribute__((used)) +#define GCC_FSEG __attribute__((section(SECTION_FREG))) __attribute__((used)) #endif // Macros to handle action functions. These are here so that I don't have to // change every single use in case the parameters change. -#define DECLARE_ACTION(name) extern VMNativeFunction *name##_VMPtr; -// This distinction is here so that CALL_ACTION produces errors when trying to -// access a function that requires parameters. #define DEFINE_ACTION_FUNCTION(cls, name) \ - static int AF_##name(VM_ARGS); \ - VMNativeFunction *name##_VMPtr; \ - static const AFuncDesc cls##_##name##_Hook = { #name, AF_##name, &name##_VMPtr }; \ + static int AF_##cls##_##name(VM_ARGS); \ + VMNativeFunction *cls##_##name##_VMPtr; \ + static const AFuncDesc cls##_##name##_Hook = { #cls, #name, AF_##cls##_##name, &cls##_##name##_VMPtr }; \ extern AFuncDesc const *const cls##_##name##_HookPtr; \ MSVC_ASEG AFuncDesc const *const cls##_##name##_HookPtr GCC_ASEG = &cls##_##name##_Hook; \ - static int AF_##name(VM_ARGS) + static int AF_##cls##_##name(VM_ARGS) -#define DEFINE_ACTION_FUNCTION_PARAMS(cls, name) DEFINE_ACTION_FUNCTION(cls, name) +// cls is the scripted class name, icls the internal one (e.g. player_t vs. Player) +#define DEFINE_FIELD_X(cls, icls, name) \ + static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), 0 }; \ + extern FieldDesc const *const VMField_##icls##_##name##_HookPtr; \ + MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##icls##_##name; -//#define DECLARE_PARAMINFO AActor *self, AActor *stateowner, FState *CallingState, int ParameterIndex, StateCallData *statecall -//#define PUSH_PARAMINFO self, stateowner, CallingState, ParameterIndex, statecall +#define DEFINE_FIELD_NAMED_X(cls, icls, name, scriptname) \ + static const FieldDesc VMField_##icls##_##scriptname = { "A" #cls, #scriptname, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), 0 }; \ + extern FieldDesc const *const VMField_##icls##_##scriptname##_HookPtr; \ + MSVC_FSEG FieldDesc const *const VMField_##icls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##icls##_##scriptname; + +#define DEFINE_FIELD_X_BIT(cls, icls, name, bitval) \ + static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), bitval }; \ + extern FieldDesc const *const VMField_##icls##_##name##_HookPtr; \ + MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##cls##_##name; + +#define DEFINE_FIELD(cls, name) \ + static const FieldDesc VMField_##cls##_##name = { #cls, #name, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), 0 }; \ + extern FieldDesc const *const VMField_##cls##_##name##_HookPtr; \ + MSVC_FSEG FieldDesc const *const VMField_##cls##_##name##_HookPtr GCC_FSEG = &VMField_##cls##_##name; + +#define DEFINE_FIELD_NAMED(cls, name, scriptname) \ + static const FieldDesc VMField_##cls##_##scriptname = { #cls, #scriptname, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), 0 }; \ + extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \ + MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname; + +#define DEFINE_FIELD_BIT(cls, name, scriptname, bitval) \ + static const FieldDesc VMField_##cls##_##scriptname = { #cls, #scriptname, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), bitval }; \ + extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \ + MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname; class AActor; -void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self); -#define CALL_ACTION(name, self) CallAction(stack, name##_VMPtr, self); #define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_STATE); return 1; } return 0; } while(0) +#define ACTION_RETURN_POINTER(v) do { void *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_GENERIC); return 1; } return 0; } while(0) #define ACTION_RETURN_OBJECT(v) do { auto state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_OBJECT); return 1; } return 0; } while(0) #define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0) +#define ACTION_RETURN_VEC2(v) do { DVector2 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector2(u); return 1; } return 0; } while(0) #define ACTION_RETURN_VEC3(v) do { DVector3 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector(u); return 1; } return 0; } while(0) #define ACTION_RETURN_INT(v) do { int u = v; if (numret > 0) { assert(ret != NULL); ret->SetInt(u); return 1; } return 0; } while(0) #define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v) +#define ACTION_RETURN_STRING(v) do { FString u = v; if (numret > 0) { assert(ret != NULL); ret->SetString(u); return 1; } return 0; } while(0) // Checks to see what called the current action function #define ACTION_CALL_FROM_ACTOR() (stateinfo == nullptr || stateinfo->mStateType == STATE_Actor) @@ -1071,6 +1126,10 @@ void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self); PARAM_PROLOGUE; \ PARAM_OBJECT(self, type); +// for structs we need to check for ATAG_GENERIC instead of ATAG_OBJECT +#define PARAM_SELF_STRUCT_PROLOGUE(type) \ + PARAM_PROLOGUE; \ + PARAM_POINTER(self, type); class PFunction; diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp index 19d89fc6f..f358fce7b 100644 --- a/src/scripting/vm/vmbuilder.cpp +++ b/src/scripting/vm/vmbuilder.cpp @@ -36,6 +36,19 @@ #include "info.h" #include "m_argv.h" #include "thingdef.h" +#include "doomerrors.h" + +struct VMRemap +{ + BYTE altOp, kReg, kType; +}; + + +#define xx(op, name, mode, alt, kreg, ktype) {OP_##alt, kreg, ktype } +VMRemap opRemap[NUM_OPS] = { +#include "vmops.h" +}; +#undef xx //========================================================================== // @@ -45,10 +58,6 @@ VMFunctionBuilder::VMFunctionBuilder(int numimplicits) { - NumIntConstants = 0; - NumFloatConstants = 0; - NumAddressConstants = 0; - NumStringConstants = 0; MaxParam = 0; ActiveParam = 0; NumImplicits = numimplicits; @@ -74,25 +83,25 @@ VMFunctionBuilder::~VMFunctionBuilder() void VMFunctionBuilder::MakeFunction(VMScriptFunction *func) { - func->Alloc(Code.Size(), NumIntConstants, NumFloatConstants, NumStringConstants, NumAddressConstants); + func->Alloc(Code.Size(), IntConstantList.Size(), FloatConstantList.Size(), StringConstantList.Size(), AddressConstantList.Size()); // Copy code block. memcpy(func->Code, &Code[0], Code.Size() * sizeof(VMOP)); // Create constant tables. - if (NumIntConstants > 0) + if (IntConstantList.Size() > 0) { FillIntConstants(func->KonstD); } - if (NumFloatConstants > 0) + if (FloatConstantList.Size() > 0) { FillFloatConstants(func->KonstF); } - if (NumAddressConstants > 0) + if (AddressConstantList.Size() > 0) { FillAddressConstants(func->KonstA, func->KonstATags()); } - if (NumStringConstants > 0) + if (StringConstantList.Size() > 0) { FillStringConstants(func->KonstS); } @@ -118,13 +127,7 @@ void VMFunctionBuilder::MakeFunction(VMScriptFunction *func) void VMFunctionBuilder::FillIntConstants(int *konst) { - TMapIterator it(IntConstants); - TMap::Pair *pair; - - while (it.NextPair(pair)) - { - konst[pair->Value] = pair->Key; - } + memcpy(konst, &IntConstantList[0], sizeof(int) * IntConstantList.Size()); } //========================================================================== @@ -135,13 +138,7 @@ void VMFunctionBuilder::FillIntConstants(int *konst) void VMFunctionBuilder::FillFloatConstants(double *konst) { - TMapIterator it(FloatConstants); - TMap::Pair *pair; - - while (it.NextPair(pair)) - { - konst[pair->Value] = pair->Key; - } + memcpy(konst, &FloatConstantList[0], sizeof(double) * FloatConstantList.Size()); } //========================================================================== @@ -152,14 +149,8 @@ void VMFunctionBuilder::FillFloatConstants(double *konst) void VMFunctionBuilder::FillAddressConstants(FVoidObj *konst, VM_ATAG *tags) { - TMapIterator it(AddressConstants); - TMap::Pair *pair; - - while (it.NextPair(pair)) - { - konst[pair->Value.KonstNum].v = pair->Key; - tags[pair->Value.KonstNum] = pair->Value.Tag; - } + memcpy(konst, &AddressConstantList[0], sizeof(void*) * AddressConstantList.Size()); + memcpy(tags, &AtagConstantList[0], sizeof(VM_ATAG) * AtagConstantList.Size()); } //========================================================================== @@ -170,12 +161,9 @@ void VMFunctionBuilder::FillAddressConstants(FVoidObj *konst, VM_ATAG *tags) void VMFunctionBuilder::FillStringConstants(FString *konst) { - TMapIterator it(StringConstants); - TMap::Pair *pair; - - while (it.NextPair(pair)) + for (auto &s : StringConstantList) { - konst[pair->Value] = pair->Key; + *konst++ = s; } } @@ -183,22 +171,21 @@ void VMFunctionBuilder::FillStringConstants(FString *konst) // // VMFunctionBuilder :: GetConstantInt // -// Returns a constant register initialized with the given value, or -1 if -// there were no more constants free. +// Returns a constant register initialized with the given value. // //========================================================================== -int VMFunctionBuilder::GetConstantInt(int val) +unsigned VMFunctionBuilder::GetConstantInt(int val) { - int *locp = IntConstants.CheckKey(val); + unsigned int *locp = IntConstantMap.CheckKey(val); if (locp != NULL) { return *locp; } else { - int loc = NumIntConstants++; - IntConstants.Insert(val, loc); + unsigned loc = IntConstantList.Push(val); + IntConstantMap.Insert(val, loc); return loc; } } @@ -207,22 +194,21 @@ int VMFunctionBuilder::GetConstantInt(int val) // // VMFunctionBuilder :: GetConstantFloat // -// Returns a constant register initialized with the given value, or -1 if -// there were no more constants free. +// Returns a constant register initialized with the given value. // //========================================================================== -int VMFunctionBuilder::GetConstantFloat(double val) +unsigned VMFunctionBuilder::GetConstantFloat(double val) { - int *locp = FloatConstants.CheckKey(val); + unsigned *locp = FloatConstantMap.CheckKey(val); if (locp != NULL) { return *locp; } else { - int loc = NumFloatConstants++; - FloatConstants.Insert(val, loc); + unsigned loc = FloatConstantList.Push(val); + FloatConstantMap.Insert(val, loc); return loc; } } @@ -231,22 +217,21 @@ int VMFunctionBuilder::GetConstantFloat(double val) // // VMFunctionBuilder :: GetConstantString // -// Returns a constant register initialized with the given value, or -1 if -// there were no more constants free. +// Returns a constant register initialized with the given value. // //========================================================================== -int VMFunctionBuilder::GetConstantString(FString val) +unsigned VMFunctionBuilder::GetConstantString(FString val) { - int *locp = StringConstants.CheckKey(val); + unsigned *locp = StringConstantMap.CheckKey(val); if (locp != NULL) { return *locp; } else { - int loc = NumStringConstants++; - StringConstants.Insert(val, loc); + int loc = StringConstantList.Push(val); + StringConstantMap.Insert(val, loc); return loc; } } @@ -260,13 +245,13 @@ int VMFunctionBuilder::GetConstantString(FString val) // //========================================================================== -int VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag) +unsigned VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag) { if (ptr == NULL) { // Make all NULL pointers generic. (Or should we allow typed NULLs?) tag = ATAG_GENERIC; } - AddrKonst *locp = AddressConstants.CheckKey(ptr); + AddrKonst *locp = AddressConstantMap.CheckKey(ptr); if (locp != NULL) { // There should only be one tag associated with a memory location. @@ -275,12 +260,71 @@ int VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag) } else { - AddrKonst loc = { NumAddressConstants++, tag }; - AddressConstants.Insert(ptr, loc); + unsigned locc = AddressConstantList.Push(ptr); + AtagConstantList.Push(tag); + + AddrKonst loc = { locc, tag }; + AddressConstantMap.Insert(ptr, loc); return loc.KonstNum; } } +//========================================================================== +// +// VMFunctionBuilder :: AllocConstants* +// +// Returns a range of constant register initialized with the given values. +// +//========================================================================== + +unsigned VMFunctionBuilder::AllocConstantsInt(unsigned count, int *values) +{ + unsigned addr = IntConstantList.Reserve(count); + memcpy(&IntConstantList[addr], values, count * sizeof(int)); + for (unsigned i = 0; i < count; i++) + { + IntConstantMap.Insert(values[i], addr + i); + } + return addr; +} + +unsigned VMFunctionBuilder::AllocConstantsFloat(unsigned count, double *values) +{ + unsigned addr = FloatConstantList.Reserve(count); + memcpy(&FloatConstantList[addr], values, count * sizeof(double)); + for (unsigned i = 0; i < count; i++) + { + FloatConstantMap.Insert(values[i], addr + i); + } + return addr; +} + +unsigned VMFunctionBuilder::AllocConstantsAddress(unsigned count, void **ptrs, VM_ATAG tag) +{ + unsigned addr = AddressConstantList.Reserve(count); + AtagConstantList.Reserve(count); + memcpy(&AddressConstantList[addr], ptrs, count * sizeof(void *)); + for (unsigned i = 0; i < count; i++) + { + AtagConstantList[addr + i] = tag; + AddrKonst loc = { addr+i, tag }; + AddressConstantMap.Insert(ptrs[i], loc); + } + return addr; +} + +unsigned VMFunctionBuilder::AllocConstantsString(unsigned count, FString *ptrs) +{ + unsigned addr = StringConstantList.Reserve(count); + for (unsigned i = 0; i < count; i++) + { + StringConstantList[addr + i] = ptrs[i]; + StringConstantMap.Insert(ptrs[i], addr + i); + } + return addr; +} + + //========================================================================== // // VMFunctionBuilder :: ParamChange @@ -492,10 +536,75 @@ size_t VMFunctionBuilder::GetAddress() size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc) { + static BYTE opcodes[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP }; + assert(opcode >= 0 && opcode < NUM_OPS); - assert(opa >= 0 && opa <= 255); - assert(opb >= 0 && opb <= 255); - assert(opc >= 0 && opc <= 255); + assert(opa >= 0); + assert(opb >= 0); + assert(opc >= 0); + + + // The following were just asserts, meaning this would silently create broken code if there was an overflow + // if this happened in a release build. Not good. + // These are critical errors that need to be reported to the user. + // In addition, the limit of 256 constants can easily be exceeded with arrays so this had to be extended to + // 65535 by adding some checks here that map byte-limited instructions to alternatives that can handle larger indices. + // (See vmops.h for the remapping info.) + + // Note: OP_CMPS also needs treatment, but I do not expect constant overflow to become an issue with strings, so for now there is no handling. + + if (opa > 255) + { + if (opRemap[opcode].kReg != 1 || opa > 32767) + { + I_Error("Register limit exceeded"); + } + int regtype = opRemap[opcode].kType; + ExpEmit emit(this, regtype); + Emit(opcodes[regtype], emit.RegNum, opa); + opcode = opRemap[opcode].altOp; + opa = emit.RegNum; + emit.Free(this); + } + if (opb > 255) + { + if (opRemap[opcode].kReg != 2 || opb > 32767) + { + I_Error("Register limit exceeded"); + } + int regtype = opRemap[opcode].kType; + ExpEmit emit(this, regtype); + Emit(opcodes[regtype], emit.RegNum, opb); + opcode = opRemap[opcode].altOp; + opb = emit.RegNum; + emit.Free(this); + } + if (opc > 255) + { + if (opcode == OP_PARAM && (opb & REGT_KONST) && opc <= 32767) + { + int regtype = opb & REGT_TYPE; + opb = regtype; + ExpEmit emit(this, regtype); + Emit(opcodes[regtype], emit.RegNum, opc); + opc = emit.RegNum; + emit.Free(this); + } + else + { + if (opRemap[opcode].kReg != 4 || opc > 32767) + { + I_Error("Register limit exceeded"); + } + int regtype = opRemap[opcode].kType; + ExpEmit emit(this, regtype); + Emit(opcodes[regtype], emit.RegNum, opc); + opcode = opRemap[opcode].altOp; + opc = emit.RegNum; + emit.Free(this); + } + } + if (opcode == OP_PARAM) { int chg; @@ -670,12 +779,15 @@ VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *c it.PrintableName = name; it.Function = new VMScriptFunction; it.Function->Name = functype->SymbolName; + it.Function->PrintableName = name; it.Function->ImplicitArgs = functype->GetImplicitArgs(); it.Proto = nullptr; it.FromDecorate = fromdecorate; it.StateIndex = stateindex; it.StateCount = statecount; it.Lump = lumpnum; + assert(it.Func->Variants.Size() == 1); + it.Func->Variants[0].Implementation = it.Function; // set prototype for named functions. if (it.Func->SymbolName != NAME_None) @@ -718,6 +830,13 @@ void FFunctionBuildList::Build() FScriptPosition::StrictErrors = !item.FromDecorate; item.Code = item.Code->Resolve(ctx); + // If we need extra space, load the frame pointer into a register so that we do not have to call the wasteful LFP instruction more than once. + if (item.Function->ExtraSpace > 0) + { + buildit.FramePointer = ExpEmit(&buildit, REGT_POINTER); + buildit.FramePointer.Fixed = true; + buildit.Emit(OP_LFP, buildit.FramePointer.RegNum); + } // Make sure resolving it didn't obliterate it. if (item.Code != nullptr) @@ -746,23 +865,30 @@ void FFunctionBuildList::Build() } // Emit code - item.Code->Emit(&buildit); - buildit.MakeFunction(sfunc); - sfunc->NumArgs = 0; - // NumArgs for the VMFunction must be the amount of stack elements, which can differ from the amount of logical function arguments if vectors are in the list. - // For the VM a vector is 2 or 3 args, depending on size. - for (auto s : item.Func->Variants[0].Proto->ArgumentTypes) + try { - sfunc->NumArgs += s->GetRegCount(); - } + item.Code->Emit(&buildit); + buildit.MakeFunction(sfunc); + sfunc->NumArgs = 0; + // NumArgs for the VMFunction must be the amount of stack elements, which can differ from the amount of logical function arguments if vectors are in the list. + // For the VM a vector is 2 or 3 args, depending on size. + for (auto s : item.Func->Variants[0].Proto->ArgumentTypes) + { + sfunc->NumArgs += s->GetRegCount(); + } - if (dump != nullptr) - { - DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len()); - codesize += sfunc->CodeSize; + if (dump != nullptr) + { + DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len()); + codesize += sfunc->CodeSize; + } + sfunc->Unsafe = ctx.Unsafe; + } + catch (CRecoverableError &err) + { + // catch errors from the code generator and pring something meaningful. + item.Code->ScriptPosition.Message(MSG_ERROR, "%s in %s", err.GetMessage(), item.PrintableName.GetChars()); } - sfunc->PrintableName = item.PrintableName; - sfunc->Unsafe = ctx.Unsafe; } delete item.Code; if (dump != nullptr) diff --git a/src/scripting/vm/vmbuilder.h b/src/scripting/vm/vmbuilder.h index 5394dec32..d53393518 100644 --- a/src/scripting/vm/vmbuilder.h +++ b/src/scripting/vm/vmbuilder.h @@ -1,7 +1,23 @@ #ifndef VMUTIL_H #define VMUTIL_H -#include "vm.h" +#include "dobject.h" + +class VMFunctionBuilder; + +struct ExpEmit +{ + ExpEmit() : RegNum(0), RegType(REGT_NIL), RegCount(1), Konst(false), Fixed(false), Final(false), Target(false) {} + ExpEmit(int reg, int type, bool konst = false, bool fixed = false) : RegNum(reg), RegType(type), RegCount(1), Konst(konst), Fixed(fixed), Final(false), Target(false) {} + ExpEmit(VMFunctionBuilder *build, int type, int count = 1); + void Free(VMFunctionBuilder *build); + void Reuse(VMFunctionBuilder *build); + + uint16_t RegNum; + uint8_t RegType, RegCount; + // We are at 8 bytes for this struct, no matter what, so it's rather pointless to squeeze these flags into bitfields. + bool Konst, Fixed, Final, Target; +}; class VMFunctionBuilder { @@ -29,10 +45,16 @@ public: void MakeFunction(VMScriptFunction *func); // Returns the constant register holding the value. - int GetConstantInt(int val); - int GetConstantFloat(double val); - int GetConstantAddress(void *ptr, VM_ATAG tag); - int GetConstantString(FString str); + unsigned GetConstantInt(int val); + unsigned GetConstantFloat(double val); + unsigned GetConstantAddress(void *ptr, VM_ATAG tag); + unsigned GetConstantString(FString str); + + unsigned AllocConstantsInt(unsigned int count, int *values); + unsigned AllocConstantsFloat(unsigned int count, double *values); + unsigned AllocConstantsAddress(unsigned int count, void **ptrs, VM_ATAG tag); + unsigned AllocConstantsString(unsigned int count, FString *ptrs); + // Returns the address of the next instruction to be emitted. size_t GetAddress(); @@ -63,22 +85,26 @@ public: // amount of implicit parameters so that proper code can be emitted for method calls int NumImplicits; + // keep the frame pointer, if needed, in a register because the LFP opcode is hideously inefficient, requiring more than 20 instructions on x64. + ExpEmit FramePointer; + private: struct AddrKonst { - int KonstNum; + unsigned KonstNum; VM_ATAG Tag; }; - // These map from the constant value to its position in the constant table. - TMap IntConstants; - TMap FloatConstants; - TMap AddressConstants; - TMap StringConstants; - int NumIntConstants; - int NumFloatConstants; - int NumAddressConstants; - int NumStringConstants; + TArray IntConstantList; + TArray FloatConstantList; + TArray AddressConstantList; + TArray AtagConstantList; + TArray StringConstantList; + // These map from the constant value to its position in the constant table. + TMap IntConstantMap; + TMap FloatConstantMap; + TMap AddressConstantMap; + TMap StringConstantMap; int MaxParam; int ActiveParam; diff --git a/src/scripting/vm/vmdisasm.cpp b/src/scripting/vm/vmdisasm.cpp index 49c929f05..2158139ab 100644 --- a/src/scripting/vm/vmdisasm.cpp +++ b/src/scripting/vm/vmdisasm.cpp @@ -31,7 +31,7 @@ ** */ -#include "vm.h" +#include "dobject.h" #include "c_console.h" #include "templates.h" @@ -97,11 +97,13 @@ #define RIRIRI MODE_AI | MODE_BI | MODE_CI #define RIRII8 MODE_AI | MODE_BI | MODE_CIMMZ +#define RFRII8 MODE_AF | MODE_BI | MODE_CIMMZ +#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ +#define RSRII8 MODE_AS | MODE_BI | MODE_CIMMZ #define RIRIKI MODE_AI | MODE_BI | MODE_CKI #define RIKIRI MODE_AI | MODE_BKI | MODE_CI #define RIKII8 MODE_AI | MODE_BKI | MODE_CIMMZ #define RIRIIs MODE_AI | MODE_BI | MODE_CIMMS -#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED #define I8RIRI MODE_AIMMZ | MODE_BI | MODE_CI #define I8RIKI MODE_AIMMZ | MODE_BI | MODE_CKI #define I8KIRI MODE_AIMMZ | MODE_BKI | MODE_CI @@ -144,7 +146,7 @@ const VMOpInfo OpInfo[NUM_OPS] = { -#define xx(op, name, mode) { #name, mode } +#define xx(op, name, mode, alt, kreg, ktype) { #name, mode } #include "vmops.h" }; @@ -258,7 +260,6 @@ void VMDumpConstants(FILE *out, const VMScriptFunction *func) void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func) { VMFunction *callfunc; - const char *callname; const char *name; int col; int mode; @@ -288,6 +289,10 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction a &= CMP_CHECK | CMP_APPROX; cmp = true; } + if (code[i].op == OP_PARAM && code[i].b & REGT_ADDROF) + { + name = "parama"; + } if (cmp) { // Comparison instruction. Modify name for inverted test. if (!(a & CMP_CHECK)) @@ -323,15 +328,15 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction case OP_CALL_K: case OP_TAIL_K: + { callfunc = (VMFunction *)func->KonstA[code[i].a].o; - callname = callfunc->Name != NAME_None ? callfunc->Name : "[anonfunc]"; - col = printf_wrapper(out, "%.23s,%d", callname, code[i].b); + col = printf_wrapper(out, "[%p],%d", callfunc, code[i].b); if (code[i].op == OP_CALL_K) { col += printf_wrapper(out, ",%d", code[i].c); } break; - + } case OP_RET: if (code[i].b != REGT_NIL) { @@ -381,18 +386,23 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction switch (code[i].c) { case CAST_I2F: + case CAST_U2F: mode = MODE_AF | MODE_BI | MODE_CUNUSED; break; case CAST_Co2S: case CAST_So2S: case CAST_N2S: case CAST_I2S: + case CAST_U2S: mode = MODE_AS | MODE_BI | MODE_CUNUSED; break; case CAST_F2I: + case CAST_F2U: mode = MODE_AI | MODE_BF | MODE_CUNUSED; break; case CAST_F2S: + case CAST_V22S: + case CAST_V32S: mode = MODE_AS | MODE_BF | MODE_CUNUSED; break; case CAST_P2S: @@ -489,7 +499,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction } else if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K) { - printf_wrapper(out, " [%p]\n", callfunc); + printf_wrapper(out, " [%s]\n", callfunc->PrintableName.GetChars()); } else { diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 57ce48a95..f51038f36 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -34,8 +34,10 @@ #include #include #include -#include "vm.h" +#include "dobject.h" #include "xs_Float.h" +#include "r_state.h" +#include "textures/textures.h" #include "math/cmath.h" #define IMPLEMENT_VMEXEC @@ -73,6 +75,7 @@ #define ASSERTF(x) assert((unsigned)(x) < f->NumRegF) #define ASSERTA(x) assert((unsigned)(x) < f->NumRegA) #define ASSERTS(x) assert((unsigned)(x) < f->NumRegS) +#define ASSERTO(x) assert((unsigned)(x) < f->NumRegA && reg.atag[x] == ATAG_OBJECT) #define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD) #define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF) @@ -143,6 +146,12 @@ VMExec_Checked::Exec #endif ; +// Note: If the VM is being used in multiple threads, this should be declared as thread_local. +// ZDoom doesn't need this at the moment so this is disabled. + +thread_local VMFrameStack GlobalVMStack; + + //=========================================================================== // // VMSelectEngine diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 02e0db3de..225c73cab 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -8,7 +8,7 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret) #if COMPGOTO static const void * const ops[256] = { -#define xx(op,sym,mode) &&op +#define xx(op,sym,mode,alt,kreg,ktype) &&op #include "vmops.h" }; #endif @@ -84,10 +84,36 @@ begin: reg.a[a] = konsta[BC].v; reg.atag[a] = konstatag[BC]; NEXTOP; + + OP(LK_R) : + ASSERTD(a); ASSERTD(B); + reg.d[a] = konstd[reg.d[B] + C]; + NEXTOP; + OP(LKF_R) : + ASSERTF(a); ASSERTD(B); + reg.f[a] = konstf[reg.d[B] + C]; + NEXTOP; + OP(LKS_R) : + ASSERTS(a); ASSERTD(B); + reg.s[a] = konsts[reg.d[B] + C]; + NEXTOP; + OP(LKP_R) : + ASSERTA(a); ASSERTD(B); + b = reg.d[B] + C; + reg.a[a] = konsta[b].v; + reg.atag[a] = konstatag[b]; + NEXTOP; + OP(LFP): ASSERTA(a); assert(sfunc != NULL); assert(sfunc->ExtraSpace > 0); reg.a[a] = f->GetExtra(); - reg.atag[a] = ATAG_FRAMEPOINTER; + reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts. + NEXTOP; + + OP(META): + ASSERTA(a); ASSERTO(B); + reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer... + reg.atag[a] = ATAG_OBJECT; NEXTOP; OP(LB): @@ -175,7 +201,7 @@ begin: OP(LO): ASSERTA(a); ASSERTA(B); ASSERTKD(C); GETADDR(PB,KC,X_READ_NIL); - reg.a[a] = *(DObject **)ptr; + reg.a[a] = GC::ReadBarrier(*(DObject **)ptr); reg.atag[a] = ATAG_OBJECT; NEXTOP; OP(LO_R): @@ -197,7 +223,7 @@ begin: reg.atag[a] = ATAG_GENERIC; NEXTOP; OP(LV2): - ASSERTF(a+2); ASSERTA(B); ASSERTKD(C); + ASSERTF(a+1); ASSERTA(B); ASSERTKD(C); GETADDR(PB,KC,X_READ_NIL); { auto v = (double *)ptr; @@ -206,7 +232,7 @@ begin: } NEXTOP; OP(LV2_R): - ASSERTF(a+2); ASSERTA(B); ASSERTD(C); + ASSERTF(a+1); ASSERTA(B); ASSERTD(C); GETADDR(PB,RC,X_READ_NIL); { auto v = (double *)ptr; @@ -311,7 +337,7 @@ begin: *(void **)ptr = reg.a[B]; NEXTOP; OP(SV2): - ASSERTA(a); ASSERTF(B+2); ASSERTKD(C); + ASSERTA(a); ASSERTF(B+1); ASSERTKD(C); GETADDR(PA,KC,X_WRITE_NIL); { auto v = (double *)ptr; @@ -320,7 +346,7 @@ begin: } NEXTOP; OP(SV2_R): - ASSERTA(a); ASSERTF(B+2); ASSERTD(C); + ASSERTA(a); ASSERTF(B+1); ASSERTD(C); GETADDR(PA,RC,X_WRITE_NIL); { auto v = (double *)ptr; @@ -405,12 +431,6 @@ begin: DoCast(reg, f, a, B, C); } NEXTOP; - OP(DYNCAST_R): - // UNDONE - NEXTOP; - OP(DYNCAST_K): - // UNDONE - NEXTOP; OP(TEST): ASSERTD(a); @@ -461,7 +481,7 @@ begin: break; case REGT_INT | REGT_ADDROF: assert(C < f->NumRegD); - ::new(param) VMValue(®.d[C], ATAG_DREGISTER); + ::new(param) VMValue(®.d[C], ATAG_GENERIC); break; case REGT_INT | REGT_KONST: assert(C < sfunc->NumKonstD); @@ -473,7 +493,7 @@ begin: break; case REGT_STRING | REGT_ADDROF: assert(C < f->NumRegS); - ::new(param) VMValue(®.s[C], ATAG_SREGISTER); + ::new(param) VMValue(®.s[C], ATAG_GENERIC); break; case REGT_STRING | REGT_KONST: assert(C < sfunc->NumKonstS); @@ -485,7 +505,7 @@ begin: break; case REGT_POINTER | REGT_ADDROF: assert(C < f->NumRegA); - ::new(param) VMValue(®.a[C], ATAG_AREGISTER); + ::new(param) VMValue(®.a[C], ATAG_GENERIC); break; case REGT_POINTER | REGT_KONST: assert(C < sfunc->NumKonstA); @@ -512,7 +532,7 @@ begin: break; case REGT_FLOAT | REGT_ADDROF: assert(C < f->NumRegF); - ::new(param) VMValue(®.f[C], ATAG_FREGISTER); + ::new(param) VMValue(®.f[C], ATAG_GENERIC); break; case REGT_FLOAT | REGT_KONST: assert(C < sfunc->NumKonstF); @@ -553,7 +573,7 @@ begin: FillReturns(reg, f, returns, pc+1, C); if (call->Native) { - numret = static_cast(call)->NativeCall(stack, reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C); + numret = static_cast(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C); } else { @@ -597,7 +617,7 @@ begin: if (call->Native) { - return static_cast(call)->NativeCall(stack, reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret); + return static_cast(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret); } else { // FIXME: Not a true tail call @@ -693,25 +713,39 @@ begin: assert(0); NEXTOP; + // Fixme: This really needs to throw something more informative than a number. Printing the message here instead of passing it to the exception is not sufficient. OP(BOUND): if (reg.d[a] >= BC) { + assert(false); + Printf("Array access out of bounds: Max. index = %u, current index = %u\n", BC, reg.d[a]); + THROW(X_ARRAY_OUT_OF_BOUNDS); + } + NEXTOP; + + OP(BOUND_K): + ASSERTKD(BC); + if (reg.d[a] >= konstd[BC]) + { + assert(false); + Printf("Array access out of bounds: Max. index = %u, current index = %u\n", konstd[BC], reg.d[a]); + THROW(X_ARRAY_OUT_OF_BOUNDS); + } + NEXTOP; + + OP(BOUND_R): + ASSERTD(B); + if (reg.d[a] >= reg.d[B]) + { + assert(false); + Printf("Array access out of bounds: Max. index = %u, current index = %u\n", reg.d[B], reg.d[a]); THROW(X_ARRAY_OUT_OF_BOUNDS); } NEXTOP; OP(CONCAT): ASSERTS(a); ASSERTS(B); ASSERTS(C); - { - FString *rB = ®.s[B]; - FString *rC = ®.s[C]; - FString concat(*rB); - for (++rB; rB <= rC; ++rB) - { - concat += *rB; - } - reg.s[a] = concat; - } + reg.s[a] = reg.s[B] + reg.s[C]; NEXTOP; OP(LENS): ASSERTD(a); ASSERTS(B); @@ -1302,42 +1336,23 @@ begin: OP(ADDV2_RR): ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C+1); fcp = ®.f[C]; - Do_ADDV2: fbp = ®.f[B]; reg.f[a] = fbp[0] + fcp[0]; reg.f[a+1] = fbp[1] + fcp[1]; NEXTOP; - OP(ADDV2_RK): - fcp = &konstf[C]; - goto Do_ADDV2; OP(SUBV2_RR): ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C+1); fbp = ®.f[B]; fcp = ®.f[C]; - Do_SUBV2: reg.f[a] = fbp[0] - fcp[0]; reg.f[a+1] = fbp[1] - fcp[1]; NEXTOP; - OP(SUBV2_RK): - ASSERTF(a+1); ASSERTF(B+1); ASSERTKF(C+1); - fbp = ®.f[B]; - fcp = &konstf[C]; - goto Do_SUBV2; - OP(SUBV2_KR): - ASSERTF(A+1); ASSERTKF(B+1); ASSERTF(C+1); - fbp = &konstf[B]; - fcp = ®.f[C]; - goto Do_SUBV2; OP(DOTV2_RR): ASSERTF(a); ASSERTF(B+1); ASSERTF(C+1); reg.f[a] = reg.f[B] * reg.f[C] + reg.f[B+1] * reg.f[C+1]; NEXTOP; - OP(DOTV2_RK): - ASSERTF(a); ASSERTF(B+1); ASSERTKF(C+1); - reg.f[a] = reg.f[B] * konstf[C] + reg.f[B+1] * konstf[C+1]; - NEXTOP; OP(MULVF2_RR): ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C); @@ -1401,50 +1416,30 @@ begin: OP(ADDV3_RR): ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2); fcp = ®.f[C]; - Do_ADDV3: fbp = ®.f[B]; reg.f[a] = fbp[0] + fcp[0]; reg.f[a+1] = fbp[1] + fcp[1]; reg.f[a+2] = fbp[2] + fcp[2]; NEXTOP; - OP(ADDV3_RK): - fcp = &konstf[C]; - goto Do_ADDV3; OP(SUBV3_RR): ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2); fbp = ®.f[B]; fcp = ®.f[C]; - Do_SUBV3: reg.f[a] = fbp[0] - fcp[0]; reg.f[a+1] = fbp[1] - fcp[1]; reg.f[a+2] = fbp[2] - fcp[2]; NEXTOP; - OP(SUBV3_RK): - ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C+2); - fbp = ®.f[B]; - fcp = &konstf[C]; - goto Do_SUBV3; - OP(SUBV3_KR): - ASSERTF(A+2); ASSERTKF(B+2); ASSERTF(C+2); - fbp = &konstf[B]; - fcp = ®.f[C]; - goto Do_SUBV3; OP(DOTV3_RR): ASSERTF(a); ASSERTF(B+2); ASSERTF(C+2); reg.f[a] = reg.f[B] * reg.f[C] + reg.f[B+1] * reg.f[C+1] + reg.f[B+2] * reg.f[C+2]; NEXTOP; - OP(DOTV3_RK): - ASSERTF(a); ASSERTF(B+2); ASSERTKF(C+2); - reg.f[a] = reg.f[B] * konstf[C] + reg.f[B+1] * konstf[C+1] + reg.f[B+2] * konstf[C+2]; - NEXTOP; OP(CROSSV_RR): ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2); fbp = ®.f[B]; fcp = ®.f[C]; - Do_CROSSV: { double t[3]; t[2] = fbp[0] * fcp[1] - fbp[1] * fcp[0]; @@ -1453,16 +1448,6 @@ begin: reg.f[a] = t[0]; reg.f[a+1] = t[1]; reg.f[a+2] = t[2]; } NEXTOP; - OP(CROSSV_RK): - ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C+2); - fbp = ®.f[B]; - fcp = &konstf[C]; - goto Do_CROSSV; - OP(CROSSV_KR): - ASSERTF(a+2); ASSERTKF(B+2); ASSERTF(C+2); - fbp = ®.f[B]; - fcp = &konstf[C]; - goto Do_CROSSV; OP(MULVF3_RR): ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C); @@ -1656,23 +1641,43 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c ASSERTF(a); ASSERTD(b); reg.f[a] = reg.d[b]; break; + case CAST_U2F: + ASSERTF(a); ASSERTD(b); + reg.f[a] = unsigned(reg.d[b]); + break; case CAST_I2S: ASSERTS(a); ASSERTD(b); reg.s[a].Format("%d", reg.d[b]); break; + case CAST_U2S: + ASSERTS(a); ASSERTD(b); + reg.s[a].Format("%u", reg.d[b]); + break; case CAST_F2I: ASSERTD(a); ASSERTF(b); reg.d[a] = (int)reg.f[b]; break; + case CAST_F2U: + ASSERTD(a); ASSERTF(b); + reg.d[a] = (int)(unsigned)reg.f[b]; + break; case CAST_F2S: - ASSERTS(a); ASSERTD(b); - reg.s[a].Format("%.14g", reg.f[b]); + ASSERTS(a); ASSERTF(b); + reg.s[a].Format("%.5f", reg.f[b]); // keep this small. For more precise conversion there should be a conversion function. + break; + case CAST_V22S: + ASSERTS(a); ASSERTF(b+1); + reg.s[a].Format("(%.5f, %.5f)", reg.f[b], reg.f[b + 1]); + break; + case CAST_V32S: + ASSERTS(a); ASSERTF(b + 2); + reg.s[a].Format("(%.5f, %.5f, %.5f)", reg.f[b], reg.f[b + 1], reg.f[b + 2]); break; case CAST_P2S: ASSERTS(a); ASSERTA(b); - reg.s[a].Format("%s<%p>", reg.atag[b] == ATAG_OBJECT ? "Object" : "Pointer", reg.a[b]); + reg.s[a].Format("%s<%p>", reg.atag[b] == ATAG_OBJECT ? (reg.a[b] == nullptr? "Object" : ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars() ) : "Pointer", reg.a[b]); break; case CAST_S2I: @@ -1717,6 +1722,19 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c reg.s[a] = S_sfx[reg.d[b]].name; break; + case CAST_SID2S: + ASSERTS(a); ASSERTD(b); + reg.s[a] = unsigned(reg.d[b]) >= sprites.Size() ? "TNT1" : sprites[reg.d[b]].name; + break; + + case CAST_TID2S: + { + ASSERTS(a); ASSERTD(b); + auto tex = TexMan[*(FTextureID*)&(reg.d[b])]; + reg.s[a] = tex == nullptr ? "(null)" : tex->Name.GetChars(); + break; + } + default: assert(0); } diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 3a593861c..d3ce5d5f9 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -32,17 +32,17 @@ */ #include -#include "vm.h" +#include "dobject.h" -IMPLEMENT_CLASS(VMException, false, false, false, false) -IMPLEMENT_CLASS(VMFunction, true, true, false, false) +IMPLEMENT_CLASS(VMException, false, false) +IMPLEMENT_CLASS(VMFunction, true, true) IMPLEMENT_POINTERS_START(VMFunction) IMPLEMENT_POINTER(Proto) IMPLEMENT_POINTERS_END -IMPLEMENT_CLASS(VMScriptFunction, false, false, false, false) -IMPLEMENT_CLASS(VMNativeFunction, false, false, false, false) +IMPLEMENT_CLASS(VMScriptFunction, false, false) +IMPLEMENT_CLASS(VMNativeFunction, false, false) VMScriptFunction::VMScriptFunction(FName name) { @@ -86,10 +86,10 @@ void VMScriptFunction::Alloc(int numops, int numkonstd, int numkonstf, int numko { assert(Code == NULL); assert(numops > 0); - assert(numkonstd >= 0 && numkonstd <= 255); - assert(numkonstf >= 0 && numkonstf <= 255); - assert(numkonsts >= 0 && numkonsts <= 255); - assert(numkonsta >= 0 && numkonsta <= 255); + assert(numkonstd >= 0 && numkonstd <= 65535); + assert(numkonstf >= 0 && numkonstf <= 65535); + assert(numkonsts >= 0 && numkonsts <= 65535); + assert(numkonsta >= 0 && numkonsta <= 65535); void *mem = M_Malloc(numops * sizeof(VMOP) + numkonstd * sizeof(int) + numkonstf * sizeof(double) + @@ -162,6 +162,34 @@ size_t VMScriptFunction::PropagateMark() return NumKonstA * sizeof(void *) + Super::PropagateMark(); } +void VMScriptFunction::InitExtra(void *addr) +{ + char *caddr = (char*)addr; + + for (auto tao : SpecialInits) + { + tao.first->InitializeValue(caddr + tao.second, nullptr); + } +} + +void VMScriptFunction::DestroyExtra(void *addr) +{ + char *caddr = (char*)addr; + + for (auto tao : SpecialInits) + { + tao.first->DestroyValue(caddr + tao.second); + } +} + +int VMScriptFunction::AllocExtraStack(PType *type) +{ + int address = ((ExtraSpace + type->Align - 1) / type->Align) * type->Align; + ExtraSpace = address + type->Size; + type->SetDefaultValue(nullptr, address, &SpecialInits); + return address; +} + //=========================================================================== // // VMFrame :: InitRegS @@ -223,34 +251,6 @@ VMFrameStack::~VMFrameStack() UnusedBlocks = NULL; } -//=========================================================================== -// -// VMFrameStack :: AllocFrame -// -// Allocates a frame from the stack with the desired number of registers. -// -//=========================================================================== - -VMFrame *VMFrameStack::AllocFrame(int numregd, int numregf, int numregs, int numrega) -{ - assert((unsigned)numregd < 255); - assert((unsigned)numregf < 255); - assert((unsigned)numregs < 255); - assert((unsigned)numrega < 255); - // To keep the arguments to this function simpler, it assumes that every - // register might be used as a parameter for a single call. - int numparam = numregd + numregf + numregs + numrega; - int size = VMFrame::FrameSize(numregd, numregf, numregs, numrega, numparam, 0); - VMFrame *frame = Alloc(size); - frame->NumRegD = numregd; - frame->NumRegF = numregf; - frame->NumRegS = numregs; - frame->NumRegA = numrega; - frame->MaxParam = numparam; - frame->InitRegS(); - return frame; -} - //=========================================================================== // // VMFrameStack :: AllocFrame @@ -273,6 +273,10 @@ VMFrame *VMFrameStack::AllocFrame(VMScriptFunction *func) frame->MaxParam = func->MaxParam; frame->Func = func; frame->InitRegS(); + if (func->SpecialInits.Size()) + { + func->InitExtra(frame->GetExtra()); + } return frame; } @@ -358,6 +362,11 @@ VMFrame *VMFrameStack::PopFrame() { return NULL; } + auto Func = static_cast(frame->Func); + if (Func->SpecialInits.Size()) + { + Func->DestroyExtra(frame->GetExtra()); + } // Free any string registers this frame had. FString *regs = frame->GetRegS(); for (int i = frame->NumRegS; i != 0; --i) @@ -413,12 +422,13 @@ VMFrame *VMFrameStack::PopFrame() int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap) { + assert(this == VMGlobalStack); // why would anyone even want to create a local stack? bool allocated = false; try - { + { if (func->Native) { - return static_cast(func)->NativeCall(this, params, func->DefaultArgs, numparams, results, numresults); + return static_cast(func)->NativeCall(params, func->DefaultArgs, numparams, results, numresults); } else { @@ -494,11 +504,3 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur throw; } } - -class AActor; -void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self) -{ - // Without the type cast this picks the 'void *' assignment... - VMValue params[3] = { (DObject*)self, (DObject*)self, VMValue(nullptr, ATAG_GENERIC) }; - stack->Call(vmfunc, params, vmfunc->ImplicitArgs, nullptr, 0, nullptr); -} diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 368f143bd..e5b6d34eb 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -1,247 +1,250 @@ #ifndef xx -#define xx(op, name, mode) OP_##op +#define xx(op, name, mode, alt, kreg, ktype) OP_##op #endif -xx(NOP, nop, NOP), // no operation +// first row is the opcode +// second row is the disassembly name +// third row is the disassembly flags +// fourth row is the alternative opcode if all 256 constant registers are exhausted. +// fifth row is the constant register index in the opcode +// sixth row is the constant register type. +// OP_PARAM and OP_CMPS need special treatment because they encode this information in the instruction. + +xx(NOP, nop, NOP, NOP, 0, 0), // no operation // Load constants. -xx(LI, li, LI), // load immediate signed 16-bit constant -xx(LK, lk, LKI), // load integer constant -xx(LKF, lk, LKF), // load float constant -xx(LKS, lk, LKS), // load string constant -xx(LKP, lk, LKP), // load pointer constant -xx(LFP, lf, LFP), // load frame pointer +xx(LI, li, LI, NOP, 0, 0), // load immediate signed 16-bit constant +xx(LK, lk, LKI, NOP, 0, 0), // load integer constant +xx(LKF, lk, LKF, NOP, 0, 0), // load float constant +xx(LKS, lk, LKS, NOP, 0, 0), // load string constant +xx(LKP, lk, LKP, NOP, 0, 0), // load pointer constant +xx(LK_R, lk, RIRII8, NOP, 0, 0), // load integer constant indexed +xx(LKF_R, lk, RFRII8, NOP, 0, 0), // load float constant indexed +xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed +xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed +xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer +xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta class address // Load from memory. rA = *(rB + rkC) -xx(LB, lb, RIRPKI), // load byte -xx(LB_R, lb, RIRPRI), -xx(LH, lh, RIRPKI), // load halfword -xx(LH_R, lh, RIRPRI), -xx(LW, lw, RIRPKI), // load word -xx(LW_R, lw, RIRPRI), -xx(LBU, lbu, RIRPKI), // load byte unsigned -xx(LBU_R, lbu, RIRPRI), -xx(LHU, lhu, RIRPKI), // load halfword unsigned -xx(LHU_R, lhu, RIRPRI), -xx(LSP, lsp, RFRPKI), // load single-precision fp -xx(LSP_R, lsp, RFRPRI), -xx(LDP, ldp, RFRPKI), // load double-precision fp -xx(LDP_R, ldp, RFRPRI), -xx(LS, ls, RSRPKI), // load string -xx(LS_R, ls, RSRPRI), -xx(LO, lo, RPRPKI), // load object -xx(LO_R, lo, RPRPRI), -xx(LP, lp, RPRPKI), // load pointer -xx(LP_R, lp, RPRPRI), -xx(LV2, lv2, RVRPKI), // load vector2 -xx(LV2_R, lv2, RVRPRI), -xx(LV3, lv3, RVRPKI), // load vector3 -xx(LV3_R, lv3, RVRPRI), +xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte +xx(LB_R, lb, RIRPRI, NOP, 0, 0), +xx(LH, lh, RIRPKI, LH_R, 4, REGT_INT), // load halfword +xx(LH_R, lh, RIRPRI, NOP, 0, 0), +xx(LW, lw, RIRPKI, LW_R, 4, REGT_INT), // load word +xx(LW_R, lw, RIRPRI, NOP, 0, 0), +xx(LBU, lbu, RIRPKI, LBU_R, 4, REGT_INT), // load byte unsigned +xx(LBU_R, lbu, RIRPRI, NOP, 0, 0), +xx(LHU, lhu, RIRPKI, LHU_R, 4, REGT_INT), // load halfword unsigned +xx(LHU_R, lhu, RIRPRI, NOP, 0, 0), +xx(LSP, lsp, RFRPKI, LSP_R, 4, REGT_INT), // load single-precision fp +xx(LSP_R, lsp, RFRPRI, NOP, 0, 0), +xx(LDP, ldp, RFRPKI, LDP_R, 4, REGT_INT), // load double-precision fp +xx(LDP_R, ldp, RFRPRI, NOP, 0, 0), +xx(LS, ls, RSRPKI, LS_R, 4, REGT_INT), // load string +xx(LS_R, ls, RSRPRI, NOP, 0, 0), +xx(LO, lo, RPRPKI, LO_R, 4, REGT_INT), // load object +xx(LO_R, lo, RPRPRI, NOP, 0, 0), +xx(LP, lp, RPRPKI, LP_R, 4, REGT_INT), // load pointer +xx(LP_R, lp, RPRPRI, NOP, 0, 0), +xx(LV2, lv2, RVRPKI, LV2_R, 4, REGT_INT), // load vector2 +xx(LV2_R, lv2, RVRPRI, NOP, 0, 0), +xx(LV3, lv3, RVRPKI, LV3_R, 4, REGT_INT), // load vector3 +xx(LV3_R, lv3, RVRPRI, NOP, 0, 0), -xx(LBIT, lbit, RIRPI8), // rA = !!(*rB & C) -- *rB is a byte +xx(LBIT, lbit, RIRPI8, NOP, 0, 0), // rA = !!(*rB & C) -- *rB is a byte // Store instructions. *(rA + rkC) = rB -xx(SB, sb, RPRIKI), // store byte -xx(SB_R, sb, RPRIRI), -xx(SH, sh, RPRIKI), // store halfword -xx(SH_R, sh, RPRIRI), -xx(SW, sw, RPRIKI), // store word -xx(SW_R, sw, RPRIRI), -xx(SSP, ssp, RPRFKI), // store single-precision fp -xx(SSP_R, ssp, RPRFRI), -xx(SDP, sdp, RPRFKI), // store double-precision fp -xx(SDP_R, sdp, RPRFRI), -xx(SS, ss, RPRSKI), // store string -xx(SS_R, ss, RPRSRI), -xx(SP, sp, RPRPKI), // store pointer -xx(SP_R, sp, RPRPRI), -xx(SV2, sv2, RPRVKI), // store vector2 -xx(SV2_R, sv2, RPRVRI), -xx(SV3, sv3, RPRVKI), // store vector3 -xx(SV3_R, sv3, RPRVRI), +xx(SB, sb, RPRIKI, SB_R, 4, REGT_INT), // store byte +xx(SB_R, sb, RPRIRI, NOP, 0, 0), +xx(SH, sh, RPRIKI, SH_R, 4, REGT_INT), // store halfword +xx(SH_R, sh, RPRIRI, NOP, 0, 0), +xx(SW, sw, RPRIKI, SW_R, 4, REGT_INT), // store word +xx(SW_R, sw, RPRIRI, NOP, 0, 0), +xx(SSP, ssp, RPRFKI, SSP_R, 4, REGT_INT), // store single-precision fp +xx(SSP_R, ssp, RPRFRI, NOP, 0, 0), +xx(SDP, sdp, RPRFKI, SDP_R, 4, REGT_INT), // store double-precision fp +xx(SDP_R, sdp, RPRFRI, NOP, 0, 0), +xx(SS, ss, RPRSKI, SS_R, 4, REGT_INT), // store string +xx(SS_R, ss, RPRSRI, NOP, 0, 0), +xx(SP, sp, RPRPKI, SP_R, 4, REGT_INT), // store pointer +xx(SP_R, sp, RPRPRI, NOP, 0, 0), +xx(SV2, sv2, RPRVKI, SV2_R, 4, REGT_INT), // store vector2 +xx(SV2_R, sv2, RPRVRI, NOP, 0, 0), +xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT), // store vector3 +xx(SV3_R, sv3, RPRVRI, NOP, 0, 0), -xx(SBIT, sbit, RPRII8), // *rA |= C if rB is true, *rA &= ~C otherwise +xx(SBIT, sbit, RPRII8, NOP, 0, 0), // *rA |= C if rB is true, *rA &= ~C otherwise // Move instructions. -xx(MOVE, mov, RIRI), // dA = dB -xx(MOVEF, mov, RFRF), // fA = fB -xx(MOVES, mov, RSRS), // sA = sB -xx(MOVEA, mov, RPRP), // aA = aB -xx(MOVEV2, mov2, RFRF), // fA = fB (2 elements) -xx(MOVEV3, mov3, RFRF), // fA = fB (3 elements) -xx(CAST, cast, CAST), // xA = xB, conversion specified by C -xx(DYNCAST_R, dyncast,RPRPRP), // aA = aB after casting to rkC (specifying a class) -xx(DYNCAST_K, dyncast,RPRPKP), +xx(MOVE, mov, RIRI, NOP, 0, 0), // dA = dB +xx(MOVEF, mov, RFRF, NOP, 0, 0), // fA = fB +xx(MOVES, mov, RSRS, NOP, 0, 0), // sA = sB +xx(MOVEA, mov, RPRP, NOP, 0, 0), // aA = aB +xx(MOVEV2, mov2, RFRF, NOP, 0, 0), // fA = fB (2 elements) +xx(MOVEV3, mov3, RFRF, NOP, 0, 0), // fA = fB (3 elements) +xx(CAST, cast, CAST, NOP, 0, 0), // xA = xB, conversion specified by C // Control flow. -xx(TEST, test, RII16), // if (dA != BC) then pc++ -xx(TESTN, testn, RII16), // if (dA != -BC) then pc++ -xx(JMP, jmp, I24), // pc += ABC -- The ABC fields contain a signed 24-bit offset. -xx(IJMP, ijmp, RII16), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP. -xx(PARAM, param, __BCP), // push parameter encoded in BC for function call (B=regtype, C=regnum) -xx(PARAMI, parami, I24), // push immediate, signed integer for function call -xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C -xx(CALL_K, call, KPI8I8), -xx(VTBL, vtbl, RPRPI8), // dereferences a virtual method table. -xx(TAIL, tail, RPI8), // Call+Ret in a single instruction -xx(TAIL_K, tail, KPI8), -xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL) -xx(RET, ret, I8BCP), // Copy value from register encoded in BC to return value A, possibly returning -xx(RETI, reti, I8I16), // Copy immediate from BC to return value A, possibly returning -xx(TRY, try, I24), // When an exception is thrown, start searching for a handler at pc + ABC -xx(UNTRY, untry, I8), // Pop A entries off the exception stack -xx(THROW, throw, THROW), // A == 0: Throw exception object pB - // A == 1: Throw exception object pkB - // A >= 2: Throw VM exception of type BC -xx(CATCH, catch, CATCH), // A == 0: continue search on next try - // A == 1: continue execution at instruction immediately following CATCH (catches any exception) - // A == 2: (pB == ) then pc++ ; next instruction must JMP to another CATCH - // A == 3: (pkB == ) then pc++ ; next instruction must JMP to another CATCH - // for A > 0, exception is stored in pC -xx(BOUND, bound, RII16), // if rA >= BC, throw exception +xx(TEST, test, RII16, NOP, 0, 0), // if (dA != BC) then pc++ +xx(TESTN, testn, RII16, NOP, 0, 0), // if (dA != -BC) then pc++ +xx(JMP, jmp, I24, NOP, 0, 0), // pc += ABC -- The ABC fields contain a signed 24-bit offset. +xx(IJMP, ijmp, RII16, NOP, 0, 0), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP. +xx(PARAM, param, __BCP, NOP, 0, 0), // push parameter encoded in BC for function call (B=regtype, C=regnum) +xx(PARAMI, parami, I24, NOP, 0, 0), // push immediate, signed integer for function call +xx(CALL, call, RPI8I8, NOP, 0, 0), // Call function pkA with parameter count B and expected result count C +xx(CALL_K, call, KPI8I8, CALL, 1, REGT_POINTER), +xx(VTBL, vtbl, RPRPI8, NOP, 0, 0), // dereferences a virtual method table. +xx(TAIL, tail, RPI8, NOP, 0, 0), // Call+Ret in a single instruction +xx(TAIL_K, tail, KPI8, TAIL, 1, REGT_POINTER), +xx(RESULT, result, __BCP, NOP, 0, 0), // Result should go in register encoded in BC (in caller, after CALL) +xx(RET, ret, I8BCP, NOP, 0, 0), // Copy value from register encoded in BC to return value A, possibly returning +xx(RETI, reti, I8I16, NOP, 0, 0), // Copy immediate from BC to return value A, possibly returning +xx(TRY, try, I24, NOP, 0, 0), // When an exception is thrown, start searching for a handler at pc + ABC +xx(UNTRY, untry, I8, NOP, 0, 0), // Pop A entries off the exception stack +xx(THROW, throw, THROW, NOP, 0, 0), // A == 0: Throw exception object pB + // A == 1: Throw exception object pkB + // A >= 2: Throw VM exception of type BC +xx(CATCH, catch, CATCH, NOP, 0, 0), // A == 0: continue search on next try + // A == 1: continue execution at instruction immediately following CATCH (catches any exception) + // A == 2: (pB == ) then pc++ ; next instruction must JMP to another CATCH + // A == 3: (pkB == ) then pc++ ; next instruction must JMP to another CATCH + // for A > 0, exception is stored in pC +xx(BOUND, bound, RII16, NOP, 0, 0), // if rA >= BC, throw exception +xx(BOUND_K, bound, LKI, NOP, 0, 0), // if rA >= const[BC], throw exception +xx(BOUND_R, bound, RIRI, NOP, 0, 0), // if rA >= rB, throw exception // String instructions. -xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC -xx(LENS, lens, RIRS), // dA = sB.Length -xx(CMPS, cmps, I8RXRX), // if ((skB op skC) != (A & 1)) then pc++ +xx(CONCAT, concat, RSRSRS, NOP, 0, 0), // sA = sB..sC +xx(LENS, lens, RIRS, NOP, 0, 0), // dA = sB.Length +xx(CMPS, cmps, I8RXRX, NOP, 0, 0), // if ((skB op skC) != (A & 1)) then pc++ // Integer math. -xx(SLL_RR, sll, RIRIRI), // dA = dkB << diC -xx(SLL_RI, sll, RIRII8), -xx(SLL_KR, sll, RIKIRI), -xx(SRL_RR, srl, RIRIRI), // dA = dkB >> diC -- unsigned -xx(SRL_RI, srl, RIRII8), -xx(SRL_KR, srl, RIKIRI), -xx(SRA_RR, sra, RIRIRI), // dA = dkB >> diC -- signed -xx(SRA_RI, sra, RIRII8), -xx(SRA_KR, sra, RIKIRI), -xx(ADD_RR, add, RIRIRI), // dA = dB + dkC -xx(ADD_RK, add, RIRIKI), -xx(ADDI, addi, RIRIIs), // dA = dB + C -- C is a signed 8-bit constant -xx(SUB_RR, sub, RIRIRI), // dA = dkB - dkC -xx(SUB_RK, sub, RIRIKI), -xx(SUB_KR, sub, RIKIRI), -xx(MUL_RR, mul, RIRIRI), // dA = dB * dkC -xx(MUL_RK, mul, RIRIKI), -xx(DIV_RR, div, RIRIRI), // dA = dkB / dkC (signed) -xx(DIV_RK, div, RIRIKI), -xx(DIV_KR, div, RIKIRI), -xx(DIVU_RR, divu, RIRIRI), // dA = dkB / dkC (unsigned) -xx(DIVU_RK, divu, RIRIKI), -xx(DIVU_KR, divu, RIKIRI), -xx(MOD_RR, mod, RIRIRI), // dA = dkB % dkC (signed) -xx(MOD_RK, mod, RIRIKI), -xx(MOD_KR, mod, RIKIRI), -xx(MODU_RR, modu, RIRIRI), // dA = dkB % dkC (unsigned) -xx(MODU_RK, modu, RIRIKI), -xx(MODU_KR, modu, RIKIRI), -xx(AND_RR, and, RIRIRI), // dA = dB & dkC -xx(AND_RK, and, RIRIKI), -xx(OR_RR, or, RIRIRI), // dA = dB | dkC -xx(OR_RK, or, RIRIKI), -xx(XOR_RR, xor, RIRIRI), // dA = dB ^ dkC -xx(XOR_RK, xor, RIRIKI), -xx(MIN_RR, min, RIRIRI), // dA = min(dB,dkC) -xx(MIN_RK, min, RIRIKI), -xx(MAX_RR, max, RIRIRI), // dA = max(dB,dkC) -xx(MAX_RK, max, RIRIKI), -xx(ABS, abs, RIRI), // dA = abs(dB) -xx(NEG, neg, RIRI), // dA = -dB -xx(NOT, not, RIRI), // dA = ~dB -xx(SEXT, sext, RIRII8), // dA = dB, sign extended by shifting left then right by C -xx(ZAP_R, zap, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are one -xx(ZAP_I, zap, RIRII8), -xx(ZAPNOT_R, zapnot, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are zero -xx(ZAPNOT_I, zapnot, RIRII8), -xx(EQ_R, beq, CIRR), // if ((dB == dkC) != A) then pc++ -xx(EQ_K, beq, CIRK), -xx(LT_RR, blt, CIRR), // if ((dkB < dkC) != A) then pc++ -xx(LT_RK, blt, CIRK), -xx(LT_KR, blt, CIKR), -xx(LE_RR, ble, CIRR), // if ((dkB <= dkC) != A) then pc++ -xx(LE_RK, ble, CIRK), -xx(LE_KR, ble, CIKR), -xx(LTU_RR, bltu, CIRR), // if ((dkB < dkC) != A) then pc++ -- unsigned -xx(LTU_RK, bltu, CIRK), -xx(LTU_KR, bltu, CIKR), -xx(LEU_RR, bleu, CIRR), // if ((dkB <= dkC) != A) then pc++ -- unsigned -xx(LEU_RK, bleu, CIRK), -xx(LEU_KR, bleu, CIKR), +xx(SLL_RR, sll, RIRIRI, NOP, 0, 0), // dA = dkB << diC +xx(SLL_RI, sll, RIRII8, NOP, 0, 0), +xx(SLL_KR, sll, RIKIRI, SLL_RR, 2, REGT_INT), +xx(SRL_RR, srl, RIRIRI, NOP, 0, 0), // dA = dkB >> diC -- unsigned +xx(SRL_RI, srl, RIRII8, NOP, 0, 0), +xx(SRL_KR, srl, RIKIRI, SRL_RR, 2, REGT_INT), +xx(SRA_RR, sra, RIRIRI, NOP, 0, 0), // dA = dkB >> diC -- signed +xx(SRA_RI, sra, RIRII8, NOP, 0, 0), +xx(SRA_KR, sra, RIKIRI, SRA_RR, 2, REGT_INT), +xx(ADD_RR, add, RIRIRI, NOP, 0, 0), // dA = dB + dkC +xx(ADD_RK, add, RIRIKI, ADD_RR, 4, REGT_INT), +xx(ADDI, addi, RIRIIs, NOP, 0, 0), // dA = dB + C -- C is a signed 8-bit constant +xx(SUB_RR, sub, RIRIRI, NOP, 0, 0), // dA = dkB - dkC +xx(SUB_RK, sub, RIRIKI, SUB_RR, 4, REGT_INT), +xx(SUB_KR, sub, RIKIRI, SUB_RR, 2, REGT_INT), +xx(MUL_RR, mul, RIRIRI, NOP, 0, 0), // dA = dB * dkC +xx(MUL_RK, mul, RIRIKI, MUL_RR, 4, REGT_INT), +xx(DIV_RR, div, RIRIRI, NOP, 0, 0), // dA = dkB / dkC (signed) +xx(DIV_RK, div, RIRIKI, DIV_RR, 4, REGT_INT), +xx(DIV_KR, div, RIKIRI, DIV_RR, 2, REGT_INT), +xx(DIVU_RR, divu, RIRIRI, NOP, 0, 0), // dA = dkB / dkC (unsigned) +xx(DIVU_RK, divu, RIRIKI, DIVU_RR,4, REGT_INT), +xx(DIVU_KR, divu, RIKIRI, DIVU_RR,2, REGT_INT), +xx(MOD_RR, mod, RIRIRI, NOP, 0, 0), // dA = dkB % dkC (signed) +xx(MOD_RK, mod, RIRIKI, MOD_RR, 4, REGT_INT), +xx(MOD_KR, mod, RIKIRI, MOD_RR, 2, REGT_INT), +xx(MODU_RR, modu, RIRIRI, NOP, 0, 0), // dA = dkB % dkC (unsigned) +xx(MODU_RK, modu, RIRIKI, MODU_RR,4, REGT_INT), +xx(MODU_KR, modu, RIKIRI, MODU_RR,2, REGT_INT), +xx(AND_RR, and, RIRIRI, NOP, 0, 0), // dA = dB & dkC +xx(AND_RK, and, RIRIKI, AND_RR, 4, REGT_INT), +xx(OR_RR, or, RIRIRI, NOP, 0, 0), // dA = dB | dkC +xx(OR_RK, or, RIRIKI, OR_RR, 4, REGT_INT), +xx(XOR_RR, xor, RIRIRI, NOP, 0, 0), // dA = dB ^ dkC +xx(XOR_RK, xor, RIRIKI, XOR_RR, 4, REGT_INT), +xx(MIN_RR, min, RIRIRI, NOP, 0, 0), // dA = min(dB,dkC) +xx(MIN_RK, min, RIRIKI, MIN_RR, 4, REGT_INT), +xx(MAX_RR, max, RIRIRI, NOP, 0, 0), // dA = max(dB,dkC) +xx(MAX_RK, max, RIRIKI, MAX_RR, 4, REGT_INT), +xx(ABS, abs, RIRI, NOP, 0, 0), // dA = abs(dB) +xx(NEG, neg, RIRI, NOP, 0, 0), // dA = -dB +xx(NOT, not, RIRI, NOP, 0, 0), // dA = ~dB +xx(SEXT, sext, RIRII8, NOP, 0, 0), // dA = dB, sign extended by shifting left then right by C +xx(ZAP_R, zap, RIRIRI, NOP, 0, 0), // dA = dB, with bytes zeroed where bits in C/dC are one +xx(ZAP_I, zap, RIRII8, NOP, 0, 0), +xx(ZAPNOT_R, zapnot, RIRIRI, NOP, 0, 0), // dA = dB, with bytes zeroed where bits in C/dC are zero +xx(ZAPNOT_I, zapnot, RIRII8, NOP, 0, 0), +xx(EQ_R, beq, CIRR, NOP, 0, 0), // if ((dB == dkC) != A) then pc++ +xx(EQ_K, beq, CIRK, EQ_R, 4, REGT_INT), +xx(LT_RR, blt, CIRR, NOP, 0, 0), // if ((dkB < dkC) != A) then pc++ +xx(LT_RK, blt, CIRK, LT_RR, 4, REGT_INT), +xx(LT_KR, blt, CIKR, LT_RR, 2, REGT_INT), +xx(LE_RR, ble, CIRR, NOP, 0, 0), // if ((dkB <= dkC) != A) then pc++ +xx(LE_RK, ble, CIRK, LE_RR, 4, REGT_INT), +xx(LE_KR, ble, CIKR, LE_RR, 2, REGT_INT), +xx(LTU_RR, bltu, CIRR, NOP, 0, 0), // if ((dkB < dkC) != A) then pc++ -- unsigned +xx(LTU_RK, bltu, CIRK, LTU_RR, 4, REGT_INT), +xx(LTU_KR, bltu, CIKR, LTU_RR, 2, REGT_INT), +xx(LEU_RR, bleu, CIRR, NOP, 0, 0), // if ((dkB <= dkC) != A) then pc++ -- unsigned +xx(LEU_RK, bleu, CIRK, LEU_RR, 4, REGT_INT), +xx(LEU_KR, bleu, CIKR, LEU_RR, 2, REGT_INT), // Double-precision floating point math. -xx(ADDF_RR, add, RFRFRF), // fA = fB + fkC -xx(ADDF_RK, add, RFRFKF), -xx(SUBF_RR, sub, RFRFRF), // fA = fkB - fkC -xx(SUBF_RK, sub, RFRFKF), -xx(SUBF_KR, sub, RFKFRF), -xx(MULF_RR, mul, RFRFRF), // fA = fB * fkC -xx(MULF_RK, mul, RFRFKF), -xx(DIVF_RR, div, RFRFRF), // fA = fkB / fkC -xx(DIVF_RK, div, RFRFKF), -xx(DIVF_KR, div, RFKFRF), -xx(MODF_RR, mod, RFRFRF), // fA = fkB % fkC -xx(MODF_RK, mod, RFRFKF), -xx(MODF_KR, mod, RFKFRF), -xx(POWF_RR, pow, RFRFRF), // fA = fkB ** fkC -xx(POWF_RK, pow, RFRFKF), -xx(POWF_KR, pow, RFKFRF), -xx(MINF_RR, min, RFRFRF), // fA = min(fB),fkC) -xx(MINF_RK, min, RFRFKF), -xx(MAXF_RR, max, RFRFRF), // fA = max(fB),fkC) -xx(MAXF_RK, max, RFRFKF), -xx(ATAN2, atan2, RFRFRF), // fA = atan2(fB,fC), result is in degrees -xx(FLOP, flop, RFRFI8), // fA = f(fB), where function is selected by C -xx(EQF_R, beq, CFRR), // if ((fB == fkC) != (A & 1)) then pc++ -xx(EQF_K, beq, CFRK), -xx(LTF_RR, blt, CFRR), // if ((fkB < fkC) != (A & 1)) then pc++ -xx(LTF_RK, blt, CFRK), -xx(LTF_KR, blt, CFKR), -xx(LEF_RR, ble, CFRR), // if ((fkb <= fkC) != (A & 1)) then pc++ -xx(LEF_RK, ble, CFRK), -xx(LEF_KR, ble, CFKR), +xx(ADDF_RR, add, RFRFRF, NOP, 0, 0), // fA = fB + fkC +xx(ADDF_RK, add, RFRFKF, ADDF_RR,4, REGT_FLOAT), +xx(SUBF_RR, sub, RFRFRF, NOP, 0, 0), // fA = fkB - fkC +xx(SUBF_RK, sub, RFRFKF, SUBF_RR,4, REGT_FLOAT), +xx(SUBF_KR, sub, RFKFRF, SUBF_RR,2, REGT_FLOAT), +xx(MULF_RR, mul, RFRFRF, NOP, 0, 0), // fA = fB * fkC +xx(MULF_RK, mul, RFRFKF, MULF_RR,4, REGT_FLOAT), +xx(DIVF_RR, div, RFRFRF, NOP, 0, 0), // fA = fkB / fkC +xx(DIVF_RK, div, RFRFKF, DIVF_RR,4, REGT_FLOAT), +xx(DIVF_KR, div, RFKFRF, DIVF_RR,2, REGT_FLOAT), +xx(MODF_RR, mod, RFRFRF, NOP, 0, 0), // fA = fkB % fkC +xx(MODF_RK, mod, RFRFKF, MODF_RR,4, REGT_FLOAT), +xx(MODF_KR, mod, RFKFRF, MODF_RR,4, REGT_FLOAT), +xx(POWF_RR, pow, RFRFRF, NOP, 0, 0), // fA = fkB ** fkC +xx(POWF_RK, pow, RFRFKF, POWF_RR,4, REGT_FLOAT), +xx(POWF_KR, pow, RFKFRF, POWF_RR,2, REGT_FLOAT), +xx(MINF_RR, min, RFRFRF, NOP, 0, 0), // fA = min(fB),fkC) +xx(MINF_RK, min, RFRFKF, MINF_RR,4, REGT_FLOAT), +xx(MAXF_RR, max, RFRFRF, NOP, 0, 0), // fA = max(fB),fkC) +xx(MAXF_RK, max, RFRFKF, MAXF_RR,4, REGT_FLOAT), +xx(ATAN2, atan2, RFRFRF, NOP, 0, 0), // fA = atan2(fB,fC), result is in degrees +xx(FLOP, flop, RFRFI8, NOP, 0, 0), // fA = f(fB), where function is selected by C +xx(EQF_R, beq, CFRR, NOP, 0, 0), // if ((fB == fkC) != (A & 1)) then pc++ +xx(EQF_K, beq, CFRK, EQF_R, 4, REGT_FLOAT), +xx(LTF_RR, blt, CFRR, NOP, 0, 0), // if ((fkB < fkC) != (A & 1)) then pc++ +xx(LTF_RK, blt, CFRK, LTF_RR, 4, REGT_FLOAT), +xx(LTF_KR, blt, CFKR, LTF_RR, 2, REGT_FLOAT), +xx(LEF_RR, ble, CFRR, NOP, 0, 0), // if ((fkb <= fkC) != (A & 1)) then pc++ +xx(LEF_RK, ble, CFRK, LEF_RR, 4, REGT_FLOAT), +xx(LEF_KR, ble, CFKR, LEF_RR, 2, REGT_FLOAT), // Vector math. (2D) -xx(NEGV2, negv2, RVRV), // vA = -vB -xx(ADDV2_RR, addv2, RVRVRV), // vA = vB + vkC -xx(ADDV2_RK, addv2, RVRVKV), -xx(SUBV2_RR, subv2, RVRVRV), // vA = vkB - vkC -xx(SUBV2_RK, subv2, RVRVKV), -xx(SUBV2_KR, subv2, RVKVRV), -xx(DOTV2_RR, dotv2, RVRVRV), // va = vB dot vkC -xx(DOTV2_RK, dotv2, RVRVKV), -xx(MULVF2_RR, mulv2, RVRVRF), // vA = vkB * fkC -xx(MULVF2_RK, mulv2, RVRVKF), -xx(DIVVF2_RR, divv2, RVRVRF), // vA = vkB / fkC -xx(DIVVF2_RK, divv2, RVRVKF), -xx(LENV2, lenv2, RFRV), // fA = vB.Length -xx(EQV2_R, beqv2, CVRR), // if ((vB == vkC) != A) then pc++ (inexact if A & 32) -xx(EQV2_K, beqv2, CVRK), +xx(NEGV2, negv2, RVRV, NOP, 0, 0), // vA = -vB +xx(ADDV2_RR, addv2, RVRVRV, NOP, 0, 0), // vA = vB + vkC +xx(SUBV2_RR, subv2, RVRVRV, NOP, 0, 0), // vA = vkB - vkC +xx(DOTV2_RR, dotv2, RVRVRV, NOP, 0, 0), // va = vB dot vkC +xx(MULVF2_RR, mulv2, RVRVRF, NOP, 0, 0), // vA = vkB * fkC +xx(MULVF2_RK, mulv2, RVRVKF, MULVF2_RR,4, REGT_FLOAT), +xx(DIVVF2_RR, divv2, RVRVRF, NOP, 0, 0), // vA = vkB / fkC +xx(DIVVF2_RK, divv2, RVRVKF, DIVVF2_RR,4, REGT_FLOAT), +xx(LENV2, lenv2, RFRV, NOP, 0, 0), // fA = vB.Length +xx(EQV2_R, beqv2, CVRR, NOP, 0, 0), // if ((vB == vkC) != A) then pc++ (inexact if A & 32) +xx(EQV2_K, beqv2, CVRK, NOP, 0, 0), // this will never be used. // Vector math (3D) -xx(NEGV3, negv3, RVRV), // vA = -vB -xx(ADDV3_RR, addv3, RVRVRV), // vA = vB + vkC -xx(ADDV3_RK, addv3, RVRVKV), -xx(SUBV3_RR, subv3, RVRVRV), // vA = vkB - vkC -xx(SUBV3_RK, subv3, RVRVKV), -xx(SUBV3_KR, subv3, RVKVRV), -xx(DOTV3_RR, dotv3, RVRVRV), // va = vB dot vkC -xx(DOTV3_RK, dotv3, RVRVKV), -xx(CROSSV_RR, crossv, RVRVRV), // vA = vkB cross vkC -xx(CROSSV_RK, crossv, RVRVKV), -xx(CROSSV_KR, crossv, RVKVRV), -xx(MULVF3_RR, mulv3, RVRVRF), // vA = vkB * fkC -xx(MULVF3_RK, mulv3, RVRVKF), -xx(DIVVF3_RR, divv3, RVRVRF), // vA = vkB / fkC -xx(DIVVF3_RK, divv3, RVRVKF), -xx(LENV3, lenv3, RFRV), // fA = vB.Length -xx(EQV3_R, beqv3, CVRR), // if ((vB == vkC) != A) then pc++ (inexact if A & 33) -xx(EQV3_K, beqv3, CVRK), +xx(NEGV3, negv3, RVRV, NOP, 0, 0), // vA = -vB +xx(ADDV3_RR, addv3, RVRVRV, NOP, 0, 0), // vA = vB + vkC +xx(SUBV3_RR, subv3, RVRVRV, NOP, 0, 0), // vA = vkB - vkC +xx(DOTV3_RR, dotv3, RVRVRV, NOP, 0, 0), // va = vB dot vkC +xx(CROSSV_RR, crossv, RVRVRV, NOP, 0, 0), // vA = vkB cross vkC +xx(MULVF3_RR, mulv3, RVRVRF, NOP, 0, 0), // vA = vkB * fkC +xx(MULVF3_RK, mulv3, RVRVKF, MULVF3_RR,4, REGT_FLOAT), +xx(DIVVF3_RR, divv3, RVRVRF, NOP, 0, 0), // vA = vkB / fkC +xx(DIVVF3_RK, divv3, RVRVKF, DIVVF3_RR,4, REGT_FLOAT), +xx(LENV3, lenv3, RFRV, NOP, 0, 0), // fA = vB.Length +xx(EQV3_R, beqv3, CVRR, NOP, 0, 0), // if ((vB == vkC) != A) then pc++ (inexact if A & 33) +xx(EQV3_K, beqv3, CVRK, NOP, 0, 0), // this will never be used. // Pointer math. -xx(ADDA_RR, add, RPRPRI), // pA = pB + dkC -xx(ADDA_RK, add, RPRPKI), -xx(SUBA, sub, RIRPRP), // dA = pB - pC -xx(EQA_R, beq, CPRR), // if ((pB == pkC) != A) then pc++ -xx(EQA_K, beq, CPRK), +xx(ADDA_RR, add, RPRPRI, NOP, 0, 0), // pA = pB + dkC +xx(ADDA_RK, add, RPRPKI, ADDA_RR,4, REGT_POINTER), +xx(SUBA, sub, RIRPRP, NOP, 0, 0), // dA = pB - pC +xx(EQA_R, beq, CPRR, NOP, 0, 0), // if ((pB == pkC) != A) then pc++ +xx(EQA_K, beq, CPRK, EQA_R, 4, REGT_POINTER), #undef xx diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index c24867837..3bdc2e244 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -585,6 +585,26 @@ static void PrintExprFuncCall(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintExprClassCast(FLispString &out, ZCC_TreeNode *node) +{ + ZCC_ClassCast *enode = (ZCC_ClassCast *)node; + assert(enode->Operation == PEX_ClassCast); + out.Open("expr-class-cast"); + out.AddName(enode->ClassName); + PrintNodes(out, enode->Parameters, false); + out.Close(); +} + +static void PrintStaticArray(FLispString &out, ZCC_TreeNode *node) +{ + ZCC_StaticArrayStatement *enode = (ZCC_StaticArrayStatement *)node; + out.Open("static-array-stmt"); + PrintNodes(out, enode->Type, false); + out.AddName(enode->Id); + PrintNodes(out, enode->Values, false); + out.Close(); +} + static void PrintExprMemberAccess(FLispString &out, ZCC_TreeNode *node) { ZCC_ExprMemberAccess *enode = (ZCC_ExprMemberAccess *)node; @@ -820,6 +840,7 @@ static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node) out.Break(); out.Open("func-declarator"); out.AddHex(dnode->Flags); + PrintNodes(out, dnode->UseFlags); PrintNodes(out, dnode->Type); out.AddName(dnode->Name); PrintNodes(out, dnode->Params); @@ -827,6 +848,16 @@ static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintDeclFlags(FLispString &out, ZCC_TreeNode *node) +{ + auto dnode = (ZCC_DeclFlags *)node; + out.Break(); + out.Open("decl-flags"); + out.AddHex(dnode->Flags); + PrintNodes(out, dnode->Id); + out.Close(); +} + static void PrintFlagStmt(FLispString &out, ZCC_TreeNode *node) { auto dnode = (ZCC_FlagStmt *)node; @@ -901,6 +932,8 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintFlagStmt, PrintPropertyStmt, PrintVectorInitializer, + PrintDeclFlags, + PrintExprClassCast, }; FString ZCC_PrintAST(ZCC_TreeNode *root) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index a29d3d943..685c401e7 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -22,7 +22,7 @@ static void SetNodeLine(ZCC_TreeNode *name, int line) // If a is non-null, appends b to a. Otherwise, sets a to b. #define SAFE_APPEND(a,b) \ - if (a == NULL) a = b; else a->AppendSibling(b); + if (a == NULL) a = b; else AppendTreeNodeSibling(a, b); #define UNARY_EXPR(X,T) NEW_AST_NODE(ExprUnary, expr1, X); expr1->Operation = T; expr1->Operand = X; expr1->Type = NULL #define BINARY_EXPR(X,Y,T) NEW_AST_NODE(ExprBinary, expr2, X); expr2->Operation = T; expr2->Type = NULL; expr2->Left = X; expr2->Right = Y @@ -221,14 +221,14 @@ dottable_id(X) ::= dottable_id(A) DOT IDENTIFIER(B). { NEW_AST_NODE(Identifier,id2,A); id2->Id = B.Name(); - A->AppendSibling(id2); + AppendTreeNodeSibling(A, id2); X = A; /*X-overwrites-A*/ } dottable_id(X) ::= dottable_id(A) DOT DEFAULT. { NEW_AST_NODE(Identifier,id2,A); id2->Id = NAME_Default; - A->AppendSibling(id2); + AppendTreeNodeSibling(A, id2); X = A; /*X-overwrites-A*/ } @@ -239,7 +239,7 @@ dottable_id(X) ::= dottable_id(A) DOT COLOR. { NEW_AST_NODE(Identifier,id2,A); id2->Id = NAME_Color; - A->AppendSibling(id2); + AppendTreeNodeSibling(A, id2); X = A; /*X-overwrites-A*/ } @@ -278,24 +278,30 @@ class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } %type struct_body{ZCC_TreeNode *} %type struct_member{ZCC_TreeNode *} -struct_def(X) ::= STRUCT(T) IDENTIFIER(A) LBRACE opt_struct_body(B) RBRACE opt_semicolon. +struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon. { NEW_AST_NODE(Struct,def,T); def->NodeName = A.Name(); def->Body = B; def->Type = nullptr; def->Symbol = nullptr; + def->Flags = S.Flags; X = def; } +%type struct_flags{ClassFlagsBlock} +struct_flags(X) ::= . { X.Flags = 0; } +struct_flags(X) ::= NATIVE. { X.Flags = ZCC_Native; } + opt_struct_body(X) ::= . { X = NULL; } opt_struct_body(X) ::= struct_body(X). +opt_struct_body(X) ::= error. { X = NULL; } + -struct_body(X) ::= error. { X = NULL; } struct_body(X) ::= struct_member(X). -struct_body(X) ::= struct_member(A) struct_body(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); } +struct_body(X) ::= struct_body(A) struct_member(B). { X = A; /*X-overwrites-A*/ AppendTreeNodeSibling(X, B); } -struct_member(X) ::= declarator_no_fun(A). { X = A; /*X-overwrites-A*/ } +struct_member(X) ::= declarator(A). { X = A; /*X-overwrites-A*/ } struct_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ } struct_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } @@ -369,11 +375,11 @@ enum_def(X) ::= ENUM(T) IDENTIFIER(A) enum_type(B) LBRACE opt_enum_list(C) RBRAC } // Add a new terminating node, to indicate that the ConstantDefs for this enum are done. NEW_AST_NODE(EnumTerminator,term,U); - C->AppendSibling(term); + AppendTreeNodeSibling(C, term); } if (C != NULL) { - def->AppendSibling(C); + AppendTreeNodeSibling(def, C); } X = def; } @@ -383,7 +389,7 @@ enum_type(X) ::= COLON int_type(A). { X = A; /*X-overwrites-A*/ } enum_list(X) ::= error. { X = NULL; } enum_list(X) ::= enumerator(X). -enum_list(X) ::= enum_list(A) COMMA enumerator(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); } +enum_list(X) ::= enum_list(A) COMMA enumerator(B). { X = A; /*X-overwrites-A*/ AppendTreeNodeSibling(X, B); } opt_enum_list(X) ::= . { X = NULL; } opt_enum_list(X) ::= enum_list(X) opt_comma. @@ -444,7 +450,7 @@ states_opt(X) ::= states_opt(A) COMMA IDENTIFIER(B). NEW_AST_NODE(Identifier,id,B); id->Id = B.Name(); X = A; /*X-overwrites-A*/ - X->AppendSibling(id); + AppendTreeNodeSibling(X, id); } @@ -539,7 +545,7 @@ state_opts(X) ::= state_opts(A) FAST. { A.Fast = true; X = A; /*X-overwri state_opts(X) ::= state_opts(A) SLOW. { A.Slow = true; X = A; /*X-overwrites-A*/ } state_opts(X) ::= state_opts(A) NODELAY. { A.NoDelay = true; X = A; /*X-overwrites-A*/ } state_opts(X) ::= state_opts(A) CANRAISE. { A.CanRaise = true; X = A; /*X-overwrites-A*/ } -state_opts(X) ::= state_opts(A) OFFSET LPAREN expr(B) COMMA expr(C) RPAREN. { A.Offset = B; B->AppendSibling(C); X = A; /*X-overwrites-A*/ } +state_opts(X) ::= state_opts(A) OFFSET LPAREN expr(B) COMMA expr(C) RPAREN. { A.Offset = B; AppendTreeNodeSibling(B, C); X = A; /*X-overwrites-A*/ } state_opts(X) ::= state_opts(A) LIGHT LPAREN light_list(B) RPAREN. { X = A; /*X-overwrites-A*/ X.Lights = B; } %type light_list {ZCC_ExprConstant *} @@ -559,7 +565,7 @@ light_list(X) ::= light_list(A) COMMA STRCONST(B). strconst->Operation = PEX_ConstValue; strconst->Type = TypeString; strconst->StringVal = B.String; - A->AppendSibling(strconst); + AppendTreeNodeSibling(A, strconst); X = A; /*X-overwrites-A*/ } @@ -734,7 +740,7 @@ type_name(X) ::= DOT dottable_id(A). /* Type names can also be used as identifiers in contexts where type names * are not normally allowed. */ %fallback IDENTIFIER - SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE. + SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16. /* Aggregate types */ %type aggregate_type {ZCC_Type *} @@ -777,16 +783,16 @@ type_or_array(X) ::= type(X). type_or_array(X) ::= type(A) array_size(B). { X = A; /*X-overwrites-A*/ X->ArraySize = B; } type_list(X) ::= type_or_array(X). /* A comma-separated list of types */ -type_list(X) ::= type_list(A) COMMA type_or_array(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); } +type_list(X) ::= type_list(A) COMMA type_or_array(B). { X = A; /*X-overwrites-A*/ AppendTreeNodeSibling(X, B); } type_list_or_void(X) ::= VOID. { X = NULL; } type_list_or_void(X) ::= type_list(X). -array_size_expr(X) ::= LBRACKET opt_expr(A) RBRACKET. +array_size_expr(X) ::= LBRACKET(L) opt_expr(A) RBRACKET. { if (A == NULL) { - NEW_AST_NODE(Expression,nil,A); + NEW_AST_NODE(Expression,nil,L.SourceLoc); nil->Operation = PEX_Nil; nil->Type = NULL; X = nil; @@ -799,7 +805,7 @@ array_size_expr(X) ::= LBRACKET opt_expr(A) RBRACKET. array_size(X) ::= array_size_expr(X). array_size(X) ::= array_size(A) array_size_expr(B). { - A->AppendSibling(B); + AppendTreeNodeSibling(A, B); X = A; /*X-overwrites-A*/ } @@ -814,20 +820,21 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C). } else if (C.FuncName != NAME_None) { // A function - NEW_AST_NODE(FuncDeclarator, decl, A.SourceLoc); + NEW_AST_NODE(FuncDeclarator, decl, A == nullptr? C.SourceLoc : A->SourceLoc); decl->Type = B; decl->Params = C.FuncParams; decl->Name = C.FuncName; - decl->Flags = A.Int | C.FuncFlags; + decl->UseFlags = A == nullptr? nullptr : A->Id; + decl->Flags = (A == nullptr? 0 : A->Flags) | C.FuncFlags; decl->Body = C.FuncBody; X = decl; } else if (B != NULL && B->SiblingNext == B) { // A variable - NEW_AST_NODE(VarDeclarator, decl, A.SourceLoc); + NEW_AST_NODE(VarDeclarator, decl, A == nullptr? B->SourceLoc : A->SourceLoc); decl->Type = B; decl->Names = C.VarNames; - decl->Flags = A.Int; + decl->Flags = (A == nullptr? 0 : A->Flags); X = decl; } else @@ -843,14 +850,6 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C). X = NULL; } } -declarator_no_fun(X) ::= decl_flags(A) type(B) variable_list(C) SEMICOLON. -{ - NEW_AST_NODE(VarDeclarator, decl, A.SourceLoc ? A.SourceLoc : B->SourceLoc); - decl->Type = B; - decl->Names = C; - decl->Flags = A.Int; - X = decl; -} // Need to split it up like this to avoid parsing conflicts. variables_or_function(X) ::= IDENTIFIER(A) LPAREN func_params(B) RPAREN func_const(C) opt_func_body(D). /* Function */ @@ -912,23 +911,56 @@ variable_name(X) ::= IDENTIFIER(A) array_size(B). variable_list(X) ::= variable_name(X). variable_list(X) ::= variable_list(A) COMMA variable_name(B). { - A->AppendSibling(B); + AppendTreeNodeSibling(A, B); X = A; /*X-overwrites-A*/ } -decl_flags(X) ::= . { X.Int = 0; X.SourceLoc = 0; } -decl_flags(X) ::= decl_flags(A) NATIVE(T). { X.Int = A.Int | ZCC_Native; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) STATIC(T). { X.Int = A.Int | ZCC_Static; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) PRIVATE(T). { X.Int = A.Int | ZCC_Private; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) PROTECTED(T). { X.Int = A.Int | ZCC_Protected; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) LATENT(T). { X.Int = A.Int | ZCC_Latent; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) FINAL(T). { X.Int = A.Int | ZCC_Final; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) META(T). { X.Int = A.Int | ZCC_Meta; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) ACTION(T). { X.Int = A.Int | ZCC_Action; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) READONLY(T). { X.Int = A.Int | ZCC_ReadOnly; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) DEPRECATED(T). { X.Int = A.Int | ZCC_Deprecated; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) VIRTUAL(T). { X.Int = A.Int | ZCC_Virtual; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) OVERRIDE(T). { X.Int = A.Int | ZCC_Override; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } +%type decl_flags { ZCC_DeclFlags * } +decl_flags(X) ::= . { X = NULL; } +decl_flags(X) ::= decl_flags(F) decl_flag(A). +{ + if (F == nullptr) + { + NEW_AST_NODE(DeclFlags,nil_f,A); + X = nil_f; + X->Id = nullptr; + X->Flags = A.Int; + } + else + { + X = F; + X->Flags |= A.Int; + } +} + + +decl_flags(X) ::= decl_flags(F) ACTION(B) states_opts(A). +{ + if (F == nullptr) + { + NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc); + X = nil_f; + X->Flags = ZCC_Action; + } + else + { + X = F; + X->Flags |= ZCC_Action; + } + X->Id = A; +} + +decl_flag(X) ::= NATIVE(T). { X.Int = ZCC_Native; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= STATIC(T). { X.Int = ZCC_Static; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= PRIVATE(T). { X.Int = ZCC_Private; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= PROTECTED(T). { X.Int = ZCC_Protected; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= LATENT(T). { X.Int = ZCC_Latent; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; } func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); } func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; } @@ -952,11 +984,11 @@ func_params(X) ::= func_param_list(A) COMMA ELLIPSIS. parm->Flags = 0; parm->Default = nullptr; X = A; /*X-overwrites-A*/ - X->AppendSibling(parm); + AppendTreeNodeSibling(X, parm); } func_param_list(X) ::= func_param(X). -func_param_list(X) ::= func_param_list(A) COMMA func_param(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); } +func_param_list(X) ::= func_param_list(A) COMMA func_param(B). { X = A; /*X-overwrites-A*/ AppendTreeNodeSibling(X, B); } func_param(X) ::= func_param_flags(A) type(B) IDENTIFIER(C). { @@ -979,9 +1011,9 @@ func_param(X) ::= func_param_flags(A) type(B) IDENTIFIER(C) EQ expr(D). } func_param_flags(X) ::= . { X.Int = 0; X.SourceLoc = 0; } -func_param_flags(X) ::= func_param_flags(A) IN(T). { X.Int = A.Int | ZCC_In; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -func_param_flags(X) ::= func_param_flags(A) OUT(T). { X.Int = A.Int | ZCC_Out; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -func_param_flags(X) ::= func_param_flags(A) OPTIONAL(T). { X.Int = A.Int | ZCC_Optional; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } +func_param_flags(X) ::= func_param_flags(A) IN(T). { X.Int = A.Int | ZCC_In; X.SourceLoc = T.SourceLoc; } +func_param_flags(X) ::= func_param_flags(A) OUT(T). { X.Int = A.Int | ZCC_Out; X.SourceLoc = T.SourceLoc; } +func_param_flags(X) ::= func_param_flags(A) OPTIONAL(T). { X.Int = A.Int | ZCC_Optional; X.SourceLoc = T.SourceLoc; } /************ Expressions ************/ @@ -1046,6 +1078,14 @@ primary(X) ::= primary(A) LPAREN func_expr_list(B) RPAREN. [DOT] // Function ca expr->Parameters = B; X = expr; } +primary(X) ::= LPAREN CLASS LT IDENTIFIER(A) GT RPAREN LPAREN func_expr_list(B) RPAREN. [DOT] // class type cast +{ + NEW_AST_NODE(ClassCast, expr, A); + expr->Operation = PEX_ClassCast; + expr->ClassName = ENamedName(A.Int); + expr->Parameters = B; + X = expr; +} primary(X) ::= primary(A) LBRACKET expr(B) RBRACKET. [DOT] // Array access { NEW_AST_NODE(ExprBinary, expr, B); @@ -1382,7 +1422,7 @@ expr_list(X) ::= expr(X). expr_list(X) ::= expr_list(A) COMMA expr(B). { X = A; /*X-overwrites-A*/ - X->AppendSibling(B); + AppendTreeNodeSibling(X, B); } /*----- Function argument lists -----*/ @@ -1413,7 +1453,7 @@ func_expr_list(X) ::= func_expr_list(A) COMMA(T) func_expr_item(B). B = nil_b; } X = A; /*X-overwrites-A*/ - X->AppendSibling(B); + AppendTreeNodeSibling(X, B); } func_expr_item(X) ::= . @@ -1517,9 +1557,32 @@ statement(X) ::= expression_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ statement(X) ::= selection_statement(X). statement(X) ::= iteration_statement(X). statement(X) ::= jump_statement(X). -//statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ } +statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ } statement(X) ::= local_var(A) SEMICOLON. { X = A; /*X-overwrites-A*/ } statement(X) ::= error SEMICOLON. { X = NULL; } +statement(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ } + +/*----- Static array Statements -----*/ + +%type staticarray_statement{ZCC_StaticArrayStatement *} + +staticarray_statement(X) ::= STATIC CONST type(A) IDENTIFIER(B) LBRACKET RBRACKET EQ LBRACE expr_list(C) RBRACE. +{ + NEW_AST_NODE(StaticArrayStatement, stmt, A); + stmt->Type = A; + stmt->Id = ENamedName(B.Int); + stmt->Values = C; + X = stmt; +} + +staticarray_statement(X) ::= STATIC CONST type(A) LBRACKET RBRACKET IDENTIFIER(B) EQ LBRACE expr_list(C) RBRACE. +{ + NEW_AST_NODE(StaticArrayStatement, stmt, A); + stmt->Type = A; + stmt->Id = ENamedName(B.Int); + stmt->Values = C; + X = stmt; +} /*----- Jump Statements -----*/ @@ -1728,11 +1791,11 @@ labeled_statement(X) ::= DEFAULT(T) COLON. /*----- Assignment Statements -----*/ -/* This is no longer being used, in favor of handling assignments as expressions, just like C and C++ do. - Keep this here in case some other parts require assignment syntax or Lua-style multi-assignments become a thing. %type assign_statement{ZCC_AssignStmt *} -assign_statement(X) ::= expr_list(A) EQ expr_list(B). [EQ] +// The grammar won't let this pass without some syntactic help. +// Parentheses and braces aren't accepted either so brackets are the only way to get this through the parser without a conflict. +assign_statement(X) ::= LBRACKET expr_list(A) RBRACKET EQ expr(B). [EQ] { NEW_AST_NODE(AssignStmt,stmt,A); stmt->AssignOp = ZCC_EQ; @@ -1740,7 +1803,6 @@ assign_statement(X) ::= expr_list(A) EQ expr_list(B). [EQ] stmt->Sources = B; X = stmt; } -*/ /*----- Local Variable Definition "Statements" -----*/ @@ -1815,6 +1877,6 @@ var_init(X) ::= IDENTIFIER EQ LBRACE error RBRACE. variable_list_with_init(X) ::= var_init(X). variable_list_with_init(X) ::= variable_list_with_init(A) COMMA var_init(B). { - A->AppendSibling(B); + AppendTreeNodeSibling(A, B); X = A; /*X-overwrites-A*/ } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 57475c50e..6eb675b89 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -51,8 +51,6 @@ #include "codegeneration/codegen.h" #include "vmbuilder.h" -#define DEFAULTS_VMEXPORT ((BYTE *)(void *)1) - //========================================================================== // // ZCCCompiler :: ProcessClass @@ -196,6 +194,10 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC cls->Fields.Push(static_cast(node)); break; + case AST_FuncDeclarator: + cls->Functions.Push(static_cast(node)); + break; + case AST_EnumTerminator: enumType = nullptr; break; @@ -411,8 +413,11 @@ void ZCCCompiler::CreateStructTypes() { for(auto s : Structs) { - s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->Type; - s->strct->Type = NewStruct(s->NodeName(), s->Outer); + s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->CType(); + if (s->strct->Flags & ZCC_Native) + s->strct->Type = NewNativeStruct(s->NodeName(), nullptr); + else + s->strct->Type = NewStruct(s->NodeName(), s->Outer); s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type()); GlobalSymbols.AddSymbol(s->strct->Symbol); @@ -498,26 +503,6 @@ void ZCCCompiler::CreateClassTypes() } else { - // The parent was the last native class in the inheritance tree. - // There is probably a better place for this somewhere else - // TODO: Do this somewhere else or find a less hackish way to do it - if (!parent->bRuntimeClass) - { - //assert(parent->VMExported != nullptr); // Ideally the macro would be used on all inheritable-native classes - /**/ if (parent->VMExported != nullptr) { /**/ // remove the if condition when all done - - parent = parent->VMExported; - assert(parent->bRuntimeClass == false); - - if (parent->Defaults == nullptr) - { - // We have to manually do that since zscript knows nothing about these - parent->Defaults = DEFAULTS_VMEXPORT; - } - - /**/ } /**/ - } - // We will never get here if the name is a duplicate, so we can just do the assignment. try { @@ -1179,6 +1164,7 @@ void ZCCCompiler::CompileAllFields() // Create copies of the arrays which can be altered auto Classes = this->Classes; auto Structs = this->Structs; + TMap HasNativeChildren; // first step: Look for native classes with native children. // These may not have any variables added to them because it'd clash with the native definitions. @@ -1193,8 +1179,8 @@ void ZCCCompiler::CompileAllFields() { if (ac->ParentClass == c->Type() && ac->Size != TentativeClass) { - Error(c->cls, "Trying to add fields to class '%s' with native children", c->Type()->TypeName.GetChars()); - Classes.Delete(i--); + // Only set a marker here, so that we can print a better message when the actual fields get added. + HasNativeChildren.Insert(ac, true); break; } } @@ -1229,7 +1215,7 @@ void ZCCCompiler::CompileAllFields() type->Size = Classes[i]->Type()->ParentClass->Size; } } - if (CompileFields(type, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false)) + if (CompileFields(type, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false, !!HasNativeChildren.CheckKey(type))) { // Remove from the list if all fields got compiled. Classes.Delete(i--); @@ -1256,16 +1242,19 @@ void ZCCCompiler::CompileAllFields() // //========================================================================== -bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct) +bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren) { while (Fields.Size() > 0) { auto field = Fields[0]; + FieldDesc *fd = nullptr; PType *fieldtype = DetermineType(type, field, field->Names->Name, field->Type, true, true); // For structs only allow 'deprecated', for classes exclude function qualifiers. - int notallowed = forstruct? ~ZCC_Deprecated : ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override; + int notallowed = forstruct? + ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension : + ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension; if (field->Flags & notallowed) { @@ -1282,12 +1271,17 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (field->Flags & ZCC_Native) { - // todo: get the native address of this field. + varflags |= VARF_Native | VARF_Transient; } + if (field->Flags & ZCC_Meta) { - varflags |= VARF_ReadOnly; // metadata implies readonly - // todo: this needs to go into the metaclass and needs some handling + varflags |= VARF_Static|VARF_ReadOnly; // metadata implies readonly + if (!(field->Flags & ZCC_Native)) + { + // Non-native meta data is not implemented yet and requires some groundwork in the class copy code. + Error(field, "Metadata member %s must be native", FName(field->Names->Name).GetChars()); + } } if (field->Type->ArraySize != nullptr) @@ -1305,8 +1299,35 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel { thisfieldtype = ResolveArraySize(thisfieldtype, name->ArraySize, &type->Symbols); } - - type->AddField(name->Name, thisfieldtype, varflags); + + if (varflags & VARF_Native) + { + auto querytype = (varflags & VARF_Static) ? type->GetClass() : type; + fd = FindField(querytype, FName(name->Name).GetChars()); + if (fd == nullptr) + { + Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars()); + } + else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0) + { + Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size); + } + // Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions. + else + { + // for bit fields the type must point to the source variable. + if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32; + type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue); + } + } + else if (hasnativechildren) + { + Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change.", type->TypeName.GetChars(), fd->FieldSize, FName(name->Name).GetChars()); + } + else + { + type->AddField(name->Name, thisfieldtype, varflags); + } } name = static_cast(name->SiblingNext); } while (name != field->Names); @@ -1363,19 +1384,19 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n switch (btype->Type) { case ZCC_SInt8: - retval = TypeSInt8; + retval = formember? TypeSInt8 : (PType*)TypeError; break; case ZCC_UInt8: - retval = TypeUInt8; + retval = formember ? TypeUInt8 : (PType*)TypeError; break; case ZCC_SInt16: - retval = TypeSInt16; + retval = formember ? TypeSInt16 : (PType*)TypeError; break; case ZCC_UInt16: - retval = TypeUInt16; + retval = formember ? TypeUInt16 : (PType*)TypeError; break; case ZCC_SInt32: @@ -1391,11 +1412,10 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n retval = TypeBool; break; - // Do we really want to allow single precision floats, despite all the problems they cause? - // These are nearly guaranteed to desync between MSVC and GCC on x87, because GCC does not implement an IEEE compliant mode - case ZCC_Float32: case ZCC_FloatAuto: - //return TypeFloat32; + retval = formember ? TypeFloat32 : TypeFloat64; + break; + case ZCC_Float64: retval = TypeFloat64; break; @@ -1429,16 +1449,29 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n break; case ZCC_UserType: - // statelabel is not a token - there really is no need to, it works just as well as an identifier. Maybe the same should be done for some other types, too? - if (btype->UserType->Id == NAME_StateLabel) + // statelabel et.al. are not tokens - there really is no need to, it works just as well as an identifier. Maybe the same should be done for some other types, too? + switch (btype->UserType->Id) { + case NAME_StateLabel: retval = TypeStateLabel; - } - else - { + break; + + case NAME_SpriteID: + retval = TypeSpriteID; + break; + + case NAME_TextureID: + retval = TypeTextureID; + break; + + default: retval = ResolveUserType(btype, &outertype->Symbols); + break; } break; + + default: + break; } break; } @@ -1490,6 +1523,9 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n } break; } + + default: + break; } if (retval != TypeError && retval->MemberOnly && !formember) { @@ -1521,7 +1557,7 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt) { return TypeSInt32; // hack this to an integer until we can resolve the enum mess. } - if (ptype->IsKindOf(RUNTIME_CLASS(PClass))) + if (ptype->IsKindOf(RUNTIME_CLASS(PNativeStruct))) // native structs and classes cannot be instantiated, they always get used as reference. { return NewPointer(ptype, type->isconst); } @@ -1928,15 +1964,6 @@ void ZCCCompiler::InitDefaults() // Copy the parent's defaults and meta data. auto ti = static_cast(c->Type()); - // Hack for the DVMObjects as they weren't in the list originally - // TODO: process them in a non hackish way obviously - if (ti->ParentClass->Defaults == DEFAULTS_VMEXPORT) - { - ti->ParentClass->Defaults = nullptr; - ti->ParentClass->InitializeDefaults(); - ti->ParentClass->ParentClass->DeriveData(ti->ParentClass); - } - ti->InitializeDefaults(); ti->ParentClass->DeriveData(ti); @@ -1976,6 +2003,9 @@ void ZCCCompiler::InitDefaults() case AST_FlagStmt: ProcessDefaultFlag(ti, static_cast(content)); break; + + default: + break; } content = static_cast(content->SiblingNext); } while (content != d->Content); @@ -1989,6 +2019,362 @@ void ZCCCompiler::InitDefaults() } } + +void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass) +{ + TArray rets(1); + TArray args; + TArray argflags; + TArray argdefaults; + TArray argnames; + + rets.Clear(); + args.Clear(); + argflags.Clear(); + bool hasdefault = false; + // For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here. + if (AddTreeNode(f->Name, f, &c->TreeNodes, false)) + { + auto t = f->Type; + if (t != nullptr) + { + do + { + auto type = DetermineType(c->Type(), f, f->Name, t, false, false); + if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) + { + // structs and classes only get passed by pointer. + type = NewPointer(type); + } + // TBD: disallow certain types? For now, let everything pass that isn't an array. + rets.Push(type); + t = static_cast(t->SiblingNext); + } while (t != f->Type); + } + + int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract; + + if (f->Flags & notallowed) + { + Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed).GetChars()); + f->Flags &= notallowed; + } + uint32_t varflags = VARF_Method; + int implicitargs = 1; + AFuncDesc *afd = nullptr; + int useflags = SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM; + if (f->UseFlags != nullptr) + { + useflags = 0; + auto p = f->UseFlags; + do + { + switch (p->Id) + { + case NAME_Actor: + useflags |= SUF_ACTOR; + break; + case NAME_Overlay: + useflags |= SUF_OVERLAY; + break; + case NAME_Weapon: + useflags |= SUF_WEAPON; + break; + case NAME_Item: + useflags |= SUF_ITEM; + break; + default: + Error(p, "Unknown Action qualifier %s", FName(p->Id).GetChars()); + break; + } + + p = static_cast(p->SiblingNext); + } while (p != f->UseFlags); + } + + // map to implementation flags. + if (f->Flags & ZCC_Private) varflags |= VARF_Private; + if (f->Flags & ZCC_Protected) varflags |= VARF_Protected; + if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated; + if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual; + if (f->Flags & ZCC_Override) varflags |= VARF_Override; + if (f->Flags & ZCC_Action) + { + // Non-Actors cannot have action functions. + if (!c->Type()->IsKindOf(RUNTIME_CLASS(PClassActor))) + { + Error(f, "'Action' can only be used in child classes of Actor"); + } + + varflags |= VARF_Final; // Action implies Final. + if (useflags & (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM)) + { + varflags |= VARF_Action; + implicitargs = 3; + } + else + { + implicitargs = 1; + } + } + if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final. + + + if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'. + // Only one of these flags may be used. + static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static }; + static const char * print[] = { "virtual", "override", "action", "static" }; + int fc = 0; + FString build; + for (int i = 0; i < 4; i++) + { + if (f->Flags & exclude[i]) + { + fc++; + if (build.Len() > 0) build += ", "; + build += print[i]; + } + } + if (fc > 1) + { + Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars()); + varflags |= VARF_Method; + } + if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well. + + if (f->Flags & ZCC_Native) + { + varflags |= VARF_Native; + afd = FindFunction(c->Type(), FName(f->Name).GetChars()); + if (afd == nullptr) + { + Error(f, "The function '%s.%s' has not been exported from the executable.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()); + } + else + { + (*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs); + } + } + SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, useflags); + argdefaults.Resize(argnames.Size()); + auto p = f->Params; + bool hasoptionals = false; + if (p != nullptr) + { + do + { + int elementcount = 1; + VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here. + if (p->Type != nullptr) + { + auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); + int flags = 0; + if (type->IsA(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) + { + // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly. + type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); + flags |= VARF_Ref; + } + else if (type->GetRegType() != REGT_NIL) + { + if (p->Flags & ZCC_Out) flags |= VARF_Out; + if (type == TypeVector2) + { + elementcount = 2; + } + else if (type == TypeVector3) + { + elementcount = 3; + } + } + if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3) + { + Error(p, "Invalid type %s for function parameter", type->DescriptiveName()); + } + else if (p->Default != nullptr) + { + flags |= VARF_Optional; + hasoptionals = true; + // The simplifier is not suited to convert the constant into something usable. + // All it does is reduce the expression to a constant but we still got to do proper type checking and conversion. + // It will also lose important type info about enums, once these get implemented + // The code generator can do this properly for us. + FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false); + FCompileContext ctx(c->Type(), false); + x = x->Resolve(ctx); + + if (x != nullptr) + { + // Vectors need special treatment because they use more than one entry in the Defaults and do not report as actual constants + if (type == TypeVector2 && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(2)) + { + auto vx = static_cast(x); + vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); + vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); + } + else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(3)) + { + auto vx = static_cast(x); + vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); + vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); + vmval[2] = static_cast(vx->xyz[2])->GetValue().GetFloat(); + } + else if (!x->isConstant()) + { + Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars()); + } + else if (x->ValueType != type) + { + Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars()); + } + else + { + auto cnst = static_cast(x); + hasdefault = true; + switch (type->GetRegType()) + { + case REGT_INT: + vmval[0] = cnst->GetValue().GetInt(); + break; + + case REGT_FLOAT: + vmval[0] = cnst->GetValue().GetFloat(); + break; + + case REGT_POINTER: + if (type->IsKindOf(RUNTIME_CLASS(PClassPointer))) + vmval[0] = (DObject*)cnst->GetValue().GetPointer(); + else + vmval[0] = cnst->GetValue().GetPointer(); + break; + + case REGT_STRING: + vmval[0] = cnst->GetValue().GetString(); + break; + + default: + assert(0 && "no valid type for constant"); + break; + } + } + } + if (x != nullptr) delete x; + } + else if (hasoptionals) + { + Error(p, "All arguments after the first optional one need also be optional."); + } + // TBD: disallow certain types? For now, let everything pass that isn't an array. + args.Push(type); + argflags.Push(flags); + argnames.Push(p->Name); + + } + else + { + args.Push(nullptr); + argflags.Push(0); + argnames.Push(NAME_None); + } + for (int i = 0; i(p->SiblingNext); + } while (p != f->Params); + } + + PFunction *sym = new PFunction(c->Type(), f->Name); + sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr ? nullptr : *(afd->VMPointer), varflags, useflags); + c->Type()->Symbols.ReplaceSymbol(sym); + + auto cls = dyn_cast(c->Type()); + PFunction *virtsym = nullptr; + if (cls != nullptr && cls->ParentClass != nullptr) virtsym = dyn_cast(cls->ParentClass->Symbols.FindSymbol(FName(f->Name), true)); + unsigned vindex = ~0u; + if (virtsym != nullptr) vindex = virtsym->Variants[0].Implementation->VirtualIndex; + + if (vindex != ~0u || (varflags & VARF_Virtual)) + { + // Todo: Check if the declaration is legal. + + // First step: compare prototypes - if they do not match the virtual base method does not apply. + + // Second step: Check flags. Possible cases: + // 1. Base method is final: Error. + // 2. This method is override: Base virtual method must exist + // 3. This method is virtual but not override: Base may not have a virtual method with the same prototype. + } + + + if (!(f->Flags & ZCC_Native)) + { + if (f->Body == nullptr) + { + Error(f, "Empty function %s", FName(f->Name).GetChars()); + return; + } + else + { + auto code = ConvertAST(c->Type(), f->Body); + if (code != nullptr) + { + FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump); + } + } + } + if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time. + { + sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults); + } + + if (varflags & VARF_Virtual) + { + if (sym->Variants[0].Implementation == nullptr) + { + Error(f, "Virtual function %s.%s not present.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()); + return; + } + if (varflags & VARF_Final) + { + sym->Variants[0].Implementation->Final = true; + } + if (forclass) + { + PClass *clstype = static_cast(c->Type()); + int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto); + // specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types. + if (varflags & VARF_Override) + { + if (vindex == -1) + { + Error(f, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars()); + } + else + { + auto oldfunc = clstype->Virtuals[vindex]; + if (oldfunc->Final) + { + Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); + } + clstype->Virtuals[vindex] = sym->Variants[0].Implementation; + sym->Variants[0].Implementation->VirtualIndex = vindex; + } + } + else + { + if (vindex != -1) + { + Error(f, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars()); + } + sym->Variants[0].Implementation->VirtualIndex = clstype->Virtuals.Push(sym->Variants[0].Implementation); + } + } + else + { + Error(p, "Virtual functions can only be defined for classes"); + } + } + } +} + //========================================================================== // // Parses the functions list @@ -1997,285 +2383,24 @@ void ZCCCompiler::InitDefaults() void ZCCCompiler::InitFunctions() { - TArray rets(1); - TArray args; - TArray argflags; - TArray argdefaults; - TArray argnames; + for (auto s : Structs) + { + for (auto f : s->Functions) + { + CompileFunction(s, f, false); + } + } for (auto c : Classes) { // cannot be done earlier because it requires the parent class to be processed by this code, too. if (c->Type()->ParentClass != nullptr) { - if (c->Type()->ParentClass->Virtuals.Size() == 0) - { - // This a VMClass which didn't get processed here. - c->Type()->ParentClass->Virtuals = c->Type()->ParentClass->ParentClass->Virtuals; - } c->Type()->Virtuals = c->Type()->ParentClass->Virtuals; } for (auto f : c->Functions) { - rets.Clear(); - args.Clear(); - argflags.Clear(); - bool hasdefault = false; - // For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here. - if (AddTreeNode(f->Name, f, &c->TreeNodes, false)) - { - auto t = f->Type; - if (t != nullptr) - { - do - { - auto type = DetermineType(c->Type(), f, f->Name, t, false, false); - if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) - { - // structs and classes only get passed by pointer. - type = NewPointer(type); - } - // TBD: disallow certain types? For now, let everything pass that isn't an array. - rets.Push(type); - t = static_cast(t->SiblingNext); - } while (t != f->Type); - } - - int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract; - - if (f->Flags & notallowed) - { - Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed).GetChars()); - f->Flags &= notallowed; - } - uint32_t varflags = VARF_Method; - int implicitargs = 1; - AFuncDesc *afd = nullptr; - - // map to implementation flags. - if (f->Flags & ZCC_Private) varflags |= VARF_Private; - if (f->Flags & ZCC_Protected) varflags |= VARF_Protected; - if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated; - if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual; - if (f->Flags & ZCC_Override) varflags |= VARF_Override; - if (f->Flags & ZCC_Action) varflags |= VARF_Action|VARF_Final, implicitargs = 3; // Action implies Final. - if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final. - - if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'. - // Only one of these flags may be used. - static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static }; - static const char * print[] = { "virtual", "override", "action", "static" }; - int fc = 0; - FString build; - for (int i = 0; i < 4; i++) - { - if (f->Flags & exclude[i]) - { - fc++; - if (build.Len() > 0) build += ", "; - build += print[i]; - } - } - if (fc > 1) - { - Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars() ); - varflags |= VARF_Method; - } - if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well. - - if (f->Flags & ZCC_Native) - { - varflags |= VARF_Native; - afd = FindFunction(FName(f->Name).GetChars()); - if (afd == nullptr) - { - Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars()); - } - else - { - (*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs); - } - } - // Todo: parse these values from the definition - int tempuseflags = (varflags & VARF_Action) ? SUF_WEAPON | SUF_ITEM | SUF_OVERLAY | SUF_ACTOR : SUF_ACTOR; - SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, tempuseflags); - argdefaults.Resize(argnames.Size()); - auto p = f->Params; - bool hasoptionals = false; - if (p != nullptr) - { - do - { - int elementcount = 1; - VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here. - if (p->Type != nullptr) - { - auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); - int flags = 0; - if (p->Flags & ZCC_In) flags |= VARF_In; - if (p->Flags & ZCC_Out) flags |= VARF_Out; - if ((type->IsA(RUNTIME_CLASS(PStruct))) || (flags & VARF_Out)) - { - // 'out' parameters and all structs except vectors are passed by reference - if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3)) - { - type = NewPointer(type); - } - else if (type == TypeVector2) - { - elementcount = 2; - } - else if (type == TypeVector3) - { - elementcount = 3; - } - } - if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3) - { - Error(p, "Invalid type %s for function parameter", type->DescriptiveName()); - } - else if (p->Default != nullptr) - { - flags |= VARF_Optional; - hasoptionals = true; - // The simplifier is not suited to convert the constant into something usable. - // All it does is reduce the expression to a constant but we still got to do proper type checking and conversion. - // It will also lose important type info about enums, once these get implemented - // The code generator can do this properly for us. - FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false); - FCompileContext ctx(c->Type(), false); - x = x->Resolve(ctx); - - if (x != nullptr) - { - // Vectors need special treatment because they use more than one entry in the Defaults and do not report as actual constants - if (type == TypeVector2 && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(2)) - { - auto vx = static_cast(x); - vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); - vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); - } - else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(3)) - { - auto vx = static_cast(x); - vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); - vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); - vmval[2] = static_cast(vx->xyz[2])->GetValue().GetFloat(); - } - else if (!x->isConstant()) - { - Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars()); - } - else if (x->ValueType != type) - { - Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars()); - } - else - { - auto cnst = static_cast(x); - hasdefault = true; - switch (type->GetRegType()) - { - case REGT_INT: - vmval[0] = cnst->GetValue().GetInt(); - break; - - case REGT_FLOAT: - vmval[0] = cnst->GetValue().GetFloat(); - break; - - case REGT_POINTER: - if (type->IsKindOf(RUNTIME_CLASS(PClassPointer))) - vmval[0] = (DObject*)cnst->GetValue().GetPointer(); - else - vmval[0] = cnst->GetValue().GetPointer(); - break; - - case REGT_STRING: - vmval[0] = cnst->GetValue().GetString(); - break; - - default: - assert(0 && "no valid type for constant"); - break; - } - } - } - if (x != nullptr) delete x; - } - else if (hasoptionals) - { - Error(p, "All arguments after the first optional one need also be optional."); - } - // TBD: disallow certain types? For now, let everything pass that isn't an array. - args.Push(type); - argflags.Push(flags); - argnames.Push(p->Name); - - } - else - { - args.Push(nullptr); - argflags.Push(0); - argnames.Push(NAME_None); - } - for(int i=0;i(p->SiblingNext); - } while (p != f->Params); - } - - PFunction *sym = new PFunction(c->Type(), f->Name); - sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags); - c->Type()->Symbols.ReplaceSymbol(sym); - - if (!(f->Flags & ZCC_Native)) - { - auto code = ConvertAST(c->Type(), f->Body); - if (code != nullptr) - { - sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump); - } - } - if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time. - { - sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults); - } - - if (varflags & VARF_Virtual) - { - if (varflags & VARF_Final) - { - sym->Variants[0].Implementation->Final = true; - } - int vindex = c->Type()->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto); - // specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types. - if (varflags & VARF_Override) - { - if (vindex == -1) - { - Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars()); - } - else - { - auto oldfunc = c->Type()->Virtuals[vindex]; - if (oldfunc->Final) - { - Error(p, "Attempt to override final function %s", FName(f->Name).GetChars()); - } - c->Type()->Virtuals[vindex] = sym->Variants[0].Implementation; - sym->Variants[0].Implementation->VirtualIndex = vindex; - } - } - else - { - if (vindex != -1) - { - Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars()); - } - sym->Variants[0].Implementation->VirtualIndex = c->Type()->Virtuals.Push(sym->Variants[0].Implementation); - } - } - } + CompileFunction(c, f, true); } } } @@ -2301,7 +2426,7 @@ static bool CheckRandom(ZCC_Expression *duration) // Sets up the action function call // //========================================================================== -FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af) +FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af, int StateFlags) { // We have 3 cases to consider here: // 1. A function without parameters. This can be called directly @@ -2327,7 +2452,11 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af) { FArgumentList argumentlist; // We can use this function directly without wrapping it in a caller. - if ((afd->Variants[0].Flags & VARF_Action) || !cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) || !afd->Variants[0].SelfClass->IsDescendantOf(RUNTIME_CLASS(AStateProvider))) + auto selfclass = dyn_cast(afd->Variants[0].SelfClass); + assert(selfclass != nullptr); // non classes are not supposed to get here. + + int comboflags = afd->Variants[0].UseFlags & StateFlags; + if (comboflags == StateFlags) // the function must satisfy all the flags the state requires { return new FxVMFunctionCall(new FxSelf(*af), afd, argumentlist, *af, false); } @@ -2477,11 +2606,12 @@ void ZCCCompiler::CompileStates() Error(sl, "Duration is not a constant"); } } - state.Fullbright = sl->bBright; - state.Fast = sl->bFast; - state.Slow = sl->bSlow; - state.CanRaise = sl->bCanRaise; - if ((state.NoDelay = sl->bNoDelay)) + if (sl->bBright) state.StateFlags |= STF_FULLBRIGHT; + if (sl->bFast) state.StateFlags |= STF_FAST; + if (sl->bSlow) state.StateFlags |= STF_SLOW; + if (sl->bCanRaise) state.StateFlags |= STF_CANRAISE; + if (sl->bNoDelay) state.StateFlags |= STF_NODELAY; + if (sl->bNoDelay) { if (statedef.GetStateLabelIndex(NAME_Spawn) != statedef.GetStateCount()) { @@ -2517,7 +2647,7 @@ void ZCCCompiler::CompileStates() if (sl->Action != nullptr) { - auto code = SetupActionFunction(static_cast(c->Type()), sl->Action); + auto code = SetupActionFunction(static_cast(c->Type()), sl->Action, state.UseFlags); if (code != nullptr) { auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags); @@ -2622,7 +2752,7 @@ void ZCCCompiler::CompileStates() // //========================================================================== -FxExpression *ZCCCompiler::ConvertAST(PClass *cls, ZCC_TreeNode *ast) +FxExpression *ZCCCompiler::ConvertAST(PStruct *cls, ZCC_TreeNode *ast) { ConvertClass = cls; // there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return. @@ -2726,6 +2856,33 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) break; } + case AST_ClassCast: + { + auto cc = static_cast(ast); + if (cc->Parameters == nullptr || cc->Parameters->SiblingNext != cc->Parameters) + { + Error(cc, "Class type cast requires exactly one parameter"); + return new FxNop(*ast); // return something so that the compiler can continue. + } + auto cls = PClass::FindClass(cc->ClassName); + if (cls == nullptr) + { + Error(cc, "Unknown class %s", FName(cc->ClassName).GetChars()); + return new FxNop(*ast); // return something so that the compiler can continue. + } + return new FxClassPtrCast(cls, ConvertNode(cc->Parameters)); + } + + case AST_StaticArrayStatement: + { + auto sas = static_cast(ast); + PType *ztype = DetermineType(ConvertClass, sas, sas->Id, sas->Type, false, false); + FArgumentList args; + ConvertNodeList(args, sas->Values); + // This has to let the code generator resolve the constants, not the Simplifier, which lacks all the necessary type info. + return new FxStaticArray(ztype, sas->Id, args, *ast); + } + case AST_ExprMemberAccess: { auto memaccess = static_cast(ast); @@ -2735,8 +2892,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) case AST_FuncParm: { auto fparm = static_cast(ast); - // ignore the label for now, that's stuff for far later, when a bit more here is working. - return ConvertNode(fparm->Value); + auto node = ConvertNode(fparm->Value); + if (fparm->Label != NAME_None) node = new FxNamedNode(fparm->Label, node, *ast); + return node; } case AST_ExprID: @@ -2844,10 +3002,12 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) case PEX_LeftShift: case PEX_RightShift: case PEX_URightShift: + return new FxShift(tok, left, right); + case PEX_BitAnd: case PEX_BitOr: case PEX_BitXor: - return new FxBinaryInt(tok, left, right); + return new FxBitOp(tok, left, right); case PEX_BoolOr: case PEX_BoolAnd: @@ -2879,10 +3039,12 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) case PEX_LshAssign: case PEX_RshAssign: case PEX_URshAssign: + return ModifyAssign(new FxShift(tok, new FxAssignSelf(*ast), right), left); + case PEX_AndAssign: case PEX_OrAssign: case PEX_XorAssign: - return ModifyAssign(new FxBinaryInt(tok, new FxAssignSelf(*ast), right), left); + return ModifyAssign(new FxBitOp(tok, new FxAssignSelf(*ast), right), left); case PEX_LTGTEQ: return new FxLtGtEq(left, right); @@ -2897,8 +3059,8 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) case PEX_Is: return new FxTypeCheck(left, right); - // todo: These do not have representations in DECORATE and no implementation exists yet. case PEX_Concat: + return new FxConcat(left, right); default: I_Error("Binary operator %d not implemented yet", op); @@ -2930,29 +3092,39 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) auto loc = static_cast(ast); auto node = loc->Vars; FxSequence *list = new FxSequence(*ast); + + PType *ztype = DetermineType(ConvertClass, node, node->Name, loc->Type, true, false); + + if (loc->Type->ArraySize != nullptr) + { + ztype = ResolveArraySize(ztype, loc->Type->ArraySize, &ConvertClass->Symbols); + } + do { - // Type determination must be done for each field to properly handle array definitions. - PType *type = DetermineType(ConvertClass, node, node->Name, loc->Type, true, false); - if (type->IsKindOf(RUNTIME_CLASS(PArray))) + PType *type; + + if (node->ArraySize != nullptr) { - Error(loc, "Local array variables not implemented yet."); + type = ResolveArraySize(ztype, node->ArraySize, &ConvertClass->Symbols); } else { - FxExpression *val; - if (node->InitIsArray) - { - Error(node, "Tried to initialize %s with an array", FName(node->Name).GetChars()); - val = nullptr; - } - else - { - val = node->Init ? ConvertNode(node->Init) : nullptr; - } - list->Add(new FxLocalVariableDeclaration(type, node->Name, val, 0, *node)); // todo: Handle flags in the grammar. + type = ztype; } + FxExpression *val; + if (node->InitIsArray) + { + Error(node, "Compound initializer not implemented yet"); + val = nullptr; + } + else + { + val = node->Init ? ConvertNode(node->Init) : nullptr; + } + list->Add(new FxLocalVariableDeclaration(type, node->Name, val, 0, *node)); // todo: Handle flags in the grammar. + node = static_cast(node->SiblingNext); } while (node != loc->Vars); return list; @@ -3042,7 +3214,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) case AST_CaseStmt: { auto cases = static_cast(ast); - return new FxCaseStatement(ConvertNode(cases->Condition), *ast); + return new FxCaseStatement(ConvertNode(cases->Condition), *ast); } case AST_CompoundStmt: @@ -3057,6 +3229,24 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) } while (node != compound->Content); return x; } + + case AST_AssignStmt: + { + auto ass = static_cast(ast); + FArgumentList args; + ConvertNodeList(args, ass->Dests); + assert(ass->Sources->SiblingNext == ass->Sources); // right side should be a single function call - nothing else + if (ass->Sources->NodeType != AST_ExprFuncCall) + { + // don't let this through to the code generator. This node is only used to assign multiple returns of a function to more than one variable. + Error(ass, "Right side of multi-assignment must be a function call"); + return new FxNop(*ast); // allow compiler to continue looking for errors. + } + return new FxMultiAssign(args, ConvertNode(ass->Sources), *ast); + } + + default: + break; } // only for development. I_Error is more convenient here than a normal error. I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType); @@ -3076,4 +3266,4 @@ FArgumentList &ZCCCompiler::ConvertNodeList(FArgumentList &args, ZCC_TreeNode *h } while (node != head); } return args; -} \ No newline at end of file +} diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 2e8840a46..8b3a9f5f1 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -20,6 +20,11 @@ struct ZCC_StructWork TArray Enums; TArray Constants; TArray Fields; + TArray Functions; + + ZCC_StructWork() + { + } ZCC_StructWork(ZCC_Struct * s, PSymbolTreeNode *n, ZCC_Class *outer) { @@ -41,34 +46,25 @@ struct ZCC_StructWork }; -struct ZCC_ClassWork +struct ZCC_ClassWork : public ZCC_StructWork { ZCC_Class *cls; - PSymbolTable TreeNodes; - PSymbolTreeNode *node; - TArray Enums; - TArray Constants; - TArray Fields; TArray Defaults; - TArray Functions; TArray States; ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n) { + strct = s; cls = s; node = n; - } - - FName NodeName() const - { - return cls->NodeName; + OuterDef = nullptr; + Outer = nullptr; } PClass *Type() { - return cls->Type; + return static_cast(strct->Type); } - }; struct ZCC_ConstantWork @@ -95,7 +91,7 @@ private: bool CompileConstant(ZCC_ConstantDef *def, PSymbolTable *Symbols); void CompileAllFields(); - bool CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct); + bool CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); FString FlagsToString(uint32_t flags); PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember); PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym); @@ -108,10 +104,11 @@ private: int GetInt(ZCC_Expression *expr); double GetDouble(ZCC_Expression *expr); const char *GetString(ZCC_Expression *expr, bool silent = false); + void CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass); void InitFunctions(); void CompileStates(); - FxExpression *SetupActionFunction(PClass *cls, ZCC_TreeNode *sl); + FxExpression *SetupActionFunction(PClass *cls, ZCC_TreeNode *sl, int stateflags); bool SimplifyingConstant; TArray Constants; @@ -142,12 +139,12 @@ private: void Error(ZCC_TreeNode *node, const char *msg, ...); void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr); - FxExpression *ConvertAST(PClass *cclass, ZCC_TreeNode *ast); + FxExpression *ConvertAST(PStruct *cclass, ZCC_TreeNode *ast); FxExpression *ConvertNode(ZCC_TreeNode *node); FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head); DObject *Outer; - PClass *ConvertClass; // class type to be used when resoving symbold while converting an AST + PStruct *ConvertClass; // class type to be used when resoving symbols while converting an AST PSymbolTable *GlobalTreeNodes; PSymbolTable *OutputSymbols; ZCC_AST &AST; diff --git a/src/scripting/zscript/zcc_exprlist.h b/src/scripting/zscript/zcc_exprlist.h index b565c00fb..faf6af6a4 100644 --- a/src/scripting/zscript/zcc_exprlist.h +++ b/src/scripting/zscript/zcc_exprlist.h @@ -8,6 +8,7 @@ xx(ConstValue, TK_Const) xx(FuncCall, '(') xx(ArrayAccess, TK_Array) xx(MemberAccess, '.') +xx(ClassCast, TK_Class) xx(TypeRef, TK_Class) xx(Vector, TK_Vector2) diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index de3d2c7e1..64ac2c86d 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -137,6 +137,10 @@ static void InitTokenMap() TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte); TOKENDEF2(TK_Short, ZCC_SHORT, NAME_Short); TOKENDEF2(TK_UShort, ZCC_USHORT, NAME_uShort); + TOKENDEF2(TK_Int8, ZCC_SBYTE, NAME_int8); + TOKENDEF2(TK_UInt8, ZCC_BYTE, NAME_uint8); + TOKENDEF2(TK_Int16, ZCC_SHORT, NAME_int16); + TOKENDEF2(TK_UInt16, ZCC_USHORT, NAME_uint16); TOKENDEF2(TK_Int, ZCC_INT, NAME_Int); TOKENDEF2(TK_UInt, ZCC_UINT, NAME_uInt); TOKENDEF2(TK_Bool, ZCC_BOOL, NAME_Bool); @@ -298,11 +302,16 @@ static void DoParse(int lumpnum) parser = ZCCParseAlloc(malloc); ZCCParseState state; -//#define TRACE -#ifdef TRACE // this costs a lot of time and should only be activated when it's really needed. - FILE *f = fopen("trace.txt", "w"); - char prompt = '\0'; - ZCCParseTrace(f, &prompt); + +#ifndef NDEBUG + FILE *f = nullptr; + const char *tracefile = Args->CheckValue("-tracefile"); + if (tracefile != nullptr) + { + f = fopen(tracefile, "w"); + char prompt = '\0'; + ZCCParseTrace(f, &prompt); + } #endif sc.OpenLumpNum(lumpnum); @@ -337,6 +346,9 @@ static void DoParse(int lumpnum) } } +#ifndef NDEBUG + if (f) fprintf(f, "Starting parsing %s\n", sc.String); +#endif ParseSingleFile(sc.String, 0, parser, state); } } @@ -352,8 +364,8 @@ static void DoParse(int lumpnum) I_Error("%d errors while parsing %s", FScriptPosition::ErrorCounter, Wads.GetLumpFullPath(lumpnum).GetChars()); } -#ifdef TRACE - if (f != NULL) +#ifndef NDEBUG + if (f != nullptr) { fclose(f); } @@ -402,10 +414,12 @@ void ParseScripts() int lump, lastlump = 0; FScriptPosition::ResetErrorCounter(); + while ((lump = Wads.FindLump("ZSCRIPT", &lastlump)) != -1) { DoParse(lump); } + } /* @@ -462,3 +476,34 @@ ZCC_TreeNode *ZCCParseState::InitNode(size_t size, EZCCTreeNodeType type) node->SourceLump = sc->LumpNum; return node; } + +// Appends a sibling to this node's sibling list. +void AppendTreeNodeSibling(ZCC_TreeNode *thisnode, ZCC_TreeNode *sibling) +{ + if (thisnode == nullptr) + { + // Some bad syntax can actually get here, so better abort so that the user can see the error which caused this. + I_FatalError("Internal script compiler error. Execution aborted."); + } + if (sibling == nullptr) + { + return; + } + + ZCC_TreeNode *&SiblingPrev = thisnode->SiblingPrev; + ZCC_TreeNode *&SiblingNext = thisnode->SiblingNext; + + // Check integrity of our sibling list. + assert(SiblingPrev->SiblingNext == thisnode); + assert(SiblingNext->SiblingPrev == thisnode); + + // Check integrity of new sibling list. + assert(sibling->SiblingPrev->SiblingNext == sibling); + assert(sibling->SiblingNext->SiblingPrev == sibling); + + ZCC_TreeNode *siblingend = sibling->SiblingPrev; + SiblingPrev->SiblingNext = sibling; + sibling->SiblingPrev = SiblingPrev; + SiblingPrev = siblingend; + siblingend->SiblingNext = thisnode; +} diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 1a187af82..7a763b2c1 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -101,6 +101,9 @@ enum EZCCTreeNodeType AST_FlagStmt, AST_PropertyStmt, AST_VectorValue, + AST_DeclFlags, + AST_ClassCast, + AST_StaticArrayStatement, NUM_AST_NODE_TYPES }; @@ -110,13 +113,12 @@ enum EZCCBuiltinType ZCC_SInt8, ZCC_UInt8, ZCC_SInt16, - ZCC_UInt16, + ZCC_UInt16, // smaller than 32 bit types are only valid in structs, classes and arrays. ZCC_SInt32, ZCC_UInt32, ZCC_IntAuto, // for enums, autoselect appropriately sized int ZCC_Bool, - ZCC_Float32, ZCC_Float64, ZCC_FloatAuto, // 32-bit in structs/classes, 64-bit everywhere else ZCC_String, @@ -159,29 +161,6 @@ struct ZCC_TreeNode // one of the structures below. EZCCTreeNodeType NodeType; - // Appends a sibling to this node's sibling list. - void AppendSibling(ZCC_TreeNode *sibling) - { - if (sibling == NULL) - { - return; - } - - // Check integrity of our sibling list. - assert(SiblingPrev->SiblingNext == this); - assert(SiblingNext->SiblingPrev == this); - - // Check integrity of new sibling list. - assert(sibling->SiblingPrev->SiblingNext == sibling); - assert(sibling->SiblingNext->SiblingPrev == sibling); - - ZCC_TreeNode *siblingend = sibling->SiblingPrev; - SiblingPrev->SiblingNext = sibling; - sibling->SiblingPrev = SiblingPrev; - SiblingPrev = siblingend; - siblingend->SiblingNext = this; - } - operator FScriptPosition() { return FScriptPosition(*SourceName, SourceLoc); @@ -189,6 +168,8 @@ struct ZCC_TreeNode }; +void AppendTreeNodeSibling(ZCC_TreeNode *thisnode, ZCC_TreeNode *sibling); + struct ZCC_Identifier : ZCC_TreeNode { ENamedName Id; @@ -200,19 +181,19 @@ struct ZCC_NamedNode : ZCC_TreeNode PSymbolType *Symbol; }; -struct ZCC_Class : ZCC_NamedNode +struct ZCC_Struct : ZCC_NamedNode +{ + VM_UWORD Flags; + ZCC_TreeNode *Body; + PStruct *Type; +}; + +struct ZCC_Class : ZCC_Struct { ZCC_Identifier *ParentName; ZCC_Identifier *Replaces; - VM_UWORD Flags; - ZCC_TreeNode *Body; - PClass *Type; -}; -struct ZCC_Struct : ZCC_NamedNode -{ - ZCC_TreeNode *Body; - PStruct *Type; + PClass *CType() { return static_cast(Type); } }; struct ZCC_Enum : ZCC_NamedNode @@ -365,6 +346,12 @@ struct ZCC_ExprFuncCall : ZCC_Expression ZCC_FuncParm *Parameters; }; +struct ZCC_ClassCast : ZCC_Expression +{ + ENamedName ClassName; + ZCC_FuncParm *Parameters; +}; + struct ZCC_ExprMemberAccess : ZCC_Expression { ZCC_Expression *Left; @@ -398,6 +385,13 @@ struct ZCC_Statement : ZCC_TreeNode { }; +struct ZCC_StaticArrayStatement : ZCC_Statement +{ + ZCC_Type *Type; + ENamedName Id; + ZCC_Expression *Values; +}; + struct ZCC_CompoundStmt : ZCC_Statement { ZCC_Statement *Content; @@ -473,6 +467,12 @@ struct ZCC_FuncParamDecl : ZCC_TreeNode int Flags; }; +struct ZCC_DeclFlags : ZCC_TreeNode +{ + ZCC_Identifier *Id; + int Flags; +}; + struct ZCC_ConstantDef : ZCC_NamedNode { ZCC_Expression *Value; @@ -498,6 +498,7 @@ struct ZCC_FuncDeclarator : ZCC_Declarator ZCC_FuncParamDecl *Params; ENamedName Name; ZCC_Statement *Body; + ZCC_Identifier *UseFlags; }; struct ZCC_Default : ZCC_CompoundStmt diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index c130b7e6a..cb4fac2cc 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -238,8 +238,8 @@ static const FEnumList ResamplerNames[] = { "No Interpolation", FMOD_DSP_RESAMPLER_NOINTERP }, { "NoInterp", FMOD_DSP_RESAMPLER_NOINTERP }, { "Linear", FMOD_DSP_RESAMPLER_LINEAR }, - // [BL] 64-bit version of FMOD Ex 4.26 crashes with these resamplers. -#if FMOD_STUDIO || !(defined(_M_X64) || defined(__amd64__)) || !(FMOD_VERSION >= 0x42600 && FMOD_VERSION <= 0x426FF) + // [BL] 64-bit versions of FMOD Ex between 4.24 and 4.26 crash with these resamplers. +#if FMOD_STUDIO || !(defined(_M_X64) || defined(__amd64__)) || !(FMOD_VERSION >= 0x42400 && FMOD_VERSION <= 0x426FF) { "Cubic", FMOD_DSP_RESAMPLER_CUBIC }, { "Spline", FMOD_DSP_RESAMPLER_SPLINE }, #endif diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index f00a60201..653d2a798 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -1335,6 +1335,7 @@ void FMultiPatchTexture::ResolvePatches() { if (Inits[i].HasLine) Inits[i].sc.Message(MSG_WARNING, "Texture '%s' references itself as patch\n", Inits[i].TexName.GetChars()); else Printf(TEXTCOLOR_YELLOW "Texture '%s' references itself as patch\n", Inits[i].TexName.GetChars()); + continue; } else { diff --git a/src/v_blend.cpp b/src/v_blend.cpp index c13ed547c..ffc16cd5c 100644 --- a/src/v_blend.cpp +++ b/src/v_blend.cpp @@ -104,7 +104,7 @@ void V_AddPlayerBlend (player_t *CPlayer, float blend[4], float maxinvalpha, int // [RH] All powerups can affect the screen blending now for (AInventory *item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory) { - PalEntry color = item->GetBlend (); + PalEntry color = item->CallGetBlend (); if (color.a != 0) { V_AddBlend (color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f, blend); diff --git a/src/v_video.cpp b/src/v_video.cpp index b086e5e6b..7192c5d03 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -69,10 +69,10 @@ int active_con_scale(); FRenderer *Renderer; -IMPLEMENT_CLASS(DCanvas, true, false, false, false) -IMPLEMENT_CLASS(DFrameBuffer, true, false, false, false) +IMPLEMENT_CLASS(DCanvas, true, false) +IMPLEMENT_CLASS(DFrameBuffer, true, false) -#if defined(_DEBUG) && defined(_M_IX86) +#if defined(_DEBUG) && defined(_M_IX86) && !defined(__MINGW32__) #define DBGBREAK { __asm int 3 } #else #define DBGBREAK @@ -106,11 +106,11 @@ public: float Gamma; }; -IMPLEMENT_CLASS(DDummyFrameBuffer, true, false, false, false) +IMPLEMENT_CLASS(DDummyFrameBuffer, true, false) // SimpleCanvas is not really abstract, but this macro does not // try to generate a CreateNew() function. -IMPLEMENT_CLASS(DSimpleCanvas, true, false, false, false) +IMPLEMENT_CLASS(DSimpleCanvas, true, false) class FPaletteTester : public FTexture { diff --git a/src/virtual.h b/src/virtual.h index d3a2bfa3a..844c51d38 100644 --- a/src/virtual.h +++ b/src/virtual.h @@ -1,134 +1,20 @@ - - - -// Templates really are powerful -#define VMEXPORTED_NATIVES_START \ - template class ExportedNatives : public ExportedNatives {}; \ - template<> class ExportedNatives { \ - protected: ExportedNatives() {} \ - public: \ - static ExportedNatives *Get() { static ExportedNatives *Instance = nullptr; if (Instance == nullptr) Instance = new ExportedNatives; return Instance; } \ - ExportedNatives(const ExportedNatives&) = delete; \ - ExportedNatives(ExportedNatives&&) = delete; - -#define VMEXPORTED_NATIVES_FUNC(func) \ - template ret func(void *ptr, args ... arglist) { return ret(); } - -#define VMEXPORTED_NATIVES_END }; - -#define VMEXPORT_NATIVES_START(cls, parent) \ - template<> class ExportedNatives : public ExportedNatives { \ - protected: ExportedNatives() {} \ - public: \ - static ExportedNatives *Get() { static ExportedNatives *Instance = nullptr; if (Instance == nullptr) Instance = new ExportedNatives; return Instance; } \ - ExportedNatives(const ExportedNatives&) = delete; \ - ExportedNatives(ExportedNatives&&) = delete; - -#define VMEXPORT_NATIVES_FUNC(func) \ - template ret func(void *ptr, args ... arglist) { return static_cast(ptr)->object::func(arglist...); } - -#define VMEXPORT_NATIVES_END(cls) }; - - -//Initial list -VMEXPORTED_NATIVES_START - VMEXPORTED_NATIVES_FUNC(Destroy) - VMEXPORTED_NATIVES_FUNC(Tick) - VMEXPORTED_NATIVES_FUNC(PostBeginPlay) -VMEXPORTED_NATIVES_END - -template -class DVMObject : public T +inline unsigned GetVirtualIndex(PClass *cls, const char *funcname) { -public: - static char *FormatClassName() - { - static char *name = nullptr; - if (name == nullptr) - { - name = new char[64]; - mysnprintf(name, 64, "DVMObject<%s>", Super::RegistrationInfo.Name); - atterm([]{ delete[] DVMObject::RegistrationInfo.Name; }); - } - return name; - } + // Look up the virtual function index in the defining class because this may have gotten overloaded in subclasses with something different than a virtual override. + auto sym = dyn_cast(cls->Symbols.FindSymbol(funcname, false)); + assert(sym != nullptr); + auto VIndex = sym->Variants[0].Implementation->VirtualIndex; + return VIndex; +} - virtual PClass *StaticType() const - { - return RegistrationInfo.MyClass; - } - static ClassReg RegistrationInfo; - static ClassReg * const RegistrationInfoPtr; - typedef T Super; +#define IFVIRTUALPTR(self, cls, funcname) \ + static unsigned VIndex = ~0u; \ + if (VIndex == ~0u) { \ + VIndex = GetVirtualIndex(RUNTIME_CLASS(cls), #funcname); \ + assert(VIndex != ~0u); \ + } \ + auto clss = self->GetClass(); \ + VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \ + if (func != nullptr) -private: - typedef DVMObject ThisClass; - static void InPlaceConstructor(void *mem) - { - new((EInPlace *)mem) DVMObject; - } - -public: - void Destroy() - { - if (this->ObjectFlags & OF_SuperCall) - { - this->ObjectFlags &= OF_SuperCall; - ExportedNatives::Get()->template Destroy(this); - } - else - { - static int VIndex = -1; - if (VIndex < 0) - { - // Look up the virtual function index in the defining class because this may have gotten overloaded in subclasses with something different than a virtual override. - auto sym = dyn_cast(RUNTIME_CLASS(DObject)->Symbols.FindSymbol("Destroy", false)); - assert(sym != nullptr); - VIndex = sym->Variants[0].Implementation->VirtualIndex; - assert(VIndex >= 0); - } - // Without the type cast this picks the 'void *' assignment... - VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(this->GetClass()->Virtuals[VIndex], params, 1, nullptr, 0, nullptr); - } - } - void Tick() - { - ExportedNatives::Get()->template Tick(this); - } - - void PostBeginPlay() - { - ExportedNatives::Get()->template PostBeginPlay(this); - } -}; - -template -ClassReg DVMObject::RegistrationInfo = -{ - nullptr, - DVMObject::FormatClassName(), - &DVMObject::Super::RegistrationInfo, - nullptr, - nullptr, - DVMObject::InPlaceConstructor, - nullptr, - sizeof(DVMObject), - DVMObject::MetaClassNum -}; -template _DECLARE_TI(DVMObject) - -VMEXPORT_NATIVES_START(DObject, void) - VMEXPORT_NATIVES_FUNC(Destroy) -VMEXPORT_NATIVES_END(DObject) - -VMEXPORT_NATIVES_START(DThinker, DObject) - VMEXPORT_NATIVES_FUNC(Tick) - VMEXPORT_NATIVES_FUNC(PostBeginPlay) - VMEXPORT_NATIVES_END(DThinker) - -/* -VMEXPORT_NATIVES_START(AActor, DThinker) -VMEXPORT_NATIVES_END(AActor) -*/ \ No newline at end of file +#define IFVIRTUAL(cls, funcname) IFVIRTUALPTR(this, cls, funcname) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index da369144a..41833fc24 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -501,6 +501,15 @@ int FWadCollection::CheckNumForName (const char *name, int space, int wadnum, bo return i != NULL_INDEX ? i : -1; } +DEFINE_ACTION_FUNCTION(_Wads, CheckNumForName) +{ + PARAM_PROLOGUE; + PARAM_STRING(name); + PARAM_INT(ns); + PARAM_INT_DEF(wadnum); + PARAM_BOOL_DEF(exact); + ACTION_RETURN_INT(Wads.CheckNumForName(name, ns, wadnum, exact)); +} //========================================================================== // // W_GetNumForName diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index 6d162bbec..0e8dd3dec 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -91,7 +91,7 @@ // TYPES ------------------------------------------------------------------- -IMPLEMENT_CLASS(D3DFB, false, false, false, false) +IMPLEMENT_CLASS(D3DFB, false, false) struct D3DFB::PackedTexture { diff --git a/src/win32/fb_ddraw.cpp b/src/win32/fb_ddraw.cpp index bd5a4db23..85f0c4768 100644 --- a/src/win32/fb_ddraw.cpp +++ b/src/win32/fb_ddraw.cpp @@ -61,7 +61,7 @@ // TYPES ------------------------------------------------------------------- -IMPLEMENT_CLASS(DDrawFB, false, false, false, false) +IMPLEMENT_CLASS(DDrawFB, false, false) // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- diff --git a/src/win32/i_crash.cpp b/src/win32/i_crash.cpp index d4804ec0e..4a0a1b160 100644 --- a/src/win32/i_crash.cpp +++ b/src/win32/i_crash.cpp @@ -75,54 +75,6 @@ #include #include -#if defined(_WIN64) && defined(__GNUC__) -struct KNONVOLATILE_CONTEXT_POINTERS { - union { - PDWORD64 IntegerContext[16]; - struct { - PDWORD64 Rax; - PDWORD64 Rcx; - PDWORD64 Rdx; - PDWORD64 Rbx; - PDWORD64 Rsp; - PDWORD64 Rbp; - PDWORD64 Rsi; - PDWORD64 Rdi; - PDWORD64 R8; - PDWORD64 R9; - PDWORD64 R10; - PDWORD64 R11; - PDWORD64 R12; - PDWORD64 R13; - PDWORD64 R14; - PDWORD64 R15; - }; - }; -}; -typedef -EXCEPTION_DISPOSITION -NTAPI -EXCEPTION_ROUTINE ( - struct _EXCEPTION_RECORD *ExceptionRecord, - PVOID EstablisherFrame, - struct _CONTEXT *ContextRecord, - PVOID DispatcherContext - ); -NTSYSAPI -EXCEPTION_ROUTINE * -NTAPI -RtlVirtualUnwind ( - DWORD HandlerType, - DWORD64 ImageBase, - DWORD64 ControlPc, - PRUNTIME_FUNCTION FunctionEntry, - PCONTEXT ContextRecord, - PVOID *HandlerData, - PDWORD64 EstablisherFrame, - KNONVOLATILE_CONTEXT_POINTERS *ContextPointers - ); -#endif - // MACROS ------------------------------------------------------------------ #define REMOTE_HOST "localhost" diff --git a/src/win32/i_keyboard.cpp b/src/win32/i_keyboard.cpp index cdcf9d8d8..e62942e8d 100644 --- a/src/win32/i_keyboard.cpp +++ b/src/win32/i_keyboard.cpp @@ -21,6 +21,11 @@ #define DINPUT_BUFFERSIZE 32 +// MinGW-w64 (TDM5.1 - 2016/11/21) +#ifndef DIK_PREVTRACK +#define DIK_PREVTRACK DIK_CIRCUMFLEX +#endif + // TYPES ------------------------------------------------------------------- class FDInputKeyboard : public FKeyboard diff --git a/src/win32/i_rawps2.cpp b/src/win32/i_rawps2.cpp index 50de1d0f4..940ef4bb1 100644 --- a/src/win32/i_rawps2.cpp +++ b/src/win32/i_rawps2.cpp @@ -389,7 +389,7 @@ bool FRawPS2Controller::ProcessInput(RAWHID *raw, int code) { // w32api has an incompatible definition of bRawData. // (But the version that comes with MinGW64 is fine.) -#if defined(__GNUC__) && !defined(_WIN64) +#if defined(__GNUC__) && !defined(__MINGW64_VERSION_MAJOR) BYTE *rawdata = &raw->bRawData; #else BYTE *rawdata = raw->bRawData; diff --git a/src/win32/i_specialpaths.cpp b/src/win32/i_specialpaths.cpp index 6d4890ea9..cddeacde6 100644 --- a/src/win32/i_specialpaths.cpp +++ b/src/win32/i_specialpaths.cpp @@ -33,6 +33,7 @@ ** */ +#define _WIN32_WINNT 0x0601 #include #include #include @@ -45,6 +46,15 @@ #include "optwin32.h" +// Vanilla MinGW does not have folder ids +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +static const GUID FOLDERID_LocalAppData = { 0xf1b32785, 0x6fba, 0x4fcf, 0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91 }; +static const GUID FOLDERID_RoamingAppData = { 0x3eb685db, 0x65f9, 0x4cf6, 0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d }; +static const GUID FOLDERID_SavedGames = { 0x4c5c32ff, 0xbb9d, 0x43b0, 0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4 }; +static const GUID FOLDERID_Documents = { 0xfdd39ad0, 0x238f, 0x46af, 0xad, 0xb4, 0x6c, 0x85, 0x48, 0x03, 0x69, 0xc7 }; +static const GUID FOLDERID_Pictures = { 0x33e28130, 0x4e1e, 0x4676, 0x83, 0x5a, 0x98, 0x39, 0x5c, 0x3b, 0xc3, 0xbb }; +#endif + //=========================================================================== // // IsProgramDirectoryWritable diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index aae738d74..b25b90a88 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1314,7 +1314,7 @@ static HCURSOR CreateCompatibleCursor(FTexture *cursorpic) HDC dc = GetDC(NULL); if (dc == NULL) { - return false; + return nullptr; } HDC and_mask_dc = CreateCompatibleDC(dc); HDC xor_mask_dc = CreateCompatibleDC(dc); @@ -1721,7 +1721,7 @@ FString I_GetLongPathName(FString shortpath) using OptWin32::GetLongPathNameA; // Doesn't exist on NT4 - if (!GetLongPathName) + if (!GetLongPathNameA) return shortpath; DWORD buffsize = GetLongPathNameA(shortpath.GetChars(), NULL, 0); diff --git a/src/win32/i_xinput.cpp b/src/win32/i_xinput.cpp index 1180f9f32..22c2dd714 100644 --- a/src/win32/i_xinput.cpp +++ b/src/win32/i_xinput.cpp @@ -33,6 +33,17 @@ #define XUSER_MAX_COUNT 4 #endif +// MinGW +#ifndef XINPUT_DLL +#define XINPUT_DLL_A "xinput1_3.dll" +#define XINPUT_DLL_W L"xinput1_3.dll" +#ifdef UNICODE + #define XINPUT_DLL XINPUT_DLL_W +#else + #define XINPUT_DLL XINPUT_DLL_A +#endif +#endif + // TYPES ------------------------------------------------------------------- typedef DWORD (WINAPI *XInputGetStateType)(DWORD index, XINPUT_STATE *state); diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index df96f7f41..0d91ed1d0 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -80,7 +80,7 @@ // TYPES ------------------------------------------------------------------- -IMPLEMENT_CLASS(BaseWinFB, true, false, false, false) +IMPLEMENT_CLASS(BaseWinFB, true, false) typedef IDirect3D9 *(WINAPI *DIRECT3DCREATE9FUNC)(UINT SDKVersion); typedef HRESULT (WINAPI *DIRECTDRAWCREATEFUNC)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter); diff --git a/src/zstring.cpp b/src/zstring.cpp index ac047f05c..eee2c4d57 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -397,6 +397,7 @@ void FString::Remove(size_t index, size_t remlen) if (Data()->RefCount == 1) { // Can do this in place memmove(Chars + index, Chars + index + remlen, Len() - index - remlen); + memset(Chars + Len() - remlen, 0, remlen); Data()->Len -= (unsigned)remlen; } else diff --git a/src/zzautozend.cpp b/src/zzautozend.cpp index 5567b1ddd..18c020310 100644 --- a/src/zzautozend.cpp +++ b/src/zzautozend.cpp @@ -43,6 +43,9 @@ __declspec(allocate(".areg$z")) void *const ARegTail = 0; #pragma section(".creg$z",read) __declspec(allocate(".creg$z")) void *const CRegTail = 0; +#pragma section(".freg$z",read) +__declspec(allocate(".freg$z")) void *const FRegTail = 0; + #pragma section(".greg$z",read) __declspec(allocate(".greg$z")) void *const GRegTail = 0; @@ -56,6 +59,7 @@ __declspec(allocate(".yreg$z")) void *const YRegTail = 0; void *const ARegTail __attribute__((section(SECTION_AREG))) = 0; void *const CRegTail __attribute__((section(SECTION_CREG))) = 0; +void *const FRegTail __attribute__((section(SECTION_FREG))) = 0; void *const GRegTail __attribute__((section(SECTION_GREG))) = 0; void *const YRegTail __attribute__((section(SECTION_YREG))) = 0; diff --git a/tools/updaterevision/CMakeLists.txt b/tools/updaterevision/CMakeLists.txt index 22890472a..db99ab812 100644 --- a/tools/updaterevision/CMakeLists.txt +++ b/tools/updaterevision/CMakeLists.txt @@ -1,19 +1,12 @@ cmake_minimum_required( VERSION 2.8.7 ) if( WIN32 ) - if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE OR ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) - add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/trustinfo.o - COMMAND windres -o ${CMAKE_CURRENT_BINARY_DIR}/trustinfo.o -i ${CMAKE_CURRENT_SOURCE_DIR}/trustinfo.rc - DEPENDS trustinfo.rc ) - set( TRUSTINFO trustinfo.o ) + if( MSVC_VERSION GREATER 1399 ) + # VC 8+ adds a manifest automatically to the executable. We need to + # merge ours with it. + set( MT_MERGE ON ) else() - if( MSVC_VERSION GREATER 1399 ) - # VC 8+ adds a manifest automatically to the executable. We need to - # merge ours with it. - set( MT_MERGE ON ) - else( MSVC_VERSION GREATER 1399 ) - set( TRUSTINFO trustinfo.rc ) - endif( MSVC_VERSION GREATER 1399 ) + set( TRUSTINFO trustinfo.rc ) endif() else( WIN32 ) set( TRUSTINFO "" ) diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 047bb1c80..4d24314d5 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -4,6 +4,10 @@ zscript/actor.txt zscript/actor_checks.txt zscript/shared/inventory.txt +zscript/shared/inv_misc.txt +zscript/shared/weapons.txt +zscript/shared/armor.txt +zscript/shared/powerups.txt zscript/shared/player.txt zscript/shared/morph.txt zscript/shared/botstuff.txt @@ -12,7 +16,7 @@ zscript/shared/blood.txt zscript/shared/debris.txt zscript/shared/decal.txt zscript/shared/splashes.txt -zscript/shared/pickups.txt +zscript/shared/itemeffects.txt zscript/shared/fountain.txt zscript/shared/spark.txt zscript/shared/soundsequence.txt @@ -31,6 +35,7 @@ zscript/shared/setcolor.txt zscript/shared/sectoraction.txt zscript/shared/ice.txt zscript/shared/dog.txt +zscript/shared/fastprojectile.txt zscript/shared/dynlights.txt zscript/doom/doomplayer.txt @@ -49,6 +54,15 @@ zscript/doom/cyberdemon.txt zscript/doom/spidermaster.txt zscript/doom/keen.txt zscript/doom/bossbrain.txt +zscript/doom/weaponfist.txt +zscript/doom/weaponpistol.txt +zscript/doom/weaponshotgun.txt +zscript/doom/weaponssg.txt +zscript/doom/weaponchaingun.txt +zscript/doom/weaponchainsaw.txt +zscript/doom/weaponrlaunch.txt +zscript/doom/weaponplasma.txt +zscript/doom/weaponbfg.txt zscript/doom/deadthings.txt zscript/doom/doomammo.txt @@ -76,7 +90,6 @@ zscript/heretic/hereticartifacts.txt zscript/heretic/heretickeys.txt zscript/heretic/hereticdecorations.txt zscript/heretic/hereticmisc.txt -zscript/heretic/hereticweaps.txt zscript/heretic/mummy.txt zscript/heretic/clink.txt zscript/heretic/beast.txt @@ -87,6 +100,14 @@ zscript/heretic/wizard.txt zscript/heretic/ironlich.txt zscript/heretic/dsparil.txt zscript/heretic/chicken.txt +zscript/heretic/weaponstaff.txt +zscript/heretic/weaponwand.txt +zscript/heretic/weaponcrossbow.txt +zscript/heretic/weapongauntlets.txt +zscript/heretic/weaponmace.txt +zscript/heretic/weaponblaster.txt +zscript/heretic/weaponskullrod.txt +zscript/heretic/weaponphoenix.txt zscript/hexen/baseweapons.txt zscript/hexen/korax.txt @@ -150,6 +171,7 @@ zscript/strife/coin.txt zscript/strife/crusader.txt zscript/strife/entityboss.txt zscript/strife/inquisitor.txt +zscript/strife/klaxon.txt zscript/strife/loremaster.txt zscript/strife/macil.txt zscript/strife/merchants.txt @@ -165,12 +187,20 @@ zscript/strife/sentinel.txt zscript/strife/stalker.txt zscript/strife/strifeammo.txt zscript/strife/strifearmor.txt +zscript/strife/strifefunctions.txt zscript/strife/strifeitems.txt zscript/strife/strifekeys.txt zscript/strife/strifestuff.txt zscript/strife/thingstoblowup.txt zscript/strife/templar.txt zscript/strife/zombie.txt +zscript/strife/weapondagger.txt +zscript/strife/weaponcrossbow.txt +zscript/strife/weaponassault.txt +zscript/strife/weaponmissile.txt +zscript/strife/weaponflamer.txt +zscript/strife/weapongrenade.txt +zscript/strife/weaponmauler.txt zscript/strife/sigil.txt zscript/chex/chexmonsters.txt diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 8e8b2fdd8..bd00ffc98 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -1,6 +1,199 @@ class Actor : Thinker native { const DEFAULT_HEALTH = 1000; + const ONFLOORZ = -2147483648.0; + const ONCEILINGZ = 2147483647.0; + const FLOATRANDZ = ONCEILINGZ-1; + const TELEFRAG_DAMAGE = 1000000; + const MinVel = 1./65536; + const LARGE_MASS = 10000000; // not INT_MAX on purpose + + + // flags are not defined here, the native fields for those get synthesized from the internal tables. + + // for some comments on these fields, see their native representations in actor.h. + native readonly Actor snext; // next in sector list. + native PlayerInfo Player; + native readonly vector3 Pos; + native vector3 Prev; + native double spriteAngle; + native double spriteRotation; + native double VisibleStartAngle; + native double VisibleStartPitch; + native double VisibleEndAngle; + native double VisibleEndPitch; + native double Angle; + native double Pitch; + native double Roll; + native vector3 Vel; + native double Speed; + native double FloatSpeed; + native SpriteID sprite; + native uint8 frame; + native vector2 Scale; + native TextureID picnum; + native double Alpha; + native readonly color fillcolor; // must be set with SetShade to initialize correctly. + native Sector CurSector; + native double CeilingZ; + native double FloorZ; + native double DropoffZ; + native Sector floorsector; + native TextureID floorpic; + native int floorterrain; + native Sector ceilingsector; + native TextureID ceilingpic; + native double Height; + native readonly double Radius; + native double projectilepassheight; + native int tics; + native readonly State CurState; + native readonly int Damage; + native int projectilekickback; + native int special1; + native int special2; + native double specialf1; + native double specialf2; + native int weaponspecial; + native int Health; + native uint8 movedir; + native int8 visdir; + native int16 movecount; + native int16 strafecount; + native Actor Target; + native Actor Master; + native Actor Tracer; + native Actor LastHeard; + native Actor LastEnemy; + native Actor LastLookActor; + native int ReactionTime; + native int Threshold; + native readonly int DefThreshold; + native readonly vector3 SpawnPoint; + native readonly uint16 SpawnAngle; + native int StartHealth; + native uint8 WeaveIndexXY; + native uint8 WeaveIndexZ; + native int skillrespawncount; + native int Args[5]; + native int Mass; + native int Special; + native readonly int TID; + native readonly int TIDtoHate; + native readonly int WaterLevel; + native int Score; + native int Accuracy; + native int Stamina; + native double MeleeRange; + native int PainThreshold; + native double Gravity; + native double FloorClip; + native name DamageType; + native name DamageTypeReceived; + native uint8 FloatBobPhase; + native int RipperLevel; + native int RipLevelMin; + native int RipLevelMax; + native name Species; + native Actor Alternative; + native Actor goal; + native uint8 MinMissileChance; + native int8 LastLookPlayerNumber; + native uint SpawnFlags; + native double meleethreshold; + native double maxtargetrange; + native double bouncefactor; + native double wallbouncefactor; + native int bouncecount; + native double friction; + native int FastChaseStrafeCount; + native double pushfactor; + native int lastpush; + native int activationtype; + native int lastbump; + native int DesignatedTeam; + native Actor BlockingMobj; + native int PoisonDamage; + native name PoisonDamageType; + native int PoisonDuration; + native int PoisonPeriod; + native int PoisonDamageReceived; + native name PoisonDamageTypeReceived; + native int PoisonDurationReceived; + native int PoisonPeriodReceived; + native Actor Poisoner; + native Inventory Inv; + native uint8 smokecounter; + native uint8 FriendPlayer; + native uint Translation; + native sound AttackSound; + native sound DeathSound; + native sound SeeSound; + native sound PainSound; + native sound ActiveSound; + native sound UseSound; + native sound BounceSound; + native sound WallBounceSound; + native sound CrushPainSound; + native double MaxDropoffHeight; + native double MaxStepHeight; + native int16 PainChance; + native name PainType; + native name DeathType; + native double DamageFactor; + native double DamageMultiply; + native Class TelefogSourceType; + native Class TelefogDestType; + native readonly State SpawnState; + native readonly State SeeState; + native State MeleeState; + native State MissileState; + + native meta String Obituary; // Player was killed by this actor + native meta String HitObituary; // Player was killed by this actor in melee + native meta double DeathHeight; // Height on normal death + native meta double BurnHeight; // Height on burning death + native meta color BloodColor; // Colorized blood + native meta int GibHealth; // Negative health below which this monster dies an extreme death + native meta int WoundHealth; // Health needed to enter wound state + native meta double FastSpeed; // speed in fast mode + native meta double RDFactor; // Radius damage factor + native meta double CameraHeight; // Height of camera when used as such + native meta Sound HowlSound; // Sound being played when electrocuted or poisoned + native meta Name BloodType; // Blood replacement type + native meta Name BloodType2; // Bloopsplatter replacement type + native meta Name BloodType3; // AxeBlood replacement type + native meta bool DontHurtShooter; + native meta int ExplosionRadius; + native meta int ExplosionDamage; + native meta int MeleeDamage; + native meta Sound MeleeSound; + native meta Name MissileName; + native meta double MissileHeight; + + + // need some definition work first + //FRenderStyle RenderStyle; + //line_t *BlockingLine; // Line that blocked the last move + //int ConversationRoot; // THe root of the current dialogue + //DecalBase DecalGenerator; + + // deprecated things. + native readonly deprecated double X; + native readonly deprecated double Y; + native readonly deprecated double Z; + native readonly deprecated double VelX; + native readonly deprecated double VelY; + native readonly deprecated double VelZ; + native readonly deprecated double MomX; + native readonly deprecated double MomY; + native readonly deprecated double MomZ; + native deprecated double ScaleX; + native deprecated double ScaleY; + + //int ConversationRoot; // THe root of the current dialogue; + //FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is used.; + Default { @@ -47,9 +240,16 @@ class Actor : Thinker native VisiblePitch 0, 0; DefaultStateUsage SUF_ACTOR|SUF_OVERLAY; } - + // Functions + // 'parked' global functions. + native static double deltaangle(double ang1, double ang2); + native static double absangle(double ang1, double ang2); + native static Vector2 AngleToVector(double angle, double length = 1); + native static Vector2 RotateVector(Vector2 vec, double angle); + + bool IsPointerEqual(int ptr_select1, int ptr_select2) { return GetPointer(ptr_select1) == GetPointer(ptr_select2); @@ -59,87 +259,197 @@ class Actor : Thinker native { return sin(fb * (180./32)) * 8; } + + virtual native void BeginPlay(); + virtual native void Activate(Actor activator); + virtual native void Deactivate(Actor activator); + virtual native int DoSpecialDamage (Actor target, int damage, Name damagetype); + virtual native int TakeSpecialDamage (Actor inflictor, Actor source, int damage, Name damagetype); + virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0); + virtual native bool Slam(Actor victim); + virtual native void Touch(Actor toucher); + + // Called when an actor is to be reflected by a disc of repulsion. + // Returns true to continue normal blast processing. + virtual bool SpecialBlastHandling (Actor source, double strength) // this is entirely on the script side with no native part at all. + { + return true; + } + + virtual int SpecialMissileHit (Actor victim) // for this no native version exists + { + return -1; + } - native static readonly GetDefaultByType(class cls); - native static float deltaangle(float ang1, float ang2); - native static float GetDefaultSpeed(class type); - native float GetBobOffset(float frac = 0); + native static int GetSpriteIndex(name sprt); + native static double GetDefaultSpeed(class type); + native static class GetSpawnableType(int spawnnum); + native void RemoveFromHash(); + native void ChangeTid(int newtid); + native static int FindUniqueTid(int start = 0, int limit = 0); + native void SetShade(color col); + + native string GetTag(string defstr = ""); + native void SetTag(string defstr = ""); + native double GetBobOffset(double frac = 0); + native void ClearCounters(); + native bool GiveBody (int num, int max=0); + native bool HitFloor(); + native bool isTeammate(Actor other); + native int PlayerNumber(); + native void SetFriendPlayer(PlayerInfo player); + native void NoiseAlert(Actor target, bool splash = false, double maxdist = 0); + native void DaggerAlert(Actor target); + native void ClearBounce(); + native TerrainDef GetFloorTerrain(); + native Inventory DoDropItem(Class type, int dropamount, int chance); + native bool CheckLocalView(int consoleplayer); + + native void ExplodeMissile(line lin = null, Actor target = null); + native void RestoreDamage(); + native int SpawnHealth(); native void SetDamage(int dmg); - native static bool isDehState(state st); native double Distance2D(Actor other); + native double Distance3D(Actor other); native void SetOrigin(vector3 newpos, bool moving); native void SetXYZ(vector3 newpos); native Actor GetPointer(int aaptr); - native void FaceMovementDirection(); + native double BulletSlope(out FTranslatedLineTarget pLineTarget = null, int aimflags = 0); native Actor AimTarget(); + native bool CheckMissileSpawn(double maxdist); + native bool CheckPosition(Vector2 pos, bool actorsonly = false); + native bool TestMobjLocation(); native static Actor Spawn(class type, vector3 pos = (0,0,0), int replace = NO_REPLACE); native Actor SpawnMissile(Actor dest, class type, Actor owner = null); + native Actor SpawnMissileXYZ(Vector3 pos, Actor dest, Class type, bool checkspawn = true, Actor owner = null); native Actor SpawnMissileZ (double z, Actor dest, class type); - native Actor SpawnMissileAngleZSpeed (double z, class type, float angle, double vz, double speed, Actor owner = null, bool checkspawn = true); + native Actor SpawnMissileAngleZSpeed (double z, class type, double angle, double vz, double speed, Actor owner = null, bool checkspawn = true); + native Actor SpawnMissileZAimed (double z, Actor dest, Class type); + native Actor SpawnSubMissile(Class type, Actor target); + native Actor, Actor SpawnPlayerMissile(class type, double angle = 0, double x = 0, double y = 0, double z = 0, out FTranslatedLineTarget pLineTarget = null, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0); + native void SpawnTeleportFog(Vector3 pos, bool beforeTele, bool setTarget); + native Actor RoughMonsterSearch(int distance, bool onlyseekable = false, bool frontonly = false); + native int ApplyDamageFactor(Name damagetype, int damage); + native int GetModifiedDamage(Name damagetype, int damage, bool passive); + native bool CheckBossDeath(); - native void A_Light(int extralight); - void A_Light0() { A_Light(0); } - void A_Light1() { A_Light(1); } - void A_Light2() { A_Light(2); } - void A_LightInverse() { A_Light(0x80000000); } + void A_Light(int extralight) { if (player) player.extralight = clamp(extralight, -20, 20); } + void A_Light0() { if (player) player.extralight = 0; } + void A_Light1() { if (player) player.extralight = 1; } + void A_Light2() { if (player) player.extralight = 2; } + void A_LightInverse() { if (player) player.extralight = 0x80000000; } native Actor OldSpawnMissile(Actor dest, class type, Actor owner = null); - native Actor SpawnPuff(class pufftype, vector3 pos, float hitdir, float particledir, int updown, int flags = 0, Actor vict = null); + native Actor SpawnPuff(class pufftype, vector3 pos, double hitdir, double particledir, int updown, int flags = 0, Actor victim = null); + native void SpawnBlood (Vector3 pos1, double dir, int damage); + native void BloodSplatter (Vector3 pos, double hitangle, bool axe = false); + native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false); + native void PlaySpawnSound(Actor missile); + native bool CountsAsKill(); + native bool Teleport(Vector3 pos, double angle, int flags); native void TraceBleed(int damage, Actor missile); + native void TraceBleedAngle(int damage, double angle, double pitch); + + native void SetIdle(bool nofunction = false); native bool CheckMeleeRange(); + native bool CheckMeleeRange2(); native int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0); + native void PoisonMobj (Actor inflictor, Actor source, int damage, int duration, int period, Name type); native double AimLineAttack(double angle, double distance, out FTranslatedLineTarget pLineTarget = null, double vrange = 0., int flags = 0, Actor target = null, Actor friender = null); native Actor, int LineAttack(double angle, double distance, double pitch, int damage, Name damageType, class pufftype, int flags = 0, out FTranslatedLineTarget victim = null); native bool CheckSight(Actor target, int flags = 0); + native bool IsVisible(Actor other, bool allaround, LookExParams params = null); native bool HitFriend(); native bool MonsterMove(); + + native void FindFloorCeiling(int flags = 0); + native double, double GetFriction(); + native bool, Actor TestMobjZ(bool quick = false); + native bool InStateSequence(State newstate, State basestate); + + bool TryWalk () + { + if (!MonsterMove ()) + { + return false; + } + movecount = random[TryWalk]() & 15; + return true; + } + + native bool TryMove(vector2 newpos, int dropoff); native void NewChaseDir(); + native void RandomChaseDir(); native bool CheckMissileRange(); native bool SetState(state st, bool nofunction = false); - native state FindState(statelabel st); // do we need exact later? + native state FindState(statelabel st, bool exact = false); bool SetStateLabel(statelabel st, bool nofunction = false) { return SetState(FindState(st), nofunction); } native action state ResolveState(statelabel st); // this one, unlike FindState, is context aware. native void LinkToWorld(); native void UnlinkFromWorld(); native bool CanSeek(Actor target); native double AngleTo(Actor target, bool absolute = false); - native void AddZ(float zadd); - native void SetZ(float z); - native vector3 Vec3Offset(float x, float y, float z, bool absolute = false); - native vector3 Vec3Angle(float length, float angle, float z = 0, bool absolute = false); - native vector3 Vec2OffsetZ(float x, float y, float atz, bool absolute = false); - native void VelFromAngle(float speed = 0, float angle = 0); - native void Thrust(float speed = 0, float angle = 0); + native void AddZ(double zadd, bool moving = true); + native void SetZ(double z); + native vector2 Vec2To(Actor other); + native vector3 Vec3To(Actor other); + native vector3 Vec3Offset(double x, double y, double z, bool absolute = false); + native vector3 Vec3Angle(double length, double angle, double z = 0, bool absolute = false); + native vector2 Vec2Angle(double length, double angle, bool absolute = false); + native vector2 Vec2Offset(double x, double y, bool absolute = false); + native vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false); + native void VelFromAngle(double speed = 0, double angle = 0); + native void Vel3DFromAngle(double speed, double angle, double pitch); + native void Thrust(double speed = 0, double angle = 0); native bool isFriend(Actor other); + native bool isHostile(Actor other); native void AdjustFloorClip(); native DropItem GetDropItems(); native void CopyFriendliness (Actor other, bool changeTarget, bool resetHealth = true); - native bool LookForPlayers(bool allaround); + native bool LookForMonsters(); + native bool LookForTid(bool allaround, LookExParams params = null); + native bool LookForEnemies(bool allaround, LookExParams params = null); + native bool LookForPlayers(bool allaround, LookExParams params = null); native bool TeleportMove(Vector3 pos, bool telefrag, bool modifyactor = true); native double DistanceBySpeed(Actor other, double speed); native name GetSpecies(); native void PlayActiveSound(); + native void Howl(); + native void DrawSplash (int count, double angle, int kind); + native void GiveSecret(bool printmsg = true, bool playsound = true); + native double GetCameraHeight(); + native double GetGravity(); + + native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); + native void AddInventory(Inventory inv); + native void RemoveInventory(Inventory inv); + native void ClearInventory(); + native Inventory FindInventory(class itemtype, bool subclass = false); + native Inventory GiveInventoryType(class itemtype); + native Inventory DropInventory (Inventory item); + native bool UseInventory(Inventory item); + native bool GiveAmmo (Class type, int amount); + native float AccuracyFactor(); // DECORATE compatible functions - native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native int CountInv(class itemtype, int ptr_select = AAPTR_DEFAULT); - native float GetDistance(bool checkz, int ptr = AAPTR_TARGET); - native float GetAngle(int flags, int ptr = AAPTR_DEFAULT); - native float GetZAt(float px = 0, float py = 0, float angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT); + native double GetDistance(bool checkz, int ptr = AAPTR_TARGET); + native double GetAngle(int flags, int ptr = AAPTR_TARGET); + native double GetZAt(double px = 0, double py = 0, double angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT); native int GetSpawnHealth(); native int GetGibHealth(); - native float GetCrouchFactor(int ptr = AAPTR_PLAYER1); - native float GetCVar(string cvar); + native double GetCrouchFactor(int ptr = AAPTR_PLAYER1); + native double GetCVar(string cvar); native int GetPlayerInput(int inputnum, int ptr = AAPTR_DEFAULT); - native int CountProximity(class classname, float distance, int flags = 0, int ptr = AAPTR_DEFAULT); - native float GetSpriteAngle(int ptr = AAPTR_DEFAULT); - native float GetSpriteRotation(int ptr = AAPTR_DEFAULT); + native int CountProximity(class classname, double distance, int flags = 0, int ptr = AAPTR_DEFAULT); + native double GetSpriteAngle(int ptr = AAPTR_DEFAULT); + native double GetSpriteRotation(int ptr = AAPTR_DEFAULT); native int GetMissileDamage(int mask, int add, int ptr = AAPTR_DEFAULT); action native int OverlayID(); - action native float OverlayX(int layer = 0); - action native float OverlayY(int layer = 0); + action native double OverlayX(int layer = 0); + action native double OverlayY(int layer = 0); // DECORATE setters - it probably makes more sense to set these values directly now... void A_SetMass(int newmass) { mass = newmass; } @@ -154,13 +464,13 @@ class Actor : Thinker native void A_NoGravity() { bNoGravity = true; } void A_Gravity() { bNoGravity = false; Gravity = 1; } void A_LowGravity() { bNoGravity = false; Gravity = 0.125; } - void A_SetGravity(float newgravity) { gravity = clamp(newgravity, 0., 10.); } + void A_SetGravity(double newgravity) { gravity = clamp(newgravity, 0., 10.); } void A_SetFloorClip() { bFloorClip = true; AdjustFloorClip(); } void A_UnSetFloorClip() { bFloorClip = false; FloorClip = 0; } void A_HideThing() { bInvisible = true; } void A_UnHideThing() { bInvisible = false; } void A_SetArg(int arg, int val) { if (arg >= 0 && arg < 5) args[arg] = val; } - void A_Turn(float turn = 0) { angle += turn; } + void A_Turn(double turn = 0) { angle += turn; } void A_SetDamageType(name newdamagetype) { damagetype = newdamagetype; } void A_SetSolid() { bSolid = true; } void A_UnsetSolid() { bSolid = false; } @@ -182,7 +492,7 @@ class Actor : Thinker native // //--------------------------------------------------------------------------- - Actor SpawnMissileAngle (class type, float angle, double vz) + Actor SpawnMissileAngle (class type, double angle, double vz) { return SpawnMissileAngleZSpeed (pos.z + 32 + GetBobOffset(), type, angle, vz, GetDefaultSpeed (type)); } @@ -193,7 +503,7 @@ class Actor : Thinker native } - void A_SetScale(float scalex, float scaley = 0, int ptr = AAPTR_DEFAULT, bool usezero = false) + void A_SetScale(double scalex, double scaley = 0, int ptr = AAPTR_DEFAULT, bool usezero = false) { Actor aptr = GetPointer(ptr); if (aptr) @@ -202,12 +512,12 @@ class Actor : Thinker native aptr.Scale.Y = scaley != 0 || usezero? scaley : scalex; // use scalex here, too, if only one parameter. } } - void A_SetSpeed(float speed, int ptr = AAPTR_DEFAULT) + void A_SetSpeed(double speed, int ptr = AAPTR_DEFAULT) { Actor aptr = GetPointer(ptr); if (aptr) aptr.Speed = speed; } - void A_SetFloatSpeed(float speed, int ptr = AAPTR_DEFAULT) + void A_SetFloatSpeed(double speed, int ptr = AAPTR_DEFAULT) { Actor aptr = GetPointer(ptr); if (aptr) aptr.FloatSpeed = speed; @@ -217,14 +527,14 @@ class Actor : Thinker native Actor aptr = GetPointer(ptr); if (aptr) aptr.PainThreshold = threshold; } - bool A_SetSpriteAngle(float angle = 0, int ptr = AAPTR_DEFAULT) + bool A_SetSpriteAngle(double angle = 0, int ptr = AAPTR_DEFAULT) { Actor aptr = GetPointer(ptr); if (!aptr) return false; aptr.SpriteAngle = angle; return true; } - bool A_SetSpriteRotation(float angle = 0, int ptr = AAPTR_DEFAULT) + bool A_SetSpriteRotation(double angle = 0, int ptr = AAPTR_DEFAULT) { Actor aptr = GetPointer(ptr); if (!aptr) return false; @@ -232,7 +542,7 @@ class Actor : Thinker native return true; } - deprecated void A_FaceConsolePlayer(float MaxTurnAngle = 0) {} + deprecated void A_FaceConsolePlayer(double MaxTurnAngle = 0) {} void A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0) { @@ -265,18 +575,70 @@ class Actor : Thinker native DamageMobj(null, null, health, damagetype, DMG_FORCED); } + void SpawnDirt (double radius) + { + static const class chunks[] = { "Dirt1", "Dirt2", "Dirt3", "Dirt4", "Dirt5", "Dirt6" }; + double zo = random[Dirt]() / 128. + 1; + Vector3 pos = Vec3Angle(radius, random[Dirt]() * (360./256), zo); - native void A_Face(Actor faceto, float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0); + Actor mo = Spawn (chunks[random[Dirt](0, 5)], pos, ALLOW_REPLACE); + if (mo) + { + mo.Vel.Z = random[Dirt]() / 64.; + } + } + + // + // A_SinkMobj + // Sink a mobj incrementally into the floor + // - void A_FaceTarget(float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0) + bool SinkMobj (double speed) + { + if (Floorclip < Height) + { + Floorclip += speed; + return false; + } + return true; + } + + // + // A_RaiseMobj + // Raise a mobj incrementally from the floor to + // + + bool RaiseMobj (double speed) + { + // Raise a mobj from the ground + if (Floorclip > 0) + { + Floorclip -= speed; + if (Floorclip <= 0) + { + Floorclip = 0; + return true; + } + else + { + return false; + } + } + return true; + } + + + native void A_Face(Actor faceto, double max_turn = 0, double max_pitch = 270, double ang_offset = 0, double pitch_offset = 0, int flags = 0, double z_ofs = 0); + + void A_FaceTarget(double max_turn = 0, double max_pitch = 270, double ang_offset = 0, double pitch_offset = 0, int flags = 0, double z_ofs = 0) { A_Face(target, max_turn, max_pitch, ang_offset, pitch_offset, flags, z_ofs); } - void A_FaceTracer(float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0) + void A_FaceTracer(double max_turn = 0, double max_pitch = 270, double ang_offset = 0, double pitch_offset = 0, int flags = 0, double z_ofs = 0) { A_Face(tracer, max_turn, max_pitch, ang_offset, pitch_offset, flags, z_ofs); } - void A_FaceMaster(float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0) + void A_FaceMaster(double max_turn = 0, double max_pitch = 270, double ang_offset = 0, double pitch_offset = 0, int flags = 0, double z_ofs = 0) { A_Face(master, max_turn, max_pitch, ang_offset, pitch_offset, flags, z_ofs); } @@ -287,7 +649,6 @@ class Actor : Thinker native // End of MBF redundant functions. native void A_MonsterRail(); - native void A_BFGSpray(class spraytype = "BFGExtra", int numrays = 40, int damagecount = 15, float angle = 90, float distance = 16*64, float vrange = 32, int damage = 0, int flags = 0); native void A_Pain(); native void A_NoBlocking(bool drop = true); void A_Fall() { A_NoBlocking(); } @@ -300,128 +661,104 @@ class Actor : Thinker native native void A_Detonate(); native bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0); - native void A_M_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff"); - native void A_ActiveSound(); native void A_FastChase(); native void A_FreezeDeath(); native void A_FreezeDeathChunks(); - native void A_GenericFreezeDeath(); - native void A_IceGuyDie(); - native void A_CStaffMissileSlither(); + void A_GenericFreezeDeath() + { + A_SetTranslation('Ice'); + A_FreezeDeath(); + } native void A_PlayerScream(); native void A_SkullPop(class skulltype = "BloodySkull"); native void A_CheckPlayerDone(); + native void A_CheckTerrain(); native void A_Wander(int flags = 0); native void A_Look2(); - native void A_TossGib(); - native void A_SentinelBob(); - native void A_SentinelRefire(); - native void A_Tracer2(); - native void A_SetShadow(); - native void A_ClearShadow(); - native void A_GetHurt(); - native void A_TurretLook(); - native void A_KlaxonBlare(); - native void A_Countdown(); - native void A_AlertMonsters(float maxdist = 0, int flags = 0); - native void A_ClearSoundTarget(); - native void A_CheckTerrain(); deprecated native void A_MissileAttack(); deprecated native void A_MeleeAttack(); deprecated native void A_ComboAttack(); deprecated native void A_BulletAttack(); - native void A_WolfAttack(int flags = 0, sound whattoplay = "weapons/pistol", float snipe = 1.0, int maxdamage = 64, int blocksize = 128, int pointblank = 2, int longrange = 4, float runspeed = 160.0, class pufftype = "BulletPuff"); - native void A_PlaySound(sound whattoplay = "weapons/pistol", int slot = CHAN_BODY, float volume = 1.0, bool looping = false, float attenuation = ATTN_NORM, bool local = false); + native void A_WolfAttack(int flags = 0, sound whattoplay = "weapons/pistol", double snipe = 1.0, int maxdamage = 64, int blocksize = 128, int pointblank = 2, int longrange = 4, double runspeed = 160.0, class pufftype = "BulletPuff"); + native void A_PlaySound(sound whattoplay = "weapons/pistol", int slot = CHAN_BODY, double volume = 1.0, bool looping = false, double attenuation = ATTN_NORM, bool local = false); deprecated void A_PlayWeaponSound(sound whattoplay) { A_PlaySound(whattoplay, CHAN_WEAPON); } - native void A_FLoopActiveSound(); - native void A_LoopActiveSound(); native void A_StopSound(int slot = CHAN_VOICE); // Bad default but that's what is originally was... deprecated native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0); deprecated native void A_StopSoundEx(name slot); native void A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10); native action state A_Jump(int chance, statelabel label, ...); - native void A_CustomMissile(class missiletype, float spawnheight = 32, float spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0, int ptr = AAPTR_TARGET); - native void A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET, class missile = null, float Spawnheight = 32, float Spawnofs_xy = 0); - native void A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = 0, color color2 = 0, int flags = 0, int aim = 0, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = null, float spawnofs_z = 0, int spiraloffset = 270, int limit = 0); + native void A_CustomMissile(class missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET); + native void A_CustomBulletAttack(double spread_xy, double spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", double range = 0, int flags = 0, int ptr = AAPTR_TARGET, class missile = null, double Spawnheight = 32, double Spawnofs_xy = 0); + native void A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = 0, color color2 = 0, int flags = 0, int aim = 0, double maxdiff = 0, class pufftype = "BulletPuff", double spread_xy = 0, double spread_z = 0, double range = 0, int duration = 0, double sparsity = 1.0, double driftspeed = 1.0, class spawnclass = null, double spawnofs_z = 0, int spiraloffset = 270, int limit = 0); native bool A_SetInventory(class itemtype, int amount, int ptr = AAPTR_DEFAULT, bool beyondMax = false); native bool A_GiveInventory(class itemtype, int amount = 0, int giveto = AAPTR_DEFAULT); native bool A_TakeInventory(class itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT); - action native bool A_SpawnItem(class itemtype = "Unknown", float distance = 0, float zheight = 0, bool useammo = true, bool transfer_translation = false); - native bool A_SpawnItemEx(class itemtype, float xofs = 0, float yofs = 0, float zofs = 0, float xvel = 0, float yvel = 0, float zvel = 0, float angle = 0, int flags = 0, int failchance = 0, int tid=0); - native void A_Print(string whattoprint, float time = 0, name fontname = "none"); - native void A_PrintBold(string whattoprint, float time = 0, name fontname = "none"); + action native bool A_SpawnItem(class itemtype = "Unknown", double distance = 0, double zheight = 0, bool useammo = true, bool transfer_translation = false); + native bool A_SpawnItemEx(class itemtype, double xofs = 0, double yofs = 0, double zofs = 0, double xvel = 0, double yvel = 0, double zvel = 0, double angle = 0, int flags = 0, int failchance = 0, int tid=0); + native void A_Print(string whattoprint, double time = 0, name fontname = "none"); + native void A_PrintBold(string whattoprint, double time = 0, name fontname = "none"); native void A_Log(string whattoprint); native void A_LogInt(int whattoprint); - native void A_LogFloat(float whattoprint); - native void A_SetTranslucent(float alpha, int style = 0); - native void A_SetRenderStyle(float alpha, int style); - native void A_FadeIn(float reduce = 0.1, int flags = 0); - native void A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true - native void A_FadeTo(float target, float amount = 0.1, int flags = 0); - native void A_SpawnDebris(class spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1); - native void A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, float size = 1, float angle = 0, float xoff = 0, float yoff = 0, float zoff = 0, float velx = 0, float vely = 0, float velz = 0, float accelx = 0, float accely = 0, float accelz = 0, float startalphaf = 1, float fadestepf = -1, float sizestep = 0); + native void A_LogFloat(double whattoprint); + native void A_SetTranslucent(double alpha, int style = 0); + native void A_SetRenderStyle(double alpha, int style); + native void A_FadeIn(double reduce = 0.1, int flags = 0); + native void A_FadeOut(double reduce = 0.1, int flags = 1); //bool remove == true + native void A_FadeTo(double target, double amount = 0.1, int flags = 0); + native void A_SpawnDebris(class spawntype, bool transfer_translation = false, double mult_h = 1, double mult_v = 1); + native void A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, double size = 1, double angle = 0, double xoff = 0, double yoff = 0, double zoff = 0, double velx = 0, double vely = 0, double velz = 0, double accelx = 0, double accely = 0, double accelz = 0, double startalphaf = 1, double fadestepf = -1, double sizestep = 0); native void A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false); native void A_DropInventory(class itemtype); - native void A_SetBlend(color color1, float alpha, int tics, color color2 = 0); + native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0); deprecated native void A_ChangeFlag(string flagname, bool value); native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE); native void A_RaiseMaster(bool copy = 0); native void A_RaiseChildren(bool copy = 0); native void A_RaiseSiblings(bool copy = 0); - deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); - action native bool A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); - native void A_Weave(int xspeed, int yspeed, float xdist, float ydist); + deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class missiletype, double missileheight); + action native bool A_ThrowGrenade(class itemtype, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true); + native void A_Weave(int xspeed, int yspeed, double xdist, double ydist); - action native state, bool A_Teleport(statelabel teleportstate = null, class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 128, int ptr = AAPTR_DEFAULT); - action native state, bool A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, statelabel success_state = null, float heightoffset = 0, float radiusoffset = 0, float pitch = 0); + action native state, bool A_Teleport(statelabel teleportstate = null, class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, double mindist = 0, double maxdist = 128, int ptr = AAPTR_DEFAULT); + action native state, bool A_Warp(int ptr_destination, double xofs = 0, double yofs = 0, double zofs = 0, double angle = 0, int flags = 0, statelabel success_state = null, double heightoffset = 0, double radiusoffset = 0, double pitch = 0); native void A_CountdownArg(int argnum, statelabel targstate = null); native state A_MonsterRefire(int chance, statelabel label); - native void A_LookEx(int flags = 0, float minseedist = 0, float maxseedist = 0, float maxheardist = 0, float fov = 0, statelabel label = null); + native void A_LookEx(int flags = 0, double minseedist = 0, double maxseedist = 0, double maxheardist = 0, double fov = 0, statelabel label = null); - native void A_Recoil(float xyvel); + native void A_Recoil(double xyvel); native bool A_GiveToTarget(class itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT); native bool A_TakeFromTarget(class itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT); - native int A_RadiusGive(class itemtype, float distance, int flags, int amount = 0, class filter = null, name species = "None", float mindist = 0, int limit = 0); + native int A_RadiusGive(class itemtype, double distance, int flags, int amount = 0, class filter = null, name species = "None", double mindist = 0, int limit = 0); native void A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true); - native void A_CustomComboAttack(class missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); + native void A_CustomComboAttack(class missiletype, double spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); native void A_Burst(class chunktype); - action native void A_Blast(int flags = 0, float strength = 255, float radius = 255, float speed = 20, class blasteffect = "BlastEffect", sound blastsound = "BlastRadius"); native void A_RadiusThrust(int force = 128, int distance = -1, int flags = RTF_AFFECTSOURCE, int fullthrustdistance = 0); - native void A_RadiusDamageSelf(int damage = 128, float distance = 128, int flags = 0, class flashtype = null); + native void A_RadiusDamageSelf(int damage = 128, double distance = 128, int flags = 0, class flashtype = null); native int A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff", name damagetype = "none"); native void A_Stop(); native void A_Respawn(int flags = 1); + native void A_RestoreSpecialPosition(); native void A_QueueCorpse(); native void A_DeQueueCorpse(); native void A_ClearLastHeard(); native bool A_SelectWeapon(class whichweapon, int flags = 0); - action native void A_Punch(); - native void A_Feathers(); native void A_ClassBossHealth(); - native void A_ShootGun(); - native void A_RocketInFlight(); - native void A_Bang4Cloud(); - native void A_DropFire(); - native void A_GiveQuestItem(int itemno); - native void A_RemoveForcefield(); - native void A_DropWeaponPieces(class p1, class p2, class p3); - native void A_PigPain (); - native void A_SetAngle(float angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); - native void A_SetPitch(float pitch, int flags = 0, int ptr = AAPTR_DEFAULT); - native void A_SetRoll(float roll, int flags = 0, int ptr = AAPTR_DEFAULT); - native void A_ScaleVelocity(float scale, int ptr = AAPTR_DEFAULT); - native void A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_ScaleVelocity(double scale, int ptr = AAPTR_DEFAULT); + native void A_ChangeVelocity(double x = 0, double y = 0, double z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); deprecated native void A_SetUserVar(name varname, int value); deprecated native void A_SetUserArray(name varname, int index, int value); - deprecated native void A_SetUserVarFloat(name varname, float value); - deprecated native void A_SetUserArrayFloat(name varname, int index, float value); + deprecated native void A_SetUserVarFloat(name varname, double value); + deprecated native void A_SetUserArrayFloat(name varname, int index, double value); native void A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake"); - native void A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1, int falloff = 0, int highpoint = 0, float rollIntensity = 0, float rollWave = 0); + native void A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, double mulWaveX = 1, double mulWaveY = 1, double mulWaveZ = 1, int falloff = 0, int highpoint = 0, double rollIntensity = 0, double rollWave = 0); action native void A_SetTics(int tics); native void A_DropItem(class item, int dropamount = -1, int chance = 256); native void A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = null, name species = "None", int src = AAPTR_DEFAULT, int inflict = AAPTR_DEFAULT); @@ -451,19 +788,19 @@ class Actor : Thinker native native void A_ResetHealth(int ptr = AAPTR_DEFAULT); native void A_SetSpecies(name species, int ptr = AAPTR_DEFAULT); native void A_SetChaseThreshold(int threshold, bool def = false, int ptr = AAPTR_DEFAULT); - native bool A_FaceMovementDirection(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + native bool A_FaceMovementDirection(double offset = 0, double anglelimit = 0, double pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT); native int A_ClearOverlays(int sstart = 0, int sstop = 0, bool safety = true); native bool A_CopySpriteFrame(int from, int to, int flags = 0); - native bool A_SetVisibleRotation(float anglestart = 0, float angleend = 0, float pitchstart = 0, float pitchend = 0, int flags = 0, int ptr = AAPTR_DEFAULT); - native void A_SetTranslation(string transname); + native bool A_SetVisibleRotation(double anglestart = 0, double angleend = 0, double pitchstart = 0, double pitchend = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetTranslation(name transname); native void A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0); native void A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); native void A_CopyFriendliness(int ptr_source = AAPTR_MASTER); action native bool A_Overlay(int layer, statelabel start = null, bool nooverride = false); - native void A_WeaponOffset(float wx = 0, float wy = 32, int flags = 0); - action native void A_OverlayOffset(int layer = PSP_WEAPON, float wx = 0, float wy = 32, int flags = 0); + native void A_WeaponOffset(double wx = 0, double wy = 32, int flags = 0); + action native void A_OverlayOffset(int layer = PSP_WEAPON, double wx = 0, double wy = 32, int flags = 0); action native void A_OverlayFlags(int layer, int flags, bool set); int ACS_NamedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0) @@ -516,5 +853,5 @@ class Actor : Thinker native // Internal functions deprecated private native int __decorate_internal_int__(int i); deprecated private native bool __decorate_internal_bool__(bool b); - deprecated private native float __decorate_internal_float__(float f); + deprecated private native double __decorate_internal_float__(double f); } diff --git a/wadsrc/static/zscript/actor_checks.txt b/wadsrc/static/zscript/actor_checks.txt index 806539753..a4f7da1d8 100644 --- a/wadsrc/static/zscript/actor_checks.txt +++ b/wadsrc/static/zscript/actor_checks.txt @@ -31,18 +31,18 @@ extend class Actor // //========================================================================== - bool CheckIfCloser(Actor targ, float dist, bool noz = false) + bool CheckIfCloser(Actor targ, double dist, bool noz = false) { if (!targ) return false; return - (Distance2D(target) < dist && (noz || + (Distance2D(targ) < dist && (noz || ((pos.z > targ.pos.z && pos.z - targ.pos.z - targ.height < dist) || (pos.z <= targ.pos.z && targ.pos.z - pos.z - height < dist) ) )); } - action state A_JumpIfCloser(float distance, statelabel label, bool noz = false) + action state A_JumpIfCloser(double distance, statelabel label, bool noz = false) { Actor targ; @@ -59,12 +59,12 @@ extend class Actor return CheckIfCloser(targ, distance, noz)? ResolveState(label) : null; } - action state A_JumpIfTracerCloser(float distance, statelabel label, bool noz = false) + action state A_JumpIfTracerCloser(double distance, statelabel label, bool noz = false) { return CheckIfCloser(tracer, distance, noz)? ResolveState(label) : null; } - action state A_JumpIfMasterCloser(float distance, statelabel label, bool noz = false) + action state A_JumpIfMasterCloser(double distance, statelabel label, bool noz = false) { return CheckIfCloser(master, distance, noz)? ResolveState(label) : null; } @@ -124,20 +124,20 @@ extend class Actor //========================================================================== native bool CheckIfSeen(); - native bool CheckSightOrRange(float distance, bool two_dimension = false); - native bool CheckRange(float distance, bool two_dimension = false); + native bool CheckSightOrRange(double distance, bool two_dimension = false); + native bool CheckRange(double distance, bool two_dimension = false); action state A_CheckSight(statelabel label) { return CheckIfSeen()? ResolveState(label) : null; } - action state A_CheckSightOrRange(float distance, statelabel label, bool two_dimension = false) + action state A_CheckSightOrRange(double distance, statelabel label, bool two_dimension = false) { return CheckSightOrRange(distance, two_dimension)? ResolveState(label) : null; } - action state A_CheckRange(float distance, statelabel label, bool two_dimension = false) + action state A_CheckRange(double distance, statelabel label, bool two_dimension = false) { return CheckRange(distance, two_dimension)? ResolveState(label) : null; } @@ -202,28 +202,28 @@ extend class Actor // //========================================================================== - native bool CheckLOF(int flags = 0, float range = 0, float minrange = 0, float angle = 0, float pitch = 0, float offsetheight = 0, float offsetwidth = 0, int ptr_target = AAPTR_DEFAULT, float offsetforward = 0); - native bool CheckIfTargetInLOS (float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0); - native bool CheckIfInTargetLOS (float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0); - native bool CheckProximity(class classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT); - native bool CheckBlock(int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0); + native bool CheckLOF(int flags = 0, double range = 0, double minrange = 0, double angle = 0, double pitch = 0, double offsetheight = 0, double offsetwidth = 0, int ptr_target = AAPTR_DEFAULT, double offsetforward = 0); + native bool CheckIfTargetInLOS (double fov = 0, int flags = 0, double dist_max = 0, double dist_close = 0); + native bool CheckIfInTargetLOS (double fov = 0, int flags = 0, double dist_max = 0, double dist_close = 0); + native bool CheckProximity(class classname, double distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT); + native bool CheckBlock(int flags = 0, int ptr = AAPTR_DEFAULT, double xofs = 0, double yofs = 0, double zofs = 0, double angle = 0); - action state A_CheckLOF(statelabel label, int flags = 0, float range = 0, float minrange = 0, float angle = 0, float pitch = 0, float offsetheight = 0, float offsetwidth = 0, int ptr_target = AAPTR_DEFAULT, float offsetforward = 0) + action state A_CheckLOF(statelabel label, int flags = 0, double range = 0, double minrange = 0, double angle = 0, double pitch = 0, double offsetheight = 0, double offsetwidth = 0, int ptr_target = AAPTR_DEFAULT, double offsetforward = 0) { return CheckLOF(flags, range, minrange, angle, pitch, offsetheight, offsetwidth, ptr_target, offsetforward)? ResolveState(label) : null; } - action state A_JumpIfTargetInLOS (statelabel label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0) + action state A_JumpIfTargetInLOS (statelabel label, double fov = 0, int flags = 0, double dist_max = 0, double dist_close = 0) { return CheckIfTargetInLOS(fov, flags, dist_max, dist_close)? ResolveState(label) : null; } - action state A_JumpIfInTargetLOS (statelabel label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0) + action state A_JumpIfInTargetLOS (statelabel label, double fov = 0, int flags = 0, double dist_max = 0, double dist_close = 0) { return CheckIfInTargetLOS(fov, flags, dist_max, dist_close)? ResolveState(label) : null; } - action state A_CheckProximity(statelabel label, class classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT) + action state A_CheckProximity(statelabel label, class classname, double distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT) { // This one was doing some weird stuff that needs to be preserved. state jumpto = ResolveState(label); @@ -237,7 +237,7 @@ extend class Actor return CheckProximity(classname, distance, count, flags, ptr)? jumpto : null; } - action state A_CheckBlock(statelabel label, int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0) + action state A_CheckBlock(statelabel label, int flags = 0, int ptr = AAPTR_DEFAULT, double xofs = 0, double yofs = 0, double zofs = 0, double angle = 0) { return CheckBlock(flags, ptr, xofs, yofs, zofs, angle)? ResolveState(label) : null; } @@ -253,7 +253,7 @@ extend class Actor // if its lower. //=========================================================================== - action state A_JumpIfHigherOrLower(statelabel high, statelabel low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET) + action state A_JumpIfHigherOrLower(statelabel high, statelabel low, double offsethigh = 0, double offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET) { Actor mobj = GetPointer(ptr); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 33c1c6135..6db88ab0a 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -1,32 +1,504 @@ class Object native { - virtual native void Destroy(); - native class GetClass(); + native bool bDestroyed; + + // These really should be global functions... + native static int G_SkillPropertyInt(int p); + native static double G_SkillPropertyFloat(int p); + native static vector3, int G_PickDeathmatchStart(); + native static vector3, int G_PickPlayerStart(int pnum, int flags = 0); + native static int GameType(); + native static void S_Sound (Sound sound_id, int channel, float volume = 1, float attenuation = ATTN_NORM); + native static bool S_ChangeMusic(String music_name, int order = 0, bool looping = true, bool force = false); + native static void C_MidPrint(string fontname, string textlabel, bool bold = false); // always uses the stringtable. + native static uint BAM(double angle); + + /*virtual*/ native void Destroy(); } class Thinker : Object native { + enum EStatnums + { + // Thinkers that don't actually think + STAT_INFO, // An info queue + STAT_DECAL, // A decal + STAT_AUTODECAL, // A decal that can be automatically deleted + STAT_CORPSEPOINTER, // An entry in Hexen's corpse queue + STAT_TRAVELLING, // An actor temporarily travelling to a new map + + // Thinkers that do think + STAT_FIRST_THINKING=32, + STAT_SCROLLER=STAT_FIRST_THINKING, // A DScroller thinker + STAT_PLAYER, // A player actor + STAT_BOSSTARGET, // A boss brain target + STAT_LIGHTNING, // The lightning thinker + STAT_DECALTHINKER, // An object that thinks for a decal + STAT_INVENTORY, // An inventory item + STAT_LIGHT, // A sector light effect + STAT_LIGHTTRANSFER, // A sector light transfer. These must be ticked after the light effects. + STAT_EARTHQUAKE, // Earthquake actors + STAT_MAPMARKER, // Map marker actors + + STAT_DEFAULT = 100, // Thinkers go here unless specified otherwise. + STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement + STAT_ACTORMOVER, // actor movers + STAT_SCRIPTS, // The ACS thinker. This is to ensure that it can't tick before all actors called PostBeginPlay + STAT_BOT, // Bot thinker + MAX_STATNUM = 127 + } + + const TICRATE = 35; + + virtual native void Tick(); + virtual native void PostBeginPlay(); + virtual native void ChangeStatNum(int stat); } class ThinkerIterator : Object native { - enum EStatnums - { - MAX_STATNUM = 127 - } - native static ThinkerIterator Create(class type = "Actor", int statnum=MAX_STATNUM+1); + native static ThinkerIterator Create(class type = "Actor", int statnum=Thinker.MAX_STATNUM+1); native Thinker Next(bool exact = false); native void Reinit(); } +class ActorIterator : Object native +{ + native static ActorIterator Create(int tid, class type = "Actor"); + native Actor Next(); + native void Reinit(); +} + +class BlockThingsIterator : Object native +{ + native Actor thing; + native Vector3 position; + native int portalflags; + + native static BlockThingsIterator Create(Actor origin, double checkradius = -1, bool ignorerestricted = false); + native static BlockThingsIterator CreateFromPos(double checkx, double checky, double checkz, double checkh, double checkradius, bool ignorerestricted); + native bool Next(); +} + class DropItem : Object native { - /* native fields listed for reference only for now native readonly DropItem Next; native readonly name Name; native readonly int Probability; - native readonly int Amount; - */ + native int Amount; } +class SpotState : Object native +{ + native static SpotState GetSpotState(); + native SpecialSpot GetNextInList(class type, int skipcounter); + native SpecialSpot GetSpotWithMinMaxDistance(Class type, double x, double y, double mindist, double maxdist); + +} + +struct LevelLocals native +{ + native readonly int time; + native readonly int maptime; + native readonly int totaltime; + native readonly int starttime; + native readonly int partime; + native readonly int sucktime; + native readonly int cluster; + native readonly int clusterflags; + native readonly int levelnum; + native readonly String LevelName; + native readonly String MapName; + native String NextMap; + native String NextSecretMap; + native readonly int maptype; + native readonly String Music; + native readonly int musicorder; + native int total_secrets; + native int found_secrets; + native int total_items; + native int found_items; + native int total_monsters; + native int killed_monsters; + native double gravity; + native double aircontrol; + native double airfriction; + native int airsupply; + native double teamdamage; + native bool monsterstelefrag; + native bool actownspecial; + native bool sndseqtotalctrl; + native bool allmap; + native bool missilesactivateimpact; + native bool monsterfallingdamage; + native bool checkswitchrange; + native bool polygrind; + native bool nomonsters; +// level_info_t *info cannot be done yet. +} + +struct StringTable native +{ + native static String Localize(String val); +} + +// a few values of this need to be readable by the play code. +// Most are handled at load time and are omitted here. +struct DehInfo native +{ + native int MaxSoulsphere; + native uint8 ExplosionStyle; + native double ExplosionAlpha; + native int NoAutofreeze; + native int BFGCells; +} + +struct State native +{ + native State NextState; + native int sprite; + native int16 Tics; + native uint16 TicRange; + native uint8 Frame; + native uint8 UseFlags; + native int Misc1; + native int Misc2; + native uint16 bSlow; + native uint16 bFast; + native bool bFullbright; + native bool bNoDelay; + native bool bSameFrame; + native bool bCanRaise; + native bool bDehacked; + + native int DistanceTo(state other); +} + +struct F3DFloor native +{ +} + +struct Line native +{ + enum ELineFlags + { + ML_BLOCKING =0x00000001, // solid, is an obstacle + ML_BLOCKMONSTERS =0x00000002, // blocks monsters only + ML_TWOSIDED =0x00000004, // backside will not be present at all if not two sided + ML_DONTPEGTOP = 0x00000008, // upper texture unpegged + ML_DONTPEGBOTTOM = 0x00000010, // lower texture unpegged + ML_SECRET = 0x00000020, // don't map as two sided: IT'S A SECRET! + ML_SOUNDBLOCK = 0x00000040, // don't let sound cross two of these + ML_DONTDRAW = 0x00000080, // don't draw on the automap + ML_MAPPED = 0x00000100, // set if already drawn in automap + ML_REPEAT_SPECIAL = 0x00000200, // special is repeatable + ML_ADDTRANS = 0x00000400, // additive translucency (can only be set internally) + + // Extended flags + ML_MONSTERSCANACTIVATE = 0x00002000, // [RH] Monsters (as well as players) can activate the line + ML_BLOCK_PLAYERS = 0x00004000, + ML_BLOCKEVERYTHING = 0x00008000, // [RH] Line blocks everything + ML_ZONEBOUNDARY = 0x00010000, + ML_RAILING = 0x00020000, + ML_BLOCK_FLOATERS = 0x00040000, + ML_CLIP_MIDTEX = 0x00080000, // Automatic for every Strife line + ML_WRAP_MIDTEX = 0x00100000, + ML_3DMIDTEX = 0x00200000, + ML_CHECKSWITCHRANGE = 0x00400000, + ML_FIRSTSIDEONLY = 0x00800000, // activated only when crossed from front side + ML_BLOCKPROJECTILE = 0x01000000, + ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line + ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight + ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks + ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING + }; + + + //native readonly vertex v1, v2; // vertices, from v1 to v2 + native readonly Vector2 delta; // precalculated v2 - v1 for side checking + native uint flags; + native uint activation; // activation type + native int special; + native int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width) + native double alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque) + //native Side sidedef[2]; + native readonly double bbox[4]; // bounding box, for the extent of the LineDef. + native readonly Sector frontsector, backsector; + native int validcount; // if == validcount, already checked + native int locknumber; // [Dusk] lock number for special + native readonly uint portalindex; +} + +struct Sector native +{ + //secplane_t floorplane, ceilingplane; + //FDynamicColormap *ColorMap; // [RH] Per-sector colormap + + native Actor SoundTarget; + + native int16 special; + native int16 lightlevel; + native int16 seqType; + + native int sky; + native Name SeqName; + + native readonly Vector2 centerspot; + native int validcount; + native Actor thinglist; + + native double friction, movefactor; + native int terrainnum[2]; + + // thinker_t for reversable actions + //SectorEffect floordata; + //SectorEffect ceilingdata; + //SectorEffect lightingdata; + + enum EInterpolationType + { + CeilingMove, + FloorMove, + CeilingScroll, + FloorScroll + }; + //Interpolation interpolations[4]; + + native uint8 soundtraversed; + native int8 stairlock; + native int prevsec; + native int nextsec; + + native readonly int16 linecount; + //line_t **lines; // this is defined internally to avoid exposing some overly complicated type to the parser + + native readonly Sector heightsec; + + native uint bottommap, midmap, topmap; + + //struct msecnode_t *touching_thinglist; + //struct msecnode_t *render_thinglist; + + native double gravity; + native Name damagetype; + native int damageamount; + native int16 damageinterval; + native int16 leakydamage; + + native uint16 ZoneNumber; + + enum ESectorMoreFlags + { + SECMF_FAKEFLOORONLY = 2, // when used as heightsec in R_FakeFlat, only copies floor + SECMF_CLIPFAKEPLANES = 4, // as a heightsec, clip planes to target sector's planes + SECMF_NOFAKELIGHT = 8, // heightsec does not change lighting + SECMF_IGNOREHEIGHTSEC= 16, // heightsec is only for triggering sector actions + SECMF_UNDERWATER = 32, // sector is underwater + SECMF_FORCEDUNDERWATER= 64, // sector is forced to be underwater + SECMF_UNDERWATERMASK = 32+64, + SECMF_DRAWN = 128, // sector has been drawn at least once + SECMF_HIDDEN = 256, // Do not draw on textured automap + } + native uint16 MoreFlags; + + enum ESectorFlags + { + SECF_SILENT = 1, // actors in sector make no noise + SECF_NOFALLINGDAMAGE= 2, // No falling damage in this sector + SECF_FLOORDROP = 4, // all actors standing on this floor will remain on it when it lowers very fast. + SECF_NORESPAWN = 8, // players can not respawn in this sector + SECF_FRICTION = 16, // sector has friction enabled + SECF_PUSH = 32, // pushers enabled + SECF_SILENTMOVE = 64, // Sector movement makes mo sound (Eternity got this so this may be useful for an extended cross-port standard.) + SECF_DMGTERRAINFX = 128, // spawns terrain splash when inflicting damage + SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode + SECF_ENDLEVEL = 512, // ends level when health goes below 10 + SECF_HAZARD = 1024, // Change to Strife's delayed damage handling. + + SECF_WASSECRET = 1 << 30, // a secret that was discovered + SECF_SECRET = 1 << 31, // a secret sector + + SECF_DAMAGEFLAGS = SECF_ENDGODMODE|SECF_ENDLEVEL|SECF_DMGTERRAINFX|SECF_HAZARD, + SECF_NOMODIFY = SECF_SECRET|SECF_WASSECRET, // not modifiable by Sector_ChangeFlags + SECF_SPECIALFLAGS = SECF_DAMAGEFLAGS|SECF_FRICTION|SECF_PUSH, // these flags originate from 'special and must be transferrable by floor thinkers + } + native uint Flags; + + native SectorAction SecActTarget; + + native readonly uint Portals[2]; + native readonly int PortalGroup; + + native readonly int sectornum; + + + native double, Sector, F3DFloor NextHighestCeilingAt(double x, double y, double bottomz, double topz, int flags = 0); + native double, Sector, F3DFloor NextLowestFloorAt(double x, double y, double z, int flags = 0, double steph = 0); + + native void RemoveForceField(); + native static Sector PointInSector(Vector2 pt); + native void SetColor(color c, int desat = 0); + native void SetFade(color c); +} + +struct Wads +{ + enum WadNamespace + { + ns_hidden = -1, + + ns_global = 0, + ns_sprites, + ns_flats, + ns_colormaps, + ns_acslibrary, + ns_newtextures, + ns_bloodraw, + ns_bloodsfx, + ns_bloodmisc, + ns_strifevoices, + ns_hires, + ns_voxels, + + ns_specialzipdirectory, + ns_sounds, + ns_patches, + ns_graphics, + ns_music, + + ns_firstskin, + } + + native static int CheckNumForName(string name, int ns, int wadnum = -1, bool exact = false); +} + +struct TerrainDef native +{ + native Name TerrainName; + native int Splash; + native int DamageAmount; + native Name DamageMOD; + native int DamageTimeMask; + native double FootClip; + native float StepVolume; + native int WalkStepTics; + native int RunStepTics; + native Sound LeftStepSound; + native Sound RightStepSound; + native bool IsLiquid; + native bool AllowProtection; + native double Friction; + native double MoveFactor; +}; + +enum EPickStart +{ + PPS_FORCERANDOM = 1, + PPS_NOBLOCKINGCHECK = 2, +} + +// Although String is a builtin type, this is a convenient way to attach methods to it. +struct String native +{ + native void Replace(String pattern, String replacement); +} + +class Floor : Thinker native +{ + // only here so that some constants and functions can be added. Not directly usable yet. + enum EFloor + { + floorLowerToLowest, + floorLowerToNearest, + floorLowerToHighest, + floorLowerByValue, + floorRaiseByValue, + floorRaiseToHighest, + floorRaiseToNearest, + floorRaiseAndCrush, + floorRaiseAndCrushDoom, + floorCrushStop, + floorLowerInstant, + floorRaiseInstant, + floorMoveToValue, + floorRaiseToLowestCeiling, + floorRaiseByTexture, + + floorLowerAndChange, + floorRaiseAndChange, + + floorRaiseToLowest, + floorRaiseToCeiling, + floorLowerToLowestCeiling, + floorLowerByTexture, + floorLowerToCeiling, + + donutRaise, + + buildStair, + waitStair, + resetStair, + + // Not to be used as parameters to EV_DoFloor() + genFloorChg0, + genFloorChgT, + genFloorChg + }; + + native static bool CreateFloor(sector sec, EFloor floortype, line ln, double speed, double height = 0, int crush = -1, int change = 0, bool crushmode = false, bool hereticlower = false); +} + +class Ceiling : Thinker native +{ + enum ECeiling + { + ceilLowerByValue, + ceilRaiseByValue, + ceilMoveToValue, + ceilLowerToHighestFloor, + ceilLowerInstant, + ceilRaiseInstant, + ceilCrushAndRaise, + ceilLowerAndCrush, + ceil_placeholder, + ceilCrushRaiseAndStay, + ceilRaiseToNearest, + ceilLowerToLowest, + ceilLowerToFloor, + + // The following are only used by Generic_Ceiling + ceilRaiseToHighest, + ceilLowerToHighest, + ceilRaiseToLowest, + ceilLowerToNearest, + ceilRaiseToHighestFloor, + ceilRaiseToFloor, + ceilRaiseByTexture, + ceilLowerByTexture, + + genCeilingChg0, + genCeilingChgT, + genCeilingChg + } + + enum ECrushMode + { + crushDoom = 0, + crushHexen = 1, + crushSlowdown = 2 + } + + native bool CreateCeiling(sector sec, int type, line ln, double speed, double speed2, double height = 0, int crush = -1, int silent = 0, int change = 0, int crushmode = crushDoom); + +} + +struct LookExParams +{ + double Fov; + double minDist; + double maxDist; + double maxHeardist; + int flags; + State seestate; +}; diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 5e47bbc7e..2ccfb7048 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1,5 +1,6 @@ // for flag changer functions. const FLAG_NO_CHANGE = -1; +const MAXPLAYERS = 8; enum EStateUseFlags { @@ -711,6 +712,7 @@ enum EPSpriteFlags // Default psprite layers enum EPSPLayers { + PSP_STRIFEHANDS = -1, PSP_WEAPON = 1, PSP_FLASH = 1000, }; @@ -829,7 +831,7 @@ enum EStateType struct FStateParamInfo { state mCallingState; - EStateType mStateType; + /*EStateType*/int mStateType; int mPSPIndex; } @@ -839,6 +841,8 @@ struct FTranslatedLineTarget Actor linetarget; double angleFromSource; bool unlinked; // found by a trace that went through an unlinked portal. + + native void TraceBleed(int damage, Actor missile); } enum EAimFlags @@ -898,3 +902,147 @@ const HR_SHADOW = (0x6800 / 65536.); // Hexen's TINTTAB is the same as Heretic's, just reversed. const HX_SHADOW = (0x9800 / 65536.); const HX_ALTSHADOW = (0x6800 / 65536.); + +enum EMapThingFlags +{ + MTF_AMBUSH = 0x0008, // Thing is deaf + MTF_DORMANT = 0x0010, // Thing is dormant (use Thing_Activate) + + MTF_SINGLE = 0x0100, // Thing appears in single-player games + MTF_COOPERATIVE = 0x0200, // Thing appears in cooperative games + MTF_DEATHMATCH = 0x0400, // Thing appears in deathmatch games + + MTF_SHADOW = 0x0800, + MTF_ALTSHADOW = 0x1000, + MTF_FRIENDLY = 0x2000, + MTF_STANDSTILL = 0x4000, + MTF_STRIFESOMETHING = 0x8000, + + MTF_SECRET = 0x080000, // Secret pickup + MTF_NOINFIGHTING = 0x100000, +}; + +enum ESkillProperty +{ + SKILLP_FastMonsters, + SKILLP_Respawn, + SKILLP_RespawnLimit, + SKILLP_DisableCheats, + SKILLP_AutoUseHealth, + SKILLP_SpawnFilter, + SKILLP_EasyBossBrain, + SKILLP_ACSReturn, + SKILLP_NoPain, + SKILLP_EasyKey, + SKILLP_SlowMonsters, + SKILLP_Infight, +}; +enum EFSkillProperty // floating point properties +{ + SKILLP_AmmoFactor, + SKILLP_DropAmmoFactor, + SKILLP_ArmorFactor, + SKILLP_HealthFactor, + SKILLP_DamageFactor, + SKILLP_Aggressiveness, + SKILLP_MonsterHealth, + SKILLP_FriendlyHealth, +}; + +enum EWeaponPos +{ + WEAPONBOTTOM = 128, + WEAPONTOP = 32 +} + +enum ETranslationTable +{ + TRANSLATION_Invalid, + TRANSLATION_Players, + TRANSLATION_PlayersExtra, + TRANSLATION_Standard, + TRANSLATION_LevelScripted, + TRANSLATION_Decals, + TRANSLATION_PlayerCorpses, + TRANSLATION_Decorate, + TRANSLATION_Blood, + TRANSLATION_RainPillar, + TRANSLATION_Custom, +}; + +enum EFindFloorCeiling +{ + FFCF_ONLYSPAWNPOS = 1, + FFCF_SAMESECTOR = 2, + FFCF_ONLY3DFLOORS = 4, // includes 3D midtexes + FFCF_3DRESTRICT = 8, // ignore 3D midtexes and floors whose floorz are above thing's z + FFCF_NOPORTALS = 16, // ignore portals (considers them impassable.) + FFCF_NOFLOOR = 32, + FFCF_NOCEILING = 64, + FFCF_RESTRICTEDPORTAL = 128, // current values in the iterator's return are through a restricted portal type (i.e. some features are blocked.) + FFCF_NODROPOFF = 256, // Caller does not need a dropoff (saves some time when checking portals) +}; + +enum ETeleport +{ + TELF_DESTFOG = 1, + TELF_SOURCEFOG = 2, + TELF_KEEPORIENTATION = 4, + TELF_KEEPVELOCITY = 8, + TELF_KEEPHEIGHT = 16, + TELF_ROTATEBOOM = 32, + TELF_ROTATEBOOMINVERSE = 64, +}; + +enum EGameType +{ + GAME_Any = 0, + GAME_Doom = 1, + GAME_Heretic = 2, + GAME_Hexen = 4, + GAME_Strife = 8, + GAME_Chex = 16, //Chex is basically Doom, but we need to have a different set of actors. + + GAME_Raven = GAME_Heretic|GAME_Hexen, + GAME_DoomChex = GAME_Doom|GAME_Chex, + GAME_DoomStrifeChex = GAME_Doom|GAME_Strife|GAME_Chex +} + +enum PaletteFlashFlags +{ + PF_HEXENWEAPONS = 1, + PF_POISON = 2, + PF_ICE = 4, + PF_HAZARD = 8, +}; + +enum EGameAction +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_newgame2, + ga_recordgame, + ga_loadgame, + ga_loadgamehidecon, + ga_loadgameplaydemo, + ga_autoloadgame, + ga_savegame, + ga_autosave, + ga_playdemo, + ga_completed, + ga_slideshow, + ga_worlddone, + ga_screenshot, + ga_togglemap, + ga_fullconsole, +}; + +enum EPuffFlags +{ + PF_HITTHING = 1, + PF_MELEERANGE = 2, + PF_TEMPORARY = 4, + PF_HITTHINGBLEED = 8, + PF_NORANDOMZ = 16 +}; diff --git a/wadsrc/static/zscript/doom/archvile.txt b/wadsrc/static/zscript/doom/archvile.txt index 0e9008570..9faa80495 100644 --- a/wadsrc/static/zscript/doom/archvile.txt +++ b/wadsrc/static/zscript/doom/archvile.txt @@ -123,7 +123,7 @@ extend class Actor } } - void A_VileAttack(sound snd = "vile/stop", int initialdmg = 20, int blastdmg = 70, int blastradius = 70, float thrust = 1.0, name damagetype = "Fire", int flags = 0) + void A_VileAttack(sound snd = "vile/stop", int initialdmg = 20, int blastdmg = 70, int blastradius = 70, double thrust = 1.0, name damagetype = "Fire", int flags = 0) { if (target) { @@ -158,7 +158,7 @@ extend class Actor // A_Fire // Keep fire in front of player unless out of sight // - void A_Fire(float spawnheight = 0) + void A_Fire(double spawnheight = 0) { Actor dest = tracer; if (!dest || !target) return; diff --git a/wadsrc/static/zscript/doom/bossbrain.txt b/wadsrc/static/zscript/doom/bossbrain.txt index 6a72de2bf..2a3f39265 100644 --- a/wadsrc/static/zscript/doom/bossbrain.txt +++ b/wadsrc/static/zscript/doom/bossbrain.txt @@ -186,11 +186,11 @@ extend class Actor void A_BrainDie() { // [RH] If noexit, then don't end the level. - if ((GetCVar("deathmatch") || GetCVar("alwaysapplydmflags")) && GetCVar("sv_noexit")) + if ((deathmatch || alwaysapplydmflags) && sv_noexit) return; // New dmflag: Kill all boss spawned monsters before ending the level. - if (GetCVar("sv_killbossmonst")) + if (sv_killbossmonst) { int count; // Repeat until we have no more boss-spawned monsters. ThinkerIterator it = ThinkerIterator.Create(); @@ -212,9 +212,7 @@ extend class Actor Exit_Normal(0); } - native - void A_BrainSpit(class spawntype = null) // needs special treatment for default - ;/* + void A_BrainSpit(class spawntype = null) { SpotState spstate = SpotState.GetSpotState(); Actor targ; @@ -222,7 +220,7 @@ extend class Actor bool isdefault = false; // shoot a cube at current target - targ = spstate.GetNextInList("BossTarget", G_SkillProperty(SKILLP_EasyBossBrain)); + targ = spstate.GetNextInList("BossTarget", G_SkillPropertyInt(SKILLP_EasyBossBrain)); if (targ) { @@ -249,7 +247,7 @@ extend class Actor { spit.special2 = 0; } - else if (abs(spit.Vel.y) > fabs(spit.Vel.x)) + else if (abs(spit.Vel.y) > abs(spit.Vel.x)) { spit.special2 = int((targ.pos.y - pos.y) / spit.Vel.y); } @@ -264,16 +262,15 @@ extend class Actor if (!isdefault) { - A_PlaySound(self.AttackSound, CHAN_WEAPON); + A_PlaySound(self.AttackSound, CHAN_WEAPON, 1., false, ATTN_NONE); } else { // compatibility fallback - A_PlaySound("brain/spit", CHAN_WEAPON); + A_PlaySound("brain/spit", CHAN_WEAPON, 1., false, ATTN_NONE); } } } - */ private void SpawnFly(class spawntype, sound snd) { @@ -377,7 +374,7 @@ extend class Actor } // Make it act as if it was around when the player first made noise // (if the player has made noise). - newmobj.LastHeard = newmobj.Sector.SoundTarget; + newmobj.LastHeard = newmobj.CurSector.SoundTarget; if (newmobj.SeeState != null && newmobj.LookForPlayers (true)) { diff --git a/wadsrc/static/zscript/doom/doommisc.txt b/wadsrc/static/zscript/doom/doommisc.txt index a11c411f8..7d4c9dec4 100644 --- a/wadsrc/static/zscript/doom/doommisc.txt +++ b/wadsrc/static/zscript/doom/doommisc.txt @@ -38,7 +38,7 @@ extend class Actor { void A_BarrelDestroy() { - if (GetCVar("sv_barrelrespawn")) + if (sv_barrelrespawn) { Height = Default.Height; bInvisible = true; diff --git a/wadsrc/static/zscript/doom/doomweapons.txt b/wadsrc/static/zscript/doom/doomweapons.txt index a6eecd189..f3a40c22a 100644 --- a/wadsrc/static/zscript/doom/doomweapons.txt +++ b/wadsrc/static/zscript/doom/doomweapons.txt @@ -1,6 +1,6 @@ // -------------------------------------------------------------------------- // -// Doom weapon base class +// Doom weap base class // // -------------------------------------------------------------------------- @@ -12,614 +12,53 @@ class DoomWeapon : Weapon } } -// -------------------------------------------------------------------------- -// -// Fist -// -// -------------------------------------------------------------------------- -class Fist : Weapon + +extend class StateProvider { - Default + + // + // [RH] A_FireRailgun + // [TP] This now takes a puff type to retain Skulltag's railgun's ability to pierce armor. + // + action void A_FireRailgun(class puffType = "BulletPuff", int offset_xy = 0) { - Weapon.SelectionOrder 3700; - Weapon.Kickback 100; - Obituary "$OB_MPFIST"; - Tag "$TAG_FIST"; - +WEAPON.WIMPY_WEAPON - +WEAPON.MELEEWEAPON + if (player == null) + { + return; + } + + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + + State flash = weap.FindState('Flash'); + if (flash != null) + { + player.SetSafeFlash(weap, flash, random[FireRail]()&1); + } + + } + + int damage = deathmatch ? 100 : 150; + A_RailAttack(damage, offset_xy, false, pufftype: puffType); // note that this function handles ammo depletion itself for Dehacked compatibility purposes. } - States + + action void A_FireRailgunLeft() { - Ready: - PUNG A 1 A_WeaponReady; - Loop; - Deselect: - PUNG A 1 A_Lower; - Loop; - Select: - PUNG A 1 A_Raise; - Loop; - Fire: - PUNG B 4; - PUNG C 4 A_Punch; - PUNG D 5; - PUNG C 4; - PUNG B 5 A_ReFire; - Goto Ready; + A_FireRailgun(offset_xy: -10); } + + action void A_FireRailgunRight() + { + A_FireRailgun(offset_xy: 10); + } + + action void A_RailWait() + { + // only here to satisfy old Dehacked patches. + } + } - - -// -------------------------------------------------------------------------- -// -// Pistol -// -// -------------------------------------------------------------------------- - -class Pistol : DoomWeapon -{ - Default - { - Weapon.SelectionOrder 1900; - Weapon.AmmoUse 1; - Weapon.AmmoGive 20; - Weapon.AmmoType "Clip"; - Obituary "$OB_MPPISTOL"; - +WEAPON.WIMPY_WEAPON - Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED"; - Tag "$TAG_PISTOL"; - } - States - { - Ready: - PISG A 1 A_WeaponReady; - Loop; - Deselect: - PISG A 1 A_Lower; - Loop; - Select: - PISG A 1 A_Raise; - Loop; - Fire: - PISG A 4; - PISG B 6 A_FirePistol; - PISG C 4; - PISG B 5 A_ReFire; - Goto Ready; - Flash: - PISF A 7 Bright A_Light1; - Goto LightDone; - PISF A 7 Bright A_Light1; - Goto LightDone; - Spawn: - PIST A -1; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// Chainsaw -// -// -------------------------------------------------------------------------- - -class Chainsaw : Weapon -{ - Default - { - Weapon.Kickback 0; - Weapon.SelectionOrder 2200; - Weapon.UpSound "weapons/sawup"; - Weapon.ReadySound "weapons/sawidle"; - Inventory.PickupMessage "$GOTCHAINSAW"; - Obituary "$OB_MPCHAINSAW"; - Tag "$TAG_CHAINSAW"; - +WEAPON.MELEEWEAPON - } - States - { - Ready: - SAWG CD 4 A_WeaponReady; - Loop; - Deselect: - SAWG C 1 A_Lower; - Loop; - Select: - SAWG C 1 A_Raise; - Loop; - Fire: - SAWG AB 4 A_Saw; - SAWG B 0 A_ReFire; - Goto Ready; - Spawn: - CSAW A -1; - Stop; - } -} - - -// -------------------------------------------------------------------------- -// -// Shotgun -// -// -------------------------------------------------------------------------- - -class Shotgun : DoomWeapon -{ - Default - { - Weapon.SelectionOrder 1300; - Weapon.AmmoUse 1; - Weapon.AmmoGive 8; - Weapon.AmmoType "Shell"; - Inventory.PickupMessage "$GOTSHOTGUN"; - Obituary "$OB_MPSHOTGUN"; - Tag "$TAG_SHOTGUN"; - } - States - { - Ready: - SHTG A 1 A_WeaponReady; - Loop; - Deselect: - SHTG A 1 A_Lower; - Loop; - Select: - SHTG A 1 A_Raise; - Loop; - Fire: - SHTG A 3; - SHTG A 7 A_FireShotgun; - SHTG BC 5; - SHTG D 4; - SHTG CB 5; - SHTG A 3; - SHTG A 7 A_ReFire; - Goto Ready; - Flash: - SHTF A 4 Bright A_Light1; - SHTF B 3 Bright A_Light2; - Goto LightDone; - Spawn: - SHOT A -1; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// Shotgun -// -// -------------------------------------------------------------------------- - -class SuperShotgun : DoomWeapon -{ - Default - { - Weapon.SelectionOrder 400; - Weapon.AmmoUse 2; - Weapon.AmmoGive 8; - Weapon.AmmoType "Shell"; - Inventory.PickupMessage "$GOTSHOTGUN2"; - Obituary "$OB_MPSSHOTGUN"; - Tag "$TAG_SUPERSHOTGUN"; - } - States - { - Ready: - SHT2 A 1 A_WeaponReady; - Loop; - Deselect: - SHT2 A 1 A_Lower; - Loop; - Select: - SHT2 A 1 A_Raise; - Loop; - Fire: - SHT2 A 3; - SHT2 A 7 A_FireShotgun2; - SHT2 B 7; - SHT2 C 7 A_CheckReload; - SHT2 D 7 A_OpenShotgun2; - SHT2 E 7; - SHT2 F 7 A_LoadShotgun2; - SHT2 G 6; - SHT2 H 6 A_CloseShotgun2; - SHT2 A 5 A_ReFire; - Goto Ready; - // unused states - SHT2 B 7; - SHT2 A 3; - Goto Deselect; - Flash: - SHT2 I 4 Bright A_Light1; - SHT2 J 3 Bright A_Light2; - Goto LightDone; - Spawn: - SGN2 A -1; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// Chaingun -// -// -------------------------------------------------------------------------- - -class Chaingun : DoomWeapon -{ - Default - { - Weapon.SelectionOrder 700; - Weapon.AmmoUse 1; - Weapon.AmmoGive 20; - Weapon.AmmoType "Clip"; - Inventory.PickupMessage "$GOTCHAINGUN"; - Obituary "$OB_MPCHAINGUN"; - Tag "$TAG_CHAINGUN"; - } - States - { - Ready: - CHGG A 1 A_WeaponReady; - Loop; - Deselect: - CHGG A 1 A_Lower; - Loop; - Select: - CHGG A 1 A_Raise; - Loop; - Fire: - CHGG AB 4 A_FireCGun; - CHGG B 0 A_ReFire; - Goto Ready; - Flash: - CHGF A 5 Bright A_Light1; - Goto LightDone; - CHGF B 5 Bright A_Light2; - Goto LightDone; - Spawn: - MGUN A -1; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// Rocket launcher -// -// -------------------------------------------------------------------------- - -class RocketLauncher : DoomWeapon -{ - Default - { - Weapon.SelectionOrder 2500; - Weapon.AmmoUse 1; - Weapon.AmmoGive 2; - Weapon.AmmoType "RocketAmmo"; - +WEAPON.NOAUTOFIRE - Inventory.PickupMessage "$GOTLAUNCHER"; - Tag "$TAG_ROCKETLAUNCHER"; - } - States - { - Ready: - MISG A 1 A_WeaponReady; - Loop; - Deselect: - MISG A 1 A_Lower; - Loop; - Select: - MISG A 1 A_Raise; - Loop; - Fire: - MISG B 8 A_GunFlash; - MISG B 12 A_FireMissile; - MISG B 0 A_ReFire; - Goto Ready; - Flash: - MISF A 3 Bright A_Light1; - MISF B 4 Bright; - MISF CD 4 Bright A_Light2; - Goto LightDone; - Spawn: - LAUN A -1; - Stop; - } -} - -class Rocket : Actor -{ - Default - { - Radius 11; - Height 8; - Speed 20; - Damage 20; - Projectile; - +RANDOMIZE - +DEHEXPLOSION - +ROCKETTRAIL - SeeSound "weapons/rocklf"; - DeathSound "weapons/rocklx"; - Obituary "$OB_MPROCKET"; - } - States - { - Spawn: - MISL A 1 Bright; - Loop; - Death: - MISL B 8 Bright A_Explode; - MISL C 6 Bright; - MISL D 4 Bright; - Stop; - BrainExplode: - MISL BC 10 Bright; - MISL D 10 A_BrainExplode; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// Grenade -- Taken and adapted from Skulltag, with MBF stuff added to it -// -// -------------------------------------------------------------------------- - -class Grenade : Actor -{ - Default - { - Radius 8; - Height 8; - Speed 25; - Damage 20; - Projectile; - -NOGRAVITY - +RANDOMIZE - +DEHEXPLOSION - +GRENADETRAIL - BounceType "Doom"; - Gravity 0.25; - SeeSound "weapons/grenlf"; - DeathSound "weapons/grenlx"; - BounceSound "weapons/grbnce"; - Obituary "$OB_GRENADE"; - DamageType "Grenade"; - } - States - { - Spawn: - SGRN A 1 Bright; - Loop; - Death: - MISL B 8 Bright A_Explode; - MISL C 6 Bright; - MISL D 4 Bright; - Stop; - Grenade: - MISL A 1000 A_Die; - Wait; - Detonate: - MISL B 4 A_Scream; - MISL C 6 A_Detonate; - MISL D 10; - Stop; - Mushroom: - MISL B 8 A_Mushroom; - Goto Death+1; - } -} - -// -------------------------------------------------------------------------- -// -// Plasma rifle -// -// -------------------------------------------------------------------------- - -class PlasmaRifle : DoomWeapon -{ - Default - { - Weapon.SelectionOrder 100; - Weapon.AmmoUse 1; - Weapon.AmmoGive 40; - Weapon.AmmoType "Cell"; - Inventory.PickupMessage "$GOTPLASMA"; - Tag "$TAG_PLASMARIFLE"; - } - States - { - Ready: - PLSG A 1 A_WeaponReady; - Loop; - Deselect: - PLSG A 1 A_Lower; - Loop; - Select: - PLSG A 1 A_Raise; - Loop; - Fire: - PLSG A 3 A_FirePlasma; - PLSG B 20 A_ReFire; - Goto Ready; - Flash: - PLSF A 4 Bright A_Light1; - Goto LightDone; - PLSF B 4 Bright A_Light1; - Goto LightDone; - Spawn: - PLAS A -1; - Stop; - } -} - -class PlasmaBall : Actor -{ - Default - { - Radius 13; - Height 8; - Speed 25; - Damage 5; - Projectile; - +RANDOMIZE - RenderStyle "Add"; - Alpha 0.75; - SeeSound "weapons/plasmaf"; - DeathSound "weapons/plasmax"; - Obituary "$OB_MPPLASMARIFLE"; - } - States - { - Spawn: - PLSS AB 6 Bright; - Loop; - Death: - PLSE ABCDE 4 Bright; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// BFG 2704 -// -// -------------------------------------------------------------------------- - -class PlasmaBall1 : PlasmaBall -{ - Default - { - Damage 4; - BounceType "Classic"; - BounceFactor 1.0; - Obituary "$OB_MPBFG_MBF"; - } - States - { - Spawn: - PLS1 AB 6 Bright; - Loop; - Death: - PLS1 CDEFG 4 Bright; - Stop; - } -} - -class PlasmaBall2 : PlasmaBall1 -{ - States - { - Spawn: - PLS2 AB 6 Bright; - Loop; - Death: - PLS2 CDE 4 Bright; - Stop; - } -} - -// -------------------------------------------------------------------------- -// -// BFG 9000 -// -// -------------------------------------------------------------------------- - -class BFG9000 : DoomWeapon -{ - Default - { - Height 20; - Weapon.SelectionOrder 2800; - Weapon.AmmoUse 40; - Weapon.AmmoGive 40; - Weapon.AmmoType "Cell"; - +WEAPON.NOAUTOFIRE; - Inventory.PickupMessage "$GOTBFG9000"; - Tag "$TAG_BFG9000"; - } - States - { - Ready: - BFGG A 1 A_WeaponReady; - Loop; - Deselect: - BFGG A 1 A_Lower; - Loop; - Select: - BFGG A 1 A_Raise; - Loop; - Fire: - BFGG A 20 A_BFGsound; - BFGG B 10 A_GunFlash; - BFGG B 10 A_FireBFG; - BFGG B 20 A_ReFire; - Goto Ready; - Flash: - BFGF A 11 Bright A_Light1; - BFGF B 6 Bright A_Light2; - Goto LightDone; - Spawn: - BFUG A -1; - Stop; - OldFire: - BFGG A 10 A_BFGsound; - BFGG BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 1 A_FireOldBFG; - BFGG B 0 A_Light0; - BFGG B 20 A_ReFire; - Goto Ready; - } -} - - -class BFGBall : Actor -{ - Default - { - Radius 13; - Height 8; - Speed 25; - Damage 100; - Projectile; - +RANDOMIZE - RenderStyle "Add"; - Alpha 0.75; - DeathSound "weapons/bfgx"; - Obituary "$OB_MPBFG_BOOM"; - } - States - { - Spawn: - BFS1 AB 4 Bright; - Loop; - Death: - BFE1 AB 8 Bright; - BFE1 C 8 Bright A_BFGSpray; - BFE1 DEF 8 Bright; - Stop; - } -} - -class BFGExtra : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - RenderStyle "Add"; - Alpha 0.75; - DamageType "BFGSplash"; - } - States - { - Spawn: - BFE2 ABCD 8 Bright; - Stop; - } -} - diff --git a/wadsrc/static/zscript/doom/fatso.txt b/wadsrc/static/zscript/doom/fatso.txt index d1a456d16..3831110c0 100644 --- a/wadsrc/static/zscript/doom/fatso.txt +++ b/wadsrc/static/zscript/doom/fatso.txt @@ -175,7 +175,7 @@ extend class Actor // Original idea: Linguica // - void A_Mushroom(class spawntype = "FatShot", int numspawns = 0, int flags = 0, float vrange = 4.0, float hrange = 0.5) + void A_Mushroom(class spawntype = "FatShot", int numspawns = 0, int flags = 0, double vrange = 4.0, double hrange = 0.5) { int i, j; @@ -192,7 +192,7 @@ extend class Actor aimtarget.Height = Height; bool shootmode = ((flags & MSF_Classic) || // Flag explicitely set, or no flags and compat options - (flags == 0 && isDehState(CurState) && GetCVar("compat_mushroom"))); + (flags == 0 && CurState.bDehacked && compat_mushroom)); for (i = -numspawns; i <= numspawns; i += 8) { diff --git a/wadsrc/static/zscript/doom/lostsoul.txt b/wadsrc/static/zscript/doom/lostsoul.txt index 7ef8b4cf0..89cefc42a 100644 --- a/wadsrc/static/zscript/doom/lostsoul.txt +++ b/wadsrc/static/zscript/doom/lostsoul.txt @@ -92,7 +92,7 @@ extend class Actor { const SKULLSPEED = 20; - void A_SkullAttack(float skullspeed = SKULLSPEED) + void A_SkullAttack(double skullspeed = SKULLSPEED) { if (target == null) return; diff --git a/wadsrc/static/zscript/doom/painelemental.txt b/wadsrc/static/zscript/doom/painelemental.txt index fde690b39..fe9e0590a 100644 --- a/wadsrc/static/zscript/doom/painelemental.txt +++ b/wadsrc/static/zscript/doom/painelemental.txt @@ -61,9 +61,140 @@ class PainElemental : Actor extend class Actor { - native void A_PainShootSkull(Class spawntype, float angle, int flags = 0, int limit = -1); + // + // A_PainShootSkull + // Spawn a lost soul and launch it at the target + // + void A_PainShootSkull(Class spawntype, double angle, int flags = 0, int limit = -1) + { + // Don't spawn if we get massacred. + if (DamageType == 'Massacre') return; - void A_PainAttack(class spawntype = "LostSoul", float addangle = 0, int flags = 0, int limit = -1) + if (spawntype == null) spawntype = "LostSoul"; + + // [RH] check to make sure it's not too close to the ceiling + if (pos.z + height + 8 > ceilingz) + { + if (bFloat) + { + Vel.Z -= 2; + bInFloat = true; + bVFriction = true; + } + return; + } + + // [RH] make this optional + if (limit < 0 && compat_limitpain) + limit = 21; + + if (limit > 0) + { + // 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; + ThinkerIterator it = ThinkerIterator.Create(spawntype); + Thinker othink; + + while ( (othink = it.Next ()) ) + { + if (--count == 0) + return; + } + } + + // okay, there's room for another one + double otherradius = GetDefaultByType(spawntype).radius; + double prestep = 4 + (radius + otherradius) * 1.5; + + Vector2 move = AngleToVector(angle, prestep); + Vector3 spawnpos = pos + (0,0,8); + Vector3 destpos = spawnpos + move; + + Actor other = Spawn(spawntype, spawnpos, ALLOW_REPLACE); + + // Now check if the spawn is legal. Unlike Boom's hopeless attempt at fixing it, let's do it the same way + // P_XYMovement solves the line skipping: Spawn the Lost Soul near the PE's center and then use multiple + // smaller steps to get it to its intended position. This will also result in proper clipping, but + // it will avoid all the problems of the Boom method, which checked too many lines that weren't even touched + // and despite some adjustments never worked with portals. + + if (other != null) + { + double maxmove = other.radius - 1; + + if (maxmove <= 0) maxmove = 16; + + double xspeed = abs(move.X); + double yspeed = abs(move.Y); + + int steps = 1; + + if (xspeed > yspeed) + { + if (xspeed > maxmove) + { + steps = int(1 + xspeed / maxmove); + } + } + else + { + if (yspeed > maxmove) + { + steps = int(1 + yspeed / maxmove); + } + } + + Vector2 stepmove = move / steps; + bool savedsolid = bSolid; + bool savednoteleport = other.bNoTeleport; + + // make the PE nonsolid for the check and the LS non-teleporting so that P_TryMove doesn't do unwanted things. + bSolid = false; + other.bNoTeleport = true; + for (int i = 0; i < steps; i++) + { + Vector2 ptry = other.pos.xy + stepmove; + double oldangle = other.angle; + if (!other.TryMove(ptry, 0)) + { + // kill it immediately + other.ClearCounters(); + other.DamageMobj(self, self, TELEFRAG_DAMAGE, 'None'); + bSolid = savedsolid; + other.bNoTeleport = savednoteleport; + return; + } + + if (other.pos.xy != ptry) + { + // If the new position does not match the desired position, the player + // must have gone through a portal. + // For that we need to adjust the movement vector for the following steps. + double anglediff = deltaangle(oldangle, other.angle); + + if (anglediff != 0) + { + stepmove = RotateVector(stepmove, anglediff); + } + } + + } + bSolid = savedsolid; + other.bNoTeleport = savednoteleport; + + // [RH] Lost souls hate the same things as their pain elementals + other.CopyFriendliness (self, !(flags & PAF_NOTARGET)); + + if (!(flags & PAF_NOSKULLATTACK)) + { + other.A_SkullAttack(); + } + } + } + + + void A_PainAttack(class spawntype = "LostSoul", double addangle = 0, int flags = 0, int limit = -1) { if (target) { @@ -71,6 +202,7 @@ extend class Actor A_PainShootSkull(spawntype, angle + addangle, flags, limit); } } + void A_DualPainAttack(class spawntype = "LostSoul") { if (target) diff --git a/wadsrc/static/zscript/doom/revenant.txt b/wadsrc/static/zscript/doom/revenant.txt index 2fc35c60f..2fa335a2d 100644 --- a/wadsrc/static/zscript/doom/revenant.txt +++ b/wadsrc/static/zscript/doom/revenant.txt @@ -129,8 +129,6 @@ class RevenantTracerSmoke : Actor extend class Actor { - const TRACEANGLE = (16.875); - void A_SkelMissile() { if (target == null) return; @@ -166,33 +164,12 @@ extend class Actor } } - void A_Tracer() + void A_Tracer2(double traceang = 19.6875) { double dist; double slope; Actor dest; - Actor smoke; - // killough 1/18/98: this is why some missiles do not have smoke - // and some do. Also, internal demos start at random gametics, thus - // the bug in which revenants cause internal demos to go out of sync. - // - // killough 3/6/98: fix revenant internal demo bug by subtracting - // levelstarttic from gametic: - // - // [RH] level.time is always 0-based, so nothing special to do here. - - if (level.time & 3) return; - - // spawn a puff of smoke behind the rocket - SpawnPuff ("BulletPuff", pos, angle, angle, 3); - smoke = Spawn ("RevenantTracerSmoke", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE); - - smoke.Vel.Z = 1.; - smoke.tics -= random[Tracer](0, 3); - if (smoke.tics < 1) - smoke.tics = 1; - // adjust direction dest = tracer; @@ -205,13 +182,13 @@ extend class Actor if (diff < 0) { - angle -= TRACEANGLE; + angle -= traceang; if (deltaangle(angle, exact) > 0) angle = exact; } else if (diff > 0) { - angle += TRACEANGLE; + angle += traceang; if (deltaangle(angle, exact) < 0.) angle = exact; } @@ -238,5 +215,31 @@ extend class Actor Vel.Z += 1. / 8; } } + + void A_Tracer() + { + // killough 1/18/98: this is why some missiles do not have smoke + // and some do. Also, internal demos start at random gametics, thus + // the bug in which revenants cause internal demos to go out of sync. + // + // killough 3/6/98: fix revenant internal demo bug by subtracting + // levelstarttic from gametic: + // + // [RH] level.time is always 0-based, so nothing special to do here. + + if (level.time & 3) return; + + // spawn a puff of smoke behind the rocket + SpawnPuff ("BulletPuff", pos, angle, angle, 3); + Actor smoke = Spawn ("RevenantTracerSmoke", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE); + + smoke.Vel.Z = 1.; + smoke.tics -= random[Tracer](0, 3); + if (smoke.tics < 1) + smoke.tics = 1; + + // The rest of this function was identical with Strife's version, except for the angle being used. + A_Tracer2(16.875); + } } diff --git a/wadsrc/static/zscript/doom/scriptedmarine.txt b/wadsrc/static/zscript/doom/scriptedmarine.txt index 7a82513cb..22766ac90 100644 --- a/wadsrc/static/zscript/doom/scriptedmarine.txt +++ b/wadsrc/static/zscript/doom/scriptedmarine.txt @@ -1,8 +1,35 @@ // Scriptable marine ------------------------------------------------------- -class ScriptedMarine : Actor native +class ScriptedMarine : Actor { + const MARINE_PAIN_CHANCE = 160; + + 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 + }; + + struct WeaponStates + { + state melee; + state missile; + } + + int CurrentWeapon; + SpriteID SpriteOverride; + Default { Health 100; @@ -10,7 +37,7 @@ class ScriptedMarine : Actor native Height 56; Mass 100; Speed 8; - Painchance 160; + Painchance MARINE_PAIN_CHANCE; MONSTER; -COUNTKILL Translation 0; @@ -19,23 +46,6 @@ class ScriptedMarine : Actor native PainSound "*pain50"; } - native void A_M_Refire (bool ignoremissile=false); - native void A_M_CheckAttack (); - native void A_MarineChase (); - native void A_MarineLook (); - native void A_MarineNoise (); - native void A_M_Punch (int force); - native void A_M_SawRefire (); - native void A_M_FirePistol (bool accurate); - native void A_M_FireShotgun (); - native void A_M_FireShotgun2 (); - native void A_M_FireCGun(bool accurate); - native void A_M_FireMissile (); - native void A_M_FirePlasma (); - native void A_M_FireRailgun (); - native void A_M_BFGsound (); - native void A_M_FireBFG (); - States { Spawn: @@ -58,25 +68,22 @@ class ScriptedMarine : Actor native PLAY E 4 A_FaceTarget; PLAY E 4 A_M_Punch(1); PLAY A 9; - PLAY A 0 A_M_Refire(1); + PLAY A 0 A_M_Refire(1, "FistEnd"); Loop; + FistEnd: PLAY A 5 A_FaceTarget; Goto See; Melee.Berserk: PLAY E 4 A_FaceTarget; PLAY E 4 A_M_Punch(10); PLAY A 9; - PLAY A 0 A_M_Refire(1); + PLAY A 0 A_M_Refire(1, "FistEnd"); Loop; - PLAY A 5 A_FaceTarget; - Goto See; Melee.Chainsaw: PLAY E 4 A_MarineNoise; PLAY E 4 A_M_Saw; PLAY E 0 A_M_SawRefire; goto Melee.Chainsaw+1; - PLAY A 0; - Goto See; Missile: Missile.None: @@ -88,16 +95,16 @@ class ScriptedMarine : Actor native PLAY E 4 A_FaceTarget; PLAY F 6 BRIGHT A_M_FirePistol(1); PLAY A 4 A_FaceTarget; - PLAY A 0 A_M_Refire; + PLAY A 0 A_M_Refire(0, "ShootEnd"); + Goto Fireloop.Pistol; + ShootEnd: PLAY A 5; Goto See; Fireloop.Pistol: PLAY F 6 BRIGHT A_M_FirePistol(0); PLAY A 4 A_FaceTarget; - PLAY A 0 A_M_Refire; + PLAY A 0 A_M_Refire(0, "ShootEnd"); Goto Fireloop.Pistol; - PLAY A 5; - Goto See; Missile.Shotgun: PLAY E 3 A_M_CheckAttack; PLAY F 7 BRIGHT A_M_FireShotgun; @@ -110,26 +117,20 @@ class ScriptedMarine : Actor native PLAY E 4 A_FaceTarget; PLAY FF 4 BRIGHT A_M_FireCGun(1); PLAY FF 4 BRIGHT A_M_FireCGun(0); - PLAY A 0 A_M_Refire; + PLAY A 0 A_M_Refire(0, "See"); Goto Missile.Chaingun+3; - PLAY A 0; - Goto See; Missile.Rocket: PLAY E 8; PLAY F 6 BRIGHT A_M_FireMissile; PLAY E 6; - PLAY A 0 A_M_Refire; + PLAY A 0 A_M_Refire(0, "See"); Loop; - PLAY A 0; - Goto See; Missile.Plasma: PLAY E 2 A_FaceTarget; PLAY E 0 A_FaceTarget; PLAY F 3 BRIGHT A_M_FirePlasma; - PLAY A 0 A_M_Refire; + PLAY A 0 A_M_Refire(0, "See"); Goto Missile.Plasma+1; - PLAY A 0; - Goto See; Missile.Railgun: PLAY E 4 A_M_CheckAttack; PLAY F 6 BRIGHT A_M_FireRailgun; @@ -139,10 +140,8 @@ class ScriptedMarine : Actor native PLAY EEEEE 5 A_FaceTarget; PLAY F 6 BRIGHT A_M_FireBFG; PLAY A 4 A_FaceTarget; - PLAY A 0 A_M_Refire; + PLAY A 0 A_M_Refire(0, "See"); Loop; - PLAY A 0; - Goto See; SkipAttack: PLAY A 1; @@ -169,6 +168,535 @@ class ScriptedMarine : Actor native PLAY MLKJIH 5; Goto See; } + + //============================================================================ + // + // + // + //============================================================================ + + private bool GetWeaponStates(int weap, out WeaponStates wstates) + { + static const statelabel MeleeNames[] = + { + "Melee.None", "Melee.Fist", "Melee.Berserk", "Melee.Chainsaw", "Melee.Pistol", "Melee.Shotgun", + "Melee.SSG", "Melee.Chaingun", "Melee.Rocket", "Melee.Plasma", "Melee.Railgun", "Melee.BFG" + }; + + static const statelabel MissileNames[] = + { + "Missile.None", "Missile.Fist", "Missile.Berserk", "Missile.Chainsaw", "Missile.Pistol", "Missile.Shotgun", + "Missile.SSG", "Missile.Chaingun", "Missile.Rocket", "Missile.Plasma", "Missile.Railgun", "Missile.BFG" + }; + + if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy; + + wstates.melee = FindState(MeleeNames[weap], true); + wstates.missile = FindState(MissileNames[weap], true); + + return wstates.melee != null || wstates.missile != null; + } + + //============================================================================ + // + // + // + //============================================================================ + + override void BeginPlay () + { + Super.BeginPlay (); + + // Set the current weapon + for(int i = WEAPON_Dummy; i <= WEAPON_BFG; i++) + { + WeaponStates wstates; + if (GetWeaponStates(i, wstates)) + { + if (wstates.melee == MeleeState && wstates.missile == MissileState) + { + CurrentWeapon = i; + } + } + } + } + + //============================================================================ + // + // + // + //============================================================================ + + override void 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: + A_PlaySound ("weapons/sshoto", CHAN_WEAPON); + break; + case 28: + A_PlaySound ("weapons/sshotl", CHAN_WEAPON); + break; + case 41: + A_PlaySound ("weapons/sshotc", CHAN_WEAPON); + break; + } + } + else + { + special1 = 0; + } + } + else + { // Wait for a long refire time + if (level.maptime >= special1) + { + special1 = 0; + } + else + { + bJustAttacked = true; + } + } + } + } + + //============================================================================ + // + // A_M_Refire + // + //============================================================================ + + void A_M_Refire (bool ignoremissile, statelabel jumpto) + { + if (target == null || target.health <= 0) + { + if (MissileState && random[SMarineRefire]() < 160) + { // Look for a new target most of the time + if (LookForPlayers (true) && CheckMissileRange ()) + { // Found somebody new and in range, so don't stop shooting + return; + } + } + SetStateLabel (jumpto); + return; + } + if (((ignoremissile || MissileState == null) && !CheckMeleeRange ()) || + !CheckSight (target) || random[SMarineRefire]() < 4) // Small chance of stopping even when target not dead + { + SetStateLabel (jumpto); + } + } + + //============================================================================ + // + // A_M_SawRefire + // + //============================================================================ + + void A_M_SawRefire () + { + if (target == null || target.health <= 0 || !CheckMeleeRange ()) + { + SetStateLabel ("See"); + } + } + + //============================================================================ + // + // A_MarineNoise + // + //============================================================================ + + void A_MarineNoise () + { + if (CurrentWeapon == WEAPON_Chainsaw) + { + A_PlaySound ("weapons/sawidle", CHAN_WEAPON); + } + } + + //============================================================================ + // + // A_MarineChase + // + //============================================================================ + + void A_MarineChase () + { + A_MarineNoise(); + A_Chase (); + } + + //============================================================================ + // + // A_MarineLook + // + //============================================================================ + + void A_MarineLook () + { + A_MarineNoise(); + A_Look(); + } + + //============================================================================ + // + // A_M_Punch (also used in the rocket attack.) + // + //============================================================================ + + void A_M_Punch(int damagemul) + { + FTranslatedLineTarget t; + + if (target == null) + return; + + int damage = (random[SMarinePunch](1, 10) << 1) * damagemul; + + A_FaceTarget (); + double ang = angle + random2[SMarinePunch]() * (5.625 / 256); + double pitch = AimLineAttack (ang, MELEERANGE); + LineAttack (ang, MELEERANGE, pitch, damage, 'Melee', "BulletPuff", true, t); + + // turn to face target + if (t.linetarget) + { + A_PlaySound ("*fist", CHAN_WEAPON); + angle = t.angleFromSource; + } + } + + //============================================================================ + // + // P_GunShot2 + // + //============================================================================ + + private void GunShot2 (bool accurate, double pitch, class pufftype) + { + int damage = 5 * random[SMarineGunshot](1,3); + double ang = angle; + + if (!accurate) + { + ang += Random2[SMarineGunshot]() * (5.625 / 256); + } + + LineAttack (ang, MISSILERANGE, pitch, damage, 'Hitscan', pufftype); + } + + //============================================================================ + // + // A_M_FirePistol + // + //============================================================================ + + void A_M_FirePistol (bool accurate) + { + if (target == null) + return; + + A_PlaySound ("weapons/pistol", CHAN_WEAPON); + A_FaceTarget (); + GunShot2 (accurate, AimLineAttack (angle, MISSILERANGE), "BulletPuff"); + } + + //============================================================================ + // + // A_M_FireShotgun + // + //============================================================================ + + void A_M_FireShotgun () + { + if (target == null) + return; + + A_PlaySound ("weapons/shotgf", CHAN_WEAPON); + A_FaceTarget (); + double pitch = AimLineAttack (angle, MISSILERANGE); + for (int i = 0; i < 7; ++i) + { + GunShot2 (false, pitch, "BulletPuff"); + } + special1 = level.maptime + 27; + } + + //============================================================================ + // + // A_M_CheckAttack + // + //============================================================================ + + void A_M_CheckAttack () + { + if (special1 != 0 || target == null) + { + SetStateLabel ("SkipAttack"); + } + else + { + A_FaceTarget (); + } + } + + //============================================================================ + // + // A_M_FireShotgun2 + // + //============================================================================ + + void A_M_FireShotgun2 () + { + if (target == null) + return; + + A_PlaySound ("weapons/sshotf", CHAN_WEAPON); + A_FaceTarget (); + double pitch = AimLineAttack (angle, MISSILERANGE); + for (int i = 0; i < 20; ++i) + { + int damage = 5*(random[SMarineFireSSG]()%3+1); + double ang = angle + Random2[SMarineFireSSG]() * (11.25 / 256); + + LineAttack (ang, MISSILERANGE, pitch + Random2[SMarineFireSSG]() * (7.097 / 256), damage, 'Hitscan', "BulletPuff"); + } + special1 = level.maptime; + } + + //============================================================================ + // + // A_M_FireCGun + // + //============================================================================ + + void A_M_FireCGun(bool accurate) + { + if (target == null) + return; + + A_PlaySound ("weapons/chngun", CHAN_WEAPON); + A_FaceTarget (); + GunShot2 (accurate, AimLineAttack (angle, MISSILERANGE), "BulletPuff"); + } + + //============================================================================ + // + // 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. + // + //============================================================================ + + void A_M_FireMissile () + { + if (target == null) + return; + + if (CheckMeleeRange ()) + { // If too close, punch it + A_M_Punch(1); + } + else + { + A_FaceTarget (); + SpawnMissile (target, "Rocket"); + } + } + + //============================================================================ + // + // A_M_FireRailgun + // + //============================================================================ + + void A_M_FireRailgun () + { + if (target == null) + return; + + A_MonsterRail(); + special1 = level.maptime + 50; + } + + //============================================================================ + // + // A_M_FirePlasma + // + //============================================================================ + + void A_M_FirePlasma () + { + if (target == null) + return; + + A_FaceTarget (); + SpawnMissile (target, "PlasmaBall"); + special1 = level.maptime + 20; + } + + //============================================================================ + // + // A_M_BFGsound + // + //============================================================================ + + void A_M_BFGsound () + { + if (target == null) + return; + + if (special1 != 0) + { + SetState (SeeState); + } + else + { + A_FaceTarget (); + A_PlaySound ("weapons/bfgf", CHAN_WEAPON); + // Don't interrupt the firing sequence + PainChance = 0; + } + } + + //============================================================================ + // + // A_M_FireBFG + // + //============================================================================ + + void A_M_FireBFG () + { + if (target == null) + return; + + A_FaceTarget (); + SpawnMissile (target, "BFGBall"); + special1 = level.maptime + 30; + PainChance = MARINE_PAIN_CHANCE; + } + + //--------------------------------------------------------------------------- + + final void SetWeapon (int type) + { + WeaponStates wstates; + if (GetWeaponStates(type, wstates)) + { + static const class classes[] = { + "ScriptedMarine", + "MarineFist", + "MarineBerserk", + "MarineChainsaw", + "MarinePistol", + "MarineShotgun", + "MarineSSG", + "MarineChaingun", + "MarineRocket", + "MarinePlasma", + "MarineRailgun", + "MarineBFG" + }; + + MeleeState = wstates.melee; + MissileState = wstates.missile; + DecalGenerator = GetDefaultByType(classes[type]).DecalGenerator; + } + } + + final void SetSprite (class source) + { + if (source == null) + { // A valid actor class wasn't passed, so use the standard sprite + SpriteOverride = sprite = SpawnState.sprite; + // Copy the standard scaling + Scale = Default.Scale; + } + else + { // Use the same sprite and scaling the passed class spawns with + readonly def = GetDefaultByType (source); + SpriteOverride = sprite = def.SpawnState.sprite; + Scale = def.Scale; + } + } +} + +extend class Actor +{ + //============================================================================ + // + // A_M_Saw (this is globally exported) + // + //============================================================================ + + void A_M_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff") + { + if (target == null) + return; + + if (pufftype == null) pufftype = "BulletPuff"; + if (damage == 0) damage = 2; + + A_FaceTarget (); + if (CheckMeleeRange ()) + { + FTranslatedLineTarget t; + + damage *= random[SMarineSaw](1, 10); + double ang = angle + Random2[SMarineSaw]() * (5.625 / 256); + + LineAttack (angle, SAWRANGE, AimLineAttack (angle, SAWRANGE), damage, 'Melee', pufftype, false, t); + + if (!t.linetarget) + { + A_PlaySound (fullsound, 1, CHAN_WEAPON); + return; + } + A_PlaySound (hitsound, CHAN_WEAPON); + + // turn to face target + ang = t.angleFromSource; + double anglediff = deltaangle(angle, ang); + + if (anglediff < 0.0) + { + if (anglediff < -4.5) + angle = ang + 90.0 / 21; + else + angle -= 4.5; + } + else + { + if (anglediff > 4.5) + angle = ang - 90.0 / 21; + else + angle += 4.5; + } + } + else + { + A_PlaySound (fullsound, 1, CHAN_WEAPON); + } + } } //--------------------------------------------------------------------------- diff --git a/wadsrc/static/zscript/doom/weaponbfg.txt b/wadsrc/static/zscript/doom/weaponbfg.txt new file mode 100644 index 000000000..9bf0084b3 --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponbfg.txt @@ -0,0 +1,259 @@ +// -------------------------------------------------------------------------- +// +// BFG 9000 +// +// -------------------------------------------------------------------------- + +class BFG9000 : DoomWeapon +{ + Default + { + Height 20; + Weapon.SelectionOrder 2800; + Weapon.AmmoUse 40; + Weapon.AmmoGive 40; + Weapon.AmmoType "Cell"; + +WEAPON.NOAUTOFIRE; + Inventory.PickupMessage "$GOTBFG9000"; + Tag "$TAG_BFG9000"; + } + States + { + Ready: + BFGG A 1 A_WeaponReady; + Loop; + Deselect: + BFGG A 1 A_Lower; + Loop; + Select: + BFGG A 1 A_Raise; + Loop; + Fire: + BFGG A 20 A_BFGsound; + BFGG B 10 A_GunFlash; + BFGG B 10 A_FireBFG; + BFGG B 20 A_ReFire; + Goto Ready; + Flash: + BFGF A 11 Bright A_Light1; + BFGF B 6 Bright A_Light2; + Goto LightDone; + Spawn: + BFUG A -1; + Stop; + OldFire: + BFGG A 10 A_BFGsound; + BFGG BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 1 A_FireOldBFG; + BFGG B 0 A_Light0; + BFGG B 20 A_ReFire; + Goto Ready; + } +} + +//=========================================================================== +// +// Weapon code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + action void A_BFGsound() + { + A_PlaySound("weapons/bfgf", CHAN_WEAPON); + } + + + // + // A_FireBFG + // + + action void A_FireBFG() + { + if (player == null) + { + return; + } + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, deh.BFGCells)) + return; + } + + SpawnPlayerMissile("BFGBall", angle, nofreeaim:sv_nobfgaim); + } + + + // + // 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. + + action void A_FireOldBFG() + { + bool doesautoaim = false; + + if (player == null) + { + return; + } + Weapon weap = player.ReadyWeapon; + + if (invoker != weap || stateinfo == null || stateinfo.mStateType != STATE_Psprite) weap = null; + if (weap != null) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + + doesautoaim = weap.bNoAutoaim; + weap.bNoAutoaim = true; + } + player.extralight = 2; + + // Save values temporarily + double SavedPlayerAngle = angle; + double SavedPlayerPitch = pitch; + for (int i = 0; i < 2; i++) // Spawn two plasma balls in sequence + { + angle += ((random[OldBFG]() & 127) - 64) * (90./768); + pitch += ((random[OldBFG]() & 127) - 64) * (90./640); + SpawnPlayerMissile (i == 0? (class)("PlasmaBall1") : (class)("PlasmaBall2")); + // Restore saved values + angle = SavedPlayerAngle; + pitch = SavedPlayerPitch; + } + // Restore autoaim setting + if (weap != null) weap.bNoAutoaim = doesautoaim; + } +} + +class BFGBall : Actor +{ + Default + { + Radius 13; + Height 8; + Speed 25; + Damage 100; + Projectile; + +RANDOMIZE + RenderStyle "Add"; + Alpha 0.75; + DeathSound "weapons/bfgx"; + Obituary "$OB_MPBFG_BOOM"; + } + States + { + Spawn: + BFS1 AB 4 Bright; + Loop; + Death: + BFE1 AB 8 Bright; + BFE1 C 8 Bright A_BFGSpray; + BFE1 DEF 8 Bright; + Stop; + } +} + +class BFGExtra : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + RenderStyle "Add"; + Alpha 0.75; + DamageType "BFGSplash"; + } + States + { + Spawn: + BFE2 ABCD 8 Bright; + Stop; + } +} + + +//=========================================================================== +// +// Code (must be attached to Actor) +// +//=========================================================================== + +extend class Actor +{ + // + // A_BFGSpray + // Spawn a BFG explosion on every monster in view + // + void A_BFGSpray(class spraytype = "BFGExtra", int numrays = 40, int damagecnt = 15, double ang = 90, double distance = 16*64, double vrange = 32, int defdamage = 0, int flags = 0) + { + int damage; + FTranslatedLineTarget t; + + // validate parameters + if (spraytype == null) spraytype = "BFGExtra"; + if (numrays <= 0) numrays = 40; + if (damagecnt <= 0) damagecnt = 15; + if (ang == 0) ang = 90.; + if (distance <= 0) distance = 16 * 64; + if (vrange == 0) vrange = 32.; + + // [RH] Don't crash if no target + if (!target) return; + + // [XA] Set the originator of the rays to the projectile (self) if + // the new flag is set, else set it to the player (target) + Actor originator = (flags & BFGF_MISSILEORIGIN) ? self : target; + + // offset angles from its attack ang + for (int i = 0; i < numrays; i++) + { + double an = angle - ang / 2 + ang / numrays*i; + + originator.AimLineAttack(an, distance, t, vrange); + + if (t.linetarget != null) + { + Actor spray = Spawn(spraytype, t.linetarget.pos + (0, 0, t.linetarget.Height / 4), ALLOW_REPLACE); + + int dmgFlags = 0; + Name dmgType = 'BFGSplash'; + + if (spray != null) + { + if ((spray.bMThruSpecies && target.GetSpecies() == t.linetarget.GetSpecies()) || + (!(flags & BFGF_HURTSOURCE) && 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.bPuffGetsOwner) spray.target = target; + if (spray.bFoilInvul) dmgFlags |= DMG_FOILINVUL; + if (spray.bFoilBuddha) dmgFlags |= DMG_FOILBUDDHA; + dmgType = spray.DamageType; + } + + if (defdamage == 0) + { + damage = 0; + for (int j = 0; j < damagecnt; ++j) + damage += Random[BFGSpray](1, 8); + } + else + { + // if this is used, damagecnt will be ignored + damage = defdamage; + } + + int newdam = t.linetarget.DamageMobj(originator, target, damage, dmgType, dmgFlags|DMG_USEANGLE, t.angleFromSource); + t.TraceBleed(newdam > 0 ? newdam : damage, self); + } + } + } +} diff --git a/wadsrc/static/zscript/doom/weaponchaingun.txt b/wadsrc/static/zscript/doom/weaponchaingun.txt new file mode 100644 index 000000000..3766b9b6e --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponchaingun.txt @@ -0,0 +1,82 @@ +// -------------------------------------------------------------------------- +// +// Chaingun +// +// -------------------------------------------------------------------------- + +class Chaingun : DoomWeapon +{ + Default + { + Weapon.SelectionOrder 700; + Weapon.AmmoUse 1; + Weapon.AmmoGive 20; + Weapon.AmmoType "Clip"; + Inventory.PickupMessage "$GOTCHAINGUN"; + Obituary "$OB_MPCHAINGUN"; + Tag "$TAG_CHAINGUN"; + } + States + { + Ready: + CHGG A 1 A_WeaponReady; + Loop; + Deselect: + CHGG A 1 A_Lower; + Loop; + Select: + CHGG A 1 A_Raise; + Loop; + Fire: + CHGG AB 4 A_FireCGun; + CHGG B 0 A_ReFire; + Goto Ready; + Flash: + CHGF A 5 Bright A_Light1; + Goto LightDone; + CHGF B 5 Bright A_Light2; + Goto LightDone; + Spawn: + MGUN A -1; + Stop; + } +} + +//=========================================================================== +// +// Code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + action void A_FireCGun() + { + if (player == null) + { + return; + } + + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + + A_PlaySound ("weapons/chngun", CHAN_WEAPON); + + State flash = weap.FindState('Flash'); + if (flash != null) + { + // Removed most of the mess that was here in the C++ code because SetSafeFlash already does some thorough validation. + State atk = weap.FindState('Fire'); + State cur = player.GetPSprite(PSP_WEAPON).CurState; + int theflash = atk == cur? 0:1; + player.SetSafeFlash(weap, flash, theflash); + } + } + player.mo.PlayAttacking2 (); + + GunShot (!player.refire, "BulletPuff", BulletSlope ()); + } +} diff --git a/wadsrc/static/zscript/doom/weaponchainsaw.txt b/wadsrc/static/zscript/doom/weaponchainsaw.txt new file mode 100644 index 000000000..0653bc16d --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponchainsaw.txt @@ -0,0 +1,166 @@ +// -------------------------------------------------------------------------- +// +// Chainsaw +// +// -------------------------------------------------------------------------- + +class Chainsaw : Weapon +{ + Default + { + Weapon.Kickback 0; + Weapon.SelectionOrder 2200; + Weapon.UpSound "weapons/sawup"; + Weapon.ReadySound "weapons/sawidle"; + Inventory.PickupMessage "$GOTCHAINSAW"; + Obituary "$OB_MPCHAINSAW"; + Tag "$TAG_CHAINSAW"; + +WEAPON.MELEEWEAPON + } + States + { + Ready: + SAWG CD 4 A_WeaponReady; + Loop; + Deselect: + SAWG C 1 A_Lower; + Loop; + Select: + SAWG C 1 A_Raise; + Loop; + Fire: + SAWG AB 4 A_Saw; + SAWG B 0 A_ReFire; + Goto Ready; + Spawn: + CSAW A -1; + Stop; + } +} + + +extend class StateProvider +{ + action void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff", int flags = 0, double range = 0, double spread_xy = 2.8125, double spread_z = 0, double lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus") + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + if (pufftype == null) + { + pufftype = 'BulletPuff'; + } + if (damage == 0) + { + damage = 2; + } + if (!(flags & SF_NORANDOM)) + { + damage *= random[Saw](1, 10); + } + if (range == 0) + { + range = SAWRANGE; + } + + double ang = angle + spread_xy * (Random2[Saw]() / 255.); + double slope = AimLineAttack (ang, range, t) + spread_z * (Random2[Saw]() / 255.); + + Weapon weap = player.ReadyWeapon; + if (weap != null && !(flags & SF_NOUSEAMMO) && !(!t.linetarget && (flags & SF_NOUSEAMMOMISS)) && !weap.bDehAmmo && + invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire)) + return; + } + + Actor puff; + int actualdamage; + [puff, actualdamage] = LineAttack (ang, range, slope, damage, 'Melee', pufftype, false, t); + + if (!t.linetarget) + { + if ((flags & SF_RANDOMLIGHTMISS) && (Random[Saw]() > 64)) + { + player.extralight = !player.extralight; + } + A_PlaySound (fullsound, CHAN_WEAPON); + return; + } + + if (flags & SF_RANDOMLIGHTHIT) + { + int randVal = Random[Saw](); + if (randVal < 64) + { + player.extralight = 0; + } + else if (randVal < 160) + { + player.extralight = 1; + } + else + { + player.extralight = 2; + } + } + + if (lifesteal && !t.linetarget.bDontDrain) + { + if (flags & SF_STEALARMOR) + { + if (armorbonustype == null) + { + armorbonustype = "ArmorBonus"; + } + if (armorbonustype != null) + { + BasicArmorBonus armorbonus = BasicArmorBonus(Spawn(armorbonustype)); + armorbonus.SaveAmount = int(armorbonus.SaveAmount * actualdamage * lifesteal); + armorbonus.MaxSaveAmount = lifestealmax <= 0 ? armorbonus.MaxSaveAmount : lifestealmax; + armorbonus.bDropped = true; + armorbonus.ClearCounters(); + + if (!armorbonus.CallTryPickup (self)) + { + armorbonus.Destroy (); + } + } + } + + else + { + GiveBody (int(actualdamage * lifesteal), lifestealmax); + } + } + + A_PlaySound (hitsound, CHAN_WEAPON); + + // turn to face target + if (!(flags & SF_NOTURN)) + { + double anglediff = deltaangle(angle, t.angleFromSource); + + if (anglediff < 0.0) + { + if (anglediff < -4.5) + angle = ang + 90.0 / 21; + else + angle -= 4.5; + } + else + { + if (anglediff > 4.5) + angle = ang - 90.0 / 21; + else + angle += 4.5; + } + } + if (!(flags & SF_NOPULLIN)) + bJustAttacked = true; + } +} diff --git a/wadsrc/static/zscript/doom/weaponfist.txt b/wadsrc/static/zscript/doom/weaponfist.txt new file mode 100644 index 000000000..3c1cc992f --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponfist.txt @@ -0,0 +1,80 @@ +// -------------------------------------------------------------------------- +// +// Fist +// +// -------------------------------------------------------------------------- + +class Fist : Weapon +{ + Default + { + Weapon.SelectionOrder 3700; + Weapon.Kickback 100; + Obituary "$OB_MPFIST"; + Tag "$TAG_FIST"; + +WEAPON.WIMPY_WEAPON + +WEAPON.MELEEWEAPON + } + States + { + Ready: + PUNG A 1 A_WeaponReady; + Loop; + Deselect: + PUNG A 1 A_Lower; + Loop; + Select: + PUNG A 1 A_Raise; + Loop; + Fire: + PUNG B 4; + PUNG C 4 A_Punch; + PUNG D 5; + PUNG C 4; + PUNG B 5 A_ReFire; + Goto Ready; + } +} + + +//=========================================================================== +// +// Code (must be attached to Actor) +// +//=========================================================================== + +extend class Actor +{ + action void A_Punch() + { + FTranslatedLineTarget t; + + if (player != null) + { + Weapon weap = player.ReadyWeapon; + if (weap != null && !weap.bDehAmmo && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire)) + return; + } + } + + int damage = random[Punch](1, 10) << 1; + + if (FindInventory("PowerStrength")) + damage *= 10; + + double ang = angle + Random2[Punch]() * (5.625 / 256); + double pitch = AimLineAttack (ang, MELEERANGE); + + LineAttack (ang, MELEERANGE, pitch, damage, 'Melee', "BulletPuff", LAF_ISMELEEATTACK, t); + + // turn to face target + if (t.linetarget) + { + A_PlaySound ("*fist", CHAN_WEAPON); + angle = t.angleFromSource; + } + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/doom/weaponpistol.txt b/wadsrc/static/zscript/doom/weaponpistol.txt new file mode 100644 index 000000000..45a1ebecd --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponpistol.txt @@ -0,0 +1,100 @@ +// -------------------------------------------------------------------------- +// +// Pistol +// +// -------------------------------------------------------------------------- + +class Pistol : DoomWeapon +{ + Default + { + Weapon.SelectionOrder 1900; + Weapon.AmmoUse 1; + Weapon.AmmoGive 20; + Weapon.AmmoType "Clip"; + Obituary "$OB_MPPISTOL"; + +WEAPON.WIMPY_WEAPON + Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED"; + Tag "$TAG_PISTOL"; + } + States + { + Ready: + PISG A 1 A_WeaponReady; + Loop; + Deselect: + PISG A 1 A_Lower; + Loop; + Select: + PISG A 1 A_Raise; + Loop; + Fire: + PISG A 4; + PISG B 6 A_FirePistol; + PISG C 4; + PISG B 5 A_ReFire; + Goto Ready; + Flash: + PISF A 7 Bright A_Light1; + Goto LightDone; + PISF A 7 Bright A_Light1; + Goto LightDone; + Spawn: + PIST A -1; + Stop; + } +} + +//=========================================================================== +// +// Code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + //=========================================================================== + // This is also used by the shotgun and chaingun + //=========================================================================== + + protected action void GunShot(bool accurate, Class pufftype, double pitch) + { + int damage = 5 * random[GunShot](1, 3); + double ang = angle; + + if (!accurate) + { + ang += Random2[GunShot]() * (5.625 / 256); + } + + LineAttack(ang, PLAYERMISSILERANGE, pitch, damage, 'Hitscan', pufftype); + } + + //=========================================================================== + action void A_FirePistol() + { + bool accurate; + + if (player != null) + { + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + + player.SetPsprite(PSP_FLASH, weap.FindState('Flash'), true); + } + player.mo.PlayAttacking2 (); + + accurate = !player.refire; + } + else + { + accurate = true; + } + + A_PlaySound ("weapons/pistol", CHAN_WEAPON); + GunShot (accurate, "BulletPuff", BulletSlope ()); + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/doom/weaponplasma.txt b/wadsrc/static/zscript/doom/weaponplasma.txt new file mode 100644 index 000000000..945339a35 --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponplasma.txt @@ -0,0 +1,148 @@ +// -------------------------------------------------------------------------- +// +// Plasma rifle +// +// -------------------------------------------------------------------------- + +class PlasmaRifle : DoomWeapon +{ + Default + { + Weapon.SelectionOrder 100; + Weapon.AmmoUse 1; + Weapon.AmmoGive 40; + Weapon.AmmoType "Cell"; + Inventory.PickupMessage "$GOTPLASMA"; + Tag "$TAG_PLASMARIFLE"; + } + States + { + Ready: + PLSG A 1 A_WeaponReady; + Loop; + Deselect: + PLSG A 1 A_Lower; + Loop; + Select: + PLSG A 1 A_Raise; + Loop; + Fire: + PLSG A 3 A_FirePlasma; + PLSG B 20 A_ReFire; + Goto Ready; + Flash: + PLSF A 4 Bright A_Light1; + Goto LightDone; + PLSF B 4 Bright A_Light1; + Goto LightDone; + Spawn: + PLAS A -1; + Stop; + } +} + +class PlasmaBall : Actor +{ + Default + { + Radius 13; + Height 8; + Speed 25; + Damage 5; + Projectile; + +RANDOMIZE + RenderStyle "Add"; + Alpha 0.75; + SeeSound "weapons/plasmaf"; + DeathSound "weapons/plasmax"; + Obituary "$OB_MPPLASMARIFLE"; + } + States + { + Spawn: + PLSS AB 6 Bright; + Loop; + Death: + PLSE ABCDE 4 Bright; + Stop; + } +} + +// -------------------------------------------------------------------------- +// +// BFG 2704 +// +// -------------------------------------------------------------------------- + +class PlasmaBall1 : PlasmaBall +{ + Default + { + Damage 4; + BounceType "Classic"; + BounceFactor 1.0; + Obituary "$OB_MPBFG_MBF"; + } + States + { + Spawn: + PLS1 AB 6 Bright; + Loop; + Death: + PLS1 CDEFG 4 Bright; + Stop; + } +} + +class PlasmaBall2 : PlasmaBall1 +{ + States + { + Spawn: + PLS2 AB 6 Bright; + Loop; + Death: + PLS2 CDE 4 Bright; + Stop; + } +} + + +//=========================================================================== +// +// Code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + + //=========================================================================== + // + // A_FirePlasma + // + //=========================================================================== + + action void A_FirePlasma() + { + if (player == null) + { + return; + } + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + + State flash = weap.FindState('Flash'); + if (flash != null) + { + player.SetSafeFlash(weap, flash, random[FirePlasma]()&1); + } + + } + + SpawnPlayerMissile ("PlasmaBall"); + } +} diff --git a/wadsrc/static/zscript/doom/weaponrlaunch.txt b/wadsrc/static/zscript/doom/weaponrlaunch.txt new file mode 100644 index 000000000..906db3823 --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponrlaunch.txt @@ -0,0 +1,189 @@ +// -------------------------------------------------------------------------- +// +// Rocket launcher +// +// -------------------------------------------------------------------------- + +class RocketLauncher : DoomWeapon +{ + Default + { + Weapon.SelectionOrder 2500; + Weapon.AmmoUse 1; + Weapon.AmmoGive 2; + Weapon.AmmoType "RocketAmmo"; + +WEAPON.NOAUTOFIRE + Inventory.PickupMessage "$GOTLAUNCHER"; + Tag "$TAG_ROCKETLAUNCHER"; + } + States + { + Ready: + MISG A 1 A_WeaponReady; + Loop; + Deselect: + MISG A 1 A_Lower; + Loop; + Select: + MISG A 1 A_Raise; + Loop; + Fire: + MISG B 8 A_GunFlash; + MISG B 12 A_FireMissile; + MISG B 0 A_ReFire; + Goto Ready; + Flash: + MISF A 3 Bright A_Light1; + MISF B 4 Bright; + MISF CD 4 Bright A_Light2; + Goto LightDone; + Spawn: + LAUN A -1; + Stop; + } +} + +class Rocket : Actor +{ + Default + { + Radius 11; + Height 8; + Speed 20; + Damage 20; + Projectile; + +RANDOMIZE + +DEHEXPLOSION + +ROCKETTRAIL + SeeSound "weapons/rocklf"; + DeathSound "weapons/rocklx"; + Obituary "$OB_MPROCKET"; + } + States + { + Spawn: + MISL A 1 Bright; + Loop; + Death: + MISL B 8 Bright A_Explode; + MISL C 6 Bright; + MISL D 4 Bright; + Stop; + BrainExplode: + MISL BC 10 Bright; + MISL D 10 A_BrainExplode; + Stop; + } +} + +// -------------------------------------------------------------------------- +// +// Grenade -- Taken and adapted from Skulltag, with MBF stuff added to it +// +// -------------------------------------------------------------------------- + +class Grenade : Actor +{ + Default + { + Radius 8; + Height 8; + Speed 25; + Damage 20; + Projectile; + -NOGRAVITY + +RANDOMIZE + +DEHEXPLOSION + +GRENADETRAIL + BounceType "Doom"; + Gravity 0.25; + SeeSound "weapons/grenlf"; + DeathSound "weapons/grenlx"; + BounceSound "weapons/grbnce"; + Obituary "$OB_GRENADE"; + DamageType "Grenade"; + } + States + { + Spawn: + SGRN A 1 Bright; + Loop; + Death: + MISL B 8 Bright A_Explode; + MISL C 6 Bright; + MISL D 4 Bright; + Stop; + Grenade: + MISL A 1000 A_Die; + Wait; + Detonate: + MISL B 4 A_Scream; + MISL C 6 A_Detonate; + MISL D 10; + Stop; + Mushroom: + MISL B 8 A_Mushroom; + Goto Death+1; + } +} + +//=========================================================================== +// +// Code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + + //=========================================================================== + // + // A_FireMissile + // + //=========================================================================== + + action void A_FireMissile() + { + if (player == null) + { + return; + } + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + } + + SpawnPlayerMissile ("Rocket"); + } + + //=========================================================================== + // + // A_FireSTGrenade: not exactly backported from ST, but should work the same + // + //=========================================================================== + + action void A_FireSTGrenade(class grenadetype = "Grenade") + { + if (grenadetype == null) + return; + + if (player == null) + { + return; + } + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + } + + // Temporarily raise the pitch to send the grenadetype slightly upwards + double savedpitch = pitch; + pitch -= 6.328125; + SpawnPlayerMissile(grenadetype); + pitch = SavedPitch; + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/doom/weaponshotgun.txt b/wadsrc/static/zscript/doom/weaponshotgun.txt new file mode 100644 index 000000000..6a08afc11 --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponshotgun.txt @@ -0,0 +1,85 @@ +// -------------------------------------------------------------------------- +// +// Shotgun +// +// -------------------------------------------------------------------------- + +class Shotgun : DoomWeapon +{ + Default + { + Weapon.SelectionOrder 1300; + Weapon.AmmoUse 1; + Weapon.AmmoGive 8; + Weapon.AmmoType "Shell"; + Inventory.PickupMessage "$GOTSHOTGUN"; + Obituary "$OB_MPSHOTGUN"; + Tag "$TAG_SHOTGUN"; + } + States + { + Ready: + SHTG A 1 A_WeaponReady; + Loop; + Deselect: + SHTG A 1 A_Lower; + Loop; + Select: + SHTG A 1 A_Raise; + Loop; + Fire: + SHTG A 3; + SHTG A 7 A_FireShotgun; + SHTG BC 5; + SHTG D 4; + SHTG CB 5; + SHTG A 3; + SHTG A 7 A_ReFire; + Goto Ready; + Flash: + SHTF A 4 Bright A_Light1; + SHTF B 3 Bright A_Light2; + Goto LightDone; + Spawn: + SHOT A -1; + Stop; + } +} + +//=========================================================================== +// +// Code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + + action void A_FireShotgun() + { + if (player == null) + { + return; + } + + A_PlaySound ("weapons/shotgf", CHAN_WEAPON); + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 1)) + return; + + player.SetPsprite(PSP_FLASH, weap.FindState('Flash'), true); + } + player.mo.PlayAttacking2 (); + + double pitch = BulletSlope (); + + for (int i = 0; i < 7; i++) + { + GunShot (false, "BulletPuff", pitch); + } + } + +} + diff --git a/wadsrc/static/zscript/doom/weaponssg.txt b/wadsrc/static/zscript/doom/weaponssg.txt new file mode 100644 index 000000000..6c06fe6ec --- /dev/null +++ b/wadsrc/static/zscript/doom/weaponssg.txt @@ -0,0 +1,117 @@ +// -------------------------------------------------------------------------- +// +// Super Shotgun +// +// -------------------------------------------------------------------------- + +class SuperShotgun : DoomWeapon +{ + Default + { + Weapon.SelectionOrder 400; + Weapon.AmmoUse 2; + Weapon.AmmoGive 8; + Weapon.AmmoType "Shell"; + Inventory.PickupMessage "$GOTSHOTGUN2"; + Obituary "$OB_MPSSHOTGUN"; + Tag "$TAG_SUPERSHOTGUN"; + } + States + { + Ready: + SHT2 A 1 A_WeaponReady; + Loop; + Deselect: + SHT2 A 1 A_Lower; + Loop; + Select: + SHT2 A 1 A_Raise; + Loop; + Fire: + SHT2 A 3; + SHT2 A 7 A_FireShotgun2; + SHT2 B 7; + SHT2 C 7 A_CheckReload; + SHT2 D 7 A_OpenShotgun2; + SHT2 E 7; + SHT2 F 7 A_LoadShotgun2; + SHT2 G 6; + SHT2 H 6 A_CloseShotgun2; + SHT2 A 5 A_ReFire; + Goto Ready; + // unused states + SHT2 B 7; + SHT2 A 3; + Goto Deselect; + Flash: + SHT2 I 4 Bright A_Light1; + SHT2 J 3 Bright A_Light2; + Goto LightDone; + Spawn: + SGN2 A -1; + Stop; + } +} + + + +//=========================================================================== +// +// Code (must be attached to StateProvider) +// +//=========================================================================== + +extend class StateProvider +{ + action void A_FireShotgun2() + { + if (player == null) + { + return; + } + + A_PlaySound ("weapons/sshotf", CHAN_WEAPON); + Weapon weap = player.ReadyWeapon; + if (weap != null && invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 2)) + return; + + player.SetPsprite(PSP_FLASH, weap.FindState('Flash'), true); + } + player.mo.PlayAttacking2 (); + + double pitch = BulletSlope (); + + for (int i = 0 ; i < 20 ; i++) + { + int damage = 5 * random[FireSG2](1, 3); + double ang = angle + Random2[FireSG2]() * (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. + + LineAttack (ang, PLAYERMISSILERANGE, pitch + Random2[FireSG2]() * (7.097 / 256), damage, 'Hitscan', "BulletPuff"); + } + } + + + action void A_OpenShotgun2() + { + A_PlaySound("weapons/sshoto", CHAN_WEAPON); + } + + action void A_LoadShotgun2() + { + A_PlaySound("weapons/sshotl", CHAN_WEAPON); + } + + action void A_CloseShotgun2() + { + A_PlaySound("weapons/sshotc", CHAN_WEAPON); + A_Refire(); + } +} diff --git a/wadsrc/static/zscript/heretic/chicken.txt b/wadsrc/static/zscript/heretic/chicken.txt index 2e5ada81c..5d6065f66 100644 --- a/wadsrc/static/zscript/heretic/chicken.txt +++ b/wadsrc/static/zscript/heretic/chicken.txt @@ -26,8 +26,6 @@ class Beak : Weapon Weapon.SisterWeapon "BeakPowered"; } - action native void A_BeakRaise (); - action native void A_BeakAttackPL1(); States { @@ -44,9 +42,56 @@ class Beak : Weapon BEAK A 18 A_BeakAttackPL1; Goto Ready; } + + //--------------------------------------------------------------------------- + // + // PROC A_BeakRaise + // + //--------------------------------------------------------------------------- + + action void A_BeakRaise () + { + + if (player == null) + { + return; + } + player.GetPSprite(PSP_WEAPON).y = WEAPONTOP; + player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState()); + } + + //---------------------------------------------------------------------------- + // + // PROC A_BeakAttackPL1 + // + //---------------------------------------------------------------------------- + + action void A_BeakAttackPL1() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + int damage = random[BeakAtk](1,3); + double ang = angle; + double slope = AimLineAttack (ang, MELEERANGE); + LineAttack (ang, MELEERANGE, slope, damage, 'Melee', "BeakPuff", true, t); + if (t.linetarget) + { + angle = t.angleFromSource; + } + A_PlaySound ("chicken/peck", CHAN_VOICE); + player.chickenPeck = 12; + player.GetPSprite(PSP_WEAPON).Tics -= random[BeakAtk](0,7); + } } +// BeakPowered --------------------------------------------------------------------- + class BeakPowered : Beak { Default @@ -55,7 +100,6 @@ class BeakPowered : Beak Weapon.SisterWeapon "Beak"; } - action native void A_BeakAttackPL2(); States { @@ -63,11 +107,40 @@ class BeakPowered : Beak BEAK A 12 A_BeakAttackPL2; Goto Ready; } + + //---------------------------------------------------------------------------- + // + // PROC A_BeakAttackPL2 + // + //---------------------------------------------------------------------------- + + action void A_BeakAttackPL2() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + int damage = random[BeakAtk](1,8) * 4; + double ang = angle; + double slope = AimLineAttack (ang, MELEERANGE); + LineAttack (ang, MELEERANGE, slope, damage, 'Melee', "BeakPuff", true, t); + if (t.linetarget) + { + angle = t.angleFromSource; + } + A_PlaySound ("chicken/peck", CHAN_VOICE); + player.chickenPeck = 12; + player.GetPSprite(PSP_WEAPON).Tics -= random[BeakAtk](0,3); + } + } // Chicken player ----------------------------------------------------------- -class ChickenPlayer : PlayerPawn native +class ChickenPlayer : PlayerPawn { Default { @@ -118,6 +191,44 @@ class ChickenPlayer : PlayerPawn native CHKN L -1; Stop; } + + //--------------------------------------------------------------------------- + // + // PROC P_UpdateBeak + // + //--------------------------------------------------------------------------- + + override void MorphPlayerThink () + { + if (health > 0) + { // Handle beak movement + PSprite pspr; + if (player != null && (pspr = player.FindPSprite(PSP_WEAPON)) != null) + { + pspr.y = WEAPONTOP + player.chickenPeck / 2; + } + } + if (player.morphTics & 15) + { + return; + } + if (Vel.X == 0 && Vel.Y == 0 && random[ChickenPlayerThink]() < 160) + { // Twitch view ang + angle += Random2[ChickenPlayerThink]() * (360. / 256. / 32.); + } + if ((pos.z <= floorz) && (random[ChickenPlayerThink]() < 32)) + { // Jump and noise + Vel.Z += JumpZ; + + State painstate = FindState('Pain'); + if (painstate != null) SetState (painstate); + } + if (random[ChickenPlayerThink]() < 48) + { // Just noise + A_PlaySound ("chicken/active", CHAN_VOICE); + } + } + } @@ -199,3 +310,37 @@ class Feather : Actor } } +extend class Actor +{ + //---------------------------------------------------------------------------- + // + // PROC A_Feathers + // This is used by both the chicken player and monster and must be in the + // common base class to be accessible by both + // + //---------------------------------------------------------------------------- + + void A_Feathers() + { + int count; + + if (health > 0) + { // Pain + count = random[Feathers]() < 32 ? 2 : 1; + } + else + { // Death + count = 5 + (random[Feathers]()&3); + } + for (int i = 0; i < count; i++) + { + Actor mo = Spawn("Feather", pos + (0, 0, 20), NO_REPLACE); + mo.target = self; + mo.Vel.X = Random2[Feathers]() / 256.; + mo.Vel.Y = Random2[Feathers]() / 256.; + mo.Vel.Z = 1. + random[Feathers]() / 128.; + mo.SetState (mo.SpawnState + (random[Feathers]()&7)); + } + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/heretic/dsparil.txt b/wadsrc/static/zscript/heretic/dsparil.txt index 8cd60c8bb..54931b65e 100644 --- a/wadsrc/static/zscript/heretic/dsparil.txt +++ b/wadsrc/static/zscript/heretic/dsparil.txt @@ -38,10 +38,6 @@ class Sorcerer1 : Actor HitObituary "$OB_DSPARIL1HIT"; } - native void A_Sor1Pain (); - native void A_Sor1Chase (); - native void A_Srcr1Attack (); - native void A_SorcererRise (); States { @@ -79,6 +75,104 @@ class Sorcerer1 : Actor SRCR L 12; SRCR P -1 A_SorcererRise; } + + + //---------------------------------------------------------------------------- + // + // PROC A_Sor1Pain + // + //---------------------------------------------------------------------------- + + void A_Sor1Pain () + { + special1 = 20; // Number of steps to walk fast + A_Pain(); + } + + //---------------------------------------------------------------------------- + // + // PROC A_Sor1Chase + // + //---------------------------------------------------------------------------- + + void A_Sor1Chase () + { + if (special1) + { + special1--; + tics -= 3; + } + A_Chase(); + } + + //---------------------------------------------------------------------------- + // + // PROC A_Srcr1Attack + // + // Sorcerer demon attack. + // + //---------------------------------------------------------------------------- + + void A_Srcr1Attack () + { + if (!target) + { + return; + } + A_PlaySound (AttackSound, CHAN_BODY); + if (CheckMeleeRange ()) + { + int damage = random[Srcr1Attack](1,8) * 8; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + + if (health > (SpawnHealth()/3)*2) + { // Spit one fireball + SpawnMissileZ (pos.z + 48, target, "SorcererFX1"); + } + else + { // Spit three fireballs + Actor mo = SpawnMissileZ (pos.z + 48, target, "SorcererFX1"); + if (mo != null) + { + double ang = mo.angle; + SpawnMissileAngleZ(pos.z + 48, "SorcererFX1", ang - 3, mo.Vel.Z); + SpawnMissileAngleZ(pos.z + 48, "SorcererFX1", ang + 3, mo.Vel.Z); + } + if (health < SpawnHealth()/3) + { // Maybe attack again + if (special1) + { // Just attacked, so don't attack again + special1 = 0; + } + else + { // Set state to attack again + special1 = 1; + SetStateLabel("Missile2"); + } + } + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_SorcererRise + // + //---------------------------------------------------------------------------- + + void A_SorcererRise () + { + bSolid = false; + Actor mo = Spawn("Sorcerer2", Pos, ALLOW_REPLACE); + mo.Translation = Translation; + mo.SetStateLabel("Rise"); + mo.angle = angle; + mo.CopyFriendliness (self, true); + } + + } @@ -142,10 +236,6 @@ class Sorcerer2 : Actor HitObituary "$OB_DSPARIL2HIT"; } - native void A_Srcr2Decide (); - native void A_Srcr2Attack (); - native void A_Sor2DthInit (); - native void A_Sor2DthLoop (); States { @@ -189,6 +279,118 @@ class Sorcerer2 : Actor SDTH O -1 A_BossDeath; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC P_DSparilTeleport + // + //---------------------------------------------------------------------------- + + void DSparilTeleport () + { + SpotState state = SpotState.GetSpotState(); + if (state == null) return; + + Actor spot = state.GetSpotWithMinMaxDistance("BossSpot", pos.x, pos.y, 128, 0); + if (spot == null) return; + + Vector3 prev = Pos; + if (TeleportMove (spot.Pos, false)) + { + Actor mo = Spawn("Sorcerer2Telefade", prev, ALLOW_REPLACE); + if (mo) + { + mo.Translation = Translation; + mo.A_PlaySound("misc/teleport", CHAN_BODY); + } + SetStateLabel ("Teleport"); + A_PlaySound ("misc/teleport", CHAN_BODY); + SetZ(floorz); + angle = spot.angle; + vel = (0,0,0); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_Srcr2Decide + // + //---------------------------------------------------------------------------- + + void A_Srcr2Decide () + { + static const int chance[] = + { + 192, 120, 120, 120, 64, 64, 32, 16, 0 + }; + + int health8 = max(1, SpawnHealth() / 8); + int chanceindex = min(8, health / health8); + + if (random[Srcr2Decide]() < chance[chanceindex]) + { + DSparilTeleport (); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_Srcr2Attack + // + //---------------------------------------------------------------------------- + + void A_Srcr2Attack () + { + if (!target) + { + return; + } + A_PlaySound (AttackSound, CHAN_BODY, 1, false, ATTN_NONE); + if (CheckMeleeRange()) + { + int damage = random[Srcr2Atk](1, 8) * 20; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + int chance = health < SpawnHealth()/2 ? 96 : 48; + if (random[Srcr2Atk]() < chance) + { // Wizard spawners + + SpawnMissileAngle("Sorcerer2FX2", Angle - 45, 0.5); + SpawnMissileAngle("Sorcerer2FX2", Angle + 45, 0.5); + } + else + { // Blue bolt + SpawnMissile (target, "Sorcerer2FX1"); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_Sor2DthInit + // + //---------------------------------------------------------------------------- + + void A_Sor2DthInit () + { + special1 = 7; // Animation loop counter + Thing_Destroy(0); // Kill monsters early + } + + //---------------------------------------------------------------------------- + // + // PROC A_Sor2DthLoop + // + //---------------------------------------------------------------------------- + + void A_Sor2DthLoop () + { + if (--special1) + { // Need to loop + SetStateLabel("DeathLoop"); + } + } } @@ -210,8 +412,6 @@ class Sorcerer2FX1 : Actor RenderStyle "Add"; } - native void A_BlueSpark (); - States { Spawn: @@ -222,6 +422,23 @@ class Sorcerer2FX1 : Actor FX16 HIJKL 5 BRIGHT; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_BlueSpark + // + //---------------------------------------------------------------------------- + + void A_BlueSpark () + { + for (int i = 0; i < 2; i++) + { + Actor mo = Spawn("Sorcerer2FXSpark", pos, ALLOW_REPLACE); + mo.Vel.X = Random2[BlueSpark]() / 128.; + mo.Vel.Y = Random2[BlueSpark]() / 128.; + mo.Vel.Z = 1. + Random[BlueSpark]() / 256.; + } + } } // Sorcerer 2 FX Spark ------------------------------------------------------ @@ -244,7 +461,7 @@ class Sorcerer2FXSpark : Actor Spawn: FX16 DEF 12 BRIGHT; Stop; - } + } } // Sorcerer 2 FX 2 ---------------------------------------------------------- @@ -263,8 +480,6 @@ class Sorcerer2FX2 : Actor RenderStyle "Add"; } - native void A_GenWizard (); - States { Spawn: @@ -277,7 +492,38 @@ class Sorcerer2FX2 : Actor Stop; } + +//---------------------------------------------------------------------------- +// +// PROC A_GenWizard +// +//---------------------------------------------------------------------------- + + void A_GenWizard () + { + Actor mo = Spawn("Wizard", pos, ALLOW_REPLACE); + if (mo != null) + { + mo.AddZ(-mo.Default.Height / 2, false); + if (!mo.TestMobjLocation ()) + { // Didn't fit + mo.ClearCounters(); + mo.Destroy (); + } + else + { // [RH] Make the new wizards inherit D'Sparil's target + mo.CopyFriendliness (self.target, true); + + Vel = (0,0,0); + SetStateLabel('Death'); + bMissile = false; + mo.master = target; + SpawnTeleportFog(pos, false, true); + } + } + } } + // Sorcerer 2 Telefade ------------------------------------------------------ class Sorcerer2Telefade : Actor diff --git a/wadsrc/static/zscript/heretic/hereticartifacts.txt b/wadsrc/static/zscript/heretic/hereticartifacts.txt index e239748ff..3ef265735 100644 --- a/wadsrc/static/zscript/heretic/hereticartifacts.txt +++ b/wadsrc/static/zscript/heretic/hereticartifacts.txt @@ -47,7 +47,7 @@ Class ArtiInvisibility : PowerupGiver // Tome of power ------------------------------------------------------------ -Class ArtiTomeOfPower : PowerupGiver native +Class ArtiTomeOfPower : PowerupGiver { Default { @@ -65,6 +65,31 @@ Class ArtiTomeOfPower : PowerupGiver native PWBK A 350; Loop; } + + bool Use (bool pickup) + { + Playerinfo p = Owner.player; + if (p && p.morphTics && (p.MorphStyle & MRF_UNDOBYTOMEOFPOWER)) + { // Attempt to undo chicken + if (!p.UndoPlayerMorph (p, MRF_UNDOBYTOMEOFPOWER)) + { // Failed + if (!(p.MorphStyle & MRF_FAILNOTELEFRAG)) + { + Owner.DamageMobj (null, null, TELEFRAG_DAMAGE, 'Telefrag'); + } + } + else + { // Succeeded + Owner.A_PlaySound ("*evillaugh", CHAN_VOICE); + } + return true; + } + else + { + return Super.Use (pickup); + } + } + } @@ -80,8 +105,6 @@ Class ActivatedTimeBomb : Actor DeathSound "misc/timebomb"; } - native void A_Timebomb(); - States { Spawn: @@ -91,10 +114,17 @@ Class ActivatedTimeBomb : Actor XPL1 BCDEF 4 BRIGHT; Stop; } + + void A_TimeBomb() + { + AddZ(32, false); + A_SetRenderStyle(1., STYLE_Add); + A_Explode(); + } } -Class ArtiTimeBomb : Inventory native +Class ArtiTimeBomb : Inventory { Default { @@ -115,4 +145,12 @@ Class ArtiTimeBomb : Inventory native FBMB E 350; Loop; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("ActivatedTimeBomb", Owner.Vec3Angle(24., Owner.angle, - Owner.Floorclip), ALLOW_REPLACE); + mo.target = Owner; + return true; + } + } diff --git a/wadsrc/static/zscript/heretic/hereticmisc.txt b/wadsrc/static/zscript/heretic/hereticmisc.txt index f858a6fa2..ea79f5f80 100644 --- a/wadsrc/static/zscript/heretic/hereticmisc.txt +++ b/wadsrc/static/zscript/heretic/hereticmisc.txt @@ -1,4 +1,12 @@ +class HereticWeapon : Weapon +{ + Default + { + Weapon.Kickback 150; + } +} + // Pod ---------------------------------------------------------------------- class Pod : Actor @@ -16,9 +24,7 @@ class Pod : Actor DeathSound "world/podexplode"; PushFactor 0.5; } - native void A_PodPain (class podtype = "PodGoo"); - native void A_RemovePod (); - + States { Spawn: @@ -37,6 +43,44 @@ class Pod : Actor PPOD IJKLMNOP 3; Goto Spawn; } + + //---------------------------------------------------------------------------- + // + // PROC A_PodPain + // + //---------------------------------------------------------------------------- + + void A_PodPain (class gootype = "PodGoo") + { + int chance = Random[PodPain](); + if (chance < 128) + { + return; + } + for (int count = chance > 240 ? 2 : 1; count; count--) + { + Actor goo = Spawn(gootype, pos + (0, 0, 48), ALLOW_REPLACE); + goo.target = self; + goo.Vel.X = Random2[PodPain]() / 128.; + goo.Vel.Y = Random2[PodPain]() / 128.; + goo.Vel.Z = 0.5 + random[PodPain]() / 128.; + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_RemovePod + // + //---------------------------------------------------------------------------- + + void A_RemovePod () + { + if (master && master.special1 > 0) + { + master.special1--; + } + } + } @@ -75,7 +119,6 @@ class PodGenerator : Actor AttackSound "world/podgrow"; } - native void A_MakePod (class podtype = "Pod"); States { @@ -83,6 +126,34 @@ class PodGenerator : Actor TNT1 A 35 A_MakePod; Loop; } + + //---------------------------------------------------------------------------- + // + // PROC A_MakePod + // + //---------------------------------------------------------------------------- + + const MAX_GEN_PODS = 16; + + void A_MakePod (class podtype = "Pod") + { + if (special1 >= MAX_GEN_PODS) + { // Too many generated pods + return; + } + Actor mo = Spawn(podtype, (pos.xy, ONFLOORZ), ALLOW_REPLACE); + if (!mo) return; + if (!mo.CheckPosition (mo.Pos.xy)) + { // Didn't fit + mo.Destroy (); + return; + } + mo.SetStateLabel("Grow"); + mo.Thrust(4.5, random[MakePod]() * (360. / 256)); + A_PlaySound (AttackSound, CHAN_BODY); + special1++; // Increment generated pod count + mo.master = self; // Link the generator to the pod + } } @@ -136,7 +207,6 @@ class TeleGlitter1 : Actor Damage 0; } - native void A_AccTeleGlitter (); States { @@ -148,6 +218,20 @@ class TeleGlitter1 : Actor TGLT E 2 BRIGHT; Loop; } + + //---------------------------------------------------------------------------- + // + // PROC A_AccTeleGlitter + // + //---------------------------------------------------------------------------- + + void A_AccTeleGlitter () + { + if (++health > 35) + { + Vel.Z *= 1.5; + } + } } // Teleglitter 2 ------------------------------------------------------------ @@ -178,9 +262,6 @@ class Volcano : Actor +SOLID } - native void A_VolcanoSet (); - native void A_VolcanoBlast (); - States { Spawn: @@ -190,7 +271,38 @@ class Volcano : Actor VLCO E 10 A_VolcanoBlast; Goto Spawn+1; } + + //---------------------------------------------------------------------------- + // + // PROC A_VolcanoSet + // + //---------------------------------------------------------------------------- + void A_VolcanoSet () + { + tics = 105 + (random[VolcanoSet]() & 127); + } + + //---------------------------------------------------------------------------- + // + // PROC A_VolcanoBlast + // + //---------------------------------------------------------------------------- + + void A_VolcanoBlast () + { + int count = random[VolcanoBlast](1,3); + for (int i = 0; i < count; i++) + { + Actor blast = Spawn("VolcanoBlast", pos + (0, 0, 44), ALLOW_REPLACE); + blast.target = self; + blast.Angle = random[VolcanoBlast]() * (360 / 256.); + blast.VelFromAngle(1.); + blast.Vel.Z = 2.5 + random[VolcanoBlast]() / 64.; + blast.A_PlaySound ("world/volcano/shoot", CHAN_BODY); + blast.CheckMissileSpawn (radius); + } + } } // Volcano blast ------------------------------------------------------------ @@ -210,8 +322,6 @@ class VolcanoBlast : Actor DeathSound "world/volcano/blast"; } - native void A_VolcBallImpact (); - States { Spawn: @@ -224,6 +334,35 @@ class VolcanoBlast : Actor XPL1 BCDEF 4 BRIGHT; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_VolcBallImpact + // + //---------------------------------------------------------------------------- + + void A_VolcBallImpact () + { + if (pos.Z <= floorz) + { + bNoGravity = true; + Gravity = 1; + AddZ(28); + } + A_Explode(25, 25, XF_NOSPLASH|XF_HURTSOURCE, false, 0, 0, 0, "BulletPuff", 'Fire'); + for (int i = 0; i < 4; i++) + { + Actor tiny = Spawn("VolcanoTBlast", Pos, ALLOW_REPLACE); + if (tiny) + { + tiny.target = self; + tiny.Angle = 90.*i; + tiny.VelFromAngle(0.7); + tiny.Vel.Z = 1. + random[VolcBallImpact]() / 128.; + tiny.CheckMissileSpawn (radius); + } + } + } } // Volcano T Blast ---------------------------------------------------------- @@ -252,4 +391,3 @@ class VolcanoTBlast : Actor } } - diff --git a/wadsrc/static/zscript/heretic/hereticweaps.txt b/wadsrc/static/zscript/heretic/hereticweaps.txt deleted file mode 100644 index 4c254500b..000000000 --- a/wadsrc/static/zscript/heretic/hereticweaps.txt +++ /dev/null @@ -1,1286 +0,0 @@ - -class HereticWeapon : Weapon -{ - Default - { - Weapon.Kickback 150; - } -} - - -// Staff -------------------------------------------------------------------- - -class Staff : HereticWeapon -{ - Default - { - Weapon.SelectionOrder 3800; - +THRUGHOST - +WEAPON.WIMPY_WEAPON - +WEAPON.MELEEWEAPON - Weapon.sisterweapon "StaffPowered"; - Obituary "$OB_MPSTAFF"; - Tag "$TAG_STAFF"; - } - - action native void A_StaffAttack (int damage, class puff); - - States - { - Ready: - STFF A 1 A_WeaponReady; - Loop; - Deselect: - STFF A 1 A_Lower; - Loop; - Select: - STFF A 1 A_Raise; - Loop; - Fire: - STFF B 6; - STFF C 8 A_StaffAttack(random[StaffAttack](5, 20), "StaffPuff"); - STFF B 8 A_ReFire; - Goto Ready; - } -} - -class StaffPowered : Staff -{ - Default - { - Weapon.sisterweapon "Staff"; - Weapon.ReadySound "weapons/staffcrackle"; - +WEAPON.POWERED_UP - +WEAPON.READYSNDHALF - +WEAPON.STAFF2_KICKBACK - Obituary "$OB_MPPSTAFF"; - Tag "$TAG_STAFFP"; - } - - States - { - Ready: - STFF DEF 4 A_WeaponReady; - Loop; - Deselect: - STFF D 1 A_Lower; - Loop; - Select: - STFF D 1 A_Raise; - Loop; - Fire: - STFF G 6; - STFF H 8 A_StaffAttack(random[StaffAttack](18, 81), "StaffPuff2"); - STFF G 8 A_ReFire; - Goto Ready; - } -} - - -// Staff puff --------------------------------------------------------------- - -class StaffPuff : Actor -{ - Default - { - RenderStyle "Translucent"; - Alpha 0.4; - VSpeed 1; - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - AttackSound "weapons/staffhit"; - } - - States - { - Spawn: - PUF3 A 4 BRIGHT; - PUF3 BCD 4; - Stop; - } -} - -// Staff puff 2 ------------------------------------------------------------- - -class StaffPuff2 : Actor -{ - Default - { - RenderStyle "Add"; - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - AttackSound "weapons/staffpowerhit"; - } - - States - { - Spawn: - PUF4 ABCDEF 4 BRIGHT; - Stop; - } -} - - - -// Gold wand ---------------------------------------------------------------- - -class GoldWand : HereticWeapon -{ - Default - { - +BLOODSPLATTER - Weapon.SelectionOrder 2000; - Weapon.AmmoGive 25; - Weapon.AmmoUse 1; - Weapon.AmmoType "GoldWandAmmo"; - Weapon.SisterWeapon "GoldWandPowered"; - Weapon.YAdjust 5; - Inventory.PickupMessage "$TXT_WPNGOLDWAND"; - Obituary "$OB_MPGOLDWAND"; - Tag "$TAG_GOLDWAND"; - } - - action native void A_FireGoldWandPL1 (); - - States - { - Spawn: - GWAN A -1; - Stop; - Ready: - GWND A 1 A_WeaponReady; - Loop; - Deselect: - GWND A 1 A_Lower; - Loop; - Select: - GWND A 1 A_Raise; - Loop; - Fire: - GWND B 3; - GWND C 5 A_FireGoldWandPL1; - GWND D 3; - GWND D 0 A_ReFire; - Goto Ready; - } -} - -class GoldWandPowered : GoldWand -{ - Default - { - +WEAPON.POWERED_UP - Weapon.AmmoGive 0; - Weapon.SisterWeapon "GoldWand"; - Obituary "$OB_MPPGOLDWAND"; - Tag "$TAG_GOLDWANDP"; - } - - action native void A_FireGoldWandPL2 (); - - States - { - Fire: - GWND B 3; - GWND C 4 A_FireGoldWandPL2; - GWND D 3; - GWND D 0 A_ReFire; - Goto Ready; - } -} - - -// Gold wand FX1 ------------------------------------------------------------ - -class GoldWandFX1 : Actor -{ - Default - { - Radius 10; - Height 6; - Speed 22; - Damage 2; - Projectile; - RenderStyle "Add"; - DeathSound "weapons/wandhit"; - Obituary "$OB_MPPGOLDWAND"; - } - - States - { - Spawn: - FX01 AB 6 BRIGHT; - Loop; - Death: - FX01 EFGH 3 BRIGHT; - Stop; - } -} - -// Gold wand FX2 ------------------------------------------------------------ - -class GoldWandFX2 : GoldWandFX1 -{ - Default - { - Speed 18; - Damage 1; - DeathSound ""; - } - - States - { - Spawn: - FX01 CD 6 BRIGHT; - Loop; - } -} - -// Gold wand puff 1 --------------------------------------------------------- - -class GoldWandPuff1 : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - RenderStyle "Add"; - } - - States - { - Spawn: - PUF2 ABCDE 3 BRIGHT; - Stop; - } -} - -// Gold wand puff 2 --------------------------------------------------------- - -class GoldWandPuff2 : GoldWandFX1 -{ - Default - { - Skip_Super; - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - } - - States - { - Spawn: - Goto Super::Death; - } -} - - -// Crossbow ----------------------------------------------------------------- - -class Crossbow : HereticWeapon -{ - Default - { - Weapon.SelectionOrder 800; - Weapon.AmmoUse 1; - Weapon.AmmoGive 10; - Weapon.AmmoType "CrossbowAmmo"; - Weapon.SisterWeapon "CrossbowPowered"; - Weapon.YAdjust 15; - Inventory.PickupMessage "$TXT_WPNCROSSBOW"; - Tag "$TAG_CROSSBOW"; - } - - action native void A_FireCrossbowPL1 (); - - States - { - Spawn: - WBOW A -1; - Stop; - Ready: - CRBW AAAAAABBBBBBCCCCCC 1 A_WeaponReady; - Loop; - Deselect: - CRBW A 1 A_Lower; - Loop; - Select: - CRBW A 1 A_Raise; - Loop; - Fire: - CRBW D 6 A_FireCrossbowPL1; - CRBW EFGH 3; - CRBW AB 4; - CRBW C 5 A_ReFire; - Goto Ready; - } -} - - -class CrossbowPowered : Crossbow -{ - Default - { - +WEAPON.POWERED_UP - Weapon.AmmoGive 0; - Weapon.SisterWeapon "Crossbow"; - Tag "$TAG_CROSSBOWP"; - } - - action native void A_FireCrossbowPL2(); - - States - { - Fire: - CRBW D 5 A_FireCrossbowPL2; - CRBW E 3; - CRBW F 2; - CRBW G 3; - CRBW H 2; - CRBW A 3; - CRBW B 3; - CRBW C 4 A_ReFire; - Goto Ready; - } -} - - -// Crossbow FX1 ------------------------------------------------------------- - -class CrossbowFX1 : Actor -{ - Default - { - Radius 11; - Height 8; - Speed 30; - Damage 10; - Projectile; - RenderStyle "Add"; - SeeSound "weapons/bowshoot"; - DeathSound "weapons/bowhit"; - Obituary "$OB_MPCROSSBOW"; - } - - States - { - Spawn: - FX03 B 1 BRIGHT; - Loop; - Death: - FX03 HIJ 8 BRIGHT; - Stop; - } -} - - -// Crossbow FX2 ------------------------------------------------------------- - -class CrossbowFX2 : CrossbowFX1 -{ - Default - { - Speed 32; - Damage 6; - Obituary "$OB_MPPCROSSBOW"; - } - - States - { - Spawn: - FX03 B 1 BRIGHT A_SpawnItemEx("CrossbowFX4", random2[BoltSpark]()*0.015625, random2[BoltSpark]()*0.015625, 0, 0,0,0,0,SXF_ABSOLUTEPOSITION, 50); - Loop; - } -} - -// Crossbow FX3 ------------------------------------------------------------- - -class CrossbowFX3 : CrossbowFX1 -{ - Default - { - Speed 20; - Damage 2; - SeeSound ""; - -NOBLOCKMAP - +WINDTHRUST - +THRUGHOST - } - - States - { - Spawn: - FX03 A 1 BRIGHT; - Loop; - Death: - FX03 CDE 8 BRIGHT; - Stop; - } -} - -// Crossbow FX4 ------------------------------------------------------------- - -class CrossbowFX4 : Actor -{ - Default - { - +NOBLOCKMAP - Gravity 0.125; - RenderStyle "Add"; - } - - States - { - Spawn: - FX03 FG 8 BRIGHT; - Stop; - } -} - - - - -// Gauntlets ---------------------------------------------------------------- - -class Gauntlets : Weapon -{ - Default - { - +BLOODSPLATTER - Weapon.SelectionOrder 2300; - +WEAPON.WIMPY_WEAPON - +WEAPON.MELEEWEAPON - Weapon.Kickback 0; - Weapon.YAdjust 15; - Weapon.UpSound "weapons/gauntletsactivate"; - Weapon.SisterWeapon "GauntletsPowered"; - Inventory.PickupMessage "$TXT_WPNGAUNTLETS"; - Tag "$TAG_GAUNTLETS"; - Obituary "$OB_MPGAUNTLETS"; - } - - action native void A_GauntletAttack (int power); - - States - { - Spawn: - WGNT A -1; - Stop; - Ready: - GAUN A 1 A_WeaponReady; - Loop; - Deselect: - GAUN A 1 A_Lower; - Loop; - Select: - GAUN A 1 A_Raise; - Loop; - Fire: - GAUN B 4 A_PlaySound("weapons/gauntletsuse", CHAN_WEAPON); - GAUN C 4; - Hold: - GAUN DEF 4 BRIGHT A_GauntletAttack(0); - GAUN C 4 A_ReFire; - GAUN B 4 A_Light0; - Goto Ready; - } -} - - -class GauntletsPowered : Gauntlets -{ - Default - { - +WEAPON.POWERED_UP - Tag "$TAG_GAUNTLETSP"; - Obituary "$OB_MPPGAUNTLETS"; - Weapon.SisterWeapon "Gauntlets"; - } - - States - { - Ready: - GAUN GHI 4 A_WeaponReady; - Loop; - Deselect: - GAUN G 1 A_Lower; - Loop; - Select: - GAUN G 1 A_Raise; - Loop; - Fire: - GAUN J 4 A_PlaySound("weapons/gauntletsuse", CHAN_WEAPON); - GAUN K 4; - Hold: - GAUN LMN 4 BRIGHT A_GauntletAttack(1); - GAUN K 4 A_ReFire; - GAUN J 4 A_Light0; - Goto Ready; - } -} - - -// Gauntlet puff 1 ---------------------------------------------------------- - -class GauntletPuff1 : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - RenderStyle "Translucent"; - Alpha 0.4; - VSpeed 0.8; - } - - States - { - Spawn: - PUF1 ABCD 4 BRIGHT; - Stop; - } -} - -// Gauntlet puff 2 --------------------------------------------------------- - -class GauntletPuff2 : GauntletPuff1 -{ - States - { - Spawn: - PUF1 EFGH 4 BRIGHT; - Stop; - } -} - - -// The mace itself ---------------------------------------------------------- - -class Mace : HereticWeapon -{ - Default - { - Weapon.SelectionOrder 1400; - Weapon.AmmoUse 1; - Weapon.AmmoGive1 50; - Weapon.YAdjust 15; - Weapon.AmmoType "MaceAmmo"; - Weapon.SisterWeapon "MacePowered"; - Inventory.PickupMessage "$TXT_WPNMACE"; - Tag "$TAG_MACE"; - } - - action native void A_FireMacePL1(); - - States - { - Spawn: - WMCE A -1; - Stop; - Ready: - MACE A 1 A_WeaponReady; - Loop; - Deselect: - MACE A 1 A_Lower; - Loop; - Select: - MACE A 1 A_Raise; - Loop; - Fire: - MACE B 4; - Hold: - MACE CDEF 3 A_FireMacePL1; - MACE C 4 A_ReFire; - MACE DEFB 4; - Goto Ready; - } -} - -class MacePowered : Mace -{ - Default - { - +WEAPON.POWERED_UP - Weapon.AmmoUse 5; - Weapon.AmmoGive 0; - Weapon.SisterWeapon "Mace"; - Tag "$TAG_MACEP"; - } - - action native void A_FireMacePL2(); - - States - { - Fire: - Hold: - MACE B 4; - MACE D 4 A_FireMacePL2; - MACE B 4; - MACE A 8 A_ReFire; - Goto Ready; - } -} - -// Mace FX1 ----------------------------------------------------------------- - -class MaceFX1 : Actor -{ - Default - { - Radius 8; - Height 6; - Speed 20; - Damage 2; - Projectile; - +THRUGHOST - BounceType "HereticCompat"; - SeeSound "weapons/maceshoot"; - Obituary "$OB_MPMACE"; - } - - native void A_MacePL1Check(); - native void A_MaceBallImpact(); - - States - { - Spawn: - FX02 AB 4 A_MacePL1Check; - Loop; - Death: - FX02 F 4 BRIGHT A_MaceBallImpact; - FX02 GHIJ 4 BRIGHT; - Stop; - } -} - -// Mace FX2 ----------------------------------------------------------------- - -class MaceFX2 : MaceFX1 -{ - Default - { - Speed 10; - Damage 6; - Gravity 0.125; - -NOGRAVITY - SeeSound ""; - } - - native void A_MaceBallImpact2(); - - States - { - Spawn: - FX02 CD 4; - Loop; - Death: - FX02 F 4 A_MaceBallImpact2; - goto Super::Death+1; - } -} - -// Mace FX3 ----------------------------------------------------------------- - -class MaceFX3 : MaceFX1 -{ - Default - { - Speed 7; - Damage 4; - -NOGRAVITY; - Gravity 0.125; - } - - States - { - Spawn: - FX02 AB 4; - Loop; - } -} - - -// Mace FX4 ----------------------------------------------------------------- - -class MaceFX4 : Actor native -{ - Default - { - Radius 8; - Height 6; - Speed 7; - Damage 18; - Gravity 0.125; - Projectile; - -NOGRAVITY - +TELESTOMP - +THRUGHOST - -NOTELEPORT - BounceType "HereticCompat"; - SeeSound ""; - Obituary "$OB_MPPMACE"; - } - - native void A_DeathBallImpact(); - - States - { - Spawn: - FX02 E 99; - Loop; - Death: - FX02 C 4 A_DeathBallImpact; - FX02 GHIJ 4 BRIGHT; - Stop; - } -} - - -// Mace spawn spot ---------------------------------------------------------- - -class MaceSpawner : SpecialSpot -{ - Default - { - +NOSECTOR - +NOBLOCKMAP - } - - States - { - Spawn: - TNT1 A 1; - TNT1 A -1 A_SpawnSingleItem("Mace", 64, 64, 0); - Stop; - } -} - - -// Blaster ------------------------------------------------------------------ - -class Blaster : HereticWeapon -{ - Default - { - +BLOODSPLATTER - Weapon.SelectionOrder 500; - Weapon.AmmoUse 1; - Weapon.AmmoGive 30; - Weapon.YAdjust 15; - Weapon.AmmoType "BlasterAmmo"; - Weapon.SisterWeapon "BlasterPowered"; - Inventory.PickupMessage "$TXT_WPNBLASTER"; - Tag "$TAG_BLASTER"; - Obituary "$OB_MPBLASTER"; - } - - action native void A_FireBlasterPL1(); - - States - { - Spawn: - WBLS A -1; - Stop; - Ready: - BLSR A 1 A_WeaponReady; - Loop; - Deselect: - BLSR A 1 A_Lower; - Loop; - Select: - BLSR A 1 A_Raise; - Loop; - Fire: - BLSR BC 3; - Hold: - BLSR D 2 A_FireBlasterPL1; - BLSR CB 2; - BLSR A 0 A_ReFire; - Goto Ready; - } -} - -class BlasterPowered : Blaster -{ - Default - { - +WEAPON.POWERED_UP - Weapon.AmmoUse 5; - Weapon.AmmoGive 0; - Weapon.SisterWeapon "Blaster"; - Tag "$TAG_BLASTERP"; - } - - States - { - Fire: - BLSR BC 0; - Hold: - BLSR D 3 A_FireCustomMissile("BlasterFX1"); - BLSR CB 4; - BLSR A 0 A_ReFire; - Goto Ready; - } -} - -// Blaster FX 1 ------------------------------------------------------------- - -class BlasterFX1 : FastProjectile native -{ - Default - { - Radius 12; - Height 8; - Speed 184; - Damage 2; - SeeSound "weapons/blastershoot"; - DeathSound "weapons/blasterhit"; - +SPAWNSOUNDSOURCE - Obituary "$OB_MPPBLASTER"; - } - - native void A_SpawnRippers(); - - States - { - Spawn: - ACLO E 200; - Loop; - Death: - FX18 A 3 BRIGHT A_SpawnRippers; - FX18 B 3 BRIGHT; - FX18 CDEFG 4 BRIGHT; - Stop; - } -} - -// Blaster smoke ------------------------------------------------------------ - -class BlasterSmoke : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +NOTELEPORT - +CANNOTPUSH - RenderStyle "Translucent"; - Alpha 0.4; - } - - States - { - Spawn: - FX18 HIJKL 4; - Stop; - } -} - -// Ripper ------------------------------------------------------------------- - -class Ripper : Actor native -{ - Default - { - Radius 8; - Height 6; - Speed 14; - Damage 1; - Projectile; - +RIPPER - DeathSound "weapons/blasterpowhit"; - Obituary "$OB_MPPBLASTER"; - } - - States - { - Spawn: - FX18 M 4; - FX18 N 5; - Loop; - Death: - FX18 OPQRS 4 BRIGHT; - Stop; - } -} - -// Blaster Puff ------------------------------------------------------------- - -class BlasterPuff : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - RenderStyle "Add"; - SeeSound "weapons/blasterhit"; - } - - States - { - Crash: - FX17 ABCDE 4 BRIGHT; - Stop; - Spawn: - FX17 FG 3 BRIGHT; - FX17 HIJKL 4 BRIGHT; - Stop; - } -} - - -// Skull (Horn) Rod --------------------------------------------------------- - -class SkullRod : HereticWeapon -{ - Default - { - Weapon.SelectionOrder 200; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 50; - Weapon.YAdjust 15; - Weapon.AmmoType1 "SkullRodAmmo"; - Weapon.SisterWeapon "SkullRodPowered"; - Inventory.PickupMessage "$TXT_WPNSKULLROD"; - Tag "$TAG_SKULLROD"; - } - - action native void A_FireSkullRodPL1(); - - States - { - Spawn: - WSKL A -1; - Stop; - Ready: - HROD A 1 A_WeaponReady; - Loop; - Deselect: - HROD A 1 A_Lower; - Loop; - Select: - HROD A 1 A_Raise; - Loop; - Fire: - HROD AB 4 A_FireSkullRodPL1; - HROD B 0 A_ReFire; - Goto Ready; - } -} - -class SkullRodPowered : SkullRod -{ - Default - { - +WEAPON.POWERED_UP - Weapon.AmmoUse1 5; - Weapon.AmmoGive1 0; - Weapon.SisterWeapon "SkullRod"; - Tag "$TAG_SKULLRODP"; - } - - action native void A_FireSkullRodPL2(); - - States - { - Fire: - HROD C 2; - HROD D 3; - HROD E 2; - HROD F 3; - HROD G 4 A_FireSkullRodPL2; - HROD F 2; - HROD E 3; - HROD D 2; - HROD C 2 A_ReFire; - Goto Ready; - } -} - -// Horn Rod FX 1 ------------------------------------------------------------ - -class HornRodFX1 : Actor -{ - Default - { - Radius 12; - Height 8; - Speed 22; - Damage 3; - Projectile; - +WINDTHRUST - -NOBLOCKMAP - RenderStyle "Add"; - SeeSound "weapons/hornrodshoot"; - DeathSound "weapons/hornrodhit"; - Obituary "$OB_MPSKULLROD"; - } - - States - { - Spawn: - FX00 AB 6 BRIGHT; - Loop; - Death: - FX00 HI 5 BRIGHT; - FX00 JK 4 BRIGHT; - FX00 LM 3 BRIGHT; - Stop; - } -} - - -// Horn Rod FX 2 ------------------------------------------------------------ - -class HornRodFX2 : Actor native -{ - Default - { - Radius 12; - Height 8; - Speed 22; - Damage 10; - Health 140; - Projectile; - RenderStyle "Add"; - SeeSound "weapons/hornrodpowshoot"; - DeathSound "weapons/hornrodpowhit"; - Obituary "$OB_MPPSKULLROD"; - } - - native void A_AddPlayerRain(); - native void A_HideInCeiling(); - native void A_SkullRodStorm(); - - States - { - Spawn: - FX00 C 3 BRIGHT; - FX00 D 3 BRIGHT A_SeekerMissile(10, 30); - FX00 E 3 BRIGHT; - FX00 F 3 BRIGHT A_SeekerMissile(10, 30); - Loop; - Death: - FX00 H 5 BRIGHT A_AddPlayerRain; - FX00 I 5 BRIGHT; - FX00 J 4 BRIGHT; - FX00 KLM 3 BRIGHT; - FX00 G 1 A_HideInCeiling; - FX00 G 1 A_SkullRodStorm; - Wait; - } -} - -// Rain pillar 1 ------------------------------------------------------------ - -class RainPillar : Actor native -{ - Default - { - Radius 5; - Height 12; - Speed 12; - Damage 5; - Mass 5; - Projectile; - -ACTIVATEPCROSS - -ACTIVATEIMPACT - RenderStyle "Add"; - Obituary "$OB_MPPSKULLROD"; - } - - native void A_RainImpact(); - - States - { - Spawn: - FX22 A -1 BRIGHT; - Stop; - Death: - FX22 B 4 BRIGHT A_RainImpact; - FX22 CDEF 4 BRIGHT; - Stop; - NotFloor: - FX22 GHI 4 BRIGHT; - Stop; - } -} - -// Rain tracker "inventory" item -------------------------------------------- - -class RainTracker : Inventory native -{ - Default - { - +INVENTORY.UNDROPPABLE - } -} - - -// Phoenix Rod -------------------------------------------------------------- - -class PhoenixRod : Weapon native -{ - Default - { - +WEAPON.NOAUTOFIRE - Weapon.SelectionOrder 2600; - Weapon.Kickback 150; - Weapon.YAdjust 15; - Weapon.AmmoUse 1; - Weapon.AmmoGive 2; - Weapon.AmmoType "PhoenixRodAmmo"; - Weapon.Sisterweapon "PhoenixRodPowered"; - Inventory.PickupMessage "$TXT_WPNPHOENIXROD"; - Tag "$TAG_PHOENIXROD"; - } - - action native void A_FirePhoenixPL1(); - - States - { - Spawn: - WPHX A -1; - Stop; - Ready: - PHNX A 1 A_WeaponReady; - Loop; - Deselect: - PHNX A 1 A_Lower; - Loop; - Select: - PHNX A 1 A_Raise; - Loop; - Fire: - PHNX B 5; - PHNX C 7 A_FirePhoenixPL1; - PHNX DB 4; - PHNX B 0 A_ReFire; - Goto Ready; - } -} - -class PhoenixRodPowered : PhoenixRod native -{ - Default - { - +WEAPON.POWERED_UP - +WEAPON.MELEEWEAPON - Weapon.SisterWeapon "PhoenixRod"; - Weapon.AmmoGive 0; - Tag "$TAG_PHOENIXRODP"; - } - - action native void A_InitPhoenixPL2(); - action native void A_FirePhoenixPL2(); - action native void A_ShutdownPhoenixPL2(); - - States - { - Fire: - PHNX B 3 A_InitPhoenixPL2; - Hold: - PHNX C 1 A_FirePhoenixPL2; - PHNX B 4 A_ReFire; - Powerdown: - PHNX B 4 A_ShutdownPhoenixPL2; - Goto Ready; - } -} - -// Phoenix FX 1 ------------------------------------------------------------- - -class PhoenixFX1 : Actor native -{ - Default - { - Radius 11; - Height 8; - Speed 20; - Damage 20; - DamageType "Fire"; - Projectile; - +THRUGHOST - +SPECIALFIREDAMAGE - SeeSound "weapons/phoenixshoot"; - DeathSound "weapons/phoenixhit"; - Obituary "$OB_MPPHOENIXROD"; - } - - native void A_PhoenixPuff(); - - States - { - Spawn: - FX04 A 4 BRIGHT A_PhoenixPuff; - Loop; - Death: - FX08 A 6 BRIGHT A_Explode; - FX08 BC 5 BRIGHT; - FX08 DEFGH 4 BRIGHT; - Stop; - } -} - -// Phoenix puff ------------------------------------------------------------- - -class PhoenixPuff : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +NOTELEPORT - +CANNOTPUSH - RenderStyle "Translucent"; - Alpha 0.4; - } - - States - { - Spawn: - FX04 BCDEF 4; - Stop; - } -} - -// Phoenix FX 2 ------------------------------------------------------------- - -class PhoenixFX2 : Actor native -{ - Default - { - Radius 6; - Height 8; - Speed 10; - Damage 2; - DamageType "Fire"; - Projectile; - RenderStyle "Add"; - Obituary "$OB_MPPPHOENIXROD"; - } - - native void A_FlameEnd(); - native void A_FloatPuff(); - - States - { - Spawn: - FX09 ABABA 2 BRIGHT; - FX09 B 2 BRIGHT A_FlameEnd; - FX09 CDEF 2 BRIGHT; - Stop; - Death: - FX09 G 3 BRIGHT; - FX09 H 3 BRIGHT A_FloatPuff; - FX09 I 4 BRIGHT; - FX09 JK 5 BRIGHT; - Stop; - } -} - diff --git a/wadsrc/static/zscript/heretic/ironlich.txt b/wadsrc/static/zscript/heretic/ironlich.txt index e6ba05e05..8a4dd4c05 100644 --- a/wadsrc/static/zscript/heretic/ironlich.txt +++ b/wadsrc/static/zscript/heretic/ironlich.txt @@ -27,7 +27,6 @@ class Ironlich : Actor DropItem "ArtiEgg", 51, 0; } - native void A_LichAttack (); States { @@ -54,6 +53,77 @@ class Ironlich : Actor LICH I -1 A_BossDeath; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_LichAttack + // + //---------------------------------------------------------------------------- + + void A_LichAttack () + { + 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 + + if (target == null) + { + return; + } + A_FaceTarget (); + if (CheckMeleeRange ()) + { + int damage = random[LichAttack](1, 8) * 6; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + int dist = Distance2D(target) > 8 * 64; + int randAttack = random[LichAttack](); + if (randAttack < atkResolve1[dist]) + { // Ice ball + SpawnMissile (target, "HeadFX1"); + A_PlaySound ("ironlich/attack2", CHAN_BODY); + } + else if (randAttack < atkResolve2[dist]) + { // Fire column + Actor baseFire = SpawnMissile (target, "HeadFX3"); + if (baseFire != null) + { + baseFire.SetStateLabel("NoGrow"); + for (int i = 0; i < 5; i++) + { + Actor fire = Spawn("HeadFX3", baseFire.Pos, ALLOW_REPLACE); + if (i == 0) + { + A_PlaySound ("ironlich/attack1", CHAN_BODY); + } + fire.target = baseFire.target; + fire.angle = baseFire.angle; + fire.Vel = baseFire.Vel; + fire.SetDamage(0); + fire.health = (i+1) * 2; + fire.CheckMissileSpawn (radius); + } + } + } + else + { // Whirlwind + Actor mo = SpawnMissile (target, "Whirlwind"); + if (mo != null) + { + mo.AddZ(-32); + mo.tracer = target; + mo.health = 20*TICRATE; // Duration + A_PlaySound ("ironlich/attack3", CHAN_BODY); + } + } + } + } // Head FX 1 ---------------------------------------------------------------- @@ -74,7 +144,6 @@ class HeadFX1 : Actor RenderStyle "Add"; } - native void A_LichIceImpact(); States { @@ -86,6 +155,25 @@ class HeadFX1 : Actor FX05 EFG 5 BRIGHT; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_LichIceImpact + // + //---------------------------------------------------------------------------- + + void A_LichIceImpact() + { + for (int i = 0; i < 8; i++) + { + Actor shard = Spawn("HeadFX2", Pos, ALLOW_REPLACE); + shard.target = target; + shard.angle = i*45.; + shard.VelFromAngle(); + shard.Vel.Z = -.6; + shard.CheckMissileSpawn (radius); + } + } } // Head FX 2 ---------------------------------------------------------------- @@ -135,8 +223,6 @@ class HeadFX3 : Actor RenderStyle "Add"; } - native void A_LichFireGrow (); - States { Spawn: @@ -149,12 +235,29 @@ class HeadFX3 : Actor FX06 DEFG 5 BRIGHT; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_LichFireGrow + // + //---------------------------------------------------------------------------- + + void A_LichFireGrow () + { + health--; + AddZ(9.); + if (health == 0) + { + RestoreDamage(); + SetStateLabel("NoGrow"); + } + } } // Whirlwind ---------------------------------------------------------------- -class Whirlwind : Actor native +class Whirlwind : Actor { Default { @@ -174,8 +277,6 @@ class Whirlwind : Actor native Alpha 0.4; } - native void A_WhirlwindSeek(); - States { Spawn: @@ -186,6 +287,64 @@ class Whirlwind : Actor native FX07 GFED 4; Stop; } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + int randVal; + + if (!target.bDontThrust) + { + target.angle += Random2[WhirlwindDamage]() * (360 / 4096.); + target.Vel.X += Random2[WhirlwindDamage]() / 64.; + target.Vel.Y += Random2[WhirlwindDamage]() / 64.; + } + + if ((level.time & 16) && !target.bBoss && !target.bDontThrust) + { + randVal = min(160, random[WhirlwindSeek]()); + target.Vel.Z += randVal / 32.; + if (target.Vel.Z > 12) + { + target.Vel.Z = 12; + } + } + if (!(level.time & 7)) + { + target.DamageMobj (null, target, 3, 'Melee'); + } + return -1; + } + + //---------------------------------------------------------------------------- + // + // PROC A_WhirlwindSeek + // + //---------------------------------------------------------------------------- + + void A_WhirlwindSeek() + { + + health -= 3; + if (health < 0) + { + Vel = (0,0,0); + SetStateLabel("Death"); + bMissile = false; + return; + } + if ((threshold -= 3) < 0) + { + threshold = 58 + (random[WhirlwindSeek]() & 31); + A_PlaySound("ironlich/attack3", CHAN_BODY); + } + if (tracer && tracer.bShadow) + { + return; + } + A_SeekerMissile(10, 30); + } + } + diff --git a/wadsrc/static/zscript/heretic/weaponblaster.txt b/wadsrc/static/zscript/heretic/weaponblaster.txt new file mode 100644 index 000000000..8f4756b5b --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponblaster.txt @@ -0,0 +1,259 @@ +// Blaster ------------------------------------------------------------------ + +class Blaster : HereticWeapon +{ + Default + { + +BLOODSPLATTER + Weapon.SelectionOrder 500; + Weapon.AmmoUse 1; + Weapon.AmmoGive 30; + Weapon.YAdjust 15; + Weapon.AmmoType "BlasterAmmo"; + Weapon.SisterWeapon "BlasterPowered"; + Inventory.PickupMessage "$TXT_WPNBLASTER"; + Tag "$TAG_BLASTER"; + Obituary "$OB_MPBLASTER"; + } + + States + { + Spawn: + WBLS A -1; + Stop; + Ready: + BLSR A 1 A_WeaponReady; + Loop; + Deselect: + BLSR A 1 A_Lower; + Loop; + Select: + BLSR A 1 A_Raise; + Loop; + Fire: + BLSR BC 3; + Hold: + BLSR D 2 A_FireBlasterPL1; + BLSR CB 2; + BLSR A 0 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireBlasterPL1 + // + //---------------------------------------------------------------------------- + + action void A_FireBlasterPL1() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + + double pitch = BulletSlope(); + int damage = random[FireBlaster](1, 8) * 4; + double ang = angle; + if (player.refire) + { + ang += Random2[FireBlaster]() * (5.625 / 256); + } + LineAttack (ang, PLAYERMISSILERANGE, pitch, damage, 'Hitscan', "BlasterPuff"); + A_PlaySound ("weapons/blastershoot", CHAN_WEAPON); + } +} + +class BlasterPowered : Blaster +{ + Default + { + +WEAPON.POWERED_UP + Weapon.AmmoUse 5; + Weapon.AmmoGive 0; + Weapon.SisterWeapon "Blaster"; + Tag "$TAG_BLASTERP"; + } + + States + { + Fire: + BLSR BC 0; + Hold: + BLSR D 3 A_FireCustomMissile("BlasterFX1"); + BLSR CB 4; + BLSR A 0 A_ReFire; + Goto Ready; + } +} + +// Blaster FX 1 ------------------------------------------------------------- + +class BlasterFX1 : FastProjectile +{ + Default + { + Radius 12; + Height 8; + Speed 184; + Damage 2; + SeeSound "weapons/blastershoot"; + DeathSound "weapons/blasterhit"; + +SPAWNSOUNDSOURCE + Obituary "$OB_MPPBLASTER"; + } + + States + { + Spawn: + ACLO E 200; + Loop; + Death: + FX18 A 3 BRIGHT A_SpawnRippers; + FX18 B 3 BRIGHT; + FX18 CDEFG 4 BRIGHT; + Stop; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target is "Ironlich") + { // Less damage to Ironlich bosses + damage = random[BlasterFX]() & 1; + if (!damage) + { + return -1; + } + } + return damage; + } + + override void Effect () + { + if (random[BlasterFX]() < 64) + { + Spawn("BlasterSmoke", (pos.xy, max(pos.z - 8, floorz)), ALLOW_REPLACE); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_SpawnRippers + // + //---------------------------------------------------------------------------- + + void A_SpawnRippers() + { + for(int i = 0; i < 8; i++) + { + Actor ripper = Spawn("Ripper", pos, ALLOW_REPLACE); + ripper.target = target; + ripper.angle = i*45; + ripper.VelFromAngle(); + ripper.CheckMissileSpawn (radius); + } + } +} + +// Blaster smoke ------------------------------------------------------------ + +class BlasterSmoke : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +NOTELEPORT + +CANNOTPUSH + RenderStyle "Translucent"; + Alpha 0.4; + } + + States + { + Spawn: + FX18 HIJKL 4; + Stop; + } +} + +// Ripper ------------------------------------------------------------------- + +class Ripper : Actor +{ + Default + { + Radius 8; + Height 6; + Speed 14; + Damage 1; + Projectile; + +RIPPER + DeathSound "weapons/blasterpowhit"; + Obituary "$OB_MPPBLASTER"; + } + + States + { + Spawn: + FX18 M 4; + FX18 N 5; + Loop; + Death: + FX18 OPQRS 4 BRIGHT; + Stop; + } + + int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target is "Ironlich") + { // Less damage to Ironlich bosses + damage = random[Ripper]() & 1; + if (!damage) + { + return -1; + } + } + return damage; + } + +} + +// Blaster Puff ------------------------------------------------------------- + +class BlasterPuff : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + RenderStyle "Add"; + SeeSound "weapons/blasterhit"; + } + + States + { + Crash: + FX17 ABCDE 4 BRIGHT; + Stop; + Spawn: + FX17 FG 3 BRIGHT; + FX17 HIJKL 4 BRIGHT; + Stop; + } +} + diff --git a/wadsrc/static/zscript/heretic/weaponcrossbow.txt b/wadsrc/static/zscript/heretic/weaponcrossbow.txt new file mode 100644 index 000000000..bd5e9517a --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponcrossbow.txt @@ -0,0 +1,208 @@ +// Crossbow ----------------------------------------------------------------- + +class Crossbow : HereticWeapon +{ + Default + { + Weapon.SelectionOrder 800; + Weapon.AmmoUse 1; + Weapon.AmmoGive 10; + Weapon.AmmoType "CrossbowAmmo"; + Weapon.SisterWeapon "CrossbowPowered"; + Weapon.YAdjust 15; + Inventory.PickupMessage "$TXT_WPNCROSSBOW"; + Tag "$TAG_CROSSBOW"; + } + + States + { + Spawn: + WBOW A -1; + Stop; + Ready: + CRBW AAAAAABBBBBBCCCCCC 1 A_WeaponReady; + Loop; + Deselect: + CRBW A 1 A_Lower; + Loop; + Select: + CRBW A 1 A_Raise; + Loop; + Fire: + CRBW D 6 A_FireCrossbowPL1; + CRBW EFGH 3; + CRBW AB 4; + CRBW C 5 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireCrossbowPL1 + // + //---------------------------------------------------------------------------- + + action void A_FireCrossbowPL1 () + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + SpawnPlayerMissile ("CrossbowFX1"); + SpawnPlayerMissile ("CrossbowFX3", angle - 4.5); + SpawnPlayerMissile ("CrossbowFX3", angle + 4.5); + } +} + + +class CrossbowPowered : Crossbow +{ + Default + { + +WEAPON.POWERED_UP + Weapon.AmmoGive 0; + Weapon.SisterWeapon "Crossbow"; + Tag "$TAG_CROSSBOWP"; + } + + States + { + Fire: + CRBW D 5 A_FireCrossbowPL2; + CRBW E 3; + CRBW F 2; + CRBW G 3; + CRBW H 2; + CRBW A 3; + CRBW B 3; + CRBW C 4 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireCrossbowPL2 + // + //---------------------------------------------------------------------------- + + action void A_FireCrossbowPL2() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + SpawnPlayerMissile ("CrossbowFX2"); + SpawnPlayerMissile ("CrossbowFX2", angle - 4.5); + SpawnPlayerMissile ("CrossbowFX2", angle + 4.5); + SpawnPlayerMissile ("CrossbowFX3", angle - 9.); + SpawnPlayerMissile ("CrossbowFX3", angle + 9.); + } +} + + +// Crossbow FX1 ------------------------------------------------------------- + +class CrossbowFX1 : Actor +{ + Default + { + Radius 11; + Height 8; + Speed 30; + Damage 10; + Projectile; + RenderStyle "Add"; + SeeSound "weapons/bowshoot"; + DeathSound "weapons/bowhit"; + Obituary "$OB_MPCROSSBOW"; + } + + States + { + Spawn: + FX03 B 1 BRIGHT; + Loop; + Death: + FX03 HIJ 8 BRIGHT; + Stop; + } +} + + +// Crossbow FX2 ------------------------------------------------------------- + +class CrossbowFX2 : CrossbowFX1 +{ + Default + { + Speed 32; + Damage 6; + Obituary "$OB_MPPCROSSBOW"; + } + + States + { + Spawn: + FX03 B 1 BRIGHT A_SpawnItemEx("CrossbowFX4", random2[BoltSpark]()*0.015625, random2[BoltSpark]()*0.015625, 0, 0,0,0,0,SXF_ABSOLUTEPOSITION, 50); + Loop; + } +} + +// Crossbow FX3 ------------------------------------------------------------- + +class CrossbowFX3 : CrossbowFX1 +{ + Default + { + Speed 20; + Damage 2; + SeeSound ""; + -NOBLOCKMAP + +WINDTHRUST + +THRUGHOST + } + + States + { + Spawn: + FX03 A 1 BRIGHT; + Loop; + Death: + FX03 CDE 8 BRIGHT; + Stop; + } +} + +// Crossbow FX4 ------------------------------------------------------------- + +class CrossbowFX4 : Actor +{ + Default + { + +NOBLOCKMAP + Gravity 0.125; + RenderStyle "Add"; + } + + States + { + Spawn: + FX03 FG 8 BRIGHT; + Stop; + } +} + diff --git a/wadsrc/static/zscript/heretic/weapongauntlets.txt b/wadsrc/static/zscript/heretic/weapongauntlets.txt new file mode 100644 index 000000000..d06efe465 --- /dev/null +++ b/wadsrc/static/zscript/heretic/weapongauntlets.txt @@ -0,0 +1,210 @@ +// Gauntlets ---------------------------------------------------------------- + +class Gauntlets : Weapon +{ + Default + { + +BLOODSPLATTER + Weapon.SelectionOrder 2300; + +WEAPON.WIMPY_WEAPON + +WEAPON.MELEEWEAPON + Weapon.Kickback 0; + Weapon.YAdjust 15; + Weapon.UpSound "weapons/gauntletsactivate"; + Weapon.SisterWeapon "GauntletsPowered"; + Inventory.PickupMessage "$TXT_WPNGAUNTLETS"; + Tag "$TAG_GAUNTLETS"; + Obituary "$OB_MPGAUNTLETS"; + } + + States + { + Spawn: + WGNT A -1; + Stop; + Ready: + GAUN A 1 A_WeaponReady; + Loop; + Deselect: + GAUN A 1 A_Lower; + Loop; + Select: + GAUN A 1 A_Raise; + Loop; + Fire: + GAUN B 4 A_PlaySound("weapons/gauntletsuse", CHAN_WEAPON); + GAUN C 4; + Hold: + GAUN DEF 4 BRIGHT A_GauntletAttack(0); + GAUN C 4 A_ReFire; + GAUN B 4 A_Light0; + Goto Ready; + } + + //--------------------------------------------------------------------------- + // + // PROC A_GauntletAttack + // + //--------------------------------------------------------------------------- + + action void A_GauntletAttack (int power) + { + int damage; + double dist; + Class pufftype; + FTranslatedLineTarget t; + int actualdamage = 0; + Actor puff; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + + player.GetPSprite(PSP_WEAPON).x = ((random[GauntletAtk]() & 3) - 2); + player.GetPSprite(PSP_WEAPON).y = WEAPONTOP + (random[GauntletAtk]() & 3); + } + double ang = angle; + if (power) + { + damage = random[GauntletAtk](1, 8) * 2; + dist = 4*MELEERANGE; + ang += random2[GauntletAtk]() * (2.8125 / 256); + pufftype = "GauntletPuff2"; + } + else + { + damage = random[GauntletAtk](1, 8) * 2; + dist = SAWRANGE; + ang += random2[GauntletAtk]() * (5.625 / 256); + pufftype = "GauntletPuff1"; + } + double slope = AimLineAttack (ang, dist); + [puff, actualdamage] = LineAttack (ang, dist, slope, damage, 'Melee', pufftype, false, t); + if (!t.linetarget) + { + if (random[GauntletAtk]() > 64) + { + player.extralight = !player.extralight; + } + A_PlaySound ("weapons/gauntletson", CHAN_AUTO); + return; + } + int randVal = random[GauntletAtk](); + if (randVal < 64) + { + player.extralight = 0; + } + else if (randVal < 160) + { + player.extralight = 1; + } + else + { + player.extralight = 2; + } + if (power) + { + if (!t.linetarget.bDontDrain) GiveBody (actualdamage >> 1); + A_PlaySound ("weapons/gauntletspowhit", CHAN_AUTO); + } + else + { + A_PlaySound ("weapons/gauntletshit", CHAN_AUTO); + } + // turn to face target + ang = t.angleFromSource; + double anglediff = deltaangle(angle, ang); + + if (anglediff < 0.0) + { + if (anglediff < -4.5) + angle = ang + 90.0 / 21; + else + angle -= 4.5; + } + else + { + if (anglediff > 4.5) + angle = ang - 90.0 / 21; + else + angle += 4.5; + } + bJustAttacked = true; + } +} + + +class GauntletsPowered : Gauntlets +{ + Default + { + +WEAPON.POWERED_UP + Tag "$TAG_GAUNTLETSP"; + Obituary "$OB_MPPGAUNTLETS"; + Weapon.SisterWeapon "Gauntlets"; + } + + States + { + Ready: + GAUN GHI 4 A_WeaponReady; + Loop; + Deselect: + GAUN G 1 A_Lower; + Loop; + Select: + GAUN G 1 A_Raise; + Loop; + Fire: + GAUN J 4 A_PlaySound("weapons/gauntletsuse", CHAN_WEAPON); + GAUN K 4; + Hold: + GAUN LMN 4 BRIGHT A_GauntletAttack(1); + GAUN K 4 A_ReFire; + GAUN J 4 A_Light0; + Goto Ready; + } +} + + +// Gauntlet puff 1 ---------------------------------------------------------- + +class GauntletPuff1 : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + RenderStyle "Translucent"; + Alpha 0.4; + VSpeed 0.8; + } + + States + { + Spawn: + PUF1 ABCD 4 BRIGHT; + Stop; + } +} + +// Gauntlet puff 2 --------------------------------------------------------- + +class GauntletPuff2 : GauntletPuff1 +{ + States + { + Spawn: + PUF1 EFGH 4 BRIGHT; + Stop; + } +} + diff --git a/wadsrc/static/zscript/heretic/weaponmace.txt b/wadsrc/static/zscript/heretic/weaponmace.txt new file mode 100644 index 000000000..fd11e685f --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponmace.txt @@ -0,0 +1,468 @@ +// The mace itself ---------------------------------------------------------- + +class Mace : HereticWeapon +{ + Default + { + Weapon.SelectionOrder 1400; + Weapon.AmmoUse 1; + Weapon.AmmoGive1 50; + Weapon.YAdjust 15; + Weapon.AmmoType "MaceAmmo"; + Weapon.SisterWeapon "MacePowered"; + Inventory.PickupMessage "$TXT_WPNMACE"; + Tag "$TAG_MACE"; + } + + States + { + Spawn: + WMCE A -1; + Stop; + Ready: + MACE A 1 A_WeaponReady; + Loop; + Deselect: + MACE A 1 A_Lower; + Loop; + Select: + MACE A 1 A_Raise; + Loop; + Fire: + MACE B 4; + Hold: + MACE CDEF 3 A_FireMacePL1; + MACE C 4 A_ReFire; + MACE DEFB 4; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireMacePL1 + // + //---------------------------------------------------------------------------- + + action void A_FireMacePL1() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + + if (random[MaceAtk]() < 28) + { + Actor ball = Spawn("MaceFX2", Pos + (0, 0, 28 - Floorclip), ALLOW_REPLACE); + ball.Vel.Z = 2 - clamp(tan(pitch), -5, 5); + ball.target = self; + ball.angle = self.angle; + ball.AddZ(ball.Vel.Z); + ball.VelFromAngle(); + ball.Vel += Vel.xy / 2; + ball.A_PlaySound ("weapons/maceshoot", CHAN_BODY); + ball.CheckMissileSpawn (radius); + } + else + { + player.GetPSprite(PSP_WEAPON).x = ((random[MaceAtk]() & 3) - 2); + player.GetPSprite(PSP_WEAPON).y = WEAPONTOP + (random[MaceAtk]() & 3); + Actor ball = SpawnPlayerMissile("MaceFX1", angle + (((random[MaceAtk]() & 7) - 4) * (360. / 256))); + if (ball) + { + ball.special1 = 16; // tics till dropoff + } + } + } +} + +class MacePowered : Mace +{ + Default + { + +WEAPON.POWERED_UP + Weapon.AmmoUse 5; + Weapon.AmmoGive 0; + Weapon.SisterWeapon "Mace"; + Tag "$TAG_MACEP"; + } + + States + { + Fire: + Hold: + MACE B 4; + MACE D 4 A_FireMacePL2; + MACE B 4; + MACE A 8 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireMacePL2 + // + //---------------------------------------------------------------------------- + + action void A_FireMacePL2() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + Actor mo = SpawnPlayerMissile ("MaceFX4", angle, pLineTarget:t); + if (mo) + { + mo.Vel.xy += Vel.xy; + mo.Vel.Z = 2 - clamp(tan(pitch), -5, 5); + if (t.linetarget && !t.unlinked) + { + mo.tracer = t.linetarget; + } + } + A_PlaySound ("weapons/maceshoot", CHAN_WEAPON); + } +} + +// Mace FX1 ----------------------------------------------------------------- + +class MaceFX1 : Actor +{ + const MAGIC_JUNK = 1234; + + Default + { + Radius 8; + Height 6; + Speed 20; + Damage 2; + Projectile; + +THRUGHOST + BounceType "HereticCompat"; + SeeSound "weapons/maceshoot"; + Obituary "$OB_MPMACE"; + } + + States + { + Spawn: + FX02 AB 4 A_MacePL1Check; + Loop; + Death: + FX02 F 4 BRIGHT A_MaceBallImpact; + FX02 GHIJ 4 BRIGHT; + Stop; + } + + //---------------------------------------------------------------------------- + // + // PROC A_MacePL1Check + // + //---------------------------------------------------------------------------- + + void A_MacePL1Check() + { + if (special1 == 0) return; + special1 -= 4; + if (special1 > 0) return; + special1 = 0; + bNoGravity = false; + Gravity = 1. / 8; + // [RH] Avoid some precision loss by scaling the velocity directly + double velscale = 7 / Vel.XY.Length(); + Vel.XY *= velscale; + Vel.Z *= 0.5; + } + + //---------------------------------------------------------------------------- + // + // PROC A_MaceBallImpact + // + //---------------------------------------------------------------------------- + + void A_MaceBallImpact() + { + if ((health != MAGIC_JUNK) && bInFloat) + { // Bounce + health = MAGIC_JUNK; + Vel.Z *= 0.75; + bBounceOnFloors = bBounceOnCeilings = false; + SetState (SpawnState); + A_PlaySound ("weapons/macebounce", CHAN_BODY); + } + else + { // Explode + Vel = (0,0,0); + bNoGravity = true; + Gravity = 1; + A_PlaySound ("weapons/macehit", CHAN_BODY); + } + } +} + +// Mace FX2 ----------------------------------------------------------------- + +class MaceFX2 : MaceFX1 +{ + Default + { + Speed 10; + Damage 6; + Gravity 0.125; + -NOGRAVITY + SeeSound ""; + } + + States + { + Spawn: + FX02 CD 4; + Loop; + Death: + FX02 F 4 A_MaceBallImpact2; + goto Super::Death+1; + } + + //---------------------------------------------------------------------------- + // + // PROC A_MaceBallImpact2 + // + //---------------------------------------------------------------------------- + + void A_MaceBallImpact2() + { + if ((pos.Z <= floorz) && HitFloor ()) + { // Landed in some sort of liquid + Destroy (); + return; + } + if (bInFloat) + { + if (Vel.Z >= 2) + { + // Bounce + Vel.Z *= 0.75; + SetState (SpawnState); + + Actor tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE); + tiny.target = target; + tiny.angle = angle + 90.; + tiny.VelFromAngle(Vel.Z - 1.); + tiny.Vel += (Vel.XY * .5, Vel.Z); + tiny.CheckMissileSpawn (radius); + + tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE); + tiny.target = target; + tiny.angle = angle - 90.; + tiny.VelFromAngle(Vel.Z - 1.); + tiny.Vel += (Vel.XY * .5, Vel.Z); + tiny.CheckMissileSpawn (radius); + return; + } + } + Vel = (0,0,0); + bNoGravity = true; + bBounceOnFloors = bBounceOnCeilings = false; + Gravity = 1; + } +} + +// Mace FX3 ----------------------------------------------------------------- + +class MaceFX3 : MaceFX1 +{ + Default + { + Speed 7; + Damage 4; + -NOGRAVITY; + Gravity 0.125; + } + + States + { + Spawn: + FX02 AB 4; + Loop; + } +} + + +// Mace FX4 ----------------------------------------------------------------- + +class MaceFX4 : Actor +{ + Default + { + Radius 8; + Height 6; + Speed 7; + Damage 18; + Gravity 0.125; + Projectile; + -NOGRAVITY + +TELESTOMP + +THRUGHOST + -NOTELEPORT + BounceType "HereticCompat"; + SeeSound ""; + Obituary "$OB_MPPMACE"; + } + + States + { + Spawn: + FX02 E 99; + Loop; + Death: + FX02 C 4 A_DeathBallImpact; + FX02 GHIJ 4 BRIGHT; + Stop; + } + + //--------------------------------------------------------------------------- + // + // FUNC P_AutoUseChaosDevice + // + //--------------------------------------------------------------------------- + + private bool AutoUseChaosDevice (PlayerInfo player) + { + Inventory arti = player.mo.FindInventory("ArtiTeleport"); + + if (arti != null) + { + player.mo.UseInventory (arti); + player.health = player.mo.health = (player.health+1)/2; + return true; + } + return false; + } + + //---------------------------------------------------------------------------- + // + // PROC DoSpecialDamage + // + //---------------------------------------------------------------------------- + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target.bBoss || target.bDontSquash || target.IsTeammate (self.target)) + { // Don't allow cheap boss kills and don't instagib teammates + return damage; + } + else if (target.player) + { // Player specific checks + if (target.player.mo.bInvulnerable) + { // Can't hurt invulnerable players + return -1; + } + if (AutoUseChaosDevice (target.player)) + { // Player was saved using chaos device + return -1; + } + } + return TELEFRAG_DAMAGE; // Something's gonna die + } + + //---------------------------------------------------------------------------- + // + // PROC A_DeathBallImpact + // + //---------------------------------------------------------------------------- + + void A_DeathBallImpact() + { + FTranslatedLineTarget t; + + if ((pos.Z <= floorz) && HitFloor ()) + { // Landed in some sort of liquid + Destroy (); + return; + } + if (bInFloat) + { + if (Vel.Z >= 2) + { + // Bounce + bool newAngle = false; + double ang = 0; + if (tracer) + { + if (!tracer.bShootable) + { // Target died + tracer = null; + } + else + { // Seek + ang = AngleTo(tracer); + newAngle = true; + } + } + else + { // Find new target + ang = 0.; + for (int i = 0; i < 16; i++) + { + AimLineAttack (ang, 640., t, 0., ALF_NOFRIENDS|ALF_PORTALRESTRICT, null, target); + if (t.linetarget && target != t.linetarget) + { + tracer = t.linetarget; + ang = t.angleFromSource; + newAngle = true; + break; + } + ang += 22.5; + } + } + if (newAngle) + { + angle = ang; + VelFromAngle(); + } + SetState (SpawnState); + A_PlaySound ("weapons/macestop", CHAN_BODY); + return; + } + } + Vel = (0,0,0); + bNoGravity = true; + Gravity = 1; + A_PlaySound ("weapons/maceexplode", CHAN_BODY); + } +} + + +// Mace spawn spot ---------------------------------------------------------- + +class MaceSpawner : SpecialSpot +{ + Default + { + +NOSECTOR + +NOBLOCKMAP + } + + States + { + Spawn: + TNT1 A 1; + TNT1 A -1 A_SpawnSingleItem("Mace", 64, 64, 0); + Stop; + } +} diff --git a/wadsrc/static/zscript/heretic/weaponphoenix.txt b/wadsrc/static/zscript/heretic/weaponphoenix.txt new file mode 100644 index 000000000..6b949b8f6 --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponphoenix.txt @@ -0,0 +1,337 @@ +// Phoenix Rod -------------------------------------------------------------- + +class PhoenixRod : Weapon +{ + Default + { + +WEAPON.NOAUTOFIRE + Weapon.SelectionOrder 2600; + Weapon.Kickback 150; + Weapon.YAdjust 15; + Weapon.AmmoUse 1; + Weapon.AmmoGive 2; + Weapon.AmmoType "PhoenixRodAmmo"; + Weapon.Sisterweapon "PhoenixRodPowered"; + Inventory.PickupMessage "$TXT_WPNPHOENIXROD"; + Tag "$TAG_PHOENIXROD"; + } + + States + { + Spawn: + WPHX A -1; + Stop; + Ready: + PHNX A 1 A_WeaponReady; + Loop; + Deselect: + PHNX A 1 A_Lower; + Loop; + Select: + PHNX A 1 A_Raise; + Loop; + Fire: + PHNX B 5; + PHNX C 7 A_FirePhoenixPL1; + PHNX DB 4; + PHNX B 0 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FirePhoenixPL1 + // + //---------------------------------------------------------------------------- + + action void A_FirePhoenixPL1() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + SpawnPlayerMissile ("PhoenixFX1"); + Thrust(4, angle + 180); + } + + +} + +class PhoenixRodPowered : PhoenixRod +{ + const FLAME_THROWER_TICS = (10*TICRATE); + + private int FlameCount; // for flamethrower duration + + Default + { + +WEAPON.POWERED_UP + +WEAPON.MELEEWEAPON + Weapon.SisterWeapon "PhoenixRod"; + Weapon.AmmoGive 0; + Tag "$TAG_PHOENIXRODP"; + } + + States + { + Fire: + PHNX B 3 A_InitPhoenixPL2; + Hold: + PHNX C 1 A_FirePhoenixPL2; + PHNX B 4 A_ReFire; + Powerdown: + PHNX B 4 A_ShutdownPhoenixPL2; + Goto Ready; + } + + + override void EndPowerup () + { + DepleteAmmo (bAltFire); + Owner.player.refire = 0; + Owner.A_StopSound (CHAN_WEAPON); + Owner.player.ReadyWeapon = SisterWeapon; + Owner.player.SetPsprite(PSP_WEAPON, SisterWeapon.GetReadyState()); + } + + //---------------------------------------------------------------------------- + // + // PROC A_InitPhoenixPL2 + // + //---------------------------------------------------------------------------- + + action void A_InitPhoenixPL2() + { + if (player != null) + { + PhoenixRodPowered flamethrower = PhoenixRodPowered(player.ReadyWeapon); + if (flamethrower != null) + { + flamethrower.FlameCount = FLAME_THROWER_TICS; + } + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_FirePhoenixPL2 + // + // Flame thrower effect. + // + //---------------------------------------------------------------------------- + + action void A_FirePhoenixPL2() + { + if (player == null) + { + return; + } + + PhoenixRodPowered flamethrower = PhoenixRodPowered(player.ReadyWeapon); + + if (flamethrower == null || --flamethrower.FlameCount == 0) + { // Out of flame + player.SetPsprite(PSP_WEAPON, flamethrower.FindState("Powerdown")); + player.refire = 0; + A_StopSound (CHAN_WEAPON); + return; + } + + double slope = -clamp(tan(pitch), -5, 5); + double xo = Random2[FirePhoenixPL2]() / 128.; + double yo = Random2[FirePhoenixPL2]() / 128.; + Vector3 spawnpos = Vec3Offset(xo, yo, 26 + slope - Floorclip); + + slope += 0.1; + Actor mo = Spawn("PhoenixFX2", spawnpos, ALLOW_REPLACE); + mo.target = self; + mo.Angle = Angle; + mo.VelFromAngle(); + mo.Vel.XY += Vel.XY; + mo.Vel.Z = mo.Speed * slope; + if (!player.refire) + { + A_PlaySound("weapons/phoenixpowshoot", CHAN_WEAPON, 1, true); + } + mo.CheckMissileSpawn (radius); + } + + //---------------------------------------------------------------------------- + // + // PROC A_ShutdownPhoenixPL2 + // + //---------------------------------------------------------------------------- + + action void A_ShutdownPhoenixPL2() + { + if (player == null) + { + return; + } + A_StopSound (CHAN_WEAPON); + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + weapon.DepleteAmmo (weapon.bAltFire); + } + } + + +} + +// Phoenix FX 1 ------------------------------------------------------------- + +class PhoenixFX1 : Actor +{ + Default + { + Radius 11; + Height 8; + Speed 20; + Damage 20; + DamageType "Fire"; + Projectile; + +THRUGHOST + +SPECIALFIREDAMAGE + SeeSound "weapons/phoenixshoot"; + DeathSound "weapons/phoenixhit"; + Obituary "$OB_MPPHOENIXROD"; + } + + States + { + Spawn: + FX04 A 4 BRIGHT A_PhoenixPuff; + Loop; + Death: + FX08 A 6 BRIGHT A_Explode; + FX08 BC 5 BRIGHT; + FX08 DEFGH 4 BRIGHT; + Stop; + } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + Sorcerer2 s2 = Sorcerer2(target); + if (s2 != null && random[HornRodFX2]() < 96) + { // D'Sparil teleports away + s2.DSparilTeleport (); + return -1; + } + return damage; + } + + //---------------------------------------------------------------------------- + // + // PROC A_PhoenixPuff + // + //---------------------------------------------------------------------------- + + void A_PhoenixPuff() + { + //[RH] Heretic never sets the target for seeking + //P_SeekerMissile (self, 5, 10); + Actor puff = Spawn("PhoenixPuff", Pos, ALLOW_REPLACE); + puff.Vel.XY = AngleToVector(Angle + 90, 1.3); + + puff = Spawn("PhoenixPuff", Pos, ALLOW_REPLACE); + puff.Vel.XY = AngleToVector(Angle - 90, 1.3); + } + + +} + +// Phoenix puff ------------------------------------------------------------- + +class PhoenixPuff : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +NOTELEPORT + +CANNOTPUSH + RenderStyle "Translucent"; + Alpha 0.4; + } + + States + { + Spawn: + FX04 BCDEF 4; + Stop; + } +} + +// Phoenix FX 2 ------------------------------------------------------------- + +class PhoenixFX2 : Actor +{ + Default + { + Radius 6; + Height 8; + Speed 10; + Damage 2; + DamageType "Fire"; + Projectile; + RenderStyle "Add"; + Obituary "$OB_MPPPHOENIXROD"; + } + + States + { + Spawn: + FX09 ABABA 2 BRIGHT; + FX09 B 2 BRIGHT A_FlameEnd; + FX09 CDEF 2 BRIGHT; + Stop; + Death: + FX09 G 3 BRIGHT; + FX09 H 3 BRIGHT A_FloatPuff; + FX09 I 4 BRIGHT; + FX09 JK 5 BRIGHT; + Stop; + } + + + int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target.player && Random[PhoenixFX2]() < 128) + { // Freeze player for a bit + target.reactiontime += 4; + } + return damage; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FlameEnd + // + //---------------------------------------------------------------------------- + + void A_FlameEnd() + { + Vel.Z += 1.5; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FloatPuff + // + //---------------------------------------------------------------------------- + + void A_FloatPuff() + { + Vel.Z += 1.8; + } + + +} diff --git a/wadsrc/static/zscript/heretic/weaponskullrod.txt b/wadsrc/static/zscript/heretic/weaponskullrod.txt new file mode 100644 index 000000000..a8d77e1d8 --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponskullrod.txt @@ -0,0 +1,425 @@ +// Skull (Horn) Rod --------------------------------------------------------- + +class SkullRod : HereticWeapon +{ + Default + { + Weapon.SelectionOrder 200; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 50; + Weapon.YAdjust 15; + Weapon.AmmoType1 "SkullRodAmmo"; + Weapon.SisterWeapon "SkullRodPowered"; + Inventory.PickupMessage "$TXT_WPNSKULLROD"; + Tag "$TAG_SKULLROD"; + } + + States + { + Spawn: + WSKL A -1; + Stop; + Ready: + HROD A 1 A_WeaponReady; + Loop; + Deselect: + HROD A 1 A_Lower; + Loop; + Select: + HROD A 1 A_Raise; + Loop; + Fire: + HROD AB 4 A_FireSkullRodPL1; + HROD B 0 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireSkullRodPL1 + // + //---------------------------------------------------------------------------- + + action void A_FireSkullRodPL1() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + Actor mo = SpawnPlayerMissile ("HornRodFX1"); + // Randomize the first frame + if (mo && random[FireSkullRod]() > 128) + { + mo.SetState (mo.CurState.NextState); + } + } + + +} + +class SkullRodPowered : SkullRod +{ + Default + { + +WEAPON.POWERED_UP + Weapon.AmmoUse1 5; + Weapon.AmmoGive1 0; + Weapon.SisterWeapon "SkullRod"; + Tag "$TAG_SKULLRODP"; + } + + States + { + Fire: + HROD C 2; + HROD D 3; + HROD E 2; + HROD F 3; + HROD G 4 A_FireSkullRodPL2; + HROD F 2; + HROD E 3; + HROD D 2; + HROD C 2 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireSkullRodPL2 + // + // The special2 field holds the player number that shot the rain missile. + // The special1 field holds the id of the rain sound. + // + //---------------------------------------------------------------------------- + + action void A_FireSkullRodPL2() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + // Use MissileActor instead of the first return value from P_SpawnPlayerMissile + // because we need to give info to it, even if it exploded immediately. + Actor mo, MissileActor; + [mo, MissileActor] = SpawnPlayerMissile ("HornRodFX2", angle, pLineTarget: t); + if (MissileActor != null) + { + if (t.linetarget && !t.unlinked) + { + MissileActor.tracer = t.linetarget; + } + MissileActor.A_PlaySound ("weapons/hornrodpowshoot", CHAN_WEAPON); + } + } + + +} + +// Horn Rod FX 1 ------------------------------------------------------------ + +class HornRodFX1 : Actor +{ + Default + { + Radius 12; + Height 8; + Speed 22; + Damage 3; + Projectile; + +WINDTHRUST + -NOBLOCKMAP + RenderStyle "Add"; + SeeSound "weapons/hornrodshoot"; + DeathSound "weapons/hornrodhit"; + Obituary "$OB_MPSKULLROD"; + } + + States + { + Spawn: + FX00 AB 6 BRIGHT; + Loop; + Death: + FX00 HI 5 BRIGHT; + FX00 JK 4 BRIGHT; + FX00 LM 3 BRIGHT; + Stop; + } +} + + +// Horn Rod FX 2 ------------------------------------------------------------ + +class HornRodFX2 : Actor +{ + Default + { + Radius 12; + Height 8; + Speed 22; + Damage 10; + Health 140; + Projectile; + RenderStyle "Add"; + SeeSound "weapons/hornrodpowshoot"; + DeathSound "weapons/hornrodpowhit"; + Obituary "$OB_MPPSKULLROD"; + } + + States + { + Spawn: + FX00 C 3 BRIGHT; + FX00 D 3 BRIGHT A_SeekerMissile(10, 30); + FX00 E 3 BRIGHT; + FX00 F 3 BRIGHT A_SeekerMissile(10, 30); + Loop; + Death: + FX00 H 5 BRIGHT A_AddPlayerRain; + FX00 I 5 BRIGHT; + FX00 J 4 BRIGHT; + FX00 KLM 3 BRIGHT; + FX00 G 1 A_HideInCeiling; + FX00 G 1 A_SkullRodStorm; + Wait; + } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + Sorcerer2 s2 = Sorcerer2(target); + if (s2 != null && random[HornRodFX2]() < 96) + { // D'Sparil teleports away + s2.DSparilTeleport (); + return -1; + } + return damage; + } + + //---------------------------------------------------------------------------- + // + // PROC A_AddPlayerRain + // + //---------------------------------------------------------------------------- + + void A_AddPlayerRain() + { + RainTracker tracker; + + if (target == null || target.health <= 0) + { // Shooter is dead or nonexistant + return; + } + + tracker = RainTracker(target.FindInventory("RainTracker")); + + // They player is only allowed two rainstorms at a time. Shooting more + // than that will cause the oldest one to terminate. + if (tracker != null) + { + if (tracker.Rain1 && tracker.Rain2) + { // Terminate an active rain + if (tracker.Rain1.health < tracker.Rain2.health) + { + if (tracker.Rain1.health > 16) + { + tracker.Rain1.health = 16; + } + tracker.Rain1 = null; + } + else + { + if (tracker.Rain2.health > 16) + { + tracker.Rain2.health = 16; + } + tracker.Rain2 = null; + } + } + } + else + { + tracker = RainTracker(target.GiveInventoryType("RainTracker")); + } + // Add rain mobj to list + if (tracker.Rain1) + { + tracker.Rain2 = self; + } + else + { + tracker.Rain1 = self; + } + ActiveSound = "misc/rain"; + } + + //---------------------------------------------------------------------------- + // + // PROC A_HideInCeiling + // + //---------------------------------------------------------------------------- + + void A_HideInCeiling() + { + // This no longer hides in the ceiling. It just makes the actor invisible and keeps it in place. + // We need its actual position to determine the correct ceiling height in A_SkullRodStorm. + bInvisible = true; + bSolid = false; + bMissile = false; + Vel = (0,0,0); + } + + //---------------------------------------------------------------------------- + // + // PROC A_SkullRodStorm + // + //---------------------------------------------------------------------------- + + void A_SkullRodStorm() + { + static const Name translations[] = + { + "RainPillar1", "RainPillar2", "RainPillar3", "RainPillar4", + "RainPillar5", "RainPillar6", "RainPillar7", "RainPillar8" + }; + + if (health-- == 0) + { + A_StopSound (CHAN_BODY); + if (target == null) + { // Player left the game + Destroy (); + return; + } + RainTracker tracker = RainTracker(target.FindInventory("RainTracker")); + if (tracker != null) + { + if (tracker.Rain1 == self) + { + tracker.Rain1 = null; + } + else if (tracker.Rain2 == self) + { + tracker.Rain2 = null; + } + } + Destroy (); + return; + } + if (Random[SkullRodStorm]() < 25) + { // Fudge rain frequency + return; + } + double xo = ((Random[SkullRodStorm]() & 127) - 64); + double yo = ((Random[SkullRodStorm]() & 127) - 64); + Vector3 spawnpos = Vec2OffsetZ(xo, yo, pos.z); + Actor mo = Spawn("RainPillar", spawnpos, ALLOW_REPLACE); + if (!mo) return; + + // Find the ceiling above the spawn location. This may come from 3D floors but will not reach through portals. + // (should probably be fixed for portals, too.) + double newz = mo.CurSector.NextHighestCeilingAt(mo.pos.x, mo.pos.y, mo.pos.z, mo.pos.z, FFCF_NOPORTALS) - mo.height; + mo.SetZ(newz); + + if (multiplayer && target.player) + { + mo.A_SetTranslation(translations[target.PlayerNumber()]); + } + mo.target = target; + mo.Vel.X = MinVel; // Force collision detection + mo.Vel.Z = -mo.Speed; + mo.CheckMissileSpawn (radius); + if (ActiveSound > 0) A_PlaySound(ActiveSound, CHAN_BODY, 1, true); + } + + +} + +// Rain pillar 1 ------------------------------------------------------------ + +class RainPillar : Actor +{ + Default + { + Radius 5; + Height 12; + Speed 12; + Damage 5; + Mass 5; + Projectile; + -ACTIVATEPCROSS + -ACTIVATEIMPACT + RenderStyle "Add"; + Obituary "$OB_MPPSKULLROD"; + } + + States + { + Spawn: + FX22 A -1 BRIGHT; + Stop; + Death: + FX22 B 4 BRIGHT A_RainImpact; + FX22 CDEF 4 BRIGHT; + Stop; + NotFloor: + FX22 GHI 4 BRIGHT; + Stop; + } + + //---------------------------------------------------------------------------- + // + // PROC A_RainImpact + // + //---------------------------------------------------------------------------- + + void A_RainImpact() + { + if (pos.z > floorz) + { + SetStateLabel("NotFloor"); + } + else if (random[RainImpact]() < 40) + { + HitFloor (); + } + } + + // Rain pillar 1 ------------------------------------------------------------ + + int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target.bBoss) + { // Decrease damage for bosses + damage = random[RainDamage](1, 8); + } + return damage; + } +} + +// Rain tracker "inventory" item -------------------------------------------- + +class RainTracker : Inventory +{ + Actor Rain1, Rain2; + + Default + { + +INVENTORY.UNDROPPABLE + } +} diff --git a/wadsrc/static/zscript/heretic/weaponstaff.txt b/wadsrc/static/zscript/heretic/weaponstaff.txt new file mode 100644 index 000000000..6349bd61b --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponstaff.txt @@ -0,0 +1,147 @@ +// Staff -------------------------------------------------------------------- + +class Staff : HereticWeapon +{ + Default + { + Weapon.SelectionOrder 3800; + +THRUGHOST + +WEAPON.WIMPY_WEAPON + +WEAPON.MELEEWEAPON + Weapon.sisterweapon "StaffPowered"; + Obituary "$OB_MPSTAFF"; + Tag "$TAG_STAFF"; + } + + + States + { + Ready: + STFF A 1 A_WeaponReady; + Loop; + Deselect: + STFF A 1 A_Lower; + Loop; + Select: + STFF A 1 A_Raise; + Loop; + Fire: + STFF B 6; + STFF C 8 A_StaffAttack(random[StaffAttack](5, 20), "StaffPuff"); + STFF B 8 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_StaffAttackPL1 + // + //---------------------------------------------------------------------------- + + action void A_StaffAttack (int damage, class puff) + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + double ang = angle + Random2[StaffAtk]() * (5.625 / 256); + double slope = AimLineAttack (ang, MELEERANGE); + LineAttack (ang, MELEERANGE, slope, damage, 'Melee', puff, true, t); + if (t.linetarget) + { + //S_StartSound(player.mo, sfx_stfhit); + // turn to face target + angle = t.angleFromSource; + } + } +} + +class StaffPowered : Staff +{ + Default + { + Weapon.sisterweapon "Staff"; + Weapon.ReadySound "weapons/staffcrackle"; + +WEAPON.POWERED_UP + +WEAPON.READYSNDHALF + +WEAPON.STAFF2_KICKBACK + Obituary "$OB_MPPSTAFF"; + Tag "$TAG_STAFFP"; + } + + States + { + Ready: + STFF DEF 4 A_WeaponReady; + Loop; + Deselect: + STFF D 1 A_Lower; + Loop; + Select: + STFF D 1 A_Raise; + Loop; + Fire: + STFF G 6; + STFF H 8 A_StaffAttack(random[StaffAttack](18, 81), "StaffPuff2"); + STFF G 8 A_ReFire; + Goto Ready; + } +} + + +// Staff puff --------------------------------------------------------------- + +class StaffPuff : Actor +{ + Default + { + RenderStyle "Translucent"; + Alpha 0.4; + VSpeed 1; + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + AttackSound "weapons/staffhit"; + } + + States + { + Spawn: + PUF3 A 4 BRIGHT; + PUF3 BCD 4; + Stop; + } +} + +// Staff puff 2 ------------------------------------------------------------- + +class StaffPuff2 : Actor +{ + Default + { + RenderStyle "Add"; + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + AttackSound "weapons/staffpowerhit"; + } + + States + { + Spawn: + PUF4 ABCDEF 4 BRIGHT; + Stop; + } +} + + + diff --git a/wadsrc/static/zscript/heretic/weaponwand.txt b/wadsrc/static/zscript/heretic/weaponwand.txt new file mode 100644 index 000000000..bd1475adb --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponwand.txt @@ -0,0 +1,217 @@ +// Gold wand ---------------------------------------------------------------- + +class GoldWand : HereticWeapon +{ + Default + { + +BLOODSPLATTER + Weapon.SelectionOrder 2000; + Weapon.AmmoGive 25; + Weapon.AmmoUse 1; + Weapon.AmmoType "GoldWandAmmo"; + Weapon.SisterWeapon "GoldWandPowered"; + Weapon.YAdjust 5; + Inventory.PickupMessage "$TXT_WPNGOLDWAND"; + Obituary "$OB_MPGOLDWAND"; + Tag "$TAG_GOLDWAND"; + } + + States + { + Spawn: + GWAN A -1; + Stop; + Ready: + GWND A 1 A_WeaponReady; + Loop; + Deselect: + GWND A 1 A_Lower; + Loop; + Select: + GWND A 1 A_Raise; + Loop; + Fire: + GWND B 3; + GWND C 5 A_FireGoldWandPL1; + GWND D 3; + GWND D 0 A_ReFire; + Goto Ready; + } + + + //---------------------------------------------------------------------------- + // + // PROC A_FireGoldWandPL1 + // + //---------------------------------------------------------------------------- + + action void A_FireGoldWandPL1 () + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + double pitch = BulletSlope(); + int damage = 7 + random[FireGoldWand]() & 7; + double ang = angle; + if (player.refire) + { + ang += Random2[FireGoldWand]() * (5.625 / 256); + } + LineAttack(ang, PLAYERMISSILERANGE, pitch, damage, 'Hitscan', "GoldWandPuff1"); + A_PlaySound("weapons/wandhit", CHAN_WEAPON); + } + +} + +class GoldWandPowered : GoldWand +{ + Default + { + +WEAPON.POWERED_UP + Weapon.AmmoGive 0; + Weapon.SisterWeapon "GoldWand"; + Obituary "$OB_MPPGOLDWAND"; + Tag "$TAG_GOLDWANDP"; + } + + States + { + Fire: + GWND B 3; + GWND C 4 A_FireGoldWandPL2; + GWND D 3; + GWND D 0 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireGoldWandPL2 + // + //---------------------------------------------------------------------------- + + action void A_FireGoldWandPL2 () + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + double pitch = BulletSlope(); + + double vz = -GetDefaultByType("GoldWandFX2").Speed * clamp(tan(pitch), -5, 5); + SpawnMissileAngle("GoldWandFX2", angle - (45. / 8), vz); + SpawnMissileAngle("GoldWandFX2", angle + (45. / 8), vz); + double ang = angle - (45. / 8); + for(int i = 0; i < 5; i++) + { + int damage = random[FireGoldWand](1, 8); + LineAttack (ang, PLAYERMISSILERANGE, pitch, damage, 'Hitscan', "GoldWandPuff2"); + ang += ((45. / 8) * 2) / 4; + } + A_PlaySound("weapons/wandhit", CHAN_WEAPON); + } + + +} + + +// Gold wand FX1 ------------------------------------------------------------ + +class GoldWandFX1 : Actor +{ + Default + { + Radius 10; + Height 6; + Speed 22; + Damage 2; + Projectile; + RenderStyle "Add"; + DeathSound "weapons/wandhit"; + Obituary "$OB_MPPGOLDWAND"; + } + + States + { + Spawn: + FX01 AB 6 BRIGHT; + Loop; + Death: + FX01 EFGH 3 BRIGHT; + Stop; + } +} + +// Gold wand FX2 ------------------------------------------------------------ + +class GoldWandFX2 : GoldWandFX1 +{ + Default + { + Speed 18; + Damage 1; + DeathSound ""; + } + + States + { + Spawn: + FX01 CD 6 BRIGHT; + Loop; + } +} + +// Gold wand puff 1 --------------------------------------------------------- + +class GoldWandPuff1 : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + RenderStyle "Add"; + } + + States + { + Spawn: + PUF2 ABCDE 3 BRIGHT; + Stop; + } +} + +// Gold wand puff 2 --------------------------------------------------------- + +class GoldWandPuff2 : GoldWandFX1 +{ + Default + { + Skip_Super; + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + } + + States + { + Spawn: + Goto Super::Death; + } +} + diff --git a/wadsrc/static/zscript/hexen/baseweapons.txt b/wadsrc/static/zscript/hexen/baseweapons.txt index eb685a873..a1e1af9bd 100644 --- a/wadsrc/static/zscript/hexen/baseweapons.txt +++ b/wadsrc/static/zscript/hexen/baseweapons.txt @@ -1,7 +1,7 @@ // The Doom and Heretic players are not excluded from pickup in case // somebody wants to use these weapons with either of those games. -class FighterWeapon : Weapon native +class FighterWeapon : Weapon { Default { @@ -10,7 +10,7 @@ class FighterWeapon : Weapon native } } -class ClericWeapon : Weapon native +class ClericWeapon : Weapon { Default { @@ -19,7 +19,7 @@ class ClericWeapon : Weapon native } } -class MageWeapon : Weapon native +class MageWeapon : Weapon { Default { @@ -27,3 +27,58 @@ class MageWeapon : Weapon native Inventory.ForbiddenTo "FighterPlayer", "ClericPlayer"; } } + +extend class Actor +{ + //============================================================================ + // + // AdjustPlayerAngle + // + //============================================================================ + + const MAX_ANGLE_ADJUST = (5.); + + void AdjustPlayerAngle(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. + double atkangle = t.unlinked ? t.angleFromSource : AngleTo(t.linetarget); + double difference = deltaangle(Angle, atkangle); + if (abs(difference) > MAX_ANGLE_ADJUST) + { + if (difference > 0) + { + angle += MAX_ANGLE_ADJUST; + } + else + { + angle -= MAX_ANGLE_ADJUST; + } + } + else + { + angle = t.angleFromSource; + } + } + + //============================================================================ + // + // A_DropQuietusPieces + // + //============================================================================ + + void A_DropWeaponPieces(class p1, class p2, class p3) + { + for (int i = 0, j = 0; i < 3; ++i) + { + Actor piece = Spawn (j == 0 ? p1 : j == 1 ? p2 : p3, Pos, ALLOW_REPLACE); + if (piece != null) + { + piece.Vel = self.Vel + AngleToVector(i * 120., 1); + piece.bDropped = true; + j = (j == 0) ? (random[PieceDrop]() & 1) + 1 : 3-j; + } + } + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/hexen/blastradius.txt b/wadsrc/static/zscript/hexen/blastradius.txt index 17ac75dc3..a9c631318 100644 --- a/wadsrc/static/zscript/hexen/blastradius.txt +++ b/wadsrc/static/zscript/hexen/blastradius.txt @@ -20,6 +20,137 @@ class ArtiBlastRadius : CustomInventory Use: TNT1 A 0 A_Blast; } + +} + +//========================================================================== +// +// A_Blast is public to Actor +// +//========================================================================== + +extend class Actor +{ + /* For reference, the default values: + #define BLAST_RADIUS_DIST 255.0 + #define BLAST_SPEED 20.0 + #define BLAST_FULLSTRENGTH 255 + */ + + //========================================================================== + // + // AArtiBlastRadius :: BlastActor + // + //========================================================================== + + private void BlastActor (Actor victim, double strength, double speed, Class blasteffect, bool dontdamage) + { + if (!victim.SpecialBlastHandling (self, strength)) + { + return; + } + + double ang = AngleTo(victim); + Vector2 move = AngleToVector(ang, speed); + victim.Vel.XY = move; + + // Spawn blast puff + ang -= 180.; + Vector3 spawnpos = victim.Vec3Offset( + (victim.radius + 1) * cos(ang), + (victim.radius + 1) * sin(ang), + (victim.Height / 2) - victim.Floorclip); + Actor mo = Spawn (blasteffect, spawnpos, ALLOW_REPLACE); + if (mo) + { + mo.Vel.XY = victim.Vel.XY; + } + if (victim.bMissile) + { + // [RH] Floor and ceiling huggers should not be blasted vertically. + if (!victim.bFloorHugger && !victim.bCeilingHugger) + { + mo.Vel.Z = victim.Vel.Z = 8; + } + } + else + { + victim.Vel.Z = 1000. / victim.Mass; + } + if (victim.player) + { + // Players handled automatically + } + else if (!dontdamage) + { + victim.bBlasted = true; + } + if (victim.bTouchy) + { // Touchy objects die when blasted + victim.bArmed = false; // Disarm + victim.DamageMobj(self, self, victim.health, 'Melee', DMG_FORCED); + } + } + + //========================================================================== + // + // AArtiBlastRadius :: Activate + // + // Blast all actors away + // + //========================================================================== + + action void A_Blast(int blastflags = 0, double strength = 255, double radius = 255, double speed = 20, class blasteffect = "BlastEffect", sound blastsound = "BlastRadius") + { + + Weapon weapon = player.ReadyWeapon; + if (player && (blastflags & BF_USEAMMO) && invoker == weapon && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + + { + if (weapon != null && !weapon.DepleteAmmo(weapon.bAltFire)) + { + return; + } + } + + A_PlaySound (blastsound, CHAN_AUTO); + + if (!(blastflags & BF_DONTWARN)) + { + NoiseAlert (self); + } + ThinkerIterator it = ThinkerIterator.Create("Actor"); + Actor mo; + while ( (mo = Actor(it.Next ())) ) + { + if (mo == self || (mo.bBoss && !(blastflags & BF_AFFECTBOSSES)) || mo.bDormant || mo.bDontBlast) + { // Not a valid monster: originator, boss, dormant, or otherwise protected + continue; + } + if (mo.bIceCorpse || mo.bCanBlast) + { + // Let these special cases go + } + else if (mo.bIsMonster && mo.health <= 0) + { + continue; + } + else if (!mo.player && !mo.bMissile && !mo.bIsMonster && !mo.bCanBlast && !mo.bTouchy && !mo.bVulnerable) + { // Must be monster, player, missile, touchy or vulnerable + continue; + } + if (Distance2D(mo) > radius) + { // Out of range + continue; + } + if (mo.CurSector.PortalGroup != CurSector.PortalGroup && !CheckSight(mo)) + { + // in another region and cannot be seen. + continue; + } + BlastActor (mo, strength, speed, blasteffect, !!(blastflags & BF_NOIMPACTDAMAGE)); + } + } } // Blast Effect ------------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/boostarmor.txt b/wadsrc/static/zscript/hexen/boostarmor.txt index 4c14b69bc..f8c797c99 100644 --- a/wadsrc/static/zscript/hexen/boostarmor.txt +++ b/wadsrc/static/zscript/hexen/boostarmor.txt @@ -1,7 +1,7 @@ // Boost Armor Artifact (Dragonskin Bracers) -------------------------------- -class ArtiBoostArmor : Inventory native +class ArtiBoostArmor : Inventory { Default { @@ -21,4 +21,49 @@ class ArtiBoostArmor : Inventory native BRAC ABCDEFGH 4 Bright; Loop; } + + override bool Use (bool pickup) + { + int count = 0; + + if (gametype() == GAME_Hexen) + { + HexenArmor armor; + + for (int i = 0; i < 4; ++i) + { + armor = HexenArmor(Spawn("HexenArmor")); + armor.bDropped = true; + armor.health = i; + armor.Amount = 1; + if (!armor.CallTryPickup (Owner)) + { + armor.Destroy (); + } + else + { + count++; + } + } + return count != 0; + } + else + { + BasicArmorBonus armor = BasicArmorBonus(Spawn("BasicArmorBonus")); + armor.bDropped = true; + armor.SaveAmount = 50; + armor.MaxSaveAmount = 300; + if (!armor.CallTryPickup (Owner)) + { + armor.Destroy (); + return false; + } + else + { + return true; + } + } + } + + } diff --git a/wadsrc/static/zscript/hexen/clericboss.txt b/wadsrc/static/zscript/hexen/clericboss.txt index a2019bae5..4bf223517 100644 --- a/wadsrc/static/zscript/hexen/clericboss.txt +++ b/wadsrc/static/zscript/hexen/clericboss.txt @@ -18,8 +18,6 @@ class ClericBoss : Actor Obituary "$OBCBOSS"; } - native void A_ClericAttack(); - States { Spawn: @@ -79,4 +77,19 @@ class ClericBoss : Actor FDTH V 4 Bright ; Stop; } + + //============================================================================ + // + // A_ClericAttack + // + //============================================================================ + + void A_ClericAttack() + { + if (!target) return; + + Actor missile = SpawnMissileZ (pos.z + 40., target, "HolyMissile"); + if (missile != null) missile.tracer = null; // No initial target + A_PlaySound ("HolySymbolFire", CHAN_WEAPON); + } } diff --git a/wadsrc/static/zscript/hexen/clericflame.txt b/wadsrc/static/zscript/hexen/clericflame.txt index 13fca8ad1..d222a07db 100644 --- a/wadsrc/static/zscript/hexen/clericflame.txt +++ b/wadsrc/static/zscript/hexen/clericflame.txt @@ -16,8 +16,6 @@ class CWeapFlame : ClericWeapon Tag "$TAG_CWEAPFLAME"; } - action native void A_CFlameAttack(); - States { Spawn: @@ -43,6 +41,29 @@ class CWeapFlame : ClericWeapon CFLM G 2; Goto Ready; } + + //============================================================================ + // + // A_CFlameAttack + // + //============================================================================ + + action void A_CFlameAttack() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + SpawnPlayerMissile ("CFlameMissile"); + A_PlaySound ("ClericFlameFire", CHAN_WEAPON); + } } // Floor Flame -------------------------------------------------------------- @@ -127,6 +148,9 @@ class FlamePuff2 : FlamePuff class CircleFlame : Actor { + const FLAMESPEED = 0.45; + const FLAMEROTSPEED = 2.; + Default { Radius 6; @@ -140,8 +164,6 @@ class CircleFlame : Actor Obituary "$OB_MPCWEAPFLAME"; } - native void A_CFlameRotate(); - States { Spawn: @@ -166,11 +188,25 @@ class CircleFlame : Actor CFCF TUVWXYZ 3 Bright; Stop; } + + //============================================================================ + // + // A_CFlameRotate + // + //============================================================================ + + void A_CFlameRotate() + { + double an = Angle + 90.; + VelFromAngle(FLAMEROTSPEED, an); + Vel.XY += (specialf1, specialf2); + Angle += 6; + } } // Flame Missile ------------------------------------------------------------ -class CFlameMissile : FastProjectile native +class CFlameMissile : FastProjectile { Default { @@ -184,9 +220,6 @@ class CFlameMissile : FastProjectile native Obituary "$OB_MPCWEAPFLAME"; } - native void A_CFlamePuff(); - native void A_CFlameMissile(); - States { Spawn: @@ -208,4 +241,82 @@ class CFlameMissile : FastProjectile native CFFX M 3 Bright; Stop; } + + override void BeginPlay () + { + special1 = 2; + } + + override void Effect () + { + if (!--special1) + { + special1 = 4; + double newz = pos.z - 12; + if (newz < floorz) + { + newz = floorz; + } + Actor mo = Spawn ("CFlameFloor", (pos.xy, newz), ALLOW_REPLACE); + if (mo) + { + mo.angle = angle; + } + } + } + + //============================================================================ + // + // A_CFlamePuff + // + //============================================================================ + + void A_CFlamePuff() + { + bInvisible = false; + Vel = (0,0,0); + A_PlaySound ("ClericFlameExplode", CHAN_BODY); + } + + //============================================================================ + // + // A_CFlameMissile + // + //============================================================================ + + void A_CFlameMissile() + { + bInvisible = false; + A_PlaySound ("ClericFlameExplode", CHAN_BODY); + if (BlockingMobj && BlockingMobj.bShootable) + { // Hit something, so spawn the flame circle around the thing + double dist = BlockingMobj.radius + 18; + for (int i = 0; i < 4; i++) + { + double an = i*45.; + Actor mo = Spawn ("CircleFlame", BlockingMobj.Vec3Angle(dist, an, 5), ALLOW_REPLACE); + if (mo) + { + mo.angle = an; + mo.target = target; + mo.VelFromAngle(CircleFlame.FLAMESPEED); + mo.specialf1 = mo.Vel.X; + mo.specialf2 = mo.Vel.Y; + mo.tics -= random[FlameMissile]()&3; + } + an += 180; + mo = Spawn("CircleFlame", BlockingMobj.Vec3Angle(dist, an, 5), ALLOW_REPLACE); + if(mo) + { + mo.angle = an; + mo.target = target; + mo.VelFromAngle(-CircleFlame.FLAMESPEED); + mo.specialf1 = mo.Vel.X; + mo.specialf2 = mo.Vel.Y; + mo.tics -= random[FlameMissile]()&3; + } + } + SetState (SpawnState); + } + } } diff --git a/wadsrc/static/zscript/hexen/clericholy.txt b/wadsrc/static/zscript/hexen/clericholy.txt index 1a4372f4d..33b69bf25 100644 --- a/wadsrc/static/zscript/hexen/clericholy.txt +++ b/wadsrc/static/zscript/hexen/clericholy.txt @@ -76,8 +76,10 @@ class WraithvergeDrop : Actor // Cleric's Wraithverge (Holy Symbol?) -------------------------------------- -class CWeapWraithverge : ClericWeapon native +class CWeapWraithverge : ClericWeapon { + int CHolyCount; + Default { Health 3; @@ -96,8 +98,6 @@ class CWeapWraithverge : ClericWeapon native Inventory.PickupSound "WeaponBuild"; } - action native void A_CHolyAttack(); - action native void A_CHolyPalette(); States { @@ -122,6 +122,66 @@ class CWeapWraithverge : ClericWeapon native CHLY G 2 Offset (0, 36) A_CHolyPalette; Goto Ready; } + + override color GetBlend () + { + if (paletteflash & PF_HEXENWEAPONS) + { + if (CHolyCount == 3) + return Color(128, 70, 70, 70); + else if (CHolyCount == 2) + return Color(128, 100, 100, 100); + else if (CHolyCount == 1) + return Color(128, 130, 130, 130); + else + return Color(0, 0, 0, 0); + } + else + { + return Color(CHolyCount * 128 / 3, 131, 131, 131); + } + } + + //============================================================================ + // + // A_CHolyAttack + // + //============================================================================ + + action void A_CHolyAttack() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + Actor missile = SpawnPlayerMissile ("HolyMissile", angle, pLineTarget:t); + if (missile != null && !t.unlinked) + { + missile.tracer = t.linetarget; + } + + invoker.CHolyCount = 3; + A_PlaySound ("HolySymbolFire", CHAN_WEAPON); + } + + //============================================================================ + // + // A_CHolyPalette + // + //============================================================================ + + action void A_CHolyPalette() + { + if (invoker.CHolyCount > 0) invoker.CHolyCount--; + } } // Holy Missile ------------------------------------------------------------- @@ -139,8 +199,6 @@ class HolyMissile : Actor +EXTREMEDEATH } - native void A_CHolyAttack2(); - States { Spawn: @@ -149,6 +207,60 @@ class HolyMissile : Actor SPIR P 1 Bright A_CHolyAttack2; Stop; } + + //============================================================================ + // + // A_CHolyAttack2 + // + // Spawns the spirits + //============================================================================ + + void A_CHolyAttack2() + { + for (int j = 0; j < 4; j++) + { + Actor mo = Spawn("HolySpirit", Pos, ALLOW_REPLACE); + if (!mo) + { + continue; + } + switch (j) + { // float bob index + + case 0: + mo.WeaveIndexZ = random[HolyAtk2]() & 7; // upper-left + break; + case 1: + mo.WeaveIndexZ = 32 + (random[HolyAtk2]() & 7); // upper-right + break; + case 2: + mo.WeaveIndexXY = 32 + (random[HolyAtk2]() & 7); // lower-left + break; + case 3: + mo.WeaveIndexXY = 32 + (random[HolyAtk2]() & 7); + mo.WeaveIndexZ = 32 + (random[HolyAtk2]() & 7); + break; + } + mo.SetZ(pos.z); + mo.angle = angle + 67.5 - 45.*j; + mo.Thrust(); + mo.target = 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 (tracer) + { + mo.tracer = tracer; + mo.bNoClip = true; + mo.bSkullFly = true; + mo.bMissile = false; + } + HolyTail.SpawnSpiritTail (mo); + } + } } // Holy Missile Puff -------------------------------------------------------- @@ -192,7 +304,7 @@ class HolyPuff : Actor // Holy Spirit -------------------------------------------------------------- -class HolySpirit : Actor native +class HolySpirit : Actor { Default { @@ -204,16 +316,13 @@ class HolySpirit : Actor native Projectile; +RIPPER +SEEKERMISSILE +FOILINVUL +SKYEXPLODE +NOEXPLODEFLOOR +CANBLAST - +EXTREMEDEATH + +EXTREMEDEATH +NOSHIELDREFLECT RenderStyle "Translucent"; Alpha 0.4; DeathSound "SpiritDie"; Obituary "$OB_MPCWEAPWRAITHVERGE"; } - native void A_CHolySeek(); - native void A_CHolyCheckScream(); - States { Spawn: @@ -226,6 +335,203 @@ class HolySpirit : Actor native SPIR FGHI 4; Stop; } + + //============================================================================ + // + // + // + //============================================================================ + + override bool Slam(Actor thing) + { + if (thing.bShootable && thing != target) + { + if (multiplayer && !deathmatch && thing.player && target.player) + { // don't attack other co-op players + return true; + } + if (thing.bReflective && (thing.player || thing.bBoss)) + { + tracer = target; + target = thing; + return true; + } + if (thing.bIsMonster || thing.player) + { + tracer = thing; + } + if (random[SpiritSlam]() < 96) + { + int dam = 12; + if (thing.player || thing.bBoss) + { + dam = 3; + // ghost burns out faster when attacking players/bosses + health -= 6; + } + thing.DamageMobj(self, target, dam, 'Melee'); + if (random[SpiritSlam]() < 128) + { + Spawn("HolyPuff", Pos, ALLOW_REPLACE); + A_PlaySound("SpiritAttack", CHAN_WEAPON); + if (thing.bIsMonster && random[SpiritSlam]() < 128) + { + thing.Howl(); + } + } + } + if (thing.health <= 0) + { + tracer = null; + } + } + return true; + } + + override bool SpecialBlastHandling (Actor source, double strength) + { + if (tracer == source) + { + tracer = target; + target = source; + } + return true; + } + + //============================================================================ + // + // CHolyFindTarget + // + //============================================================================ + + private void CHolyFindTarget () + { + Actor target; + + if ( (target = RoughMonsterSearch (6, true)) ) + { + tracer = target; + bNoClip = true; + bSkullFly = true; + bMissile = false; + } + } + + //============================================================================ + // + // CHolySeekerMissile + // + // Similar to P_SeekerMissile, but seeks to a random Z on the target + //============================================================================ + + private void CHolySeekerMissile (double thresh, double turnMax) + { + Actor target = tracer; + if (target == NULL) + { + return; + } + if (!target.bShootable || (!target.bIsMonster && !target.player)) + { // Target died/target isn't a player or creature + tracer = null; + bNoClip = false; + bSkullFly = false; + bMissile = true; + CHolyFindTarget(); + return; + } + double ang = deltaangle(angle, AngleTo(target)); + double delta = abs(ang); + + if (delta > thresh) + { + delta /= 2; + if (delta > turnMax) + { + delta = turnMax; + } + } + if (ang > 0) + { // Turn clockwise + angle += delta; + } + else + { // Turn counter clockwise + angle -= delta; + } + VelFromAngle(); + + if (!(level.time&15) + || pos.z > target.pos.z + target.height + || pos.z + height < target.pos.z) + { + double newZ = target.pos.z + ((random[HolySeeker]()*target.Height) / 256.); + double deltaZ = newZ - pos.z; + if (abs(deltaZ) > 15) + { + if (deltaZ > 0) + { + deltaZ = 15; + } + else + { + deltaZ = -15; + } + } + Vel.Z = deltaZ / DistanceBySpeed(target, Speed); + } + } + + //============================================================================ + // + // A_CHolySeek + // + //============================================================================ + + void A_CHolySeek() + { + health--; + if (health <= 0) + { + Vel.X /= 4; + Vel.Y /= 4; + Vel.Z = 0; + SetStateLabel ("Death"); + tics -= random[HolySeeker]()&3; + return; + } + if (tracer) + { + CHolySeekerMissile (args[0], args[0]*2.); + if (!((level.time+7)&15)) + { + args[0] = 5+(random[HolySeeker]()/20); + } + } + + int xyspeed = (random[HolySeeker]() % 5); + int zspeed = (random[HolySeeker]() % 5); + A_Weave(xyspeed, zspeed, 4., 2.); + } + + //============================================================================ + // + // A_CHolyCheckScream + // + //============================================================================ + + void A_CHolyCheckScream() + { + A_CHolySeek(); + if (random[HolyScream]() < 20) + { + A_PlaySound ("SpiritActive", CHAN_VOICE); + } + if (!tracer) + { + CHolyFindTarget(); + } + } } // Holy Tail ---------------------------------------------------------------- @@ -242,8 +548,6 @@ class HolyTail : Actor Alpha 0.6; } - native void A_CHolyTail(); - States { Spawn: @@ -253,6 +557,108 @@ class HolyTail : Actor SPIR D -1; Stop; } + + //============================================================================ + // + // SpawnSpiritTail + // + //============================================================================ + + static void SpawnSpiritTail (Actor spirit) + { + Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE); + tail.target = spirit; // parent + for (int i = 1; i < 3; i++) + { + Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE); + tail.tracer = next; + tail = next; + } + tail.tracer = null; // last tail bit + } + + //============================================================================ + // + // CHolyTailFollow + // + //============================================================================ + + private void CHolyTailFollow(double dist) + { + Actor mo = self; + while (mo) + { + Actor child = mo.tracer; + if (child) + { + double an = mo.AngleTo(child); + double oldDistance = child.Distance2D(mo); + if (child.TryMove(mo.Pos.XY + AngleToVector(an, dist), true)) + { + double newDistance = child.Distance2D(mo) - 1; + if (oldDistance < 1) + { + if (child.pos.z < mo.pos.z) + { + child.SetZ(mo.pos.z - dist); + } + else + { + child.SetZ(mo.pos.z + dist); + } + } + else + { + child.SetZ(mo.pos.z + (newDistance * (child.pos.z - mo.pos.z) / oldDistance)); + } + } + } + mo = child; + dist -= 1; + } + } + + //============================================================================ + // + // CHolyTailRemove + // + //============================================================================ + + private void CHolyTailRemove () + { + Actor mo = self; + while (mo) + { + Actor next = mo.tracer; + mo.Destroy (); + mo = next; + } + } + + //============================================================================ + // + // A_CHolyTail + // + //============================================================================ + + void A_CHolyTail() + { + Actor parent = 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 (); + return; + } + else + { + if (TryMove(parent.Vec2Angle(14., parent.Angle, true), true)) + { + SetZ(parent.pos.z - 5.); + } + CHolyTailFollow(10); + } + } } // Holy Tail Trail --------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/clericmace.txt b/wadsrc/static/zscript/hexen/clericmace.txt index 5e00b9c3b..b6f42c7e4 100644 --- a/wadsrc/static/zscript/hexen/clericmace.txt +++ b/wadsrc/static/zscript/hexen/clericmace.txt @@ -13,8 +13,6 @@ class CWeapMace : ClericWeapon Tag "$TAG_CWEAPMACE"; } - action native void A_CMaceAttack(); - States { Select: @@ -46,4 +44,44 @@ class CWeapMace : ClericWeapon CMCE A 1 Offset (8, 45); Goto Ready; } + + //=========================================================================== + // + // A_CMaceAttack + // + //=========================================================================== + + action void A_CMaceAttack() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + int damage = 25+(random[MaceAtk]()&15); + for (int i = 0; i < 16; i++) + { + for (int j = 1; j >= -1; j -= 2) + { + double ang = angle + j*i*(45. / 16); + double slope = AimLineAttack(ang, 2 * MELEERANGE, t); + if (t.linetarget) + { + LineAttack(ang, 2 * MELEERANGE, slope, damage, 'Melee', "HammerPuff", true, t); + if (t.linetarget != null) + { + AdjustPlayerAngle(t); + return; + } + } + } + } + // didn't find any creatures, so try to strike any walls + weaponspecial = 0; + + double slope = AimLineAttack (angle, MELEERANGE); + LineAttack (angle, MELEERANGE, slope, damage, 'Melee', "HammerPuff"); + } } diff --git a/wadsrc/static/zscript/hexen/clericstaff.txt b/wadsrc/static/zscript/hexen/clericstaff.txt index 7ae0e9da3..0d7ca0a99 100644 --- a/wadsrc/static/zscript/hexen/clericstaff.txt +++ b/wadsrc/static/zscript/hexen/clericstaff.txt @@ -16,11 +16,6 @@ class CWeapStaff : ClericWeapon Tag "$TAG_CWEAPSTAFF"; } - action native void A_CStaffInitBlink(); - action native void A_CStaffCheckBlink(); - action native void A_CStaffCheck(); - action native void A_CStaffAttack(); - States { Spawn: @@ -55,11 +50,132 @@ class CWeapStaff : ClericWeapon CSSF K 10 Offset (0, 36); Goto Ready + 2; } + + //============================================================================ + // + // A_CStaffCheck + // + //============================================================================ + + action void A_CStaffCheck() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + Weapon weapon = player.ReadyWeapon; + + int damage = 20 + (random[StaffCheck]() & 15); + int max = player.mo.GetMaxHealth(); + for (int i = 0; i < 3; i++) + { + for (int j = 1; j >= -1; j -= 2) + { + double ang = angle + j*i*(45. / 16); + double slope = AimLineAttack(ang, 1.5 * MELEERANGE, t, 0., ALF_CHECK3D); + if (t.linetarget) + { + LineAttack(ang, 1.5 * MELEERANGE, slope, damage, 'Melee', "CStaffPuff", false, t); + if (t.linetarget != null) + { + angle = t.angleFromSource; + if (((t.linetarget.player && (!t.linetarget.IsTeammate(self) || level.teamdamage != 0)) || t.linetarget.bIsMonster) + && (!t.linetarget.bDormant && !bInvulnerable)) + { + int newLife = player.health + (damage >> 3); + newLife = newLife > max ? max : newLife; + if (newLife > player.health) + { + health = player.health = newLife; + } + if (weapon != null) + { + State newstate = weapon.FindState("Drain"); + if (newstate != null) player.SetPsprite(PSP_WEAPON, newstate); + } + } + if (weapon != null) + { + weapon.DepleteAmmo(weapon.bAltFire, false); + } + } + return; + } + } + } + } + + //============================================================================ + // + // A_CStaffAttack + // + //============================================================================ + + action void A_CStaffAttack() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + Actor mo = SpawnPlayerMissile ("CStaffMissile", angle - 3.0); + if (mo) + { + mo.WeaveIndexXY = 32; + } + mo = SpawnPlayerMissile ("CStaffMissile", angle + 3.0); + if (mo) + { + mo.WeaveIndexXY = 0; + } + A_PlaySound ("ClericCStaffFire", CHAN_WEAPON); + } + + //============================================================================ + // + // A_CStaffInitBlink + // + //============================================================================ + + action void A_CStaffInitBlink() + { + weaponspecial = (random[CStaffBlink]() >> 1) + 20; + } + + //============================================================================ + // + // A_CStaffCheckBlink + // + //============================================================================ + + action void A_CStaffCheckBlink() + { + if (player && player.ReadyWeapon) + { + if (!--weaponspecial) + { + player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.FindState ("Blink")); + weaponspecial = (random[CStaffBlink]() + 50) >> 2; + } + else + { + A_WeaponReady(); + } + } + } } // Serpent Staff Missile ---------------------------------------------------- -class CStaffMissile : Actor native +class CStaffMissile : Actor { Default { @@ -82,6 +198,34 @@ class CStaffMissile : Actor native CSSF HI 3 Bright; Stop; } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + // Cleric Serpent Staff does poison damage + if (target.player) + { + target.player.PoisonPlayer (self, self.target, 20); + damage >>= 1; + } + return damage; + } + +} + +extend class Actor +{ + + //============================================================================ + // + // A_CStaffMissileSlither + // + //============================================================================ + + void A_CStaffMissileSlither() + { + A_Weave(3, 0, 1., 0.); + } + } // Serpent Staff Puff ------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/demons.txt b/wadsrc/static/zscript/hexen/demons.txt index 8aa65629d..25e078509 100644 --- a/wadsrc/static/zscript/hexen/demons.txt +++ b/wadsrc/static/zscript/hexen/demons.txt @@ -58,11 +58,9 @@ class Demon1 : Actor DEMN H 6; DEMN I 6 { - A_SpawnItemEx("Demon1Chunk1", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle+90), frandom[DemonChunks](1,4.984375)*sin(Angle+90), 8, 90, ChunkFlags); - A_SpawnItemEx("Demon1Chunk2", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); - A_SpawnItemEx("Demon1Chunk3", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); - A_SpawnItemEx("Demon1Chunk4", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); - A_SpawnItemEx("Demon1Chunk5", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); + static const class chunks[] = { "Demon1Chunk1", "Demon1Chunk2", "Demon1Chunk3", "Demon1Chunk4", "Demon1Chunk5" }; + for(int i = 0; i < 5; i++) + A_SpawnItemEx(chunks[i], 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle+90), frandom[DemonChunks](1,4.984375)*sin(Angle+90), 8, 90, ChunkFlags); } Goto Death+2; Ice: @@ -266,11 +264,9 @@ class Demon2 : Demon1 DEM2 H 6; DEM2 I 6 { - A_SpawnItemEx("Demon2Chunk1", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle+90), frandom[DemonChunks](1,4.984375)*sin(Angle+90), 8, 90, ChunkFlags); - A_SpawnItemEx("Demon2Chunk2", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); - A_SpawnItemEx("Demon2Chunk3", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); - A_SpawnItemEx("Demon2Chunk4", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); - A_SpawnItemEx("Demon2Chunk5", 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle-90), frandom[DemonChunks](1,4.984375)*sin(Angle-90), 8, 270, ChunkFlags); + static const class chunks[] = { "Demon2Chunk1", "Demon2Chunk2", "Demon2Chunk3", "Demon2Chunk4", "Demon2Chunk5" }; + for(int i = 0; i < 5; i++) + A_SpawnItemEx(chunks[i], 0,0,45, frandom[DemonChunks](1,4.984375)*cos(Angle+90), frandom[DemonChunks](1,4.984375)*sin(Angle+90), 8, 90, ChunkFlags); } Goto Death+2; } diff --git a/wadsrc/static/zscript/hexen/dragon.txt b/wadsrc/static/zscript/hexen/dragon.txt index 6df3da943..6082d1c61 100644 --- a/wadsrc/static/zscript/hexen/dragon.txt +++ b/wadsrc/static/zscript/hexen/dragon.txt @@ -1,6 +1,4 @@ - // Dragon ------------------------------------------------------------------- - class Dragon : Actor { Default @@ -23,13 +21,6 @@ class Dragon : Actor Obituary "$OB_DRAGON"; } - native void A_DragonInitFlight(); - native void A_DragonFlap(); - native void A_DragonFlight(); - native void A_DragonPain(); - native void A_DragonAttack(); - native void A_DragonCheckCrash(); - States { Spawn: @@ -58,6 +49,253 @@ class Dragon : Actor DRAG M -1; Stop; } + + //============================================================================ + // + // DragonSeek + // + //============================================================================ + + private void DragonSeek (double thresh, double turnMax) + { + double dist; + double delta; + Actor targ; + int i; + double bestAngle; + double angleToSpot, angleToTarget; + Actor mo; + + targ = tracer; + if(targ == null) + { + return; + } + + double diff = deltaangle(angle, AngleTo(targ)); + delta = abs(diff); + + if (delta > thresh) + { + delta /= 2; + if (delta > turnMax) + { + delta = turnMax; + } + } + if (diff > 0) + { // Turn clockwise + angle = angle + delta; + } + else + { // Turn counter clockwise + angle = angle - delta; + } + VelFromAngle(); + + dist = DistanceBySpeed(targ, Speed); + if (pos.z + height < targ.pos.z || targ.pos.z + targ.height < pos.z) + { + Vel.Z = (targ.pos.z - pos.z) / dist; + } + if (targ.bShootable && random[DragonSeek]() < 64) + { // attack the destination mobj if it's attackable + Actor oldTarget; + + if (absangle(angle, AngleTo(targ)) < 22.5) + { + oldTarget = target; + target = targ; + if (CheckMeleeRange ()) + { + int damage = random[DragonSeek](1, 8) * 10; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + else if (random[DragonSeek]() < 128 && CheckMissileRange()) + { + SpawnMissile(targ, "DragonFireball"); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + target = oldTarget; + } + } + if (dist < 4) + { // Hit the target thing + if (target && random[DragonSeek]() < 200) + { + Actor bestActor = null; + bestAngle = 360.; + angleToTarget = AngleTo(target); + for (i = 0; i < 5; i++) + { + if (!targ.args[i]) + { + continue; + } + ActorIterator iter = ActorIterator.Create(targ.args[i]); + mo = iter.Next (); + if (mo == null) + { + continue; + } + angleToSpot = AngleTo(mo); + double diff = absangle(angleToSpot, angleToTarget); + if (diff < bestAngle) + { + bestAngle = diff; + bestActor = mo; + } + } + if (bestActor != null) + { + tracer = bestActor; + } + } + else + { + // [RH] Don't lock up if the dragon doesn't have any + // targs defined + for (i = 0; i < 5; ++i) + { + if (targ.args[i] != 0) + { + break; + } + } + if (i < 5) + { + do + { + i = (random[DragonSeek]() >> 2) % 5; + } while(!targ.args[i]); + ActorIterator iter = ActorIterator.Create(targ.args[i]); + tracer = iter.Next (); + } + } + } + } + + //============================================================================ + // + // A_DragonInitFlight + // + //============================================================================ + + void A_DragonInitFlight() + { + ActorIterator iter = ActorIterator.Create(tid); + + do + { // find the first tid identical to the dragon's tid + tracer = iter.Next (); + if (tracer == null) + { + SetState (SpawnState); + return; + } + } while (tracer == self); + RemoveFromHash (); + } + + //============================================================================ + // + // A_DragonFlight + // + //============================================================================ + + void A_DragonFlight() + { + double ang; + + DragonSeek (4., 8.); + if (target) + { + if(!target.bShootable) + { // target died + target = null; + return; + } + ang = absangle(angle, AngleTo(target)); + if (ang <22.5 && CheckMeleeRange()) + { + int damage = random[DragonFlight](1, 8) * 8; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + else if (ang <= 20) + { + SetState (MissileState); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + } + else + { + LookForPlayers (true); + } + } + + //============================================================================ + // + // A_DragonFlap + // + //============================================================================ + + void A_DragonFlap() + { + A_DragonFlight(); + if (random[DragonFlight]() < 240) + { + A_PlaySound ("DragonWingflap", CHAN_BODY); + } + else + { + PlayActiveSound (); + } + } + + //============================================================================ + // + // A_DragonAttack + // + //============================================================================ + + void A_DragonAttack() + { + SpawnMissile (target, "DragonFireball"); + } + + + //============================================================================ + // + // A_DragonPain + // + //============================================================================ + + void A_DragonPain() + { + A_Pain(); + if (!tracer) + { // no destination spot yet + SetState (SeeState); + } + } + + //============================================================================ + // + // A_DragonCheckCrash + // + //============================================================================ + + void A_DragonCheckCrash() + { + if (pos.z <= floorz) + { + SetStateLabel ("Crash"); + } + } } // Dragon Fireball ---------------------------------------------------------- @@ -77,8 +315,6 @@ class DragonFireball : Actor DeathSound "DragonFireballExplode"; } - native void A_DragonFX2(); - States { Spawn: @@ -90,6 +326,30 @@ class DragonFireball : Actor DRFX KL 3 Bright; Stop; } + + //============================================================================ + // + // A_DragonFX2 + // + //============================================================================ + + void A_DragonFX2() + { + int delay = 16+(random[DragonFX2]()>>3); + for (int i = random[DragonFX2](1, 4); i; i--) + { + double xo = (random[DragonFX2]() - 128) / 4.; + double yo = (random[DragonFX2]() - 128) / 4.; + double zo = (random[DragonFX2]() - 128) / 16.; + + Actor mo = Spawn ("DragonExplosion", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + if (mo) + { + mo.tics = delay + (random[DragonFX2](0, 3)) * i*2; + mo.target = target; + } + } + } } // Dragon Fireball Secondary Explosion -------------------------------------- diff --git a/wadsrc/static/zscript/hexen/fighteraxe.txt b/wadsrc/static/zscript/hexen/fighteraxe.txt index 30b235192..a0d1deba1 100644 --- a/wadsrc/static/zscript/hexen/fighteraxe.txt +++ b/wadsrc/static/zscript/hexen/fighteraxe.txt @@ -1,8 +1,10 @@ // The Fighter's Axe -------------------------------------------------------- -class FWeapAxe : FighterWeapon native +class FWeapAxe : FighterWeapon { + const AXERANGE = (2.25 * MELEERANGE); + Default { Weapon.SelectionOrder 1500; @@ -17,13 +19,6 @@ class FWeapAxe : FighterWeapon native Tag "$TAG_FWEAPAXE"; } - action native void A_FAxeCheckUp(); - action native void A_FAxeCheckReady(); - action native void A_FAxeCheckAtk(); - action native void A_FAxeAttack(); - action native void A_FAxeCheckUpG(); - action native void A_FAxeCheckReadyG(); - States { Spawn: @@ -45,6 +40,7 @@ class FWeapAxe : FighterWeapon native FAXE D 1 Offset (-5, 70) A_FAxeAttack; FAXE D 2 Offset (-25, 90); FAXE E 1 Offset (15, 32); + EndAttack: FAXE E 2 Offset (10, 54); FAXE E 7 Offset (10, 150); FAXE A 1 Offset (0, 60) A_ReFire; @@ -79,6 +75,207 @@ class FWeapAxe : FighterWeapon native FAXE A 1; Goto ReadyGlow; } + + override State GetUpState () + { + return Ammo1.Amount ? FindState ("SelectGlow") : Super.GetUpState(); + } + + override State GetDownState () + { + return Ammo1.Amount ? FindState ("DeselectGlow") : Super.GetDownState(); + } + + override State GetReadyState () + { + return Ammo1.Amount ? FindState ("ReadyGlow") : Super.GetReadyState(); + } + + override State GetAtkState (bool hold) + { + return Ammo1.Amount ? FindState ("FireGlow") : Super.GetAtkState(hold); + } + + + + //============================================================================ + // + // A_FAxeCheckReady + // + //============================================================================ + + action void A_FAxeCheckReady() + { + if (player == null) + { + return; + } + Weapon w = player.ReadyWeapon; + if (w.Ammo1 && w.Ammo1.Amount > 0) + { + player.SetPsprite(PSP_WEAPON, w.FindState("ReadyGlow")); + } + else + { + A_WeaponReady(); + } + } + + //============================================================================ + // + // A_FAxeCheckReadyG + // + //============================================================================ + + action void A_FAxeCheckReadyG() + { + if (player == null) + { + return; + } + Weapon w = player.ReadyWeapon; + if (!w.Ammo1 || w.Ammo1.Amount <= 0) + { + player.SetPsprite(PSP_WEAPON, w.FindState("Ready")); + } + else + { + A_WeaponReady(); + } + } + + //============================================================================ + // + // A_FAxeCheckUp + // + //============================================================================ + + action void A_FAxeCheckUp() + { + if (player == null) + { + return; + } + Weapon w = player.ReadyWeapon; + if (w.Ammo1 && w.Ammo1.Amount > 0) + { + player.SetPsprite(PSP_WEAPON, w.FindState("SelectGlow")); + } + else + { + A_Raise(); + } + } + + //============================================================================ + // + // A_FAxeCheckUpG + // + //============================================================================ + + action void A_FAxeCheckUpG() + { + if (player == null) + { + return; + } + Weapon w = player.ReadyWeapon; + if (!w.Ammo1 || w.Ammo1.Amount <= 0) + { + player.SetPsprite(PSP_WEAPON, w.FindState("Select")); + } + else + { + A_Raise(); + } + } + + //============================================================================ + // + // A_FAxeCheckAtk + // + //============================================================================ + + action void A_FAxeCheckAtk() + { + if (player == null) + { + return; + } + Weapon w = player.ReadyWeapon; + if (w.Ammo1 && w.Ammo1.Amount > 0) + { + player.SetPsprite(PSP_WEAPON, w.FindState("FireGlow")); + } + } + + //============================================================================ + // + // A_FAxeAttack + // + //============================================================================ + + action void A_FAxeAttack() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + int damage = 40+(random[AxeAtk]() & 15); + damage += random[AxeAtk]() & 7; + int power = 0; + Weapon weapon = player.ReadyWeapon; + class pufftype; + int usemana; + if ((usemana = (weapon.Ammo1 && weapon.Ammo1.Amount > 0))) + { + damage <<= 1; + power = 6; + pufftype = "AxePuffGlow"; + } + else + { + pufftype = "AxePuff"; + } + for (int i = 0; i < 16; i++) + { + for (int j = 1; j >= -1; j -= 2) + { + double ang = angle + j*i*(45. / 16); + double slope = AimLineAttack(ang, AXERANGE, t); + if (t.linetarget) + { + LineAttack(ang, AXERANGE, slope, damage, 'Melee', pufftype, true, t); + if (t.linetarget != null) + { + if (t.linetarget.bIsMonster || t.linetarget.player) + { + t.linetarget.Thrust(power, t.angleFromSource); + } + AdjustPlayerAngle(t); + + weapon.DepleteAmmo (weapon.bAltFire, false); + + if ((weapon.Ammo1 == null || weapon.Ammo1.Amount == 0) && + (!(weapon.bPrimary_Uses_Both) || + weapon.Ammo2 == null || weapon.Ammo2.Amount == 0)) + { + player.SetPsprite(PSP_WEAPON, weapon.FindState("EndAttack")); + } + return; + } + } + } + } + // didn't find any creatures, so try to strike any walls + self.weaponspecial = 0; + + double slope = AimLineAttack (angle, MELEERANGE); + LineAttack (angle, MELEERANGE, slope, damage, 'Melee', pufftype, true); + } } // Axe Puff ----------------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/fighterboss.txt b/wadsrc/static/zscript/hexen/fighterboss.txt index 8e862de30..9871cf571 100644 --- a/wadsrc/static/zscript/hexen/fighterboss.txt +++ b/wadsrc/static/zscript/hexen/fighterboss.txt @@ -19,8 +19,6 @@ class FighterBoss : Actor Obituary "$OB_FBOSS"; } - native void A_FighterAttack(); - States { Spawn: @@ -80,4 +78,22 @@ class FighterBoss : Actor FDTH V 4 Bright; Stop; } + + //============================================================================ + // + // A_FighterAttack + // + //============================================================================ + + void A_FighterAttack() + { + if (!target) return; + + SpawnMissileAngle("FSwordMissile", Angle + (45. / 4), 0); + SpawnMissileAngle("FSwordMissile", Angle + (45. / 8), 0); + SpawnMissileAngle("FSwordMissile", Angle, 0); + SpawnMissileAngle("FSwordMissile", Angle - (45. / 8), 0); + SpawnMissileAngle("FSwordMissile", Angle - (45. / 4), 0); + A_PlaySound ("FighterSwordFire", CHAN_WEAPON); + } } diff --git a/wadsrc/static/zscript/hexen/fighterfist.txt b/wadsrc/static/zscript/hexen/fighterfist.txt index 8428ebe7d..712055e6e 100644 --- a/wadsrc/static/zscript/hexen/fighterfist.txt +++ b/wadsrc/static/zscript/hexen/fighterfist.txt @@ -13,8 +13,6 @@ class FWeapFist : FighterWeapon Tag "$TAG_FWEAPFIST"; } - action native void A_FPunchAttack(); - States { Select: @@ -44,6 +42,85 @@ class FWeapFist : FighterWeapon FPCH E 10 Offset (0, 150); Goto Ready; } + + //============================================================================ + // + // TryPunch + // + // Returns true if an actor was punched, false if not. + // + //============================================================================ + + private action bool TryPunch(double angle, int damage, int power) + { + Class pufftype; + FTranslatedLineTarget t; + + double slope = AimLineAttack (angle, 2*MELEERANGE, t); + if (t.linetarget != null) + { + if (++weaponspecial >= 3) + { + damage <<= 1; + power *= 3; + pufftype = "HammerPuff"; + } + else + { + pufftype = "PunchPuff"; + } + LineAttack (angle, 2*MELEERANGE, slope, damage, 'Melee', pufftype, true, t); + if (t.linetarget != null) + { + // The mass threshold has been changed to CommanderKeen's value which has been used most often for 'unmovable' stuff. + if (t.linetarget.player != null || + (t.linetarget.Mass < 10000000 && (t.linetarget.bIsMonster))) + { + if (!t.linetarget.bDontThrust) + t.linetarget.Thrust(power, t.angleFromSource); + } + AdjustPlayerAngle(t); + return true; + } + } + return false; + } + + //============================================================================ + // + // A_FPunchAttack + // + //============================================================================ + + action void A_FPunchAttack() + { + if (player == null) + { + return; + } + + int damage = 40 + (random[FighterAtk]() & 15); + for (int i = 0; i < 16; i++) + { + if (TryPunch(angle + i*(45./16), damage, 2) || + TryPunch(angle - i*(45./16), damage, 2)) + { // hit something + if (weaponspecial >= 3) + { + weaponspecial = 0; + player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.FindState("Fire2")); + A_PlaySound ("*fistgrunt", CHAN_VOICE); + } + return; + } + } + // didn't find any creatures, so try to strike any walls + weaponspecial = 0; + + double slope = AimLineAttack (angle, MELEERANGE); + LineAttack (angle, MELEERANGE, slope, damage, 'Melee', "PunchPuff", true); + } + } // Punch puff --------------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/fighterhammer.txt b/wadsrc/static/zscript/hexen/fighterhammer.txt index f415cbb72..655e59379 100644 --- a/wadsrc/static/zscript/hexen/fighterhammer.txt +++ b/wadsrc/static/zscript/hexen/fighterhammer.txt @@ -3,6 +3,8 @@ class FWeapHammer : FighterWeapon { + const HAMMER_RANGE = 1.5 * MELEERANGE; + Default { +BLOODSPLATTER @@ -18,9 +20,6 @@ class FWeapHammer : FighterWeapon Tag "$TAG_FWEAPHAMMER"; } - action native void A_FHammerAttack(); - action native void A_FHammerThrow(); - States { Spawn: @@ -50,6 +49,87 @@ class FWeapHammer : FighterWeapon FHMR A 1; Goto Ready; } + + //============================================================================ + // + // A_FHammerAttack + // + //============================================================================ + + action void A_FHammerAttack() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + int damage = 60+(random[HammerAtk]() & 63); + for (int i = 0; i < 16; i++) + { + for (int j = 1; j >= -1; j -= 2) + { + double ang = angle + j*i*(45. / 32); + double slope = AimLineAttack(ang, HAMMER_RANGE, t, 0., ALF_CHECK3D); + if (t.linetarget != null) + { + LineAttack(ang, HAMMER_RANGE, slope, damage, 'Melee', "HammerPuff", true, t); + if (t.linetarget != null) + { + AdjustPlayerAngle(t); + if (t.linetarget.bIsMonster || t.linetarget.player) + { + t.linetarget.Thrust(10, t.angleFromSource); + } + weaponspecial = false; // Don't throw a hammer + return; + } + } + } + } + // didn't find any targets in meleerange, so set to throw out a hammer + double slope = AimLineAttack (angle, HAMMER_RANGE, null, 0., ALF_CHECK3D); + weaponspecial = (LineAttack (angle, HAMMER_RANGE, slope, damage, 'Melee', "HammerPuff", true) == null); + + // Don't spawn a hammer if the player doesn't have enough mana + if (player.ReadyWeapon == null || + !player.ReadyWeapon.CheckAmmo (player.ReadyWeapon.bAltFire ? + Weapon.AltFire : Weapon.PrimaryFire, false, true)) + { + weaponspecial = false; + } + } + + //============================================================================ + // + // A_FHammerThrow + // + //============================================================================ + + action void A_FHammerThrow() + { + if (player == null) + { + return; + } + + if (!weaponspecial) + { + return; + } + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire, false)) + return; + } + Actor mo = SpawnPlayerMissile ("HammerMissile"); + if (mo) + { + mo.special1 = 0; + } + } } // Hammer Missile ----------------------------------------------------------- @@ -76,7 +156,7 @@ class HammerMissile : Actor FHFX CDEFGH 2 Bright; Loop; Death: - FHFX I 3 Bright A_SetTranslucent(1,1); + FHFX I 3 Bright A_SetRenderStyle(1, STYLE_Add); FHFX J 3 Bright; FHFX K 3 Bright A_Explode (128, 128, 0); FHFX LM 3 Bright; diff --git a/wadsrc/static/zscript/hexen/fighterquietus.txt b/wadsrc/static/zscript/hexen/fighterquietus.txt index 7c94f7901..53388eece 100644 --- a/wadsrc/static/zscript/hexen/fighterquietus.txt +++ b/wadsrc/static/zscript/hexen/fighterquietus.txt @@ -97,8 +97,6 @@ class FWeapQuietus : FighterWeapon Tag "$TAG_FWEAPQUIETUS"; } - action native void A_FSwordAttack(); - States { Spawn: @@ -127,11 +125,40 @@ class FWeapQuietus : FighterWeapon FSRD B 1 Bright Offset (5, 40); Goto Ready; } + + //============================================================================ + // + // A_FSwordAttack + // + //============================================================================ + + action void A_FSwordAttack() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + SpawnPlayerMissile ("FSwordMissile", Angle + (45./4),0, 0, -10); + SpawnPlayerMissile ("FSwordMissile", Angle + (45./8),0, 0, -5); + SpawnPlayerMissile ("FSwordMissile", Angle ,0, 0, 0); + SpawnPlayerMissile ("FSwordMissile", Angle - (45./8),0, 0, 5); + SpawnPlayerMissile ("FSwordMissile", Angle - (45./4),0, 0, 10); + A_PlaySound ("FighterSwordFire", CHAN_WEAPON); + } + + } // Fighter Sword Missile ---------------------------------------------------- -class FSwordMissile : Actor native +class FSwordMissile : Actor { Default { @@ -146,8 +173,6 @@ class FSwordMissile : Actor native Obituary "$OB_MPFWEAPQUIETUS"; } - native void A_FSwordFlames(); - States { Spawn: @@ -164,6 +189,32 @@ class FSwordMissile : Actor native FSFX KLM 3 Bright; Stop; } + + override int DoSpecialDamage(Actor victim, int damage, Name damagetype) + { + if (victim.player) + { + damage -= damage >> 2; + } + return damage; + } + + //============================================================================ + // + // A_FSwordFlames + // + //============================================================================ + + void A_FSwordFlames() + { + for (int i = 1+(random[FSwordFlame]()&3); i; i--) + { + double xo = (random[FSwordFlame]() - 128) / 16.; + double yo = (random[FSwordFlame]() - 128) / 16.; + double zo = (random[FSwordFlame]() - 128) / 8.; + Spawn ("FSwordFlame", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + } + } } // Fighter Sword Flame ------------------------------------------------------ diff --git a/wadsrc/static/zscript/hexen/firedemon.txt b/wadsrc/static/zscript/hexen/firedemon.txt index b425ecb11..9ca12529f 100644 --- a/wadsrc/static/zscript/hexen/firedemon.txt +++ b/wadsrc/static/zscript/hexen/firedemon.txt @@ -4,7 +4,7 @@ class FireDemon : Actor { const FIREDEMON_ATTACK_RANGE = 64*8.; - int strafecount; + int fdstrafecount; Default { @@ -77,7 +77,7 @@ class FireDemon : Actor // Fire Demon AI // // special1 index into floatbob - // strafecount whether strafing or not + // fdstrafecount whether strafing or not //============================================================================ //============================================================================ @@ -125,7 +125,7 @@ class FireDemon : Actor } // Initialize fire demon - strafecount = 0; + fdstrafecount = 0; bJustAttacked = false; } @@ -188,13 +188,13 @@ class FireDemon : Actor } // Strafe - if (strafecount > 0) + if (fdstrafecount > 0) { - strafecount--; + fdstrafecount--; } else { - strafecount = 0; + fdstrafecount = 0; Vel.X = Vel.Y = 0; dist = Distance2D(target); if (dist < FIREDEMON_ATTACK_RANGE) @@ -207,7 +207,7 @@ class FireDemon : Actor else ang -= 90; Thrust(8, ang); - strafecount = 3; // strafe time + fdstrafecount = 3; // strafe time } } } @@ -215,7 +215,7 @@ class FireDemon : Actor FaceMovementDirection (); // Normal movement - if (!strafecount) + if (!fdstrafecount) { if (--movecount<0 || !MonsterMove ()) { diff --git a/wadsrc/static/zscript/hexen/flechette.txt b/wadsrc/static/zscript/hexen/flechette.txt index 6f7b3a818..fc9268c5b 100644 --- a/wadsrc/static/zscript/hexen/flechette.txt +++ b/wadsrc/static/zscript/hexen/flechette.txt @@ -10,8 +10,6 @@ class PoisonBag : Actor +NOBLOCKMAP +NOGRAVITY } - native void A_PoisonBagInit(); - States { Spawn: @@ -21,6 +19,21 @@ class PoisonBag : Actor PSBG C 1 A_PoisonBagInit; Stop; } + + //=========================================================================== + // + // A_PoisonBagInit + // + //=========================================================================== + + void A_PoisonBagInit() + { + Actor mo = Spawn("PoisonCloud", pos + (0, 0, 28), ALLOW_REPLACE); + if (mo) + { + mo.target = target; + } + } } // Fire Bomb (Flechette used by Mage) --------------------------------------- @@ -37,8 +50,6 @@ class FireBomb : Actor DeathSound "FlechetteExplode"; } - native void A_TimeBomb(); - States { Spawn: @@ -50,6 +61,13 @@ class FireBomb : Actor XPL1 BCDEF 4 Bright; Stop; } + + void A_TimeBomb() + { + AddZ(32, false); + A_SetRenderStyle(1., STYLE_Add); + A_Explode(); + } } // Throwing Bomb (Flechette used by Fighter) -------------------------------- @@ -69,9 +87,6 @@ class ThrowingBomb : Actor DeathSound "FlechetteExplode"; } - native void A_CheckThrowBomb(); - native void A_CheckThrowBomb2(); - States { Spawn: @@ -79,6 +94,7 @@ class ThrowingBomb : Actor THRW BCDE 3 A_CheckThrowBomb; THRW F 3 A_CheckThrowBomb2; Loop; + Tail: THRW G 6 A_CheckThrowBomb; THRW F 4 A_CheckThrowBomb; THRW H 6 A_CheckThrowBomb; @@ -97,11 +113,46 @@ class ThrowingBomb : Actor CFCF Z 3 Bright; Stop; } + + //=========================================================================== + // + // A_CheckThrowBomb + // + //=========================================================================== + + void A_CheckThrowBomb() + { + if (--health <= 0) + { + SetStateLabel("Death"); + } + } + + //=========================================================================== + // + // A_CheckThrowBomb2 + // + //=========================================================================== + + void A_CheckThrowBomb2() + { + // [RH] Check using actual velocity, although the vel.z < 2 check still stands + if (Vel.Z < 2 && Vel.Length() < 1.5) + { + SetStateLabel("Tail"); + SetZ(floorz); + Vel.Z = 0; + ClearBounce(); + bMissile = false; + } + A_CheckThrowBomb(); + } + } // Poison Bag Artifact (Flechette) ------------------------------------------ -class ArtiPoisonBag : Inventory native +class ArtiPoisonBag : Inventory { Default { @@ -120,64 +171,261 @@ class ArtiPoisonBag : Inventory native PSBG A -1; Stop; } + + //============================================================================ + // + // AArtiPoisonBag :: BeginPlay + // + //============================================================================ + + override void BeginPlay () + { + Super.BeginPlay (); + // If a subclass's specific icon is not defined, let it use the base class's. + if (!Icon.isValid()) + { + Icon = GetDefaultByType("ArtiPoisonBag").Icon; + } + } + + //============================================================================ + // + // GetFlechetteType + // + //============================================================================ + + private class GetFlechetteType(Actor other) + { + class spawntype = null; + PlayerPawn pp = PlayerPawn(other); + if (pp) + { + spawntype = pp.FlechetteType; + } + if (spawntype == null) + { + // default fallback if nothing valid defined. + spawntype = "ArtiPoisonBag3"; + } + return spawntype; + } + + //============================================================================ + // + // AArtiPoisonBag :: HandlePickup + // + //============================================================================ + + override bool HandlePickup (Inventory item) + { + // Only do special handling when picking up the base class + if (item.GetClass() != "ArtiPoisonBag") + { + 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.bPickupGood = true; + } + return true; + } + return false; + } + + //============================================================================ + // + // AArtiPoisonBag :: CreateCopy + // + //============================================================================ + + override Inventory CreateCopy (Actor other) + { + // Only the base class gets special handling + if (GetClass() != "ArtiPoisonBag") + { + return Super.CreateCopy (other); + } + + class spawntype = GetFlechetteType(other); + Inventory copy = Inventory(Spawn (spawntype)); + copy.Amount = Amount; + copy.MaxAmount = MaxAmount; + GoAwayAndDie (); + return copy; + } } // Poison Bag 1 (The Cleric's) ---------------------------------------------- -class ArtiPoisonBag1 : ArtiPoisonBag native +class ArtiPoisonBag1 : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB1"; Tag "$TAG_ARTIPOISONBAG1"; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("PoisonBag", Owner.Vec3Offset( + 16 * cos(Owner.angle), + 24 * sin(Owner.angle), + -Owner.Floorclip + 8), ALLOW_REPLACE); + if (mo) + { + mo.target = Owner; + return true; + } + return false; + } + } // Poison Bag 2 (The Mage's) ------------------------------------------------ -class ArtiPoisonBag2 : ArtiPoisonBag native +class ArtiPoisonBag2 : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB2"; Tag "$TAG_ARTIPOISONBAG2"; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("FireBomb", Owner.Vec3Offset( + 16 * cos(Owner.angle), + 24 * sin(Owner.angle), + -Owner.Floorclip + 8), ALLOW_REPLACE); + if (mo) + { + mo.target = Owner; + return true; + } + return false; + } + } // Poison Bag 3 (The Fighter's) --------------------------------------------- -class ArtiPoisonBag3 : ArtiPoisonBag native +class ArtiPoisonBag3 : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB3"; Tag "$TAG_ARTIPOISONBAG3"; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("ThrowingBomb", Owner.Pos + (0,0,35. - Owner.Floorclip + (Owner.player? Owner.player.crouchoffset : 0)), ALLOW_REPLACE); + if (mo) + { + mo.angle = Owner.angle + (((random[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.ang, 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 self with a proper trajectory, we + // aim the projectile ~20 degrees higher than we're looking at and increase the + // speed we fire at accordingly. + double orgpitch = -Owner.Pitch; + double modpitch = clamp(-Owner.Pitch + 20, -89., 89.); + double ang = mo.angle; + double speed = (mo.Speed, 4.).Length(); + double xyscale = speed * cos(modpitch); + + mo.Vel.Z = speed * sin(modpitch); + mo.Vel.X = xyscale * cos(ang) + Owner.Vel.X / 2; + mo.Vel.Y = xyscale * sin(ang) + Owner.Vel.Y / 2; + mo.AddZ(mo.Speed * sin(modpitch)); + + mo.target = Owner; + mo.tics -= random[PoisonBag]()&3; + mo.CheckMissileSpawn(Owner.radius); + return true; + } + return false; + } + + } // Poison Bag 4 (Custom Giver) ---------------------------------------------- -class ArtiPoisonBagGiver : ArtiPoisonBag native +class ArtiPoisonBagGiver : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB4"; } + + override bool Use (bool pickup) + { + Class missiletype = MissileName; + if (missiletype != null) + { + Actor mo = Spawn (missiletype, Owner.Pos, ALLOW_REPLACE); + if (mo != null) + { + Inventory inv = Inventory(mo); + if (inv && inv.CallTryPickup(Owner)) return true; + mo.Destroy(); // Destroy if not inventory or couldn't be picked up + } + } + return false; + } + } // Poison Bag 5 (Custom Thrower) -------------------------------------------- -class ArtiPoisonBagShooter : ArtiPoisonBag native +class ArtiPoisonBagShooter : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB5"; } + + override bool Use (bool pickup) + { + Class missiletype = MissileName; + if (missiletype != null) + { + Actor mo = Owner.SpawnPlayerMissile(missiletype); + if (mo != null) + { + // automatic handling of seeker missiles + if (mo.bSeekerMissile) + { + mo.tracer = Owner.target; + } + return true; + } + } + return false; + } + } // Poison Cloud ------------------------------------------------------------- -class PoisonCloud : Actor native +class PoisonCloud : Actor { Default { @@ -193,9 +441,6 @@ class PoisonCloud : Actor native DamageType "PoisonCloud"; } - native void A_PoisonBagDamage(); - native void A_PoisonBagCheck(); - States { Spawn: @@ -210,6 +455,97 @@ class PoisonCloud : Actor native PSBG FD 6; Stop; } + + //=========================================================================== + // + // + // + //=========================================================================== + + override void BeginPlay () + { + Vel.X = MinVel; // missile objects must move to impact other objects + special1 = 24+(random[PoisonCloud]() & 7); + special2 = 0; + } + + //=========================================================================== + // + // + // + //=========================================================================== + + override int DoSpecialDamage (Actor victim, int damage, Name 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) + { + damage = 15 + (random[PoisonCloud]() & 15); + if (mate) + { + damage = (int)(damage * level.teamdamage); + } + // Handle passive damage modifiers (e.g. PowerProtection) + damage = victim.GetModifiedDamage(damagetype, damage, true); + // Modify with damage factors + damage = victim.ApplyDamageFactor(damagetype, damage); + if (damage > 0) + { + victim.player.PoisonDamage (self, 15 + (random[PoisonCloud]() & 15), false); // Don't play painsound + + // If successful, play the poison sound. + if (victim.player.PoisonPlayer (self, self.target, 50)) + victim.A_PlaySound ("*poison", CHAN_VOICE); + } + } + return -1; + } + else if (!victim.bIsMonster) + { // only damage monsters/players with the poison cloud + return -1; + } + return damage; + } + + //=========================================================================== + // + // A_PoisonBagCheck + // + //=========================================================================== + + void A_PoisonBagCheck() + { + if (--special1 <= 0) + { + SetStateLabel("Death"); + } + } + + //=========================================================================== + // + // A_PoisonBagDamage + // + //=========================================================================== + + void A_PoisonBagDamage() + { + A_Explode(4, 40); + AddZ(BobSin(special2) / 16); + special2 = (special2 + 1) & 63; + } } // Poison Shroom ------------------------------------------------------------ @@ -233,8 +569,6 @@ class ZPoisonShroom : PoisonBag DeathSound "PoisonShroomDeath"; } - native void A_PoisonShroom(); - States { Spawn: @@ -250,5 +584,16 @@ class ZPoisonShroom : PoisonBag SHRM F -1; Stop; } + + //=========================================================================== + // + // A_PoisonShroom + // + //=========================================================================== + + void A_PoisonShroom() + { + tics = 128 + (random[PoisonShroom]() << 1); + } } diff --git a/wadsrc/static/zscript/hexen/flies.txt b/wadsrc/static/zscript/hexen/flies.txt index 464e6e9d9..d9800552b 100644 --- a/wadsrc/static/zscript/hexen/flies.txt +++ b/wadsrc/static/zscript/hexen/flies.txt @@ -15,9 +15,6 @@ class LittleFly : Actor ActiveSound "FlyBuzz"; } - native void A_FlySearch(); - native void A_FlyBuzz(); - States { Spawn: @@ -27,4 +24,107 @@ class LittleFly : Actor AFLY ABCD 3 A_FlyBuzz; Loop; } + + //=========================================================================== + // + // FindCorpse + // + // Finds a corpse to buzz around. We can't use a blockmap check because + // corpses generally aren't linked into the blockmap. + // + //=========================================================================== + + private Actor FindCorpse(sector sec, int recurselimit) + { + Actor fallback = null; + sec.validcount = validcount; + + // Search the current sector + for (Actor check = sec.thinglist; check != null; check = check.snext) + { + if (check == self) + continue; + if (!check.bCorpse) + continue; + if (!CheckSight(check)) + continue; + fallback = check; + if (random[Fly](0,1)) // 50% chance to try to pick a different corpse + continue; + return check; + } + if (--recurselimit <= 0 || (fallback != null && random[Fly](0,1))) + { + return fallback; + } + // Try neighboring sectors + for (int i = 0; i < sec.linecount; ++i) + { + line ln = sec.lines[i]; + sector sec2 = (ln.frontsector == sec) ? ln.backsector : ln.frontsector; + if (sec2 != null && sec2.validcount != validcount) + { + Actor neighbor = FindCorpse(sec2, recurselimit); + if (neighbor != null) + { + return neighbor; + } + } + } + return fallback; + } + + void 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. + validcount++; + Actor other = FindCorpse(CurSector, 5); + if (other != null) + { + target = other; + SetStateLabel("Buzz"); + } + } + + void A_FlyBuzz() + { + Actor targ = target; + + if (targ == null || !targ.bCorpse || random[Fly]() < 5) + { + SetIdle(); + return; + } + + Angle = AngleTo(targ); + args[0]++; + if (!TryMove(Pos.XY + AngleToVector(angle, 6), true)) + { + SetIdle(true); + return; + } + if (args[0] & 2) + { + Vel.X += (random[Fly]() - 128) / 512.; + Vel.Y += (random[Fly]() - 128) / 512.; + } + int zrand = random[Fly](); + if (targ.pos.Z + 5. < pos.z && zrand > 150) + { + zrand = -zrand; + } + Vel.Z = zrand / 512.; + if (random[Fly]() < 40) + { + A_PlaySound(ActiveSound, CHAN_VOICE, 0.5f, false, ATTN_STATIC); + } + } } diff --git a/wadsrc/static/zscript/hexen/fog.txt b/wadsrc/static/zscript/hexen/fog.txt index 93b5e9593..276cf2d73 100644 --- a/wadsrc/static/zscript/hexen/fog.txt +++ b/wadsrc/static/zscript/hexen/fog.txt @@ -1,3 +1,15 @@ +//========================================================================== +// 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 +// +//========================================================================== // Fog Spawner -------------------------------------------------------------- @@ -11,7 +23,6 @@ class FogSpawner : Actor +INVISIBLE } - native void A_FogSpawn(); States { @@ -19,6 +30,45 @@ class FogSpawner : Actor TNT1 A 20 A_FogSpawn; Loop; } + + //========================================================================== + // + // A_FogSpawn + // + //========================================================================== + + void A_FogSpawn() + { + if (special1-- > 0) + { + return; + } + special1 = args[2]; // Reset frequency count + + class fog; + switch (random[FogSpawn](0,2)) + { + default: + case 0: fog = "FogPatchSmall"; break; + case 1: fog = "FogPatchMedium"; break; + case 2: fog = "FogPatchLarge"; break; + } + Actor mo = Spawn (fog, Pos, ALLOW_REPLACE); + + if (mo) + { + int delta = args[1]; + if (delta == 0) delta = 1; + mo.angle = angle + (((random[FogSpawn]() % delta) - (delta >> 1)) * (360 / 256.)); + mo.target = self; + if (args[0] < 1) args[0] = 1; + mo.args[0] = (random[FogSpawn]() % (args[0]))+1; // Random speed + mo.args[3] = args[3]; // Set lifetime + mo.args[4] = 1; // Set to moving + mo.WeaveIndexZ = random[FogSpawn](0, 63); + } + } + } // Small Fog Patch ---------------------------------------------------------- @@ -34,7 +84,6 @@ class FogPatchSmall : Actor Alpha 0.6; } - native void A_FogMove(); States { @@ -45,6 +94,37 @@ class FogPatchSmall : Actor FOGS E 5; Stop; } + + //========================================================================== + // + // A_FogMove + // + //========================================================================== + + void A_FogMove() + { + double speed = args[0]; + int weaveindex; + + if (!args[4]) + { + return; + } + + if (args[3]-- <= 0) + { + SetStateLabel ('Death', true); + return; + } + + if ((args[3] % 4) == 0) + { + weaveindex = WeaveIndexZ; + AddZ(BobSin(weaveindex) / 2); + WeaveIndexZ = (weaveindex + 1) & 63; + } + VelFromAngle(speed); + } } // Medium Fog Patch --------------------------------------------------------- @@ -76,3 +156,4 @@ class FogPatchLarge : FogPatchMedium Goto Super::Death; } } + diff --git a/wadsrc/static/zscript/hexen/healingradius.txt b/wadsrc/static/zscript/hexen/healingradius.txt index 94d8e2dd5..52b87c15b 100644 --- a/wadsrc/static/zscript/hexen/healingradius.txt +++ b/wadsrc/static/zscript/hexen/healingradius.txt @@ -1,8 +1,10 @@ // Healing Radius Artifact -------------------------------------------------- -class ArtiHealingRadius : Inventory native +class ArtiHealingRadius : Inventory { + const HEAL_RADIUS_DIST = 255.; + Default { +COUNTITEM @@ -21,6 +23,70 @@ class ArtiHealingRadius : Inventory native Spawn: HRAD ABCDEFGHIJKLMNOP 4 Bright; Loop; + } + + override bool Use (bool pickup) + { + bool effective = false; + Name mode = 'Health'; + + PlayerPawn pp = PlayerPawn(Owner); + if (pp) mode = pp.HealingRadiusType; + + for (int i = 0; i < MAXPLAYERS; ++i) + { + PlayerPawn mo = players[i].mo; + if (playeringame[i] && mo != null && mo.health > 0 && 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 'Armor': + for (int j = 0; j < 4; ++j) + { + HexenArmor armor = HexenArmor(Spawn("HexenArmor")); + armor.health = j; + armor.Amount = 1; + if (!armor.CallTryPickup (mo)) + { + armor.Destroy (); + } + else + { + gotsome = true; + } + } + break; + + case 'Mana': + { + int amount = 50 + (random[HealRadius]() % 50); + + if (mo.GiveAmmo ("Mana1", amount) || + mo.GiveAmmo ("Mana2", amount)) + { + gotsome = true; + } + break; + } + + default: + //case NAME_Health: + gotsome = mo.GiveBody (50 + (random[HealRadius]() % 50)); + break; + } + if (gotsome) + { + mo.A_PlaySound ("MysticIncant", CHAN_AUTO); + effective=true; + } + } + } + return effective; + } + } diff --git a/wadsrc/static/zscript/hexen/heresiarch.txt b/wadsrc/static/zscript/hexen/heresiarch.txt index 7d3ba63a5..9cfdde19d 100644 --- a/wadsrc/static/zscript/hexen/heresiarch.txt +++ b/wadsrc/static/zscript/hexen/heresiarch.txt @@ -1,8 +1,53 @@ // The Heresiarch him/itself ------------------------------------------------ -class Heresiarch native +//============================================================================ +// +// 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) +//============================================================================ + +class Heresiarch : Actor { + + const SORCBALL_INITIAL_SPEED = 7; + const SORCBALL_TERMINAL_SPEED = 25; + const SORCBALL_SPEED_ROTATIONS = 5; + const SORC_DEFENSE_TIME = 255; + const SORC_DEFENSE_HEIGHT = 45; + const BOUNCE_TIME_UNIT = (35/2); + const SORCFX4_RAPIDFIRE_TIME = (6*3); // 3 seconds + const SORCFX4_SPREAD_ANGLE = 20; + + enum ESorc + { + SORC_DECELERATE, + SORC_ACCELERATE, + SORC_STOPPING, + SORC_FIRESPELL, + SORC_STOPPED, + SORC_NORMAL, + SORC_FIRING_SPELL + } + + const BALL1_ANGLEOFFSET = 0.; + const BALL2_ANGLEOFFSET = 120.; + const BALL3_ANGLEOFFSET = 240.; + + double BallAngle; + class StopBall; + Default { Health 5000; @@ -27,11 +72,6 @@ class Heresiarch native Obituary "$OB_HERESIARCH"; } - native void A_SorcSpinBalls(); - native void A_SpeedBalls(); - native void A_SorcBossAttack(); - native void A_SpawnFizzle(); - States { Spawn: @@ -71,11 +111,153 @@ class Heresiarch native SORC Z -1 Bright; Stop; } + + void Die (Actor source, Actor 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) + { + ACS_Execute(script, 0); + } + } + + //============================================================================ + // + // A_StopBalls + // + // Instant stop when rotation gets to ball in special2 + // self is sorcerer + // + //============================================================================ + + void A_StopBalls() + { + int chance = random[Heresiarch](); + args[3] = SORC_STOPPING; // stopping mode + args[1] = 0; // Reset rotation counter + + if ((args[0] <= 0) && (chance < 200)) + { + StopBall = "SorcBall2"; // Blue + } + else if((health < (SpawnHealth() >> 1)) && (chance < 200)) + { + StopBall = "SorcBall3"; // Green + } + else + { + StopBall = "SorcBall1"; // Yellow + } + } + + //============================================================================ + // + // A_SorcSpinBalls + // + // Spawn spinning balls above head - actor is sorcerer + //============================================================================ + + void A_SorcSpinBalls() + { + A_SlowBalls(); + args[0] = 0; // Currently no defense + args[3] = SORC_NORMAL; + args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed + BallAngle = 1.; + + Vector3 ballpos = (pos.xy, -Floorclip + Height); + + Actor 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; + } + + + //============================================================================ + // + // A_SpeedBalls + // + // Set balls to speed mode - self is sorcerer + // + //============================================================================ + + void A_SpeedBalls() + { + args[3] = SORC_ACCELERATE; // speed mode + args[2] = SORCBALL_TERMINAL_SPEED; // target speed + } + + + //============================================================================ + // + // A_SlowBalls + // + // Set balls to slow mode - actor is sorcerer + // + //============================================================================ + + void A_SlowBalls() + { + args[3] = SORC_DECELERATE; // slow mode + args[2] = SORCBALL_INITIAL_SPEED; // target speed + } + + //============================================================================ + // + // A_SorcBossAttack + // + // Resume ball spinning + // + //============================================================================ + + void A_SorcBossAttack() + { + args[3] = SORC_ACCELERATE; + args[2] = SORCBALL_INITIAL_SPEED; + } + + //============================================================================ + // + // A_SpawnFizzle + // + // spell cast magic fizzle + // + //============================================================================ + + void A_SpawnFizzle() + { + Vector3 pos = Vec3Angle(5., Angle, -Floorclip + Height / 2. ); + for (int ix=0; ix<5; ix++) + { + Actor mo = Spawn("SorcSpark1", pos, ALLOW_REPLACE); + if (mo) + { + double rangle = Angle + (random[Heresiarch]() % 5) * (4096 / 360.); + mo.Vel.X = (random[Heresiarch]() % speed) * cos(rangle); + mo.Vel.Y = (random[Heresiarch]() % speed) * sin(rangle); + mo.Vel.Z = 2; + } + } + } + + } // Base class for the balls flying around the Heresiarch's head ------------- -class SorcBall native +class SorcBall : Actor { Default { @@ -93,20 +275,293 @@ class SorcBall native DeathSound "SorcererBigBallExplode"; } - native void A_SorcBallOrbit(); - native void A_SorcBallPop(); - native void A_BounceCheck (); + double OldAngle, AngleOffset; + + //============================================================================ + // + // SorcBall::DoFireSpell + // + //============================================================================ + + virtual void DoFireSpell () + { + CastSorcererSpell (); + target.args[3] = Heresiarch.SORC_STOPPED; + } + + virtual void SorcUpdateBallAngle () + { + } + + override bool SpecialBlastHandling (Actor source, double strength) + { // don't blast sorcerer balls + return false; + } + + //============================================================================ + // + // ASorcBall::CastSorcererSpell + // + // Make noise and change the parent sorcerer's animation + // + //============================================================================ + + virtual void CastSorcererSpell () + { + target.A_PlaySound ("SorcererSpellCast", CHAN_VOICE); + + // Put sorcerer into throw spell animation + if (target.health > 0) + target.SetStateLabel ("Attack2"); + } + + //============================================================================ + // + // A_SorcBallOrbit + // + // - actor is ball + //============================================================================ + + void A_SorcBallOrbit() + { + // [RH] If no parent, then die instead of crashing + if (target == null || target.health <= 0) + { + SetStateLabel ("Pain"); + return; + } + + int mode = target.args[3]; + Heresiarch parent = Heresiarch(target); + double dist = parent.radius - (radius*2); + + double prevangle = OldAngle; + double baseangle = parent.BallAngle; + double curangle = baseangle + AngleOffset; + + angle = curangle; + + switch (mode) + { + case Heresiarch.SORC_NORMAL: // Balls rotating normally + SorcUpdateBallAngle (); + break; + + case Heresiarch.SORC_DECELERATE: // Balls decelerating + A_DecelBalls(); + SorcUpdateBallAngle (); + break; + + case Heresiarch.SORC_ACCELERATE: // Balls accelerating + A_AccelBalls(); + SorcUpdateBallAngle (); + break; + + case Heresiarch.SORC_STOPPING: // Balls stopping + if ((parent.StopBall == GetClass()) && + (parent.args[1] > Heresiarch.SORCBALL_SPEED_ROTATIONS) && + absangle(curangle, parent.angle) < 42.1875) + { + // Can stop now + target.args[3] = Heresiarch.SORC_FIRESPELL; + target.args[4] = 0; + // Set angle so self angle == sorcerer angle + parent.BallAngle = parent.angle - AngleOffset; + } + else + { + SorcUpdateBallAngle (); + } + break; + + case Heresiarch.SORC_FIRESPELL: // Casting spell + if (parent.StopBall == GetClass()) + { + // Put sorcerer into special throw spell anim + if (parent.health > 0) + parent.SetStateLabel("Attack1"); + + DoFireSpell (); + } + break; + + case Heresiarch.SORC_FIRING_SPELL: + if (parent.StopBall == GetClass()) + { + if (special2-- <= 0) + { + // Done rapid firing + parent.args[3] = Heresiarch.SORC_STOPPED; + // Back to orbit balls + if (parent.health > 0) + parent.SetStateLabel("Attack2"); + } + else + { + // Do rapid fire spell + A_SorcOffense2(); + } + } + break; + + default: + break; + } + + // The comparison here depends on binary angle semantics and cannot be done in floating point. + // It also requires very exact conversion that must be done natively. + if (BAM(curangle) < BAM(prevangle) && (parent.args[4] == Heresiarch.SORCBALL_TERMINAL_SPEED)) + { + parent.args[1]++; // Bump rotation counter + // Completed full rotation - make woosh sound + A_PlaySound ("SorcererBallWoosh", CHAN_BODY); + } + OldAngle = curangle; // Set previous angle + + Vector3 pos = parent.Vec3Angle(dist, curangle, -parent.Floorclip + parent.Height); + SetOrigin (pos, true); + floorz = parent.floorz; + ceilingz = parent.ceilingz; + } + + //============================================================================ + // + // A_SorcOffense2 + // + // Actor is ball + // + //============================================================================ + + void A_SorcOffense2() + { + Actor parent = target; + Actor dest = parent.target; + + // [RH] If no enemy, then don't try to shoot. + if (dest == null) + { + return; + } + + int index = args[4]; + args[4] = (args[4] + 15) & 255; + double delta = sin(index * (360 / 256.f)) * Heresiarch.SORCFX4_SPREAD_ANGLE; + + double ang1 = Angle + delta; + Actor mo = parent.SpawnMissileAngle("SorcFX4", ang1, 0); + if (mo) + { + mo.special2 = 35*5/2; // 5 seconds + double dist = mo.DistanceBySpeed(dest, mo.Speed); + mo.Vel.Z = (dest.pos.z - mo.pos.z) / dist; + } + } + + //============================================================================ + // + // A_AccelBalls + // + // Increase ball orbit speed - actor is ball + // + //============================================================================ + + void A_AccelBalls() + { + Heresiarch sorc = Heresiarch(target); + + if (sorc.args[4] < sorc.args[2]) + { + sorc.args[4]++; + } + else + { + sorc.args[3] = Heresiarch.SORC_NORMAL; + if (sorc.args[4] >= Heresiarch.SORCBALL_TERMINAL_SPEED) + { + // Reached terminal velocity - stop balls + sorc.A_StopBalls(); + } + } + } + + //============================================================================ + // + // A_DecelBalls + // + // Decrease ball orbit speed - actor is ball + // + //============================================================================ + + void A_DecelBalls() + { + Actor sorc = target; + + if (sorc.args[4] > sorc.args[2]) + { + sorc.args[4]--; + } + else + { + sorc.args[3] = Heresiarch.SORC_NORMAL; + } + } + + void A_SorcBallExplode() { - bNOBOUNCESOUND = true; + bNoBounceSound = true; A_Explode(255, 255); } + + //============================================================================ + // + // A_SorcBallPop + // + // Ball death - bounce away in a random direction + // + //============================================================================ + + void A_SorcBallPop() + { + A_PlaySound ("SorcererBallPop", CHAN_BODY, 1, false, ATTN_NONE); + bNoGravity = false; + Gravity = 1. / 8; + + Vel.X = ((random[Heresiarch]()%10)-5); + Vel.Y = ((random[Heresiarch]()%10)-5); + Vel.Z = (2+(random[Heresiarch]()%3)); + args[4] = Heresiarch.BOUNCE_TIME_UNIT; // Bounce time unit + args[3] = 5; // Bounce time in seconds + } + + //============================================================================ + // + // A_BounceCheck + // + //============================================================================ + + void A_BounceCheck () + { + if (args[4]-- <= 0) + { + if (args[3]-- <= 0) + { + SetStateLabel("Death"); + A_PlaySound ("SorcererBigBallExplode", CHAN_BODY, 1, false, ATTN_NONE); + } + else + { + args[4] = Heresiarch.BOUNCE_TIME_UNIT; + } + } + } + } // First ball (purple) - fires projectiles ---------------------------------- -class SorcBall1 : SorcBall native +class SorcBall1 : SorcBall { States { @@ -123,12 +578,90 @@ class SorcBall1 : SorcBall native SBS4 FGH 6; Stop; } + + override void BeginPlay () + { + Super.BeginPlay (); + AngleOffset = Heresiarch.BALL1_ANGLEOFFSET; + A_Log("Ball1 begins " .. AngleOffset); + } + + //============================================================================ + // + // SorcBall1::CastSorcererSpell + // + // Offensive + // + //============================================================================ + + override void CastSorcererSpell () + { + Super.CastSorcererSpell (); + + Actor parent = target; + + double ang1 = Angle + 70; + double ang2 = Angle - 70; + Class cls = "SorcFX1"; + Actor mo = parent.SpawnMissileAngle (cls, ang1, 0); + if (mo) + { + mo.target = parent; + mo.tracer = parent.target; + mo.args[4] = Heresiarch.BOUNCE_TIME_UNIT; + mo.args[3] = 15; // Bounce time in seconds + } + mo = parent.SpawnMissileAngle (cls, ang2, 0); + if (mo) + { + mo.target = parent; + mo.tracer = parent.target; + mo.args[4] = Heresiarch.BOUNCE_TIME_UNIT; + mo.args[3] = 15; // Bounce time in seconds + } + } + + + //============================================================================ + // + // ASorcBall1::SorcUpdateBallAngle + // + // Update angle if first ball + //============================================================================ + + override void SorcUpdateBallAngle () + { + (Heresiarch(target)).BallAngle += target.args[4]; + } + + //============================================================================ + // + // SorcBall1::DoFireSpell + // + //============================================================================ + + override void DoFireSpell () + { + if (random[Heresiarch]() < 200) + { + target.A_PlaySound ("SorcererSpellCast", CHAN_VOICE, 1, false, ATTN_NONE); + special2 = Heresiarch.SORCFX4_RAPIDFIRE_TIME; + args[4] = 128; + target.args[3] = Heresiarch.SORC_FIRING_SPELL; + } + else + { + Super.DoFireSpell (); + } + } + + } // Second ball (blue) - generates the shield -------------------------------- -class SorcBall2 : SorcBall native +class SorcBall2 : SorcBall { States { @@ -145,11 +678,40 @@ class SorcBall2 : SorcBall native SBS3 FGH 6; Stop; } + + override void BeginPlay () + { + Super.BeginPlay (); + AngleOffset = Heresiarch.BALL2_ANGLEOFFSET; + A_Log("Ball2 begins " .. AngleOffset); + } + + //============================================================================ + // + // ASorcBall2::CastSorcererSpell + // + // Defensive + // + //============================================================================ + + override void CastSorcererSpell () + { + Super.CastSorcererSpell (); + + Actor parent = target; + Actor mo = Spawn("SorcFX2", Pos + (0, 0, parent.Floorclip + Heresiarch.SORC_DEFENSE_HEIGHT), ALLOW_REPLACE); + bReflective = true; + bInvulnerable = true; + parent.args[0] = Heresiarch.SORC_DEFENSE_TIME; + if (mo) mo.target = parent; + } + + } // Third ball (green) - summons Bishops ------------------------------------- -class SorcBall3 : SorcBall native +class SorcBall3 : SorcBall { States { @@ -166,6 +728,47 @@ class SorcBall3 : SorcBall native SBS3 FGH 6; Stop; } + + override void BeginPlay () + { + Super.BeginPlay (); + AngleOffset = Heresiarch.BALL3_ANGLEOFFSET; + A_Log("Ball3 begins " .. AngleOffset); + } + + //============================================================================ + // + // ASorcBall3::CastSorcererSpell + // + // Reinforcements + // + //============================================================================ + + override void CastSorcererSpell () + { + Actor mo; + Super.CastSorcererSpell (); + Actor parent = target; + + double ang1 = Angle - 45; + double ang2 = Angle + 45; + Class cls = "SorcFX3"; + if (health < (SpawnHealth()/3)) + { // Spawn 2 at a time + mo = parent.SpawnMissileAngle(cls, ang1, 4.); + if (mo) mo.target = parent; + mo = parent.SpawnMissileAngle(cls, ang2, 4.); + if (mo) mo.target = parent; + } + else + { + if (random[Heresiarch]() < 128) ang1 = ang2; + mo = parent.SpawnMissileAngle(cls, ang1, 4.); + if (mo) mo.target = parent; + } + } + + } @@ -191,8 +794,6 @@ class SorcFX1 : Actor DeathSound "SorcererHeadScream"; } - native void A_SorcFX1Seek(); - States { Spawn: @@ -204,6 +805,32 @@ class SorcFX1 : Actor FHFX SS 6 Bright; Stop; } + + //============================================================================ + // + // A_SorcFX1Seek + // + // Yellow spell - offense + // + //============================================================================ + + void A_SorcFX1Seek() + { + if (args[4]-- <= 0) + { + if (args[3]-- <= 0) + { + SetStateLabel("Death"); + A_PlaySound ("SorcererHeadScream", CHAN_BODY, 1, false, ATTN_NONE); + } + else + { + args[4] = Heresiarch.BOUNCE_TIME_UNIT; + } + } + A_SeekerMissile(2, 6); + } + } @@ -221,9 +848,6 @@ class SorcFX2 : Actor +NOTELEPORT } - native void A_SorcFX2Split(); - native void A_SorcFX2Orbit (); - states { Spawn: @@ -237,6 +861,106 @@ class SorcFX2 : Actor SBS2 A 10; Stop; } + + //============================================================================ + // + // A_SorcFX2Split + // + // Blue spell - defense + // + //============================================================================ + // + // FX2 Variables + // specialf1 current angle + // special2 + // args[0] 0 = CW, 1 = CCW + // args[1] + //============================================================================ + + // Split ball in two + void A_SorcFX2Split() + { + Actor mo = Spawn(GetClass(), Pos, NO_REPLACE); + if (mo) + { + mo.target = target; + mo.args[0] = 0; // CW + mo.specialf1 = Angle; // Set angle + mo.SetStateLabel("Orbit"); + } + mo = Spawn(GetClass(), Pos, NO_REPLACE); + if (mo) + { + mo.target = target; + mo.args[0] = 1; // CCW + mo.specialf1 = Angle; // Set angle + mo.SetStateLabel("Orbit"); + } + Destroy (); + } + + //============================================================================ + // + // A_SorcFX2Orbit + // + // Orbit FX2 about sorcerer + // + //============================================================================ + + void A_SorcFX2Orbit () + { + Actor parent = target; + + // [RH] If no parent, then disappear + if (parent == null) + { + Destroy(); + return; + } + + double dist = parent.radius; + + if ((parent.health <= 0) || // Sorcerer is dead + (!parent.args[0])) // Time expired + { + SetStateLabel("Death"); + parent.args[0] = 0; + parent.bReflective = false; + parent.bInvulnerable = false; + } + + if (args[0] && (parent.args[0]-- <= 0)) // Time expired + { + SetStateLabel("Death"); + parent.args[0] = 0; + parent.bReflective = false; + } + + Vector3 posi; + // Move to new position based on angle + if (args[0]) // Counter clock-wise + { + specialf1 += 10; + angle = specialf1; + posi = parent.Vec3Angle(dist, angle, parent.Floorclip + Heresiarch.SORC_DEFENSE_HEIGHT); + posi.Z += 15 * cos(angle); + // Spawn trailer + Spawn("SorcFX2T1", pos, ALLOW_REPLACE); + } + else // Clock wise + { + specialf1 -= 10; + angle = specialf1; + posi = parent.Vec3Angle(dist, angle, parent.Floorclip + Heresiarch.SORC_DEFENSE_HEIGHT); + posi.Z += 20 * sin(angle); + // Spawn trailer + Spawn("SorcFX2T1", pos, ALLOW_REPLACE); + } + + SetOrigin (posi, true); + floorz = parent.floorz; + ceilingz = parent.ceilingz; + } } // The translucent trail behind SorcFX2 ------------------------------------- @@ -271,9 +995,6 @@ class SorcFX3 : Actor SeeSound "SorcererBishopSpawn"; } - native void A_SpawnBishop(); - native void A_SorcererBishopEntry(); - States { Spawn: @@ -287,6 +1008,45 @@ class SorcFX3 : Actor BISH G 3 A_SpawnBishop; Stop; } + + //============================================================================ + // + // A_SorcererBishopEntry + // + //============================================================================ + + void A_SorcererBishopEntry() + { + Spawn("SorcFX3Explosion", Pos, ALLOW_REPLACE); + A_PlaySound (SeeSound, CHAN_VOICE); + } + + //============================================================================ + // + // A_SpawnBishop + // + // Green spell - spawn bishops + // + //============================================================================ + + void A_SpawnBishop() + { + Actor mo = Spawn("Bishop", Pos, ALLOW_REPLACE); + if (mo) + { + if (!mo.TestMobjLocation()) + { + mo.ClearCounters(); + mo.Destroy (); + } + else if (target != null) + { // [RH] Make the new bishops inherit the Heriarch's target + mo.CopyFriendliness (target, true); + mo.master = target; + } + } + Destroy (); + } } @@ -326,8 +1086,6 @@ class SorcFX4 : Actor DeathSound "SorcererBallExplode"; } - native void A_SorcFX4Check(); - States { Spawn: @@ -339,6 +1097,22 @@ class SorcFX4 : Actor SBS4 FGH 2 Bright; Stop; } + + //============================================================================ + // + // A_SorcFX4Check + // + // FX4 - rapid fire balls + // + //============================================================================ + + void A_SorcFX4Check() + { + if (special2-- <= 0) + { + SetStateLabel ("Death"); + } + } } diff --git a/wadsrc/static/zscript/hexen/hexenspecialdecs.txt b/wadsrc/static/zscript/hexen/hexenspecialdecs.txt index 30ae0fff1..feebf515f 100644 --- a/wadsrc/static/zscript/hexen/hexenspecialdecs.txt +++ b/wadsrc/static/zscript/hexen/hexenspecialdecs.txt @@ -83,19 +83,18 @@ class TreeDestructible : Actor // Pottery1 ------------------------------------------------------------------ -class Pottery1 : Actor native +class Pottery1 : Actor { Default { Health 15; Speed 10; Height 32; - +SOLID +SHOOTABLE +NOBLOOD +DROPOFF + +SOLID +SHOOTABLE +NOBLOOD +DROPOFF +SMASHABLE +SLIDESONWALLS +PUSHABLE +TELESTOMP +CANPASS +NOICEDEATH } - native void A_PotteryExplode(); States { @@ -106,6 +105,40 @@ class Pottery1 : Actor native POT1 A 0 A_PotteryExplode; Stop; } + + //============================================================================ + // + // A_PotteryExplode + // + //============================================================================ + + void A_PotteryExplode() + { + Actor mo = null; + int i; + + for(i = (random[Pottery]()&3)+3; i; i--) + { + mo = Spawn ("PotteryBit", Pos, ALLOW_REPLACE); + if (mo) + { + mo.SetState (mo.SpawnState + (random[Pottery]()%5)); + mo.Vel.X = random2[Pottery]() / 64.; + mo.Vel.Y = random2[Pottery]() / 64.; + mo.Vel.Z = ((random[Pottery]() & 7) + 5) * 0.75; + } + } + mo.A_PlaySound ("PotteryExplode", CHAN_BODY); + // Spawn an item? + Class type = GetSpawnableType(args[0]); + if (type != null) + { + if (!(level.nomonsters || sv_nomonsters) || !(GetDefaultByType (type).bIsMonster)) + { // Only spawn monsters if not -nomonsters + Spawn (type, Pos, ALLOW_REPLACE); + } + } + } } // Pottery2 ----------------------------------------------------------------- @@ -144,6 +177,8 @@ class Pottery3 : Pottery1 class PotteryBit : Actor { + State LoopState; + Default { Radius 5; @@ -153,9 +188,6 @@ class PotteryBit : Actor +NOICEDEATH } - native void A_PotteryChooseBit(); - native void A_PotteryCheck(); - States { Spawn: @@ -185,6 +217,42 @@ class PotteryBit : Actor PBIT J 1 A_PotteryCheck; Stop; } + + //============================================================================ + // + // A_PotteryChooseBit + // + //============================================================================ + + void A_PotteryChooseBit() + { + static const statelabel bits[] = { "Pottery1", "Pottery2", "Pottery3", "Pottery4", "Pottery5" }; + LoopState = FindState(bits[random[PotteryBit]() % 5]); // Save the state for jumping back to. + SetState (LoopState); + tics = 256 + (random[PotteryBit]() << 1); + } + + //============================================================================ + // + // A_PotteryCheck + // + //============================================================================ + + void A_PotteryCheck() + { + for(int i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + Actor pmo = players[i].mo; + if (CheckSight (pmo) && (absangle(pmo.AngleTo(self), pmo.Angle) <= 45)) + { // jump back to previpusly set state. + SetState (LoopState); + return; + } + } + } + } } @@ -203,7 +271,7 @@ class BloodPool : Actor // Lynched corpse (no heart) ------------------------------------------------ -class ZCorpseLynchedNoHeart : Actor native +class ZCorpseLynchedNoHeart : Actor { Default { @@ -212,14 +280,32 @@ class ZCorpseLynchedNoHeart : Actor native +SOLID +SPAWNCEILING +NOGRAVITY } - native void A_CorpseBloodDrip(); - States { Spawn: CPS5 A 140 A_CorpseBloodDrip; Loop; } + + override void PostBeginPlay () + { + Super.PostBeginPlay (); + Spawn ("BloodPool", (pos.xy, floorz), ALLOW_REPLACE); + } + + //============================================================================ + // + // A_CorpseBloodDrip + // + //============================================================================ + + void A_CorpseBloodDrip() + { + if (random[CorpseDrip]() <= 128) + { + Spawn ("CorpseBloodDrip", pos + (0, 0, Height / 2), ALLOW_REPLACE); + } + } } @@ -289,8 +375,6 @@ class ZCorpseSitting : Actor DeathSound "FireDemonDeath"; } - native void A_CorpseExplode(); - States { Spawn: @@ -300,6 +384,40 @@ class ZCorpseSitting : Actor CPS6 A 1 A_CorpseExplode; Stop; } + + //============================================================================ + // + // A_CorpseExplode + // + //============================================================================ + + void A_CorpseExplode() + { + Actor mo; + + for (int i = (random[CorpseExplode]() & 3) + 3; i; i--) + { + mo = Spawn ("CorpseBit", Pos, ALLOW_REPLACE); + if (mo) + { + mo.SetState (mo.SpawnState + (random[CorpseExplode]() % 3)); + mo.Vel.X = random2[CorpseExplode]() / 64.; + mo.Vel.Y = random2[CorpseExplode]() / 64.; + mo.Vel.Z = ((random[CorpseExplode]() & 7) + 5) * 0.75; + } + } + // Spawn a skull + mo = Spawn ("CorpseBit", Pos, ALLOW_REPLACE); + if (mo) + { + mo.SetState (mo.SpawnState + 3); + mo.Vel.X = random2[CorpseExplode]() / 64.; + mo.Vel.Y = random2[CorpseExplode]() / 64.; + mo.Vel.Z = ((random[CorpseExplode]() & 7) + 5) * 0.75; + } + A_PlaySound (DeathSound, CHAN_BODY); + Destroy (); + } } @@ -313,7 +431,6 @@ class LeafSpawner : Actor +INVISIBLE } - native void A_LeafSpawn(); States { @@ -321,6 +438,32 @@ class LeafSpawner : Actor TNT1 A 20 A_LeafSpawn; Loop; } + + //============================================================================ + // + // A_LeafSpawn + // + //============================================================================ + + void A_LeafSpawn() + { + static const class leaves[] = { "Leaf1", "Leaf2" }; + + for (int i = (random[LeafSpawn]() & 3) + 1; i; i--) + { + double xo = random2[LeafSpawn]() / 4.; + double yo = random2[LeafSpawn]() / 4.; + double zo = random[LeafSpawn]() / 4.; + Actor mo = Spawn (leaves[random[LeafSpawn]()&1], Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + + if (mo) + { + mo.Thrust(random[LeafSpawn]() / 128. + 3); + mo.target = self; + mo.special1 = 0; + } + } + } } @@ -338,15 +481,13 @@ class Leaf1 : Actor +NOICEDEATH } - native void A_LeafThrust(); - native void A_LeafCheck(); - States { Spawn: LEF1 ABC 4; LEF1 D 4 A_LeafThrust; LEF1 EFG 4; + Looping: LEF1 H 4 A_LeafThrust; LEF1 I 4; LEF1 AB 4; @@ -359,6 +500,49 @@ class Leaf1 : Actor LEF3 D 10 A_LeafCheck; Wait; } + + //============================================================================ + // + // A_LeafThrust + // + //============================================================================ + + void A_LeafThrust() + { + if (random[LeafThrust]() <= 96) + { + Vel.Z += random[LeafThrust]() / 128. + 1; + } + } + + //============================================================================ + // + // A_LeafCheck + // + //============================================================================ + + void A_LeafCheck() + { + special1++; + if (special1 >= 20) + { + Destroy(); + return; + } + double ang = target ? target.angle : angle; + if (random[LeafCheck]() > 64) + { + if (Vel.X == 0 && Vel.Y == 0) + { + Thrust(random[LeafCheck]() / 128. + 1, ang); + } + return; + } + SetStateLabel ("Looping"); + Vel.Z = random[LeafCheck]() / 128. + 1; + Thrust(random[LeafCheck]() / 128. + 2, ang); + bMissile = true; + } } @@ -556,8 +740,6 @@ class ZSuitOfArmor : Actor DeathSound "SuitofArmorBreak"; } - native void A_SoAExplode(); - States { Spawn: @@ -567,6 +749,41 @@ class ZSuitOfArmor : Actor ZSUI A 1 A_SoAExplode; Stop; } + + //=========================================================================== + // + // A_SoAExplode - Suit of Armor Explode + // + //=========================================================================== + + void A_SoAExplode() + { + for (int i = 0; i < 10; i++) + { + double xo = (random[SoAExplode]() - 128) / 16.; + double yo = (random[SoAExplode]() - 128) / 16.; + double zo = random[SoAExplode]() * Height / 256.; + Actor mo = Spawn ("ZArmorChunk", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + if (mo) + { + mo.SetState (mo.SpawnState + i); + mo.Vel.X = random2[SoAExplode]() / 64.; + mo.Vel.Y = random2[SoAExplode]() / 64.; + mo.Vel.Z = (random[SoAExplode]() & 7) + 5; + } + } + // Spawn an item? + Class type = GetSpawnableType(args[0]); + if (type != null) + { + if (!(level.nomonsters || sv_nomonsters) || !(GetDefaultByType (type).bIsMonster)) + { // Only spawn monsters if not -nomonsters + Spawn (type, Pos, ALLOW_REPLACE); + } + } + A_PlaySound (DeathSound, CHAN_BODY); + Destroy (); + } } @@ -608,7 +825,7 @@ class ZArmorChunk : Actor // Bell --------------------------------------------------------------------- -class ZBell : Actor native +class ZBell : Actor { Default { @@ -621,9 +838,6 @@ class ZBell : Actor native DeathSound "BellRing"; } - native void A_BellReset1(); - native void A_BellReset2(); - States { Spawn: @@ -665,6 +879,45 @@ class ZBell : Actor native BBLL A 1 A_BellReset2; Goto Spawn; } + + override void Activate (Actor activator) + { + if (health > 0) A_Die(); + { + DamageMobj (activator, activator, 10, 'Melee', DMG_THRUSTLESS); // 'ring' the bell + } + } + + //=========================================================================== + // + // A_BellReset1 + // + //=========================================================================== + + void A_BellReset1() + { + bNoGravity = true; + Height = Default.Height; + if (special) + { // Initiate death action + A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]); + special = 0; + } + } + + //=========================================================================== + // + // A_BellReset2 + // + //=========================================================================== + + void A_BellReset2() + { + bShootable = true; + bCorpse = false; + bKilled = false; + health = 5; + } } diff --git a/wadsrc/static/zscript/hexen/iceguy.txt b/wadsrc/static/zscript/hexen/iceguy.txt index 8081d5a61..41105b728 100644 --- a/wadsrc/static/zscript/hexen/iceguy.txt +++ b/wadsrc/static/zscript/hexen/iceguy.txt @@ -22,9 +22,6 @@ class IceGuy : Actor Obituary "$OB_ICEGUY"; } - native void A_IceGuyLook(); - native void A_IceGuyChase(); - native void A_IceGuyAttack(); States { @@ -51,6 +48,84 @@ class IceGuy : Actor ICEY A -1; Goto See; } + + //============================================================================ + // + // SpawnWisp + // + //============================================================================ + + private void SpawnWisp() + { + static const class WispTypes[] = { "IceGuyWisp1", "IceGuyWisp2" }; + + double dist = (random[IceGuyLook]() - 128) * radius / 128.; + double an = angle + 90; + Actor mo = Spawn(WispTypes[random[IceGuyLook]() & 1], Vec3Angle(dist, an, 60.), ALLOW_REPLACE); + if (mo) + { + mo.Vel = Vel; + mo.target = self; + } + } + + //============================================================================ + // + // A_IceGuyLook + // + //============================================================================ + + void A_IceGuyLook() + { + A_Look(); + if (random[IceGuyLook]() < 64) SpawnWisp(); + } + + //============================================================================ + // + // A_IceGuyChase + // + //============================================================================ + + void A_IceGuyChase() + { + A_Chase(); + if (random[IceGuyLook]() < 128) SpawnWisp(); + } + + //============================================================================ + // + // A_IceGuyAttack + // + //============================================================================ + + void A_IceGuyAttack() + { + if(!target) + { + return; + } + SpawnMissileXYZ(Vec3Angle(radius / 2, angle + 90, 40.), target, "IceGuyFX"); + SpawnMissileXYZ(Vec3Angle(radius / 2, angle - 90, 40.), target, "IceGuyFX"); + A_PlaySound (AttackSound, CHAN_WEAPON); + } +} + +extend class Actor +{ + //============================================================================ + // + // A_IceGuyDie (globally accessible) + // + //============================================================================ + + void A_IceGuyDie() + { + Vel = (0,0,0); + Height = Default.Height; + A_FreezeDeathChunks(); + } + } // Ice Guy Projectile ------------------------------------------------------- @@ -69,8 +144,6 @@ class IceGuyFX : Actor DeathSound "IceGuyMissileExplode"; } - native void A_IceGuyMissileExplode(); - States { Spawn: @@ -83,6 +156,25 @@ class IceGuyFX : Actor ICPR H 3 Bright; Stop; } + + + //============================================================================ + // + // A_IceGuyMissileExplode + // + //============================================================================ + + void A_IceGuyMissileExplode() + { + for (int i = 0; i < 8; i++) + { + Actor mo = SpawnMissileAngleZ (pos.z+3, "IceGuyFX2", i*45., -0.3); + if (mo) + { + mo.target = target; + } + } + } } // Ice Guy Projectile's Puff ------------------------------------------------ @@ -183,3 +275,4 @@ class IceGuyWisp2 : IceGuyWisp1 Stop; } } + diff --git a/wadsrc/static/zscript/hexen/korax.txt b/wadsrc/static/zscript/hexen/korax.txt index 16bf172a4..1c1f31712 100644 --- a/wadsrc/static/zscript/hexen/korax.txt +++ b/wadsrc/static/zscript/hexen/korax.txt @@ -1,5 +1,43 @@ +//=========================================================================== +// 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) +//=========================================================================== + class Korax : Actor { + const KORAX_ARM_EXTENSION_SHORT = 40; + const KORAX_ARM_EXTENSION_LONG = 55; + + const KORAX_ARM1_HEIGHT = 108; + const KORAX_ARM2_HEIGHT = 82; + const KORAX_ARM3_HEIGHT = 54; + const KORAX_ARM4_HEIGHT = 104; + const KORAX_ARM5_HEIGHT = 86; + const KORAX_ARM6_HEIGHT = 53; + + const KORAX_FIRST_TELEPORT_TID = 248; + const KORAX_TELEPORT_TID = 249; + + const KORAX_DELTAANGLE = 85; + + const KORAX_COMMAND_HEIGHT = 120; + const KORAX_COMMAND_OFFSET = 27; + + const KORAX_SPIRIT_LIFETIME = 5*TICRATE/5; // 5 seconds + Default { Health 5000; @@ -24,12 +62,6 @@ class Korax : Actor Obituary "$OB_KORAX"; } - native void A_KoraxChase(); - native void A_KoraxDecide(); - native void A_KoraxBonePop(); - native void A_KoraxMissile(); - native void A_KoraxCommand(); - States { Spawn: @@ -39,13 +71,11 @@ class Korax : Actor KORX AAA 3 A_KoraxChase; KORX B 3 A_Chase; KORX BBB 3 A_KoraxChase; - KORX C 0 A_PlaySound("KoraxStep"); - KORX C 3 A_Chase; + KORX C 3 A_KoraxStep; KORX CCC 3 A_KoraxChase; KORX D 3 A_Chase; KORX DDD 3 A_KoraxChase; - KORX A 0 A_PlaySound("KoraxStep"); - KORX A 3 A_Chase; + KORX A 3 A_KoraxStep; Loop; Pain: KORX H 5 A_Pain; @@ -79,6 +109,255 @@ class Korax : Actor KORX E 5 Bright; Goto See; } + + + void A_KoraxStep() + { + A_PlaySound("KoraxStep"); + A_Chase(); + } + + //============================================================================ + // + // A_KoraxChase + // + //============================================================================ + + + void A_KoraxChase() + { + if ((!special2) && (health <= (SpawnHealth()/2))) + { + ActorIterator it = ActorIterator.Create(KORAX_FIRST_TELEPORT_TID); + Actor spot = it.Next (); + if (spot != null) + { + Teleport ((spot.pos.xy, ONFLOORZ), spot.angle, TELF_SOURCEFOG | TELF_DESTFOG); + } + ACS_Execute(249, 0); + special2 = 1; // Don't run again + return; + } + + if (target == null) + { + return; + } + if (random[KoraxChase]() < 30) + { + SetState (MissileState); + } + else if (random[KoraxChase]() < 30) + { + A_PlaySound("KoraxActive", CHAN_VOICE, 1, false, ATTN_NONE); + } + + // Teleport away + if (health < (SpawnHealth() >> 1)) + { + if (random[KoraxChase]() < 10) + { + ActorIterator it = ActorIterator.Create(KORAX_TELEPORT_TID); + Actor spot; + + if (tracer != null) + { // Find the previous teleport destination + do + { + spot = it.Next (); + } while (spot != null && spot != tracer); + } + + // Go to the next teleport destination + spot = it.Next (); + tracer = spot; + if (spot) + { + Teleport ((spot.pos.xy, ONFLOORZ), spot.angle, TELF_SOURCEFOG | TELF_DESTFOG); + } + } + } + } + + //============================================================================ + // + // A_KoraxDecide + // + //============================================================================ + + void A_KoraxDecide() + { + if (random[KoraxDecide]() < 220) + { + SetStateLabel ("Attack"); + } + else + { + SetStateLabel ("Command"); + } + } + + //============================================================================ + // + // A_KoraxBonePop + // + //============================================================================ + + void A_KoraxBonePop() + { + // Spawn 6 spirits equalangularly + for (int i = 0; i < 6; ++i) + { + Actor mo = SpawnMissileAngle ("KoraxSpirit", 60.*i, 5.); + if (mo) + { + KSpiritInit (mo); + } + } + ACS_Execute(255, 0); + } + + //============================================================================ + // + // KSpiritInit + // + //============================================================================ + + private void KSpiritInit (Actor spirit) + { + spirit.health = KORAX_SPIRIT_LIFETIME; + + spirit.tracer = self; // Swarm around korax + spirit.WeaveIndexZ = 32 + (random[Kspiritnit]() & 7); // Float bob index + spirit.args[0] = 10; // initial turn value + spirit.args[1] = 0; // initial look angle + + // Spawn a tail for spirit + HolyTail.SpawnSpiritTail (spirit); + } + + //============================================================================ + // + // A_KoraxMissile + // + //============================================================================ + + void A_KoraxMissile() + { + static const class choices[] = + { + "WraithFX1", "Demon1FX1", "Demon2FX1", "FireDemonMissile", "CentaurFX", "SerpentFX" + }; + static const sound sounds[] = + { + "WraithMissileFire", "DemonMissileFire", "DemonMissileFire", "FireDemonAttack", "CentaurLeaderAttack", "SerpentLeaderAttack" + }; + int type = random[KoraxMissile]() % 6; + + A_PlaySound("KoraxAttack", CHAN_VOICE); + + // Fire all 6 missiles at once + A_PlaySound(sounds[type], CHAN_WEAPON, 1, false, ATTN_NONE); + class info = choices[type]; + for (int i = 0; i < 6; ++i) + { + KoraxFire(info, i); + } + } + + //============================================================================ + // + // 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 (Class type, int arm) + { + static const int extension[] = + { + 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[] = + { + KORAX_ARM1_HEIGHT, + KORAX_ARM2_HEIGHT, + KORAX_ARM3_HEIGHT, + KORAX_ARM4_HEIGHT, + KORAX_ARM5_HEIGHT, + KORAX_ARM6_HEIGHT + }; + + double ang = angle + (arm < 3 ? -KORAX_DELTAANGLE : KORAX_DELTAANGLE); + Vector3 pos = Vec3Angle(extension[arm], ang, armheight[arm] - Floorclip); + SpawnKoraxMissile (pos, target, type); + } + + //============================================================================ + // + // P_SpawnKoraxMissile + // + //============================================================================ + + private void SpawnKoraxMissile (Vector3 pos, Actor dest, Class type) + { + Actor th = Spawn (type, pos, ALLOW_REPLACE); + th.target = self; // Originator + double an = th.AngleTo(dest); + if (dest.bShadow) + { // Invisible target + an += Random2[KoraxMissile]() * (45/256.); + } + th.angle = an; + th.VelFromAngle(); + double dist = dest.DistanceBySpeed(th, th.Speed); + th.Vel.Z = (dest.pos.z - pos.Z + 30) / dist; + th.CheckMissileSpawn(radius); + } + + //============================================================================ + // + // A_KoraxCommand + // + // Call action code scripts (250-254) + // + //============================================================================ + + void A_KoraxCommand() + { + int numcommands; + + A_PlaySound("KoraxCommand", CHAN_VOICE); + + // Shoot stream of lightning to ceiling + double ang = angle - 90; + Vector3 pos = Vec3Angle(KORAX_COMMAND_OFFSET, ang, KORAX_COMMAND_HEIGHT); + Spawn("KoraxBolt", pos, ALLOW_REPLACE); + + if (health <= (SpawnHealth() >> 1)) + { + numcommands = 5; + } + else + { + numcommands = 4; + } + + ACS_Execute(250 + (random[KoraxCommand]()%numcommands), 0); + } } class KoraxSpirit : Actor @@ -94,8 +373,6 @@ class KoraxSpirit : Actor Alpha 0.4; } - native void A_KSpiritRoam(); - States { Spawn: @@ -105,10 +382,98 @@ class KoraxSpirit : Actor SPIR DEFGHI 5; Stop; } + + //============================================================================ + // + // A_KSpiritSeeker + // + //============================================================================ + + private void KSpiritSeeker (double thresh, double turnMax) + { + Actor target = tracer; + if (target == null) + { + return; + } + double dir = deltaangle(angle, AngleTo(target)); + double delta = abs(dir); + if (delta > thresh) + { + delta /= 2; + if(delta > turnMax) + { + delta = turnMax; + } + } + if(dir > 0) + { // Turn clockwise + angle += delta; + } + else + { // Turn counter clockwise + angle -= delta; + } + VelFromAngle(); + + if (!(level.time&15) + || pos.z > target.pos.z + target.Default.Height + || pos.z + height < target.pos.z) + { + double newZ = target.pos.z + random[KoraxRoam]() * target.Default.Height / 256; + double deltaZ = newZ - pos.z; + + if (abs(deltaZ) > 15) + { + if(deltaZ > 0) + { + deltaZ = 15; + } + else + { + deltaZ = -15; + } + } + Vel.Z = deltaZ + DistanceBySpeed(target, Speed); + } + } + + //============================================================================ + // + // A_KSpiritRoam + // + //============================================================================ + + void A_KSpiritRoam() + { + if (health-- <= 0) + { + A_PlaySound("SpiritDie", CHAN_VOICE); + SetStateLabel ("Death"); + } + else + { + if (tracer) + { + KSpiritSeeker(args[0], args[0] * 2.); + } + int xyspeed = (random[KoraxRoam]() % 5); + int zspeed = (random[KoraxRoam]() % 5); + A_Weave(xyspeed, zspeed, 4., 2.); + + if (random[KoraxRoam]() < 50) + { + A_PlaySound("SpiritActive", CHAN_VOICE, 1, false, ATTN_NONE); + } + } + } } class KoraxBolt : Actor { + const KORAX_BOLT_HEIGHT = 48.; + const KORAX_BOLT_LIFETIME = 3; + Default { Radius 15; @@ -119,9 +484,6 @@ class KoraxBolt : Actor RenderStyle "Add"; } - native void A_KBolt(); - native void A_KBoltRaise(); - States { Spawn: @@ -130,4 +492,40 @@ class KoraxBolt : Actor MLFX IJKLM 2 Bright A_KBolt; Stop; } + + //============================================================================ + // + // A_KBolt + // + //============================================================================ + + void A_KBolt() + { + // Countdown lifetime + if (special1-- <= 0) + { + Destroy (); + } + } + + //============================================================================ + // + // A_KBoltRaise + // + //============================================================================ + + void A_KBoltRaise() + { + // Spawn a child upward + double z = pos.z + KORAX_BOLT_HEIGHT; + + if ((z + KORAX_BOLT_HEIGHT) < ceilingz) + { + Actor mo = Spawn("KoraxBolt", (pos.xy, z), ALLOW_REPLACE); + if (mo) + { + mo.special1 = KORAX_BOLT_LIFETIME; + } + } + } } diff --git a/wadsrc/static/zscript/hexen/mageboss.txt b/wadsrc/static/zscript/hexen/mageboss.txt index f9637f8d1..0c081d786 100644 --- a/wadsrc/static/zscript/hexen/mageboss.txt +++ b/wadsrc/static/zscript/hexen/mageboss.txt @@ -18,8 +18,6 @@ class MageBoss : Actor Obituary "$OB_MBOSS"; } - native void A_MageAttack(); - States { Spawn: @@ -83,4 +81,39 @@ class MageBoss : Actor FDTH V 4 Bright; Stop; } + + //============================================================================ + // + // MStaffSpawn2 - for use by mage class boss + // + //============================================================================ + + void MStaffSpawn2 (double angle) + { + Actor mo = SpawnMissileAngleZ (pos.z + 40, "MageStaffFX2", angle, 0.); + if (mo) + { + mo.target = self; + mo.tracer = RoughMonsterSearch(10, true, true); + } + } + + //============================================================================ + // + // A_MStaffAttack2 - for use by mage class boss + // + //============================================================================ + + void A_MageAttack() + { + if (target == NULL) + { + return; + } + MStaffSpawn2(angle); + MStaffSpawn2(angle - 5); + MStaffSpawn2(angle + 5); + A_PlaySound("MageStaffFire", CHAN_WEAPON); + } + } diff --git a/wadsrc/static/zscript/hexen/magecone.txt b/wadsrc/static/zscript/hexen/magecone.txt index a9f2d29dc..2bbd58f3d 100644 --- a/wadsrc/static/zscript/hexen/magecone.txt +++ b/wadsrc/static/zscript/hexen/magecone.txt @@ -17,8 +17,6 @@ class MWeapFrost : MageWeapon Tag "$TAG_MWEAPFROST"; } - action native void A_FireConePL1(); - States { Spawn: @@ -45,12 +43,68 @@ class MWeapFrost : MageWeapon CONE A 10 A_ReFire; Goto Ready; } + + //============================================================================ + // + // A_FireConePL1 + // + //============================================================================ + + action void A_FireConePL1() + { + bool conedone=false; + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + A_PlaySound ("MageShardsFire", CHAN_WEAPON); + + int damage = 90+(random[MageCone]() & 15); + for (int i = 0; i < 16; i++) + { + double ang = angle + i*(45./16); + double slope = AimLineAttack (ang, MELEERANGE, t, 0., ALF_CHECK3D); + if (t.linetarget) + { + t.linetarget.DamageMobj (self, self, damage, 'Ice', DMG_USEANGLE, t.angleFromSource); + conedone = true; + break; + } + } + + // didn't find any creatures, so fire projectiles + if (!conedone) + { + Actor mo = SpawnPlayerMissile ("FrostMissile"); + if (mo) + { + mo.special1 = FrostMissile.SHARDSPAWN_LEFT|FrostMissile.SHARDSPAWN_DOWN|FrostMissile.SHARDSPAWN_UP|FrostMissile.SHARDSPAWN_RIGHT; + mo.special2 = 3; // Set sperm count (levels of reproductivity) + mo.target = self; + mo.args[0] = 3; // Mark Initial shard as super damage + } + } + } } // Frost Missile ------------------------------------------------------------ -class FrostMissile : Actor native +class FrostMissile : Actor { + const SHARDSPAWN_LEFT = 1; + const SHARDSPAWN_RIGHT = 2; + const SHARDSPAWN_UP = 4; + const SHARDSPAWN_DOWN = 8; + Default { Speed 25; @@ -63,8 +117,6 @@ class FrostMissile : Actor native Obituary "$OB_MPMWEAPFROST"; } - native void A_ShedShard(); - States { Spawn: @@ -77,6 +129,88 @@ class FrostMissile : Actor native SHEX ABCDE 5 Bright; Stop; } + + override int DoSpecialDamage (Actor victim, int damage, Name damagetype) + { + if (special2 > 0) + { + damage <<= special2; + } + return damage; + } + + //============================================================================ + // + // A_ShedShard + // + //============================================================================ + + void A_ShedShard() + { + int spawndir = special1; + int spermcount = special2; + Actor mo; + + if (spermcount <= 0) + { + return; // No sperm left + } + special2 = 0; + spermcount--; + + // every so many calls, spawn a new missile in its set directions + if (spawndir & SHARDSPAWN_LEFT) + { + mo = SpawnMissileAngleZSpeed(pos.z, "FrostMissile", angle + 5, 0, (20. + 2 * spermcount), target); + if (mo) + { + mo.special1 = SHARDSPAWN_LEFT; + mo.special2 = spermcount; + mo.Vel.Z = Vel.Z; + mo.args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_RIGHT) + { + mo = SpawnMissileAngleZSpeed(pos.z, "FrostMissile", angle - 5, 0, (20. + 2 * spermcount), target); + if (mo) + { + mo.special1 = SHARDSPAWN_RIGHT; + mo.special2 = spermcount; + mo.Vel.Z = Vel.Z; + mo.args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_UP) + { + mo = SpawnMissileAngleZSpeed(pos.z + 8., "FrostMissile", angle, 0, (15. + 2 * spermcount), target); + if (mo) + { + mo.Vel.Z = 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 = SpawnMissileAngleZSpeed(pos.z - 4., "FrostMissile", angle, 0, (15. + 2 * spermcount), target); + if (mo) + { + mo.Vel.Z = 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 = target; + mo.args[0] = (spermcount==3)?2:0; + } + } + } } // Ice Shard ---------------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/magelightning.txt b/wadsrc/static/zscript/hexen/magelightning.txt index 154776d15..343c96012 100644 --- a/wadsrc/static/zscript/hexen/magelightning.txt +++ b/wadsrc/static/zscript/hexen/magelightning.txt @@ -16,9 +16,6 @@ class MWeapLightning : MageWeapon Tag "$TAG_MWEAPLIGHTNING"; } - action native void A_LightningReady(); - action native void A_MLightningAttack(class floor = "LightningFloor", class ceiling = "LightningCeiling"); - States { Spawn: @@ -50,11 +47,62 @@ class MWeapLightning : MageWeapon MLNG B 2 Bright Offset (0, 40); Goto Ready; } + + //============================================================================ + // + // A_LightningReady + // + //============================================================================ + + action void A_LightningReady() + { + A_WeaponReady(); + if (random[LightningReady]() < 160) + { + A_PlaySound ("MageLightningReady", CHAN_WEAPON); + } + } + + //============================================================================ + // + // A_MLightningAttack + // + //============================================================================ + + action void A_MLightningAttack(class floor = "LightningFloor", class ceiling = "LightningCeiling") + { + LightningFloor fmo = LightningFloor(SpawnPlayerMissile (floor)); + LightningCeiling cmo = LightningCeiling(SpawnPlayerMissile (ceiling)); + if (fmo) + { + fmo.special1 = 0; + fmo.lastenemy = cmo; + fmo.A_LightningZap(); + } + if (cmo) + { + cmo.tracer = NULL; + cmo.lastenemy = fmo; + cmo.A_LightningZap(); + } + A_PlaySound ("MageLightningFire", CHAN_BODY); + + if (player != NULL) + { + Weapon weapon = player.ReadyWeapon; + if (weapon != NULL) + { + weapon.DepleteAmmo (weapon.bAltFire); + } + } + } + + } // Ceiling Lightning -------------------------------------------------------- -class Lightning : Actor native +class Lightning : Actor { Default { @@ -63,10 +111,51 @@ class Lightning : Actor native ActiveSound "MageLightningContinuous"; Obituary "$OB_MPMWEAPLIGHTNING"; } + + override int SpecialMissileHit (Actor thing) + { + if (thing.bShootable && thing != target) + { + if (thing.Mass < LARGE_MASS) + { + thing.Vel.X += Vel.X / 16; + thing.Vel.Y += Vel.Y / 16; + } + if ((!thing.player && !thing.bBoss) || !(level.time&1)) + { + thing.DamageMobj(self, target, 3, 'Electric'); + A_PlaySound(AttackSound, CHAN_WEAPON, 1, true); + if (thing.bIsMonster && random[LightningHit]() < 64) + { + thing.Howl (); + } + } + health--; + if (health <= 0 || thing.health <= 0) + { + return 0; + } + if (bFloorHugger) + { + if (lastenemy && ! lastenemy.tracer) + { + lastenemy.tracer = thing; + } + } + else if (!tracer) + { + tracer = thing; + } + } + return 1; // lightning zaps through all sprites + } + } class LightningCeiling : Lightning { + const ZAGSPEED = 1; + Default { Health 144; @@ -79,10 +168,6 @@ class LightningCeiling : Lightning RenderStyle "Add"; } - native void A_LightningZap(); - native void A_LightningClip(); - native void A_LightningRemove(); - States { Spawn: @@ -104,6 +189,126 @@ class LightningCeiling : Lightning ACLO E 1050; Stop; } + + //============================================================================ + // + // A_LightningClip + // + //============================================================================ + + void A_LightningClip() + { + Actor cMo; + Actor target = NULL; + int zigZag; + + if (bFloorHugger) + { + if (lastenemy == NULL) + { + return; + } + SetZ(floorz); + target = lastenemy.tracer; + } + else if (bCeilingHugger) + { + SetZ(ceilingz - Height); + target = tracer; + } + if (bFloorHugger) + { // floor lightning zig-zags, and forces the ceiling lightning to mimic + cMo = lastenemy; + zigZag = random[LightningClip](); + if((zigZag > 128 && special1 < 2) || special1 < -2) + { + Thrust(ZAGSPEED, angle + 90); + if(cMo) + { + cMo.Thrust(ZAGSPEED, angle + 90); + } + special1++; + } + else + { + Thrust(ZAGSPEED,angle - 90); + if(cMo) + { + cMo.Thrust(ZAGSPEED, angle - 90); + } + special1--; + } + } + if(target) + { + if(target.health <= 0) + { + ExplodeMissile(); + } + else + { + angle = AngleTo(target); + VelFromAngle(Speed / 2); + } + } + } + + + //============================================================================ + // + // A_LightningZap + // + //============================================================================ + + void A_LightningZap() + { + Class lightning = MissileName; + if (lightning == NULL) lightning = "LightningZap"; + + A_LightningClip(); + + health -= 8; + if (health <= 0) + { + SetStateLabel ("Death"); + return; + } + double deltaX = (random[LightningZap]() - 128) * radius / 256; + double deltaY = (random[LightningZap]() - 128) * radius / 256; + double deltaZ = (bFloorHugger) ? 10 : -10; + + Actor mo = Spawn(lightning, Vec3Offset(deltaX, deltaY, deltaZ), ALLOW_REPLACE); + if (mo) + { + mo.lastenemy = self; + mo.Vel.X = Vel.X; + mo.Vel.Y = Vel.Y; + mo.Vel.Z = (bFloorHugger) ? 20 : -20; + mo.target = target; + } + if (bFloorHugger && random[LightningZap]() < 160) + { + A_PlaySound (ActiveSound, CHAN_BODY); + } + } + + //============================================================================ + // + // A_LightningRemove + // + //============================================================================ + + void A_LightningRemove() + { + Actor mo = lastenemy; + if (mo) + { + bNoTarget = true; // tell A_ZapMimic that we are dead. The original code did a state pointer compare which is not safe. + mo.lastenemy = NULL; + mo.ExplodeMissile (); + } + } + } // Floor Lightning ---------------------------------------------------------- @@ -117,8 +322,6 @@ class LightningFloor : LightningCeiling RenderStyle "Add"; } - native void A_LastZap(); - States { Spawn: @@ -137,11 +340,31 @@ class LightningFloor : LightningCeiling MLF2 P 1 Bright A_HideThing; Goto Super::Death + 19; } + + //============================================================================ + // + // A_LastZap + // + //============================================================================ + + void A_LastZap() + { + Class lightning = MissileName; + if (lightning == NULL) lightning = "LightningZap"; + + Actor mo = Spawn(lightning, self.Pos, ALLOW_REPLACE); + if (mo) + { + mo.SetStateLabel ("Death"); + mo.Vel.Z = 40; + mo.SetDamage(0); + } + } } // Lightning Zap ------------------------------------------------------------ -class LightningZap : Actor native +class LightningZap : Actor { Default { @@ -155,8 +378,6 @@ class LightningZap : Actor native Obituary "$OB_MPMWEAPLIGHTNING"; } - native void A_ZapMimic(); - States { Spawn: @@ -166,4 +387,58 @@ class LightningZap : Actor native MLFX NOPQRSTU 2 Bright; Stop; } + + override int SpecialMissileHit (Actor thing) + { + Actor lmo; + + if (thing.bShootable && thing != target) + { + lmo = lastenemy; + if (lmo) + { + if (lmo.bFloorHugger) + { + 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_ZapMimic + // + //============================================================================ + + void A_ZapMimic() + { + Actor mo = lastenemy; + if (mo) + { + if (mo.bNoTarget) + { + ExplodeMissile (); + } + else + { + Vel.X = mo.Vel.X; + Vel.Y = mo.Vel.Y; + } + } + } + + } diff --git a/wadsrc/static/zscript/hexen/magestaff.txt b/wadsrc/static/zscript/hexen/magestaff.txt index c11542834..e45095240 100644 --- a/wadsrc/static/zscript/hexen/magestaff.txt +++ b/wadsrc/static/zscript/hexen/magestaff.txt @@ -76,8 +76,10 @@ class BloodscourgeDrop : Actor // The Mages's Staff (Bloodscourge) ----------------------------------------- -class MWeapBloodscourge : MageWeapon native +class MWeapBloodscourge : MageWeapon { + int MStaffCount; + Default { Health 3; @@ -97,9 +99,6 @@ class MWeapBloodscourge : MageWeapon native Tag "$TAG_MWEAPBLOODSCOURGE"; } - action native void A_MStaffAttack(); - action native void A_MStaffPalette(); - States { Spawn: @@ -123,11 +122,104 @@ class MWeapBloodscourge : MageWeapon native MSTF J 5 Offset (0, 36); Goto Ready; } + + //============================================================================ + // + // + // + //============================================================================ + + override Color GetBlend () + { + if (paletteflash & PF_HEXENWEAPONS) + { + if (MStaffCount == 3) + return Color(128, 100, 73, 0); + else if (MStaffCount == 2) + return Color(128, 125, 92, 0); + else if (MStaffCount == 1) + return Color(128, 150, 110, 0); + else + return Color(0, 0, 0, 0); + } + else + { + return Color (MStaffCount * 128 / 3, 151, 110, 0); + } + } + + //============================================================================ + // + // MStaffSpawn + // + //============================================================================ + + private action void MStaffSpawn (double angle, Actor alttarget) + { + FTranslatedLineTarget t; + + Actor mo = SpawnPlayerMissile ("MageStaffFX2", angle, pLineTarget:t); + if (mo) + { + mo.target = self; + if (t.linetarget && !t.unlinked) + mo.tracer = t.linetarget; + else + mo.tracer = alttarget; + } + } + + //============================================================================ + // + // A_MStaffAttack + // + //============================================================================ + + action void A_MStaffAttack() + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != NULL) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + + // [RH] Let's try and actually track what the player aimed at + AimLineAttack (angle, PLAYERMISSILERANGE, t, 32.); + if (t.linetarget == NULL) + { + t.linetarget = RoughMonsterSearch(10, true, true); + } + MStaffSpawn (angle, t.linetarget); + MStaffSpawn (angle-5, t.linetarget); + MStaffSpawn (angle+5, t.linetarget); + A_PlaySound ("MageStaffFire", CHAN_WEAPON); + invoker.MStaffCount = 3; + } + + //============================================================================ + // + // A_MStaffPalette + // + //============================================================================ + + action void A_MStaffPalette() + { + if (invoker.MStaffCount > 0) invoker.MStaffCount--; + } } + // Mage Staff FX2 (Bloodscourge) -------------------------------------------- -class MageStaffFX2 : Actor native +class MageStaffFX2 : Actor { Default { @@ -143,7 +235,6 @@ class MageStaffFX2 : Actor native Obituary "$OB_MPMWEAPBLOODSCOURGE"; } - native void A_MStaffTrack(); States { @@ -157,4 +248,43 @@ class MageStaffFX2 : Actor native MSP2 I 4 Bright; Stop; } + + //============================================================================ + // + // + // + //============================================================================ + + override int SpecialMissileHit (Actor victim) + { + if (victim != target && !victim.player && !victim.bBoss) + { + victim.DamageMobj (self, target, 10, 'Fire'); + return 1; // Keep going + } + return -1; + } + + override bool SpecialBlastHandling (Actor source, double strength) + { + // Reflect to originator + tracer = target; + target = source; + return true; + } + + //============================================================================ + // + // A_MStaffTrack + // + //============================================================================ + + void A_MStaffTrack() + { + if (tracer == null && random[MStaffTrack]() < 50) + { + tracer = RoughMonsterSearch (10, true); + } + A_SeekerMissile(2, 10); + } } diff --git a/wadsrc/static/zscript/hexen/pig.txt b/wadsrc/static/zscript/hexen/pig.txt index 8fa446882..9f62e661e 100644 --- a/wadsrc/static/zscript/hexen/pig.txt +++ b/wadsrc/static/zscript/hexen/pig.txt @@ -34,8 +34,6 @@ class Snout : Weapon Weapon.YAdjust 10; } - action native void A_SnoutAttack (); - States { Ready: @@ -54,12 +52,43 @@ class Snout : Weapon WPIG B 8; Goto Ready; } + + //============================================================================ + // + // A_SnoutAttack + // + //============================================================================ + + action void A_SnoutAttack () + { + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + int damage = random[SnoutAttack](3, 6); + double ang = angle; + double slope = AimLineAttack(ang, MELEERANGE); + Actor puff = LineAttack(ang, MELEERANGE, slope, damage, 'Melee', "SnoutPuff", true, t); + A_PlaySound("PigActive", CHAN_VOICE); + if(t.linetarget) + { + AdjustPlayerAngle(t); + if(puff != null) + { // Bit something + A_PlaySound("PigAttack", CHAN_VOICE); + } + } + } + } // Pig player --------------------------------------------------------------- -class PigPlayer : PlayerPawn native +class PigPlayer : PlayerPawn { Default { @@ -112,6 +141,30 @@ class PigPlayer : PlayerPawn native PIGY M 1 A_FreezeDeathChunks; Wait; } + + + override void MorphPlayerThink () + { + if (player.morphTics & 15) + { + return; + } + if(Vel.X == 0 && Vel.Y == 0 && random[PigPlayerThink]() < 64) + { // Snout sniff + if (player.ReadyWeapon != null) + { + player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.FindState('Grunt')); + } + A_PlaySound ("PigActive1", CHAN_VOICE); // snort + return; + } + if (random[PigPlayerThink]() < 48) + { + A_PlaySound ("PigActive", CHAN_VOICE); // snort + } + } + + } @@ -168,3 +221,21 @@ class Pig : MorphedMonster } } + +extend class Actor +{ + //============================================================================ + // + // A_PigPain + // + //============================================================================ + + void A_PigPain () + { + A_Pain(); + if (pos.z <= floorz) + { + Vel.Z = 3.5; + } + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/hexen/serpent.txt b/wadsrc/static/zscript/hexen/serpent.txt index 70fb2195a..6eaafcfd1 100644 --- a/wadsrc/static/zscript/hexen/serpent.txt +++ b/wadsrc/static/zscript/hexen/serpent.txt @@ -24,16 +24,6 @@ class Serpent : Actor HitObituary "$OB_SERPENTHIT"; } - native void A_SerpentHumpDecide(); - native void A_SerpentHide(); - native void A_SerpentCheckForAttack(); - native void A_SerpentSpawnGibs(); - native void A_SerpentUnHide(); - native void A_SerpentRaiseHump(); - native void A_SerpentLowerHump(); - native void A_SerpentChooseAttack(); - native void A_SerpentMeleeAttack(); - States { Spawn: @@ -99,6 +89,203 @@ class Serpent : Actor SSPT N 5 A_SerpentMeleeAttack; Goto Dive; } + + //============================================================================ + // + // A_SerpentUnHide + // + //============================================================================ + + void A_SerpentUnHide() + { + bInvisible = false; + Floorclip = 24; + } + + //============================================================================ + // + // A_SerpentHide + // + //============================================================================ + + void A_SerpentHide() + { + bInvisible = true; + Floorclip = 0; + } + + //============================================================================ + // + // A_SerpentRaiseHump + // + // Raises the hump above the surface by raising the floorclip level + //============================================================================ + + void A_SerpentRaiseHump() + { + Floorclip -= 4; + } + + //============================================================================ + // + // A_SerpentLowerHump + // + //============================================================================ + + void A_SerpentLowerHump() + { + Floorclip += 4; + } + + //============================================================================ + // + // A_SerpentHumpDecide + // + // Decided whether to hump up, or if the mobj is a serpent leader, + // to missile attack + //============================================================================ + + void A_SerpentHumpDecide() + { + if (MissileState != NULL) + { + if (random[SerpentHump]() > 30) + { + return; + } + else if (random[SerpentHump]() < 40) + { // Missile attack + SetState (MeleeState); + return; + } + } + else if (random[SerpentHump]() > 3) + { + return; + } + if (!CheckMeleeRange ()) + { // The hump shouldn't occur when within melee range + if (MissileState != NULL && random[SerpentHump]() < 128) + { + SetState (MeleeState); + } + else + { + SetStateLabel("Hump"); + A_PlaySound ("SerpentActive", CHAN_BODY); + } + } + } + + //============================================================================ + // + // A_SerpentCheckForAttack + // + //============================================================================ + + void A_SerpentCheckForAttack() + { + if (!target) + { + return; + } + if (MissileState != NULL) + { + if (!CheckMeleeRange ()) + { + SetStateLabel ("Attack"); + return; + } + } + if (CheckMeleeRange2 ()) + { + SetStateLabel ("Walk"); + } + else if (CheckMeleeRange ()) + { + if (random[SerpentAttack]() < 32) + { + SetStateLabel ("Walk"); + } + else + { + SetStateLabel ("Attack"); + } + } + } + + //============================================================================ + // + // A_SerpentChooseAttack + // + //============================================================================ + + void A_SerpentChooseAttack() + { + if (!target || CheckMeleeRange()) + { + return; + } + if (MissileState != NULL) + { + SetState (MissileState); + } + } + + //============================================================================ + // + // A_SerpentMeleeAttack + // + //============================================================================ + + void A_SerpentMeleeAttack() + { + if (!target) + { + return; + } + if (CheckMeleeRange ()) + { + int damage = random[SerpentAttack](1, 8) * 5; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + A_PlaySound ("SerpentMeleeHit", CHAN_BODY); + } + if (random[SerpentAttack]() < 96) + { + A_SerpentCheckForAttack(); + } + } + + //============================================================================ + // + // A_SerpentSpawnGibs + // + //============================================================================ + + void A_SerpentSpawnGibs() + { + static const class GibTypes[] = + { + "SerpentGib3", + "SerpentGib2", + "SerpentGib1" + }; + + for (int i = 2; i >= 0; --i) + { + double x = (random[SerpentGibs]() - 128) / 16.; + double y = (random[SerpentGibs]() - 128) / 16.; + + Actor mo = Spawn (GibTypes[i], Vec2OffsetZ(x, y, floorz + 1), ALLOW_REPLACE); + if (mo) + { + mo.Vel.X = (random[SerpentGibs]() - 128) / 1024.f; + mo.Vel.Y = (random[SerpentGibs]() - 128) / 1024.f; + mo.Floorclip = 6; + } + } + } } // Serpent Leader ----------------------------------------------------------- @@ -159,8 +346,6 @@ class SerpentHead : Actor +NOBLOCKMAP } - native void A_SerpentHeadCheck(); - States { Spawn: @@ -170,6 +355,28 @@ class SerpentHead : Actor SSXD S -1; Loop; } + + //============================================================================ + // + // A_SerpentHeadCheck + // + //============================================================================ + + void A_SerpentHeadCheck() + { + if (pos.z <= floorz) + { + if (GetFloorTerrain().IsLiquid) + { + HitFloor (); + Destroy(); + } + else + { + SetStateLabel ("NAME_Death"); + } + } + } } // Serpent Gib 1 ------------------------------------------------------------ @@ -183,10 +390,6 @@ class SerpentGib1 : Actor +NOBLOCKMAP +NOGRAVITY } - native void A_FloatGib(); - native void A_DelayGib(); - native void A_SinkGib(); - States { Spawn: @@ -199,6 +402,41 @@ class SerpentGib1 : Actor SSXD QQQ 8 A_SinkGib; Stop; } + + //============================================================================ + // + // A_FloatGib + // + //============================================================================ + + void A_FloatGib() + { + Floorclip -= 1; + } + + //============================================================================ + // + // A_SinkGib + // + //============================================================================ + + void A_SinkGib() + { + Floorclip += 1; + } + + //============================================================================ + // + // A_DelayGib + // + //============================================================================ + + void A_DelayGib() + { + tics -= random[DelayGib]() >> 2; + } + + } // Serpent Gib 2 ------------------------------------------------------------ diff --git a/wadsrc/static/zscript/hexen/spike.txt b/wadsrc/static/zscript/hexen/spike.txt index 58f763ccc..248c95726 100644 --- a/wadsrc/static/zscript/hexen/spike.txt +++ b/wadsrc/static/zscript/hexen/spike.txt @@ -18,7 +18,7 @@ class DirtClump : Actor // Spike (thrust floor) ----------------------------------------------------- -class ThrustFloor : Actor native +class ThrustFloor : Actor { Default { @@ -26,12 +26,6 @@ class ThrustFloor : Actor native Height 128; } - native void A_ThrustRaise(); - native void A_ThrustImpale(); - native void A_ThrustLower(); - native void A_ThrustInitDn(); - native void A_ThrustInitUp(); - States { ThrustRaising: @@ -81,6 +75,131 @@ class ThrustFloor : Actor native TSPK B 2 A_ThrustImpale; Loop; } + + override void Activate (Actor activator) + { + if (args[0] == 0) + { + A_PlaySound ("ThrustSpikeLower", CHAN_BODY); + bInvisible = false; + if (args[1]) + SetStateLabel("BloodThrustRaise"); + else + SetStateLabel("ThrustRaise"); + } + } + + override void Deactivate (Actor activator) + { + if (args[0] == 1) + { + A_PlaySound ("ThrustSpikeRaise", CHAN_BODY); + if (args[1]) + SetStateLabel("BloodThrustLower"); + else + SetStateLabel("ThrustLower"); + } + } + + //=========================================================================== + // + // Thrust floor stuff + // + // Thrust Spike Variables + // master pointer to dirt clump actor + // special2 speed of raise + // args[0] 0 = lowered, 1 = raised + // args[1] 0 = normal, 1 = bloody + //=========================================================================== + + void A_ThrustInitUp() + { + special2 = 5; // Raise speed + args[0] = 1; // Mark as up + Floorclip = 0; + bSolid = true; + bNoTeleport = true; + bFloorClip = true; + special1 = 0; + } + + void A_ThrustInitDn() + { + special2 = 5; // Raise speed + args[0] = 0; // Mark as down + Floorclip = Default.Height; + bSolid = false; + bNoTeleport = true; + bFloorClip = true; + bInvisible = true; + master = Spawn("DirtClump", Pos, ALLOW_REPLACE); + } + + + void A_ThrustRaise() + { + if (RaiseMobj (special2)) + { // Reached it's target height + args[0] = 1; + if (args[1]) + SetStateLabel ("BloodThrustInit2", true); + else + SetStateLabel ("ThrustInit2", true); + } + + // Lose the dirt clump + if ((Floorclip < Height) && master) + { + master.Destroy (); + master = null; + } + + // Spawn some dirt + if (random[Thrustraise]()<40) + SpawnDirt (radius); + special2++; // Increase raise speed + } + + void A_ThrustLower() + { + if (SinkMobj (6)) + { + args[0] = 0; + if (args[1]) + SetStateLabel ("BloodThrustInit1", true); + else + SetStateLabel ("ThrustInit1", true); + } + } + + + void A_ThrustImpale() + { + BlockThingsIterator it = BlockThingsIterator.Create(self); + while (it.Next()) + { + double blockdist = radius + it.thing.radius; + if (abs(it.thing.pos.x - it.Position.X) >= blockdist || abs(it.thing.pos.y - it.Position.Y) >= blockdist) + continue; + + // Q: Make this z-aware for everything? It never was before. + if (it.thing.pos.z + it.thing.height < pos.z || it.thing.pos.z > pos.z + height) + { + if (CurSector.PortalGroup != it.thing.CurSector.PortalGroup) + continue; + } + + if (!it.thing.bShootable) + continue; + + if (it.thing == self) + continue; // don't clip against self + + int newdam = it.thing.DamageMobj (self, self, 10001, 'Crush'); + it.thing.TraceBleed (newdam > 0 ? newdam : 10001, null); + args[1] = 1; // Mark thrust thing as bloody + } + } } // Spike up ----------------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/summon.txt b/wadsrc/static/zscript/hexen/summon.txt index 06ceb7e17..c9e9b9295 100644 --- a/wadsrc/static/zscript/hexen/summon.txt +++ b/wadsrc/static/zscript/hexen/summon.txt @@ -1,7 +1,7 @@ // Dark Servant Artifact ---------------------------------------------------- -class ArtiDarkServant : Inventory native +class ArtiDarkServant : Inventory { Default { @@ -23,6 +23,26 @@ class ArtiDarkServant : Inventory native SUMN A 350; Loop; } + + //============================================================================ + // + // Activate the summoning artifact + // + //============================================================================ + + override bool Use (bool pickup) + { + Actor mo = Owner.SpawnPlayerMissile ("SummoningDoll"); + if (mo) + { + mo.target = Owner; + mo.tracer = Owner; + mo.Vel.Z = 5; + } + return true; + } + + } // Summoning Doll ----------------------------------------------------------- @@ -36,8 +56,6 @@ class SummoningDoll : Actor +NOTELEPORT } - native void A_Summon(); - States { Spawn: @@ -48,6 +66,49 @@ class SummoningDoll : Actor SUMN A 4 A_Summon; Stop; } + + //============================================================================ + // + // A_Summon + // + //============================================================================ + + void A_Summon() + { + Actor mo = Spawn("MinotaurFriend", pos, ALLOW_REPLACE); + if (mo) + { + if (mo.TestMobjLocation() == false || !tracer) + { // Didn't fit - change back to artifact + mo.Destroy(); + Actor arti = Spawn("ArtiDarkServant", Pos, ALLOW_REPLACE); + if (arti) arti.bDropped = true; + return; + } + + // Careful! The Minotaur might have been replaced + // so only set the time if we got a genuine one. + MinotaurFriend m = MinotaurFriend(mo); + if (m) m.StartTime = level.maptime; + + if (tracer.bCorpse) + { // Master dead + mo.tracer = null; // No master + } + else + { + mo.tracer = tracer; // Pointer to master + Inventory power = Inventory(Spawn("PowerMinotaur")); + power.CallTryPickup(tracer); + mo.SetFriendPlayer(tracer.player); + } + + // Make smoke puff + Spawn("MinotaurSmoke", Pos, ALLOW_REPLACE); + A_PlaySound(mo.ActiveSound, CHAN_VOICE); + } + } + } // Minotaur Smoke ----------------------------------------------------------- @@ -69,3 +130,4 @@ class MinotaurSmoke : Actor Stop; } } + diff --git a/wadsrc/static/zscript/hexen/teleportother.txt b/wadsrc/static/zscript/hexen/teleportother.txt index 2de144d5f..f7be0ebff 100644 --- a/wadsrc/static/zscript/hexen/teleportother.txt +++ b/wadsrc/static/zscript/hexen/teleportother.txt @@ -1,7 +1,7 @@ // Teleport Other Artifact -------------------------------------------------- -class ArtiTeleportOther : Inventory native +class ArtiTeleportOther : Inventory { Default { @@ -22,13 +22,29 @@ class ArtiTeleportOther : Inventory native TELO ABCD 5; Loop; } + + //=========================================================================== + // + // Activate Teleport Other + // + //=========================================================================== + + override bool Use (bool pickup) + { + Owner.SpawnPlayerMissile ("TelOtherFX1"); + return true; + } + + } // Teleport Other FX -------------------------------------------------------- -class TelOtherFX1 : Actor native +class TelOtherFX1 : Actor { + const TELEPORT_LIFE = 1; + Default { Damage 10001; @@ -41,11 +57,6 @@ class TelOtherFX1 : Actor native Speed 20; } - native void A_TeloSpawnA(); - native void A_TeloSpawnB(); - native void A_TeloSpawnC(); - native void A_TeloSpawnD(); - native void A_CheckTeleRing (); States { @@ -63,6 +74,115 @@ class TelOtherFX1 : Actor native TRNG E 3 Bright; Stop; } + + private void TeloSpawn (class type) + { + Actor fx = Spawn (type, pos, ALLOW_REPLACE); + if (fx) + { + fx.special1 = TELEPORT_LIFE; // Lifetime countdown + fx.angle = angle; + fx.target = target; + fx.Vel = Vel / 2; + } + } + + void A_TeloSpawnA() + { + TeloSpawn ("TelOtherFX2"); + } + + void A_TeloSpawnB() + { + TeloSpawn ("TelOtherFX3"); + } + + void A_TeloSpawnC() + { + TeloSpawn ("TelOtherFX4"); + } + + void A_TeloSpawnD() + { + TeloSpawn ("TelOtherFX5"); + } + + void A_CheckTeleRing () + { + if (self.special1-- <= 0) + { + self.SetStateLabel("Death"); + } + } + + //=========================================================================== + // + // Perform Teleport Other + // + //=========================================================================== + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if ((target.bIsMonster || target.player != NULL) && + !target.bBoss && !target.bNoTeleOther) + { + if (target.player) + { + if (deathmatch) + P_TeleportToDeathmatchStarts (target); + else + P_TeleportToPlayerStarts (target); + } + else + { + // If death action, run it upon teleport + if (target.bIsMonster && target.special) + { + target.RemoveFromHash (); + Actor caller = level.ActOwnSpecial? target : self.target; + caller.A_CallSpecial(target.special, 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 + // + //=========================================================================== + + private static void P_TeleportToPlayerStarts (Actor victim) + { + Vector3 dest; + double destAngle; + + [dest, destAngle] = G_PickPlayerStart(0, PPS_FORCERANDOM | PPS_NOBLOCKINGCHECK); + dest.Z = ONFLOORZ; + victim.Teleport ((dest.xy, ONFLOORZ), destangle, TELF_SOURCEFOG | TELF_DESTFOG); + } + + //=========================================================================== + // + // P_TeleportToDeathmatchStarts + // + //=========================================================================== + + private void P_TeleportToDeathmatchStarts (Actor victim) + { + Vector3 dest; + double destAngle; + + [dest, destAngle] = G_PickDeathmatchStart(); + if (destAngle < 65536) victim.Teleport((dest.xy, ONFLOORZ), destangle, TELF_SOURCEFOG | TELF_DESTFOG); + else P_TeleportToPlayerStarts(victim); + } + } diff --git a/wadsrc/static/zscript/hexen/wraith.txt b/wadsrc/static/zscript/hexen/wraith.txt index 86df72d94..42a81ae6c 100644 --- a/wadsrc/static/zscript/hexen/wraith.txt +++ b/wadsrc/static/zscript/hexen/wraith.txt @@ -23,11 +23,6 @@ class Wraith : Actor Obituary "$OB_WRAITH"; } - native void A_WraithInit(); - native void A_WraithChase(); - native void A_WraithFX3(); - native void A_WraithMelee(); - States { Spawn: @@ -78,6 +73,150 @@ class Wraith : Actor WRT2 I 1 A_FreezeDeathChunks; Wait; } + + //============================================================================ + // + // A_WraithInit + // + //============================================================================ + + void A_WraithInit() + { + AddZ(48); + + // [RH] Make sure the wraith didn't go into the ceiling + if (pos.z + height > ceilingz) + { + SetZ(ceilingz - Height); + } + + WeaveIndexZ = 0; // index into floatbob + } + + + //============================================================================ + // + // A_WraithChase + // + //============================================================================ + + void A_WraithChase() + { + int weaveindex = WeaveIndexZ; + AddZ(BobSin(weaveindex)); + WeaveIndexZ = (weaveindex + 2) & 63; + A_Chase (); + A_WraithFX4 (); + } + + //============================================================================ + // + // A_WraithFX3 + // + // Spawn an FX3 around the wraith during attacks + // + //============================================================================ + + void A_WraithFX3() + { + int numdropped = random[WraithFX3](0,14); + + while (numdropped-- > 0) + { + double xo = (random[WraithFX3]() - 128) / 32.; + double yo = (random[WraithFX3]() - 128) / 32.; + double zo = random[WraithFX3]() / 64.; + + Actor mo = Spawn("WraithFX3", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + if (mo) + { + mo.floorz = floorz; + mo.ceilingz = ceilingz; + mo.target = self; + } + } + } + + //============================================================================ + // + // A_WraithFX4 + // + // Spawn an FX4 during movement + // + //============================================================================ + + void A_WraithFX4 () + { + int chance = random[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 = (random[WraithFX4]() - 128) / 16.; + double yo = (random[WraithFX4]() - 128) / 16.; + double zo = (random[WraithFX4]() / 64.); + + Actor mo = Spawn ("WraithFX4", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + if (mo) + { + mo.floorz = floorz; + mo.ceilingz = ceilingz; + mo.target = self; + } + } + if (spawn5) + { + double xo = (random[WraithFX4]() - 128) / 32.; + double yo = (random[WraithFX4]() - 128) / 32.; + double zo = (random[WraithFX4]() / 64.); + + Actor mo = Spawn ("WraithFX5", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + if (mo) + { + mo.floorz = floorz; + mo.ceilingz = ceilingz; + mo.target = self; + } + } + } + + //============================================================================ + // + // A_WraithMelee + // + //============================================================================ + + void A_WraithMelee() + { + // Steal health from target and give to self + if (CheckMeleeRange() && (random[StealHealth]()<220)) + { + int amount = random[StealHealth](1, 8) * 2; + target.DamageMobj (self, self, amount, 'Melee'); + health += amount; + } + } } // Buried wraith ------------------------------------------------------------ @@ -97,8 +236,6 @@ class WraithBuried : Wraith PainChance 0; } - native void A_WraithRaiseInit(); - native void A_WraithRaise(); States { @@ -113,6 +250,47 @@ class WraithBuried : Wraith Chase: Goto Super::See; } + + //============================================================================ + // + // A_WraithRaiseInit + // + //============================================================================ + + void A_WraithRaiseInit() + { + bInvisible = false; + bNonShootable = false; + bDontBlast = false; + bShootable = true; + bSolid = true; + Floorclip = Height; + } + + //============================================================================ + // + // A_WraithRaise + // + //============================================================================ + + void A_WraithRaise() + { + if (RaiseMobj (2)) + { + // Reached it's target height + // [RH] Once a buried wraith is fully raised, it should be + // morphable, right? + bDontMorph = false; + bSpecialFloorClip = false; + SetStateLabel ("Chase"); + // [RH] Reset PainChance to a normal wraith's. + PainChance = GetDefaultByType("Wraith").PainChance; + } + + SpawnDirt (radius); + } + + } // Wraith FX 1 -------------------------------------------------------------- @@ -133,7 +311,6 @@ class WraithFX1 : Actor DeathSound "WraithMissileExplode"; } - native void A_WraithFX2(); States { @@ -150,6 +327,36 @@ class WraithFX1 : Actor WRBL I 3 Bright; Stop; } + + //============================================================================ + // + // A_WraithFX2 - spawns sparkle tail of missile + // + //============================================================================ + + void A_WraithFX2() + { + for (int i = 2; i; --i) + { + Actor mo = Spawn ("WraithFX2", Pos, ALLOW_REPLACE); + if(mo) + { + double newangle = random[WraithFX2]() * (360 / 1024.f); + if (random[WraithFX2]() >= 128) + { + newangle = -newangle; + } + newangle += angle; + mo.Vel.X = ((random[WraithFX2]() / 512.) + 1) * cos(newangle); + mo.Vel.Y = ((random[WraithFX2]() / 512.) + 1) * sin(newangle); + mo.Vel.Z = 0; + mo.target = self; + mo.Floorclip = 10; + } + } + } + + } // Wraith FX 2 -------------------------------------------------------------- diff --git a/wadsrc/static/zscript/raven/artiegg.txt b/wadsrc/static/zscript/raven/artiegg.txt index ef3c6e2be..f0287048e 100644 --- a/wadsrc/static/zscript/raven/artiegg.txt +++ b/wadsrc/static/zscript/raven/artiegg.txt @@ -47,11 +47,10 @@ class ArtiEgg : CustomInventory EGGC ABCB 6; Loop; Use: - TNT1 A 0 A_FireCustomMissile("EggFX", -15, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("EggFX", -7.5, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("EggFX", 0, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("EggFX", 7.5, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("EggFX", 15, 0, 0, 0, 1); + TNT1 A 0 + { + for (double i = -15; i <= 15; i += 7.5) A_FireCustomMissile("EggFX", i, 0, 0, 0, 1); + } Stop; } } @@ -103,11 +102,10 @@ class ArtiPork : CustomInventory PORK ABCDEFGH 5; Loop; Use: - TNT1 A 0 A_FireCustomMissile("PorkFX", -15, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("PorkFX", -7.5, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("PorkFX", 0, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("PorkFX", 7.5, 0, 0, 0, 1); - TNT1 A 0 A_FireCustomMissile("PorkFX", 15, 0, 0, 0, 1); + TNT1 A 0 + { + for (double i = -15; i <= 15; i += 7.5) A_FireCustomMissile("PorkFX", i, 0, 0, 0, 1); + } Stop; } } diff --git a/wadsrc/static/zscript/raven/artitele.txt b/wadsrc/static/zscript/raven/artitele.txt index 9f21bd733..3242db92e 100644 --- a/wadsrc/static/zscript/raven/artitele.txt +++ b/wadsrc/static/zscript/raven/artitele.txt @@ -1,7 +1,7 @@ // Teleport (self) ---------------------------------------------------------- -class ArtiTeleport : Inventory native +class ArtiTeleport : Inventory { Default { @@ -22,6 +22,38 @@ class ArtiTeleport : Inventory native ATLP ABCB 4; Loop; } + + override bool Use (bool pickup) + { + Vector3 dest; + int destAngle; + + if (deathmatch) + { + [dest, destAngle] = G_PickDeathmatchStart(); + } + else + { + [dest, destAngle] = G_PickPlayerStart(Owner.PlayerNumber()); + } + dest.Z = ONFLOORZ; + Owner.Teleport (dest, destAngle, TELF_SOURCEFOG | TELF_DESTFOG); + bool canlaugh = true; + Playerinfo p = Owner.player; + if (p && p.morphTics && (p.MorphStyle & MRF_UNDOBYCHAOSDEVICE)) + { // Teleporting away will undo any morph effects (pig) + if (!p.UndoPlayerMorph (p, MRF_UNDOBYCHAOSDEVICE) && (p.MorphStyle & MRF_FAILNOLAUGH)) + { + canlaugh = false; + } + } + if (canlaugh) + { // Full volume laugh + A_PlaySound ("*evillaugh", CHAN_VOICE, 1, false, ATTN_NONE); + } + return true; + } + } diff --git a/wadsrc/static/zscript/raven/minotaur.txt b/wadsrc/static/zscript/raven/minotaur.txt index 9bbbe81a0..5e3d0cfe3 100644 --- a/wadsrc/static/zscript/raven/minotaur.txt +++ b/wadsrc/static/zscript/raven/minotaur.txt @@ -1,5 +1,9 @@ -class Minotaur : Actor native +class Minotaur : Actor { + const MAULATORTICS = 25 * TICRATE; + const MNTR_CHARGE_SPEED =13.; + const MINOTAUR_LOOK_DIST = 16*54.; + Default { Health 3000; @@ -26,16 +30,6 @@ class Minotaur : Actor native DropItem "PhoenixRodAmmo", 84, 10; } - native void A_MinotaurDecide(); - native void A_MinotaurAtk1(); - native void A_MinotaurAtk2(); - native void A_MinotaurAtk3(); - native void A_MinotaurCharge(); - native void A_MinotaurLook(); - native void A_MinotaurRoam(); - native void A_MinotaurChase(); - native void A_MinotaurDeath(); - States { Spawn: @@ -101,10 +95,478 @@ class Minotaur : Actor native MNTR E 10 A_BossDeath; Stop; } + + //--------------------------------------------------------------------------- + // + // FUNC P_MinotaurSlam + // + //--------------------------------------------------------------------------- + + void MinotaurSlam (Actor target) + { + double ang = AngleTo(target); + double thrust = 16 + random[MinotaurSlam]() / 64.; + target.VelFromAngle(ang, thrust); + int damage = random[MinotaurSlam](1, 8) * (bSummonedMonster? 4 : 6); + int newdam = target.DamageMobj (null, null, damage, 'Melee'); + target.TraceBleedAngle (newdam > 0 ? newdam : damage, ang, 0.); + if (target.player) + { + target.reactiontime = random[MinotaurSlam](14, 21); + } + } + + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + override void Tick () + { + Super.Tick (); + + // The unfriendly Minotaur (Heretic's) is invulnerable while charging + if (!bSummonedMonster) + { + bInvulnerable = bSkullFly; + } + } + + override bool Slam (Actor thing) + { + // Slamming minotaurs shouldn't move non-creatures + if (!thing.bIsMonster && !thing.player) + { + return false; + } + return Super.Slam (thing); + } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + damage = Super.DoSpecialDamage (target, damage, damagetype); + if (damage != -1 && bSkullFly) + { // Slam only when in charge mode + MinotaurSlam (target); + return -1; + } + return damage; + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurAtk1 + // + // Melee attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurAtk1() + { + if (!target) + { + return; + } + A_PlaySound ("minotaur/melee", CHAN_WEAPON); + if (CheckMeleeRange()) + { + int damage = random[MinotaurAtk1](1, 8) * 4; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + PlayerInfo player = target.player; + if (player != null && player.mo == target) + { // Squish the player + player.deltaviewheight = -16; + } + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurDecide + // + // Choose a missile attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurDecide() + { + bool friendly = bSummonedMonster; + + if (!target) + { + return; + } + if (!friendly) + { + A_PlaySound ("minotaur/sight", CHAN_WEAPON); + } + double dist = Distance2D(target); + if (target.pos.z + target.height > pos.z + && target.pos.z + target.height < pos.z + height + && dist < (friendly ? 16*64. : 8*64.) + && dist > 1*64. + && random[MinotaurDecide]() < 150) + { // Charge attack + // Don't call the state function right away + SetStateLabel("Charge", true); + bSkullFly = true; + if (!friendly) + { // Heretic's Minotaur is invulnerable during charge attack + bInvulnerable = true; + } + A_FaceTarget (); + VelFromAngle(MNTR_CHARGE_SPEED); + special1 = TICRATE/2; // Charge duration + } + else if (target.pos.z == target.floorz + && dist < 9*64. + && random[MinotaurDecide]() < (friendly ? 100 : 220)) + { // Floor fire attack + SetStateLabel("Hammer"); + special2 = 0; + } + else + { // Swing attack + A_FaceTarget (); + // Don't need to call P_SetMobjState because the current state + // falls through to the swing attack + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurCharge + // + //---------------------------------------------------------------------------- + + void A_MinotaurCharge() + { + if (target == null) + { + return; + } + if (special1 > 0) + { + Class type; + + //if (gameinfo.gametype == GAME_Heretic) + if (gametype() == GAME_Heretic) + { + type = "PhoenixPuff"; + } + else + { + type = "PunchPuff"; + } + Actor puff = Spawn (type, Pos, ALLOW_REPLACE); + puff.Vel.Z = 2; + special1--; + } + else + { + bSkullFly = false; + bInvulnerable = false; + SetState (SeeState); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurAtk2 + // + // Swing attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurAtk2() + { + bool friendly = bSummonedMonster; + + if (target == null) + { + return; + } + A_PlaySound ("minotaur/attack2", CHAN_WEAPON); + if (CheckMeleeRange()) + { + int damage = random[MinotaurAtk2](1, 8) * (friendly ? 3 : 5); + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + double z = pos.z + 40; + Class fx = "MinotaurFX1"; + Actor mo = SpawnMissileZ (z, target, fx); + if (mo != null) + { +// S_Sound (mo, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM); + double vz = mo.Vel.Z; + double ang = mo.angle; + SpawnMissileAngleZ (z, fx, ang-(45./8), vz); + SpawnMissileAngleZ (z, fx, ang+(45./8), vz); + SpawnMissileAngleZ (z, fx, ang-(45./16), vz); + SpawnMissileAngleZ (z, fx, ang+(45./16), vz); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurAtk3 + // + // Floor fire attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurAtk3() + { + bool friendly = bSummonedMonster; + + if (!target) + { + return; + } + A_PlaySound ("minotaur/attack3", CHAN_VOICE); + if (CheckMeleeRange()) + { + int damage = random[MinotaurAtk3](1, 8) * (friendly ? 3 : 5); + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + PlayerInfo player = target.player; + if (player != null && player.mo == target) + { // Squish the player + player.deltaviewheight = -16; + } + } + else + { + if (Floorclip > 0 && compat_minotaur) + { + // only play the sound. + A_PlaySound ("minotaur/fx2hit", CHAN_WEAPON); + } + else + { + Actor mo = SpawnMissile (target, "MinotaurFX2"); + if (mo != null) + { + mo.A_PlaySound ("minotaur/attack1", CHAN_WEAPON); + } + } + } + if (random[MinotaurAtk3]() < 192 && special2 == 0) + { + SetStateLabel ("HammerLoop"); + special2 = 1; + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurDeath + // + //---------------------------------------------------------------------------- + + void A_MinotaurDeath() + { + if (Wads.CheckNumForName ("MNTRF1", Wads.ns_sprites) < 0 && + Wads.CheckNumForName ("MNTRF0", Wads.ns_sprites) < 0) + SetStateLabel("FadeOut"); + } + + + //---------------------------------------------------------------------------- + // + // A_MinotaurRoam + // + //---------------------------------------------------------------------------- + + void A_MinotaurRoam() + { + // In case pain caused him to skip his fade in. + A_SetRenderStyle(1, STYLE_Normal); + + MinotaurFriend mf = MinotaurFriend(self); + if (mf) + { + if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS) + { + DamageMobj (null, null, TELEFRAG_DAMAGE, 'None'); + return; + } + } + + if (random[MinotaurRoam]() < 30) + A_MinotaurLook(); // adjust to closest target + + if (random[MinotaurRoam]() < 6) + { + //Choose new direction + movedir = random[MinotaurRoam]() % 8; + FaceMovementDirection (); + } + if (!MonsterMove()) + { + // Turn + if (random[MinotaurRoam]() & 1) + movedir = (movedir + 1) % 8; + else + movedir = (movedir + 7) % 8; + FaceMovementDirection (); + } + } + + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurLook + // + // Look for enemy of player + //---------------------------------------------------------------------------- + + void A_MinotaurLook() + { + if (!(self is "MinotaurFriend")) + { + A_Look(); + return; + } + + Actor mo = null; + PlayerInfo player; + double dist; + Actor master = tracer; + + target = null; + if (deathmatch) // Quick search for players + { + for (int 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 = Distance2D(mo); + if (dist > MINOTAUR_LOOK_DIST) continue; + target = mo; + break; + } + } + + if (!target) // Near player monster search + { + if (master && (master.health > 0) && (master.player)) + mo = master.RoughMonsterSearch(20); + else + mo = RoughMonsterSearch(20); + target = mo; + } + + if (!target) // Normal monster search + { + ThinkerIterator it = ThinkerIterator.Create("Actor"); + + while ((mo = Actor(it.Next())) != null) + { + if (!mo.bIsMonster) continue; + if (mo.health <= 0) continue; + if (!mo.bShootable) continue; + dist = Distance2D(mo); + if (dist > MINOTAUR_LOOK_DIST) continue; + if (mo == master || mo == self) continue; + if (mo.bSummonedMonster && mo.tracer == master) continue; + target = mo; + break; // Found actor to attack + } + } + + if (target) + { + SetState (SeeState, true); + } + else + { + SetStateLabel ("Roam", true); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurChase + // + //---------------------------------------------------------------------------- + + void A_MinotaurChase() + { + MinotaurFriend mf = MinotaurFriend(self); + if (!mf) + { + A_Chase(); + return; + } + + + // In case pain caused him to skip his fade in. + A_SetRenderStyle(1, STYLE_Normal); + + if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS) + { + DamageMobj (null, null, TELEFRAG_DAMAGE, 'None'); + return; + } + + if (random[MinotaurChase]() < 30) + A_MinotaurLook(); // adjust to closest target + + if (!target || (target.health <= 0) || !target.bShootable) + { // look for a new target + SetIdle(); + return; + } + + FaceMovementDirection (); + reactiontime = 0; + + // Melee attack + if (MeleeState && CheckMeleeRange ()) + { + if (AttackSound) + { + A_PlaySound (AttackSound, CHAN_WEAPON); + } + SetState (MeleeState); + return; + } + + // Missile attack + if (MissileState && CheckMissileRange()) + { + SetState (MissileState); + return; + } + + // chase towards target + if (!MonsterMove ()) + { + NewChaseDir (); + FaceMovementDirection (); + } + + // Active sound + if (random[MinotaurChase]() < 6) + { + PlayActiveSound (); + } + } } -class MinotaurFriend : Minotaur native +class MinotaurFriend : Minotaur { + int StartTime; + Default { Health 2500; @@ -132,6 +594,51 @@ class MinotaurFriend : Minotaur native Death: Goto FadeOut; } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + override void BeginPlay () + { + Super.BeginPlay (); + StartTime = -1; + } + + override void Die (Actor source, Actor inflictor, int dmgflags) + { + Super.Die (source, inflictor, dmgflags); + + if (tracer && tracer.health > 0 && tracer.player) + { + // Search thinker list for minotaur + ThinkerIterator it = ThinkerIterator.Create("MinotaurFriend"); + MinotaurFriend mo; + + while ((mo = MinotaurFriend(it.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.bCorpse) continue; + if (mo.StartTime >= 0 && (level.maptime - StartTime) >= MAULATORTICS) continue; + if (mo.tracer != null && mo.tracer.player == tracer.player) break; + } + + if (mo == null) + { + Inventory power = tracer.FindInventory("PowerMinotaur"); + if (power != null) + { + power.Destroy (); + } + } + } + } + + } // Minotaur FX 1 ------------------------------------------------------------ @@ -179,8 +686,6 @@ class MinotaurFX2 : MinotaurFX1 DeathSound "minotaur/fx2hit"; } - native void A_MntrFloorFire(); - states { Spawn: @@ -191,6 +696,24 @@ class MinotaurFX2 : MinotaurFX1 FX13 JKLM 4 Bright; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_MntrFloorFire + // + //---------------------------------------------------------------------------- + + void A_MntrFloorFire() + { + SetZ(floorz); + double x = Random2[MntrFloorFire]() / 64.; + double y = Random2[MntrFloorFire]() / 64.; + + Actor mo = Spawn("MinotaurFX3", Vec2OffsetZ(x, y, floorz), ALLOW_REPLACE); + mo.target = target; + mo.Vel.X = MinVel; // Force block checking + mo.CheckMissileSpawn (radius); + } } // Minotaur FX 3 ------------------------------------------------------------ @@ -234,3 +757,50 @@ class MinotaurSmokeExit : Actor } } +extend class Actor +{ + enum dirtype_t + { + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS + }; + + void FaceMovementDirection() + { + switch (movedir) + { + case DI_EAST: + angle = 0.; + break; + case DI_NORTHEAST: + angle = 45.; + break; + case DI_NORTH: + angle = 90.; + break; + case DI_NORTHWEST: + angle = 135.; + break; + case DI_WEST: + angle = 180.; + break; + case DI_SOUTHWEST: + angle = 225.; + break; + case DI_SOUTH: + angle = 270.; + break; + case DI_SOUTHEAST: + angle = 315.; + break; + } + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/armor.txt b/wadsrc/static/zscript/shared/armor.txt new file mode 100644 index 000000000..3e7f57e21 --- /dev/null +++ b/wadsrc/static/zscript/shared/armor.txt @@ -0,0 +1,72 @@ +class Armor : Inventory native +{ + Default + { + Inventory.PickupSound "misc/armor_pkup"; + } +} + +class BasicArmor : Armor native +{ + + native int AbsorbCount; + native double SavePercent; + native int MaxAbsorb; + native int MaxFullAbsorb; + native int BonusCount; + native Name ArmorType; + native int ActualSaveAmount; + + Default + { + +Inventory.KEEPDEPLETED + } +} + +class BasicArmorBonus : Armor native +{ + native double SavePercent; // The default, for when you don't already have armor + native int MaxSaveAmount; + native int MaxAbsorb; + native int MaxFullAbsorb; + native int SaveAmount; + native int BonusCount; + native int BonusMax; + + Default + { + +Inventory.AUTOACTIVATE + +Inventory.ALWAYSPICKUP + Inventory.MaxAmount 0; + Armor.SavePercent 33.335; + } +} + +class BasicArmorPickup : Armor native +{ + + native double SavePercent; + native int MaxAbsorb; + native int MaxFullAbsorb; + native int SaveAmount; + + Default + { + +Inventory.AUTOACTIVATE; + Inventory.MaxAmount 0; + } +} + +class HexenArmor : Armor native +{ + + native double Slots[5]; + native double SlotsIncrement[4]; + + Default + { + +Inventory.KEEPDEPLETED + +Inventory.UNDROPPABLE + } +} + diff --git a/wadsrc/static/zscript/shared/botstuff.txt b/wadsrc/static/zscript/shared/botstuff.txt index cf671ea8b..3ae2d1889 100644 --- a/wadsrc/static/zscript/shared/botstuff.txt +++ b/wadsrc/static/zscript/shared/botstuff.txt @@ -23,3 +23,7 @@ class CajunTrace : Actor +NOTELEPORT } } + +struct Bot native +{ +} diff --git a/wadsrc/static/zscript/shared/camera.txt b/wadsrc/static/zscript/shared/camera.txt index 9420c98d5..abb394745 100644 --- a/wadsrc/static/zscript/shared/camera.txt +++ b/wadsrc/static/zscript/shared/camera.txt @@ -9,21 +9,118 @@ class DoomBuilderCamera : Actor } -class SecurityCamera : Actor native +class SecurityCamera : Actor { default { +NOBLOCKMAP +NOGRAVITY +DONTSPLASH - } - default - { RenderStyle "None"; CameraHeight 0; } + + double Center; + double Acc; + double Delta; + double Range; + + override void PostBeginPlay () + { + Super.PostBeginPlay (); + Center = Angle; + if (args[2]) + Delta = 360. / (args[2] * TICRATE / 8); + else + Delta = 0.; + if (args[1]) + Delta /= 2; + Acc = 0.; + Pitch = clamp(args[0], -89, 89); + Range = args[1]; + } + + override void Tick () + { + Acc += Delta; + if (Range != 0) + Angle = Center + Range * sin(Acc); + else if (Delta != 0) + Angle = Acc; + } + + } -class AimingCamera : SecurityCamera native +class AimingCamera : SecurityCamera { + double MaxPitchChange; + + override void PostBeginPlay () + { + int changepitch = args[2]; + + args[2] = 0; + Super.PostBeginPlay (); + MaxPitchChange = double(changepitch / TICRATE); + Range /= TICRATE; + + ActorIterator it = ActorIterator.Create(args[3]); + tracer = it.Next (); + if (tracer == NULL) + { + //Printf ("AimingCamera %d: Can't find TID %d\n", tid, args[3]); + } + else + { // Don't try for a new target upon losing this one. + args[3] = 0; + } + } + + override void Tick () + { + if (tracer == NULL && args[3] != 0) + { // Recheck, in case something with this TID was created since the last time. + ActorIterator it = ActorIterator.Create(args[3]); + tracer = it.Next (); + } + if (tracer != NULL) + { + double dir = deltaangle(angle, AngleTo(tracer)); + double delta = abs(dir); + if (delta > Range) + { + delta = Range; + } + if (dir > 0) + { + Angle += delta; + } + else + { + Angle -= delta; + } + if (MaxPitchChange != 0) + { // Aim camera's pitch; use floats for precision + Vector2 vect = tracer.Vec2To(self); + double dz = pos.z - tracer.pos.z - tracer.Height/2; + double dist = vect.Length(); + double desiredPitch = dist != 0.f ? VectorAngle(dist, dz) : 0.; + double diff = deltaangle(pitch, desiredPitch); + if (abs (diff) < MaxPitchChange) + { + pitch = desiredPitch; + } + else if (diff < 0) + { + pitch -= MaxPitchChange; + } + else + { + pitch += MaxPitchChange; + } + } + } + } + } diff --git a/wadsrc/static/zscript/shared/debris.txt b/wadsrc/static/zscript/shared/debris.txt index 24eae25be..3ffcea057 100644 --- a/wadsrc/static/zscript/shared/debris.txt +++ b/wadsrc/static/zscript/shared/debris.txt @@ -187,7 +187,7 @@ class Dirt6 : Actor // Stained glass ------------------------------------------------------------ -class GlassShard : Actor native +class GlassShard : Actor { Default { @@ -199,6 +199,16 @@ class GlassShard : Actor native BounceType "HexenCompat"; BounceFactor 0.3; } + + override void Tick() + { + Super.Tick(); + if (Vel.Z > 0 && Vel.Z < 0.5 && pos.z < floorz + 1) + { + Destroy (); + } + } + } class SGShard1 : GlassShard diff --git a/wadsrc/static/zscript/shared/fastprojectile.txt b/wadsrc/static/zscript/shared/fastprojectile.txt new file mode 100644 index 000000000..b1d509073 --- /dev/null +++ b/wadsrc/static/zscript/shared/fastprojectile.txt @@ -0,0 +1,41 @@ +// Fast projectiles -------------------------------------------------------- + +class FastProjectile : Actor native +{ + Default + { + Projectile; + MissileHeight 0; + } + + + virtual void Effect() + { + class trail = MissileName; + if (trail != null) + { + double hitz = pos.z - 8; + + if (hitz < floorz) + { + hitz = floorz; + } + // Do not clip this offset to the floor. + hitz += MissileHeight; + + Actor act = Spawn (trail, (pos.xy, hitz), ALLOW_REPLACE); + if (act != null) + { + if (bGetOwner && target != null) + act.target = target; + else + act.target = self; + + act.angle = angle; + act.pitch = pitch; + } + } + } + +} + diff --git a/wadsrc/static/zscript/shared/hatetarget.txt b/wadsrc/static/zscript/shared/hatetarget.txt index 6d2263f61..e90abcdbb 100644 --- a/wadsrc/static/zscript/shared/hatetarget.txt +++ b/wadsrc/static/zscript/shared/hatetarget.txt @@ -2,7 +2,7 @@ // Hate Target -------------------------------------------------------------- -class HateTarget : Actor native +class HateTarget : Actor { default { @@ -19,4 +19,32 @@ class HateTarget : Actor native Spawn: TNT1 A -1; } + + override void BeginPlay() + { + Super.BeginPlay(); + if (SpawnAngle != 0) + { // Each degree translates into 10 units of health + health = SpawnAngle * 10; + } + else + { + special2 = 1; + health = 1000001; + } + } + + override int TakeSpecialDamage(Actor inflictor, Actor source, int damage, Name damagetype) + { + if (special2 != 0) + { + return 0; + } + else + { + return damage; + } + } + + } \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/inv_misc.txt b/wadsrc/static/zscript/shared/inv_misc.txt new file mode 100644 index 000000000..032c9e826 --- /dev/null +++ b/wadsrc/static/zscript/shared/inv_misc.txt @@ -0,0 +1,87 @@ +class ScoreItem : Inventory +{ + Default + { + Height 10; + +COUNTITEM + Inventory.Amount 1; + +Inventory.ALWAYSPICKUP + } + + override bool TryPickup (in out Actor toucher) + { + toucher.Score += Amount; + GoAwayAndDie(); + return true; + } +} + +class Health : Inventory native +{ + native int PrevHealth; + + Default + { + Inventory.Amount 1; + Inventory.MaxAmount 0; + Inventory.PickupSound "misc/health_pkup"; + } +} + +class HealthPickup : Inventory native +{ + native int autousemode; + + Default + { + Inventory.DefMaxAmount; + +INVENTORY.INVBAR + } +} + +class Key : Inventory native +{ + native uint8 KeyNumber; + + Default + { + +DONTGIB; // Don't disappear due to a crusher + Inventory.InterHubAmount 0; + Inventory.PickupSound "misc/k_pkup"; + } +} + +class MapRevealer : Inventory +{ + //=========================================================================== + // + // AMapRevealer :: TryPickup + // + // A MapRevealer reveals the whole map for the player who picks it up. + // The MapRevealer doesn't actually go in your inventory. Instead, it sets + // a flag on the level. + // + //=========================================================================== + + override bool TryPickup (in out Actor toucher) + { + level.allmap = true; + GoAwayAndDie (); + return true; + } +} + +class PuzzleItem : Inventory native +{ + native int PuzzleItemNumber; + + Default + { + +NOGRAVITY + +INVENTORY.INVBAR + Inventory.DefMaxAmount; + Inventory.UseSound "PuzzleSuccess"; + Inventory.PickupSound "misc/i_pkup"; + } +} + diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt index 377463adc..06e4d7552 100644 --- a/wadsrc/static/zscript/shared/inventory.txt +++ b/wadsrc/static/zscript/shared/inventory.txt @@ -1,5 +1,20 @@ class Inventory : Actor native { + + native Actor Owner; // Who owns this item? NULL if it's still a pickup. + native int Amount; // Amount of item this instance has + native int MaxAmount; // Max amount of item this instance can have + native int InterHubAmount; // Amount of item that can be kept between hubs or levels + native int RespawnTics; // Tics from pickup time to respawn time + native TextureID Icon; // Icon to show on status bar or HUD + native int DropTime; // Countdown after dropping + native Class SpawnPointClass; // For respawning like Heretic's mace + native Class PickupFlash; // actor to spawn as pickup flash + native Sound PickupSound; + native bool bPickupGood; + native bool bCreateCopyMoved; + native bool bInitEffectFailed; + Default { Inventory.Amount 1; @@ -10,12 +25,37 @@ class Inventory : Actor native Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG"; } + virtual native bool Use (bool pickup); + virtual native color GetBlend (); + virtual native bool HandlePickup(Inventory item); + virtual native Inventory CreateCopy(Actor other); + virtual native Inventory CreateTossable(); + virtual native bool SpecialDropAction (Actor dropper); + virtual native String PickupMessage(); + virtual native bool ShouldStay(); + virtual native void DoEffect(); + virtual native void PlayPickupSound(Actor user); + virtual native void AttachToOwner(Actor user); + virtual native void DetachFromOwner(); + + virtual double GetSpeedFactor() { return 1; } + virtual bool GetNoTeleportFreeze() { return false; } + + native void GoAwayAndDie(); + native void BecomeItem(); + native void BecomePickup(); + // These are regular functions for the item itself. - private native void A_RestoreSpecialPosition(); private native void A_RestoreSpecialDoomThing(); private native void A_RestoreSpecialThing1(); private native void A_RestoreSpecialThing2(); + // In this case the caller function is more than a simple wrapper around the virtual method and + // is what must be actually called to pick up an item. + virtual protected native bool TryPickup(in out Actor toucher); + virtual protected native bool TryPickupRestricted(in out Actor toucher); + native bool, Actor CallTryPickup(Actor toucher); + States(Actor, Overlay, Weapon, Item) { HideDoomish: @@ -42,115 +82,31 @@ class Inventory : Actor native class StateProvider : Inventory native { action native state A_JumpIfNoAmmo(statelabel label); - action native void A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus", sound MeleeSound = 0, sound MissSound = ""); - action native void A_FireBullets(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", int flags = 1, float range = 0, class missile = null, float Spawnheight = 32, float Spawnofs_xy = 0); - action native void A_FireCustomMissile(class missiletype, float angle = 0, bool useammo = true, float spawnofs_xy = 0, float spawnheight = 0, int flags = 0, float pitch = 0); - action native void A_RailAttack(int damage, int spawnofs_xy = 0, bool useammo = true, color color1 = 0, color color2 = 0, int flags = 0, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270, int limit = 0); + action native void A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", double range = 0, double lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus", sound MeleeSound = 0, sound MissSound = ""); + action native void A_FireBullets(double spread_xy, double spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", int flags = 1, double range = 0, class missile = null, double Spawnheight = 32, double Spawnofs_xy = 0); + action native void A_FireCustomMissile(class missiletype, double angle = 0, bool useammo = true, double spawnofs_xy = 0, double spawnheight = 0, int flags = 0, double pitch = 0); + action native void A_RailAttack(int damage, int spawnofs_xy = 0, bool useammo = true, color color1 = 0, color color2 = 0, int flags = 0, double maxdiff = 0, class pufftype = "BulletPuff", double spread_xy = 0, double spread_z = 0, double range = 0, int duration = 0, double sparsity = 1.0, double driftspeed = 1.0, class spawnclass = "none", double spawnofs_z = 0, int spiraloffset = 270, int limit = 0); action native void A_WeaponReady(int flags = 0); action native void A_Lower(); action native void A_Raise(); - action native void A_FirePistol(); - action native void A_FireShotgun(); - action native void A_FireShotgun2(); - action void A_OpenShotgun2() { A_PlaySound("weapons/sshoto", CHAN_WEAPON); } - action void A_LoadShotgun2() { A_PlaySound("weapons/sshotl", CHAN_WEAPON); } - action void A_CloseShotgun2() { A_PlaySound("weapons/sshotc", CHAN_WEAPON); } - - action native void A_FireCGun(); - action native void A_FireSTGrenade(class grenadetype = "Grenade"); - action native void A_FireMissile(); - action native void A_FirePlasma(); - action native void A_FireRailgun(); - action native void A_FireRailgunLeft(); - action native void A_FireRailgunRight(); - action void A_RailWait() {} - action void A_BFGsound() { A_PlaySound("weapons/bfgf", CHAN_WEAPON); } - action native void A_FireBFG(); - action native void A_FireOldBFG(); action native void A_ReFire(statelabel flash = null); action native void A_ClearReFire(); action native void A_CheckReload(); action native void A_GunFlash(statelabel flash = null, int flags = 0); - action native void A_FireAssaultGun(); - action native void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff", int flags = 0, float range = 0, float spread_xy = 2.8125, float spread_z = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus"); action native state A_CheckForReload(int counter, statelabel label, bool dontincrement = false); action native void A_ResetReloadCounter(); } -class ScoreItem : Inventory native -{ - Default - { - Height 10; - +COUNTITEM - Inventory.Amount 1; - +Inventory.ALWAYSPICKUP - } -} - -class Ammo : Inventory native -{ - Default - { - +INVENTORY.KEEPDEPLETED - Inventory.PickupSound "misc/ammo_pkup"; - } -} - -class BackpackItem : Inventory native +class DehackedPickup : Inventory native { } -class Armor : Inventory native +class FakeInventory : Inventory native { - Default - { - Inventory.PickupSound "misc/armor_pkup"; - } + native bool Respawnable; } -class BasicArmor : Armor native -{ - Default - { - +Inventory.KEEPDEPLETED - } -} - -class BasicArmorBonus : Armor native -{ - Default - { - +Inventory.AUTOACTIVATE - +Inventory.ALWAYSPICKUP - Inventory.MaxAmount 0; - Armor.SavePercent 33.335; - } -} - -class BasicArmorPickup : Armor native -{ - Default - { - +Inventory.AUTOACTIVATE; - Inventory.MaxAmount 0; - } -} - -class HexenArmor : Armor native -{ - Default - { - +Inventory.KEEPDEPLETED - +Inventory.UNDROPPABLE - } -} - -class DehackedPickup : Inventory native {} - -class FakeInventory : Inventory native {} - class CustomInventory : StateProvider native { Default @@ -158,351 +114,3 @@ class CustomInventory : StateProvider native DefaultStateUsage SUF_ACTOR|SUF_OVERLAY|SUF_ITEM; } } - -class Health : Inventory native -{ - Default - { - Inventory.Amount 1; - Inventory.MaxAmount 0; - Inventory.PickupSound "misc/health_pkup"; - } -} - -class HealthPickup : Inventory native -{ - Default - { - Inventory.DefMaxAmount; - +INVENTORY.INVBAR - } -} - -class Key : Inventory native -{ - Default - { - +DONTGIB; // Don't disappear due to a crusher - Inventory.InterHubAmount 0; - Inventory.PickupSound "misc/k_pkup"; - } -} - -class PowerupGiver : Inventory native -{ - Default - { - Inventory.DefMaxAmount; - +INVENTORY.INVBAR - +INVENTORY.FANCYPICKUPSOUND - Inventory.PickupSound "misc/p_pkup"; - } -} - -class Powerup : Inventory native {} - -class PowerInvulnerable : Powerup native -{ - Default - { - Powerup.Duration -30; - inventory.icon "SPSHLD0"; - } -} - -class PowerStrength : Powerup native -{ - Default - { - Powerup.Duration 1; - Powerup.Color "ff 00 00", 0.5; - +INVENTORY.HUBPOWER - } -} - -class PowerInvisibility : Powerup native -{ - Default - { - +SHADOW; - Powerup.Duration -60; - Powerup.Strength 80; - Powerup.Mode "Fuzzy"; - } -} - -class PowerGhost : PowerInvisibility -{ - Default - { - +GHOST; - Powerup.Duration -60; - Powerup.Strength 60; - Powerup.Mode "None"; - } -} - -class PowerShadow : PowerInvisibility -{ - Default - { - +INVENTORY.HUBPOWER - Powerup.Duration -55; - Powerup.Strength 75; - Powerup.Mode "Cumulative"; - } -} - -class PowerIronFeet : Powerup native -{ - Default - { - Powerup.Duration -60; - Powerup.Color "00 ff 00", 0.125; - } -} - -class PowerMask : PowerIronFeet native -{ - Default - { - Powerup.Duration -80; - Powerup.Color "00 00 00", 0; - +INVENTORY.HUBPOWER - Inventory.Icon "I_MASK"; - } -} - -class PowerLightAmp : Powerup native -{ - Default - { - Powerup.Duration -120; - } -} - -class PowerTorch : PowerLightAmp native {} - -class PowerFlight : Powerup native -{ - Default - { - Powerup.Duration -60; - +INVENTORY.HUBPOWER - } -} - -class PowerWeaponLevel2 : Powerup native -{ - Default - { - Powerup.Duration -40; - Inventory.Icon "SPINBK0"; - +INVENTORY.NOTELEPORTFREEZE - } -} - -class PowerSpeed : Powerup native -{ - Default - { - Powerup.Duration -45; - Speed 1.5; - Inventory.Icon "SPBOOT0"; - +INVENTORY.NOTELEPORTFREEZE - } -} - -// Player Speed Trail (used by the Speed Powerup) ---------------------------- - -class PlayerSpeedTrail native -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - Alpha 0.6; - RenderStyle "Translucent"; - } -} - -class PowerMinotaur : Powerup native -{ - Default - { - Powerup.Duration -25; - Inventory.Icon "SPMINO0"; - } -} - -class PowerTargeter : Powerup native -{ - Default - { - Powerup.Duration -160; - +INVENTORY.HUBPOWER - } - States - { - Targeter: - TRGT A -1; - Stop; - TRGT B -1; - Stop; - TRGT C -1; - Stop; - } -} - -class PowerFrightener : Powerup native -{ - Default - { - Powerup.Duration -60; - } -} - -class PowerBuddha : Powerup native -{ - Default - { - Powerup.Duration -60; - } -} - -class PowerScanner : Powerup native -{ - Default - { - Powerup.Duration -80; - +INVENTORY.HUBPOWER - } -} - -class PowerTimeFreezer : Powerup native -{ - Default - { - Powerup.Duration -12; - } -} - -class PowerDamage : Powerup native -{ - Default - { - Powerup.Duration -25; - } -} - -class PowerProtection : Powerup native -{ - Default - { - Powerup.Duration -25; - } -} - -class PowerDrain : Powerup native -{ - Default - { - Powerup.Duration -60; - } -} - -class PowerRegeneration : Powerup native -{ - Default - { - Powerup.Duration -120; - Powerup.Strength 5; - } -} - -class PowerHighJump : Powerup native {} - -class PowerDoubleFiringSpeed : Powerup native {} - -class PowerMorph : Powerup native -{ - Default - { - Powerup.Duration -40; - } -} - -class PowerInfiniteAmmo : Powerup native -{ - Default - { - Powerup.Duration -30; - } -} - -class MapRevealer : Inventory native {} - -class PuzzleItem : Inventory native -{ - Default - { - +NOGRAVITY - +INVENTORY.INVBAR - Inventory.DefMaxAmount; - Inventory.UseSound "PuzzleSuccess"; - Inventory.PickupSound "misc/i_pkup"; - } -} - -class Weapon : StateProvider native -{ - Default - { - Inventory.PickupSound "misc/w_pkup"; - Weapon.DefaultKickback; - Weapon.BobSpeed 1.0; - Weapon.BobRangeX 1.0; - Weapon.BobRangeY 1.0; - +WEAPONSPAWN - DefaultStateUsage SUF_ACTOR|SUF_OVERLAY|SUF_WEAPON; - } - States - { - LightDone: - SHTG E 0 A_Light0; - Stop; - } - - native void A_ZoomFactor(float scale = 1, int flags = 0); - const ZOOM_INSTANT = 1; - const ZOOM_NOSCALETURNING = 2; - - native void A_SetCrosshair(int xhair); -} - -class WeaponGiver : Weapon native -{ - Default - { - Weapon.AmmoGive1 -1; - Weapon.AmmoGive2 -1; - } -} - -class WeaponHolder : Inventory native -{ - Default - { - +NOBLOCKMAP - +NOSECTOR - +INVENTORY.UNDROPPABLE - } -} - -class WeaponPiece : Inventory native -{ - Default - { - +WEAPONSPAWN; - } -} diff --git a/wadsrc/static/zscript/shared/pickups.txt b/wadsrc/static/zscript/shared/itemeffects.txt similarity index 100% rename from wadsrc/static/zscript/shared/pickups.txt rename to wadsrc/static/zscript/shared/itemeffects.txt diff --git a/wadsrc/static/zscript/shared/mapmarker.txt b/wadsrc/static/zscript/shared/mapmarker.txt index 1afa00459..25e197307 100644 --- a/wadsrc/static/zscript/shared/mapmarker.txt +++ b/wadsrc/static/zscript/shared/mapmarker.txt @@ -1,5 +1,20 @@ +// Map Marker -------------------------------------------------------------- +// +// This class uses the following argument: +// args[0] == 0, shows the sprite at this actor +// != 0, shows the sprite for all actors whose TIDs match instead +// +// args[1] == 0, show the sprite always +// == 1, show the sprite only after its sector has been drawn +// +// To enable display of the sprite, activate it. To turn off the sprite, +// deactivate it. +// +// All the code to display it is in am_map.cpp. +// +//-------------------------------------------------------------------------- -class MapMarker : Actor native +class MapMarker : Actor { default { @@ -15,4 +30,20 @@ class MapMarker : Actor native AMRK A -1; Stop; } + + override void BeginPlay () + { + ChangeStatNum (STAT_MAPMARKER); + } + + override void Activate (Actor activator) + { + bDormant = true; + } + + override void Deactivate (Actor activator) + { + bDormant = false; + } + } diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index ff58d897b..c5c2cbe0a 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -1,5 +1,10 @@ class MorphProjectile : Actor native { + + native Class PlayerClass; + native Class MonsterClass, MorphFlash, UnMorphFlash; + native int Duration, MorphStyle; + Default { Damage 1; @@ -11,6 +16,10 @@ class MorphProjectile : Actor native class MorphedMonster : Actor native { + native Actor UnmorphedMe; + native int UnmorphTime, MorphStyle; + native Class MorphExitFlash; + Default { Monster; diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 224011acd..2f6dc4bc1 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1,5 +1,43 @@ class PlayerPawn : Actor native { + + native int crouchsprite; + native int MaxHealth; + native int MugShotMaxHealth; + native int RunHealth; + native int PlayerFlags; + native Inventory InvFirst; // first inventory item displayed on inventory bar + native Inventory InvSel; // selected inventory item + native meta String DisplayName; // Display name (used in menus, etc.) + native meta String SoundClass; // Sound class + native meta String Face; // Doom status bar face (when used) + native meta String Portrait; + native meta String Slot[10]; + native meta Name InvulMode; + native meta Name HealingRadiusType; + native meta double HexenArmor[5]; + native meta uint8 ColorRangeStart; // Skin color range + native meta uint8 ColorRangeEnd; + //FPlayerColorSetMap ColorSets; + //PainFlashList PainFlashes; + + // [GRB] Player class properties + native double JumpZ; + native double GruntSpeed; + native double FallingScreamMinSpeed, FallingScreamMaxSpeed; + native double ViewHeight; + native double ForwardMove1, ForwardMove2; + native double SideMove1, SideMove2; + native TextureID ScoreIcon; + native int SpawnMask; + native Name MorphWeapon; + native double AttackZOffset; // attack height, relative to player center + native double UseRange; // [NS] Distance at which player can +use + native double AirCapacity; // Multiplier for air supply underwater. + native Class FlechetteType; + native color DamageFade; // [CW] Fades for when you are being damaged. + native double ViewBob; // [SP] ViewBob Multiplier + Default { Health 100; @@ -38,6 +76,35 @@ class PlayerPawn : Actor native Player.ViewBob 1; Obituary "$OB_MPDEFAULT"; } + + virtual void PlayIdle () + { + if (InStateSequence(CurState, SeeState)) + SetState (SpawnState); + } + + virtual void PlayRunning () + { + if (InStateSequence(CurState, SpawnState) && SeeState != NULL) + SetState (SeeState); + } + + virtual void PlayAttacking () + { + if (MissileState != null) SetState (MissileState); + } + + virtual void PlayAttacking2 () + { + if (MeleeState != null) SetState (MeleeState); + } + + virtual void MorphPlayerThink() + { + } + + native int GetMaxHealth(); + native bool ResetAirSupply (bool playgasp = false); } class PlayerChunk : PlayerPawn native @@ -57,3 +124,141 @@ class PlayerChunk : PlayerPawn native -TELESTOMP } } + +class PSprite : Object native +{ + native readonly State CurState; + native readonly Actor Caller; + native readonly PSprite Next; + native readonly PlayerInfo Owner; + native SpriteID Sprite; + native int Frame; + native readonly int ID; + native Bool processPending; + native double x; + native double y; + native double oldx; + native double oldy; + native Bool firstTic; + native int Tics; + native bool bAddWeapon; + native bool bAddBob; + native bool bPowDouble; + native bool bCVarFast; + native bool bFlip; + + native void SetState(State newstate, bool pending = false); + +} + +enum EPlayerState +{ + PST_LIVE, // Playing or camping. + PST_DEAD, // Dead on the ground, view follows killer. + PST_REBORN, // Ready to restart/respawn??? + PST_ENTER, // [BC] Entered the game + PST_GONE // Player has left the game +} + +struct PlayerInfo native // this is what internally is known as player_t +{ + native readonly PlayerPawn mo; + native uint8 playerstate; + native uint original_oldbuttons; + native readonly Class cls; + native float DesiredFOV; + native readonly float FOV; + native double viewz; + native double viewheight; + native double deltaviewheight; + native double bob; + native vector2 vel; + native bool centering; + native uint8 turnticks; + native bool attackdown; + native bool usedown; + native uint oldbuttons; + native int health; + native int inventorytics; + native uint8 CurrentPlayerClass; + native int frags[MAXPLAYERS]; + native int fragcount; + native int lastkilltime; + native uint8 multicount; + native uint8 spreecount; + native uint16 WeaponState; + native Weapon ReadyWeapon; + native Weapon PendingWeapon; + native PSprite psprites; + native int cheats; + native int timefreezer; + native int16 refire; + native int16 inconsistent; + native bool waiting; + native int killcount; + native int itemcount; + native int secretcount; + native int damagecount; + native int bonuscount; + native int hazardcount; + native int hazardinterval; + native Name hazardtype; + native int poisoncount; + native Name poisontype; + native Name poisonpaintype; + native Actor poisoner; + native Actor attacker; + native int extralight; + native int16 fixedcolormap; + native int16 fixedlightlevel; + native int morphtics; + native ClassMorphedPlayerClass; + native int MorphStyle; + native Class MorphExitFlash; + native Class PremorphWeapon; + native int chickenPeck; + native int jumpTics; + native bool onground; + native int respawn_time; + native Actor camera; + native int air_finished; + native Name LastDamageType; + native Actor MUSINFOactor; + native int8 MUSINFOtics; + native bool settings_controller; + native int8 crouching; + native int8 crouchdir; + native Bot bot; + native float BlendR; + native float BlendG; + native float BlendB; + native float BlendA; + native String LogText; + native double MinPitch; + native double MaxPitch; + native double crouchfactor; + native double crouchoffset; + native double crouchviewdelta; + native Actor ConversationNPC; + native Actor ConversationPC; + native double ConversationNPCAngle; + native bool ConversationFaceTalker; +/* these are not doable yet +ticcmd_t cmd; +usercmd_t original_cmd; +userinfo_t userinfo; // [RH] who is this? +FWeaponSlots weapons; +*/ + + + native bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false); + native bool PoisonPlayer(Actor poisoner, Actor source, int poison); + native void PoisonDamage(Actor source, int damage, bool playPainSound); + native void SetPsprite(int id, State stat, bool pending = false); + native void SetSafeFlash(Weapon weap, State flashstate, int index); + native PSprite GetPSprite(int id); + native PSprite FindPSprite(int id); + native void SetLogNumber (int text); + native void SetLogText (String text); + +} diff --git a/wadsrc/static/zscript/shared/powerups.txt b/wadsrc/static/zscript/shared/powerups.txt new file mode 100644 index 000000000..d28799ff2 --- /dev/null +++ b/wadsrc/static/zscript/shared/powerups.txt @@ -0,0 +1,288 @@ +class PowerupGiver : Inventory native +{ + + native Class PowerupType; + native int EffectTics; // Non-0 to override the powerup's default tics + native color BlendColor; // Non-0 to override the powerup's default blend + native Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility + native double Strength; // Meaning depends on powerup - currently used only by Invisibility + + Default + { + Inventory.DefMaxAmount; + +INVENTORY.INVBAR + +INVENTORY.FANCYPICKUPSOUND + Inventory.PickupSound "misc/p_pkup"; + } +} + +class Powerup : Inventory native +{ + native int EffectTics; + native color BlendColor; + native Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility + native double Strength; // Meaning depends on powerup - currently used only by Invisibility + + // Note, that while this is an inventory flag, it only has meaning on an active powerup. + override bool GetNoTeleportFreeze() { return bNoTeleportFreeze; } + +} + +class PowerInvulnerable : Powerup native +{ + Default + { + Powerup.Duration -30; + inventory.icon "SPSHLD0"; + } +} + +class PowerStrength : Powerup native +{ + Default + { + Powerup.Duration 1; + Powerup.Color "ff 00 00", 0.5; + +INVENTORY.HUBPOWER + } +} + +class PowerInvisibility : Powerup native +{ + Default + { + +SHADOW; + Powerup.Duration -60; + Powerup.Strength 80; + Powerup.Mode "Fuzzy"; + } +} + +class PowerGhost : PowerInvisibility +{ + Default + { + +GHOST; + Powerup.Duration -60; + Powerup.Strength 60; + Powerup.Mode "None"; + } +} + +class PowerShadow : PowerInvisibility +{ + Default + { + +INVENTORY.HUBPOWER + Powerup.Duration -55; + Powerup.Strength 75; + Powerup.Mode "Cumulative"; + } +} + +class PowerIronFeet : Powerup native +{ + Default + { + Powerup.Duration -60; + Powerup.Color "00 ff 00", 0.125; + } +} + +class PowerMask : PowerIronFeet native +{ + Default + { + Powerup.Duration -80; + Powerup.Color "00 00 00", 0; + +INVENTORY.HUBPOWER + Inventory.Icon "I_MASK"; + } +} + +class PowerLightAmp : Powerup native +{ + Default + { + Powerup.Duration -120; + } +} + +class PowerTorch : PowerLightAmp native {} + +class PowerFlight : Powerup native +{ + Default + { + Powerup.Duration -60; + +INVENTORY.HUBPOWER + } +} + +class PowerWeaponLevel2 : Powerup native +{ + Default + { + Powerup.Duration -40; + Inventory.Icon "SPINBK0"; + +INVENTORY.NOTELEPORTFREEZE + } +} + +class PowerSpeed : Powerup native +{ + native int SpeedFlags; + + Default + { + Powerup.Duration -45; + Speed 1.5; + Inventory.Icon "SPBOOT0"; + +INVENTORY.NOTELEPORTFREEZE + } + + override double GetSpeedFactor() { return Speed; } +} + +// Player Speed Trail (used by the Speed Powerup) ---------------------------- + +class PlayerSpeedTrail : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + Alpha 0.6; + RenderStyle "Translucent"; + } + + override void Tick() + { + Alpha -= .6 / 8; + if (Alpha <= 0) + { + Destroy (); + } + } +} + +class PowerMinotaur : Powerup native +{ + Default + { + Powerup.Duration -25; + Inventory.Icon "SPMINO0"; + } +} + +class PowerTargeter : Powerup native +{ + Default + { + Powerup.Duration -160; + +INVENTORY.HUBPOWER + } + States + { + Targeter: + TRGT A -1; + Stop; + TRGT B -1; + Stop; + TRGT C -1; + Stop; + } +} + +class PowerFrightener : Powerup native +{ + Default + { + Powerup.Duration -60; + } +} + +class PowerBuddha : Powerup native +{ + Default + { + Powerup.Duration -60; + } +} + +class PowerScanner : Powerup native +{ + Default + { + Powerup.Duration -80; + +INVENTORY.HUBPOWER + } +} + +class PowerTimeFreezer : Powerup native +{ + Default + { + Powerup.Duration -12; + } +} + +class PowerDamage : Powerup native +{ + Default + { + Powerup.Duration -25; + } +} + +class PowerProtection : Powerup native +{ + Default + { + Powerup.Duration -25; + } +} + +class PowerDrain : Powerup native +{ + Default + { + Powerup.Duration -60; + } +} + +class PowerRegeneration : Powerup native +{ + Default + { + Powerup.Duration -120; + Powerup.Strength 5; + } +} + +class PowerHighJump : Powerup native {} + +class PowerDoubleFiringSpeed : Powerup native {} + +class PowerMorph : Powerup native +{ + native Class PlayerClass; + native Class MorphFlash, UnMorphFlash; + native int MorphStyle; + native PlayerInfo MorphedPlayer; + native bool bInUndoMorph; + + Default + { + Powerup.Duration -40; + } +} + +class PowerInfiniteAmmo : Powerup native +{ + Default + { + Powerup.Duration -30; + } +} + diff --git a/wadsrc/static/zscript/shared/secrettrigger.txt b/wadsrc/static/zscript/shared/secrettrigger.txt index 48c93686e..c60399600 100644 --- a/wadsrc/static/zscript/shared/secrettrigger.txt +++ b/wadsrc/static/zscript/shared/secrettrigger.txt @@ -1,5 +1,5 @@ -class SecretTrigger : Actor native +class SecretTrigger : Actor { default { @@ -8,5 +8,19 @@ class SecretTrigger : Actor native +NOGRAVITY +DONTSPLASH } + + override void PostBeginPlay () + { + Super.PostBeginPlay (); + level.total_secrets++; + } + + override void Activate (Actor activator) + { + activator.GiveSecret(args[0] <= 1, (args[0] == 0 || args[0] == 2)); + Destroy (); + } + + } diff --git a/wadsrc/static/zscript/shared/setcolor.txt b/wadsrc/static/zscript/shared/setcolor.txt index 8689e5fd1..18105ef54 100644 --- a/wadsrc/static/zscript/shared/setcolor.txt +++ b/wadsrc/static/zscript/shared/setcolor.txt @@ -1,4 +1,4 @@ -class ColorSetter : Actor native +class ColorSetter : Actor { default { @@ -7,10 +7,18 @@ class ColorSetter : Actor native +DONTSPLASH RenderStyle "None"; } + + override void PostBeginPlay() + { + Super.PostBeginPlay(); + CurSector.SetColor(color(args[0], args[1], args[2]), args[3]); + Destroy(); + } + } -class FadeSetter : Actor native +class FadeSetter : Actor { default { @@ -19,4 +27,13 @@ class FadeSetter : Actor native +DONTSPLASH RenderStyle "None"; } + + void PostBeginPlay() + { + Super.PostBeginPlay(); + CurSector.SetFade(color(args[0], args[1], args[2])); + Destroy(); + } + + } diff --git a/wadsrc/static/zscript/shared/sharedmisc.txt b/wadsrc/static/zscript/shared/sharedmisc.txt index bebf28549..1b5ad4ee9 100644 --- a/wadsrc/static/zscript/shared/sharedmisc.txt +++ b/wadsrc/static/zscript/shared/sharedmisc.txt @@ -157,13 +157,25 @@ class CustomSprite : Actor native // SwitchableDecoration: Activate and Deactivate change state -------------- -class SwitchableDecoration : Actor native +class SwitchableDecoration : Actor { + override void Activate (Actor activator) + { + SetStateLabel("Active"); + } + + override void Deactivate (Actor activator) + { + SetStateLabel("Inactive"); + } + } - -class SwitchingDecoration : SwitchableDecoration native +class SwitchingDecoration : SwitchableDecoration { + override void Deactivate (Actor activator) + { + } } // Random spawner ---------------------------------------------------------- @@ -179,20 +191,9 @@ class RandomSpawner : Actor native } } -// Fast projectiles -------------------------------------------------------- - -class FastProjectile : Actor native -{ - Default - { - Projectile; - MissileHeight 0; - } -} - // Sector flag setter ------------------------------------------------------ -class SectorFlagSetter : Actor native +class SectorFlagSetter : Actor { Default { @@ -201,6 +202,12 @@ class SectorFlagSetter : Actor native +DONTSPLASH RenderStyle "None"; } + + override void BeginPlay () + { + Super.BeginPlay (); + CurSector.Flags |= args[0]; + } } // Marker for sounds : Actor ------------------------------------------------------- diff --git a/wadsrc/static/zscript/shared/spark.txt b/wadsrc/static/zscript/shared/spark.txt index 5adffdfe1..c5ae6a455 100644 --- a/wadsrc/static/zscript/shared/spark.txt +++ b/wadsrc/static/zscript/shared/spark.txt @@ -1,5 +1,5 @@ -class Spark : Actor native +class Spark : Actor { default { @@ -8,4 +8,12 @@ class Spark : Actor native +NOGRAVITY +DONTSPLASH } + + override void Activate (Actor activator) + { + Super.Activate (activator); + DrawSplash (args[0] ? args[0] : 32, Angle, 1); + A_PlaySound ("world/spark", CHAN_AUTO, 1, false, ATTN_STATIC); + } + } \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/waterzone.txt b/wadsrc/static/zscript/shared/waterzone.txt index 975c8a473..49d950b00 100644 --- a/wadsrc/static/zscript/shared/waterzone.txt +++ b/wadsrc/static/zscript/shared/waterzone.txt @@ -1,4 +1,4 @@ -class WaterZone : Actor native +class WaterZone : Actor { default { @@ -7,4 +7,13 @@ class WaterZone : Actor native +NOGRAVITY +DONTSPLASH } + + override void PostBeginPlay () + { + Super.PostBeginPlay (); + CurSector.MoreFlags |= Sector.SECMF_UNDERWATER; + Destroy (); + } + + } diff --git a/wadsrc/static/zscript/shared/weapons.txt b/wadsrc/static/zscript/shared/weapons.txt new file mode 100644 index 000000000..c3ebf299d --- /dev/null +++ b/wadsrc/static/zscript/shared/weapons.txt @@ -0,0 +1,144 @@ +class Weapon : StateProvider native +{ + enum EFireMode + { + PrimaryFire, + AltFire, + EitherFire + }; + + native uint WeaponFlags; + native class AmmoType1, AmmoType2; // Types of ammo used by this weapon + native int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon + native int MinAmmo1, MinAmmo2; // Minimum ammo needed to switch to this weapon + native int AmmoUse1, AmmoUse2; // How much ammo to use with each shot + native int Kickback; + native float YAdjust; // For viewing the weapon fullscreen (visual only so no need to be a double) + native sound UpSound, ReadySound; // Sounds when coming up and idle + native class SisterWeaponType; // Another weapon to pick up with this one + native class ProjectileType; // Projectile used by primary attack + native class AltProjectileType; // Projectile used by alternate attack + native int SelectionOrder; // Lower-numbered weapons get picked first + native int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo + native double MoveCombatDist; // Used by bots, but do they *really* need it? + native int ReloadCounter; // For A_CheckForReload + native int BobStyle; // [XA] Bobbing style. Defines type of bobbing (e.g. Normal, Alpha) (visual only so no need to be a double) + native float BobSpeed; // [XA] Bobbing speed. Defines how quickly a weapon bobs. + native float BobRangeX, BobRangeY; // [XA] Bobbing range. Defines how far a weapon bobs in either direction. + native Ammo Ammo1, Ammo2; // In-inventory instance variables + native Weapon SisterWeapon; + native float FOVScale; + native int Crosshair; // 0 to use player's crosshair + native bool GivenAsMorphWeapon; + native bool bAltFire; // Set when this weapon's alternate fire is used. + native readonly bool bDehAmmo; + + Default + { + Inventory.PickupSound "misc/w_pkup"; + Weapon.DefaultKickback; + Weapon.BobSpeed 1.0; + Weapon.BobRangeX 1.0; + Weapon.BobRangeY 1.0; + +WEAPONSPAWN + DefaultStateUsage SUF_ACTOR|SUF_OVERLAY|SUF_WEAPON; + } + States + { + LightDone: + SHTG E 0 A_Light0; + Stop; + } + + native bool CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo = false, int ammocount = -1); + native bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1); + native virtual void EndPowerup(); + + virtual State GetReadyState () + { + return FindState('Ready'); + } + + virtual State GetUpState () + { + return FindState('Select'); + } + + virtual State GetDownState () + { + return FindState('Deselect'); + } + + virtual State GetAtkState (bool hold) + { + State s = null; + if (hold) s = FindState('Hold'); + if (s == null) s = FindState('Fire'); + return s; + } + + virtual State GetAltAtkState (bool hold) + { + State s = null; + if (hold) s = FindState('AltHold'); + if (s == null) s = FindState('AltFire'); + return s; + } + + native action void A_ZoomFactor(double scale = 1, int flags = 0); + native action void A_SetCrosshair(int xhair); + const ZOOM_INSTANT = 1; + const ZOOM_NOSCALETURNING = 2; + +} + +class WeaponGiver : Weapon native +{ + + native double DropAmmoFactor; + + Default + { + Weapon.AmmoGive1 -1; + Weapon.AmmoGive2 -1; + } +} + +class WeaponHolder : Inventory native +{ + native int PieceMask; + native Class PieceWeapon; + + Default + { + +NOBLOCKMAP + +NOSECTOR + +INVENTORY.UNDROPPABLE + } +} + +class WeaponPiece : Inventory native +{ + Default + { + +WEAPONSPAWN; + } +} + +class Ammo : Inventory native +{ + native int BackpackAmount; + native int BackpackMaxAmount; + + Default + { + +INVENTORY.KEEPDEPLETED + Inventory.PickupSound "misc/ammo_pkup"; + } +} + +class BackpackItem : Inventory native +{ + native bool bDepleted; +} + diff --git a/wadsrc/static/zscript/strife/acolyte.txt b/wadsrc/static/zscript/strife/acolyte.txt index 0c1305b57..e5a019ea8 100644 --- a/wadsrc/static/zscript/strife/acolyte.txt +++ b/wadsrc/static/zscript/strife/acolyte.txt @@ -26,10 +26,6 @@ class Acolyte : StrifeHumanoid Obituary "$OB_ACOLYTE"; } - native void A_BeShadowyFoe (); - native void A_AcolyteBits (); - native void A_AcolyteDie (); - States { Spawn: @@ -76,6 +72,79 @@ class Acolyte : StrifeHumanoid GIBS L 1400; Stop; } + + //============================================================================ + // + // A_AcolyteDie + // + //============================================================================ + + void A_AcolyteDie () + { + // [RH] Disable translucency here. + A_SetRenderStyle(1, STYLE_Normal); + + // Only the Blue Acolyte does extra stuff on death. + if (self is "AcolyteBlue") + { + int i; + // Make sure somebody is still alive + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].health > 0) + break; + } + if (i == MAXPLAYERS) + return; + + // Make sure all the other blue acolytes are dead, but do this only once in case of simultaneous kills. + if (CheckBossDeath() && !players[i].mo.FindInventory("QuestItem7")) + { + players[i].mo.GiveInventoryType ("QuestItem7"); + players[i].SetLogNumber (14); + A_StopSound (CHAN_VOICE); + A_PlaySound ("svox/voc14", CHAN_VOICE); + } + } + } + + //============================================================================ + // + // A_BeShadowyFoe + // + //============================================================================ + + void A_BeShadowyFoe() + { + A_SetRenderStyle(HR_SHADOW, STYLE_Translucent); + bFriendly = false; + } + + //============================================================================ + // + // A_AcolyteBits + // + //============================================================================ + + void A_AcolyteBits() + { + if (SpawnFlags & MTF_SHADOW) + { + A_BeShadowyFoe(); + } + if (SpawnFlags & MTF_ALTSHADOW) + { + if (bShadow) + { + // I dunno. + } + else + { + A_SetRenderStyle(0, STYLE_None); + } + } + } + } @@ -202,8 +271,6 @@ class AcolyteToBe : Acolyte -ISMONSTER } - native void A_HideDecepticon (); - States { Spawn: @@ -215,4 +282,27 @@ class AcolyteToBe : Acolyte Death: Goto XDeath; } + + //============================================================================ + // + // A_HideDecepticon + // + // Hide the Acolyte-to-be -> + // Hide the guy transforming into an Acolyte -> + // Hide the transformer -> + // Transformers are Autobots and Decepticons, and + // Decepticons are the bad guys, so... -> + // + // Hide the Decepticon! + // + //============================================================================ + + void A_HideDecepticon () + { + Door_Close(999, 64); + if (target != null && target.player != null) + { + NoiseAlert (target); + } + } } diff --git a/wadsrc/static/zscript/strife/alienspectres.txt b/wadsrc/static/zscript/strife/alienspectres.txt index 0fe9826f0..39cbc8331 100644 --- a/wadsrc/static/zscript/strife/alienspectres.txt +++ b/wadsrc/static/zscript/strife/alienspectres.txt @@ -32,8 +32,6 @@ class AlienSpectre1 : SpectralMonster +NOICEDEATH } - native void A_AlienSpectreDeath (); - States { Spawn: @@ -76,6 +74,116 @@ class AlienSpectre1 : SpectralMonster AL1P R 5 Bright A_AlienSpectreDeath; Stop; } + + //============================================================================ + + void A_AlienSpectreDeath () + { + PlayerPawn player = null; + int log = 0; + + A_NoBlocking(); // [RH] Need this for Sigil rewarding + if (!CheckBossDeath ()) + { + return; + } + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].health > 0) + { + player = players[i].mo; + break; + } + } + if (player == null) + { + return; + } + + class cls = GetClass(); + if (cls == "AlienSpectre1") + { + Floor_LowerToLowest(999, 8); + log = 95; + } + else if (cls == "AlienSpectre2") + { + C_MidPrint("SmallFont", "$TXT_KILLED_BISHOP"); + log = 74; + player.GiveInventoryType ("QuestItem21"); + } + else if (cls == "AlienSpectre3") + { + C_MidPrint("SmallFont", "$TXT_KILLED_ORACLE"); + // If there are any Oracles still alive, kill them. + ThinkerIterator it = ThinkerIterator.Create("Oracle"); + Actor oracle; + + while ( (oracle = Actor(it.Next())) != null) + { + if (oracle.health > 0) + { + oracle.health = 0; + oracle.Die (self, self); + } + } + player.GiveInventoryType ("QuestItem23"); + if (player.FindInventory ("QuestItem21")) + { // If the Bishop is dead, set quest item 22 + player.GiveInventoryType ("QuestItem22"); + } + if (player.FindInventory ("QuestItem24") == null) + { // Macil is calling us back... + log = 87; + } + else + { // You wield the power of the complete Sigil. + log = 85; + } + Door_Open(222, 64); + } + else if (cls == "AlienSpectre4") + { + C_MidPrint("SmallFont", "$TXT_KILLED_MACIL"); + player.GiveInventoryType ("QuestItem24"); + if (player.FindInventory ("QuestItem25") == null) + { // Richter has taken over. Macil is a snake. + log = 79; + } + else + { // Back to the factory for another Sigil! + log = 106; + } + } + else if (cls == "AlienSpectre5") + { + C_MidPrint("SmallFont", "$TXT_KILLED_LOREMASTER"); + + player.GiveInventoryType ("QuestItem26"); + if (!multiplayer) + { + player.GiveInventoryType ("UpgradeStamina"); + player.GiveInventoryType ("UpgradeAccuracy"); + } + Sigil sigl = Sigil(player.FindInventory("Sigil")); + if (sigl != null /*&& sigl.NumPieces == 5*/) + { // You wield the power of the complete Sigil. + log = 85; + } + else + { // Another Sigil piece. Woohoo! + log = 83; + } + Floor_LowerToLowest(666, 8); + } + if (log > 0) + { + String voc = "svox/voc" .. log; + A_PlaySound(voc, CHAN_VOICE); + player.player.SetLogNumber (log); + } + } + } diff --git a/wadsrc/static/zscript/strife/coin.txt b/wadsrc/static/zscript/strife/coin.txt index a930cb0ba..b94a3f1d1 100644 --- a/wadsrc/static/zscript/strife/coin.txt +++ b/wadsrc/static/zscript/strife/coin.txt @@ -1,7 +1,7 @@ // Coin --------------------------------------------------------------------- -class Coin : Inventory native +class Coin : Inventory { Default { @@ -20,6 +20,108 @@ class Coin : Inventory native COIN A -1; Stop; } + + // Coin --------------------------------------------------------------------- + + override String PickupMessage () + { + if (Amount == 1) + { + return Super.PickupMessage(); + } + else + { + String msg = StringTable.Localize("TXT_XGOLD"); + msg.Replace("%d", "" .. Amount); + return msg; + } + } + + override bool HandlePickup (Inventory item) + { + if (item is "Coin") + { + if (Amount < MaxAmount) + { + if (MaxAmount - Amount < item.Amount) + { + Amount = MaxAmount; + } + else + { + Amount += item.Amount; + } + item.bPickupGood = true; + } + return true; + } + return false; + } + + override Inventory CreateCopy (Actor other) + { + if (GetClass() == "Coin") + { + return Super.CreateCopy (other); + } + Inventory copy = Inventory(Spawn("Coin")); + copy.Amount = Amount; + copy.BecomeItem (); + GoAwayAndDie (); + return copy; + } + + //=========================================================================== + // + // ACoin :: CreateTossable + // + // Gold drops in increments of 50 if you have that much, less if you don't. + // + //=========================================================================== + + override Inventory CreateTossable () + { + Coin tossed; + + if (bUndroppable || Owner == NULL || Amount <= 0) + { + return NULL; + } + if (Amount >= 50) + { + Amount -= 50; + tossed = Coin(Spawn("Gold50")); + } + else if (Amount >= 25) + { + Amount -= 25; + tossed = Coin(Spawn("Gold25")); + } + else if (Amount >= 10) + { + Amount -= 10; + tossed = Coin(Spawn("Gold10")); + } + else if (Amount > 1 || bKeepDepleted) + { + Amount -= 1; + tossed = Coin(Spawn("Coin")); + } + else // Amount == 1 && !(ItemFlags & IF_KEEPDEPLETED) + { + BecomePickup (); + tossed = self; + } + tossed.bSpecial = false; + tossed.bSolid = false; + tossed.DropTime = 30; + if (tossed != self && Amount <= 0) + { + Destroy (); + } + return tossed; + } + } diff --git a/wadsrc/static/zscript/strife/crusader.txt b/wadsrc/static/zscript/strife/crusader.txt index 02b08fc93..ce2ba6ef8 100644 --- a/wadsrc/static/zscript/strife/crusader.txt +++ b/wadsrc/static/zscript/strife/crusader.txt @@ -28,12 +28,6 @@ class Crusader : Actor Obituary "$OB_CRUSADER"; } - native void A_CrusaderChoose (); - native void A_CrusaderSweepLeft (); - native void A_CrusaderSweepRight (); - native void A_CrusaderRefire (); - native void A_CrusaderDeath (); - States { Spawn: @@ -58,14 +52,91 @@ class Crusader : Actor ROB2 G 3 A_Scream; ROB2 H 5 A_TossGib; ROB2 I 4 Bright A_TossGib; - ROB2 J 4 Bright A_Explode(64,64,1,1); + ROB2 J 4 Bright A_Explode(64, 64, alert:true); ROB2 K 4 Bright A_Fall; - ROB2 L 4 A_Explode(64,64,1,1); + ROB2 L 4 A_Explode(64, 64, alert:true); ROB2 MN 4 A_TossGib; - ROB2 O 4 A_Explode(64,64,1,1); + ROB2 O 4 A_Explode(64, 64, alert:true); ROB2 P -1 A_CrusaderDeath; Stop; } + +// Crusader ----------------------------------------------------------------- + + private bool CrusaderCheckRange () + { + if (reactiontime == 0 && CheckSight (target)) + { + return Distance2D (target) < 264.; + } + return false; + } + + void A_CrusaderChoose () + { + if (target == null) + return; + + if (CrusaderCheckRange ()) + { + A_FaceTarget (); + angle -= 180./16; + SpawnMissileZAimed (pos.z + 40, target, "FastFlameMissile"); + } + else + { + if (CheckMissileRange ()) + { + A_FaceTarget (); + SpawnMissileZAimed (pos.z + 56, target, "CrusaderMissile"); + angle -= 45./32; + SpawnMissileZAimed (pos.z + 40, target, "CrusaderMissile"); + angle += 45./16; + SpawnMissileZAimed (pos.z + 40, target, "CrusaderMissile"); + angle -= 45./16; + reactiontime += 15; + } + SetState (SeeState); + } + } + + void A_CrusaderSweepLeft () + { + angle += 90./16; + Actor misl = SpawnMissileZAimed (pos.z + 48, target, "FastFlameMissile"); + if (misl != null) + { + misl.Vel.Z += 1; + } + } + + void A_CrusaderSweepRight () + { + angle -= 90./16; + Actor misl = SpawnMissileZAimed (pos.z + 48, target, "FastFlameMissile"); + if (misl != null) + { + misl.Vel.Z += 1; + } + } + + void A_CrusaderRefire () + { + if (target == null || + target.health <= 0 || + !CheckSight (target)) + { + SetState (SeeState); + } + } + + void A_CrusaderDeath () + { + if (CheckBossDeath ()) + { + Floor_LowerToLowest(667, 8); + } + } } @@ -105,7 +176,7 @@ class CrusaderMissile : Actor MICR A 6 Bright A_RocketInFlight; Loop; Death: - SMIS A 0 Bright A_SetTranslucent(1,1); + SMIS A 0 Bright A_SetRenderStyle(1, STYLE_Normal); SMIS A 5 Bright; SMIS B 5 Bright; SMIS C 4 Bright; diff --git a/wadsrc/static/zscript/strife/entityboss.txt b/wadsrc/static/zscript/strife/entityboss.txt index 8ccdbbd02..daf1a78ee 100644 --- a/wadsrc/static/zscript/strife/entityboss.txt +++ b/wadsrc/static/zscript/strife/entityboss.txt @@ -32,7 +32,6 @@ class EntityPod : Actor SeeSound "misc/gibbed"; } - native void A_SpawnEntity (); States { @@ -47,10 +46,23 @@ class EntityPod : Actor PODD E -1; Stop; } + + void A_SpawnEntity () + { + Actor entity = Spawn("EntityBoss", pos + (0,0,70), ALLOW_REPLACE); + if (entity != null) + { + entity.Angle = self.Angle; + entity.CopyFriendliness(self, true); + entity.Vel.Z = 5; + entity.tracer = self; + } + } + } -// Entity Boss -------------------------------------------------------------- +// -------------------------------------------------------------- class EntityBoss : SpectralMonster { @@ -87,9 +99,6 @@ class EntityBoss : SpectralMonster Obituary "$OB_ENTITY"; } - native void A_EntityAttack(); - native void A_EntityDeath(); - States { Spawn: @@ -129,6 +138,76 @@ class EntityBoss : SpectralMonster MNAL Q 6 Bright A_EntityDeath; Stop; } + + // -------------------------------------------------------------- + + private void A_SpectralMissile (class missilename) + { + if (target != null) + { + Actor missile = SpawnMissileXYZ (Pos + (0,0,32), target, missilename, false); + if (missile != null) + { + missile.tracer = target; + missile.CheckMissileSpawn(radius); + } + } + } + + // -------------------------------------------------------------- + + void A_EntityAttack() + { + // Apparent Strife bug: Case 5 was unreachable because they used % 5 instead of % 6. + // I've fixed that by making case 1 duplicate it, since case 1 did nothing. + switch (random[Entity]() % 5) + { + case 0: + A_SpotLightning(); + break; + + case 2: + A_SpectralMissile ("SpectralLightningH3"); + break; + + case 3: + A_Spectre3Attack(); + break; + + case 4: + A_SpectralMissile ("SpectralLightningBigV2"); + break; + + default: + A_SpectralMissile ("SpectralLightningBigBall2"); + break; + } + } + + // -------------------------------------------------------------- + + void A_EntityDeath() + { + Actor second; + double secondRadius = GetDefaultByType("EntitySecond").radius * 2; + + static const double turns[] = { 0, 90, -90 }; + + Actor spot = tracer; + if (spot == null) spot = self; + + for (int i = 0; i < 3; i++) + { + double an = Angle + turns[i]; + Vector3 pos = spot.Vec3Angle(secondRadius, an, tracer ? 70. : 0.); + + second = Spawn("EntitySecond", pos, ALLOW_REPLACE); + second.CopyFriendliness(self, true); + second.A_FaceTarget(); + second.VelFromAngle(i == 0? 4.8828125 : secondRadius * 4., an); + } + } + } // Second Entity Boss ------------------------------------------------------- @@ -167,8 +246,6 @@ class EntitySecond : SpectralMonster Obituary "$OB_ENTITY"; } - native void A_SubEntityDeath (); - States { Spawn: @@ -202,6 +279,16 @@ class EntitySecond : SpectralMonster MDTH O 3 Bright A_SubEntityDeath; Stop; } + + // -------------------------------------------------------------- + + void A_SubEntityDeath () + { + if (CheckBossDeath ()) + { + Exit_Normal(0); + } + } } diff --git a/wadsrc/static/zscript/strife/inquisitor.txt b/wadsrc/static/zscript/strife/inquisitor.txt index b27818a61..6d3c5bc7f 100644 --- a/wadsrc/static/zscript/strife/inquisitor.txt +++ b/wadsrc/static/zscript/strife/inquisitor.txt @@ -25,14 +25,6 @@ class Inquisitor : Actor Obituary "$OB_INQUISITOR"; } - native void A_InquisitorWalk (); - native void A_InquisitorDecide (); - native void A_InquisitorAttack (); - native void A_InquisitorJump (); - native void A_InquisitorCheckLand (); - native void A_TossArm (); - native void A_ReaverRanged (); - States { Spawn: @@ -66,22 +58,125 @@ class Inquisitor : Actor ROB3 L 4 A_TossGib; ROB3 M 4 A_Scream; ROB3 N 4 A_TossGib; - ROB3 O 4 Bright A_Explode(128,128,1,1); + ROB3 O 4 Bright A_Explode(128, 128, alert:true); ROB3 P 4 Bright A_TossGib; ROB3 Q 4 Bright A_NoBlocking; ROB3 RSTUV 4 A_TossGib; - ROB3 W 4 Bright A_Explode(128,128,1,1); + ROB3 W 4 Bright A_Explode(128, 128, alert:true); ROB3 XY 4 Bright A_TossGib; ROB3 Z 4 A_TossGib; ROB3 [ 4 A_TossGib; ROB3 \ 3 A_TossGib; - ROB3 ] 3 Bright A_Explode(128,128,1,1); + ROB3 ] 3 Bright A_Explode(128, 128, alert:true); RBB3 A 3 Bright A_TossArm; RBB3 B 3 Bright A_TossGib; RBB3 CD 3 A_TossGib; RBB3 E -1; Stop; } + + + // Inquisitor --------------------------------------------------------------- + + void A_InquisitorWalk () + { + A_PlaySound ("inquisitor/walk", CHAN_BODY); + A_Chase (); + } + + private bool InquisitorCheckDistance () + { + if (reactiontime == 0 && CheckSight (target)) + { + return Distance2D (target) < 264.; + } + return false; + } + + void A_InquisitorDecide () + { + if (target == null) + return; + + A_FaceTarget (); + if (!InquisitorCheckDistance ()) + { + SetStateLabel("Grenade"); + } + if (target.pos.z != pos.z) + { + if (pos.z + height + 54 < ceilingz) + { + SetStateLabel("Jump"); + } + } + } + + void A_InquisitorAttack () + { + if (target == null) + return; + + A_FaceTarget (); + + AddZ(32); + angle -= 45./32; + Actor proj = SpawnMissileZAimed (pos.z, target, "InquisitorShot"); + if (proj != null) + { + proj.Vel.Z += 9; + } + angle += 45./16; + proj = SpawnMissileZAimed (pos.z, target, "InquisitorShot"); + if (proj != null) + { + proj.Vel.Z += 16; + } + AddZ(-32); + } + + void A_InquisitorJump () + { + if (target == null) + return; + + A_PlaySound ("inquisitor/jump", CHAN_ITEM, 1, true); + AddZ(64); + A_FaceTarget (); + speed = Speed * (2./3); + VelFromAngle(speed); + double dist = DistanceBySpeed(target, speed); + Vel.Z = (target.pos.z - pos.z) / dist; + reactiontime = 60; + bNoGravity = true; + } + + void A_InquisitorCheckLand () + { + reactiontime--; + if (reactiontime < 0 || + Vel.X == 0 || + Vel.Y == 0 || + pos.z <= floorz) + { + SetState (SeeState); + reactiontime = 0; + bNoGravity = false; + A_StopSound (CHAN_ITEM); + return; + } + A_PlaySound ("inquisitor/jump", CHAN_ITEM, 1, true); + } + + void A_TossArm () + { + Actor foo = Spawn("InquisitorArm", Pos + (0,0,24), ALLOW_REPLACE); + foo.angle = angle - 90. + Random2[Inquisitor]() * (360./1024.); + foo.VelFromAngle(foo.Speed / 8); + foo.Vel.Z = random[Inquisitor]() / 64.; + } + + } // Inquisitor Shot ---------------------------------------------------------- @@ -110,8 +205,8 @@ class InquisitorShot : Actor UBAM AB 3 A_Countdown; Loop; Death: - BNG2 A 0 Bright A_SetTranslucent(1,1); - BNG2 A 4 Bright A_Explode(192, 192, 1, 1); + BNG2 A 0 Bright A_SetRenderStyle(1, STYLE_Normal); + BNG2 A 4 Bright A_Explode(192, 192, alert:true); BNG2 B 4 Bright; BNG2 C 4 Bright; BNG2 D 4 Bright; diff --git a/wadsrc/static/zscript/strife/klaxon.txt b/wadsrc/static/zscript/strife/klaxon.txt new file mode 100644 index 000000000..51c4d7df2 --- /dev/null +++ b/wadsrc/static/zscript/strife/klaxon.txt @@ -0,0 +1,125 @@ +// Klaxon Warning Light ----------------------------------------------------- + +class KlaxonWarningLight : Actor +{ + Default + { + ReactionTime 60; + Radius 5; + +NOBLOCKMAP +AMBUSH + +SPAWNCEILING +NOGRAVITY + +FIXMAPTHINGPOS +NOSPLASHALERT + +SYNCHRONIZED + } + States + { + Spawn: + KLAX A 5 A_TurretLook; + Loop; + See: + KLAX B 6 A_KlaxonBlare; + KLAX C 60; + Loop; + } + +} + +// CeilingTurret ------------------------------------------------------------ + +class CeilingTurret : Actor +{ + Default + { + Health 125; + Speed 0; + Painchance 0; + Mass 10000000; + Monster; + -SOLID + -CANPASS + +AMBUSH + +SPAWNCEILING + +NOGRAVITY + +NOBLOOD + +NOSPLASHALERT + +DONTFALL + MinMissileChance 150; + DeathSound "turret/death"; + } + States + { + Spawn: + TURT A 5 A_TurretLook; + Loop; + See: + TURT A 2 A_Chase; + Loop; + Missile: + Pain: + TURT B 4 Slow A_ShootGun; + TURT D 3 Slow A_SentinelRefire; + TURT A 4 A_SentinelRefire; + Loop; + Death: + BALL A 6 Bright A_Scream; + BALL BCDE 6 Bright; + TURT C -1; + Stop; + } +} + + +extend class Actor +{ + void A_TurretLook() + { + if (bInConversation) + return; + + threshold = 0; + Actor targ = LastHeard; + if (targ != NULL && targ.health > 0 && targ.bShootable && !IsFriend(targ)) + { + target = targ; + if (bAmbush && !CheckSight (targ)) + { + return; + } + if (SeeSound != 0) + { + A_PlaySound (SeeSound, CHAN_VOICE); + } + LastHeard = NULL; + threshold = 10; + SetState (SeeState); + } + } + + void A_KlaxonBlare() + { + if (--reactiontime < 0) + { + target = NULL; + reactiontime = Default.reactiontime; + A_TurretLook(); + if (target == NULL) + { + SetIdle(); + } + else + { + reactiontime = 50; + } + } + if (reactiontime == 2) + { + // [RH] Unalert monsters near the alarm and not just those in the same sector as it. + NoiseAlert (NULL, false); + } + else if (reactiontime > 50) + { + A_PlaySound ("misc/alarm", CHAN_VOICE); + } + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/strife/loremaster.txt b/wadsrc/static/zscript/strife/loremaster.txt index 4a48cf4d3..14571f91c 100644 --- a/wadsrc/static/zscript/strife/loremaster.txt +++ b/wadsrc/static/zscript/strife/loremaster.txt @@ -76,7 +76,7 @@ class Loremaster : Actor // Loremaster Projectile ---------------------------------------------------- -class LoreShot : Actor native +class LoreShot : Actor { Default { @@ -91,8 +91,6 @@ class LoreShot : Actor native ActiveSound "loremaster/swish"; } - native void A_LoremasterChain (); - States { Spawn: @@ -102,6 +100,26 @@ class LoreShot : Actor native OCLW A 6; Stop; } + + override int DoSpecialDamage (Actor victim, int damage, Name damagetype) + { + + if (victim != NULL && target != NULL && !victim.bDontThrust) + { + Vector3 thrust = victim.Vec3To(target); + victim.Vel += thrust.Unit() * (255. * 50 / max(victim.Mass, 1)); + } + return damage; + } + + void A_LoremasterChain () + { + A_PlaySound ("loremaster/active", CHAN_BODY); + Spawn("LoreShot2", Pos, ALLOW_REPLACE); + Spawn("LoreShot2", Vec3Offset(-Vel.x/2., -Vel.y/2., -Vel.z/2.), ALLOW_REPLACE); + Spawn("LoreShot2", Vec3Offset(-Vel.x, -Vel.y, -Vel.z), ALLOW_REPLACE); + } + } // Loremaster Subprojectile ------------------------------------------------- diff --git a/wadsrc/static/zscript/strife/oracle.txt b/wadsrc/static/zscript/strife/oracle.txt index 6023629c2..740a7b69a 100644 --- a/wadsrc/static/zscript/strife/oracle.txt +++ b/wadsrc/static/zscript/strife/oracle.txt @@ -19,8 +19,6 @@ class Oracle : Actor DropItem "Meat"; } - native void A_WakeOracleSpectre (); - States { Spawn: @@ -35,4 +33,19 @@ class Oracle : Actor ORCL Q -1; Stop; } + + void A_WakeOracleSpectre () + { + ThinkerIterator it = ThinkerIterator.Create("AlienSpectre3"); + Actor spectre = Actor(it.Next()); + + if (spectre != NULL && spectre.health > 0 && self.target != spectre) + { + spectre.CurSector.SoundTarget = spectre.LastHeard = self.LastHeard; + spectre.target = self.target; + spectre.SetState (spectre.SeeState); + } + } + + } diff --git a/wadsrc/static/zscript/strife/programmer.txt b/wadsrc/static/zscript/strife/programmer.txt index dc88a790f..3fa34b659 100644 --- a/wadsrc/static/zscript/strife/programmer.txt +++ b/wadsrc/static/zscript/strife/programmer.txt @@ -33,11 +33,6 @@ class Programmer : Actor DropItem "Sigil1"; } - native void A_ProgrammerMelee (); - native void A_SpawnProgrammerBase (); - native void A_ProgrammerDeath (); - native void A_SpotLightning(); - States { Spawn: @@ -80,6 +75,90 @@ class Programmer : Actor PRGR X -1 Bright A_ProgrammerDeath; Stop; } + + //============================================================================ + // + // A_ProgrammerMelee + // + //============================================================================ + + void A_ProgrammerMelee () + { + if (target == null) + return; + + A_FaceTarget (); + + if (!CheckMeleeRange ()) + return; + + A_PlaySound("programmer/clank", CHAN_WEAPON); + + int damage = ((random[Programmer]() % 10) + 1) * 6; + int newdam = DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + } + + //============================================================================ + // + // A_SpawnProgrammerBase + // + //============================================================================ + + void A_SpawnProgrammerBase () + { + Actor foo = Spawn("ProgrammerBase", Pos + (0,0,24), ALLOW_REPLACE); + if (foo != null) + { + foo.Angle = Angle + 180. + Random2[Programmer]() * (360. / 1024.); + foo.VelFromAngle(); + foo.Vel.Z = random[Programmer]() / 128.; + } + } + + //============================================================================ + // + // A_ProgrammerDeath + // + //============================================================================ + + void A_ProgrammerDeath () + { + if (!CheckBossDeath ()) + return; + + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].health > 0) + { + players[i].mo.GiveInventoryType ("ProgLevelEnder"); + break; + } + } + // the sky change scripts are now done as special actions in MAPINFO + A_BossDeath(); + } + + //============================================================================ + // + // A_SpotLightning + // + //============================================================================ + + void A_SpotLightning() + { + if (target == null) return; + + Actor spot = Spawn("SpectralLightningSpot", (target.pos.xy, target.floorz), ALLOW_REPLACE); + if (spot != null) + { + spot.threshold = 25; + spot.target = self; + spot.FriendPlayer = 0; + spot.tracer = target; + } + } + } @@ -107,10 +186,52 @@ class ProgrammerBase : Actor // The Programmer level ending thing ---------------------------------------- -class ProgLevelEnder : Inventory native +class ProgLevelEnder : Inventory { Default { +INVENTORY.UNDROPPABLE } + + + //============================================================================ + // + // AProgLevelEnder :: Tick + // + // Fade to black, end the level, then unfade. + // + //============================================================================ + + override void Tick () + { + if (special2 == 0) + { // fade out over .66 second + special1 += 255 / (TICRATE*2/3); + if (++special1 >= 255) + { + special1 = 255; + special2 = 1; + Exit_Normal(0); + } + } + else + { // fade in over two seconds + special1 -= 255 / (TICRATE*2); + if (special1 <= 0) + { + Destroy (); + } + } + } + + //============================================================================ + // + // AProgLevelEnder :: GetBlend + // + //============================================================================ + + override Color GetBlend () + { + return Color(special1, 0, 0, 0); + } } diff --git a/wadsrc/static/zscript/strife/reaver.txt b/wadsrc/static/zscript/strife/reaver.txt index 7656a37a9..6ebcd47e2 100644 --- a/wadsrc/static/zscript/strife/reaver.txt +++ b/wadsrc/static/zscript/strife/reaver.txt @@ -22,8 +22,6 @@ class Reaver : Actor Obituary "$OB_REAVER"; } - native void A_ReaverRanged (); - States { Spawn: @@ -51,7 +49,7 @@ class Reaver : Actor ROB1 L 5; ROB1 M 5 A_NoBlocking; ROB1 NOP 5; - ROB1 Q 6 A_Explode(32,32,1,1); + ROB1 Q 6 A_Explode(32, 32, alert:true); ROB1 R -1; Stop; XDeath: @@ -64,4 +62,27 @@ class Reaver : Actor } } - \ No newline at end of file + +extend class Actor +{ + // The Inquisitor also uses this function + + void A_ReaverRanged () + { + if (target != null) + { + A_FaceTarget (); + A_PlaySound ("reaver/attack", CHAN_WEAPON); + double bangle = Angle; + double pitch = AimLineAttack (bangle, MISSILERANGE); + + for (int i = 0; i < 3; ++i) + { + double ang = bangle + Random2[ReaverAttack]() * (22.5 / 256); + int damage = ((random[ReaverAttack]() & 7) + 1) * 3; + LineAttack (ang, MISSILERANGE, pitch, damage, 'Hitscan', "StrifePuff"); + } + } + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/strife/rebels.txt b/wadsrc/static/zscript/strife/rebels.txt index d9e65560f..24deea25b 100644 --- a/wadsrc/static/zscript/strife/rebels.txt +++ b/wadsrc/static/zscript/strife/rebels.txt @@ -107,7 +107,7 @@ class Rebel6 : Rebel // Teleporter Beacon -------------------------------------------------------- -class TeleporterBeacon : Inventory native +class TeleporterBeacon : Inventory { Default { @@ -122,8 +122,6 @@ class TeleporterBeacon : Inventory native Inventory.PickupMessage "$TXT_BEACON"; } - native void A_Beacon (); - States { Spawn: @@ -137,4 +135,73 @@ class TeleporterBeacon : Inventory native BEAC A 1 A_FadeOut(0.015); Loop; } + + // Teleporter Beacon -------------------------------------------------------- + + override bool Use (bool pickup) + { + // Increase the amount by one so that when DropInventory decrements it, + // the actor will have the same number of beacons that he started with. + // When we return to UseInventory, it will take care of decrementing + // Amount again and disposing of self item if there are no more. + Amount++; + Inventory drop = Owner.DropInventory (self); + if (drop == null) + { + Amount--; + return false; + } + else + { + drop.SetStateLabel("Drop"); + drop.target = Owner; + return true; + } + } + + void A_Beacon() + { + Actor owner = target; + Actor rebel = Spawn("Rebel1", (pos.xy, floorz), ALLOW_REPLACE); + if (!rebel.TryMove (rebel.Pos.xy, true)) + { + rebel.Destroy (); + return; + } + // Once the rebels start teleporting in, you can't pick up the beacon anymore. + bSpecial = false; + Inventory(self).DropTime = 0; + // Set up the new rebel. + rebel.threshold = rebel.DefThreshold; + rebel.target = null; + rebel.bInCombat = true; + rebel.LastHeard = owner; // Make sure the rebels look for targets + if (deathmatch) + { + rebel.health *= 2; + } + if (owner != null) + { + // Rebels are the same color as their owner (but only in multiplayer) + if (multiplayer) + { + rebel.Translation = owner.Translation; + } + rebel.SetFriendPlayer(owner.player); + // Set the rebel's target to whatever last hurt the player, so long as it's not + // one of the player's other rebels. + if (owner.target != null && !rebel.IsFriend (owner.target)) + { + rebel.target = owner.target; + } + } + + rebel.SetState (rebel.SeeState); + rebel.Angle = Angle; + rebel.SpawnTeleportFog(rebel.Vec3Angle(20., Angle, 0), false, true); + if (--health < 0) + { + SetStateLabel("Death"); + } + } } \ No newline at end of file diff --git a/wadsrc/static/zscript/strife/sentinel.txt b/wadsrc/static/zscript/strife/sentinel.txt index 946a99ea2..e9e6255d4 100644 --- a/wadsrc/static/zscript/strife/sentinel.txt +++ b/wadsrc/static/zscript/strife/sentinel.txt @@ -28,8 +28,6 @@ class Sentinel : Actor Obituary "$OB_SENTINEL"; } - native void A_SentinelAttack (); - States { Spawn: @@ -56,6 +54,34 @@ class Sentinel : Actor SEWR J 5; Stop; } + + void A_SentinelAttack () + { + // [BB] Without a target the P_SpawnMissileZAimed call will crash. + if (!target) + { + return; + } + + Actor missile = SpawnMissileZAimed (pos.z + 32, target, "SentinelFX2"); + + if (missile != NULL && (missile.Vel.X != 0 || missile.Vel.Y != 0)) + { + for (int i = 8; i > 1; --i) + { + Actor trail = Spawn("SentinelFX1", Vec3Angle(missile.radius*i, missile.angle, 32 + missile.Vel.Z / 4 * i), ALLOW_REPLACE); + if (trail != NULL) + { + trail.target = self; + trail.Vel = missile.Vel; + trail.CheckMissileSpawn (radius); + } + } + missile.AddZ(missile.Vel.Z / 4); + } + } + + } // Sentinel FX 1 ------------------------------------------------------------ @@ -102,3 +128,52 @@ class SentinelFX2 : SentinelFX1 } } + +extend class Actor +{ + // These are used elsewhere, too. + void A_SentinelBob() + { + if (bInFloat) + { + Vel.Z = 0; + return; + } + if (threshold != 0) + return; + + double maxz = ceilingz - Height - 16; + double minz = floorz + 96; + if (minz > maxz) + { + minz = maxz; + } + if (minz < pos.z) + { + Vel.Z -= 1; + } + else + { + Vel.Z += 1; + } + reactiontime = (minz >= pos.z) ? 4 : 0; + } + + void A_SentinelRefire() + { + A_FaceTarget (); + + if (random[SentinelRefire]() >= 30) + { + if (target == NULL || + target.health <= 0 || + !CheckSight (target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) || + HitFriend() || + (MissileState == NULL && !CheckMeleeRange()) || + random[SentinelRefire]() < 40) + { + SetState (SeeState); + } + } + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/strife/sigil.txt b/wadsrc/static/zscript/strife/sigil.txt index d8b1f80f8..096915ccf 100644 --- a/wadsrc/static/zscript/strife/sigil.txt +++ b/wadsrc/static/zscript/strife/sigil.txt @@ -1,8 +1,11 @@ // The Almighty Sigil! ------------------------------------------------------ -class Sigil : Weapon native +class Sigil : Weapon { + // NUmPieces gets stored in 'health', so that it can be quickly accessed by ACS's GetSigilPieces function. + int downpieces; + Default { Weapon.Kickback 100; @@ -16,18 +19,7 @@ class Sigil : Weapon native Inventory.PickupMessage "$TXT_SIGIL"; } - action native void A_SelectPiece (); - action native void A_SelectSigilView (); - action native void A_SelectSigilDown (); - action native void A_SelectSigilAttack (); - action native void A_SigilCharge (); - action native void A_FireSigil1 (); - action native void A_FireSigil2 (); - action native void A_FireSigil3 (); - action native void A_FireSigil4 (); - action native void A_FireSigil5 (); - - States + States(Actor) { Spawn: SIGL A 1; @@ -41,6 +33,9 @@ class Sigil : Weapon native Stop; SIGL E -1; Stop; + } + States(Weapon) + { Ready: SIGH A 0 Bright A_SelectSigilView; Wait; @@ -120,6 +115,353 @@ class Sigil : Weapon native SIGF C 0 Bright A_Light0; Stop; } + + + //============================================================================ + // + // ASigil :: HandlePickup + // + //============================================================================ + + override bool HandlePickup (Inventory item) + { + if (item is "Sigil") + { + int otherPieces = item.health; + if (otherPieces > health) + { + item.bPickupGood = true; + Icon = item.Icon; + // If the player is holding the Sigil right now, drop it and bring + // it back with the new piece(s) in view. + if (Owner.player != null && Owner.player.ReadyWeapon == self) + { + DownPieces = health; + Owner.player.PendingWeapon = self; + } + health = otherPieces; + } + return true; + } + return false; + } + + //============================================================================ + // + // ASigil :: CreateCopy + // + //============================================================================ + + override Inventory CreateCopy (Actor other) + { + Sigil copy = Sigil(Spawn("Sigil")); + copy.Amount = Amount; + copy.MaxAmount = MaxAmount; + copy.health = health; + copy.Icon = Icon; + GoAwayAndDie (); + return copy; + } + + //============================================================================ + // + // A_SelectPiece + // + // Decide which sprite frame self Sigil should use as an item, based on how + // many pieces it represents. + // + //============================================================================ + + void A_SelectPiece () + { + int pieces = min (health, 5); + + if (pieces > 1) + { + SetState (FindState("Spawn") + pieces); + } + } + + //============================================================================ + // + // A_SelectSigilView + // + // Decide which first-person frame self Sigil should show, based on how many + // pieces it represents. Strife did self by selecting a flash that looked like + // the Sigil whenever you switched to it and at the end of an attack. I have + // chosen to make the weapon sprite choose the correct frame and let the flash + // be a regular flash. It means I need to use more states, but I think it's + // worth it. + // + //============================================================================ + + action void A_SelectSigilView () + { + if (player == null) + { + return; + } + PSprite pspr = player.GetPSprite(PSP_WEAPON); + pspr.SetState(pspr.CurState + invoker.health); + } + + //============================================================================ + // + // A_SelectSigilDown + // + // Same as A_SelectSigilView, except it uses DownPieces. self is so that when + // you pick up a Sigil, the old one will drop and *then* change to the new + // one. + // + //============================================================================ + + action void A_SelectSigilDown () + { + if (player == null) + { + return; + } + PSprite pspr = player.GetPSprite(PSP_WEAPON); + int pieces = invoker.downpieces; + if (pieces < 1 || pieces > 5) pieces = invoker.health; + pspr.SetState(pspr.CurState + pieces); + } + + //============================================================================ + // + // A_SelectSigilAttack + // + // Same as A_SelectSigilView, but used just before attacking. + // + //============================================================================ + + action void A_SelectSigilAttack () + { + if (player == null) + { + return; + } + PSprite pspr = player.GetPSprite(PSP_WEAPON); + pspr.SetState(pspr.CurState + (4 * invoker.health - 3)); + } + + //============================================================================ + // + // A_SigilCharge + // + //============================================================================ + + action void A_SigilCharge () + { + A_PlaySound ("weapons/sigilcharge", CHAN_WEAPON); + if (player != null) + { + player.extralight = 2; + } + } + + //============================================================================ + // + // A_FireSigil1 + // + //============================================================================ + + action void A_FireSigil1 () + { + Actor spot; + FTranslatedLineTarget t; + + if (player == null || player.ReadyWeapon == null) + return; + + DamageMobj (self, null, 1*4, 'Sigil', DMG_NO_ARMOR); + A_PlaySound ("weapons/sigilcharge", CHAN_WEAPON); + + BulletSlope (t, ALF_PORTALRESTRICT); + if (t.linetarget != null) + { + spot = Spawn("SpectralLightningSpot", (t.linetarget.pos.xy, t.linetarget.floorz), ALLOW_REPLACE); + if (spot != null) + { + spot.tracer = t.linetarget; + } + } + else + { + spot = Spawn("SpectralLightningSpot", Pos, ALLOW_REPLACE); + if (spot != null) + { + spot.VelFromAngle(28., angle); + } + } + if (spot != null) + { + spot.SetFriendPlayer(player); + spot.target = self; + } + } + + //============================================================================ + // + // A_FireSigil2 + // + //============================================================================ + + action void A_FireSigil2 () + { + if (player == null || player.ReadyWeapon == null) + return; + + DamageMobj (self, null, 2*4, 'Sigil', DMG_NO_ARMOR); + A_PlaySound ("weapons/sigilcharge", CHAN_WEAPON); + SpawnPlayerMissile ("SpectralLightningH1"); + } + + //============================================================================ + // + // A_FireSigil3 + // + //============================================================================ + + action void A_FireSigil3 () + { + if (player == null || player.ReadyWeapon == null) + return; + + DamageMobj (self, null, 3*4, 'Sigil', DMG_NO_ARMOR); + A_PlaySound ("weapons/sigilcharge", CHAN_WEAPON); + + angle -= 90.; + for (int i = 0; i < 20; ++i) + { + angle += 9.; + Actor spot = SpawnSubMissile ("SpectralLightningBall1", self); + if (spot != null) + { + spot.SetZ(pos.z + 32); + } + } + angle -= 90.; + } + + //============================================================================ + // + // A_FireSigil4 + // + //============================================================================ + + action void A_FireSigil4 () + { + FTranslatedLineTarget t; + + if (player == null || player.ReadyWeapon == null) + return; + + DamageMobj (self, null, 4*4, 'Sigil', DMG_NO_ARMOR); + A_PlaySound ("weapons/sigilcharge", CHAN_WEAPON); + + BulletSlope (t, ALF_PORTALRESTRICT); + if (t.linetarget != null) + { + Actor spot = SpawnPlayerMissile ("SpectralLightningBigV1", angle, pLineTarget: t, aimFlags: ALF_PORTALRESTRICT); + if (spot != null) + { + spot.tracer = t.linetarget; + } + } + else + { + Actor spot = SpawnPlayerMissile ("SpectralLightningBigV1"); + if (spot != null) + { + spot.VelFromAngle(spot.Speed, angle); + } + } + } + + //============================================================================ + // + // A_FireSigil5 + // + //============================================================================ + + action void A_FireSigil5 () + { + if (player == null || player.ReadyWeapon == null) + return; + + DamageMobj (self, null, 5*4, 'Sigil', DMG_NO_ARMOR); + A_PlaySound ("weapons/sigilcharge", CHAN_WEAPON); + + SpawnPlayerMissile ("SpectralLightningBigBall1"); + } + + //============================================================================ + // + // ASigil :: SpecialDropAction + // + // Monsters don't drop Sigil pieces. The Sigil pieces grab hold of the person + // who killed the dropper and automatically enter their inventory. That's the + // way it works if you believe Macil, anyway... + // + //============================================================================ + + override bool SpecialDropAction (Actor dropper) + { + // Give a Sigil piece to every player in the game + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].mo != null) + { + GiveSigilPiece (players[i].mo); + Destroy (); + } + } + return true; + } + + //============================================================================ + // + // ASigil :: GiveSigilPiece + // + // Gives the actor another Sigil piece, up to 5. Returns the number of Sigil + // pieces the actor previously held. + // + //============================================================================ + + static int GiveSigilPiece (Actor receiver) + { + Sigil sigl = Sigil(receiver.FindInventory("Sigil")); + if (sigl == null) + { + sigl = Sigil(Spawn("Sigil1")); + if (!sigl.CallTryPickup (receiver)) + { + sigl.Destroy (); + } + return 0; + } + else if (sigl.health < 5) + { + ++sigl.health; + static const class sigils[] = + { + "Sigil1", "Sigil2", "Sigil3", "Sigil4", "Sigil5" + }; + sigl.Icon = GetDefaultByType(sigils[clamp(sigl.health, 1, 5)-1]).Icon; + // If the player has the Sigil out, drop it and bring it back up. + if (sigl.Owner.player != null && sigl.Owner.player.ReadyWeapon == sigl) + { + sigl.Owner.player.PendingWeapon = sigl; + sigl.DownPieces = sigl.health - 1; + } + return sigl.health - 1; + } + else + { + return 5; + } + } } // Sigil 1 ------------------------------------------------------------------ diff --git a/wadsrc/static/zscript/strife/spectral.txt b/wadsrc/static/zscript/strife/spectral.txt index d6a234904..0878cff94 100644 --- a/wadsrc/static/zscript/strife/spectral.txt +++ b/wadsrc/static/zscript/strife/spectral.txt @@ -2,7 +2,7 @@ // base for all spectral monsters which hurt when being touched-------------- -class SpectralMonster : Actor native +class SpectralMonster : Actor { Default { @@ -12,10 +12,91 @@ class SpectralMonster : Actor native +NOICEDEATH } - native void A_SpectreChunkSmall (); - native void A_SpectreChunkLarge (); - native void A_Spectre3Attack (); - native void A_SpotLightning (); + override void Touch (Actor toucher) + { + toucher.DamageMobj (self, self, 5, 'Melee'); + } + + + //============================================================================ + + void A_SpectreChunkSmall () + { + Actor foo = Spawn("AlienChunkSmall", pos + (0, 0, 10), ALLOW_REPLACE); + + if (foo != null) + { + int t; + + t = random[SpectreChunk]() & 15; + foo.Vel.X = (t - (random[SpectreChunk]() & 7)); + + t = random[SpectreChunk]() & 15; + foo.Vel.Y = (t - (random[SpectreChunk]() & 7)); + + foo.Vel.Z = (random[SpectreChunk]() & 15); + } + } + + void A_SpectreChunkLarge () + { + Actor foo = Spawn("AlienChunkLarge", pos + (0, 0, 10), ALLOW_REPLACE); + + if (foo != null) + { + int t; + + t = random[SpectreChunk]() & 7; + foo.Vel.X = (t - (random[SpectreChunk]() & 15)); + + t = random[SpectreChunk]() & 7; + foo.Vel.Y = (t - (random[SpectreChunk]() & 15)); + + foo.Vel.Z = (random[SpectreChunk]() & 7); + } + } + + void A_Spectre3Attack () + { + if (target == null) + return; + + Actor foo = Spawn("SpectralLightningV2", Pos + (0, 0, 32), ALLOW_REPLACE); + + foo.Vel.Z = -12; + foo.target = self; + foo.FriendPlayer = 0; + foo.tracer = target; + + Angle -= 90.; + for (int i = 0; i < 20; ++i) + { + Angle += 9.; + SpawnSubMissile ("SpectralLightningBall2", self); + } + Angle -= 90.; + } + + //============================================================================ + // + // A_SpotLightning + // + //============================================================================ + + void A_SpotLightning() + { + if (target == null) return; + + Actor spot = Spawn("SpectralLightningSpot", (target.pos.xy, target.floorz), ALLOW_REPLACE); + if (spot != null) + { + spot.threshold = 25; + spot.target = self; + spot.FriendPlayer = 0; + spot.tracer = target; + } + } + } @@ -124,7 +205,6 @@ class SpectralLightningH1 : SpectralLightningBase +SPECTRAL } - native void A_SpectralLightningTail (); States { @@ -133,6 +213,14 @@ class SpectralLightningH1 : SpectralLightningBase ZAP6 BC 4 Bright A_SpectralLightningTail; Loop; } + + void A_SpectralLightningTail () + { + Actor foo = Spawn("SpectralLightningHTail", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE); + + foo.Angle = Angle; + foo.FriendPlayer = FriendPlayer; + } } @@ -189,8 +277,6 @@ class SpectralLightningBigBall1 : SpectralLightningDeath2 +SPECTRAL } - native void A_SpectralBigBallLightning (); - States { Spawn: @@ -198,6 +284,21 @@ class SpectralLightningBigBall1 : SpectralLightningDeath2 ZAP7 CDE 6 Bright A_SpectralBigBallLightning; Loop; } + + void A_SpectralBigBallLightning () + { + Class cls = "SpectralLightningH3"; + if (cls) + { + angle += 90.; + SpawnSubMissile (cls, target); + angle += 180.; + SpawnSubMissile (cls, target); + angle -= 270.; + SpawnSubMissile (cls, target); + } + } + } @@ -259,8 +360,6 @@ class SpectralLightningSpot : SpectralLightningDeath1 Alpha 0.6; } - native void A_SpectralLightning (); - States { Spawn: @@ -269,6 +368,35 @@ class SpectralLightningSpot : SpectralLightningDeath1 ZAP5 CD 4 Bright A_Countdown; Loop; } + + void A_SpectralLightning () + { + if (threshold != 0) + --threshold; + + Vel.X += random2[Zap5](3); + Vel.Y += random2[Zap5](3); + + double xo = random2[Zap5](3) * 50.; + double yo = random2[Zap5](3) * 50.; + + class cls; + if (threshold > 25) cls = "SpectralLightningV2"; + else cls = "SpectralLightningV1"; + + Actor flash = Spawn (cls, Vec2OffsetZ(xo, yo, ONCEILINGZ), ALLOW_REPLACE); + + flash.target = target; + flash.Vel.Z = -18; + flash.FriendPlayer = FriendPlayer; + + flash = Spawn("SpectralLightningV2", (pos.xy, ONCEILINGZ), ALLOW_REPLACE); + + flash.target = target; + flash.Vel.Z = -18; + flash.FriendPlayer = FriendPlayer; + } + } // Sigil Lightning (Big Vertical #1) ---------------------------------------- @@ -301,7 +429,3 @@ class SpectralLightningBigV2 : SpectralLightningBigV1 Damage 60; } } - - - - diff --git a/wadsrc/static/zscript/strife/stalker.txt b/wadsrc/static/zscript/strife/stalker.txt index 51a919041..cede9cae1 100644 --- a/wadsrc/static/zscript/strife/stalker.txt +++ b/wadsrc/static/zscript/strife/stalker.txt @@ -28,12 +28,6 @@ class Stalker : Actor HitObituary "$OB_STALKER"; } - native void A_StalkerLookInit (); - native void A_StalkerChaseDecide (); - native void A_StalkerWalk (); - native void A_StalkerDrop (); - native void A_StalkerAttack (); - States { Spawn: @@ -76,4 +70,66 @@ class Stalker : Actor STLK "XYZ[" 4 Bright; Stop; } + + void A_StalkerChaseDecide () + { + if (!bNoGravity) + { + SetStateLabel("SeeFloor"); + } + else if (ceilingz > pos.z + height) + { + SetStateLabel("Drop"); + } + } + + void A_StalkerLookInit () + { + State st; + if (!bNoGravity) + { + st = FindState("LookCeiling"); + } + else + { + st = FindState("LookFloor"); + } + if (st.NextState != st) + { + SetState (st); + } + } + + void A_StalkerDrop () + { + bNoVerticalMeleeRange = false; + bNoGravity = false; + } + + void A_StalkerAttack () + { + if (bNoGravity) + { + SetStateLabel("Drop"); + } + else if (target != null) + { + A_FaceTarget (); + if (CheckMeleeRange ()) + { + int damage = (random[Stalker]() & 7) * 2 + 2; + + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + } + } + } + + void A_StalkerWalk () + { + A_PlaySound ("stalker/walk", CHAN_BODY); + A_Chase (); + } + + } diff --git a/wadsrc/static/zscript/strife/strifebishop.txt b/wadsrc/static/zscript/strife/strifebishop.txt index 98b09ecee..b7de186a2 100644 --- a/wadsrc/static/zscript/strife/strifebishop.txt +++ b/wadsrc/static/zscript/strife/strifebishop.txt @@ -47,7 +47,7 @@ class StrifeBishop : Actor MLDR G 3 Bright; MLDR H 5 Bright A_Scream; MLDR I 4 Bright A_TossGib; - MLDR J 4 Bright A_Explode(64,64,1,1); + MLDR J 4 Bright A_Explode(64, 64, alert:true); MLDR KL 3 Bright; MLDR M 4 Bright A_NoBlocking; MLDR N 4 Bright; @@ -88,8 +88,8 @@ class BishopMissile : Actor MISS B 3 Bright A_Tracer2; Loop; Death: - SMIS A 0 Bright A_SetTranslucent(1,1); - SMIS A 5 Bright A_Explode(64,64,1,1); + SMIS A 0 Bright A_SetRenderStyle(1, STYLE_Normal); + SMIS A 5 Bright A_Explode(64, 64, alert:true); SMIS B 5 Bright; SMIS C 4 Bright; SMIS DEFG 2 Bright; diff --git a/wadsrc/static/zscript/strife/strifefunctions.txt b/wadsrc/static/zscript/strife/strifefunctions.txt new file mode 100644 index 000000000..7cfdf3974 --- /dev/null +++ b/wadsrc/static/zscript/strife/strifefunctions.txt @@ -0,0 +1,198 @@ +// common Strife action functions that are used by multiple different actors + +extend class Actor +{ + + //============================================================================ + + void A_FLoopActiveSound() + { + if (ActiveSound != 0 && !(level.time & 7)) + { + A_PlaySound (ActiveSound, CHAN_VOICE); + } + } + + void A_LoopActiveSound() + { + A_PlaySound(ActiveSound, CHAN_VOICE, 1, true); + } + + //============================================================================ + // + // + // + //============================================================================ + + void A_Countdown() + { + if (--reactiontime <= 0) + { + ExplodeMissile (); + bSkullFly = false; + } + } + + //============================================================================ + // + // A_ClearSoundTarget + // + //============================================================================ + + void A_ClearSoundTarget() + { + CurSector.SoundTarget = null; + for (Actor mo = CurSector.thinglist; mo != null; mo = mo.snext) + { + mo.LastHeard = null; + } + } + + //========================================================================== + // + // A_TossGib + // + //========================================================================== + + void A_TossGib() + { + class gibtype; + if (bNoBlood) gibtype = "Junk"; + else gibtype = "Meat"; + Actor gib = Spawn (gibtype, pos + (0,0,24), ALLOW_REPLACE); + + if (gib == null) + { + return; + } + + gib.Angle = random[GibTosser]() * (360 / 256.f); + gib.VelFromAngle(random[GibTosser]() & 15); + gib.Vel.Z = random[GibTosser]() & 15; + } + + //========================================================================== + // + // + // + //========================================================================== + + void A_ShootGun() + { + if (!target) return; + A_PlaySound ("monsters/rifle", CHAN_WEAPON); + A_FaceTarget (); + double pitch = AimLineAttack (angle, MISSILERANGE); + LineAttack (Angle + Random2[ShootGun]() * (11.25 / 256), MISSILERANGE, pitch, 3*(random[ShootGun]() % 5 + 1), 'Hitscan', "StrifePuff"); + } + + //========================================================================== + // + // + // + //========================================================================== + + void A_SetShadow() + { + bShadow = true; + A_SetRenderStyle(HR_SHADOW, STYLE_Translucent); + } + + void A_ClearShadow() + { + bShadow = false; + A_SetRenderStyle(1, STYLE_Normal); + } + + //========================================================================== + // + // + // + //========================================================================== + + void A_GetHurt() + { + bInCombat = true; + if ((random[HurtMe]() % 5) == 0) + { + A_PlaySound (PainSound, CHAN_VOICE); + health--; + } + if (health <= 0) + { + Die (target, target); + } + } + + //========================================================================== + // + // + // + //========================================================================== + + void A_DropFire() + { + Actor drop = Spawn("FireDroplet", pos + (0,0,24), ALLOW_REPLACE); + drop.Vel.Z = -1.; + A_Explode(64, 64, XF_NOSPLASH, damagetype: 'Fire'); + } + + //========================================================================== + // + // + // + //========================================================================== + + void A_RemoveForceField() + { + bSpecial = false; + CurSector.RemoveForceField(); + } + + //========================================================================== + // + // + // + //========================================================================== + + void A_AlertMonsters(double maxdist = 0, int flags = 0) + { + Actor target = null; + Actor emitter = self; + + if (player != null || (Flags & AMF_TARGETEMITTER)) + { + target = self; + } + else if (target != null && (self.target.player != null || (Flags & AMF_TARGETNONPLAYER))) + { + target = self.target; + } + + if (Flags & AMF_EMITFROMTARGET) emitter = target; + + if (target != null && emitter != null) + { + emitter.NoiseAlert(target, false, maxdist); + } + } + + //============================================================================ + // + // A_RocketInFlight + // + //============================================================================ + + void A_RocketInFlight() + { + A_PlaySound ("misc/missileinflight", CHAN_VOICE); + SpawnPuff ("MiniMissilePuff", Pos, Angle - 180, Angle - 180, 2, PF_HITTHING); + Actor trail = Spawn("RocketTrail", Vec3Offset(-Vel.X, -Vel.Y, 0.), ALLOW_REPLACE); + if (trail != null) + { + trail.Vel.Z = 1; + } + } + + +} diff --git a/wadsrc/static/zscript/strife/strifehumanoid.txt b/wadsrc/static/zscript/strife/strifehumanoid.txt index 7ac776026..26709cd93 100644 --- a/wadsrc/static/zscript/strife/strifehumanoid.txt +++ b/wadsrc/static/zscript/strife/strifehumanoid.txt @@ -54,5 +54,3 @@ class FireDroplet : Actor Stop; } } - - diff --git a/wadsrc/static/zscript/strife/strifeitems.txt b/wadsrc/static/zscript/strife/strifeitems.txt index cfc939413..6d8037771 100644 --- a/wadsrc/static/zscript/strife/strifeitems.txt +++ b/wadsrc/static/zscript/strife/strifeitems.txt @@ -389,7 +389,7 @@ class Communicator : Inventory // Degnin Ore --------------------------------------------------------------- -class DegninOre : Inventory native +class DegninOre : Inventory { Default { @@ -415,12 +415,38 @@ class DegninOre : Inventory native Stop; Death: XPRK A 1 A_RemoveForceField; - BNG3 A 0 A_SetTranslucent(1,1); + BNG3 A 0 A_SetRenderStyle(1, STYLE_Normal); BNG3 A 0 A_Scream; - BNG3 A 3 Bright A_Explode(192,192,1,1); + BNG3 A 3 Bright A_Explode(192, 192, alert:true); BNG3 BCDEFGH 3 Bright; Stop; } + + override bool Use (bool pickup) + { + if (pickup) + { + return false; + } + else + { + Inventory drop; + + // Increase the amount by one so that when DropInventory decrements it, + // the actor will have the same number of beacons that he started with. + // When we return to UseInventory, it will take care of decrementing + // Amount again and disposing of this item if there are no more. + Amount++; + drop = Owner.DropInventory (self); + if (drop == NULL) + { + Amount--; + return false; + } + return true; + } + } + } // Gun Training ------------------------------------------------------------- @@ -446,7 +472,7 @@ class GunTraining : Inventory // Health Training ---------------------------------------------------------- -class HealthTraining : Inventory native +class HealthTraining : Inventory { Default { @@ -463,13 +489,23 @@ class HealthTraining : Inventory native HELT A -1; Stop; } + + override bool TryPickup (in out Actor toucher) + { + if (Super.TryPickup(toucher)) + { + toucher.GiveInventoryType ("GunTraining"); + toucher.A_GiveInventory("Coin", toucher.player.mo.accuracy*5 + 300); + return true; + } + return false; + } + } - - // Scanner ------------------------------------------------------------------ -class Scanner : PowerupGiver native +class Scanner : PowerupGiver { Default { @@ -488,11 +524,25 @@ class Scanner : PowerupGiver native PMUP AB 6; Loop; } + + override bool Use (bool pickup) + { + if (!level.AllMap) + { + if (Owner.CheckLocalView (consoleplayer)) + { + C_MidPrint("SmallFont", "$TXT_NEEDMAP"); + } + return false; + } + return Super.Use (pickup); + } + } // Prison Pass -------------------------------------------------------------- -class PrisonPass : Key native +class PrisonPass : Key { Default { @@ -506,6 +556,32 @@ class PrisonPass : Key native TOKN A -1; Stop; } + + override bool TryPickup (in out Actor toucher) + { + Super.TryPickup (toucher); + Door_Open(223, 16); + toucher.GiveInventoryType ("QuestItem10"); + return true; + } + + //============================================================================ + // + // APrisonPass :: SpecialDropAction + // + // Trying to make a monster that drops a prison pass turns it into an + // OpenDoor223 item instead. That means the only way to get it in Strife + // is through dialog, which is why it doesn't have its own sprite. + // + //============================================================================ + + override bool SpecialDropAction (Actor dropper) + { + Door_Open(223, 16); + Destroy (); + return true; + } + } //--------------------------------------------------------------------------- @@ -513,7 +589,7 @@ class PrisonPass : Key native // actions and cannot be held. ---------------------------------------------- //--------------------------------------------------------------------------- -class DummyStrifeItem : Inventory native +class DummyStrifeItem : Inventory { States { @@ -525,73 +601,221 @@ class DummyStrifeItem : Inventory native // Sound the alarm! --------------------------------------------------------- -class RaiseAlarm : DummyStrifeItem native +class RaiseAlarm : DummyStrifeItem { Default { Tag "$TAG_ALARM"; } + + override bool TryPickup (in out Actor toucher) + { + toucher.NoiseAlert (toucher); + + ThinkerIterator it = ThinkerIterator.Create("AlienSpectre3"); + Actor spectre = Actor(it.Next()); + + if (spectre != NULL && spectre.health > 0 && toucher != spectre) + { + spectre.CurSector.SoundTarget = spectre.LastHeard = toucher; + spectre.target = toucher; + spectre.SetState (spectre.SeeState); + } + GoAwayAndDie (); + return true; + } + + override bool SpecialDropAction (Actor dropper) + { + if (dropper.target != null) + { + dropper.target.NoiseAlert(dropper.target); + if (dropper.target.CheckLocalView(consoleplayer)) + { + A_Log("You Fool! You've set off the alarm."); + } + } + Destroy (); + return true; + } + } // Open door tag 222 -------------------------------------------------------- -class OpenDoor222 : DummyStrifeItem native +class OpenDoor222 : DummyStrifeItem { + override bool TryPickup (in out Actor toucher) + { + Door_Open(222, 16); + GoAwayAndDie (); + return true; + } + } // Close door tag 222 ------------------------------------------------------- -class CloseDoor222 : DummyStrifeItem native +class CloseDoor222 : DummyStrifeItem { + override bool TryPickup (in out Actor toucher) + { + Door_Close(222, 16); + GoAwayAndDie (); + return true; + } + + override bool SpecialDropAction (Actor dropper) + { + Door_Close(222, 16); + if (dropper.target != null) + { + if (dropper.target.CheckLocalView(consoleplayer)) + { + A_Log("You're dead! You set off the alarm."); + } + dropper.target.NoiseAlert(dropper.target); + } + Destroy (); + return true; + } + } // Open door tag 224 -------------------------------------------------------- -class OpenDoor224 : DummyStrifeItem native +class OpenDoor224 : DummyStrifeItem { + override bool TryPickup (in out Actor toucher) + { + Door_Open(224, 16); + GoAwayAndDie (); + return true; + } + + override bool SpecialDropAction (Actor dropper) + { + Door_Open(224, 16); + Destroy (); + return true; + } + } // Ammo --------------------------------------------------------------------- -class AmmoFillup : DummyStrifeItem native +class AmmoFillup : DummyStrifeItem { Default { Tag "$TAG_AMMOFILLUP"; } + + override bool TryPickup (in out Actor toucher) + { + Inventory item = toucher.FindInventory("ClipOfBullets"); + if (item == NULL) + { + item = toucher.GiveInventoryType ("ClipOfBullets"); + if (item != NULL) + { + item.Amount = 50; + } + } + else if (item.Amount < 50) + { + item.Amount = 50; + } + else + { + return false; + } + GoAwayAndDie (); + return true; + } + } // Health ------------------------------------------------------------------- -class HealthFillup : DummyStrifeItem native +class HealthFillup : DummyStrifeItem { Default { Tag "$TAG_HEALTHFILLUP"; } + + override bool TryPickup (in out Actor toucher) + { + static const int skillhealths[] = { -100, -75, -50, -50, -100 }; + + int index = clamp(skill, 0,4); + if (!toucher.GiveBody (skillhealths[index])) + { + return false; + } + GoAwayAndDie (); + return true; + } + } // Upgrade Stamina ---------------------------------------------------------- -class UpgradeStamina : DummyStrifeItem native +class UpgradeStamina : DummyStrifeItem { Default { Inventory.Amount 10; Inventory.MaxAmount 100; } + + override bool TryPickup (in out Actor toucher) + { + if (toucher.player == NULL) + return false; + + toucher.player.mo.stamina += Amount; + if (toucher.player.mo.stamina >= MaxAmount) + toucher.player.mo.stamina = MaxAmount; + + toucher.GiveBody (-100); + GoAwayAndDie (); + return true; + } + } // Upgrade Accuracy --------------------------------------------------------- -class UpgradeAccuracy : DummyStrifeItem native +class UpgradeAccuracy : DummyStrifeItem { + override bool TryPickup (in out Actor toucher) + { + if (toucher.player == NULL || toucher.player.mo.accuracy >= 100) + return false; + toucher.player.mo.accuracy += 10; + GoAwayAndDie (); + return true; + } + } // Start a slideshow -------------------------------------------------------- -class SlideshowStarter : DummyStrifeItem native +class SlideshowStarter : DummyStrifeItem { + override bool TryPickup (in out Actor toucher) + { + gameaction = ga_slideshow; + if (level.levelnum == 10) + { + toucher.GiveInventoryType ("QuestItem17"); + } + GoAwayAndDie (); + return true; + } + } diff --git a/wadsrc/static/zscript/strife/strifeplayer.txt b/wadsrc/static/zscript/strife/strifeplayer.txt index 0b61e3122..2033fdb5e 100644 --- a/wadsrc/static/zscript/strife/strifeplayer.txt +++ b/wadsrc/static/zscript/strife/strifeplayer.txt @@ -36,10 +36,6 @@ class StrifePlayer : PlayerPawn Player.Colorset 7, "Blue", 0x90, 0x9F, 0x92, 0x20, 0x3F, 0x00, 0x1F, 0x50, 0x5F, 0x40, 0x4F, 0xC1, 0xCF, 0x01, 0x0F, 0xC0,0xC0,1,1, 0xD0, 0xDF, 0x10, 0x1F; } - native void A_ItBurnsItBurns(); - native void A_CrispyPlayer(); - native void A_HandLower(); - States { Spawn: @@ -101,5 +97,69 @@ class StrifePlayer : PlayerPawn WAVE ABCD 3 A_HandLower; Loop; } + + void A_ItBurnsItBurns() + { + A_PlaySound ("human/imonfire", CHAN_VOICE); + + if (player != null && player.mo == self) + { + player.SetPsprite(PSP_STRIFEHANDS, FindState("FireHands")); + + player.ReadyWeapon = null; + player.PendingWeapon = WP_NOCHANGE; + player.playerstate = PST_LIVE; + player.extralight = 3; + } + } + + void A_CrispyPlayer() + { + if (player != null && player.mo == self) + { + PSprite psp = player.GetPSprite(PSP_STRIFEHANDS); + + State firehandslower = FindState("FireHandsLower"); + State firehands = FindState("FireHands"); + + if (psp.CurState != null && firehandslower != null && firehands != null) + { + // Calculate state to go to. + int dist = firehands.DistanceTo(psp.curState); + if (dist > 0) + { + player.playerstate = PST_DEAD; + psp.SetState(firehandslower + dist); + return; + } + } + player.playerstate = PST_DEAD; + psp.SetState(null); + } + } + + void A_HandLower() + { + if (player != null) + { + PSprite psp = player.GetPSprite(PSP_STRIFEHANDS); + + if (psp.CurState == null) + { + psp.SetState(null); + return; + } + + psp.y += 9; + if (psp.y > WEAPONBOTTOM*2) + { + psp.SetState(null); + } + + if (player.extralight > 0) player.extralight--; + } + return; + } + } diff --git a/wadsrc/static/zscript/strife/strifestuff.txt b/wadsrc/static/zscript/strife/strifestuff.txt index e72e8d924..20bee61d0 100644 --- a/wadsrc/static/zscript/strife/strifestuff.txt +++ b/wadsrc/static/zscript/strife/strifestuff.txt @@ -1,3 +1,12 @@ +// Notes so I don't forget them: +// +// When shooting missiles at something, if MF_SHADOW is set, the angle is adjusted with the formula: +// angle += pr_spawnmissile.Random2() << 21 +// When MF_STRIFEx4000000 is set, the angle is adjusted similarly: +// angle += pr_spawnmissile.Random2() << 22 +// Note that these numbers are different from those used by all the other Doom engine games. + + // Tank 1 Huge ------------------------------------------------------------ class Tank1 : Actor @@ -192,7 +201,7 @@ class ExplosiveBarrel2 : Actor BART B 2 Bright A_Scream; BART CD 2 Bright; BART E 2 Bright A_NoBlocking; - BART F 2 Bright A_Explode(64, 64, 1, 1); + BART F 2 Bright A_Explode(64, 64, alert:true); BART GHIJ 2 Bright; BART K 3 Bright; BART L -1; @@ -1726,7 +1735,7 @@ class TargetPractice : Actor // Force Field Guard -------------------------------------------------------- -class ForceFieldGuard : Actor native +class ForceFieldGuard : Actor { Default { @@ -1748,6 +1757,16 @@ class ForceFieldGuard : Actor native TNT1 A 1 A_RemoveForceField; Stop; } + + override int TakeSpecialDamage (Actor inflictor, Actor source, int damage, Name damagetype) + { + if (inflictor == NULL || !(inflictor is "DegninOre")) + { + return -1; + } + return health; + } + } // Kneeling Guy ------------------------------------------------------------- @@ -1796,84 +1815,11 @@ class KneelingGuy : Actor NEAL J -1; Stop; } - - } -// Klaxon Warning Light ----------------------------------------------------- - -class KlaxonWarningLight : Actor -{ - Default - { - ReactionTime 60; - Radius 5; - +NOBLOCKMAP +AMBUSH - +SPAWNCEILING +NOGRAVITY - +FIXMAPTHINGPOS +NOSPLASHALERT - +SYNCHRONIZED - } - States - { - Spawn: - KLAX A 5 A_TurretLook; - Loop; - See: - KLAX B 6 A_KlaxonBlare; - KLAX C 60; - Loop; - } - -} - -// CeilingTurret ------------------------------------------------------------ - -class CeilingTurret : Actor -{ - Default - { - Health 125; - Speed 0; - Painchance 0; - Mass 10000000; - Monster; - -SOLID - -CANPASS - +AMBUSH - +SPAWNCEILING - +NOGRAVITY - +NOBLOOD - +NOSPLASHALERT - +DONTFALL - MinMissileChance 150; - DeathSound "turret/death"; - } - States - { - Spawn: - TURT A 5 A_TurretLook; - Loop; - See: - TURT A 2 A_Chase; - Loop; - Missile: - Pain: - TURT B 4 Slow A_ShootGun; - TURT D 3 Slow A_SentinelRefire; - TURT A 4 A_SentinelRefire; - Loop; - Death: - BALL A 6 Bright A_Scream; - BALL BCDE 6 Bright; - TURT C -1; - Stop; - } -} - - // Power Coupling ----------------------------------------------------------- -class PowerCoupling : Actor native +class PowerCoupling : Actor { Default { @@ -1894,11 +1840,39 @@ class PowerCoupling : Actor native COUP AB 5; Loop; } + + override void Die (Actor source, Actor inflictor, int dmgflags) + { + Super.Die (source, inflictor, dmgflags); + + int i; + + for (i = 0; i < MAXPLAYERS; ++i) + if (playeringame[i] && players[i].health > 0) + break; + + if (i == MAXPLAYERS) + return; + + // [RH] In case the player broke it with the dagger, alert the guards now. + if (LastHeard != source) + { + NoiseAlert (source); + } + Door_Close(225, 16); + Floor_LowerToHighestEE(44, 8); + players[i].mo.GiveInventoryType ("QuestItem6"); + S_Sound ("svox/voc13", CHAN_VOICE); + players[i].SetLogNumber (13); + DoDropItem ("BrokenPowerCoupling", -1, 256); + Destroy (); + } + } // Gibs for things that bleed ----------------------------------------------- -class Meat : Actor native +class Meat : Actor { Default { @@ -1948,6 +1922,13 @@ class Meat : Actor native MEAT T 700; Stop; } + + override void BeginPlay () + { + // Strife used mod 19, but there are 20 states. Hmm. + SetState (SpawnState + random[GibTosser]() % 20); + } + } // Gibs for things that don't bleed ----------------------------------------- diff --git a/wadsrc/static/zscript/strife/strifeweapons.txt b/wadsrc/static/zscript/strife/strifeweapons.txt index 9e4463893..5cb01342e 100644 --- a/wadsrc/static/zscript/strife/strifeweapons.txt +++ b/wadsrc/static/zscript/strife/strifeweapons.txt @@ -50,842 +50,3 @@ class StrifeSpark : StrifePuff } } -// Punch Dagger ------------------------------------------------------------- - -class PunchDagger : StrifeWeapon -{ - Default - { - Weapon.SelectionOrder 3900; - +WEAPON.NOALERT - Obituary "$OB_MPPUNCHDAGGER"; - Tag "$TAG_PUNCHDAGGER"; - } - - action native void A_JabDagger (); - - States - { - Ready: - PNCH A 1 A_WeaponReady; - Loop; - Deselect: - PNCH A 1 A_Lower; - Loop; - Select: - PNCH A 1 A_Raise; - Loop; - Fire: - PNCH B 4; - PNCH C 4 A_JabDagger; - PNCH D 5; - PNCH C 4; - PNCH B 5 A_ReFire; - Goto Ready; - } - -} - - -// The base for Strife projectiles that die with ZAP1 ----------------------- - -class StrifeZap1 : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +DROPOFF - } - States - { - Spawn: - Death: - ZAP1 A 3 A_AlertMonsters; - ZAP1 BCDEFE 3; - ZAP1 DCB 2; - ZAP1 A 1; - Stop; - } -} - - -// Electric Bolt ------------------------------------------------------------ - -class ElectricBolt : StrifeZap1 -{ - Default - { - Speed 30; - Radius 10; - Height 10; - Damage 10; - Projectile; - +STRIFEDAMAGE - MaxStepHeight 4; - SeeSound "misc/swish"; - ActiveSound "misc/swish"; - DeathSound "weapons/xbowhit"; - Obituary "$OB_MPELECTRICBOLT"; - } - States - { - Spawn: - AROW A 10 A_LoopActiveSound; - Loop; - } -} - - -// Poison Bolt -------------------------------------------------------------- - -class PoisonBolt : Actor native -{ - Default - { - Speed 30; - Radius 10; - Height 10; - Damage 500; - Projectile; - +STRIFEDAMAGE - MaxStepHeight 4; - SeeSound "misc/swish"; - ActiveSound "misc/swish"; - Obituary "$OB_MPPOISONBOLT"; - } - States - { - Spawn: - ARWP A 10 A_LoopActiveSound; - Loop; - Death: - AROW A 1; - Stop; - } -} - - -// Strife's Crossbow -------------------------------------------------------- - -class StrifeCrossbow : StrifeWeapon -{ - Default - { - +FLOORCLIP - Weapon.SelectionOrder 1200; - +WEAPON.NOALERT - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 8; - Weapon.AmmoType1 "ElectricBolts"; - Weapon.SisterWeapon "StrifeCrossbow2"; - Inventory.PickupMessage "$TXT_STRIFECROSSBOW"; - Tag "$TAG_STRIFECROSSBOW1"; - Inventory.Icon "CBOWA0"; - } - - action native void A_ClearFlash (); - action native void A_ShowElectricFlash (); - action native void A_FireArrow (class proj); - - States - { - Spawn: - CBOW A -1; - Stop; - Ready: - XBOW A 0 A_ShowElectricFlash; - XBOW A 1 A_WeaponReady; - Wait; - Deselect: - XBOW A 1 A_Lower; - Loop; - Select: - XBOW A 1 A_Raise; - Loop; - Fire: - XBOW A 3 A_ClearFlash; - XBOW B 6 A_FireArrow("ElectricBolt"); - XBOW C 4; - XBOW D 6; - XBOW E 3; - XBOW F 5; - XBOW G 0 A_ShowElectricFlash; - XBOW G 5 A_CheckReload; - Goto Ready+1; - Flash: - XBOW KLM 5; - Loop; - } -} - - -class StrifeCrossbow2 : StrifeCrossbow -{ - Default - { - Weapon.SelectionOrder 2700; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 0; - Weapon.AmmoType1 "PoisonBolts"; - Weapon.SisterWeapon "StrifeCrossbow"; - Tag "$TAG_STRIFECROSSBOW2"; - } - States - { - Ready: - XBOW H 1 A_WeaponReady; - Loop; - Deselect: - XBOW H 1 A_Lower; - Loop; - Select: - XBOW H 1 A_Raise; - Loop; - Fire: - XBOW H 3; - XBOW B 6 A_FireArrow("PoisonBolt"); - XBOW C 4; - XBOW D 6; - XBOW E 3; - XBOW I 5; - XBOW J 5 A_CheckReload; - Goto Ready; - Flash: - Stop; - } -} - -// Assault Gun -------------------------------------------------------------- - -class AssaultGun : StrifeWeapon -{ - Default - { - +FLOORCLIP - Weapon.SelectionOrder 600; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 20; - Weapon.AmmoType1 "ClipOfBullets"; - Inventory.Icon "RIFLA0"; - Tag "$TAG_ASSAULTGUN"; - Inventory.PickupMessage "$TXT_ASSAULTGUN"; - Obituary "$OB_MPASSAULTGUN"; - } - States - { - Spawn: - RIFL A -1; - Stop; - Ready: - RIFG A 1 A_WeaponReady; - Loop; - Deselect: - RIFG B 1 A_Lower; - Loop; - Select: - RIFG A 1 A_Raise; - Loop; - Fire: - RIFF AB 3 A_FireAssaultGun; - RIFG D 3 A_FireAssaultGun; - RIFG C 0 A_ReFire; - RIFG B 2 A_Light0; - Goto Ready; - } -} - - -// Standing variant of the assault gun -------------------------------------- - -class AssaultGunStanding : WeaponGiver -{ - Default - { - DropItem "AssaultGun"; - Inventory.PickupMessage "$TXT_ASSAULTGUN"; - } - States - { - Spawn: - RIFL B -1; - Stop; - } -} - - -// Mini-Missile Launcher ---------------------------------------------------- - - -class MiniMissileLauncher : StrifeWeapon -{ - Default - { - +FLOORCLIP - Weapon.SelectionOrder 1800; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 8; - Weapon.AmmoType1 "MiniMissiles"; - Inventory.Icon "MMSLA0"; - Tag "$TAG_MMLAUNCHER"; - Inventory.PickupMessage "$TXT_MMLAUNCHER"; - } - - action native void A_FireMiniMissile (); - - States - { - Spawn: - MMSL A -1; - Stop; - Ready: - MMIS A 1 A_WeaponReady; - Loop; - Deselect: - MMIS A 1 A_Lower; - Loop; - Select: - MMIS A 1 A_Raise; - Loop; - Fire: - MMIS A 4 A_FireMiniMissile; - MMIS B 4 A_Light1; - MMIS C 5 Bright; - MMIS D 2 Bright A_Light2; - MMIS E 2 Bright; - MMIS F 2 Bright A_Light0; - MMIS F 0 A_ReFire; - Goto Ready; - } -} - - -// Rocket Trail ------------------------------------------------------------- - -class RocketTrail : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - RenderStyle "Translucent"; - Alpha 0.25; - SeeSound "misc/missileinflight"; - } - States - { - Spawn: - PUFY BCBCD 4; - Stop; - } -} - -// Rocket Puff -------------------------------------------------------------- - -class MiniMissilePuff : StrifePuff -{ - Default - { - -ALLOWPARTICLES - } - States - { - Spawn: - Goto Crash; - } -} - -// Mini Missile ------------------------------------------------------------- - -class MiniMissile : Actor -{ - Default - { - Speed 20; - Radius 10; - Height 14; - Damage 10; - Projectile; - +STRIFEDAMAGE - MaxStepHeight 4; - SeeSound "weapons/minimissile"; - DeathSound "weapons/minimissilehit"; - Obituary "$OB_MPMINIMISSILELAUNCHER"; - } - States - { - Spawn: - MICR A 6 Bright A_RocketInFlight; - Loop; - Death: - SMIS A 0 Bright A_SetTranslucent(1,1); - SMIS A 5 Bright A_Explode(64,64,1,1); - SMIS B 5 Bright; - SMIS C 4 Bright; - SMIS DEFG 2 Bright; - Stop; - } -} - -// Flame Thrower ------------------------------------------------------------ - -class FlameThrower : StrifeWeapon -{ - Default - { - +FLOORCLIP - Weapon.SelectionOrder 2100; - Weapon.Kickback 0; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 100; - Weapon.UpSound "weapons/flameidle"; - Weapon.ReadySound "weapons/flameidle"; - Weapon.AmmoType1 "EnergyPod"; - Inventory.Icon "FLAMA0"; - Tag "$TAG_FLAMER"; - Inventory.PickupMessage "$TXT_FLAMER"; - } - - action native void A_FireFlamer (); - - States - { - Spawn: - FLAM A -1; - Stop; - Ready: - FLMT AB 3 A_WeaponReady; - Loop; - Deselect: - FLMT A 1 A_Lower; - Loop; - Select: - FLMT A 1 A_Raise; - Loop; - Fire: - FLMF A 2 A_FireFlamer; - FLMF B 3 A_ReFire; - Goto Ready; - } -} - - -// Flame Thrower Projectile ------------------------------------------------- - -class FlameMissile : Actor -{ - Default - { - Speed 15; - Height 11; - Radius 8; - Mass 10; - Damage 4; - DamageType "Fire"; - ReactionTime 8; - Projectile; - -NOGRAVITY - +STRIFEDAMAGE - MaxStepHeight 4; - RenderStyle "Add"; - SeeSound "weapons/flamethrower"; - Obituary "$OB_MPFLAMETHROWER"; - } - - native void A_FlameDie (); - - States - { - Spawn: - FRBL AB 3 Bright; - FRBL C 3 Bright A_Countdown; - Loop; - Death: - FRBL D 5 Bright A_FlameDie; - FRBL EFGHI 5 Bright; - Stop; - } - -} - - -// Mauler ------------------------------------------------------------------- -// The scatter version - -class Mauler : StrifeWeapon -{ - Default - { - +FLOORCLIP - Weapon.SelectionOrder 300; - Weapon.AmmoUse1 20; - Weapon.AmmoGive1 40; - Weapon.AmmoType1 "EnergyPod"; - Weapon.SisterWeapon "Mauler2"; - Inventory.Icon "TRPDA0"; - Tag "$TAG_MAULER1"; - Inventory.PickupMessage "$TXT_MAULER"; - Obituary "$OB_MPMAULER1"; - } - - action native void A_FireMauler1 (); - - States - { - Ready: - MAUL FGHA 6 A_WeaponReady; - Loop; - Deselect: - MAUL A 1 A_Lower; - Loop; - Select: - MAUL A 1 A_Raise; - Loop; - Fire: - BLSF A 5 Bright A_FireMauler1; - MAUL B 3 Bright A_Light1; - MAUL C 2 A_Light2; - MAUL DE 2; - MAUL A 7 A_Light0; - MAUL H 7; - MAUL G 7 A_CheckReload; - Goto Ready; - Spawn: - TRPD A -1; - Stop; - } -} - - -// Mauler Torpedo version --------------------------------------------------- - -class Mauler2 : Mauler -{ - Default - { - Weapon.SelectionOrder 3300; - Weapon.AmmoUse1 30; - Weapon.AmmoGive1 0; - Weapon.AmmoType1 "EnergyPod"; - Weapon.SisterWeapon "Mauler"; - Tag "$TAG_MAULER2"; - } - - action native void A_FireMauler2Pre (); - action native void A_FireMauler2 (); - - States - { - Ready: - MAUL IJKL 7 A_WeaponReady; - Loop; - Deselect: - MAUL I 1 A_Lower; - Loop; - Select: - MAUL I 1 A_Raise; - Loop; - Fire: - MAUL I 20 A_FireMauler2Pre; - MAUL J 10 A_Light1; - BLSF A 10 Bright A_FireMauler2; - MAUL B 10 Bright A_Light2; - MAUL C 2; - MAUL D 2 A_Light0; - MAUL E 2 A_ReFire; - Goto Ready; - } -} - - -// Mauler "Bullet" Puff ----------------------------------------------------- - -class MaulerPuff : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - RenderStyle "Add"; - DamageType "Disintegrate"; - } - States - { - Spawn: - MPUF AB 5; - POW1 ABCDE 4; - Stop; - } -} - -// The Mauler's Torpedo ----------------------------------------------------- - -class MaulerTorpedo : Actor -{ - Default - { - Speed 20; - Height 8; - Radius 13; - Damage 1; - DamageType "Disintegrate"; - Projectile; - +STRIFEDAMAGE - MaxStepHeight 4; - RenderStyle "Add"; - SeeSound "weapons/mauler2fire"; - DeathSound "weapons/mauler2hit"; - Obituary "$OB_MPMAULER"; - } - - native void A_MaulerTorpedoWave (); - - States - { - Spawn: - TORP ABCD 4 Bright; - Loop; - Death: - THIT AB 8 Bright; - THIT C 8 Bright A_MaulerTorpedoWave; - THIT DE 8 Bright; - Stop; - } -} - - -// The mini torpedoes shot by the big torpedo -------------------------------- - -class MaulerTorpedoWave : Actor -{ - Default - { - Speed 35; - Radius 13; - Height 13; - Damage 10; - DamageType "Disintegrate"; - Projectile; - +STRIFEDAMAGE - MaxStepHeight 4; - RenderStyle "Add"; - Obituary "$OB_MPMAULER"; - } - States - { - Spawn: - TWAV AB 9 Bright; - Death: - TWAV C 9 Bright; - Stop; - } -} - - -// High-Explosive Grenade --------------------------------------------------- - -class HEGrenade : Actor -{ - Default - { - Speed 15; - Radius 13; - Height 13; - Mass 20; - Damage 1; - Reactiontime 30; - Projectile; - -NOGRAVITY - +STRIFEDAMAGE - +BOUNCEONACTORS - +EXPLODEONWATER - MaxStepHeight 4; - BounceType "Doom"; - BounceFactor 0.5; - BounceCount 2; - SeeSound "weapons/hegrenadeshoot"; - DeathSound "weapons/hegrenadebang"; - Obituary "$OB_MPSTRIFEGRENADE"; - } - States - { - Spawn: - GRAP AB 3 A_Countdown; - Loop; - Death: - BNG4 A 0 Bright A_NoGravity; - BNG4 A 0 Bright A_SetTranslucent(1,1); - BNG4 A 2 Bright A_Explode(192,192,1,1); - BNG4 BCDEFGHIJKLMN 3 Bright; - Stop; - } -} - -// White Phosphorous Grenade ------------------------------------------------ - -class PhosphorousGrenade : Actor -{ - Default - { - Speed 15; - Radius 13; - Height 13; - Mass 20; - Damage 1; - Reactiontime 40; - Projectile; - -NOGRAVITY - +STRIFEDAMAGE - +BOUNCEONACTORS - +EXPLODEONWATER - BounceType "Doom"; - MaxStepHeight 4; - BounceFactor 0.5; - BounceCount 2; - SeeSound "weapons/phgrenadeshoot"; - DeathSound "weapons/phgrenadebang"; - Obituary "$OB_MPPHOSPHOROUSGRENADE"; - } - States - { - Spawn: - GRIN AB 3 A_Countdown; - Loop; - Death: - BNG3 A 2 A_SpawnItemEx("PhosphorousFire"); - Stop; - } -} - -// Fire from the Phoshorous Grenade ----------------------------------------- - -class PhosphorousFire : Actor native -{ - Default - { - Reactiontime 120; - DamageType "Fire"; - +NOBLOCKMAP - +FLOORCLIP - +NOTELEPORT - +NODAMAGETHRUST - +DONTSPLASH - RenderStyle "Add"; - Obituary "$OB_MPPHOSPHOROUSGRENADE"; - } - - native void A_Burnarea (); - native void A_Burnination (); - - States - { - Spawn: - BNG3 B 2 Bright A_Burnarea; - BNG3 C 2 Bright A_Countdown; - FLBE A 2 Bright A_Burnination; - FLBE B 2 Bright A_Countdown; - FLBE C 2 Bright A_Burnarea; - FLBE D 3 Bright A_Countdown; - FLBE E 3 Bright A_Burnarea; - FLBE F 3 Bright A_Countdown; - FLBE G 3 Bright A_Burnination; - Goto Spawn+5; - Death: - FLBE H 2 Bright; - FLBE I 2 Bright A_Burnination; - FLBE JK 2 Bright; - Stop; - } -} - -// High-Explosive Grenade Launcher ------------------------------------------ - -class StrifeGrenadeLauncher : StrifeWeapon -{ - Default - { - +FLOORCLIP - Weapon.SelectionOrder 2400; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 12; - Weapon.AmmoType1 "HEGrenadeRounds"; - Weapon.SisterWeapon "StrifeGrenadeLauncher2"; - Inventory.Icon "GRNDA0"; - Tag "$TAG_GLAUNCHER1"; - Inventory.PickupMessage "$TXT_GLAUNCHER"; - } - - action native void A_FireGrenade (class grenadetype, float angleofs, statelabel flash); - - States - { - Spawn: - GRND A -1; - Stop; - Ready: - GREN A 1 A_WeaponReady; - Loop; - Deselect: - GREN A 1 A_Lower; - Loop; - Select: - GREN A 1 A_Raise; - Loop; - Fire: - GREN A 5 A_FireGrenade("HEGrenade", -90, "Flash"); - GREN B 10; - GREN A 5 A_FireGrenade("HEGrenade", 90, "Flash2"); - GREN C 10; - GREN A 0 A_ReFire; - Goto Ready; - Flash: - GREF A 5 Bright A_Light1; - Goto LightDone; - Flash2: - GREF B 5 Bright A_Light2; - Goto LightDone; - } - -} - -// White Phosphorous Grenade Launcher --------------------------------------- - -class StrifeGrenadeLauncher2 : StrifeGrenadeLauncher -{ - Default - { - Weapon.SelectionOrder 3200; - Weapon.AmmoUse1 1; - Weapon.AmmoGive1 0; - Weapon.AmmoType1 "PhosphorusGrenadeRounds"; - Weapon.SisterWeapon "StrifeGrenadeLauncher"; - Tag "$TAG_GLAUNCHER2"; - } - States - { - Ready: - GREN D 1 A_WeaponReady; - Loop; - Deselect: - GREN D 1 A_Lower; - Loop; - Select: - GREN D 1 A_Raise; - Loop; - Fire: - GREN D 5 A_FireGrenade("PhosphorousGrenade", -90, "Flash"); - GREN E 10; - GREN D 5 A_FireGrenade("PhosphorousGrenade", 90, "Flash2"); - GREN F 10; - GREN A 0 A_ReFire; - Goto Ready; - Flash: - GREF C 5 Bright A_Light1; - Goto LightDone; - Flash2: - GREF D 5 Bright A_Light2; - Goto LightDone; - } -} - diff --git a/wadsrc/static/zscript/strife/templar.txt b/wadsrc/static/zscript/strife/templar.txt index 5b1605cf4..23290011c 100644 --- a/wadsrc/static/zscript/strife/templar.txt +++ b/wadsrc/static/zscript/strife/templar.txt @@ -25,7 +25,6 @@ class Templar : Actor Obituary "$OB_TEMPLAR"; DropItem "EnergyPod"; } - native void A_TemplarAttack(); States { @@ -64,5 +63,22 @@ class Templar : Actor PGRD \ -1; Stop; } + + void A_TemplarAttack() + { + if (target != null) + { + A_PlaySound ("templar/shoot", CHAN_WEAPON); + A_FaceTarget (); + double pitch = AimLineAttack (angle, MISSILERANGE); + + for (int i = 0; i < 10; ++i) + { + int damage = (random[Templar]() & 4) * 2; + double ang = angle + random2[Templar]() * (11.25 / 256); + LineAttack (ang, MISSILERANGE+64., pitch + random2[Templar]() * (7.097 / 256), damage, 'Hitscan', "MaulerPuff"); + } + } + } } diff --git a/wadsrc/static/zscript/strife/thingstoblowup.txt b/wadsrc/static/zscript/strife/thingstoblowup.txt index dbbad8ed6..5e5bfac21 100644 --- a/wadsrc/static/zscript/strife/thingstoblowup.txt +++ b/wadsrc/static/zscript/strife/thingstoblowup.txt @@ -1,4 +1,42 @@ +extend class Actor +{ + void A_Bang4Cloud() + { + double xo = (random[Bang4Cloud]() & 3) * (10. / 64); + double yo = (random[Bang4Cloud]() & 3) * (10. / 64); + Spawn("Bang4Cloud", Vec3Offset(xo, yo, 0.), ALLOW_REPLACE); + } + + void A_GiveQuestItem(int questitem) + { + // Give one of these quest items to every player in the game + if (questitem >= 0) + { + String itemname = "QuestItem" .. questitem; + class item = itemname; + if (item != null) + { + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + players[i].mo.GiveInventoryType(item); + } + } + } + } + + String msgid = "TXT_QUEST_" .. questitem; + String msg = StringTable.Localize(msgid); + + if (msg != msgid) // if both are identical there was no message of this name in the stringtable. + { + C_MidPrint ("SmallFont", msg); + } + } + +} // A Cloud used for varius explosions --------------------------------------- // This actor has no direct equivalent in strife. To create this, Strife @@ -121,10 +159,6 @@ class PowerCrystal : Actor ActiveSound "misc/reactor"; } - native void A_ExtraLightOff (); - native void A_Explode512 (); - native void A_LightGoesOut (); - States { Spawn: @@ -150,4 +184,48 @@ class PowerCrystal : Actor BOOM VWXY 3 Bright; Stop; } + + // PowerCrystal ------------------------------------------------------------------- + + void A_ExtraLightOff() + { + if (target != NULL && target.player != NULL) + { + target.player.extralight = 0; + } + } + + void A_Explode512() + { + A_Explode(512, 512); + if (target != NULL && target.player != NULL) + { + target.player.extralight = 5; + } + A_SetRenderStyle(1, STYLE_Add); + } + + void A_LightGoesOut() + { + sector sec = CurSector; + + sec.Flags |= Sector.SECF_SILENTMOVE; + sec.lightlevel = 0; + // Do this right with proper checks instead of just hacking the floor height. + Floor.CreateFloor(sec, Floor.floorLowerToLowest, null, 65536.); + + + for (int i = 0; i < 8; ++i) + { + Actor foo = Spawn("Rubble1", Pos, ALLOW_REPLACE); + if (foo != NULL) + { + int t = random[LightOut]() & 15; + foo.Vel.X = t - (random[LightOut]() & 7); + foo.Vel.Y = random2[LightOut]() & 7; + foo.Vel.Z = 7 + (random[LightOut]() & 3); + } + } + } + } diff --git a/wadsrc/static/zscript/strife/weaponassault.txt b/wadsrc/static/zscript/strife/weaponassault.txt new file mode 100644 index 000000000..c382d8e84 --- /dev/null +++ b/wadsrc/static/zscript/strife/weaponassault.txt @@ -0,0 +1,94 @@ +// Assault Gun -------------------------------------------------------------- + +class AssaultGun : StrifeWeapon +{ + Default + { + +FLOORCLIP + Weapon.SelectionOrder 600; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 20; + Weapon.AmmoType1 "ClipOfBullets"; + Inventory.Icon "RIFLA0"; + Tag "$TAG_ASSAULTGUN"; + Inventory.PickupMessage "$TXT_ASSAULTGUN"; + Obituary "$OB_MPASSAULTGUN"; + } + States + { + Spawn: + RIFL A -1; + Stop; + Ready: + RIFG A 1 A_WeaponReady; + Loop; + Deselect: + RIFG B 1 A_Lower; + Loop; + Select: + RIFG A 1 A_Raise; + Loop; + Fire: + RIFF AB 3 A_FireAssaultGun; + RIFG D 3 A_FireAssaultGun; + RIFG C 0 A_ReFire; + RIFG B 2 A_Light0; + Goto Ready; + } +} + +extend class StateProvider +{ + //============================================================================ + // + // A_FireAssaultGun + // + //============================================================================ + + void A_FireAssaultGun() + { + if (player == null) + { + return; + } + + A_PlaySound ("weapons/assaultgun", CHAN_WEAPON); + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + player.mo.PlayAttacking2 (); + + int damage = 4*(random[StrifeGun]() % 3 + 1); + double ang = angle; + + if (player.refire) + { + ang += Random2[StrifeGun]() * (22.5 / 256) * AccuracyFactor(); + } + LineAttack (ang, PLAYERMISSILERANGE, BulletSlope (), damage, 'Hitscan', "StrifePuff"); + } +} + + +// Standing variant of the assault gun -------------------------------------- + +class AssaultGunStanding : WeaponGiver +{ + Default + { + DropItem "AssaultGun"; + Inventory.PickupMessage "$TXT_ASSAULTGUN"; + } + States + { + Spawn: + RIFL B -1; + Stop; + } +} + + diff --git a/wadsrc/static/zscript/strife/weaponcrossbow.txt b/wadsrc/static/zscript/strife/weaponcrossbow.txt new file mode 100644 index 000000000..1617fbfcd --- /dev/null +++ b/wadsrc/static/zscript/strife/weaponcrossbow.txt @@ -0,0 +1,227 @@ +// Strife's Crossbow -------------------------------------------------------- + +class StrifeCrossbow : StrifeWeapon +{ + Default + { + +FLOORCLIP + Weapon.SelectionOrder 1200; + +WEAPON.NOALERT + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 8; + Weapon.AmmoType1 "ElectricBolts"; + Weapon.SisterWeapon "StrifeCrossbow2"; + Inventory.PickupMessage "$TXT_STRIFECROSSBOW"; + Tag "$TAG_STRIFECROSSBOW1"; + Inventory.Icon "CBOWA0"; + } + + States + { + Spawn: + CBOW A -1; + Stop; + Ready: + XBOW A 0 A_ShowElectricFlash; + XBOW A 1 A_WeaponReady; + Wait; + Deselect: + XBOW A 1 A_Lower; + Loop; + Select: + XBOW A 1 A_Raise; + Loop; + Fire: + XBOW A 3 A_ClearFlash; + XBOW B 6 A_FireArrow("ElectricBolt"); + XBOW C 4; + XBOW D 6; + XBOW E 3; + XBOW F 5; + XBOW G 0 A_ShowElectricFlash; + XBOW G 5 A_CheckReload; + Goto Ready+1; + Flash: + XBOW KLM 5; + Loop; + } + + //============================================================================ + // + // A_ClearFlash + // + //============================================================================ + + action void A_ClearFlash () + { + if (player == null) + return; + + player.SetPsprite (PSP_FLASH, null); + } + + //============================================================================ + // + // A_ShowElectricFlash + // + //============================================================================ + + action void A_ShowElectricFlash () + { + if (player != null) + { + player.SetPsprite (PSP_FLASH, player.ReadyWeapon.FindState('Flash')); + } + } + + //============================================================================ + // + // A_FireElectric + // + //============================================================================ + + action void A_FireArrow (class proj) + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + if (proj) + { + double savedangle = angle; + angle += Random2[Electric]() * (5.625/256) * AccuracyFactor(); + player.mo.PlayAttacking2 (); + SpawnPlayerMissile (proj); + angle = savedangle; + A_PlaySound ("weapons/xbowshoot", CHAN_WEAPON); + } + } +} + + +class StrifeCrossbow2 : StrifeCrossbow +{ + Default + { + Weapon.SelectionOrder 2700; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 0; + Weapon.AmmoType1 "PoisonBolts"; + Weapon.SisterWeapon "StrifeCrossbow"; + Tag "$TAG_STRIFECROSSBOW2"; + } + States + { + Ready: + XBOW H 1 A_WeaponReady; + Loop; + Deselect: + XBOW H 1 A_Lower; + Loop; + Select: + XBOW H 1 A_Raise; + Loop; + Fire: + XBOW H 3; + XBOW B 6 A_FireArrow("PoisonBolt"); + XBOW C 4; + XBOW D 6; + XBOW E 3; + XBOW I 5; + XBOW J 5 A_CheckReload; + Goto Ready; + Flash: + Stop; + } +} + +// Electric Bolt ------------------------------------------------------------ + +class ElectricBolt : Actor +{ + Default + { + Speed 30; + Radius 10; + Height 10; + Damage 10; + Projectile; + +STRIFEDAMAGE + +NOBLOCKMAP + +NOGRAVITY + +DROPOFF + MaxStepHeight 4; + SeeSound "misc/swish"; + ActiveSound "misc/swish"; + DeathSound "weapons/xbowhit"; + Obituary "$OB_MPELECTRICBOLT"; + } + States + { + Spawn: + AROW A 10 A_LoopActiveSound; + Loop; + Death: + ZAP1 A 3 A_AlertMonsters; + ZAP1 BCDEFE 3; + ZAP1 DCB 2; + ZAP1 A 1; + Stop; + } +} + + +// Poison Bolt -------------------------------------------------------------- + +class PoisonBolt : Actor +{ + Default + { + Speed 30; + Radius 10; + Height 10; + Damage 500; + Projectile; + +STRIFEDAMAGE + MaxStepHeight 4; + SeeSound "misc/swish"; + ActiveSound "misc/swish"; + Obituary "$OB_MPPOISONBOLT"; + } + States + { + Spawn: + ARWP A 10 A_LoopActiveSound; + Loop; + Death: + AROW A 1; + Stop; + } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target.bNoBlood) + { + return -1; + } + if (target.health < 1000000) + { + if (!target.bBoss) + return target.health + 10; + else + return 50; + } + return 1; + } + + +} + + diff --git a/wadsrc/static/zscript/strife/weapondagger.txt b/wadsrc/static/zscript/strife/weapondagger.txt new file mode 100644 index 000000000..24aef209b --- /dev/null +++ b/wadsrc/static/zscript/strife/weapondagger.txt @@ -0,0 +1,69 @@ + +// Punch Dagger ------------------------------------------------------------- + +class PunchDagger : StrifeWeapon +{ + Default + { + Weapon.SelectionOrder 3900; + +WEAPON.NOALERT + Obituary "$OB_MPPUNCHDAGGER"; + Tag "$TAG_PUNCHDAGGER"; + } + + States + { + Ready: + PNCH A 1 A_WeaponReady; + Loop; + Deselect: + PNCH A 1 A_Lower; + Loop; + Select: + PNCH A 1 A_Raise; + Loop; + Fire: + PNCH B 4; + PNCH C 4 A_JabDagger; + PNCH D 5; + PNCH C 4; + PNCH B 5 A_ReFire; + Goto Ready; + } + + //============================================================================ + // + // A_JabDagger + // + //============================================================================ + + action void A_JabDagger () + { + FTranslatedLineTarget t; + + int power = MIN(10, stamina / 10); + int damage = (random[JabDagger]() % (power + 8)) * (power + 2); + + if (FindInventory("PowerStrength")) + { + damage *= 10; + } + + double angle = angle + random2[JabDagger]() * (5.625 / 256); + double pitch = AimLineAttack (angle, 80.); + LineAttack (angle, 80., pitch, damage, 'Melee', "StrifeSpark", true, t); + + // turn to face target + if (t.linetarget) + { + S_Sound (t.linetarget.bNoBlood ? sound("misc/metalhit") : sound("misc/meathit"), CHAN_WEAPON); + angle = t.angleFromSource; + bJustAttacked = true; + t.linetarget.DaggerAlert (self); + } + else + { + A_PlaySound ("misc/swish", CHAN_WEAPON); + } + } +} diff --git a/wadsrc/static/zscript/strife/weaponflamer.txt b/wadsrc/static/zscript/strife/weaponflamer.txt new file mode 100644 index 000000000..9a47bc242 --- /dev/null +++ b/wadsrc/static/zscript/strife/weaponflamer.txt @@ -0,0 +1,118 @@ +// Flame Thrower ------------------------------------------------------------ + +class FlameThrower : StrifeWeapon +{ + Default + { + +FLOORCLIP + Weapon.SelectionOrder 2100; + Weapon.Kickback 0; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 100; + Weapon.UpSound "weapons/flameidle"; + Weapon.ReadySound "weapons/flameidle"; + Weapon.AmmoType1 "EnergyPod"; + Inventory.Icon "FLAMA0"; + Tag "$TAG_FLAMER"; + Inventory.PickupMessage "$TXT_FLAMER"; + } + + States + { + Spawn: + FLAM A -1; + Stop; + Ready: + FLMT AB 3 A_WeaponReady; + Loop; + Deselect: + FLMT A 1 A_Lower; + Loop; + Select: + FLMT A 1 A_Raise; + Loop; + Fire: + FLMF A 2 A_FireFlamer; + FLMF B 3 A_ReFire; + Goto Ready; + } + + //============================================================================ + // + // A_FireFlamer + // + //============================================================================ + + action void A_FireFlamer () + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + + player.mo.PlayAttacking2 (); + + Angle += Random2[Flamethrower]() * (5.625/256.); + Actor mo = SpawnPlayerMissile ("FlameMissile"); + if (mo != NULL) + { + mo.Vel.Z += 5; + } + } +} + + +// Flame Thrower Projectile ------------------------------------------------- + +class FlameMissile : Actor +{ + Default + { + Speed 15; + Height 11; + Radius 8; + Mass 10; + Damage 4; + DamageType "Fire"; + ReactionTime 8; + Projectile; + -NOGRAVITY + +STRIFEDAMAGE + MaxStepHeight 4; + RenderStyle "Add"; + SeeSound "weapons/flamethrower"; + Obituary "$OB_MPFLAMETHROWER"; + } + + States + { + Spawn: + FRBL AB 3 Bright; + FRBL C 3 Bright A_Countdown; + Loop; + Death: + FRBL D 5 Bright A_FlameDie; + FRBL EFGHI 5 Bright; + Stop; + } + + //============================================================================ + // + // A_FlameDie + // + //============================================================================ + + void A_FlameDie () + { + bNoGravity = true; + Vel.Z = random[FlameDie]() & 3; + } +} + diff --git a/wadsrc/static/zscript/strife/weapongrenade.txt b/wadsrc/static/zscript/strife/weapongrenade.txt new file mode 100644 index 000000000..7a775c57b --- /dev/null +++ b/wadsrc/static/zscript/strife/weapongrenade.txt @@ -0,0 +1,315 @@ +// High-Explosive Grenade Launcher ------------------------------------------ + +class StrifeGrenadeLauncher : StrifeWeapon +{ + Default + { + +FLOORCLIP + Weapon.SelectionOrder 2400; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 12; + Weapon.AmmoType1 "HEGrenadeRounds"; + Weapon.SisterWeapon "StrifeGrenadeLauncher2"; + Inventory.Icon "GRNDA0"; + Tag "$TAG_GLAUNCHER1"; + Inventory.PickupMessage "$TXT_GLAUNCHER"; + } + + States + { + Spawn: + GRND A -1; + Stop; + Ready: + GREN A 1 A_WeaponReady; + Loop; + Deselect: + GREN A 1 A_Lower; + Loop; + Select: + GREN A 1 A_Raise; + Loop; + Fire: + GREN A 5 A_FireGrenade("HEGrenade", -90, "Flash"); + GREN B 10; + GREN A 5 A_FireGrenade("HEGrenade", 90, "Flash2"); + GREN C 10; + GREN A 0 A_ReFire; + Goto Ready; + Flash: + GREF A 5 Bright A_Light1; + Goto LightDone; + Flash2: + GREF B 5 Bright A_Light2; + Goto LightDone; + } + + //============================================================================ + // + // A_FireGrenade + // + //============================================================================ + + action void A_FireGrenade (class grenadetype, double angleofs, statelabel flash) + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + + player.SetPsprite (PSP_FLASH, weapon.FindState(flash), true); + } + + if (grenadetype != null) + { + AddZ(32); + Actor grenade = SpawnSubMissile (grenadetype, self); + AddZ(-32); + if (grenade == null) + return; + + if (grenade.SeeSound != 0) + { + grenade.A_PlaySound (grenade.SeeSound, CHAN_VOICE); + } + + grenade.Vel.Z = (-clamp(tan(Pitch), -5, 5)) * grenade.Speed + 8; + + Vector2 offset = AngleToVector(angle, radius + grenade.radius); + double an = Angle + angleofs; + offset += AngleToVector(an, 15); + grenade.SetOrigin(grenade.Vec3Offset(offset.X, offset.Y, 0.), false); + } + } +} + +// White Phosphorous Grenade Launcher --------------------------------------- + +class StrifeGrenadeLauncher2 : StrifeGrenadeLauncher +{ + Default + { + Weapon.SelectionOrder 3200; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 0; + Weapon.AmmoType1 "PhosphorusGrenadeRounds"; + Weapon.SisterWeapon "StrifeGrenadeLauncher"; + Tag "$TAG_GLAUNCHER2"; + } + States + { + Ready: + GREN D 1 A_WeaponReady; + Loop; + Deselect: + GREN D 1 A_Lower; + Loop; + Select: + GREN D 1 A_Raise; + Loop; + Fire: + GREN D 5 A_FireGrenade("PhosphorousGrenade", -90, "Flash"); + GREN E 10; + GREN D 5 A_FireGrenade("PhosphorousGrenade", 90, "Flash2"); + GREN F 10; + GREN A 0 A_ReFire; + Goto Ready; + Flash: + GREF C 5 Bright A_Light1; + Goto LightDone; + Flash2: + GREF D 5 Bright A_Light2; + Goto LightDone; + } +} + +// High-Explosive Grenade --------------------------------------------------- + +class HEGrenade : Actor +{ + Default + { + Speed 15; + Radius 13; + Height 13; + Mass 20; + Damage 1; + Reactiontime 30; + Projectile; + -NOGRAVITY + +STRIFEDAMAGE + +BOUNCEONACTORS + +EXPLODEONWATER + MaxStepHeight 4; + BounceType "Doom"; + BounceFactor 0.5; + BounceCount 2; + SeeSound "weapons/hegrenadeshoot"; + DeathSound "weapons/hegrenadebang"; + Obituary "$OB_MPSTRIFEGRENADE"; + } + States + { + Spawn: + GRAP AB 3 A_Countdown; + Loop; + Death: + BNG4 A 0 Bright A_NoGravity; + BNG4 A 0 Bright A_SetRenderStyle(1, STYLE_Normal); + BNG4 A 2 Bright A_Explode(192, 192, alert:true); + BNG4 BCDEFGHIJKLMN 3 Bright; + Stop; + } +} + +// White Phosphorous Grenade ------------------------------------------------ + +class PhosphorousGrenade : Actor +{ + Default + { + Speed 15; + Radius 13; + Height 13; + Mass 20; + Damage 1; + Reactiontime 40; + Projectile; + -NOGRAVITY + +STRIFEDAMAGE + +BOUNCEONACTORS + +EXPLODEONWATER + BounceType "Doom"; + MaxStepHeight 4; + BounceFactor 0.5; + BounceCount 2; + SeeSound "weapons/phgrenadeshoot"; + DeathSound "weapons/phgrenadebang"; + Obituary "$OB_MPPHOSPHOROUSGRENADE"; + } + States + { + Spawn: + GRIN AB 3 A_Countdown; + Loop; + Death: + BNG3 A 2 A_SpawnItemEx("PhosphorousFire"); + Stop; + } +} + +// Fire from the Phoshorous Grenade ----------------------------------------- + +class PhosphorousFire : Actor +{ + Default + { + Reactiontime 120; + DamageType "Fire"; + +NOBLOCKMAP + +FLOORCLIP + +NOTELEPORT + +NODAMAGETHRUST + +DONTSPLASH + RenderStyle "Add"; + Obituary "$OB_MPPHOSPHOROUSGRENADE"; + } + + States + { + Spawn: + BNG3 B 2 Bright A_Burnarea; + BNG3 C 2 Bright A_Countdown; + FLBE A 2 Bright A_Burnination; + FLBE B 2 Bright A_Countdown; + FLBE C 2 Bright A_Burnarea; + FLBE D 3 Bright A_Countdown; + FLBE E 3 Bright A_Burnarea; + FLBE F 3 Bright A_Countdown; + FLBE G 3 Bright A_Burnination; + Goto Spawn+5; + Death: + FLBE H 2 Bright; + FLBE I 2 Bright A_Burnination; + FLBE JK 2 Bright; + Stop; + } + + int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target.bNoBlood) + { + return damage / 2; + } + return Super.DoSpecialDamage (target, damage, damagetype); + } + + // This function is mostly redundant and only kept in case some mod references it. + void A_Burnarea () + { + A_Explode(128, 128); + } + + void A_Burnination () + { + Vel.Z -= 8; + Vel.X += (random2[PHBurn] (3)); + Vel.Y += (random2[PHBurn] (3)); + A_PlaySound ("world/largefire", CHAN_VOICE); + + // Only the main fire spawns more. + if (!bDropped) + { + // Original x and y offsets seemed to be like this: + // x + (((pr_phburn() + 12) & 31) << F.RACBITS); + // + // But that creates a lop-sided burn because it won't use negative offsets. + int xofs, xrand = random[PHBurn](); + int yofs, yrand = random[PHBurn](); + + // Adding 12 is pointless if you're going to mask it afterward. + xofs = xrand & 31; + if (xrand & 128) + { + xofs = -xofs; + } + + yofs = yrand & 31; + if (yrand & 128) + { + yofs = -yofs; + } + + Vector2 newpos = Vec2Offset(xofs, yofs); + + Sector sec = Sector.PointInSector(newpos); + // Consider portals and 3D floors instead of just using the current sector's z. + double floorh = sec.NextLowestFloorAt(newpos.x, newpos.y, pos.z+4, 0, MaxStepHeight); + + // The sector's floor is too high so spawn the flame elsewhere. + if (floorh + MaxStepHeight) + { + newpos = Pos.xy; + } + + Actor drop = Spawn("PhosphorousFire", (newpos, pos.z + 4.), ALLOW_REPLACE); + if (drop != NULL) + { + drop.Vel.X = Vel.X + random2[PHBurn] (7); + drop.Vel.Y = Vel.Y + random2[PHBurn] (7); + drop.Vel.Z = Vel.Z - 1; + drop.reactiontime = (random[PHBurn]() & 3) + 2; + drop.bDropped = true; + } + } + } + + +} + diff --git a/wadsrc/static/zscript/strife/weaponmauler.txt b/wadsrc/static/zscript/strife/weaponmauler.txt new file mode 100644 index 000000000..80f3afcde --- /dev/null +++ b/wadsrc/static/zscript/strife/weaponmauler.txt @@ -0,0 +1,286 @@ +// Mauler ------------------------------------------------------------------- +// The scatter version + +class Mauler : StrifeWeapon +{ + Default + { + +FLOORCLIP + Weapon.SelectionOrder 300; + Weapon.AmmoUse1 20; + Weapon.AmmoGive1 40; + Weapon.AmmoType1 "EnergyPod"; + Weapon.SisterWeapon "Mauler2"; + Inventory.Icon "TRPDA0"; + Tag "$TAG_MAULER1"; + Inventory.PickupMessage "$TXT_MAULER"; + Obituary "$OB_MPMAULER1"; + } + + States + { + Ready: + MAUL FGHA 6 A_WeaponReady; + Loop; + Deselect: + MAUL A 1 A_Lower; + Loop; + Select: + MAUL A 1 A_Raise; + Loop; + Fire: + BLSF A 5 Bright A_FireMauler1; + MAUL B 3 Bright A_Light1; + MAUL C 2 A_Light2; + MAUL DE 2; + MAUL A 7 A_Light0; + MAUL H 7; + MAUL G 7 A_CheckReload; + Goto Ready; + Spawn: + TRPD A -1; + Stop; + } + + //============================================================================ + // + // A_FireMauler1 + // + // Hey! This is exactly the same as a super shotgun except for the sound + // and the bullet puffs and the disintegration death. + // + //============================================================================ + + action void A_FireMauler1() + { + if (player == null) + { + return; + } + + A_PlaySound ("weapons/mauler1", CHAN_WEAPON); + Weapon weap = player.ReadyWeapon; + if (weap != null) + { + if (!weap.DepleteAmmo (weap.bAltFire, true, 2)) + return; + + } + player.mo.PlayAttacking2 (); + + double pitch = BulletSlope (); + + for (int i = 0 ; i < 20 ; i++) + { + int damage = 5 * random[Mauler1](1, 3); + double ang = angle + Random2[Mauler1]() * (11.25 / 256); + + // Strife used a range of 2112 units for the mauler to signal that + // it should use a different puff. ZDoom's default range is longer + // than this, so let's not handicap it by being too faithful to the + // original. + + LineAttack (ang, PLAYERMISSILERANGE, pitch + Random2[Mauler1]() * (7.097 / 256), damage, 'Hitscan', "MaulerPuff"); + } + } +} + + +// Mauler Torpedo version --------------------------------------------------- + +class Mauler2 : Mauler +{ + Default + { + Weapon.SelectionOrder 3300; + Weapon.AmmoUse1 30; + Weapon.AmmoGive1 0; + Weapon.AmmoType1 "EnergyPod"; + Weapon.SisterWeapon "Mauler"; + Tag "$TAG_MAULER2"; + } + + States + { + Ready: + MAUL IJKL 7 A_WeaponReady; + Loop; + Deselect: + MAUL I 1 A_Lower; + Loop; + Select: + MAUL I 1 A_Raise; + Loop; + Fire: + MAUL I 20 A_FireMauler2Pre; + MAUL J 10 A_Light1; + BLSF A 10 Bright A_FireMauler2; + MAUL B 10 Bright A_Light2; + MAUL C 2; + MAUL D 2 A_Light0; + MAUL E 2 A_ReFire; + Goto Ready; + } + + //============================================================================ + // + // A_FireMauler2Pre + // + // Makes some noise and moves the psprite. + // + //============================================================================ + + action void A_FireMauler2Pre () + { + A_PlaySound ("weapons/mauler2charge", CHAN_WEAPON); + + if (player != null) + { + PSprite psp = player.GetPSprite(PSP_WEAPON); + psp.x += Random2[Mauler2]() / 64.; + psp.y += Random2[Mauler2]() / 64.; + } + } + + //============================================================================ + // + // A_FireMauler2Pre + // + // Fires the torpedo. + // + //============================================================================ + + action void A_FireMauler2 () + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + player.mo.PlayAttacking2 (); + + SpawnPlayerMissile ("MaulerTorpedo"); + DamageMobj (self, null, 20, 'Disintegrate'); + Thrust(7.8125, Angle+180.); + } +} + + +// Mauler "Bullet" Puff ----------------------------------------------------- + +class MaulerPuff : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + RenderStyle "Add"; + DamageType "Disintegrate"; + } + States + { + Spawn: + MPUF AB 5; + POW1 ABCDE 4; + Stop; + } +} + +// The Mauler's Torpedo ----------------------------------------------------- + +class MaulerTorpedo : Actor +{ + Default + { + Speed 20; + Height 8; + Radius 13; + Damage 1; + DamageType "Disintegrate"; + Projectile; + +STRIFEDAMAGE + MaxStepHeight 4; + RenderStyle "Add"; + SeeSound "weapons/mauler2fire"; + DeathSound "weapons/mauler2hit"; + Obituary "$OB_MPMAULER"; + } + + States + { + Spawn: + TORP ABCD 4 Bright; + Loop; + Death: + THIT AB 8 Bright; + THIT C 8 Bright A_MaulerTorpedoWave; + THIT DE 8 Bright; + Stop; + } + + //============================================================================ + // + // A_MaulerTorpedoWave + // + // Launches lots of balls when the torpedo hits something. + // + //============================================================================ + + action void A_MaulerTorpedoWave() + { + readonly wavedef = GetDefaultByType("MaulerTorpedoWave"); + double savedz = pos.z; + angle += 180.; + + // If the torpedo hit the ceiling, it should still spawn the wave + if (wavedef && ceilingz < pos.z + wavedef.Height) + { + SetZ(ceilingz - wavedef.Height); + } + + for (int i = 0; i < 80; ++i) + { + Angle += 4.5; + SpawnSubMissile ("MaulerTorpedoWave", target); + } + SetZ(savedz); + } +} + + +// The mini torpedoes shot by the big torpedo -------------------------------- + +class MaulerTorpedoWave : Actor +{ + Default + { + Speed 35; + Radius 13; + Height 13; + Damage 10; + DamageType "Disintegrate"; + Projectile; + +STRIFEDAMAGE + MaxStepHeight 4; + RenderStyle "Add"; + Obituary "$OB_MPMAULER"; + } + States + { + Spawn: + TWAV AB 9 Bright; + Death: + TWAV C 9 Bright; + Stop; + } + +} + + diff --git a/wadsrc/static/zscript/strife/weaponmissile.txt b/wadsrc/static/zscript/strife/weaponmissile.txt new file mode 100644 index 000000000..589f75493 --- /dev/null +++ b/wadsrc/static/zscript/strife/weaponmissile.txt @@ -0,0 +1,137 @@ +// Mini-Missile Launcher ---------------------------------------------------- + +class MiniMissileLauncher : StrifeWeapon +{ + Default + { + +FLOORCLIP + Weapon.SelectionOrder 1800; + Weapon.AmmoUse1 1; + Weapon.AmmoGive1 8; + Weapon.AmmoType1 "MiniMissiles"; + Inventory.Icon "MMSLA0"; + Tag "$TAG_MMLAUNCHER"; + Inventory.PickupMessage "$TXT_MMLAUNCHER"; + } + + States + { + Spawn: + MMSL A -1; + Stop; + Ready: + MMIS A 1 A_WeaponReady; + Loop; + Deselect: + MMIS A 1 A_Lower; + Loop; + Select: + MMIS A 1 A_Raise; + Loop; + Fire: + MMIS A 4 A_FireMiniMissile; + MMIS B 4 A_Light1; + MMIS C 5 Bright; + MMIS D 2 Bright A_Light2; + MMIS E 2 Bright; + MMIS F 2 Bright A_Light0; + MMIS F 0 A_ReFire; + Goto Ready; + } + + //============================================================================ + // + // A_FireMiniMissile + // + //============================================================================ + + action void A_FireMiniMissile () + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + + double savedangle = angle; + angle += Random2[MiniMissile]() * (11.25 / 256) * AccuracyFactor(); + player.mo.PlayAttacking2 (); + SpawnPlayerMissile ("MiniMissile"); + angle = savedangle; + } +} + + +// Rocket Trail ------------------------------------------------------------- + +class RocketTrail : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + RenderStyle "Translucent"; + Alpha 0.25; + SeeSound "misc/missileinflight"; + } + States + { + Spawn: + PUFY BCBCD 4; + Stop; + } +} + +// Rocket Puff -------------------------------------------------------------- + +class MiniMissilePuff : StrifePuff +{ + Default + { + -ALLOWPARTICLES + } + States + { + Spawn: + Goto Crash; + } +} + +// Mini Missile ------------------------------------------------------------- + +class MiniMissile : Actor +{ + Default + { + Speed 20; + Radius 10; + Height 14; + Damage 10; + Projectile; + +STRIFEDAMAGE + MaxStepHeight 4; + SeeSound "weapons/minimissile"; + DeathSound "weapons/minimissilehit"; + Obituary "$OB_MPMINIMISSILELAUNCHER"; + } + States + { + Spawn: + MICR A 6 Bright A_RocketInFlight; + Loop; + Death: + SMIS A 0 Bright A_SetRenderStyle(1, STYLE_Normal); + SMIS A 5 Bright A_Explode(64, 64, alert:true); + SMIS B 5 Bright; + SMIS C 4 Bright; + SMIS DEFG 2 Bright; + Stop; + } +} +