From 192104aea2a267e67be13aa3e3685ee4f323b2e4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 07:45:49 +0100 Subject: [PATCH 01/48] - scriptified P_MorphPlayer and dependencies. It still needs its counterpart scriptified as well before it can work. --- src/g_shared/a_morph.cpp | 12 -- src/gi.cpp | 1 + src/p_user.cpp | 7 + src/scripting/backend/vmbuilder.cpp | 4 +- wadsrc/static/zscript/base.txt | 1 + wadsrc/static/zscript/shared/morph.txt | 218 ++++++++++++++++++++++++ wadsrc/static/zscript/shared/player.txt | 23 ++- 7 files changed, 250 insertions(+), 16 deletions(-) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 95b85349cc..c97e3111dd 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -173,18 +173,6 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassActor *spawntype, in return true; } -DEFINE_ACTION_FUNCTION(_PlayerInfo, MorphPlayer) -{ - PARAM_SELF_STRUCT_PROLOGUE(player_t); - PARAM_POINTER(activator, player_t); - PARAM_CLASS(spawntype, APlayerPawn); - PARAM_INT(duration); - PARAM_INT(style); - PARAM_CLASS(enter_flash, AActor); - PARAM_CLASS(exit_flash, AActor); - ACTION_RETURN_BOOL(P_MorphPlayer(activator, self, spawntype, duration, style, enter_flash, exit_flash)); -} - //---------------------------------------------------------------------------- // // FUNC P_UndoPlayerMorph diff --git a/src/gi.cpp b/src/gi.cpp index 87c8b68cf9..38aa76d1f3 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -61,6 +61,7 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_single) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_coop) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_dm) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mSliderColor) +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, telefogheight) const char *GameNames[17] = diff --git a/src/p_user.cpp b/src/p_user.cpp index 48ad0ee671..fef2d51d5b 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -293,6 +293,13 @@ CCMD (playerclasses) } } +DEFINE_ACTION_FUNCTION(APlayerPawn, Substitute) +{ + PARAM_SELF_PROLOGUE(APlayerPawn); + PARAM_OBJECT(replace, APlayerPawn); + DObject::StaticPointerSubstitution(self, replace); + return 0; +} // // Movement. diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index a141c3f8e9..9d910bc63a 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -802,7 +802,6 @@ VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, const VersionInfo void FFunctionBuildList::Build() { - int errorcount = 0; int codesize = 0; int datasize = 0; FILE *dump = nullptr; @@ -911,7 +910,8 @@ void FFunctionBuildList::Build() } VMFunction::CreateRegUseInfo(); FScriptPosition::StrictErrors = false; - if (Args->CheckParm("-dumpjit")) DumpJit(); + + if (FScriptPosition::ErrorCounter == 0 && Args->CheckParm("-dumpjit")) DumpJit(); mItems.Clear(); mItems.ShrinkToFit(); FxAlloc.FreeAllBlocks(); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 2576ea9b20..fff6f9ecce 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -366,6 +366,7 @@ struct GameInfoStruct native native double gibfactor; native bool intermissioncounter; native Name mSliderColor; + native double telefogheight; } class Object native diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index 149cf3ceaa..a2eed3dc06 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -1,3 +1,221 @@ +extend class PlayerPawn +{ + //=========================================================================== + // + // EndAllPowerupEffects + // + // Calls EndEffect() on every Powerup in the inventory list. + // + //=========================================================================== + + void InitAllPowerupEffects() + { + let item = Inv; + while (item != null) + { + let power = Powerup(item); + if (power != null) + { + power.InitEffect(); + } + item = item.Inv; + } + } + + //=========================================================================== + // + // EndAllPowerupEffects + // + // Calls EndEffect() on every Powerup in the inventory list. + // + //=========================================================================== + + void EndAllPowerupEffects() + { + let item = Inv; + while (item != null) + { + let power = Powerup(item); + if (power != null) + { + power.EndEffect(); + } + item = item.Inv; + } + } + + //=========================================================================== + // + // + // + //=========================================================================== + + virtual void ActivateMorphWeapon () + { + class morphweaponcls = MorphWeapon; + player.PendingWeapon = WP_NOCHANGE; + + if (player.ReadyWeapon != null) + { + player.GetPSprite(PSP_WEAPON).y = WEAPONTOP; + } + + if (morphweaponcls == null || !(morphweaponcls is 'Weapon')) + { // No weapon at all while morphed! + player.ReadyWeapon = null; + } + else + { + player.ReadyWeapon = Weapon(FindInventory (morphweaponcls)); + if (player.ReadyWeapon == null) + { + player.ReadyWeapon = Weapon(GiveInventoryType (morphweaponcls)); + if (player.ReadyWeapon != null) + { + player.ReadyWeapon.GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph + } + } + if (player.ReadyWeapon != null) + { + player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState()); + } + } + + if (player.ReadyWeapon != null) + { + player.SetPsprite(PSP_FLASH, null); + } + + player.PendingWeapon = WP_NOCHANGE; + } + + //--------------------------------------------------------------------------- + // + // FUNC P_MorphPlayer + // + // Returns true if the player gets turned into a chicken/pig. + // + // TODO: Allow morphed players to receive weapon sets (not just one weapon), + // since they have their own weapon slots now. + // + //--------------------------------------------------------------------------- + + virtual bool MorphPlayer(playerinfo activator, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) + { + if (bDontMorph) + { + return false; + } + if (bInvulnerable && (player != activator || !(style & MRF_WHENINVULNERABLE))) + { // Immune when invulnerable unless this is a power we activated + return false; + } + if (player.morphTics) + { // Player is already a beast + if ((GetClass() == spawntype) && bCanSuperMorph + && (player.morphTics < (((duration) ? duration : DEFMORPHTICS) - TICRATE)) + && FindInventory('PowerWeaponLevel2', true) == null) + { // Make a super chicken + GiveInventoryType ('PowerWeaponLevel2'); + } + return false; + } + if (health <= 0) + { // Dead players cannot morph + return false; + } + if (spawntype == null) + { + return false; + } + if (!(spawntype is 'PlayerPawn')) + { + return false; + } + if (spawntype == GetClass()) + { + return false; + } + + let morphed = PlayerPawn(Spawn (spawntype, Pos, NO_REPLACE)); + EndAllPowerupEffects(); + Substitute(morphed); + if ((style & MRF_TRANSFERTRANSLATION) && !morphed.bDontTranslate) + { + morphed.Translation = Translation; + } + if (tid != 0 && (style & MRF_NEWTIDBEHAVIOUR)) + { + morphed.ChangeTid(tid); + ChangeTid(0); + } + morphed.Angle = Angle; + morphed.target = target; + morphed.tracer = tracer; + morphed.alternative = self; + morphed.FriendPlayer = FriendPlayer; + morphed.DesignatedTeam = DesignatedTeam; + morphed.Score = Score; + player.PremorphWeapon = player.ReadyWeapon; + + morphed.special2 = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility + morphed.player = player; + + if (morphed.ViewHeight > player.viewheight && player.deltaviewheight == 0) + { // If the new view height is higher than the old one, start moving toward it. + player.deltaviewheight = player.GetDeltaViewHeight(); + } + morphed.bShadow |= bShadow; + morphed.bNoGravity |= bNoGravity; + morphed.bFly |= bFly; + morphed.bGhost |= bGhost; + + if (enter_flash == null) enter_flash = 'TeleportFog'; + let eflash = Spawn(enter_flash, Pos + (0, 0, gameinfo.telefogheight), ALLOW_REPLACE); + let p = player; + player = null; + alternative = morphed; + bSolid = false; + bShootable = false; + bUnmorphed = true; + bInvisible = true; + + p.morphTics = (duration) ? duration : DEFMORPHTICS; + + // [MH] Used by SBARINFO to speed up face drawing + p.MorphedPlayerClass = spawntype; + + p.MorphStyle = style; + if (exit_flash == null) exit_flash = 'TeleportFog'; + p.MorphExitFlash = exit_flash; + p.health = morphed.health; + p.mo = morphed; + p.vel = (0, 0); + morphed.ObtainInventory (self); + // Remove all armor + for (Inventory item = morphed.Inv; item != null; ) + { + let next = item.Inv; + if (item is 'Armor') + { + item.DepleteOrDestroy(); + } + item = next; + } + morphed.InitAllPowerupEffects(); + morphed.ActivateMorphWeapon (); + if (p.camera == self) // can this happen? + { + p.camera = morphed; + } + morphed.ScoreIcon = ScoreIcon; // [GRB] + if (eflash) + eflash.target = morphed; + return true; + } + +} + class MorphProjectile : Actor { diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index dce27e106d..4d47552b95 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -15,6 +15,7 @@ class PlayerPawn : Actor native const CROUCHSPEED = (1./12); // [RH] # of ticks to complete a turn180 const TURN180_TICKS = ((TICRATE / 4) + 1); + const DEFMORPHTICS = 40 * TICRATE; native int crouchsprite; native int MaxHealth; @@ -1261,6 +1262,7 @@ class PlayerPawn : Actor native player.mo.CheckAirSupply(); } } + //---------------------------------------------------------------------------- // @@ -1278,6 +1280,7 @@ class PlayerPawn : Actor native native void CheckWeaponButtons(); native Weapon BestWeapon(class ammotype); native Weapon PickNewWeapon(class ammotype); + native void Substitute(PlayerPawn replacement); } @@ -1434,7 +1437,7 @@ struct PlayerInfo native play // this is what internally is known as player_t native ClassMorphedPlayerClass; native int MorphStyle; native Class MorphExitFlash; - native Class PremorphWeapon; + native Weapon PremorphWeapon; native int chickenPeck; native int jumpTics; native bool onground; @@ -1467,7 +1470,16 @@ struct PlayerInfo native play // this is what internally is known as player_t native readonly @UserCmd original_cmd; - native bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null); + bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) + { + if (mo != null) + { + // The actual implementation is on PlayerPawn where it can be overridden. + return mo.MorphPlayer(p, spawntype, duration, style, enter_flash, exit_flash); + } + return false; + } + 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); @@ -1538,6 +1550,13 @@ struct PlayerInfo native play // this is what internally is known as player_t return allfrags; } + double GetDeltaViewHeight() + { + return (mo.ViewHeight + crouchviewdelta - viewheight) / 8; + } + + + } struct PlayerClass native From 78d6832d14ac76c2de4a6730f06e563bacb4050a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 08:17:30 +0100 Subject: [PATCH 02/48] - P_UndoPlayerMorph scriptified. Not tested yet and still missing a new native interface. --- src/d_player.h | 1 - src/g_inventory/a_weapons.cpp | 68 ++++--- src/g_inventory/a_weapons.h | 4 +- src/g_shared/a_morph.cpp | 18 +- src/p_saveg.cpp | 2 +- src/p_user.cpp | 36 +--- src/scripting/thingdef.cpp | 1 + wadsrc/static/zscript/inventory/weapons.txt | 28 +++ wadsrc/static/zscript/shared/morph.txt | 192 ++++++++++++++++++++ wadsrc/static/zscript/shared/player.txt | 18 +- 10 files changed, 301 insertions(+), 67 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index ecb3a71eac..9341310e69 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -105,7 +105,6 @@ public: void GiveDeathmatchInventory (); void FilterCoopRespawnInventory (APlayerPawn *oldplayer); - void SetupWeaponSlots (); void GiveDefaultInventory (); // These are virtual on the script side only. diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index a72a10d7e8..9cef1686ea 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -382,31 +382,6 @@ DEFINE_ACTION_FUNCTION(AWeapon, DepleteAmmo) } -//=========================================================================== -// -// AWeapon :: PostMorphWeapon -// -// Bring this weapon up after a player unmorphs. -// -//=========================================================================== - -void AWeapon::PostMorphWeapon () -{ - DPSprite *pspr; - if (Owner == nullptr) - { - return; - } - Owner->player->PendingWeapon = WP_NOCHANGE; - Owner->player->ReadyWeapon = this; - Owner->player->refire = 0; - - pspr = Owner->player->GetPSprite(PSP_WEAPON); - pspr->y = WEAPONBOTTOM; - pspr->ResetInterpolation(); - pspr->SetState(GetUpState()); -} - //=========================================================================== // // AWeapon :: GetUpState @@ -1382,6 +1357,49 @@ void P_PlaybackKeyConfWeapons(FWeaponSlots *slots) PlayingKeyConf = nullptr; } +//=========================================================================== +// +// APlayerPawn :: SetupWeaponSlots +// +// Sets up the default weapon slots for this player. If this is also the +// local player, determines local modifications and sends those across the +// network. Ignores voodoo dolls. +// +//=========================================================================== + +void FWeaponSlots::SetupWeaponSlots(APlayerPawn *pp) +{ + auto player = pp->player; + if (player != nullptr && player->mo == pp) + { + player->weapons.StandardSetup(pp->GetClass()); + // If we're the local player, then there's a bit more work to do. + // This also applies if we're a bot and this is the net arbitrator. + if (player - players == consoleplayer || + (player->Bot != nullptr && consoleplayer == Net_Arbitrator)) + { + FWeaponSlots local_slots(player->weapons); + if (player->Bot != nullptr) + { // Bots only need weapons from KEYCONF, not INI modifications. + P_PlaybackKeyConfWeapons(&local_slots); + } + else + { + local_slots.LocalSetup(pp->GetClass()); + } + local_slots.SendDifferences(int(player - players), player->weapons); + } + } +} + +DEFINE_ACTION_FUNCTION(FWeaponSlots, SetupWeaponSlots) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(pawn, APlayerPawn); + FWeaponSlots::SetupWeaponSlots(pawn); + return 0; +} + //=========================================================================== // // P_SetupWeapons_ntohton diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 1e0634ddd5..3f5d34dd8f 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -3,6 +3,7 @@ #include "a_pickups.h" class PClassActor; class AWeapon; +class APlayerPawn; class FWeaponSlot { @@ -71,6 +72,7 @@ struct FWeaponSlots void SendDifferences(int playernum, const FWeaponSlots &other); int RestoreSlots (FConfigFile *config, const char *section); void PrintSettings(); + static void SetupWeaponSlots(APlayerPawn *pp); void AddSlot(int slot, PClassActor *type, bool feedback); void AddSlotDefault(int slot, PClassActor *type, bool feedback); @@ -126,8 +128,6 @@ public: void Finalize(FStateDefinitions &statedef) override; void Serialize(FSerializer &arc) override; - void PostMorphWeapon(); - // scripted virtuals. FState *GetUpState (); FState *GetDownState (); diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index c97e3111dd..a5216a226d 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -38,6 +38,18 @@ static FRandom pr_morphmonst ("MorphMonster"); + +bool P_MorphPlayer(player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) +{ + return false; +} + +bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) +{ + return false; +} + +#if 0 void EndAllPowerupEffects(AInventory *item); void InitAllPowerupEffects(AInventory *item); @@ -336,7 +348,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, beastweap = player->ReadyWeapon; if (player->PremorphWeapon != nullptr) { - player->PremorphWeapon->PostMorphWeapon (); + player->PremorphWeapon-> P ostMorphWeapon (); } else { @@ -386,9 +398,11 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, UndoPlayerMorph) PARAM_POINTER_NOT_NULL(player, player_t); PARAM_INT(unmorphflag); PARAM_BOOL(force); - ACTION_RETURN_BOOL(P_UndoPlayerMorph(self, player, unmorphflag, force)); + ACTION_RETURN_BOOL(P_UndoPlayerMorph(player, self, unmorphflag, force)); } +#endif + //--------------------------------------------------------------------------- // // FUNC P_MorphMonster diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index e5c111a8f6..fa1e6f1053 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -1025,7 +1025,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) { if (playeringame[i] && players[i].mo != NULL) { - players[i].mo->SetupWeaponSlots(); + FWeaponSlots::SetupWeaponSlots(players[i].mo); } } } diff --git a/src/p_user.cpp b/src/p_user.cpp index fef2d51d5b..ea583dde3d 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -916,7 +916,7 @@ void APlayerPawn::Tick() void APlayerPawn::PostBeginPlay() { Super::PostBeginPlay(); - SetupWeaponSlots(); + FWeaponSlots::SetupWeaponSlots(this); // Voodoo dolls: restore original floorz/ceilingz logic if (player == NULL || player->mo != this) @@ -931,40 +931,6 @@ void APlayerPawn::PostBeginPlay() } } -//=========================================================================== -// -// APlayerPawn :: SetupWeaponSlots -// -// Sets up the default weapon slots for this player. If this is also the -// local player, determines local modifications and sends those across the -// network. Ignores voodoo dolls. -// -//=========================================================================== - -void APlayerPawn::SetupWeaponSlots() -{ - if (player != NULL && player->mo == this) - { - player->weapons.StandardSetup(GetClass()); - // If we're the local player, then there's a bit more work to do. - // This also applies if we're a bot and this is the net arbitrator. - if (player - players == consoleplayer || - (player->Bot != NULL && consoleplayer == Net_Arbitrator)) - { - FWeaponSlots local_slots(player->weapons); - if (player->Bot != NULL) - { // Bots only need weapons from KEYCONF, not INI modifications. - P_PlaybackKeyConfWeapons(&local_slots); - } - else - { - local_slots.LocalSetup(GetClass()); - } - local_slots.SendDifferences(int(player - players), player->weapons); - } - } -} - //=========================================================================== // // APlayerPawn :: AddInventory diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index d7869c26a3..95206801ef 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -186,6 +186,7 @@ PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *func auto cls_target = funcsym ? PType::toClass(funcsym->OwningClass) : nullptr; if (funcsym == nullptr) { + if (PClass::FindClass(name)) return nullptr; // Special case when a class's member variable hides a global class name. This should still work. sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars()); } else if ((funcsym->Variants[0].Flags & VARF_Private) && symtable != &funccls->Symbols) diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 4786e44d7a..8b9b71ff7b 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -677,6 +677,33 @@ class Weapon : StateProvider native } } + + //=========================================================================== + // + // Weapon :: PostMorphWeapon + // + // Bring this weapon up after a player unmorphs. + // + //=========================================================================== + + void PostMorphWeapon () + { + if (Owner == null) + { + return; + } + let p = owner.player; + p.PendingWeapon = WP_NOCHANGE; + p.ReadyWeapon = self; + p.refire = 0; + + let pspr = p.GetPSprite(PSP_WEAPON); + pspr.y = WEAPONBOTTOM; + pspr.ResetInterpolation(); + pspr.SetState(GetUpState()); + } + + } @@ -746,4 +773,5 @@ class WeaponGiver : Weapon struct WeaponSlots native { native bool, int, int LocateWeapon(class weap); + native static void SetupWeaponSlots(PlayerPawn pp); } diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index a2eed3dc06..137fd9c0eb 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -214,6 +214,198 @@ extend class PlayerPawn return true; } + //---------------------------------------------------------------------------- + // + // FUNC P_UndoPlayerMorph + // + //---------------------------------------------------------------------------- + + virtual bool UndoPlayerMorph(playerinfo activator, int unmorphflag = 0, bool force = false) + { + if (alternative == null) + { + return false; + } + + let player = self.player; + bool DeliberateUnmorphIsOkay = !!(MRF_STANDARDUNDOING & unmorphflag); + + if ((bInvulnerable) // If the player is invulnerable + && ((player != activator) // and either did not decide to unmorph, + || (!((player.MorphStyle & MRF_WHENINVULNERABLE) // or the morph style does not allow it + || (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it), + { // Then the player is immune to the unmorph. + return false; + } + + let altmo = PlayerPawn(alternative); + altmo.SetOrigin (Pos, false); + altmo.bSolid = true; + bSolid = false; + if (!force && !altmo.TestMobjLocation()) + { // Didn't fit + altmo.bSolid = false; + bSolid = true; + player.morphTics = 2*TICRATE; + return false; + } + // No longer using tracer as morph storage. That is what 'alternative' is for. + // If the tracer has changed on the morph, change the original too. + altmo.target = target; + altmo.tracer = tracer; + self.player = null; + altmo.alternative = alternative = null; + + // Remove the morph power if the morph is being undone prematurely. + for (Inventory item = Inv; item != null;) + { + let next = item.Inv; + if (item is "PowerMorph") + { + item.Destroy(); + } + item = next; + } + EndAllPowerupEffects(); + altmo.ObtainInventory (self); + Substitute(altmo); + if ((tid != 0) && (player.MorphStyle & MRF_NEWTIDBEHAVIOUR)) + { + altmo.ChangeTid(tid); + } + altmo.Angle = Angle; + altmo.player = player; + altmo.reactiontime = 18; + altmo.bSolid = !!(special2 & 2); + altmo.bShootable = !!(special2 & 4); + altmo.bInvisible = !!(special2 & 0x40); + altmo.Vel.XY = (0, 0); + player.Vel = (0, 0); + altmo.Vel.Z = Vel.Z; + altmo.floorz = floorz; + altmo.bShadow = bShadow; + altmo.bNoGravity = bNoGravity; + altmo.bGhost = bGhost; + altmo.Score = Score; + altmo.InitAllPowerupEffects(); + + let exit_flash = player.MorphExitFlash; + bool correctweapon = !!(player.MorphStyle & MRF_LOSEACTUALWEAPON); + bool undobydeathsaves = !!(player.MorphStyle & MRF_UNDOBYDEATHSAVES); + + player.morphTics = 0; + player.MorphedPlayerClass = null; + player.MorphStyle = 0; + player.MorphExitFlash = null; + player.viewheight = altmo.ViewHeight; + Inventory level2 = altmo.FindInventory("PowerWeaponLevel2", true); + if (level2 != null) + { + level2.Destroy (); + } + + if ((player.health > 0) || undobydeathsaves) + { + player.health = altmo.health = altmo.SpawnHealth(); + } + else // killed when morphed so stay dead + { + altmo.health = player.health; + } + + player.mo = altmo; + if (player.camera == self) + { + player.camera = altmo; + } + + // [MH] + // If the player that was morphed is the one + // taking events, reset up the face, if any; + // this is only needed for old-skool skins + // and for the original DOOM status bar. + if (player == players[consoleplayer]) + { + if (face != 'None') + { + // Assume root-level base skin to begin with + let skinindex = 0; + let skin = player.GetSkin(); + // If a custom skin was in use, then reload it + // or else the base skin for the player class. + if (skin >= PlayerClasses.Size () && skin < PlayerSkins.Size()) + { + skinindex = skin; + } + else if (PlayerClasses.Size () > 1) + { + let whatami = altmo.GetClass(); + for (int i = 0; i < PlayerClasses.Size (); ++i) + { + if (PlayerClasses[i].Type == whatami) + { + skinindex = i; + break; + } + } + } + } + } + + Actor eflash = null; + if (exit_flash != null) + { + eflash = Spawn(exit_flash, Vec3Angle(20., altmo.Angle, gameinfo.telefogheight), ALLOW_REPLACE); + if (eflash) eflash.target = altmo; + } + WeaponSlots.SetupWeaponSlots(altmo); // Use original class's weapon slots. + let beastweap = player.ReadyWeapon; + if (player.PremorphWeapon != null) + { + player.PremorphWeapon.PostMorphWeapon (); + } + else + { + player.ReadyWeapon = player.PendingWeapon = null; + } + if (correctweapon) + { // Better "lose morphed weapon" semantics + class morphweaponcls = MorphWeapon; + if (morphweaponcls != null && morphweaponcls is 'Weapon') + { + let OriginalMorphWeapon = Weapon(altmo.FindInventory (morphweapon)); + if ((OriginalMorphWeapon != null) && (OriginalMorphWeapon.GivenAsMorphWeapon)) + { // You don't get to keep your morphed weapon. + if (OriginalMorphWeapon.SisterWeapon != null) + { + OriginalMorphWeapon.SisterWeapon.Destroy (); + } + OriginalMorphWeapon.Destroy (); + } + } + } + else // old behaviour (not really useful now) + { // Assumptions made here are no longer valid + if (beastweap != null) + { // You don't get to keep your morphed weapon. + if (beastweap.SisterWeapon != null) + { + beastweap.SisterWeapon.Destroy (); + } + beastweap.Destroy (); + } + } + Destroy (); + // Restore playerclass armor to its normal amount. + let hxarmor = HexenArmor(altmo.FindInventory('HexenArmor')); + if (hxarmor != null) + { + hxarmor.Slots[4] = altmo.HexenArmor[0]; + } + return true; + } + + } class MorphProjectile : Actor diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 4d47552b95..dc5d92512d 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1358,6 +1358,13 @@ class PSprite : Object native play } } } + + void ResetInterpolation() + { + oldx = x; + oldy = y; + } + } enum EPlayerState @@ -1480,7 +1487,16 @@ struct PlayerInfo native play // this is what internally is known as player_t return false; } - native bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false); + // This somehow got its arguments mixed up. 'self' should have been the player to be unmorphed, not the activator + bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false) + { + if (player.mo != null) + { + return player.mo.UndoPlayerMorph(self, unmorphflag, force); + } + return 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); From cce1bad04214bc54222c5a07ec2a94da82269fd0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 08:39:35 +0100 Subject: [PATCH 03/48] - testing and cleanup of scripted morph code. --- src/d_net.cpp | 4 +- src/d_player.h | 2 +- src/g_shared/a_morph.cpp | 372 +----------------- src/m_cheat.cpp | 45 +-- src/m_cheat.h | 2 +- wadsrc/static/zscript/inventory/powerups.txt | 4 +- wadsrc/static/zscript/raven/artitele.txt | 2 +- wadsrc/static/zscript/shared/morph.txt | 13 +- wadsrc/static/zscript/shared/player.txt | 8 +- wadsrc/static/zscript/shared/player_cheat.txt | 27 ++ 10 files changed, 80 insertions(+), 399 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 85a093cb83..37969fa1de 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2536,10 +2536,10 @@ void Net_DoCommand (int type, uint8_t **stream, int player) case DEM_MORPHEX: { s = ReadString (stream); - const char *msg = cht_Morph (players + player, PClass::FindActor (s), false); + FString msg = cht_Morph (players + player, PClass::FindActor (s), false); if (player == consoleplayer) { - Printf ("%s\n", *msg != '\0' ? msg : "Morph failed."); + Printf ("%s\n", msg[0] != '\0' ? msg.GetChars() : "Morph failed."); } } break; diff --git a/src/d_player.h b/src/d_player.h index 9341310e69..009af57fa1 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -445,7 +445,7 @@ public: int morphTics = 0; // player is a chicken/pig if > 0 PClassActor *MorphedPlayerClass = nullptr; // [MH] (for SBARINFO) class # for this player instance when morphed int MorphStyle = 0; // which effects to apply for this player instance when morphed - PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to P_MorphPlayer) + PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to MorphPlayer) TObjPtr PremorphWeapon = nullptr; // ready weapon before morphing int chickenPeck = 0; // chicken peck countdown int jumpTics = 0; // delay the next jump for a moment diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index a5216a226d..7cc62b0ebf 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -41,368 +41,32 @@ static FRandom pr_morphmonst ("MorphMonster"); bool P_MorphPlayer(player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) { + if (!p->mo) return false; + IFVIRTUALPTR(p->mo, APlayerPawn, MorphPlayer) + { + VMValue params[] = { p->mo, activator, spawntype, duration, style, enter_flash, exit_flash }; + int retval; + VMReturn ret(&retval); + VMCall(func, params, countof(params), &ret, 1); + return !!retval; + } return false; } bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) { + if (!player->mo) return false; + IFVIRTUALPTR(player->mo, APlayerPawn, MorphPlayer) + { + VMValue params[] = { player->mo, activator, unmorphflag, force }; + int retval; + VMReturn ret(&retval); + VMCall(func, params, countof(params), &ret, 1); + return !!retval; + } return false; } -#if 0 -void EndAllPowerupEffects(AInventory *item); -void InitAllPowerupEffects(AInventory *item); - -//--------------------------------------------------------------------------- -// -// FUNC P_MorphPlayer -// -// Returns true if the player gets turned into a chicken/pig. -// -// TODO: Allow morphed players to receive weapon sets (not just one weapon), -// since they have their own weapon slots now. -// -//--------------------------------------------------------------------------- - -bool P_MorphPlayer (player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) -{ - AInventory *item; - APlayerPawn *morphed; - APlayerPawn *actor; - - actor = p->mo; - if (actor == nullptr) - { - return false; - } - if (actor->flags3 & MF3_DONTMORPH) - { - return false; - } - if ((p->mo->flags2 & MF2_INVULNERABLE) && ((p != activator) || (!(style & MORPH_WHENINVULNERABLE)))) - { // Immune when invulnerable unless this is a power we activated - return false; - } - if (p->morphTics) - { // Player is already a beast - if ((p->mo->GetClass() == spawntype) - && (p->mo->PlayerFlags & PPF_CANSUPERMORPH) - && (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE)) - && (p->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true) == nullptr)) - { // Make a super chicken - p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2)); - } - return false; - } - if (p->health <= 0) - { // Dead players cannot morph - return false; - } - if (spawntype == nullptr) - { - return false; - } - if (!spawntype->IsDescendantOf (RUNTIME_CLASS(APlayerPawn))) - { - return false; - } - if (spawntype == p->mo->GetClass()) - { - return false; - } - - morphed = static_cast(Spawn (spawntype, actor->Pos(), NO_REPLACE)); - EndAllPowerupEffects(actor->Inventory); - DObject::StaticPointerSubstitution (actor, morphed); - if ((style & MORPH_TRANSFERTRANSLATION) && !(morphed->flags2 & MF2_DONTTRANSLATE)) - { - morphed->Translation = actor->Translation; - } - if ((actor->tid != 0) && (style & MORPH_NEWTIDBEHAVIOUR)) - { - morphed->tid = actor->tid; - morphed->AddToHash (); - actor->RemoveFromHash (); - actor->tid = 0; - } - morphed->Angles.Yaw = actor->Angles.Yaw; - morphed->target = actor->target; - morphed->tracer = actor->tracer; - morphed->alternative = actor; - morphed->FriendPlayer = actor->FriendPlayer; - morphed->DesignatedTeam = actor->DesignatedTeam; - morphed->Score = actor->Score; - p->PremorphWeapon = p->ReadyWeapon; - morphed->special2 = actor->flags & ~MF_JUSTHIT; - morphed->player = p; - if (actor->renderflags & RF_INVISIBLE) - { - morphed->special2 |= MF_JUSTHIT; - } - if (morphed->ViewHeight > p->viewheight && p->deltaviewheight == 0) - { // If the new view height is higher than the old one, start moving toward it. - p->deltaviewheight = p->GetDeltaViewHeight(); - } - morphed->flags |= actor->flags & (MF_SHADOW|MF_NOGRAVITY); - morphed->flags2 |= actor->flags2 & MF2_FLY; - morphed->flags3 |= actor->flags3 & MF3_GHOST; - AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - actor->player = nullptr; - actor->alternative = morphed; - actor->flags &= ~(MF_SOLID|MF_SHOOTABLE); - actor->flags |= MF_UNMORPHED; - actor->renderflags |= RF_INVISIBLE; - p->morphTics = (duration) ? duration : MORPHTICS; - - // [MH] Used by SBARINFO to speed up face drawing - p->MorphedPlayerClass = spawntype; - - p->MorphStyle = style; - p->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog"); - p->health = morphed->health; - p->mo = morphed; - p->Vel.X = p->Vel.Y = 0; - morphed->ObtainInventory (actor); - // Remove all armor - for (item = morphed->Inventory; item != nullptr; ) - { - AInventory *next = item->Inventory; - if (item->IsKindOf (PClass::FindActor(NAME_Armor))) - { - item->DepleteOrDestroy(); - } - item = next; - } - InitAllPowerupEffects(morphed->Inventory); - morphed->ActivateMorphWeapon (); - if (p->camera == actor) - { - p->camera = morphed; - } - morphed->ScoreIcon = actor->ScoreIcon; // [GRB] - if (eflash) - eflash->target = p->mo; - return true; -} - -//---------------------------------------------------------------------------- -// -// FUNC P_UndoPlayerMorph -// -//---------------------------------------------------------------------------- - -bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, bool force) -{ - AWeapon *beastweap; - APlayerPawn *mo; - APlayerPawn *pmo; - - pmo = player->mo; - // [MH] - // Checks pmo as well; the PowerMorph destroyer will - // try to unmorph the player; if the destroyer runs - // because the level or game is ended while morphed, - // by the time it gets executed the morphed player - // pawn instance may have already been destroyed. - if (pmo == nullptr || pmo->alternative == nullptr) - { - return false; - } - - bool DeliberateUnmorphIsOkay = !!(MORPH_STANDARDUNDOING & unmorphflag); - - if ((pmo->flags2 & MF2_INVULNERABLE) // If the player is invulnerable - && ((player != activator) // and either did not decide to unmorph, - || (!((player->MorphStyle & MORPH_WHENINVULNERABLE) // or the morph style does not allow it - || (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it), - { // Then the player is immune to the unmorph. - return false; - } - - mo = barrier_cast(pmo->alternative); - mo->SetOrigin (pmo->Pos(), false); - mo->flags |= MF_SOLID; - pmo->flags &= ~MF_SOLID; - if (!force && !P_TestMobjLocation (mo)) - { // Didn't fit - mo->flags &= ~MF_SOLID; - pmo->flags |= MF_SOLID; - player->morphTics = 2*TICRATE; - return false; - } - // No longer using tracer as morph storage. That is what 'alternative' is for. - // If the tracer has changed on the morph, change the original too. - mo->target = pmo->target; - mo->tracer = pmo->tracer; - pmo->player = nullptr; - mo->alternative = pmo->alternative = nullptr; - - // Remove the morph power if the morph is being undone prematurely. - auto pmtype = PClass::FindActor("PowerMorph"); - for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next) - { - next = item->Inventory; - if (item->IsKindOf(pmtype)) - { - item->Destroy(); - } - } - EndAllPowerupEffects(pmo->Inventory); - mo->ObtainInventory (pmo); - DObject::StaticPointerSubstitution (pmo, mo); - if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR)) - { - mo->tid = pmo->tid; - mo->AddToHash (); - } - mo->Angles.Yaw = pmo->Angles.Yaw; - mo->player = player; - mo->reactiontime = 18; - mo->flags = ActorFlags::FromInt (pmo->special2) & ~MF_JUSTHIT; - mo->Vel.X = mo->Vel.Y = 0; - player->Vel.Zero(); - mo->Vel.Z = pmo->Vel.Z; - mo->floorz = pmo->floorz; - if (!(pmo->special2 & MF_JUSTHIT)) - { - mo->renderflags &= ~RF_INVISIBLE; - } - mo->flags = (mo->flags & ~(MF_SHADOW|MF_NOGRAVITY)) | (pmo->flags & (MF_SHADOW|MF_NOGRAVITY)); - mo->flags2 = (mo->flags2 & ~MF2_FLY) | (pmo->flags2 & MF2_FLY); - mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST); - mo->Score = pmo->Score; - InitAllPowerupEffects(mo->Inventory); - - PClassActor *exit_flash = player->MorphExitFlash; - bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON); - bool undobydeathsaves = !!(player->MorphStyle & MORPH_UNDOBYDEATHSAVES); - - player->morphTics = 0; - player->MorphedPlayerClass = 0; - player->MorphStyle = 0; - player->MorphExitFlash = nullptr; - player->viewheight = mo->ViewHeight; - AInventory *level2 = mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true); - if (level2 != nullptr) - { - level2->Destroy (); - } - - if ((player->health > 0) || undobydeathsaves) - { - player->health = mo->health = mo->SpawnHealth(); - } - else // killed when morphed so stay dead - { - mo->health = player->health; - } - - player->mo = mo; - if (player->camera == pmo) - { - player->camera = mo; - } - - // [MH] - // If the player that was morphed is the one - // taking events, reset up the face, if any; - // this is only needed for old-skool skins - // and for the original DOOM status bar. - if (player == &players[consoleplayer]) - { - FName face = pmo->Face; - if (face != NAME_None) - { - // Assume root-level base skin to begin with - size_t skinindex = 0; - // If a custom skin was in use, then reload it - // or else the base skin for the player class. - if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () && - (unsigned)player->userinfo.GetSkin() < Skins.Size()) - { - - skinindex = player->userinfo.GetSkin(); - } - else if (PlayerClasses.Size () > 1) - { - const PClass *whatami = player->mo->GetClass(); - for (unsigned int i = 0; i < PlayerClasses.Size (); ++i) - { - if (PlayerClasses[i].Type == whatami) - { - skinindex = i; - break; - } - } - } - } - } - - AActor *eflash = nullptr; - if (exit_flash != nullptr) - { - eflash = Spawn(exit_flash, pmo->Vec3Angle(20., mo->Angles.Yaw, TELEFOGHEIGHT), ALLOW_REPLACE); - if (eflash) eflash->target = mo; - } - mo->SetupWeaponSlots(); // Use original class's weapon slots. - beastweap = player->ReadyWeapon; - if (player->PremorphWeapon != nullptr) - { - player->PremorphWeapon-> P ostMorphWeapon (); - } - else - { - player->ReadyWeapon = player->PendingWeapon = nullptr; - } - if (correctweapon) - { // Better "lose morphed weapon" semantics - PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon); - if (morphweapon != nullptr && morphweapon->IsDescendantOf(NAME_Weapon)) - { - AWeapon *OriginalMorphWeapon = static_cast(mo->FindInventory (morphweapon)); - if ((OriginalMorphWeapon != nullptr) && (OriginalMorphWeapon->GivenAsMorphWeapon)) - { // You don't get to keep your morphed weapon. - if (OriginalMorphWeapon->SisterWeapon != nullptr) - { - OriginalMorphWeapon->SisterWeapon->Destroy (); - } - OriginalMorphWeapon->Destroy (); - } - } - } - else // old behaviour (not really useful now) - { // Assumptions made here are no longer valid - if (beastweap != nullptr) - { // You don't get to keep your morphed weapon. - if (beastweap->SisterWeapon != nullptr) - { - beastweap->SisterWeapon->Destroy (); - } - beastweap->Destroy (); - } - } - pmo->Destroy (); - // Restore playerclass armor to its normal amount. - auto hxarmor = mo->FindInventory(NAME_HexenArmor); - if (hxarmor != nullptr) - { - double *Slots = (double*)hxarmor->ScriptVar(NAME_Slots, nullptr); - Slots[4] = mo->HexenArmor[0]; - } - return true; -} - -DEFINE_ACTION_FUNCTION(_PlayerInfo, UndoPlayerMorph) -{ - PARAM_SELF_STRUCT_PROLOGUE(player_t); - PARAM_POINTER_NOT_NULL(player, player_t); - PARAM_INT(unmorphflag); - PARAM_BOOL(force); - ACTION_RETURN_BOOL(P_UndoPlayerMorph(player, self, unmorphflag, force)); -} - -#endif - //--------------------------------------------------------------------------- // // FUNC P_MorphMonster diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 768802693e..b64d047594 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -90,10 +90,17 @@ void cht_DoCheat (player_t *player, int cheat) }; PClassActor *type; AInventory *item; + FString smsg; const char *msg = ""; char msgbuild[32]; int i; + // No cheating when not having a pawn attached. + if (player->mo == nullptr) + { + return; + } + switch (cheat) { case CHT_IDDQD: @@ -188,7 +195,8 @@ void cht_DoCheat (player_t *player, int cheat) break; case CHT_MORPH: - msg = cht_Morph (player, PClass::FindActor (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true); + smsg = cht_Morph (player, PClass::FindActor (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true); + msg = smsg.GetChars(); break; case CHT_NOTARGET: @@ -546,32 +554,17 @@ void cht_DoCheat (player_t *player, int cheat) Printf ("%s cheats: %s\n", player->userinfo.GetName(), msg); } -const char *cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo) +FString cht_Morph(player_t *player, PClassActor *morphclass, bool quickundo) { - if (player->mo == NULL) - { - return ""; - } - auto oldclass = player->mo->GetClass(); + if (player->mo == nullptr) return ""; - // Set the standard morph style for the current game - int style = MORPH_UNDOBYTOMEOFPOWER; - if (gameinfo.gametype == GAME_Hexen) style |= MORPH_UNDOBYCHAOSDEVICE; - - if (player->morphTics) + IFVIRTUALPTR(player->mo, APlayerPawn, CheatMorph) { - if (P_UndoPlayerMorph (player, player)) - { - if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style)) - { - return GStrings("TXT_STRANGER"); - } - return GStrings("TXT_NOTSTRANGE"); - } - } - else if (P_MorphPlayer (player, player, morphclass, 0, style)) - { - return GStrings("TXT_STRANGE"); + FString message; + VMReturn msgret(&message); + VMValue params[3] = { player->mo, morphclass, quickundo }; + VMCall(func, params, 3, nullptr, 0); + return message; } return ""; } @@ -602,8 +595,6 @@ void cht_SetInv(player_t *player, const char *string, int amount, bool beyond) void cht_Give (player_t *player, const char *name, int amount) { - if (player->mo == nullptr) return; - IFVIRTUALPTR(player->mo, APlayerPawn, CheatGive) { FString namestr = name; @@ -614,8 +605,6 @@ void cht_Give (player_t *player, const char *name, int amount) void cht_Take (player_t *player, const char *name, int amount) { - if (player->mo == nullptr) return; - IFVIRTUALPTR(player->mo, APlayerPawn, CheatTake) { FString namestr = name; diff --git a/src/m_cheat.h b/src/m_cheat.h index c6ebcb1a16..6701bddc47 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -15,6 +15,6 @@ void cht_Give (player_t *player, const char *item, int amount=1); void cht_Take (player_t *player, const char *item, int amount=1); void cht_SetInv(player_t *player, const char *item, int amount = 1, bool beyondMax = false); void cht_Suicide (player_t *player); -const char *cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo); +FString cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo); #endif diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 5ef03d4209..bcfdca31a7 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -1893,7 +1893,7 @@ class PowerMorph : Powerup if (Owner != null && Owner.player != null && PlayerClass != null) { let realplayer = Owner.player; // Remember the identity of the player - if (realplayer.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash)) + if (realplayer.mo.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash)) { Owner = realplayer.mo; // Replace the new owner in our owner; safe because we are not attached to anything yet bCreateCopyMoved = true; // Let the caller know the "real" owner has changed (to the morphed actor) @@ -1930,7 +1930,7 @@ class PowerMorph : Powerup } int savedMorphTics = MorphedPlayer.morphTics; - MorphedPlayer.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS)); + MorphedPlayer.mo.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS)); MorphedPlayer = null; } diff --git a/wadsrc/static/zscript/raven/artitele.txt b/wadsrc/static/zscript/raven/artitele.txt index 77a233dbe5..0e65fe051f 100644 --- a/wadsrc/static/zscript/raven/artitele.txt +++ b/wadsrc/static/zscript/raven/artitele.txt @@ -42,7 +42,7 @@ class ArtiTeleport : Inventory 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)) + if (!p.mo.UndoPlayerMorph (p, MRF_UNDOBYCHAOSDEVICE) && (p.MorphStyle & MRF_FAILNOLAUGH)) { canlaugh = false; } diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index 137fd9c0eb..e643623fa2 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -72,7 +72,7 @@ extend class PlayerPawn player.ReadyWeapon = Weapon(GiveInventoryType (morphweaponcls)); if (player.ReadyWeapon != null) { - player.ReadyWeapon.GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph + player.ReadyWeapon.GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in UndoPlayerMorph } } if (player.ReadyWeapon != null) @@ -91,7 +91,7 @@ extend class PlayerPawn //--------------------------------------------------------------------------- // - // FUNC P_MorphPlayer + // MorphPlayer // // Returns true if the player gets turned into a chicken/pig. // @@ -216,7 +216,7 @@ extend class PlayerPawn //---------------------------------------------------------------------------- // - // FUNC P_UndoPlayerMorph + // FUNC UndoPlayerMorph // //---------------------------------------------------------------------------- @@ -279,13 +279,13 @@ extend class PlayerPawn altmo.bSolid = !!(special2 & 2); altmo.bShootable = !!(special2 & 4); altmo.bInvisible = !!(special2 & 0x40); - altmo.Vel.XY = (0, 0); + altmo.Vel = (0, 0, Vel.Z); player.Vel = (0, 0); - altmo.Vel.Z = Vel.Z; altmo.floorz = floorz; altmo.bShadow = bShadow; altmo.bNoGravity = bNoGravity; altmo.bGhost = bGhost; + altmo.bUnmorphed = false; altmo.Score = Score; altmo.InitAllPowerupEffects(); @@ -427,7 +427,8 @@ class MorphProjectile : Actor { if (target.player) { - target.player.MorphPlayer (NULL, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); + // Voodoo dolls forward this to the real player + target.player.mo.MorphPlayer (NULL, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); } else { diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index dc5d92512d..2d6ef3cac1 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1111,7 +1111,7 @@ class PlayerPawn : Actor native } if (!--player.morphTics) { // Attempt to undo the chicken/pig - player.UndoPlayerMorph(player, MRF_UNDOBYTIMEOUT); + player.mo.UndoPlayerMorph(player, MRF_UNDOBYTIMEOUT); } } } @@ -1477,18 +1477,18 @@ struct PlayerInfo native play // this is what internally is known as player_t native readonly @UserCmd original_cmd; - bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) + // The actual implementation is on PlayerPawn where it can be overridden. Use that directly in the future. + deprecated("3.7") bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) { if (mo != null) { - // The actual implementation is on PlayerPawn where it can be overridden. return mo.MorphPlayer(p, spawntype, duration, style, enter_flash, exit_flash); } return false; } // This somehow got its arguments mixed up. 'self' should have been the player to be unmorphed, not the activator - bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false) + deprecated("3.7") bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false) { if (player.mo != null) { diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt index 949c89a3c4..d450ee447b 100644 --- a/wadsrc/static/zscript/shared/player_cheat.txt +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -399,4 +399,31 @@ extend class PlayerPawn } return; } + + virtual String CheatMorph(class morphClass, bool quickundo) + { + let oldclass = GetClass(); + + // Set the standard morph style for the current game + int style = MRF_UNDOBYTOMEOFPOWER; + if (gameinfo.gametype == GAME_Hexen) style |= MRF_UNDOBYCHAOSDEVICE; + + if (player.morphTics) + { + if (UndoPlayerMorph (player)) + { + if (!quickundo && oldclass != morphclass && MorphPlayer (player, morphclass, 0, style)) + { + return StringTable.Localize("TXT_STRANGER"); + } + return StringTable.Localize("TXT_NOTSTRANGE"); + } + } + else if (MorphPlayer (player, morphclass, 0, style)) + { + return StringTable.Localize("TXT_STRANGE"); + } + return ""; + } + } From 3ed7f4066dc89951367b0601a0fa441e195423be Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 08:41:07 +0100 Subject: [PATCH 04/48] - fixed message output. --- src/m_cheat.cpp | 2 +- wadsrc/static/zscript/shared/player_cheat.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index b64d047594..bcaccc6fba 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -563,7 +563,7 @@ FString cht_Morph(player_t *player, PClassActor *morphclass, bool quickundo) FString message; VMReturn msgret(&message); VMValue params[3] = { player->mo, morphclass, quickundo }; - VMCall(func, params, 3, nullptr, 0); + VMCall(func, params, 3, &msgret, 1); return message; } return ""; diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt index d450ee447b..1a222386e8 100644 --- a/wadsrc/static/zscript/shared/player_cheat.txt +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -414,14 +414,14 @@ extend class PlayerPawn { if (!quickundo && oldclass != morphclass && MorphPlayer (player, morphclass, 0, style)) { - return StringTable.Localize("TXT_STRANGER"); + return StringTable.Localize("$TXT_STRANGER"); } - return StringTable.Localize("TXT_NOTSTRANGE"); + return StringTable.Localize("$TXT_NOTSTRANGE"); } } else if (MorphPlayer (player, morphclass, 0, style)) { - return StringTable.Localize("TXT_STRANGE"); + return StringTable.Localize("$TXT_STRANGE"); } return ""; } From ac1bffc51be822cb166207fd715bc5751d15a5c3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 09:33:03 +0100 Subject: [PATCH 05/48] - scriptified P_MorphMonster. --- src/g_shared/a_morph.cpp | 175 +----------------- src/g_shared/a_morph.h | 7 +- src/p_acs.cpp | 19 +- src/p_user.cpp | 6 +- wadsrc/static/zscript/actor.txt | 8 +- .../zscript/heretic/hereticartifacts.txt | 2 +- wadsrc/static/zscript/shared/morph.txt | 96 ++++++++++ wadsrc/static/zscript/shared/player.txt | 3 - 8 files changed, 115 insertions(+), 201 deletions(-) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 7cc62b0ebf..631510106b 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -39,12 +39,11 @@ static FRandom pr_morphmonst ("MorphMonster"); -bool P_MorphPlayer(player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) +bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassActor *mtype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) { - if (!p->mo) return false; - IFVIRTUALPTR(p->mo, APlayerPawn, MorphPlayer) + IFVIRTUALPTR(victim, AActor, Morph) { - VMValue params[] = { p->mo, activator, spawntype, duration, style, enter_flash, exit_flash }; + VMValue params[] = { victim, activator, ptype, mtype, duration, style, enter_flash, exit_flash }; int retval; VMReturn ret(&retval); VMCall(func, params, countof(params), &ret, 1); @@ -56,7 +55,7 @@ bool P_MorphPlayer(player_t *activator, player_t *p, PClassActor *spawntype, int bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) { if (!player->mo) return false; - IFVIRTUALPTR(player->mo, APlayerPawn, MorphPlayer) + IFVIRTUALPTR(player->mo, APlayerPawn, UndoPlayerMorph) { VMValue params[] = { player->mo, activator, unmorphflag, force }; int retval; @@ -67,76 +66,6 @@ bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, b return false; } -//--------------------------------------------------------------------------- -// -// FUNC P_MorphMonster -// -// Returns true if the monster gets turned into a chicken/pig. -// -//--------------------------------------------------------------------------- - -bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) -{ - AMorphedMonster *morphed; - - if (actor == NULL || actor->player || spawntype == NULL || - actor->flags3 & MF3_DONTMORPH || - !(actor->flags3 & MF3_ISMONSTER) || - !spawntype->IsDescendantOf (PClass::FindActor(NAME_MorphedMonster))) - { - return false; - } - - morphed = static_cast(Spawn (spawntype, actor->Pos(), NO_REPLACE)); - DObject::StaticPointerSubstitution (actor, morphed); - if ((style & MORPH_TRANSFERTRANSLATION) && !(morphed->flags2 & MF2_DONTTRANSLATE)) - { - morphed->Translation = actor->Translation; - } - morphed->tid = actor->tid; - morphed->Angles.Yaw = actor->Angles.Yaw; - morphed->UnmorphedMe = actor; - morphed->Alpha = actor->Alpha; - morphed->RenderStyle = actor->RenderStyle; - morphed->Score = actor->Score; - - morphed->UnmorphTime = level.time + ((duration) ? duration : MORPHTICS) + pr_morphmonst(); - morphed->MorphStyle = style; - morphed->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog"); - morphed->FlagsSave = actor->flags & ~MF_JUSTHIT; - morphed->special = actor->special; - memcpy (morphed->args, actor->args, sizeof(actor->args)); - morphed->CopyFriendliness (actor, true); - morphed->flags |= actor->flags & MF_SHADOW; - morphed->flags3 |= actor->flags3 & MF3_GHOST; - if (actor->renderflags & RF_INVISIBLE) - { - morphed->FlagsSave |= MF_JUSTHIT; - } - morphed->AddToHash (); - actor->RemoveFromHash (); - actor->special = 0; - actor->tid = 0; - actor->flags &= ~(MF_SOLID|MF_SHOOTABLE); - actor->flags |= MF_UNMORPHED; - actor->renderflags |= RF_INVISIBLE; - AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - if (eflash) - eflash->target = morphed; - return true; -} - -DEFINE_ACTION_FUNCTION(AActor, MorphMonster) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_CLASS(spawntype, AActor); - PARAM_INT(duration); - PARAM_INT(style); - PARAM_CLASS(enter_flash, AActor); - PARAM_CLASS(exit_flash, AActor); - ACTION_RETURN_BOOL(P_MorphMonster(self, spawntype, duration, style, enter_flash, exit_flash)); -} - //---------------------------------------------------------------------------- // // FUNC P_UndoMonsterMorph @@ -197,23 +126,6 @@ bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force) return true; } -//---------------------------------------------------------------------------- -// -// FUNC P_UpdateMorphedMonster -// -// Returns true if the monster unmorphs. -// -//---------------------------------------------------------------------------- - -bool P_UpdateMorphedMonster (AMorphedMonster *beast) -{ - if (beast->UnmorphTime > level.time) - { - return false; - } - return P_UndoMonsterMorph (beast); -} - //---------------------------------------------------------------------------- // // FUNC P_MorphedDeath @@ -280,55 +192,6 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor return false; } -//=========================================================================== -// -// EndAllPowerupEffects -// -// Calls EndEffect() on every Powerup in the inventory list. -// -//=========================================================================== - -void EndAllPowerupEffects(AInventory *item) -{ - auto ptype = PClass::FindActor(NAME_Powerup); - while (item != NULL) - { - if (item->IsKindOf(ptype)) - { - IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) - { - VMValue params[1] = { item }; - VMCall(func, params, 1, nullptr, 0); - } - } - item = item->Inventory; - } -} - -//=========================================================================== -// -// InitAllPowerupEffects -// -// Calls InitEffect() on every Powerup in the inventory list. -// -//=========================================================================== - -void InitAllPowerupEffects(AInventory *item) -{ - auto ptype = PClass::FindActor(NAME_Powerup); - while (item != NULL) - { - if (item->IsKindOf(ptype)) - { - IFVIRTUALPTRNAME(item, NAME_Powerup, InitEffect) - { - VMValue params[1] = { item }; - VMCall(func, params, 1, nullptr, 0); - } - } - item = item->Inventory; - } -} // Morphed Monster (you must subclass this to do something useful) --------- @@ -380,35 +243,9 @@ void AMorphedMonster::Die (AActor *source, AActor *inflictor, int dmgflags, FNam void AMorphedMonster::Tick () { - if (!P_UpdateMorphedMonster (this)) + if (UnmorphTime > level.time || !P_UndoMonsterMorph(this)) { - Super::Tick (); + Super::Tick(); } } - -DEFINE_ACTION_FUNCTION(AActor, A_Morph) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_CLASS(type, AActor); - PARAM_INT(duration); - PARAM_INT(flags); - PARAM_CLASS(enter_flash, AActor); - PARAM_CLASS(exit_flash, AActor); - bool res = false; - if (self->player) - { - if (type->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) - { - res = P_MorphPlayer(self->player, self->player, type, duration, flags, enter_flash, exit_flash); - } - } - else - { - if (type->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - res = P_MorphMonster(self, type, duration, flags, enter_flash, exit_flash); - } - } - ACTION_RETURN_BOOL(res); -} diff --git a/src/g_shared/a_morph.h b/src/g_shared/a_morph.h index 1c0047c69d..4a71773014 100644 --- a/src/g_shared/a_morph.h +++ b/src/g_shared/a_morph.h @@ -35,13 +35,10 @@ class AActor; class player_t; class AMorphedMonster; -bool P_MorphPlayer (player_t *activator, player_t *player, PClassActor *morphclass, int duration = 0, int style = 0, - PClassActor *enter_flash = NULL, PClassActor *exit_flash = NULL); +bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassActor *mtype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash); + bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag = 0, bool force = false); -bool P_MorphMonster (AActor *actor, PClassActor *morphclass, int duration = 0, int style = 0, - PClassActor *enter_flash = NULL, PClassActor *exit_flash = NULL); bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force = false); -bool P_UpdateMorphedMonster (AActor *actor); bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth); #endif //__A_MORPH__ diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 808b724e19..04db70d324 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -10305,14 +10305,7 @@ scriptwait: if (tag == 0) { - if (activator != NULL && activator->player) - { - changes += P_MorphPlayer(activator->player, activator->player, playerclass, duration, style, morphflash, unmorphflash); - } - else - { - changes += P_MorphMonster(activator, monsterclass, duration, style, morphflash, unmorphflash); - } + changes = P_MorphActor(activator, activator, playerclass, monsterclass, duration, style, morphflash, unmorphflash); } else { @@ -10321,15 +10314,7 @@ scriptwait: while ( (actor = iterator.Next ()) ) { - if (actor->player) - { - changes += P_MorphPlayer(activator == NULL ? NULL : activator->player, - actor->player, playerclass, duration, style, morphflash, unmorphflash); - } - else - { - changes += P_MorphMonster(actor, monsterclass, duration, style, morphflash, unmorphflash); - } + changes += P_MorphActor(activator, actor, playerclass, monsterclass, duration, style, morphflash, unmorphflash); } } diff --git a/src/p_user.cpp b/src/p_user.cpp index ea583dde3d..579b33f0c6 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -293,10 +293,10 @@ CCMD (playerclasses) } } -DEFINE_ACTION_FUNCTION(APlayerPawn, Substitute) +DEFINE_ACTION_FUNCTION(AActor, Substitute) { - PARAM_SELF_PROLOGUE(APlayerPawn); - PARAM_OBJECT(replace, APlayerPawn); + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(replace, AActor); DObject::StaticPointerSubstitution(self, replace); return 0; } diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index c5426936e8..4b710f2ab6 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -75,6 +75,7 @@ class Actor : Thinker native const LARGE_MASS = 10000000; // not INT_MAX on purpose const ORIG_FRICTION = (0xE800/65536.); // original value const ORIG_FRICTION_FACTOR = (2048/65536.); // original value + const DEFMORPHTICS = 40 * TICRATE; // flags are not defined here, the native fields for those get synthesized from the internal tables. @@ -329,6 +330,7 @@ class Actor : Thinker native // need some definition work first //FRenderStyle RenderStyle; + native private int RenderStyle; // This is kept private until its real type has been implemented into the VM. But some code needs to copy this. //int ConversationRoot; // THe root of the current dialogue // deprecated things. @@ -438,6 +440,7 @@ class Actor : Thinker native virtual native bool Slam(Actor victim); virtual native void Touch(Actor toucher); virtual native void MarkPrecacheSounds(); + native void Substitute(Actor replacement); // Called by PIT_CheckThing to check if two actors actually can collide. virtual bool CanCollideWith(Actor other, bool passive) @@ -735,7 +738,7 @@ class Actor : Thinker native native bool GiveAmmo (Class type, int amount); native bool UsePuzzleItem(int PuzzleItemType); native float AccuracyFactor(); - native bool MorphMonster (Class spawntype, int duration, int style, Class enter_flash, Class exit_flash); + action native void SetCamera(Actor cam, bool revert = false); native bool Warp(Actor dest, double xofs = 0, double yofs = 0, double zofs = 0, double angle = 0, int flags = 0, double heightoffset = 0, double radiusoffset = 0, double pitch = 0); @@ -1097,8 +1100,6 @@ class Actor : Thinker native native void Revive(); action native bool, Actor 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); - native bool A_Morph(class type, int duration = 0, int flags = 0, class enter_flash = null, class exit_flash = null); - action native state, bool A_Teleport(statelabel teleportstate = null, class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, double mindist = 128, double maxdist = 0, 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); @@ -1218,6 +1219,7 @@ class Actor : Thinker native return ACS_ExecuteWithResult(-int(script), arg1, arg2, arg3, arg4); } + States(Actor, Overlay, Weapon, Item) { Spawn: diff --git a/wadsrc/static/zscript/heretic/hereticartifacts.txt b/wadsrc/static/zscript/heretic/hereticartifacts.txt index 3b7f14ad82..688ca6ebf9 100644 --- a/wadsrc/static/zscript/heretic/hereticartifacts.txt +++ b/wadsrc/static/zscript/heretic/hereticartifacts.txt @@ -71,7 +71,7 @@ Class ArtiTomeOfPower : PowerupGiver Playerinfo p = Owner.player; if (p && p.morphTics && (p.MorphStyle & MRF_UNDOBYTOMEOFPOWER)) { // Attempt to undo chicken - if (!p.UndoPlayerMorph (p, MRF_UNDOBYTOMEOFPOWER)) + if (!p.mo.UndoPlayerMorph (p, MRF_UNDOBYTOMEOFPOWER)) { // Failed if (!(p.MorphStyle & MRF_FAILNOTELEFRAG)) { diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index e643623fa2..ebb4a3437e 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -1,3 +1,99 @@ +extend class Actor +{ + //=========================================================================== + // + // Main entry point + // + //=========================================================================== + + bool Morph(Actor activator, class playerclass, class monsterclass, int duration = 0, int style = 0, class morphflash = null, classunmorphflash = null) + { + if (player != null && player.mo != null && playerclass != null) + { + return player.mo.MorphPlayer(activator.player, playerclass, duration, style, morphflash, unmorphflash); + } + else + { + return MorphMonster(monsterclass, duration, style, morphflash, unmorphflash); + } + } + + //=========================================================================== + // + // Action function variant whose arguments differ from the generic one. + // + //=========================================================================== + + bool A_Morph(class type, int duration = 0, int style = 0, class morphflash = null, classunmorphflash = null) + { + if (self.player != null) + { + let playerclass = (class)(type); + if (playerclass && self.player.mo != null) return player.mo.MorphPlayer(self.player, playerclass, duration, style, morphflash, unmorphflash); + } + else + { + return MorphMonster(type, duration, style, morphflash, unmorphflash); + } + return false; + } + + + //--------------------------------------------------------------------------- + // + // FUNC P_MorphMonster + // + // Returns true if the monster gets turned into a chicken/pig. + // + //--------------------------------------------------------------------------- + + virtual bool MorphMonster (Class spawntype, int duration, int style, Class enter_flash, Class exit_flash) + { + if (player || spawntype == NULL || bDontMorph || !bIsMonster || !(spawntype is 'MorphedMonster')) + { + return false; + } + + let morphed = MorphedMonster(Spawn (spawntype, Pos, NO_REPLACE)); + Substitute (morphed); + if ((style & MRF_TRANSFERTRANSLATION) && !morphed.bDontTranslate) + { + morphed.Translation = Translation; + } + morphed.ChangeTid(tid); + ChangeTid(0); + morphed.Angle = Angle; + morphed.UnmorphedMe = self; + morphed.Alpha = Alpha; + morphed.RenderStyle = RenderStyle; + morphed.Score = Score; + + morphed.UnmorphTime = level.time + ((duration) ? duration : DEFMORPHTICS) + random[morphmonst](); + morphed.MorphStyle = style; + morphed.MorphExitFlash = (exit_flash) ? exit_flash : (class)("TeleportFog"); + //morphed.FlagsSave = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility + + morphed.special = special; + morphed.args[0] = args[0]; + morphed.args[1] = args[1]; + morphed.args[2] = args[2]; + morphed.args[3] = args[3]; + morphed.args[4] = args[4]; + morphed.CopyFriendliness (self, true); + morphed.bShadow |= bShadow; + morphed.bGhost |= bGhost; + special = 0; + bSolid = false; + bShootable = false; + bUnmorphed = true; + bInvisible = true; + let eflash = Spawn(enter_flash ? enter_flash : (class)("TeleportFog"), Pos + (0, 0, gameinfo.TELEFOGHEIGHT), ALLOW_REPLACE); + if (eflash) + eflash.target = morphed; + return true; + } +} + extend class PlayerPawn { //=========================================================================== diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 2d6ef3cac1..25f025d29d 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -15,7 +15,6 @@ class PlayerPawn : Actor native const CROUCHSPEED = (1./12); // [RH] # of ticks to complete a turn180 const TURN180_TICKS = ((TICRATE / 4) + 1); - const DEFMORPHTICS = 40 * TICRATE; native int crouchsprite; native int MaxHealth; @@ -1280,8 +1279,6 @@ class PlayerPawn : Actor native native void CheckWeaponButtons(); native Weapon BestWeapon(class ammotype); native Weapon PickNewWeapon(class ammotype); - native void Substitute(PlayerPawn replacement); - } class PlayerChunk : PlayerPawn From bd84a60663b1890a1596f7c8831df7b865b40e12 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 10:47:42 +0100 Subject: [PATCH 06/48] - scriptified the rest of the morph code. --- src/g_game.cpp | 2 +- src/g_shared/a_morph.cpp | 194 +--------------------- src/g_shared/a_morph.h | 6 +- src/g_shared/a_sharedglobal.h | 17 -- src/p_acs.cpp | 38 +---- src/p_interaction.cpp | 42 ++--- src/p_user.cpp | 2 +- wadsrc/static/zscript/shared/morph.txt | 213 ++++++++++++++++++++++++- 8 files changed, 237 insertions(+), 277 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 9be9b54559..27aff3abfa 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1249,7 +1249,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) if (p->morphTics != 0) { // Undo morph - P_UndoPlayerMorph (p, p, 0, true); + P_UnmorphActor(p->mo, p->mo, 0, true); } // Strip all current powers, unless moving in a hub and the power is okay to keep. diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 631510106b..d917df3d4d 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -1,9 +1,6 @@ //----------------------------------------------------------------------------- // -// Copyright 1994-1996 Raven Software -// Copyright 1999-2016 Randy Heit -// Copyright 2002-2016 Christoph Oelckers -// Copyright 2005-2008 Martin Howe +// Copyright 2018 Christoph Oelckers // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -52,12 +49,11 @@ bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassA return false; } -bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) +bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags, bool force) { - if (!player->mo) return false; - IFVIRTUALPTR(player->mo, APlayerPawn, UndoPlayerMorph) + IFVIRTUALPTR(morphed, AActor, UnMorph) { - VMValue params[] = { player->mo, activator, unmorphflag, force }; + VMValue params[] = { morphed, activator, flags, force }; int retval; VMReturn ret(&retval); VMCall(func, params, countof(params), &ret, 1); @@ -66,186 +62,4 @@ bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, b return false; } -//---------------------------------------------------------------------------- -// -// FUNC P_UndoMonsterMorph -// -// Returns true if the monster unmorphs. -// -//---------------------------------------------------------------------------- - -bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force) -{ - AActor *actor; - - if (beast->UnmorphTime == 0 || - beast->UnmorphedMe == NULL || - beast->flags3 & MF3_STAYMORPHED || - beast->UnmorphedMe->flags3 & MF3_STAYMORPHED) - { - return false; - } - actor = beast->UnmorphedMe; - actor->SetOrigin (beast->Pos(), false); - actor->flags |= MF_SOLID; - beast->flags &= ~MF_SOLID; - ActorFlags6 beastflags6 = beast->flags6; - beast->flags6 &= ~MF6_TOUCHY; - if (!force && !P_TestMobjLocation (actor)) - { // Didn't fit - actor->flags &= ~MF_SOLID; - beast->flags |= MF_SOLID; - beast->flags6 = beastflags6; - beast->UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds - return false; - } - actor->Angles.Yaw = beast->Angles.Yaw; - actor->target = beast->target; - actor->FriendPlayer = beast->FriendPlayer; - actor->flags = beast->FlagsSave & ~MF_JUSTHIT; - actor->flags = (actor->flags & ~(MF_FRIENDLY|MF_SHADOW)) | (beast->flags & (MF_FRIENDLY|MF_SHADOW)); - actor->flags3 = (actor->flags3 & ~(MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST)) - | (beast->flags3 & (MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST)); - actor->flags4 = (actor->flags4 & ~MF4_NOHATEPLAYERS) | (beast->flags4 & MF4_NOHATEPLAYERS); - if (!(beast->FlagsSave & MF_JUSTHIT)) - actor->renderflags &= ~RF_INVISIBLE; - actor->health = actor->SpawnHealth(); - actor->Vel = beast->Vel; - actor->tid = beast->tid; - actor->special = beast->special; - actor->Score = beast->Score; - memcpy (actor->args, beast->args, sizeof(actor->args)); - actor->AddToHash (); - beast->UnmorphedMe = NULL; - DObject::StaticPointerSubstitution (beast, actor); - PClassActor *exit_flash = beast->MorphExitFlash; - beast->Destroy (); - AActor *eflash = Spawn(exit_flash, beast->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - if (eflash) - eflash->target = actor; - return true; -} - -//---------------------------------------------------------------------------- -// -// FUNC P_MorphedDeath -// -// Unmorphs the actor if possible. -// Returns the unmorphed actor, the style with which they were morphed and the -// health (of the AActor, not the player_t) they last had before unmorphing. -// -//---------------------------------------------------------------------------- - -bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth) -{ - // May be a morphed player - if ((actor->player) && - (actor->player->morphTics) && - (actor->player->MorphStyle & MORPH_UNDOBYDEATH) && - (actor->player->mo) && - (actor->player->mo->alternative)) - { - AActor *realme = actor->player->mo->alternative; - int realstyle = actor->player->MorphStyle; - int realhealth = actor->health; - if (P_UndoPlayerMorph(actor->player, actor->player, 0, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED))) - { - *morphed = realme; - *morphedstyle = realstyle; - *morphedhealth = realhealth; - return true; - } - return false; - } - - // May be a morphed monster - if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - AMorphedMonster *fakeme = static_cast(actor); - AActor *realme = fakeme->UnmorphedMe; - if (realme != NULL) - { - if ((fakeme->UnmorphTime) && - (fakeme->MorphStyle & MORPH_UNDOBYDEATH)) - { - int realstyle = fakeme->MorphStyle; - int realhealth = fakeme->health; - if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) - { - *morphed = realme; - *morphedstyle = realstyle; - *morphedhealth = realhealth; - return true; - } - } - if (realme->flags4 & MF4_BOSSDEATH) - { - realme->health = 0; // make sure that A_BossDeath considers it dead. - A_BossDeath(realme); - } - } - fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die() - return false; - } - - // Not a morphed player or monster - return false; -} - - -// Morphed Monster (you must subclass this to do something useful) --------- - -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); - arc("unmorphedme", UnmorphedMe) - ("unmorphtime", UnmorphTime) - ("morphstyle", MorphStyle) - ("morphexitflash", MorphExitFlash) - ("flagsave", FlagsSave); -} - -void AMorphedMonster::OnDestroy () -{ - if (UnmorphedMe != NULL) - { - UnmorphedMe->Destroy (); - } - Super::OnDestroy(); -} - -void AMorphedMonster::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) -{ - // Dead things don't unmorph -// flags3 |= MF3_STAYMORPHED; - // [MH] - // But they can now, so that line above has been - // moved into P_MorphedDeath() and is now set by - // that function if and only if it is needed. - Super::Die (source, inflictor, dmgflags, MeansOfDeath); - if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED)) - { - UnmorphedMe->health = health; - UnmorphedMe->CallDie (source, inflictor, dmgflags, MeansOfDeath); - } -} - -void AMorphedMonster::Tick () -{ - if (UnmorphTime > level.time || !P_UndoMonsterMorph(this)) - { - Super::Tick(); - } -} diff --git a/src/g_shared/a_morph.h b/src/g_shared/a_morph.h index 4a71773014..00303533cb 100644 --- a/src/g_shared/a_morph.h +++ b/src/g_shared/a_morph.h @@ -33,12 +33,8 @@ enum class PClass; class AActor; class player_t; -class AMorphedMonster; bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassActor *mtype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash); - -bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag = 0, bool force = false); -bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force = false); -bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth); +bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags = 0, bool force = false); #endif //__A_MORPH__ diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index e2a033b073..a630569148 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -154,21 +154,4 @@ private: DEarthquake (); }; -class AMorphedMonster : public AActor -{ - DECLARE_CLASS (AMorphedMonster, AActor) - HAS_OBJECT_POINTERS -public: - void Tick (); - - void Serialize(FSerializer &arc); - void Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) override; - void OnDestroy() override; - - TObjPtr UnmorphedMe; - int UnmorphTime, MorphStyle; - PClassActor *MorphExitFlash; - ActorFlags FlagsSave; -}; - #endif //__A_SHAREDGLOBAL_H__ diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 04db70d324..22ada77127 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -10331,24 +10331,7 @@ scriptwait: if (tag == 0) { - if (activator->player) - { - if (P_UndoPlayerMorph(activator->player, activator->player, 0, force)) - { - changes++; - } - } - else - { - if (activator->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - AMorphedMonster *morphed_actor = barrier_cast(activator); - if (P_UndoMonsterMorph(morphed_actor, force)) - { - changes++; - } - } - } + changes += P_UnmorphActor(activator, activator, 0, force); } else { @@ -10357,24 +10340,7 @@ scriptwait: while ( (actor = iterator.Next ()) ) { - if (actor->player) - { - if (P_UndoPlayerMorph(activator->player, actor->player, 0, force)) - { - changes++; - } - } - else - { - if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - AMorphedMonster *morphed_actor = static_cast(actor); - if (P_UndoMonsterMorph(morphed_actor, force)) - { - changes++; - } - } - } + changes += P_UnmorphActor(activator, actor, 0, force); } } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index f40d010349..aba33a5cd7 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -289,39 +289,41 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf { // Handle possible unmorph on death bool wasgibbed = (health < GetGibHealth()); - AActor *realthis = NULL; - int realstyle = 0; - int realhealth = 0; - if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth)) + { - if (!(realstyle & MORPH_UNDOBYDEATHSAVES)) + IFVIRTUAL(AActor, MorphedDeath) { - if (wasgibbed) + AActor *realthis = NULL; + int realstyle = 0; + int realhealth = 0; + + VMValue params[] = { this }; + VMReturn returns[3]; + returns[0].PointerAt((void**)&realthis); + returns[1].IntAt(&realstyle); + returns[2].IntAt(&realhealth); + VMCall(func, params, 1, returns, 3); + + if (realthis && !(realstyle & MORPH_UNDOBYDEATHSAVES)) { - int realgibhealth = realthis->GetGibHealth(); - if (realthis->health >= realgibhealth) + if (wasgibbed) { - realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l + int realgibhealth = realthis->GetGibHealth(); + if (realthis->health >= realgibhealth) + { + realthis->health = realgibhealth - 1; // if morphed was gibbed, so must original be (where allowed)l + } } + realthis->CallDie(source, inflictor, dmgflags, MeansOfDeath); } - realthis->CallDie(source, inflictor, dmgflags, MeansOfDeath); + } - return; } // [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) effects &= ~FX_RESPAWNINVUL; //flags &= ~MF_INVINCIBLE; - if (debugfile && this->player) - { - static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger... - int pnum = int(this->player-players); - dieticks[pnum] = gametic; - fprintf(debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, - this->player->cheats&CF_PREDICTING ? "predicting" : "real"); - } - // [RH] Notify this actor's items. for (AInventory *item = Inventory; item != NULL; ) { diff --git a/src/p_user.cpp b/src/p_user.cpp index 579b33f0c6..de67ac55e2 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -695,7 +695,7 @@ bool player_t::Resurrect() if (morphTics) { - P_UndoPlayerMorph(this, this); + P_UnmorphActor(mo, mo); } // player is now alive. diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index ebb4a3437e..4c67621252 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -1,5 +1,34 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1994-1996 Raven Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2018 Christoph Oelckers +// Copyright 2005-2008 Martin Howe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// + extend class Actor { + virtual Actor, int, int MorphedDeath() + { + return null, 0, 0; + } + + //=========================================================================== // // Main entry point @@ -10,7 +39,7 @@ extend class Actor { if (player != null && player.mo != null && playerclass != null) { - return player.mo.MorphPlayer(activator.player, playerclass, duration, style, morphflash, unmorphflash); + return player.mo.MorphPlayer(activator? activator.player : null, playerclass, duration, style, morphflash, unmorphflash); } else { @@ -38,7 +67,28 @@ extend class Actor return false; } + //=========================================================================== + // + // Main entry point + // + //=========================================================================== + bool UnMorph(Actor activator, int flags, bool force) + { + if (player) + { + return player.mo.UndoPlayerMorph(activator? activator.player : null, flags, force); + } + else + { + let morphed = MorphedMonster(self); + if (morphed) + return morphed.UndoMonsterMorph(force); + } + return false; + } + + //--------------------------------------------------------------------------- // // FUNC P_MorphMonster @@ -71,7 +121,7 @@ extend class Actor morphed.UnmorphTime = level.time + ((duration) ? duration : DEFMORPHTICS) + random[morphmonst](); morphed.MorphStyle = style; morphed.MorphExitFlash = (exit_flash) ? exit_flash : (class)("TeleportFog"); - //morphed.FlagsSave = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility + morphed.FlagsSave = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility morphed.special = special; morphed.args[0] = args[0]; @@ -501,9 +551,38 @@ extend class PlayerPawn return true; } - + //=========================================================================== + // + // + // + //=========================================================================== + + override Actor, int, int MorphedDeath() + { + // Voodoo dolls should not unmorph the real player here. + if ((player.mo == self) && + (player.morphTics) && + (player.MorphStyle & MRF_UNDOBYDEATH) && + (alternative)) + { + Actor realme = alternative; + int realstyle = player.MorphStyle; + int realhealth = health; + if (UndoPlayerMorph(player, 0, !!(player.MorphStyle & MRF_UNDOBYDEATHFORCED))) + { + return realme, realstyle, realhealth; + } + } + return null, 0, 0; + } } +//=========================================================================== +// +// +// +//=========================================================================== + class MorphProjectile : Actor { @@ -536,11 +615,18 @@ class MorphProjectile : Actor } -class MorphedMonster : Actor native +//=========================================================================== +// +// +// +//=========================================================================== + +class MorphedMonster : Actor { - native Actor UnmorphedMe; - native int UnmorphTime, MorphStyle; - native Class MorphExitFlash; + Actor UnmorphedMe; + int UnmorphTime, MorphStyle; + Class MorphExitFlash; + int FlagsSave; Default { @@ -548,5 +634,118 @@ class MorphedMonster : Actor native -COUNTKILL +FLOORCLIP } + + override void OnDestroy () + { + if (UnmorphedMe != NULL) + { + UnmorphedMe.Destroy (); + } + Super.OnDestroy(); + } + + override void Die (Actor source, Actor inflictor, int dmgflags, Name MeansOfDeath) + { + Super.Die (source, inflictor, dmgflags, MeansOfDeath); + if (UnmorphedMe != NULL && UnmorphedMe.bUnmorphed) + { + UnmorphedMe.health = health; + UnmorphedMe.Die (source, inflictor, dmgflags, MeansOfDeath); + } + } + + override void Tick () + { + if (UnmorphTime > level.time || !UndoMonsterMorph()) + { + Super.Tick(); + } + } + + //---------------------------------------------------------------------------- + // + // FUNC P_UndoMonsterMorph + // + // Returns true if the monster unmorphs. + // + //---------------------------------------------------------------------------- + + virtual bool UndoMonsterMorph(bool force = false) + { + if (UnmorphTime == 0 || UnmorphedMe == NULL || bStayMorphed || UnmorphedMe.bStayMorphed) + { + return false; + } + let unmorphed = UnmorphedMe; + unmorphed.SetOrigin (Pos, false); + unmorphed.bSolid = true; + bSolid = false; + bool save = bTouchy; + bTouchy = false; + if (!force && !unmorphed.TestMobjLocation ()) + { // Didn't fit + unmorphed.bSolid = false; + bSolid = true; + bTouchy = save; + UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds + return false; + } + unmorphed.Angle = Angle; + unmorphed.target = target; + unmorphed.bShadow = bShadow; + unmorphed.bGhost = bGhost; + unmorphed.bSolid = !!(flagssave & 2); + unmorphed.bShootable = !!(flagssave & 4); + unmorphed.bInvisible = !!(flagssave & 0x40); + unmorphed.health = unmorphed.SpawnHealth(); + unmorphed.Vel = Vel; + unmorphed.ChangeTid(tid); + unmorphed.special = special; + unmorphed.Score = Score; + unmorphed.args[0] = args[0]; + unmorphed.args[1] = args[1]; + unmorphed.args[2] = args[2]; + unmorphed.args[3] = args[3]; + unmorphed.args[4] = args[4]; + unmorphed.CopyFriendliness (self, true); + UnmorphedMe = NULL; + Substitute(unmorphed); + Destroy (); + let eflash = Spawn(MorphExitFlash, Pos + (0, 0, gameinfo.TELEFOGHEIGHT), ALLOW_REPLACE); + if (eflash) + eflash.target = unmorphed; + return true; + } + + //=========================================================================== + // + // + // + //=========================================================================== + + override Actor, int, int MorphedDeath() + { + let realme = UnmorphedMe; + if (realme != NULL) + { + if ((UnmorphTime) && + (MorphStyle & MRF_UNDOBYDEATH)) + { + int realstyle = MorphStyle; + int realhealth = health; + if (UndoMonsterMorph(!!(MorphStyle & MRF_UNDOBYDEATHFORCED))) + { + return realme, realstyle, realhealth; + } + } + if (realme.bBossDeath) + { + realme.health = 0; // make sure that A_BossDeath considers it dead. + realme.A_BossDeath(); + } + } + return null, 0, 0; + } + } From ac77ca64742ab31dc3caa44901e3d9f72fcaaaa1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 11:29:57 +0100 Subject: [PATCH 07/48] - scriptified Weapon.CheckAmmo and Weapon.DepleteAmmo --- src/g_inventory/a_weapons.cpp | 133 +------------------ wadsrc/static/zscript/inventory/weapons.txt | 139 +++++++++++++++++++- 2 files changed, 135 insertions(+), 137 deletions(-) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 9cef1686ea..50e77f44b8 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -224,85 +224,9 @@ bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int amm VMCall(func, params, 5, &ret, 1); return !!retval; } - return DoCheckAmmo(fireMode, autoSwitch, requireAmmo, ammocount); -} - -bool AWeapon::DoCheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo, int ammocount) -{ - int altFire; - int count1, count2; - int enough, enoughmask; - int lAmmoUse1; - - if ((dmflags & DF_INFINITE_AMMO) || (Owner->FindInventory (PClass::FindActor(NAME_PowerInfiniteAmmo), true) != nullptr)) - { - return true; - } - if (fireMode == EitherFire) - { - bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false); - if (!gotSome && autoSwitch) - { - barrier_cast(Owner)->PickNewWeapon (nullptr); - } - return gotSome; - } - altFire = (fireMode == AltFire); - if (!requireAmmo && (WeaponFlags & (WIF_AMMO_OPTIONAL << altFire))) - { - return true; - } - count1 = (Ammo1 != nullptr) ? Ammo1->Amount : 0; - count2 = (Ammo2 != nullptr) ? Ammo2->Amount : 0; - - if ((WeaponFlags & WIF_DEHAMMO) && (Ammo1 == nullptr)) - { - lAmmoUse1 = 0; - } - else if (ammocount >= 0 && (WeaponFlags & WIF_DEHAMMO)) - { - lAmmoUse1 = ammocount; - } - else - { - lAmmoUse1 = AmmoUse1; - } - - enough = (count1 >= lAmmoUse1) | ((count2 >= AmmoUse2) << 1); - if (WeaponFlags & (WIF_PRIMARY_USES_BOTH << altFire)) - { - enoughmask = 3; - } - else - { - enoughmask = 1 << altFire; - } - if (altFire && FindState(NAME_AltFire) == nullptr) - { // If this weapon has no alternate fire, then there is never enough ammo for it - enough &= 1; - } - if (((enough & enoughmask) == enoughmask) || (enough && (WeaponFlags & WIF_AMMO_CHECKBOTH))) - { - return true; - } - // out of ammo, pick a weapon to change to - if (autoSwitch) - { - barrier_cast(Owner)->PickNewWeapon (nullptr); - } return false; } -DEFINE_ACTION_FUNCTION(AWeapon, CheckAmmo) -{ - PARAM_SELF_PROLOGUE(AWeapon); - PARAM_INT(mode); - PARAM_BOOL(autoswitch); - PARAM_BOOL(require); - PARAM_INT(ammocnt); - ACTION_RETURN_BOOL(self->DoCheckAmmo(mode, autoswitch, require, ammocnt)); -} - //=========================================================================== // // AWeapon :: DepleteAmmo @@ -324,64 +248,9 @@ bool AWeapon::DepleteAmmo(bool altFire, bool checkEnough, int ammouse) VMCall(func, params, 4, &ret, 1); return !!retval; } - return DoDepleteAmmo(altFire, checkEnough, ammouse); + return false; } -bool AWeapon::DoDepleteAmmo (bool altFire, bool checkEnough, int ammouse) -{ - if (!((dmflags & DF_INFINITE_AMMO) || (Owner->FindInventory (PClass::FindActor(NAME_PowerInfiniteAmmo), true) != nullptr))) - { - if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false, false, ammouse)) - { - return false; - } - if (!altFire) - { - if (Ammo1 != nullptr) - { - if (ammouse >= 0 && (WeaponFlags & WIF_DEHAMMO)) - { - Ammo1->Amount -= ammouse; - } - else - { - Ammo1->Amount -= AmmoUse1; - } - } - if ((WeaponFlags & WIF_PRIMARY_USES_BOTH) && Ammo2 != nullptr) - { - Ammo2->Amount -= AmmoUse2; - } - } - else - { - if (Ammo2 != nullptr) - { - Ammo2->Amount -= AmmoUse2; - } - if ((WeaponFlags & WIF_ALT_USES_BOTH) && Ammo1 != nullptr) - { - Ammo1->Amount -= AmmoUse1; - } - } - if (Ammo1 != nullptr && Ammo1->Amount < 0) - Ammo1->Amount = 0; - if (Ammo2 != nullptr && Ammo2->Amount < 0) - Ammo2->Amount = 0; - } - return true; -} - -DEFINE_ACTION_FUNCTION(AWeapon, DepleteAmmo) -{ - PARAM_SELF_PROLOGUE(AWeapon); - PARAM_BOOL(altfire); - PARAM_BOOL(checkenough); - PARAM_INT(ammouse); - ACTION_RETURN_BOOL(self->DoDepleteAmmo(altfire, checkenough, ammouse)); -} - - //=========================================================================== // // AWeapon :: GetUpState diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 8b9b71ff7b..80a892883d 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -78,9 +78,6 @@ class Weapon : StateProvider native Stop; } - native virtual bool CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo = false, int ammocount = -1); - native virtual bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1); - virtual State GetReadyState () { return FindState('Ready'); @@ -262,13 +259,13 @@ class Weapon : StateProvider native zoom = 1 / clamp(zoom, 0.1, 50.0); if (flags & 1) { // Make the zoom instant. - self.player.FOV = self.player.DesiredFOV * zoom; + player.FOV = player.DesiredFOV * zoom; } if (flags & 2) { // Disable pitch/yaw scaling. zoom = -zoom; } - self.player.ReadyWeapon.FOVScale = zoom; + player.ReadyWeapon.FOVScale = zoom; } } @@ -703,6 +700,138 @@ class Weapon : StateProvider native pspr.SetState(GetUpState()); } + //=========================================================================== + // + // AWeapon :: CheckAmmo + // + // Returns true if there is enough ammo to shoot. If not, selects the + // next weapon to use. + // + //=========================================================================== + + virtual bool CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo = false, int ammocount = -1) + { + int count1, count2; + int enough, enoughmask; + int lAmmoUse1; + + if (sv_infiniteammo || (Owner.FindInventory ('PowerInfiniteAmmo', true) != null)) + { + return true; + } + if (fireMode == EitherFire) + { + bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false); + if (!gotSome && autoSwitch) + { + PlayerPawn(Owner).PickNewWeapon (null); + } + return gotSome; + } + let altFire = (fireMode == AltFire); + let optional = (altFire? bAlt_Ammo_Optional : bAmmo_Optional); + let useboth = (altFire? bPrimary_Uses_Both : bAlt_Uses_Both); + + if (!requireAmmo && optional) + { + return true; + } + count1 = (Ammo1 != null) ? Ammo1.Amount : 0; + count2 = (Ammo2 != null) ? Ammo2.Amount : 0; + + if (bDehAmmo && Ammo1 == null) + { + lAmmoUse1 = 0; + } + else if (ammocount >= 0 && bDehAmmo) + { + lAmmoUse1 = ammocount; + } + else + { + lAmmoUse1 = AmmoUse1; + } + + enough = (count1 >= lAmmoUse1) | ((count2 >= AmmoUse2) << 1); + if (useboth) + { + enoughmask = 3; + } + else + { + enoughmask = 1 << altFire; + } + if (altFire && FindState('AltFire') == null) + { // If this weapon has no alternate fire, then there is never enough ammo for it + enough &= 1; + } + if (((enough & enoughmask) == enoughmask) || (enough && bAmmo_CheckBoth)) + { + return true; + } + // out of ammo, pick a weapon to change to + if (autoSwitch) + { + PlayerPawn(Owner).PickNewWeapon (null); + } + return false; + } + + + //=========================================================================== + // + // AWeapon :: DepleteAmmo + // + // Use up some of the weapon's ammo. Returns true if the ammo was successfully + // depleted. If checkEnough is false, then the ammo will always be depleted, + // even if it drops below zero. + // + //=========================================================================== + + virtual bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1) + { + if (!(sv_infiniteammo || (Owner.FindInventory ('PowerInfiniteAmmo', true) != null))) + { + if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false, false, ammouse)) + { + return false; + } + if (!altFire) + { + if (Ammo1 != null) + { + if (ammouse >= 0 && bDehAmmo) + { + Ammo1.Amount -= ammouse; + } + else + { + Ammo1.Amount -= AmmoUse1; + } + } + if (bPRIMARY_USES_BOTH && Ammo2 != null) + { + Ammo2.Amount -= AmmoUse2; + } + } + else + { + if (Ammo2 != null) + { + Ammo2.Amount -= AmmoUse2; + } + if (bALT_USES_BOTH && Ammo1 != null) + { + Ammo1.Amount -= AmmoUse1; + } + } + if (Ammo1 != null && Ammo1.Amount < 0) + Ammo1.Amount = 0; + if (Ammo2 != null && Ammo2.Amount < 0) + Ammo2.Amount = 0; + } + return true; + } } From 6fc63b9b7817a801e5239e6c4d323b224f4a2bac Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 13:06:01 +0100 Subject: [PATCH 08/48] - started with a ScriptUtil class which will allow moving function implementations for ACS and FraggleScript to zscript.txt So far 3 functions for testing implemented. --- src/CMakeLists.txt | 1 + src/fragglescript/t_func.cpp | 42 +++---- src/namedef.h | 5 + src/p_acs.cpp | 106 +----------------- src/p_acs.h | 1 + src/scripting/vm/vm.h | 17 +++ src/scriptutil.cpp | 104 +++++++++++++++++ src/scriptutil.h | 27 +++++ wadsrc/static/zscript.txt | 2 + .../static/zscript/scriptutil/scriptutil.txt | 105 +++++++++++++++++ 10 files changed, 280 insertions(+), 130 deletions(-) create mode 100644 src/scriptutil.cpp create mode 100644 src/scriptutil.h create mode 100644 wadsrc/static/zscript/scriptutil/scriptutil.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c8e62c3a44..056897f409 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -998,6 +998,7 @@ set (PCH_SOURCES s_sound.cpp serializer.cpp sc_man.cpp + scriptutil.cpp st_stuff.cpp statistics.cpp stats.cpp diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index c034b51161..be6c185201 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -48,6 +48,7 @@ #include "r_utility.h" #include "g_levellocals.h" #include "actorinlines.h" +#include "scriptutil.h" static FRandom pr_script("FScript"); @@ -283,6 +284,17 @@ static int T_GetPlayerNum(const svalue_t &arg) return playernum; } +APlayerPawn *T_GetPlayerActor(const svalue_t &arg) +{ + int num = T_GetPlayerNum(arg); + return num == -1 ? nullptr : players[num].mo; +} + +PClassActor *T_ClassType(const svalue_t &arg) +{ + return PClass::FindActor(stringvalue(arg)); +} + //========================================================================== // // Finds a sector from a tag. This has been extended to allow looking for @@ -2834,34 +2846,8 @@ void FParser::SF_SetWeapon() { if (CheckArgs(2)) { - int playernum=T_GetPlayerNum(t_argv[0]); - if (playernum!=-1) - { - AInventory *item = players[playernum].mo->FindInventory (PClass::FindActor (stringvalue(t_argv[1]))); - - if (item == NULL || !item->IsKindOf(NAME_Weapon)) - { - } - else if (players[playernum].ReadyWeapon == item) - { - // The weapon is already selected, so setweapon succeeds by default, - // but make sure the player isn't switching away from it. - players[playernum].PendingWeapon = WP_NOCHANGE; - t_return.value.i = 1; - } - else - { - auto weap = static_cast (item); - - if (weap->CheckAmmo (AWeapon::EitherFire, false)) - { - // There's enough ammo, so switch to it. - t_return.value.i = 1; - players[playernum].PendingWeapon = weap; - } - } - } - t_return.value.i = 0; + t_return.type = svt_int; + t_return.value.i = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, T_GetPlayerActor(t_argv[0]), ScriptUtil::Class, T_ClassType(t_argv[1]), ScriptUtil::End); } } diff --git a/src/namedef.h b/src/namedef.h index 2d0c2c1269..6dc22c1132 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -983,3 +983,8 @@ xx(snd_output) xx(snd_output_format) xx(snd_speakermode) xx(snd_resampler) + +// ScriptUtil entry points +xx(ScriptUtil) +xx(SetMarineWeapon) +xx(SetMarineSprite) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 22ada77127..b90558632c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -74,6 +74,7 @@ #include "g_levellocals.h" #include "actorinlines.h" #include "types.h" +#include "scriptutil.h" // P-codes for ACS scripts enum @@ -6963,28 +6964,6 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, FACSStackMemory& return true; } -static void SetMarineWeapon(AActor *marine, int weapon) -{ - static VMFunction *smw = nullptr; - if (smw == nullptr) PClass::FindFunction(&smw, NAME_ScriptedMarine, NAME_SetWeapon); - if (smw) - { - VMValue params[2] = { marine, weapon }; - VMCall(smw, params, 2, nullptr, 0); - } -} - -static void SetMarineSprite(AActor *marine, PClassActor *source) -{ - static VMFunction *sms = nullptr; - if (sms == nullptr) PClass::FindFunction(&sms, NAME_ScriptedMarine, NAME_SetSprite); - if (sms) - { - VMValue params[2] = { marine, source }; - VMCall(sms, params, 2, nullptr, 0); - } -} - int DLevelScript::RunScript () { DACSThinker *controller = DACSThinker::ActiveThinker; @@ -9832,93 +9811,16 @@ scriptwait: break; case PCD_SETWEAPON: - if (activator == NULL || activator->player == NULL) - { - STACK(1) = 0; - } - else - { - AInventory *item = activator->FindInventory (PClass::FindActor (FBehavior::StaticLookupString (STACK(1)))); - - if (item == NULL || !item->IsKindOf(NAME_Weapon)) - { - STACK(1) = 0; - } - else if (activator->player->ReadyWeapon == item) - { - // The weapon is already selected, so setweapon succeeds by default, - // but make sure the player isn't switching away from it. - activator->player->PendingWeapon = WP_NOCHANGE; - STACK(1) = 1; - } - else - { - AWeapon *weap = static_cast (item); - - if (weap->CheckAmmo (AWeapon::EitherFire, false)) - { - // There's enough ammo, so switch to it. - STACK(1) = 1; - activator->player->PendingWeapon = weap; - } - else - { - STACK(1) = 0; - } - } - } + STACK(1) = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, activator, ScriptUtil::ACSClass, STACK(1), ScriptUtil::End); break; case PCD_SETMARINEWEAPON: - if (STACK(2) != 0) - { - AActor *marine; - NActorIterator iterator(NAME_ScriptedMarine, STACK(2)); - - while ((marine = iterator.Next()) != NULL) - { - SetMarineWeapon(marine, STACK(1)); - } - } - else - { - if (activator != nullptr && activator->IsKindOf (NAME_ScriptedMarine)) - { - SetMarineWeapon(activator, STACK(1)); - } - } + ScriptUtil::Exec(NAME_SetMarineWeapon, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::Int, STACK(1), ScriptUtil::End); sp -= 2; break; case PCD_SETMARINESPRITE: - { - PClassActor *type = PClass::FindActor(FBehavior::StaticLookupString (STACK(1))); - - if (type != NULL) - { - if (STACK(2) != 0) - { - AActor *marine; - NActorIterator iterator(NAME_ScriptedMarine, STACK(2)); - - while ((marine = iterator.Next()) != NULL) - { - SetMarineSprite(marine, type); - } - } - else - { - if (activator != nullptr && activator->IsKindOf(NAME_ScriptedMarine)) - { - SetMarineSprite(activator, type); - } - } - } - else - { - Printf ("Unknown actor type: %s\n", FBehavior::StaticLookupString (STACK(1))); - } - } + ScriptUtil::Exec(NAME_SetMarineSprite, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::ACSClass, STACK(1), ScriptUtil::End); sp -= 2; break; diff --git a/src/p_acs.h b/src/p_acs.h index fc361a230f..9115a370be 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -43,6 +43,7 @@ class FFont; class FileReader; struct line_t; +class FSerializer; enum diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 1e5b65b2e4..d52dc0f6f9 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -675,6 +675,23 @@ VMFunction *FindVMFunction(PClass *cls, const char *name); FString FStringFormat(VM_ARGS, int offset = 0); +#define IFVM(cls, funcname) \ + static VMFunction * func = nullptr; \ + if (func == nullptr) { \ + func = dyn_cast(RUNTIME_CLASS(cls)->FindSymbol(#funcname, false)); \ + assert(func); \ + } \ + if (func != nullptr) + +#define IFVMNAME(cls, funcname) \ + static VMFunction * func = nullptr; \ + if (func == nullptr) { \ + func = dyn_cast(PClass::FindClass(cls)->FindSymbol(#funcname, false)); \ + assert(func); \ + } \ + if (func != nullptr) + + unsigned GetVirtualIndex(PClass *cls, const char *funcname); diff --git a/src/scriptutil.cpp b/src/scriptutil.cpp new file mode 100644 index 0000000000..f5d88087bc --- /dev/null +++ b/src/scriptutil.cpp @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2018 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// +// DESCRIPTION: generalized interface for implementing ACS/FS functions +// in ZScript. +// +//----------------------------------------------------------------------------- + +#include "i_system.h" +#include "tarray.h" +#include "dobject.h" +#include "vm.h" +#include "scriptutil.h" +#include "p_acs.h" + + +static TArray parameters; +static TMap functions; + + +void ScriptUtil::BuildParameters(va_list ap) +{ + for(int type = va_arg(ap, int); type != End; type = va_arg(ap, int)) + { + switch (type) + { + case Int: + parameters.Push(VMValue(va_arg(ap, int))); + break; + + case Pointer: + case Class: // this is just a pointer. + case String: // must be passed by reference to a persistent location! + parameters.Push(VMValue(va_arg(ap, void*))); + break; + + case Float: + parameters.Push(VMValue(va_arg(ap, double))); + break; + + case ACSClass: + parameters.Push(VMValue(PClass::FindActor(FBehavior::StaticLookupString(va_arg(ap, int))))); + break; + } + } +} + +void ScriptUtil::RunFunction(FName functionname, unsigned paramstart, VMReturn &returns) +{ + VMFunction *func = nullptr; + auto check = functions.CheckKey(functionname); + if (!check) + { + PClass::FindFunction(&func, NAME_ScriptUtil, functionname); + if (func == nullptr) + { + I_Error("Call to undefined function ScriptUtil.%s", functionname.GetChars()); + } + functions.Insert(functionname, func); + } + else func = *check; + + VMCall(func, ¶meters[paramstart], parameters.Size() - paramstart, &returns, 1); +} + +int ScriptUtil::Exec(FName functionname, ...) +{ + unsigned paramstart = parameters.Size(); + va_list ap; + va_start(ap, functionname); + try + { + BuildParameters(ap); + int ret = 0; + VMReturn returns(&ret); + RunFunction(functionname, paramstart, returns); + va_end(ap); + parameters.Clamp(paramstart); + return ret; + } + catch(...) + { + va_end(ap); + parameters.Clamp(paramstart); + throw; + } +} diff --git a/src/scriptutil.h b/src/scriptutil.h new file mode 100644 index 0000000000..6d8f83cc8e --- /dev/null +++ b/src/scriptutil.h @@ -0,0 +1,27 @@ +#pragma once + + +#include +#include "name.h" + + +class ScriptUtil +{ + static void BuildParameters(va_list ap); + static void RunFunction(FName function, unsigned paramstart, VMReturn &returns); + +public: + enum + { + End, + Int, + Pointer, + Float, + String, + Class, + ACSString, // convenience helpers taking an ACS string index instead of a string + ACSClass, + }; + + static int Exec(FName functionname, ...); +}; diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 41b3851412..0cc9c8f6b0 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -260,3 +260,5 @@ version "3.7" #include "zscript/chex/chexitems.txt" #include "zscript/chex/chexdecorations.txt" #include "zscript/chex/chexplayer.txt" + +#include "zscript/scriptutil/scriptutil.txt" diff --git a/wadsrc/static/zscript/scriptutil/scriptutil.txt b/wadsrc/static/zscript/scriptutil/scriptutil.txt new file mode 100644 index 0000000000..a47f79486c --- /dev/null +++ b/wadsrc/static/zscript/scriptutil/scriptutil.txt @@ -0,0 +1,105 @@ + +// Container for utility functions used by ACS and FraggleScript. + +class ScriptUtil play +{ + + //========================================================================== + // + // + // + //========================================================================== + + static int SetWeapon(Actor activator, class cls) + { + if(activator != NULL && activator.player != NULL && cls != null) + { + let item = Weapon(activator.FindInventory(cls)); + + if(item != NULL) + { + if(activator.player.ReadyWeapon == item) + { + // The weapon is already selected, so setweapon succeeds by default, + // but make sure the player isn't switching away from it. + activator.player.PendingWeapon = WP_NOCHANGE; + return 1; + } + else + { + if(item.CheckAmmo(Weapon.EitherFire, false)) + { + // There's enough ammo, so switch to it. + activator.player.PendingWeapon = item; + return 1; + } + } + } + } + return 0; + } + + //========================================================================== + // + // + // + //========================================================================== + + static void SetMarineWeapon(Actor activator, int tid, int marineweapontype) + { + if (tid != 0) + { + let it = ActorIterator.Create(tid, 'ScriptedMarine'); + ScriptedMarine marine; + + while ((marine = ScriptedMarine(it.Next())) != NULL) + { + marine.SetWeapon(marineweapontype); + } + } + else + { + let marine = ScriptedMarine(activator); + if (marine != null) + { + marine.SetWeapon(marineweapontype); + } + } + } + + //========================================================================== + // + // + // + //========================================================================== + + static void SetMarineSprite(Actor activator, int tid, class type) + { + if (type != NULL) + { + if (tid != 0) + { + let it = ActorIterator.Create(tid, 'ScriptedMarine'); + ScriptedMarine marine; + + while ((marine = ScriptedMarine(it.Next())) != NULL) + { + marine.SetSprite(type); + } + } + else + { + let marine = ScriptedMarine(activator); + if (marine != null) + { + marine.SetSprite(type); + } + } + } + else + { + Console.Printf ("Unknown actor type: %s\n", type.GetClassName()); + } + } + +} From 4440bff83a26f7f09f5a7eb2d96f269f7eaa5515 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 13:08:44 +0100 Subject: [PATCH 09/48] - deleted redundant native ActivateMorphWeapon method. --- src/d_player.h | 1 - src/g_inventory/a_weapons.h | 2 -- src/p_user.cpp | 39 ------------------------------------- 3 files changed, 42 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 009af57fa1..774aba83d2 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -99,7 +99,6 @@ public: bool ResetAirSupply (bool playgasp = true); int GetMaxHealth(bool withupgrades = false) const; - void ActivateMorphWeapon (); AWeapon *PickNewWeapon (PClassActor *ammotype); AWeapon *BestWeapon (PClassActor *ammotype); void GiveDeathmatchInventory (); diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 3f5d34dd8f..73a9d15ece 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -140,9 +140,7 @@ public: EitherFire }; bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1); - bool DoCheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int ammocount); bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1); - bool DoDepleteAmmo(bool altFire, bool checkEnough, int ammouse); enum { diff --git a/src/p_user.cpp b/src/p_user.cpp index de67ac55e2..3b3802cc2a 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1503,45 +1503,6 @@ void APlayerPawn::GiveDefaultInventory () } } -void APlayerPawn::ActivateMorphWeapon () -{ - PClassActor *morphweapon = PClass::FindActor (MorphWeapon); - player->PendingWeapon = WP_NOCHANGE; - - if (player->ReadyWeapon != nullptr) - { - player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP; - } - - if (morphweapon == nullptr || !morphweapon->IsDescendantOf (RUNTIME_CLASS(AWeapon))) - { // No weapon at all while morphed! - player->ReadyWeapon = nullptr; - } - else - { - player->ReadyWeapon = static_cast(player->mo->FindInventory (morphweapon)); - if (player->ReadyWeapon == nullptr) - { - player->ReadyWeapon = static_cast(player->mo->GiveInventoryType (morphweapon)); - if (player->ReadyWeapon != nullptr) - { - player->ReadyWeapon->GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph - } - } - if (player->ReadyWeapon != nullptr) - { - P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetReadyState()); - } - } - - if (player->ReadyWeapon != nullptr) - { - P_SetPsprite(player, PSP_FLASH, nullptr); - } - - player->PendingWeapon = WP_NOCHANGE; -} - //=========================================================================== // // APlayerPawn :: Die From e071be8371ba7dc47c28c9ec453e087e7fbfa2ba Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 13:50:27 +0100 Subject: [PATCH 10/48] - scriptified A_FireBullets and A_CustomBulletAttack. --- src/p_actionfunctions.cpp | 278 ------------------ wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/actor.txt | 1 - wadsrc/static/zscript/actor_attacks.txt | 120 ++++++++ .../zscript/inventory/stateprovider.txt | 114 ++++++- 5 files changed, 234 insertions(+), 280 deletions(-) create mode 100644 wadsrc/static/zscript/actor_attacks.txt diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 93a521a25f..464eb1cb6e 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -75,7 +75,6 @@ AActor *SingleActorFromTID(int tid, AActor *defactor); static FRandom pr_camissile ("CustomActorfire"); static FRandom pr_cabullet ("CustomBullet"); -static FRandom pr_cwbullet ("CustomWpBullet"); static FRandom pr_cwjump ("CustomWpJump"); static FRandom pr_cwpunch ("CustomWpPunch"); static FRandom pr_grenade ("ThrowGrenade"); @@ -1516,112 +1515,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpawnProjectile) ACTION_RETURN_OBJECT(missile); } -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum CBA_Flags -{ - CBAF_AIMFACING = 1, - CBAF_NORANDOM = 2, - CBAF_EXPLICITANGLE = 4, - CBAF_NOPITCH = 8, - CBAF_NORANDOMPUFFZ = 16, - CBAF_PUFFTARGET = 32, - CBAF_PUFFMASTER = 64, - CBAF_PUFFTRACER = 128, -}; - -static void AimBulletMissile(AActor *proj, AActor *puff, int flags, bool temp, bool cba); - -DEFINE_ACTION_FUNCTION(AActor, A_CustomBulletAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_ANGLE (spread_xy); - PARAM_ANGLE (spread_z); - PARAM_INT (numbullets); - PARAM_INT (damageperbullet); - PARAM_CLASS (pufftype, AActor); - PARAM_FLOAT (range); - PARAM_INT (flags); - PARAM_INT (ptr); - PARAM_CLASS (missile, AActor); - PARAM_FLOAT (Spawnheight); - PARAM_FLOAT (Spawnofs_xy); - - AActor *ref = COPY_AAPTR(self, ptr); - - if (range == 0) - range = MISSILERANGE; - - int i; - DAngle bangle; - DAngle bslope = 0.; - int laflags = (flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if (ref != NULL || (flags & CBAF_AIMFACING)) - { - if (!(flags & CBAF_AIMFACING)) - { - A_Face(self, ref); - } - bangle = self->Angles.Yaw; - - if (!(flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); - if (pufftype == nullptr) pufftype = PClass::FindActor(NAME_BulletPuff); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i = 0; i < numbullets; i++) - { - DAngle angle = bangle; - DAngle slope = bslope; - - if (flags & CBAF_EXPLICITANGLE) - { - angle += spread_xy; - slope += spread_z; - } - else - { - angle += spread_xy * (pr_cwbullet.Random2() / 255.); - slope += spread_z * (pr_cwbullet.Random2() / 255.); - } - - int damage = damageperbullet; - - if (!(flags & CBAF_NORANDOM)) - damage *= ((pr_cabullet()%3)+1); - - AActor *puff = P_LineAttack(self, angle, range, slope, damage, NAME_Hitscan, pufftype, laflags); - if (missile != nullptr && pufftype != nullptr) - { - double x = Spawnofs_xy * angle.Cos(); - double y = Spawnofs_xy * angle.Sin(); - - DVector3 pos = self->Pos(); - self->SetXYZ(self->Vec3Offset(x, y, 0.)); - AActor *proj = P_SpawnMissileAngleZSpeed(self, self->Z() + self->GetBobOffset() + Spawnheight, missile, self->Angles.Yaw, 0, GetDefaultByType(missile)->Speed, self, false); - self->SetXYZ(pos); - - if (proj) - { - bool temp = (puff == nullptr); - if (!puff) - { - puff = P_LineAttack(self, angle, range, slope, 0, NAME_Hitscan, pufftype, laflags | LAF_NOINTERACT); - } - if (puff) - { - AimBulletMissile(proj, puff, flags, temp, true); - } - } - } - } - } - return 0; -} - //========================================================================== // // A fully customizable melee attack @@ -1732,177 +1625,6 @@ DEFINE_ACTION_FUNCTION(AStateProvider, A_JumpIfNoAmmo) } -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum FB_Flags -{ - FBF_USEAMMO = 1, - FBF_NORANDOM = 2, - FBF_EXPLICITANGLE = 4, - FBF_NOPITCH = 8, - FBF_NOFLASH = 16, - FBF_NORANDOMPUFFZ = 32, - FBF_PUFFTARGET = 64, - FBF_PUFFMASTER = 128, - FBF_PUFFTRACER = 256, -}; - -static void AimBulletMissile(AActor *proj, AActor *puff, int flags, bool temp, bool cba) -{ - if (proj && puff) - { - if (proj) - { - // FAF_BOTTOM = 1 - // Aim for the base of the puff as that's where blood puffs will spawn... roughly. - - A_Face(proj, puff, 0., 0., 0., 0., 1); - proj->Vel3DFromAngle(proj->Angles.Pitch, proj->Speed); - - if (!temp) - { - if (cba) - { - if (flags & CBAF_PUFFTARGET) proj->target = puff; - if (flags & CBAF_PUFFMASTER) proj->master = puff; - if (flags & CBAF_PUFFTRACER) proj->tracer = puff; - } - else - { - if (flags & FBF_PUFFTARGET) proj->target = puff; - if (flags & FBF_PUFFMASTER) proj->master = puff; - if (flags & FBF_PUFFTRACER) proj->tracer = puff; - } - } - } - } - if (puff && temp) - { - puff->Destroy(); - } -} - -DEFINE_ACTION_FUNCTION(AStateProvider, A_FireBullets) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - PARAM_ANGLE (spread_xy); - PARAM_ANGLE (spread_z); - PARAM_INT (numbullets); - PARAM_INT (damageperbullet); - PARAM_CLASS (pufftype, AActor); - PARAM_INT (flags); - PARAM_FLOAT (range); - PARAM_CLASS (missile, AActor); - PARAM_FLOAT (Spawnheight); - PARAM_FLOAT (Spawnofs_xy); - - if (!self->player) return 0; - - player_t *player = self->player; - AWeapon *weapon = player->ReadyWeapon; - - int i; - DAngle bangle; - DAngle bslope = 0.; - int laflags = (flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if ((flags & FBF_USEAMMO) && weapon && ACTION_CALL_FROM_PSPRITE()) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) - return 0; // out of ammo - } - - if (range == 0) range = PLAYERMISSILERANGE; - - if (!(flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); - - if (!(flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); - bangle = self->Angles.Yaw; - - if (pufftype == NULL) pufftype = PClass::FindActor(NAME_BulletPuff); - - if (weapon != NULL) - { - S_Sound(self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if ((numbullets == 1 && !player->refire) || numbullets == 0) - { - int damage = damageperbullet; - - if (!(flags & FBF_NORANDOM)) - damage *= ((pr_cwbullet()%3)+1); - - AActor *puff = P_LineAttack(self, bangle, range, bslope, damage, NAME_Hitscan, pufftype, laflags); - - if (missile != nullptr) - { - bool temp = false; - DAngle ang = self->Angles.Yaw - 90; - DVector2 ofs = ang.ToVector(Spawnofs_xy); - AActor *proj = P_SpawnPlayerMissile(self, ofs.X, ofs.Y, Spawnheight, missile, bangle, nullptr, nullptr, false, true); - if (proj) - { - if (!puff) - { - temp = true; - puff = P_LineAttack(self, bangle, range, bslope, 0, NAME_Hitscan, pufftype, laflags | LAF_NOINTERACT); - } - AimBulletMissile(proj, puff, flags, temp, false); - } - } - } - else - { - if (numbullets < 0) - numbullets = 1; - for (i = 0; i < numbullets; i++) - { - DAngle angle = bangle; - DAngle slope = bslope; - - if (flags & FBF_EXPLICITANGLE) - { - angle += spread_xy; - slope += spread_z; - } - else - { - angle += spread_xy * (pr_cwbullet.Random2() / 255.); - slope += spread_z * (pr_cwbullet.Random2() / 255.); - } - - int damage = damageperbullet; - - if (!(flags & FBF_NORANDOM)) - damage *= ((pr_cwbullet()%3)+1); - - AActor *puff = P_LineAttack(self, angle, range, slope, damage, NAME_Hitscan, pufftype, laflags); - - if (missile != nullptr) - { - bool temp = false; - DAngle ang = self->Angles.Yaw - 90; - DVector2 ofs = ang.ToVector(Spawnofs_xy); - AActor *proj = P_SpawnPlayerMissile(self, ofs.X, ofs.Y, Spawnheight, missile, angle, nullptr, nullptr, false, true); - if (proj) - { - if (!puff) - { - temp = true; - puff = P_LineAttack(self, angle, range, slope, 0, NAME_Hitscan, pufftype, laflags | LAF_NOINTERACT); - } - AimBulletMissile(proj, puff, flags, temp, false); - } - } - } - } - return 0; -} - //========================================================================== // diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 0cc9c8f6b0..6c9df6c72c 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -5,6 +5,7 @@ version "3.7" #include "zscript/dynarrays.txt" #include "zscript/constants.txt" #include "zscript/actor.txt" +#include "zscript/actor_attacks.txt" #include "zscript/actor_checks.txt" #include "zscript/events.txt" #include "zscript/destructible.txt" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 4b710f2ab6..2e9f4be15f 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -1067,7 +1067,6 @@ class Actor : Thinker native 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 Actor A_SpawnProjectile(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, double veleffect = 3); 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); diff --git a/wadsrc/static/zscript/actor_attacks.txt b/wadsrc/static/zscript/actor_attacks.txt new file mode 100644 index 0000000000..3c6c16d0c2 --- /dev/null +++ b/wadsrc/static/zscript/actor_attacks.txt @@ -0,0 +1,120 @@ +extend class Actor +{ + //--------------------------------------------------------------------------- + // + // Used by A_CustomBulletAttack and A_FireBullets + // + //--------------------------------------------------------------------------- + + static void AimBulletMissile(Actor proj, Actor puff, int flags, bool temp, bool cba) + { + if (proj && puff) + { + // FAF_BOTTOM = 1 + // Aim for the base of the puff as that's where blood puffs will spawn... roughly. + + proj.A_Face(puff, 0., 0., 0., 0., 1); + proj.Vel3DFromAngle(proj.Speed, proj.Angle, proj.Pitch); + + if (!temp) + { + if (cba) + { + if (flags & CBAF_PUFFTARGET) proj.target = puff; + if (flags & CBAF_PUFFMASTER) proj.master = puff; + if (flags & CBAF_PUFFTRACER) proj.tracer = puff; + } + else + { + if (flags & FBF_PUFFTARGET) proj.target = puff; + if (flags & FBF_PUFFMASTER) proj.master = puff; + if (flags & FBF_PUFFTRACER) proj.tracer = puff; + } + } + } + if (puff && temp) + { + puff.Destroy(); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + 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) + { + let ref = GetPointer(ptr); + + if (range == 0) + range = MISSILERANGE; + + int i; + double bangle; + double bslope = 0.; + int laflags = (flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if (ref != NULL || (flags & CBAF_AIMFACING)) + { + if (!(flags & CBAF_AIMFACING)) + { + A_Face(ref); + } + bangle = self.Angle; + + if (!(flags & CBAF_NOPITCH)) bslope = AimLineAttack (bangle, MISSILERANGE); + if (pufftype == null) pufftype = 'BulletPuff'; + + A_PlaySound(AttackSound, CHAN_WEAPON); + for (i = 0; i < numbullets; i++) + { + double pangle = bangle; + double slope = bslope; + + if (flags & CBAF_EXPLICITANGLE) + { + pangle += spread_xy; + slope += spread_z; + } + else + { + pangle += spread_xy * Random2[cwbullet]() / 255.; + slope += spread_z * Random2[cwbullet]() / 255.; + } + + int damage = damageperbullet; + + if (!(flags & CBAF_NORANDOM)) + damage *= random[cwbullet](1, 3); + + let puff = LineAttack(pangle, range, slope, damage, 'Hitscan', pufftype, laflags); + if (missile != null && pufftype != null) + { + double x = Spawnofs_xy * cos(pangle); + double y = Spawnofs_xy * sin(pangle); + + SetXYZ(Vec3Offset(x, y, 0.)); + let proj = SpawnMissileAngleZSpeed(Pos.Z + GetBobOffset() + Spawnheight, missile, self.Angle, 0, GetDefaultByType(missile).Speed, self, false); + SetXYZ(pos); + + if (proj) + { + bool temp = (puff == null); + if (!puff) + { + puff = LineAttack(pangle, range, slope, 0, 'Hitscan', pufftype, laflags | LAF_NOINTERACT); + } + if (puff) + { + AimBulletMissile(proj, puff, flags, temp, true); + } + } + } + } + } + } + + +} diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index abbc8db71c..ee056aea3a 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -3,11 +3,123 @@ 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", 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 Actor A_FireProjectile(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 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) + { + let player = self.player; + if (!player) return; + + let pawn = PlayerPawn(self); + let weapon = player.ReadyWeapon; + + int i; + double bangle; + double bslope = 0.; + int laflags = (flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if ((flags & FBF_USEAMMO) && weapon && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weapon.DepleteAmmo(weapon.bAltFire, true)) + return; // out of ammo + } + + if (range == 0) range = PLAYERMISSILERANGE; + + if (!(flags & FBF_NOFLASH)) pawn.PlayAttacking2 (); + + if (!(flags & FBF_NOPITCH)) bslope = BulletSlope(); + bangle = self.Angle; + + if (pufftype == NULL) pufftype = 'BulletPuff'; + + if (weapon != NULL) + { + A_PlaySound(weapon.AttackSound, CHAN_WEAPON); + } + + if ((numbullets == 1 && !player.refire) || numbullets == 0) + { + int damage = damageperbullet; + + if (!(flags & FBF_NORANDOM)) + damage *= random[cabullet](1, 3); + + let puff = LineAttack(bangle, range, bslope, damage, 'Hitscan', pufftype, laflags); + + if (missile != null) + { + bool temp = false; + double ang = self.Angle - 90; + Vector2 ofs = AngleToVector(Spawnofs_xy); + Actor proj = SpawnPlayerMissile(missile, bangle, ofs.X, ofs.Y, Spawnheight); + if (proj) + { + if (!puff) + { + temp = true; + puff = LineAttack(bangle, range, bslope, 0, 'Hitscan', pufftype, laflags | LAF_NOINTERACT); + } + AimBulletMissile(proj, puff, flags, temp, false); + } + } + } + else + { + if (numbullets < 0) + numbullets = 1; + for (i = 0; i < numbullets; i++) + { + double pangle = bangle; + double slope = bslope; + + if (flags & FBF_EXPLICITANGLE) + { + pangle += spread_xy; + slope += spread_z; + } + else + { + pangle += spread_xy * Random2[cabullet]() / 255.; + slope += spread_z * Random2[cabullet]() / 255.; + } + + int damage = damageperbullet; + + if (!(flags & FBF_NORANDOM)) + damage *= random[cabullet](1, 3); + + let puff = LineAttack(pangle, range, slope, damage, 'Hitscan', pufftype, laflags); + + if (missile != null) + { + bool temp = false; + double ang = self.Angle - 90; + Vector2 ofs = AngleToVector(Spawnofs_xy); + Actor proj = SpawnPlayerMissile(missile, bangle, ofs.X, ofs.Y, Spawnheight); + if (proj) + { + if (!puff) + { + temp = true; + puff = LineAttack(bangle, range, bslope, 0, 'Hitscan', pufftype, laflags | LAF_NOINTERACT); + } + AimBulletMissile(proj, puff, flags, temp, false); + } + } + } + } + } + + //--------------------------------------------------------------------------- // // PROC A_ReFire From 7bb3855439b09e09e0a45530234da06b849280ba Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 14:16:08 +0100 Subject: [PATCH 11/48] - scriptified A_FireProjectile --- src/p_actionfunctions.cpp | 71 ------------------- .../zscript/inventory/stateprovider.txt | 68 ++++++++++++++++-- 2 files changed, 62 insertions(+), 77 deletions(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 464eb1cb6e..35276fd662 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1626,77 +1626,6 @@ DEFINE_ACTION_FUNCTION(AStateProvider, A_JumpIfNoAmmo) -//========================================================================== -// -// A_FireProjectile -// -//========================================================================== -enum FP_Flags -{ - FPF_AIMATANGLE = 1, - FPF_TRANSFERTRANSLATION = 2, - FPF_NOAUTOAIM = 4, -}; -DEFINE_ACTION_FUNCTION(AStateProvider, A_FireProjectile) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - PARAM_CLASS (ti, AActor); - PARAM_ANGLE (angle); - PARAM_BOOL (useammo); - PARAM_FLOAT (spawnofs_xy); - PARAM_FLOAT (spawnheight); - PARAM_INT (flags); - PARAM_ANGLE (pitch); - - if (!self->player) - ACTION_RETURN_OBJECT(nullptr); - - player_t *player = self->player; - AWeapon *weapon = player->ReadyWeapon; - FTranslatedLineTarget t; - - // Only use ammo if called from a weapon - if (useammo && ACTION_CALL_FROM_PSPRITE() && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) - ACTION_RETURN_OBJECT(nullptr); // out of ammo - } - - if (ti) - { - DAngle ang = self->Angles.Yaw - 90; - DVector2 ofs = ang.ToVector(spawnofs_xy); - DAngle shootangle = self->Angles.Yaw; - - if (flags & FPF_AIMATANGLE) shootangle += angle; - - // Temporarily adjusts the pitch - DAngle saved_player_pitch = self->Angles.Pitch; - self->Angles.Pitch += pitch; - AActor * misl=P_SpawnPlayerMissile (self, ofs.X, ofs.Y, spawnheight, ti, shootangle, &t, NULL, false, (flags & FPF_NOAUTOAIM) != 0); - self->Angles.Pitch = saved_player_pitch; - - // automatic handling of seeker missiles - if (misl) - { - if (flags & FPF_TRANSFERTRANSLATION) - misl->Translation = self->Translation; - if (t.linetarget && !t.unlinked && (misl->flags2 & MF2_SEEKERMISSILE)) - misl->tracer = t.linetarget; - if (!(flags & FPF_AIMATANGLE)) - { - // This original implementation is to aim straight ahead and then offset - // the angle from the resulting direction. - misl->Angles.Yaw += angle; - misl->VelFromAngle(misl->VelXYToSpeed()); - } - } - ACTION_RETURN_OBJECT(misl); - } - ACTION_RETURN_OBJECT(nullptr); -} - - //========================================================================== // // A_CustomPunch diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index ee056aea3a..62d58b4eb0 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -3,7 +3,6 @@ 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", double range = 0, double lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus", sound MeleeSound = 0, sound MissSound = ""); - action native Actor A_FireProjectile(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); @@ -15,7 +14,7 @@ class StateProvider : Inventory native action 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) { - let player = self.player; + let player = player; if (!player) return; let pawn = PlayerPawn(self); @@ -37,7 +36,7 @@ class StateProvider : Inventory native if (!(flags & FBF_NOFLASH)) pawn.PlayAttacking2 (); if (!(flags & FBF_NOPITCH)) bslope = BulletSlope(); - bangle = self.Angle; + bangle = Angle; if (pufftype == NULL) pufftype = 'BulletPuff'; @@ -58,7 +57,7 @@ class StateProvider : Inventory native if (missile != null) { bool temp = false; - double ang = self.Angle - 90; + double ang = Angle - 90; Vector2 ofs = AngleToVector(Spawnofs_xy); Actor proj = SpawnPlayerMissile(missile, bangle, ofs.X, ofs.Y, Spawnheight); if (proj) @@ -102,7 +101,7 @@ class StateProvider : Inventory native if (missile != null) { bool temp = false; - double ang = self.Angle - 90; + double ang = Angle - 90; Vector2 ofs = AngleToVector(Spawnofs_xy); Actor proj = SpawnPlayerMissile(missile, bangle, ofs.X, ofs.Y, Spawnheight); if (proj) @@ -119,6 +118,63 @@ class StateProvider : Inventory native } } + + //========================================================================== + // + // A_FireProjectile + // + //========================================================================== + + action Actor A_FireProjectile(class ti, double spawnangle = 0, bool useammo = true, double spawnofs_xy = 0, double spawnheight = 0, int flags = 0, double pitch = 0) + { + let player = self.player; + if (!player) return null; + + let weapon = player.ReadyWeapon; + + FTranslatedLineTarget t; + + // Only use ammo if called from a weapon + if (useammo && weapon && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weapon.DepleteAmmo(weapon.bAltFire, true)) + return null; // out of ammo + } + + if (ti) + { + double ang = Angle - 90; + Vector2 ofs = AngleToVector(Spawnofs_xy); + double shootangle = Angle; + + if (flags & FPF_AIMATANGLE) shootangle += spawnangle; + + // Temporarily adjusts the pitch + double saved_player_pitch = self.Pitch; + self.Pitch += pitch; + let misl = SpawnPlayerMissile (ti, shootangle, ofs.X, ofs.Y, spawnheight, t, NULL, false, (flags & FPF_NOAUTOAIM) != 0); + self.Pitch = saved_player_pitch; + + // automatic handling of seeker missiles + if (misl) + { + if (flags & FPF_TRANSFERTRANSLATION) + misl.Translation = Translation; + if (t.linetarget && !t.unlinked && misl.bSeekerMissile) + misl.tracer = t.linetarget; + if (!(flags & FPF_AIMATANGLE)) + { + // This original implementation is to aim straight ahead and then offset + // the angle from the resulting direction. + misl.Angle += spawnangle; + misl.VelFromAngle(misl.Vel.XY.Length()); + } + } + return misl; + } + return null; + } + //--------------------------------------------------------------------------- // @@ -130,7 +186,7 @@ class StateProvider : Inventory native action void A_ReFire(statelabel flash = null) { - let player = self.player; + let player = player; bool pending; if (NULL == player) From 5c130737c49f0bea8d1454f655d1e7c3fc9598c8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 14:48:30 +0100 Subject: [PATCH 12/48] - scriptified A_CustomPunch --- src/p_actionfunctions.cpp | 116 ------------------ src/p_enemy.cpp | 65 ---------- src/p_enemy.h | 1 - wadsrc/static/zscript/actor.txt | 1 - wadsrc/static/zscript/actor_attacks.txt | 54 ++++++++ .../zscript/inventory/stateprovider.txt | 93 +++++++++++++- 6 files changed, 146 insertions(+), 184 deletions(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 35276fd662..c7cd2a199d 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1626,122 +1626,6 @@ DEFINE_ACTION_FUNCTION(AStateProvider, A_JumpIfNoAmmo) -//========================================================================== -// -// A_CustomPunch -// -// Berserk is not handled here. That can be done with A_CheckIfInventory -// -//========================================================================== - -enum -{ - CPF_USEAMMO = 1, - CPF_DAGGER = 2, - CPF_PULLIN = 4, - CPF_NORANDOMPUFFZ = 8, - CPF_NOTURN = 16, - CPF_STEALARMOR = 32, -}; - -DEFINE_ACTION_FUNCTION(AStateProvider, A_CustomPunch) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - PARAM_INT (damage); - PARAM_BOOL (norandom); - PARAM_INT (flags); - PARAM_CLASS (pufftype, AActor); - PARAM_FLOAT (range); - PARAM_FLOAT (lifesteal); - PARAM_INT (lifestealmax); - PARAM_CLASS (armorbonustype, AActor); - PARAM_SOUND (MeleeSound); - PARAM_SOUND (MissSound); - - if (!self->player) - return 0; - - player_t *player = self->player; - AWeapon *weapon = player->ReadyWeapon; - - - DAngle angle; - DAngle pitch; - FTranslatedLineTarget t; - int actualdamage; - - if (!norandom) - damage *= pr_cwpunch() % 8 + 1; - - angle = self->Angles.Yaw + pr_cwpunch.Random2() * (5.625 / 256); - if (range == 0) range = DEFMELEERANGE; - pitch = P_AimLineAttack (self, angle, range, &t, 0., ALF_CHECK3D); - - // only use ammo when actually hitting something! - if ((flags & CPF_USEAMMO) && t.linetarget && weapon && ACTION_CALL_FROM_PSPRITE()) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) - return 0; // out of ammo - } - - if (pufftype == NULL) - pufftype = PClass::FindActor(NAME_BulletPuff); - int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); - - P_LineAttack (self, angle, range, pitch, damage, NAME_Melee, pufftype, puffFlags, &t, &actualdamage); - - if (!t.linetarget) - { - if (MissSound) S_Sound(self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); - } - else - { - if (lifesteal > 0 && !(t.linetarget->flags5 & MF5_DONTDRAIN)) - { - if (flags & CPF_STEALARMOR) - { - if (armorbonustype == NULL) - { - armorbonustype = PClass::FindActor("ArmorBonus"); - } - if (armorbonustype != NULL) - { - auto armorbonus = Spawn(armorbonustype); - armorbonus->IntVar(NAME_SaveAmount) *= int(actualdamage * lifesteal); - if (lifestealmax > 0) armorbonus->IntVar("MaxSaveAmount") = lifestealmax; - armorbonus->flags |= MF_DROPPED; - armorbonus->ClearCounters(); - - if (!static_cast(armorbonus)->CallTryPickup(self)) - { - armorbonus->Destroy (); - } - } - } - else - { - P_GiveBody (self, int(actualdamage * lifesteal), lifestealmax); - } - } - if (weapon != NULL) - { - if (MeleeSound) S_Sound(self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - else S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if (!(flags & CPF_NOTURN)) - { - // turn to face target - self->Angles.Yaw = t.angleFromSource; - } - - if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; - if (flags & CPF_DAGGER) P_DaggerAlert (self, t.linetarget); - } - return 0; -} - - //========================================================================== // // customizable railgun attack function diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 12f43e9fc1..4e7499e1b4 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -268,71 +268,6 @@ DEFINE_ACTION_FUNCTION(AActor, SoundAlert) 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; -} - - //---------------------------------------------------------------------------- // // AActor :: CheckMeleeRange diff --git a/src/p_enemy.h b/src/p_enemy.h index bdc044c5c6..0e917e0af6 100644 --- a/src/p_enemy.h +++ b/src/p_enemy.h @@ -46,7 +46,6 @@ struct FLookExParams FState *seestate; }; -void P_DaggerAlert (AActor *target, AActor *emitter); bool P_HitFriend (AActor *self); void P_NoiseAlert (AActor *target, AActor *emmiter, bool splash=false, double maxdist=0); diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 2e9f4be15f..89ee2ed50d 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -584,7 +584,6 @@ class Actor : Thinker native native clearscope int PlayerNumber() const; native void SetFriendPlayer(PlayerInfo player); native void SoundAlert(Actor target, bool splash = false, double maxdist = 0); - native void DaggerAlert(Actor target); native void ClearBounce(); native TerrainDef GetFloorTerrain(); native bool CheckLocalView(int consoleplayer); diff --git a/wadsrc/static/zscript/actor_attacks.txt b/wadsrc/static/zscript/actor_attacks.txt index 3c6c16d0c2..3e4c08f807 100644 --- a/wadsrc/static/zscript/actor_attacks.txt +++ b/wadsrc/static/zscript/actor_attacks.txt @@ -116,5 +116,59 @@ extend class Actor } } + //============================================================================ + // + // P_DaggerAlert + // + //============================================================================ + + void DaggerAlert(Actor target) + { + Actor looker; + + if (LastHeard != NULL) + return; + if (health <= 0) + return; + if (!bIsMonster) + return; + if (bInCombat) + return; + bInCombat = true; + + self.target = target; + let painstate = FindState('Pain', 'Dagger'); + if (painstate != NULL) + { + SetState(painstate); + } + + for (looker = cursector.thinglist; looker != NULL; looker = looker.snext) + { + if (looker == self || looker == target) + continue; + + if (looker.health <= 0) + continue; + + if (!looker.bSeesDaggers) + continue; + + if (!looker.bInCombat) + { + if (!looker.CheckSight(target) && !looker.CheckSight(self)) + continue; + + looker.target = target; + if (looker.SeeSound) + { + looker.A_PlaySound(looker.SeeSound, CHAN_VOICE); + } + looker.SetState(looker.SeeState); + looker.bInCombat = true; + } + } + } + } diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index 62d58b4eb0..25c3ad1f38 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -2,7 +2,6 @@ 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", double range = 0, double lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus", sound MeleeSound = 0, sound MissSound = ""); 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); @@ -175,6 +174,98 @@ class StateProvider : Inventory native return null; } +//========================================================================== +// +// A_CustomPunch +// +// Berserk is not handled here. That can be done with A_CheckIfInventory +// +//========================================================================== + + action 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 = "") + { + let player = self.player; + if (!player) return; + + let weapon = player.ReadyWeapon; + + double angle; + double pitch; + FTranslatedLineTarget t; + int actualdamage; + + if (!norandom) + damage *= random[cwpunch](1, 8); + + angle = self.Angle + random2[cwpunch]() * (5.625 / 256); + if (range == 0) range = DEFMELEERANGE; + pitch = AimLineAttack (angle, range, t, 0., ALF_CHECK3D); + + // only use ammo when actually hitting something! + if ((flags & CPF_USEAMMO) && t.linetarget && weapon && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weapon.DepleteAmmo(weapon.bAltFire, true)) + return; // out of ammo + } + + if (pufftype == NULL) + pufftype = 'BulletPuff'; + int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); + + Actor puff; + [puff, actualdamage] = LineAttack (angle, range, pitch, damage, 'Melee', pufftype, puffFlags, t); + + if (!t.linetarget) + { + if (MissSound) A_PlaySound(MissSound, CHAN_WEAPON); + } + else + { + if (lifesteal > 0 && !(t.linetarget.bDontDrain)) + { + if (flags & CPF_STEALARMOR) + { + if (armorbonustype == NULL) + { + armorbonustype = 'ArmorBonus'; + } + if (armorbonustype != NULL) + { + let armorbonus = ArmorBonus(Spawn(armorbonustype)); + armorbonus.SaveAmount *= int(actualdamage * lifesteal); + if (lifestealmax > 0) armorbonus.MaxSaveAmount = lifestealmax; + armorbonus.bDropped = true; + armorbonus.ClearCounters(); + + if (!armorbonus.CallTryPickup(self)) + { + armorbonus.Destroy (); + } + } + } + else + { + GiveBody (int(actualdamage * lifesteal), lifestealmax); + } + } + if (weapon != NULL) + { + if (MeleeSound) A_PlaySound(MeleeSound, CHAN_WEAPON); + else A_PlaySound(AttackSound, CHAN_WEAPON); + } + + if (!(flags & CPF_NOTURN)) + { + // turn to face target + self.Angle = t.angleFromSource; + } + + if (flags & CPF_PULLIN) self.bJustAttacked = true; + if (flags & CPF_DAGGER) t.linetarget.DaggerAlert (self); + } + } + + //--------------------------------------------------------------------------- // From 4c1b3f81abc5690a2e30c8b288a89f134f0eab2e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 15:12:30 +0100 Subject: [PATCH 13/48] - scriptified A_RailAttack. --- src/p_actionfunctions.cpp | 71 ------------------- src/p_map.cpp | 28 ++++++++ src/scripting/thingdef_data.cpp | 5 ++ wadsrc/static/zscript/actor.txt | 1 + wadsrc/static/zscript/base.txt | 21 ++++++ .../zscript/inventory/stateprovider.txt | 55 +++++++++++++- 6 files changed, 107 insertions(+), 74 deletions(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index c7cd2a199d..e1b31a0eab 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1626,77 +1626,6 @@ DEFINE_ACTION_FUNCTION(AStateProvider, A_JumpIfNoAmmo) -//========================================================================== -// -// customizable railgun attack function -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AStateProvider, A_RailAttack) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - PARAM_INT (damage); - PARAM_INT (spawnofs_xy); - PARAM_BOOL (useammo); - PARAM_COLOR (color1); - PARAM_COLOR (color2); - PARAM_INT (flags); - PARAM_FLOAT (maxdiff); - PARAM_CLASS (pufftype, AActor); - PARAM_ANGLE (spread_xy); - PARAM_ANGLE (spread_z); - PARAM_FLOAT (range) ; - PARAM_INT (duration); - PARAM_FLOAT (sparsity); - PARAM_FLOAT (driftspeed); - PARAM_CLASS (spawnclass, AActor); - PARAM_FLOAT (spawnofs_z); - PARAM_INT (SpiralOffset); - PARAM_INT (limit); - - if (range == 0) range = 8192; - if (sparsity == 0) sparsity=1.0; - - if (self->player == NULL) - return 0; - - AWeapon *weapon = self->player->ReadyWeapon; - - // only use ammo when actually hitting something! - if (useammo && weapon != NULL && ACTION_CALL_FROM_PSPRITE()) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) - return 0; // out of ammo - } - - if (!(flags & RAF_EXPLICITANGLE)) - { - spread_xy = spread_xy * pr_crailgun.Random2() / 255; - spread_z = spread_z * pr_crailgun.Random2() / 255; - } - - FRailParams p; - p.source = self; - p.damage = damage; - p.offset_xy = spawnofs_xy; - p.offset_z = spawnofs_z; - p.color1 = color1; - p.color2 = color2; - p.maxdiff = maxdiff; - p.flags = flags; - p.puff = pufftype; - p.angleoffset = spread_xy; - p.pitchoffset = spread_z; - p.distance = range; - p.duration = duration; - p.sparsity = sparsity; - p.drift = driftspeed; - p.spawnclass = spawnclass; - p.SpiralOffset = SpiralOffset; - p.limit = limit; - P_RailAttack(&p); - return 0; -} - //========================================================================== // // also for monsters diff --git a/src/p_map.cpp b/src/p_map.cpp index d1670dc373..33916fdb23 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -156,6 +156,25 @@ DEFINE_FIELD_X(FCheckPosition, FCheckPosition, portalstep); DEFINE_FIELD_X(FCheckPosition, FCheckPosition, portalgroup); DEFINE_FIELD_X(FCheckPosition, FCheckPosition, PushTime); +DEFINE_FIELD_X(FRailParams, FRailParams, source); +DEFINE_FIELD_X(FRailParams, FRailParams, damage); +DEFINE_FIELD_X(FRailParams, FRailParams, offset_xy); +DEFINE_FIELD_X(FRailParams, FRailParams, offset_z); +DEFINE_FIELD_X(FRailParams, FRailParams, color1); +DEFINE_FIELD_X(FRailParams, FRailParams, color2); +DEFINE_FIELD_X(FRailParams, FRailParams, maxdiff); +DEFINE_FIELD_X(FRailParams, FRailParams, flags); +DEFINE_FIELD_X(FRailParams, FRailParams, puff); +DEFINE_FIELD_X(FRailParams, FRailParams, angleoffset); +DEFINE_FIELD_X(FRailParams, FRailParams, pitchoffset); +DEFINE_FIELD_X(FRailParams, FRailParams, distance); +DEFINE_FIELD_X(FRailParams, FRailParams, duration); +DEFINE_FIELD_X(FRailParams, FRailParams, sparsity); +DEFINE_FIELD_X(FRailParams, FRailParams, drift); +DEFINE_FIELD_X(FRailParams, FRailParams, spawnclass); +DEFINE_FIELD_X(FRailParams, FRailParams, SpiralOffset); +DEFINE_FIELD_X(FRailParams, FRailParams, limit); + //========================================================================== // // CanCollideWith @@ -5603,6 +5622,15 @@ void P_RailAttack(FRailParams *p) P_DrawRailTrail(source, rail_data.PortalHits, p->color1, p->color2, p->maxdiff, p->flags, p->spawnclass, angle, p->duration, p->sparsity, p->drift, p->SpiralOffset, pitch); } +DEFINE_ACTION_FUNCTION(AActor, RailAttack) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER(p, FRailParams); + p->source = self; + P_RailAttack(p); + return 0; +} + //========================================================================== // // [RH] P_AimCamera diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 019c10b3ae..f5d10a4172 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -948,6 +948,11 @@ void InitThingdef() fcp->Size = sizeof(FCheckPosition); fcp->Align = alignof(FCheckPosition); + //This must also have its size set. + auto frp = NewStruct("FRailParams", nullptr); + frp->Size = sizeof(FRailParams); + frp->Align = alignof(FRailParams); + FieldTable.Clear(); if (FieldTable.Size() == 0) diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 89ee2ed50d..e4eff57aab 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -592,6 +592,7 @@ class Actor : Thinker native native bool IsZeroDamage(); native void ClearInterpolation(); native clearscope Vector3 PosRelative(sector sec) const; + native void RailAttack(FRailParams p); native void HandleSpawnFlags(); native void ExplodeMissile(line lin = null, Actor target = null, bool onsky = false); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index fff6f9ecce..0bf5cc175f 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -958,3 +958,24 @@ struct Shader native native clearscope static void SetUniform3f(PlayerInfo player, string shaderName, string uniformName, vector3 value); native clearscope static void SetUniform1i(PlayerInfo player, string shaderName, string uniformName, int value); } + +struct FRailParams +{ + native int damage; + native double offset_xy; + native double offset_z; + native int color1, color2; + native double maxdiff; + native int flags; + native Class puff; + native double angleoffset; + native double pitchoffset; + native double distance; + native int duration; + native double sparsity; + native double drift; + native Class spawnclass; + native int SpiralOffset; + native int limit; +}; // [RH] Shoot a railgun + diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index 25c3ad1f38..c9d74a27b9 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -2,8 +2,9 @@ class StateProvider : Inventory native { action native state A_JumpIfNoAmmo(statelabel label); - 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 state A_CheckForReload(int counter, statelabel label, bool dontincrement = false); + action native void A_ResetReloadCounter(); //--------------------------------------------------------------------------- // @@ -265,6 +266,55 @@ class StateProvider : Inventory native } } + //========================================================================== + // + // customizable railgun attack function + // + //========================================================================== + + action 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) + { + if (range == 0) range = 8192; + if (sparsity == 0) sparsity=1.0; + + let player = self.player; + if (!player) return; + + let weapon = player.ReadyWeapon; + + if (useammo && weapon != NULL && stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + if (!weapon.DepleteAmmo(weapon.bAltFire, true)) + return; // out of ammo + } + + if (!(flags & RGF_EXPLICITANGLE)) + { + spread_xy = spread_xy * Random2[crailgun]() / 255.; + spread_z = spread_z * Random2[crailgun]() / 255.; + } + + FRailParams p; + p.damage = damage; + p.offset_xy = spawnofs_xy; + p.offset_z = spawnofs_z; + p.color1 = color1; + p.color2 = color2; + p.maxdiff = maxdiff; + p.flags = flags; + p.puff = pufftype; + p.angleoffset = spread_xy; + p.pitchoffset = spread_z; + p.distance = range; + p.duration = duration; + p.sparsity = sparsity; + p.drift = driftspeed; + p.spawnclass = spawnclass; + p.SpiralOffset = SpiralOffset; + p.limit = limit; + self.RailAttack(p); + } + //--------------------------------------------------------------------------- @@ -304,8 +354,7 @@ class StateProvider : Inventory native } } - action native state A_CheckForReload(int counter, statelabel label, bool dontincrement = false); - action native void A_ResetReloadCounter(); + action void A_ClearReFire() { From 6be7fc33f314de04fe7f613b5b5a714f41b4e3bc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 15:42:43 +0100 Subject: [PATCH 14/48] - scriptified 3 more functions in stateprovider. --- src/p_actionfunctions.cpp | 95 ------------------- .../zscript/inventory/stateprovider.txt | 73 +++++++++++++- 2 files changed, 70 insertions(+), 98 deletions(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index e1b31a0eab..db72ad8123 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1602,30 +1602,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_CustomComboAttack) return 0; } -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AStateProvider, A_JumpIfNoAmmo) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - PARAM_STATE_ACTION(jump); - - if (!ACTION_CALL_FROM_PSPRITE() || self->player->ReadyWeapon == nullptr) - { - ACTION_RETURN_STATE(NULL); - } - - if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) - { - ACTION_RETURN_STATE(jump); - } - ACTION_RETURN_STATE(NULL); -} - - - //========================================================================== // // also for monsters @@ -3830,77 +3806,6 @@ DEFINE_ACTION_FUNCTION(AActor, CheckIfInTargetLOS) ACTION_RETURN_BOOL(true); } -//=========================================================================== -// -// Modified code pointer from Skulltag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AStateProvider, A_CheckForReload) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - { - ACTION_RETURN_STATE(NULL); - } - PARAM_INT (count); - PARAM_STATE_ACTION (jump); - PARAM_BOOL (dontincrement); - - if (numret > 0) - { - ret->SetPointer(NULL); - numret = 1; - } - - AWeapon *weapon = self->player->ReadyWeapon; - - int ReloadCounter = weapon->ReloadCounter; - if (!dontincrement || ReloadCounter != 0) - ReloadCounter = (weapon->ReloadCounter+1) % count; - else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. - ReloadCounter = 1; - - // If we have not made our last shot... - if (ReloadCounter != 0) - { - // Go back to the refire frames, instead of continuing on to the reload frames. - if (numret != 0) - { - ret->SetPointer(jump); - } - } - else - { - // We need to reload. However, don't reload if we're out of ammo. - weapon->CheckAmmo(false, false); - } - if (!dontincrement) - { - weapon->ReloadCounter = ReloadCounter; - } - return numret; -} - -//=========================================================================== -// -// Resets the counter for the above function -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AStateProvider, A_ResetReloadCounter) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - - if (self->player == NULL || self->player->ReadyWeapon == NULL) - return 0; - - AWeapon *weapon = self->player->ReadyWeapon; - weapon->ReloadCounter = 0; - return 0; -} - //=========================================================================== // // A_ChangeFlag diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index c9d74a27b9..03cfd9b8d0 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -1,11 +1,78 @@ class StateProvider : Inventory native { - action native state A_JumpIfNoAmmo(statelabel label); action native void A_WeaponReady(int flags = 0); - action native state A_CheckForReload(int counter, statelabel label, bool dontincrement = false); - action native void A_ResetReloadCounter(); + //========================================================================== + // + // State jump function + // + //========================================================================== + + action state A_JumpIfNoAmmo(statelabel label) + { + if (stateinfo == null || stateinfo.mStateType != STATE_Psprite || player == null || player.ReadyWeapon == null || + !player.ReadyWeapon.CheckAmmo(player.ReadyWeapon.bAltFire, false, true)) + { + return null; + } + else return ResolveState(label); + } + + //=========================================================================== + // + // Modified code pointer from Skulltag + // + //=========================================================================== + + action state A_CheckForReload(int count, statelabel jump, bool dontincrement = false) + { + if (stateinfo == null || stateinfo.mStateType != STATE_Psprite || player == null || player.ReadyWeapon == null) + { + return null; + } + + state ret = null; + + let weapon = player.ReadyWeapon; + + int ReloadCounter = weapon.ReloadCounter; + if (!dontincrement || ReloadCounter != 0) + ReloadCounter = (weapon.ReloadCounter+1) % count; + else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. + ReloadCounter = 1; + + // If we have not made our last shot... + if (ReloadCounter != 0) + { + // Go back to the refire frames, instead of continuing on to the reload frames. + ret = ResolveState(jump); + } + else + { + // We need to reload. However, don't reload if we're out of ammo. + weapon.CheckAmmo(false, false); + } + if (!dontincrement) + { + weapon.ReloadCounter = ReloadCounter; + } + return ret; + } + + //=========================================================================== + // + // Resets the counter for the above function + // + //=========================================================================== + + action void A_ResetReloadCounter() + { + if (stateinfo != null && stateinfo.mStateType == STATE_Psprite && player != null && player.ReadyWeapon != null) + player.ReadyWeapon.ReloadCounter = 0; + } + + //--------------------------------------------------------------------------- // // From b4c272ddffdca8af6184bb56dc780aacf0097214 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 17:01:12 +0100 Subject: [PATCH 15/48] - scriptified A_SpawnItem(Ex) and A_ThrowGrenade. These were the last native functions referencing AWeapon::DepleteAmmo, so that function is now exclusively on the scripting side. --- src/g_inventory/a_weapons.cpp | 24 -- src/g_inventory/a_weapons.h | 1 - src/p_actionfunctions.cpp | 446 ------------------------ wadsrc/static/zscript/actor.txt | 3 - wadsrc/static/zscript/actor_attacks.txt | 361 +++++++++++++++++++ 5 files changed, 361 insertions(+), 474 deletions(-) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 50e77f44b8..37611c8b04 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -227,30 +227,6 @@ bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int amm return false; } -//=========================================================================== -// -// AWeapon :: DepleteAmmo -// -// Use up some of the weapon's ammo. Returns true if the ammo was successfully -// depleted. If checkEnough is false, then the ammo will always be depleted, -// even if it drops below zero. -// -//=========================================================================== - -bool AWeapon::DepleteAmmo(bool altFire, bool checkEnough, int ammouse) -{ - IFVIRTUAL(AWeapon, DepleteAmmo) - { - VMValue params[] = { (DObject*)this, altFire, checkEnough, ammouse }; - VMReturn ret; - int retval; - ret.IntAt(&retval); - VMCall(func, params, 4, &ret, 1); - return !!retval; - } - return false; -} - //=========================================================================== // // AWeapon :: GetUpState diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 73a9d15ece..f1ab58fd48 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -140,7 +140,6 @@ public: EitherFire }; bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1); - bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1); enum { diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index db72ad8123..50ff36e23b 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1960,452 +1960,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_TakeFromSiblings) ACTION_RETURN_INT(count); } -//=========================================================================== -// -// Common code for A_SpawnItem and A_SpawnItemEx -// -//=========================================================================== - -enum SIX_Flags -{ - SIXF_TRANSFERTRANSLATION = 0x00000001, - SIXF_ABSOLUTEPOSITION = 0x00000002, - SIXF_ABSOLUTEANGLE = 0x00000004, - SIXF_ABSOLUTEVELOCITY = 0x00000008, - SIXF_SETMASTER = 0x00000010, - SIXF_NOCHECKPOSITION = 0x00000020, - SIXF_TELEFRAG = 0x00000040, - SIXF_CLIENTSIDE = 0x00000080, // only used by Skulldronum - SIXF_TRANSFERAMBUSHFLAG = 0x00000100, - SIXF_TRANSFERPITCH = 0x00000200, - SIXF_TRANSFERPOINTERS = 0x00000400, - SIXF_USEBLOODCOLOR = 0x00000800, - SIXF_CLEARCALLERTID = 0x00001000, - SIXF_MULTIPLYSPEED = 0x00002000, - SIXF_TRANSFERSCALE = 0x00004000, - SIXF_TRANSFERSPECIAL = 0x00008000, - SIXF_CLEARCALLERSPECIAL = 0x00010000, - SIXF_TRANSFERSTENCILCOL = 0x00020000, - SIXF_TRANSFERALPHA = 0x00040000, - SIXF_TRANSFERRENDERSTYLE = 0x00080000, - SIXF_SETTARGET = 0x00100000, - SIXF_SETTRACER = 0x00200000, - SIXF_NOPOINTERS = 0x00400000, - SIXF_ORIGINATOR = 0x00800000, - SIXF_TRANSFERSPRITEFRAME = 0x01000000, - SIXF_TRANSFERROLL = 0x02000000, - SIXF_ISTARGET = 0x04000000, - SIXF_ISMASTER = 0x08000000, - SIXF_ISTRACER = 0x10000000, -}; - -static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) -{ - if (mo == NULL) - { - return false; - } - AActor *originator = self; - - if (!(mo->flags2 & MF2_DONTTRANSLATE)) - { - if (flags & SIXF_TRANSFERTRANSLATION) - { - mo->Translation = self->Translation; - } - else if (flags & SIXF_USEBLOODCOLOR) - { - // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. - mo->Translation = self->BloodTranslation; - } - } - if (flags & SIXF_TRANSFERPOINTERS) - { - mo->target = self->target; - mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set - mo->tracer = self->tracer; - } - - mo->Angles.Yaw = self->Angles.Yaw; - if (flags & SIXF_TRANSFERPITCH) - { - mo->Angles.Pitch = self->Angles.Pitch; - } - if (!(flags & SIXF_ORIGINATOR)) - { - while (originator && originator->isMissile()) - { - originator = originator->target; - } - } - if (flags & SIXF_TELEFRAG) - { - P_TeleportMove(mo, mo->Pos(), true); - // This is needed to ensure consistent behavior. - // Otherwise it will only spawn if nothing gets telefragged - flags |= SIXF_NOCHECKPOSITION; - } - if (mo->flags3 & MF3_ISMONSTER) - { - if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) - { - // The monster is blocked so don't spawn it at all! - mo->ClearCounters(); - mo->Destroy(); - return false; - } - else if (originator && !(flags & SIXF_NOPOINTERS)) - { - if (originator->flags3 & MF3_ISMONSTER) - { - // If this is a monster transfer all friendliness information - mo->CopyFriendliness(originator, true); - } - else if (originator->player) - { - // A player always spawns a monster friendly to him - mo->flags |= MF_FRIENDLY; - mo->SetFriendPlayer(originator->player); - - AActor * attacker=originator->player->attacker; - if (attacker) - { - if (!(attacker->flags&MF_FRIENDLY) || - (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) - { - // Target the monster which last attacked the player - mo->LastHeard = mo->target = attacker; - } - } - } - } - } - else if (!(flags & SIXF_TRANSFERPOINTERS)) - { - // If this is a missile or something else set the target to the originator - mo->target = originator ? originator : self; - } - if (flags & SIXF_NOPOINTERS) - { - //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. - mo->LastHeard = NULL; //Sanity check. - mo->target = NULL; - mo->master = NULL; - mo->tracer = NULL; - } - if (flags & SIXF_SETMASTER) - { // don't let it attack you (optional)! - mo->master = originator; - } - if (flags & SIXF_SETTARGET) - { - mo->target = originator; - } - if (flags & SIXF_SETTRACER) - { - mo->tracer = originator; - } - if (flags & SIXF_TRANSFERSCALE) - { - mo->Scale = self->Scale; - } - if (flags & SIXF_TRANSFERAMBUSHFLAG) - { - mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); - } - if (flags & SIXF_CLEARCALLERTID) - { - self->RemoveFromHash(); - self->tid = 0; - } - if (flags & SIXF_TRANSFERSPECIAL) - { - mo->special = self->special; - memcpy(mo->args, self->args, sizeof(self->args)); - } - if (flags & SIXF_CLEARCALLERSPECIAL) - { - self->special = 0; - memset(self->args, 0, sizeof(self->args)); - } - if (flags & SIXF_TRANSFERSTENCILCOL) - { - mo->fillcolor = self->fillcolor; - } - if (flags & SIXF_TRANSFERALPHA) - { - mo->Alpha = self->Alpha; - } - if (flags & SIXF_TRANSFERRENDERSTYLE) - { - mo->RenderStyle = self->RenderStyle; - } - - if (flags & SIXF_TRANSFERSPRITEFRAME) - { - mo->sprite = self->sprite; - mo->frame = self->frame; - } - - if (flags & SIXF_TRANSFERROLL) - { - mo->Angles.Roll = self->Angles.Roll; - } - - if (flags & SIXF_ISTARGET) - { - self->target = mo; - } - if (flags & SIXF_ISMASTER) - { - self->master = mo; - } - if (flags & SIXF_ISTRACER) - { - self->tracer = mo; - } - return true; -} - -//=========================================================================== -// -// A_SpawnItem -// -// Spawns an item in front of the caller like Heretic's time bomb -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnItem) -{ - PARAM_ACTION_PROLOGUE(AActor); - PARAM_CLASS (missile, AActor) - PARAM_FLOAT (distance) - PARAM_FLOAT (zheight) - PARAM_BOOL (useammo) - PARAM_BOOL (transfer_translation); - - if (numret > 1) ret[1].SetObject(nullptr); - - if (missile == NULL) - { - if (numret > 0) ret[0].SetInt(false); - return MIN(numret, 2); - } - - // Don't spawn monsters if this actor has been massacred - if (self->DamageType == NAME_Massacre && (GetDefaultByType(missile)->flags3 & MF3_ISMONSTER)) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - - if (ACTION_CALL_FROM_PSPRITE()) - { - // Used from a weapon, so use some ammo - AWeapon *weapon = self->player->ReadyWeapon; - - if (weapon == NULL) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - } - - AActor *mo = Spawn( missile, self->Vec3Angle(distance, self->Angles.Yaw, -self->Floorclip + self->GetBobOffset() + zheight), ALLOW_REPLACE); - - int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); - bool res = InitSpawnedItem(self, mo, flags); // for an inventory item's use state - if (numret > 0) ret[0].SetInt(res); - if (numret > 1) ret[1].SetObject(mo); - return MIN(numret, 2); - -} - -//=========================================================================== -// -// A_SpawnItemEx -// -// Enhanced spawning function -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_SpawnItemEx) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_CLASS (missile, AActor); - PARAM_FLOAT (xofs) - PARAM_FLOAT (yofs) - PARAM_FLOAT (zofs) - PARAM_FLOAT (xvel) - PARAM_FLOAT (yvel) - PARAM_FLOAT (zvel) - PARAM_ANGLE (angle) - PARAM_INT (flags) - PARAM_INT (chance) - PARAM_INT (tid) - - if (numret > 1) ret[1].SetObject(nullptr); - - if (missile == NULL) - { - if (numret > 0) ret[0].SetInt(false); - return MIN(numret, 2); - } - if (chance > 0 && pr_spawnitemex() < chance) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - // Don't spawn monsters if this actor has been massacred - if (self->DamageType == NAME_Massacre && (GetDefaultByType(missile)->flags3 & MF3_ISMONSTER)) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - - DVector2 pos; - - if (!(flags & SIXF_ABSOLUTEANGLE)) - { - angle += self->Angles.Yaw; - } - double s = angle.Sin(); - double c = angle.Cos(); - - if (flags & SIXF_ABSOLUTEPOSITION) - { - pos = self->Vec2Offset(xofs, yofs); - } - else - { - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - pos = self->Vec2Offset(xofs * c + yofs * s, xofs * s - yofs*c); - } - - if (!(flags & SIXF_ABSOLUTEVELOCITY)) - { - // Same orientation issue here! - double newxvel = xvel * c + yvel * s; - yvel = xvel * s - yvel * c; - xvel = newxvel; - } - - AActor *mo = Spawn(missile, DVector3(pos, self->Z() - self->Floorclip + self->GetBobOffset() + zofs), ALLOW_REPLACE); - bool res = InitSpawnedItem(self, mo, flags); - if (res) - { - if (tid != 0) - { - assert(mo->tid == 0); - mo->tid = tid; - mo->AddToHash(); - } - mo->Vel = {xvel, yvel, zvel}; - if (flags & SIXF_MULTIPLYSPEED) - { - mo->Vel *= mo->Speed; - } - mo->Angles.Yaw = angle; - } - if (numret > 0) ret[0].SetInt(res); - if (numret > 1) ret[1].SetObject(mo); - return MIN(numret, 2); -} - -//=========================================================================== -// -// A_ThrowGrenade -// -// Throws a grenade (like Hexen's fighter flechette) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_ThrowGrenade) -{ - PARAM_ACTION_PROLOGUE(AActor); - PARAM_CLASS (missile, AActor); - PARAM_FLOAT (zheight) - PARAM_FLOAT (xyvel) - PARAM_FLOAT (zvel) - PARAM_BOOL (useammo) - - if (numret > 1) ret[1].SetObject(nullptr); - - if (missile == NULL) - { - if (numret > 0) ret[0].SetInt(false); - return MIN(numret, 2); - } - if (ACTION_CALL_FROM_PSPRITE()) - { - // Used from a weapon, so use some ammo - AWeapon *weapon = self->player->ReadyWeapon; - - if (weapon == NULL) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) - { - if (numret > 0) ret[0].SetInt(true); - return MIN(numret, 2); - } - } - - AActor *bo; - - bo = Spawn(missile, - self->PosPlusZ(-self->Floorclip + self->GetBobOffset() + zheight + 35 + (self->player? self->player->crouchoffset : 0.)), - ALLOW_REPLACE); - if (bo) - { - P_PlaySpawnSound(bo, self); - if (xyvel != 0) - bo->Speed = xyvel; - bo->Angles.Yaw = self->Angles.Yaw + (((pr_grenade()&7) - 4) * (360./256.)); - - DAngle pitch = -self->Angles.Pitch; - DAngle angle = bo->Angles.Yaw; - - // There are two vectors we are concerned about here: xy and z. We rotate - // them separately according to the shooter's pitch and then sum them to - // get the final velocity vector to shoot with. - - double xy_xyscale = bo->Speed * pitch.Cos(); - double xy_velz = bo->Speed * pitch.Sin(); - double xy_velx = xy_xyscale * angle.Cos(); - double xy_vely = xy_xyscale * angle.Sin(); - - pitch = self->Angles.Pitch; - double z_xyscale = zvel * pitch.Sin(); - double z_velz = zvel * pitch.Cos(); - double z_velx = z_xyscale * angle.Cos(); - double z_vely = z_xyscale * angle.Sin(); - - bo->Vel.X = xy_velx + z_velx + self->Vel.X / 2; - bo->Vel.Y = xy_vely + z_vely + self->Vel.Y / 2; - bo->Vel.Z = xy_velz + z_velz; - - bo->target = self; - if (!P_CheckMissileSpawn(bo, self->radius)) bo = nullptr; - - if (numret > 0) ret[0].SetInt(true); - if (numret > 1) ret[1].SetObject(bo); - return MIN(numret, 2); - } - else - { - if (numret > 0) ret[0].SetInt(false); - return MIN(numret, 2); - } -} - - //=========================================================================== // // A_Recoil diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index e4eff57aab..1021977e9e 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -1071,8 +1071,6 @@ class Actor : Thinker native 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, Actor A_SpawnItem(class itemtype = "Unknown", double distance = 0, double zheight = 0, bool useammo = true, bool transfer_translation = false); - native bool, Actor 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, bool local = false); @@ -1097,7 +1095,6 @@ class Actor : Thinker native native bool RaiseActor(Actor other, int flags = 0); native bool CanRaise(); native void Revive(); - action native bool, Actor 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, double mindist = 128, double maxdist = 0, int ptr = AAPTR_DEFAULT); diff --git a/wadsrc/static/zscript/actor_attacks.txt b/wadsrc/static/zscript/actor_attacks.txt index 3e4c08f807..44c32d1f98 100644 --- a/wadsrc/static/zscript/actor_attacks.txt +++ b/wadsrc/static/zscript/actor_attacks.txt @@ -170,5 +170,366 @@ extend class Actor } } + //=========================================================================== + // + // Common code for A_SpawnItem and A_SpawnItemEx + // + //=========================================================================== + + bool InitSpawnedItem(Actor mo, int flags) + { + if (mo == NULL) + { + return false; + } + Actor originator = self; + + if (!(mo.bDontTranslate)) + { + if (flags & SXF_TRANSFERTRANSLATION) + { + mo.Translation = Translation; + } + else if (flags & SXF_USEBLOODCOLOR) + { + // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. + mo.Translation = BloodTranslation; + } + } + if (flags & SXF_TRANSFERPOINTERS) + { + mo.target = self.target; + mo.master = self.master; // This will be overridden later if SXF_SETMASTER is set + mo.tracer = self.tracer; + } + + mo.Angle = self.Angle; + if (flags & SXF_TRANSFERPITCH) + { + mo.Pitch = self.Pitch; + } + if (!(flags & SXF_ORIGINATOR)) + { + while (originator && (originator.bMissile || originator.default.bMissile)) + { + originator = originator.target; + } + } + if (flags & SXF_TELEFRAG) + { + mo.TeleportMove(mo.Pos, true); + // This is needed to ensure consistent behavior. + // Otherwise it will only spawn if nothing gets telefragged + flags |= SXF_NOCHECKPOSITION; + } + if (mo.bIsMonster) + { + if (!(flags & SXF_NOCHECKPOSITION) && !mo.TestMobjLocation()) + { + // The monster is blocked so don't spawn it at all! + mo.ClearCounters(); + mo.Destroy(); + return false; + } + else if (originator && !(flags & SXF_NOPOINTERS)) + { + if (originator.bIsMonster) + { + // If this is a monster transfer all friendliness information + mo.CopyFriendliness(originator, true); + } + else if (originator.player) + { + // A player always spawns a monster friendly to him + mo.bFriendly = true; + mo.SetFriendPlayer(originator.player); + + Actor attacker=originator.player.attacker; + if (attacker) + { + if (!(attacker.bFriendly) || + (deathmatch && attacker.FriendPlayer != 0 && attacker.FriendPlayer != mo.FriendPlayer)) + { + // Target the monster which last attacked the player + mo.LastHeard = mo.target = attacker; + } + } + } + } + } + else if (!(flags & SXF_TRANSFERPOINTERS)) + { + // If this is a missile or something else set the target to the originator + mo.target = originator ? originator : self; + } + if (flags & SXF_NOPOINTERS) + { + //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. + mo.LastHeard = NULL; //Sanity check. + mo.target = NULL; + mo.master = NULL; + mo.tracer = NULL; + } + if (flags & SXF_SETMASTER) + { // don't let it attack you (optional)! + mo.master = originator; + } + if (flags & SXF_SETTARGET) + { + mo.target = originator; + } + if (flags & SXF_SETTRACER) + { + mo.tracer = originator; + } + if (flags & SXF_TRANSFERSCALE) + { + mo.Scale = self.Scale; + } + if (flags & SXF_TRANSFERAMBUSHFLAG) + { + mo.bAmbush = bAmbush; + } + if (flags & SXF_CLEARCALLERTID) + { + self.ChangeTid(0); + } + if (flags & SXF_TRANSFERSPECIAL) + { + mo.special = self.special; + mo.args[0] = self.args[0]; + mo.args[1] = self.args[1]; + mo.args[2] = self.args[2]; + mo.args[3] = self.args[3]; + mo.args[4] = self.args[4]; + } + if (flags & SXF_CLEARCALLERSPECIAL) + { + self.special = 0; + mo.args[0] = 0; + mo.args[1] = 0; + mo.args[2] = 0; + mo.args[3] = 0; + mo.args[4] = 0; + } + if (flags & SXF_TRANSFERSTENCILCOL) + { + mo.SetShade(self.fillcolor); + } + if (flags & SXF_TRANSFERALPHA) + { + mo.Alpha = self.Alpha; + } + if (flags & SXF_TRANSFERRENDERSTYLE) + { + mo.RenderStyle = self.RenderStyle; + } + + if (flags & SXF_TRANSFERSPRITEFRAME) + { + mo.sprite = self.sprite; + mo.frame = self.frame; + } + + if (flags & SXF_TRANSFERROLL) + { + mo.Roll = self.Roll; + } + + if (flags & SXF_ISTARGET) + { + self.target = mo; + } + if (flags & SXF_ISMASTER) + { + self.master = mo; + } + if (flags & SXF_ISTRACER) + { + self.tracer = mo; + } + return true; + } + + + //=========================================================================== + // + // A_SpawnItem + // + // Spawns an item in front of the caller like Heretic's time bomb + // + //=========================================================================== + + action bool, Actor A_SpawnItem(class missile = "Unknown", double distance = 0, double zheight = 0, bool useammo = true, bool transfer_translation = false) + { + if (missile == NULL) + { + return false, null; + } + + // Don't spawn monsters if this actor has been massacred + if (DamageType == 'Massacre' && GetDefaultByType(missile).bIsMonster) + { + return true, null; + } + + if (stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + let player = self.player; + if (player == null) return false, null; + let weapon = player.ReadyWeapon; + // Used from a weapon, so use some ammo + + if (weapon == NULL || (useammo && !weapon.DepleteAmmo(weapon.bAltFire))) + { + return true, null; + } + } + + let mo = Spawn(missile, Vec3Angle(distance, Angle, -Floorclip + GetBobOffset() + zheight), ALLOW_REPLACE); + + int flags = (transfer_translation ? SXF_TRANSFERTRANSLATION : 0) + (useammo ? SXF_SETMASTER : 0); + bool res = InitSpawnedItem(mo, flags); // for an inventory item's use state + return res, mo; + } + + //=========================================================================== + // + // A_SpawnItemEx + // + // Enhanced spawning function + // + //=========================================================================== + bool, Actor A_SpawnItemEx(class missile, 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 chance = 0, int tid=0) + { + if (missile == NULL) + { + return false, null; + } + if (chance > 0 && random[spawnitemex]() < chance) + { + return true, null; + } + // Don't spawn monsters if this actor has been massacred + if (DamageType == 'Massacre' && GetDefaultByType(missile).bIsMonster) + { + return true, null; + } + + Vector2 pos; + + if (!(flags & SXF_ABSOLUTEANGLE)) + { + angle += self.Angle; + } + double s = sin(angle); + double c = cos(angle); + + if (flags & SXF_ABSOLUTEPOSITION) + { + pos = Vec2Offset(xofs, yofs); + } + else + { + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + pos = Vec2Offset(xofs * c + yofs * s, xofs * s - yofs*c); + } + + if (!(flags & SXF_ABSOLUTEVELOCITY)) + { + // Same orientation issue here! + double newxvel = xvel * c + yvel * s; + yvel = xvel * s - yvel * c; + xvel = newxvel; + } + + let mo = Spawn(missile, (pos, self.pos.Z - Floorclip + GetBobOffset() + zofs), ALLOW_REPLACE); + bool res = InitSpawnedItem(mo, flags); + if (res) + { + if (tid != 0) + { + mo.ChangeTid(tid); + } + mo.Vel = (xvel, yvel, zvel); + if (flags & SXF_MULTIPLYSPEED) + { + mo.Vel *= mo.Speed; + } + mo.Angle = angle; + } + return res, mo; + } + + + //=========================================================================== + // + // A_ThrowGrenade + // + // Throws a grenade (like Hexen's fighter flechette) + // + //=========================================================================== + action bool, Actor A_ThrowGrenade(class missile, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true) + { + if (missile == NULL) + { + return false, null; + } + if (stateinfo != null && stateinfo.mStateType == STATE_Psprite) + { + let player = self.player; + if (player == null) return false, null; + let weapon = player.ReadyWeapon; + // Used from a weapon, so use some ammo + + if (weapon == NULL || (useammo && !weapon.DepleteAmmo(weapon.bAltFire))) + { + return true, null; + } + } + + let bo = Spawn(missile, pos + (0, 0, (-Floorclip + GetBobOffset() + zheight + 35 + (player? player.crouchoffset : 0.))), ALLOW_REPLACE); + if (bo) + { + bo.PlaySpawnSound(self); + if (xyvel != 0) + bo.Speed = xyvel; + bo.Angle = Angle + (((random[grenade]()&7) - 4) * (360./256.)); + + let pitch = -self.Pitch; + let angle = bo.Angle; + + // There are two vectors we are concerned about here: xy and z. We rotate + // them separately according to the shooter's pitch and then sum them to + // get the final velocity vector to shoot with. + + double xy_xyscale = bo.Speed * cos(pitch); + double xy_velz = bo.Speed * sin(pitch); + double xy_velx = xy_xyscale * cos(angle); + double xy_vely = xy_xyscale * sin(angle); + + pitch = self.Pitch; + double z_xyscale = zvel * sin(pitch); + double z_velz = zvel * cos(pitch); + double z_velx = z_xyscale * cos(angle); + double z_vely = z_xyscale * sin(angle); + + bo.Vel.X = xy_velx + z_velx + Vel.X / 2; + bo.Vel.Y = xy_vely + z_vely + Vel.Y / 2; + bo.Vel.Z = xy_velz + z_velz; + + bo.target = self; + if (!bo.CheckMissileSpawn(radius)) bo = null; + return true, bo; + } + else + { + return false, null; + } + } + + + } From 9584e3bc53f6250a8cffa7dbd466ab6ae561288b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 17:22:59 +0100 Subject: [PATCH 16/48] - scriptified P_BringUpWeapon because this was the only native function still referencing AWeapon::GetReadyState. --- src/g_inventory/a_weapons.cpp | 21 -------- src/g_inventory/a_weapons.h | 1 - src/p_pspr.cpp | 51 ++----------------- src/scripting/vm/vm.h | 10 +--- wadsrc/static/zscript/inventory/weapons.txt | 12 ++++- wadsrc/static/zscript/shared/player.txt | 54 +++++++++++++++++++-- 6 files changed, 65 insertions(+), 84 deletions(-) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 37611c8b04..0cd0583bec 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -267,27 +267,6 @@ FState *AWeapon::GetDownState () return nullptr; } -//=========================================================================== -// -// AWeapon :: GetReadyState -// -//=========================================================================== - -FState *AWeapon::GetReadyState () -{ - IFVIRTUAL(AWeapon, GetReadyState) - { - VMValue params[1] = { (DObject*)this }; - VMReturn ret; - FState *retval; - ret.PointerAt((void**)&retval); - VMCall(func, params, 1, &ret, 1); - return retval; - } - return nullptr; -} - - /* Weapon slots ***********************************************************/ //=========================================================================== diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index f1ab58fd48..ea2460b081 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -131,7 +131,6 @@ public: // scripted virtuals. FState *GetUpState (); FState *GetDownState (); - FState *GetReadyState (); enum { diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 76587f6cbd..2eaa4dee1f 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -556,56 +556,11 @@ DEFINE_ACTION_FUNCTION(DPSprite, SetState) void P_BringUpWeapon (player_t *player) { - AWeapon *weapon; - - if (player->PendingWeapon == WP_NOCHANGE) + IFVM(PlayerPawn, BringUpWeapon) { - if (player->ReadyWeapon != nullptr) - { - player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP; - P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetReadyState()); - } - return; + VMValue param = player->mo; + VMCall(func, ¶m, 1, nullptr, 0); } - - weapon = player->PendingWeapon; - - // If the player has a tome of power, use this weapon's powered up - // version, if one is available. - if (weapon != nullptr && - weapon->SisterWeapon && - weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP && - player->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true)) - { - weapon = weapon->SisterWeapon; - } - - player->PendingWeapon = WP_NOCHANGE; - player->ReadyWeapon = weapon; - player->mo->weaponspecial = 0; - - if (weapon != nullptr) - { - if (weapon->UpSound) - { - S_Sound (player->mo, CHAN_WEAPON, weapon->UpSound, 1, ATTN_NORM); - } - player->refire = 0; - - player->GetPSprite(PSP_WEAPON)->y = player->cheats & CF_INSTANTWEAPSWITCH - ? WEAPONTOP : WEAPONBOTTOM; - // make sure that the previous weapon's flash state is terminated. - // When coming here from a weapon drop it may still be active. - P_SetPsprite(player, PSP_FLASH, nullptr); - P_SetPsprite(player, PSP_WEAPON, weapon->GetUpState()); - } -} - -DEFINE_ACTION_FUNCTION(_PlayerInfo, BringUpWeapon) -{ - PARAM_SELF_STRUCT_PROLOGUE(player_t); - P_BringUpWeapon(self); - return 0; } //--------------------------------------------------------------------------- diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index d52dc0f6f9..030388efde 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -678,15 +678,7 @@ FString FStringFormat(VM_ARGS, int offset = 0); #define IFVM(cls, funcname) \ static VMFunction * func = nullptr; \ if (func == nullptr) { \ - func = dyn_cast(RUNTIME_CLASS(cls)->FindSymbol(#funcname, false)); \ - assert(func); \ - } \ - if (func != nullptr) - -#define IFVMNAME(cls, funcname) \ - static VMFunction * func = nullptr; \ - if (func == nullptr) { \ - func = dyn_cast(PClass::FindClass(cls)->FindSymbol(#funcname, false)); \ + PClass::FindFunction(&func, #cls, #funcname); \ assert(func); \ } \ if (func != nullptr) diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 80a892883d..3c4353f9a2 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -109,6 +109,14 @@ class Weapon : StateProvider native return s; } + virtual void PlayUpSound(Actor origin) + { + if (UpSound) + { + origin.A_PlaySound(UpSound, CHAN_WEAPON); + } + } + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) { // Weapons may never return HitObituary by default. Override this if it is needed. @@ -165,7 +173,7 @@ class Weapon : StateProvider native } if (null == player.ReadyWeapon) { - player.BringUpWeapon(); + player.mo.BringUpWeapon(); return; } let psp = player.GetPSprite(PSP_WEAPON); @@ -190,7 +198,7 @@ class Weapon : StateProvider native } // [RH] Clear the flash state. Only needed for Strife. player.SetPsprite(PSP_FLASH, null); - player.BringUpWeapon (); + player.mo.BringUpWeapon (); return; } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 25f025d29d..046d9cfa70 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -421,7 +421,7 @@ class PlayerPawn : Actor native if (player.ReadyWeapon == null) { if (player.PendingWeapon != WP_NOCHANGE) - player.BringUpWeapon(); + player.mo.BringUpWeapon(); } else { @@ -1262,6 +1262,56 @@ class PlayerPawn : Actor native } } + //--------------------------------------------------------------------------- + // + // PROC P_BringUpWeapon + // + // Starts bringing the pending weapon up from the bottom of the screen. + // This is only called to start the rising, not throughout it. + // + //--------------------------------------------------------------------------- + + void BringUpWeapon () + { + if (player.PendingWeapon == WP_NOCHANGE) + { + if (player.ReadyWeapon != null) + { + player.GetPSprite(PSP_WEAPON).y = WEAPONTOP; + player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState()); + } + return; + } + + let weapon = player.PendingWeapon; + + // If the player has a tome of power, use this weapon's powered up + // version, if one is available. + if (weapon != null && + weapon.SisterWeapon && + weapon.SisterWeapon.bPowered_Up && + player.mo.FindInventory ('PowerWeaponLevel2', true)) + { + weapon = weapon.SisterWeapon; + } + + player.PendingWeapon = WP_NOCHANGE; + player.ReadyWeapon = weapon; + player.mo.weaponspecial = 0; + + if (weapon != null) + { + weapon.PlayUpSound(self); + player.refire = 0; + + player.GetPSprite(PSP_WEAPON).y = player.cheats & CF_INSTANTWEAPSWITCH? WEAPONTOP : WEAPONBOTTOM; + // make sure that the previous weapon's flash state is terminated. + // When coming here from a weapon drop it may still be active. + player.SetPsprite(PSP_FLASH, null); + player.SetPsprite(PSP_WEAPON, weapon.GetUpState()); + } + } + //---------------------------------------------------------------------------- // @@ -1503,7 +1553,6 @@ struct PlayerInfo native play // this is what internally is known as player_t native void SetLogNumber (int text); native void SetLogText (String text); native void DropWeapon(); - native void BringUpWeapon(); native bool Resurrect(); native String GetUserName() const; @@ -1567,7 +1616,6 @@ struct PlayerInfo native play // this is what internally is known as player_t { return (mo.ViewHeight + crouchviewdelta - viewheight) / 8; } - } From d8aa39e03e49530eeb660999ad3b2569ece24af7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 17:24:08 +0100 Subject: [PATCH 17/48] - let player_t::Resurrect use P_BringUpWeapon to raise the weapon again instead of doing it directly. This seems a bit safer. --- src/g_inventory/a_weapons.cpp | 20 -------------------- src/g_inventory/a_weapons.h | 1 - src/p_user.cpp | 3 ++- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 0cd0583bec..251a58bcb4 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -227,26 +227,6 @@ bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int amm return false; } -//=========================================================================== -// -// AWeapon :: GetUpState -// -//=========================================================================== - -FState *AWeapon::GetUpState () -{ - IFVIRTUAL(AWeapon, GetUpState) - { - VMValue params[1] = { (DObject*)this }; - VMReturn ret; - FState *retval; - ret.PointerAt((void**)&retval); - VMCall(func, params, 1, &ret, 1); - return retval; - } - return nullptr; -} - //=========================================================================== // // AWeapon :: GetDownState diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index ea2460b081..581629af64 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -129,7 +129,6 @@ public: void Serialize(FSerializer &arc) override; // scripted virtuals. - FState *GetUpState (); FState *GetDownState (); enum diff --git a/src/p_user.cpp b/src/p_user.cpp index 3b3802cc2a..245a9fd211 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -690,7 +690,8 @@ bool player_t::Resurrect() } if (ReadyWeapon != nullptr) { - P_SetPsprite(this, PSP_WEAPON, ReadyWeapon->GetUpState()); + PendingWeapon = ReadyWeapon; + P_BringUpWeapon(this); } if (morphTics) From 595208f2fd0e34be765679299e39384b65de570a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 18:21:40 +0100 Subject: [PATCH 18/48] - exported a few more weapon handling functions so that the native GetDownState stub could be removed. --- src/g_inventory/a_weapons.cpp | 20 --- src/g_inventory/a_weapons.h | 3 - src/p_interaction.cpp | 8 +- src/p_local.h | 1 - src/p_pspr.cpp | 28 ---- src/p_pspr.h | 1 - src/p_user.cpp | 91 +--------- wadsrc/static/zscript/inventory/weapons.txt | 2 +- wadsrc/static/zscript/shared/player.txt | 174 ++++++++++++++++---- 9 files changed, 159 insertions(+), 169 deletions(-) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 251a58bcb4..6408c90c4f 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -227,26 +227,6 @@ bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int amm return false; } -//=========================================================================== -// -// AWeapon :: GetDownState -// -//=========================================================================== - -FState *AWeapon::GetDownState () -{ - IFVIRTUAL(AWeapon, GetDownState) - { - VMValue params[1] = { (DObject*)this }; - VMReturn ret; - FState *retval; - ret.PointerAt((void**)&retval); - VMCall(func, params, 1, &ret, 1); - return retval; - } - return nullptr; -} - /* Weapon slots ***********************************************************/ //=========================================================================== diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 581629af64..747f0bdb35 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -128,9 +128,6 @@ public: void Finalize(FStateDefinitions &statedef) override; void Serialize(FSerializer &arc) override; - // scripted virtuals. - FState *GetDownState (); - enum { PrimaryFire, diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index aba33a5cd7..148f0a4d40 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -599,7 +599,13 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf flags &= ~MF_SOLID; player->playerstate = PST_DEAD; - P_DropWeapon (player); + + IFVM(PlayerPawn, DropWeapon) + { + VMValue param = player->mo; + VMCall(func, ¶m, 1, nullptr, 0); + } + if (this == players[consoleplayer].camera && automapactive) { // don't die in auto map, switch view prior to dying diff --git a/src/p_local.h b/src/p_local.h index 9b41253a9a..66a1b304e2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -80,7 +80,6 @@ extern int bmapnegy; // P_PSPR // void P_SetupPsprites (player_t* curplayer, bool startweaponup); -void P_DropWeapon (player_t* player); // diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 2eaa4dee1f..f0a1a38564 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -563,34 +563,6 @@ void P_BringUpWeapon (player_t *player) } } -//--------------------------------------------------------------------------- -// -// PROC P_DropWeapon -// -// The player died, so put the weapon away. -// -//--------------------------------------------------------------------------- - -void P_DropWeapon (player_t *player) -{ - if (player == nullptr) - { - return; - } - // Since the weapon is dropping, stop blocking switching. - player->WeaponState &= ~WF_DISABLESWITCH; - if ((player->ReadyWeapon != nullptr) && (player->health > 0 || !(player->ReadyWeapon->WeaponFlags & WIF_NODEATHDESELECT))) - { - P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetDownState()); - } -} - -DEFINE_ACTION_FUNCTION(_PlayerInfo, DropWeapon) -{ - PARAM_SELF_STRUCT_PROLOGUE(player_t); - P_DropWeapon(self); - return 0; -} //============================================================================ // // P_BobWeapon diff --git a/src/p_pspr.h b/src/p_pspr.h index c98918ded5..2113a854e5 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -124,7 +124,6 @@ void P_CalcSwing (player_t *player); void P_SetPsprite(player_t *player, PSPLayers id, FState *state, bool pending = false); void P_BringUpWeapon (player_t *player); void P_FireWeapon (player_t *player); -void P_DropWeapon (player_t *player); 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); diff --git a/src/p_user.cpp b/src/p_user.cpp index 245a9fd211..bcc4a4ef90 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1041,73 +1041,6 @@ bool APlayerPawn::UseInventory (AInventory *item) return true; } -//=========================================================================== -// -// APlayerPawn :: BestWeapon -// -// Returns the best weapon a player has, possibly restricted to a single -// type of ammo. -// -//=========================================================================== - -AWeapon *APlayerPawn::BestWeapon(PClassActor *ammotype) -{ - AWeapon *bestMatch = NULL; - int bestOrder = INT_MAX; - AInventory *item; - AWeapon *weap; - bool tomed = NULL != FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true); - - // Find the best weapon the player has. - for (item = Inventory; item != NULL; item = item->Inventory) - { - if (!item->IsKindOf(NAME_Weapon)) - continue; - - weap = static_cast (item); - - // Don't select it if it's worse than what was already found. - if (weap->SelectionOrder > bestOrder) - continue; - - // Don't select it if its primary fire doesn't use the desired ammo. - if (ammotype != NULL && - (weap->Ammo1 == NULL || - weap->Ammo1->GetClass() != ammotype)) - continue; - - // Don't select it if the Tome is active and this isn't the powered-up version. - if (tomed && weap->SisterWeapon != NULL && weap->SisterWeapon->WeaponFlags & WIF_POWERED_UP) - continue; - - // Don't select it if it's powered-up and the Tome is not active. - if (!tomed && weap->WeaponFlags & WIF_POWERED_UP) - continue; - - // Don't select it if there isn't enough ammo to use its primary fire. - if (!(weap->WeaponFlags & WIF_AMMO_OPTIONAL) && - !weap->CheckAmmo (AWeapon::PrimaryFire, false)) - continue; - - // Don't select if if there isn't enough ammo as determined by the weapon's author. - if (weap->MinSelAmmo1 > 0 && (weap->Ammo1 == NULL || weap->Ammo1->Amount < weap->MinSelAmmo1)) - continue; - if (weap->MinSelAmmo2 > 0 && (weap->Ammo2 == NULL || weap->Ammo2->Amount < weap->MinSelAmmo2)) - continue; - - // This weapon is usable! - bestOrder = weap->SelectionOrder; - bestMatch = weap; - } - return bestMatch; -} - -DEFINE_ACTION_FUNCTION(APlayerPawn, BestWeapon) -{ - PARAM_SELF_PROLOGUE(APlayerPawn); - PARAM_CLASS(ammo, AActor); - ACTION_RETURN_POINTER(self->BestWeapon(ammo)); -} //=========================================================================== // // APlayerPawn :: PickNewWeapon @@ -1120,29 +1053,17 @@ DEFINE_ACTION_FUNCTION(APlayerPawn, BestWeapon) AWeapon *APlayerPawn::PickNewWeapon(PClassActor *ammotype) { - AWeapon *best = BestWeapon (ammotype); - - if (best != NULL) + AWeapon *best = nullptr; + IFVM(PlayerPawn, DropWeapon) { - player->PendingWeapon = best; - if (player->ReadyWeapon != NULL) - { - P_DropWeapon(player); - } - else if (player->PendingWeapon != WP_NOCHANGE) - { - P_BringUpWeapon (player); - } + VMValue param = player->mo; + VMReturn ret((void**)&best); + VMCall(func, ¶m, 1, &ret, 1); } + return best; } -DEFINE_ACTION_FUNCTION(APlayerPawn, PickNewWeapon) -{ - PARAM_SELF_PROLOGUE(APlayerPawn); - PARAM_CLASS(ammo, AActor); - ACTION_RETURN_POINTER(self->PickNewWeapon(ammo)); -} //=========================================================================== // // APlayerPawn :: GiveDeathmatchInventory diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 3c4353f9a2..dd63a61592 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -218,7 +218,7 @@ class Weapon : StateProvider native } if (player.PendingWeapon != WP_NOCHANGE) { - player.DropWeapon(); + player.mo.DropWeapon(); return; } if (player.ReadyWeapon == null) diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 046d9cfa70..92c41ea6a1 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -382,7 +382,7 @@ class PlayerPawn : Actor native if ((player.PendingWeapon != WP_NOCHANGE || player.health <= 0) && player.WeaponState & WF_WEAPONSWITCHOK) { - player.DropWeapon(); + DropWeapon(); } } @@ -1312,7 +1312,119 @@ class PlayerPawn : Actor native } } + //=========================================================================== + // + // APlayerPawn :: BestWeapon + // + // Returns the best weapon a player has, possibly restricted to a single + // type of ammo. + // + //=========================================================================== + + Weapon BestWeapon(Class ammotype) + { + Weapon bestMatch = NULL; + int bestOrder = int.max; + Inventory item; + bool tomed = !!FindInventory ('PowerWeaponLevel2', true); + + // Find the best weapon the player has. + for (item = Inv; item != NULL; item = item.Inv) + { + let weap = Weapon(item); + if (weap == null) + continue; + + // Don't select it if it's worse than what was already found. + if (weap.SelectionOrder > bestOrder) + continue; + + // Don't select it if its primary fire doesn't use the desired ammo. + if (ammotype != NULL && + (weap.Ammo1 == NULL || + weap.Ammo1.GetClass() != ammotype)) + continue; + + // Don't select it if the Tome is active and this isn't the powered-up version. + if (tomed && weap.SisterWeapon != NULL && weap.SisterWeapon.bPowered_Up) + continue; + + // Don't select it if it's powered-up and the Tome is not active. + if (!tomed && weap.bPowered_Up) + continue; + + // Don't select it if there isn't enough ammo to use its primary fire. + if (!(weap.bAMMO_OPTIONAL) && + !weap.CheckAmmo (Weapon.PrimaryFire, false)) + continue; + + // Don't select if if there isn't enough ammo as determined by the weapon's author. + if (weap.MinSelAmmo1 > 0 && (weap.Ammo1 == NULL || weap.Ammo1.Amount < weap.MinSelAmmo1)) + continue; + if (weap.MinSelAmmo2 > 0 && (weap.Ammo2 == NULL || weap.Ammo2.Amount < weap.MinSelAmmo2)) + continue; + + // This weapon is usable! + bestOrder = weap.SelectionOrder; + bestMatch = weap; + } + return bestMatch; + } + + //--------------------------------------------------------------------------- + // + // PROC P_DropWeapon + // + // The player died, so put the weapon away. + // + //--------------------------------------------------------------------------- + + void DropWeapon () + { + let player = self.player; + if (player == null) + { + return; + } + // Since the weapon is dropping, stop blocking switching. + player.WeaponState &= ~WF_DISABLESWITCH; + Weapon weap = player.ReadyWeapon; + if ((weap != null) && (player.health > 0 || !weap.bNoDeathDeselect)) + { + player.SetPsprite(PSP_WEAPON, weap.GetDownState()); + } + } + + //=========================================================================== + // + // APlayerPawn :: PickNewWeapon + // + // Picks a new weapon for this player. Used mostly for running out of ammo, + // but it also works when an ACS script explicitly takes the ready weapon + // away or the player picks up some ammo they had previously run out of. + // + //=========================================================================== + + Weapon PickNewWeapon(Class ammotype) + { + Weapon best = BestWeapon (ammotype); + + if (best != NULL) + { + player.PendingWeapon = best; + if (player.ReadyWeapon != NULL) + { + DropWeapon(); + } + else if (player.PendingWeapon != WP_NOCHANGE) + { + BringUpWeapon (); + } + } + return best; + } + //---------------------------------------------------------------------------- // // @@ -1327,8 +1439,6 @@ class PlayerPawn : Actor native native void CheckEnvironment(); native void CheckUse(); native void CheckWeaponButtons(); - native Weapon BestWeapon(class ammotype); - native Weapon PickNewWeapon(class ammotype); } class PlayerChunk : PlayerPawn @@ -1523,6 +1633,30 @@ struct PlayerInfo native play // this is what internally is known as player_t native @UserCmd cmd; native readonly @UserCmd original_cmd; + 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) const; + native PSprite FindPSprite(int id) const; + native void SetLogNumber (int text); + native void SetLogText (String text); + native bool Resurrect(); + + native String GetUserName() const; + native Color GetColor() const; + native Color GetDisplayColor() const; + native int GetColorSet() const; + native int GetPlayerClassNum() const; + native int GetSkin() const; + native bool GetNeverSwitch() const; + native int GetGender() const; + native int GetTeam() const; + native float GetAutoaim() const; + native bool GetNoAutostartMap() const; + native void SetFOV(float fov); + native bool GetClassicFlight() const; + native clearscope bool HasWeaponsInSlot(int slot) const; // The actual implementation is on PlayerPawn where it can be overridden. Use that directly in the future. deprecated("3.7") bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) @@ -1543,33 +1677,15 @@ struct PlayerInfo native play // this is what internally is known as player_t } return false; } + + deprecated("3.7") void DropWeapon() + { + if (mo != null) + { + mo.DropWeapon(); + } + } - 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) const; - native PSprite FindPSprite(int id) const; - native void SetLogNumber (int text); - native void SetLogText (String text); - native void DropWeapon(); - native bool Resurrect(); - - native String GetUserName() const; - native Color GetColor() const; - native Color GetDisplayColor() const; - native int GetColorSet() const; - native int GetPlayerClassNum() const; - native int GetSkin() const; - native bool GetNeverSwitch() const; - native int GetGender() const; - native int GetTeam() const; - native float GetAutoaim() const; - native bool GetNoAutostartMap() const; - native void SetFOV(float fov); - native bool GetClassicFlight() const; - native clearscope bool HasWeaponsInSlot(int slot) const; - bool IsTotallyFrozen() { return From 814af66864f78d2f55b4b7599885de9abcbb5afa Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 18:33:42 +0100 Subject: [PATCH 19/48] - exported one FraggleScript function for testing. --- src/fragglescript/t_func.cpp | 43 +---------------- .../static/zscript/scriptutil/scriptutil.txt | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index be6c185201..c16896f318 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -2588,50 +2588,11 @@ void FParser::SF_PlayerAmmo(void) void FParser::SF_MaxPlayerAmmo() { - int playernum, amount; - PClassActor * ammotype; - if (CheckArgs(2)) { - playernum=T_GetPlayerNum(t_argv[0]); - if (playernum==-1) return; - - ammotype=T_GetAmmo(t_argv[1]); - if (!ammotype) return; - - if(t_argc == 2) - { - } - else if(t_argc >= 3) - { - auto iammo = players[playernum].mo->FindInventory(ammotype); - amount = intvalue(t_argv[2]); - if(amount < 0) amount = 0; - if (!iammo) - { - players[playernum].mo->GiveAmmo(ammotype, 1); - iammo = players[playernum].mo->FindInventory(ammotype); - iammo->Amount = 0; - } - iammo->MaxAmount = amount; - - - for (AInventory *item = players[playernum].mo->Inventory; item != NULL; item = item->Inventory) - { - if (item->IsKindOf(NAME_BackpackItem)) - { - if (t_argc>=4) amount = intvalue(t_argv[3]); - else amount*=2; - break; - } - } - iammo->IntVar("BackpackMaxAmount") = amount; - } - t_return.type = svt_int; - AInventory * iammo = players[playernum].mo->FindInventory(ammotype); - if (iammo) t_return.value.i = iammo->MaxAmount; - else t_return.value.i = ((AInventory*)GetDefaultByType(ammotype))->MaxAmount; + t_return.value.i = ScriptUtil::Exec("MaxPlayerAmmo", ScriptUtil::Pointer, T_GetPlayerActor(t_argv[0]), ScriptUtil::Class, T_ClassType(t_argv[1]), + ScriptUtil::Int, t_argc >= 3? intvalue(t_argv[2]) : INT_MIN, ScriptUtil::Int, t_argc >= 4 ? intvalue(t_argv[3]) : INT_MIN, ScriptUtil::End); } } diff --git a/wadsrc/static/zscript/scriptutil/scriptutil.txt b/wadsrc/static/zscript/scriptutil/scriptutil.txt index a47f79486c..d29fb3f342 100644 --- a/wadsrc/static/zscript/scriptutil/scriptutil.txt +++ b/wadsrc/static/zscript/scriptutil/scriptutil.txt @@ -102,4 +102,50 @@ class ScriptUtil play } } + + //========================================================================== + // + // + // + //========================================================================== + + static int PlayerMaxAmmo(Actor activator, class type, int newmaxamount = int.min, int newbpmaxamount = int.min) + { + if (activator == null) return 0; + let ammotype = (class)(type); + if (ammotype == null) return 0; + + if (newmaxamount != int.min) + { + let iammo = Ammo(activator.FindInventory(ammotype)); + if(newmaxamount < 0) newmaxamount = 0; + if (!iammo) + { + activator.GiveAmmo(ammotype, 1); + iammo = Ammo(activator.FindInventory(ammotype)); + if (iammo) + iammo.Amount = 0; + } + + for (Inventory item = activator.Inv; item != NULL; item = item.Inv) + { + if (item is 'BackpackItem') + { + if (newbpmaxamount == int.min) newbpmaxamount = newmaxamount * 2; + break; + } + } + if (iammo) + { + iammo.MaxAmount = newmaxamount; + iammo.BackpackMaxAmount = newbpmaxamount; + } + } + + let rammo = activator.FindInventory(ammotype); + if (rammo) return rammo.maxamount; + else return GetDefaultByType(ammotype).MaxAmount; + } + + } From be100fa5d3ef2a5919d20e6b482166cac8ce1cd8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 18:42:10 +0100 Subject: [PATCH 20/48] - removed MeleeWeapon flag from the tomed PhoenixRod and the fighterhammer. In both cases, having this flag on will render the monster-backing-off-check for melee attacks ineffective because it would misinterpret these weapons as close range only - which they aren't. Even for the PhoenixRod the range is longer than what gets checked here. As a consequence, the bot's check for missile shooting melee weapons has also become pointless because no such weapon is defined anymore. --- src/b_func.cpp | 21 ++----------------- .../static/zscript/heretic/weaponphoenix.txt | 1 - wadsrc/static/zscript/hexen/fighterhammer.txt | 2 +- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/b_func.cpp b/src/b_func.cpp index 3c6425116b..6f8191b95d 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -205,25 +205,8 @@ void DBot::Dofire (ticcmd_t *cmd) //FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go. if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) { - if ((player->ReadyWeapon->ProjectileType != NULL)) - { - if (player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true)) - { - // This weapon can fire a projectile and has enough ammo to do so - goto shootmissile; - } - else if (!(player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL)) - { - // Ammo is required, so don't shoot. This is for weapons that shoot - // missiles that die at close range, such as the powered-up Phoneix Rod. - return; - } - } - else - { - //*4 is for atmosphere, the chainsaws sounding and all.. - no_fire = (Dist > DEFMELEERANGE*4); - } + //*4 is for atmosphere, the chainsaws sounding and all.. + no_fire = (Dist > DEFMELEERANGE*4); } else if (player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) { diff --git a/wadsrc/static/zscript/heretic/weaponphoenix.txt b/wadsrc/static/zscript/heretic/weaponphoenix.txt index 162184f6e2..5feeb46d04 100644 --- a/wadsrc/static/zscript/heretic/weaponphoenix.txt +++ b/wadsrc/static/zscript/heretic/weaponphoenix.txt @@ -73,7 +73,6 @@ class PhoenixRodPowered : PhoenixRod Default { +WEAPON.POWERED_UP - +WEAPON.MELEEWEAPON Weapon.SisterWeapon "PhoenixRod"; Weapon.AmmoGive 0; Tag "$TAG_PHOENIXRODP"; diff --git a/wadsrc/static/zscript/hexen/fighterhammer.txt b/wadsrc/static/zscript/hexen/fighterhammer.txt index 27d85bdabf..82f2040d91 100644 --- a/wadsrc/static/zscript/hexen/fighterhammer.txt +++ b/wadsrc/static/zscript/hexen/fighterhammer.txt @@ -9,7 +9,7 @@ class FWeapHammer : FighterWeapon { +BLOODSPLATTER Weapon.SelectionOrder 900; - +WEAPON.AMMO_OPTIONAL +WEAPON.MELEEWEAPON + +WEAPON.AMMO_OPTIONAL Weapon.AmmoUse1 3; Weapon.AmmoGive1 25; Weapon.KickBack 150; From 44d51a6de9e5bd39dcf0c3f4183dea689da03c65 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 19:03:33 +0100 Subject: [PATCH 21/48] - scriptified GetDefaultInventory. --- src/p_user.cpp | 80 +--------------------- wadsrc/static/zscript/shared/player.txt | 91 +++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 77 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index bcc4a4ef90..e70543a8bb 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1344,84 +1344,10 @@ void APlayerPawn::PlayAttacking2 () void APlayerPawn::GiveDefaultInventory () { - if (player == NULL) return; - - // HexenArmor must always be the first item in the inventory because - // it provides player class based protection that should not affect - // any other protection item. - auto myclass = GetClass(); - GiveInventoryType(PClass::FindActor(NAME_HexenArmor)); - auto harmor = FindInventory(NAME_HexenArmor); - - double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); - double *SlotsIncrement = (double*)harmor->ScriptVar(NAME_SlotsIncrement, nullptr); - Slots[4] = HexenArmor[0]; - for (int i = 0; i < 4; ++i) + IFVIRTUAL(APlayerPawn, GiveDefaultInventory) { - SlotsIncrement[i] = HexenArmor[i + 1]; - } - - // BasicArmor must come right after that. It should not affect any - // other protection item as well but needs to process the damage - // before the HexenArmor does. - auto barmor = (AInventory*)Spawn(NAME_BasicArmor); - barmor->BecomeItem (); - AddInventory (barmor); - - // Now add the items from the DECORATE definition - auto di = GetDropItems(); - - while (di) - { - PClassActor *ti = PClass::FindActor (di->Name); - if (ti) - { - if (!ti->IsDescendantOf(RUNTIME_CLASS(AInventory))) - { - Printf(TEXTCOLOR_ORANGE "%s is not an inventory item and cannot be given to a player as start item.\n", ti->TypeName.GetChars()); - } - else - { - AInventory *item = FindInventory(ti); - if (item != NULL) - { - item->Amount = clamp( - item->Amount + (di->Amount ? di->Amount : ((AInventory *)item->GetDefault())->Amount), - 0, item->MaxAmount); - } - else - { - item = static_cast(Spawn(ti)); - item->ItemFlags |= IF_IGNORESKILL; // no skill multiplicators here - item->Amount = di->Amount; - if (item->IsKindOf(NAME_Weapon)) - { - // To allow better control any weapon is emptied of - // ammo before being given to the player. - static_cast(item)->AmmoGive1 = - static_cast(item)->AmmoGive2 = 0; - } - AActor *check; - if (!item->CallTryPickup(this, &check)) - { - if (check != this) - { - // Player was morphed. This is illegal at game start. - // This problem is only detectable when it's too late to do something about it... - I_Error("Cannot give morph items when starting a game"); - } - item->Destroy(); - item = NULL; - } - } - if (item != NULL && item->IsKindOf(NAME_Weapon) && - static_cast(item)->CheckAmmo(AWeapon::EitherFire, false)) - { - player->ReadyWeapon = player->PendingWeapon = static_cast (item); - } - } - } - di = di->Next; + VMValue params[1] = { (DObject*)this }; + VMCall(func, params, 1, nullptr, 0); } } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 92c41ea6a1..72cee7276c 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1425,6 +1425,97 @@ class PlayerPawn : Actor native return best; } + //=========================================================================== + // + // APlayerPawn :: GiveDefaultInventory + // + //=========================================================================== + + virtual void GiveDefaultInventory () + { + let player = self.player; + if (player == NULL) return; + + // HexenArmor must always be the first item in the inventory because + // it provides player class based protection that should not affect + // any other protection item. + let myclass = GetClass(); + GiveInventoryType('HexenArmor'); + let harmor = HexenArmor(FindInventory('HexenArmor')); + + harmor.Slots[4] = self.HexenArmor[0]; + for (int i = 0; i < 4; ++i) + { + harmor.SlotsIncrement[i] = self.HexenArmor[i + 1]; + } + + // BasicArmor must come right after that. It should not affect any + // other protection item as well but needs to process the damage + // before the HexenArmor does. + let barmor = BasicArmor(Spawn('BasicArmor')); + barmor.BecomeItem (); + AddInventory (barmor); + + // Now add the items from the DECORATE definition + let di = GetDropItems(); + + while (di) + { + Class ti = di.Name; + if (ti) + { + let tinv = (class)(ti); + if (!tinv) + { + Console.Printf(TEXTCOLOR_ORANGE .. "%s is not an inventory item and cannot be given to a player as start item.\n", di.Name); + } + else + { + let item = FindInventory(tinv); + if (item != NULL) + { + item.Amount = clamp( + item.Amount + (di.Amount ? di.Amount : item.default.Amount), 0, item.MaxAmount); + } + else + { + item = Inventory(Spawn(ti)); + item.bIgnoreSkill = true; // no skill multipliers here + item.Amount = di.Amount; + let weap = Weapon(item); + if (weap) + { + // To allow better control any weapon is emptied of + // ammo before being given to the player. + weap.AmmoGive1 = weap.AmmoGive2 = 0; + } + bool res; + Actor check; + [res, check] = item.CallTryPickup(self); + if (check != self) + { + // Player was morphed. This is illegal at game start. + // This problem is only detectable when it's too late to do something about it... + ThrowAbortException("Cannot give morph item '%s' when starting a game!", di.Name); + } + else if (!res) + { + item.Destroy(); + item = NULL; + } + } + let weap = Weapon(item); + if (weap != NULL && weap.CheckAmmo(Weapon.EitherFire, false)) + { + player.ReadyWeapon = player.PendingWeapon = weap; + } + } + } + di = di.Next; + } + } + + //---------------------------------------------------------------------------- // // From 652606f70b89c62cb419fe1f151564a1e0e5df54 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 19:29:52 +0100 Subject: [PATCH 22/48] - scriptified A_Explode and relatives. --- src/p_actionfunctions.cpp | 113 --------------- src/p_enemy.cpp | 14 -- src/p_local.h | 2 +- src/p_map.cpp | 14 +- src/p_mobj.cpp | 23 ---- wadsrc/static/zscript/actor.txt | 68 +-------- wadsrc/static/zscript/actor_attacks.txt | 175 +++++++++++++++++++++++- wadsrc/static/zscript/constants.txt | 10 ++ 8 files changed, 200 insertions(+), 219 deletions(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 50ff36e23b..ff375974c3 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1186,119 +1186,6 @@ DEFINE_ACTION_FUNCTION(AActor, CheckInventory) } -//========================================================================== -// -// Parameterized version of A_Explode -// -//========================================================================== - -enum -{ - XF_HURTSOURCE = 1, - XF_NOTMISSILE = 4, - XF_NOACTORTYPE = 1 << 3, - XF_NOSPLASH = 16, -}; - -DEFINE_ACTION_FUNCTION(AActor, A_Explode) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT (damage); - PARAM_INT (distance); - PARAM_INT (flags); - PARAM_BOOL (alert); - PARAM_INT (fulldmgdistance); - PARAM_INT (nails); - PARAM_INT (naildamage); - PARAM_CLASS (pufftype, AActor); - PARAM_NAME (damagetype); - - if (damage < 0) // get parameters from metadata - { - damage = self->IntVar(NAME_ExplosionDamage); - distance = self->IntVar(NAME_ExplosionRadius); - flags = !self->BoolVar(NAME_DontHurtShooter); - alert = false; - } - if (distance <= 0) distance = damage; - - // NailBomb effect, from SMMU but not from its source code: instead it was implemented and - // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html - - if (nails) - { - DAngle ang; - for (int i = 0; i < nails; i++) - { - ang = i*360./nails; - // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim - P_LineAttack(self, ang, MISSILERANGE, 0., - //P_AimLineAttack (self, ang, MISSILERANGE), - naildamage, NAME_Hitscan, pufftype, (self->flags & MF_MISSILE) ? LAF_TARGETISSOURCE : 0); - } - } - - if (!(flags & XF_NOACTORTYPE) && damagetype == NAME_None) - { - damagetype = self->DamageType; - } - - int pflags = 0; - if (flags & XF_HURTSOURCE) pflags |= RADF_HURTSOURCE; - if (flags & XF_NOTMISSILE) pflags |= RADF_SOURCEISSPOT; - - int count = P_RadiusAttack (self, self->target, damage, distance, damagetype, pflags, fulldmgdistance); - if (!(flags & XF_NOSPLASH)) P_CheckSplash(self, distance); - if (alert && self->target != NULL && self->target->player != NULL) - { - P_NoiseAlert(self->target, self); - } - ACTION_RETURN_INT(count); -} - -//========================================================================== -// -// A_RadiusThrust -// -//========================================================================== - -enum -{ - RTF_AFFECTSOURCE = 1, - RTF_NOIMPACTDAMAGE = 2, - RTF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION(AActor, A_RadiusThrust) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT (force); - PARAM_INT (distance); - PARAM_INT (flags); - PARAM_INT (fullthrustdistance); - - bool sourcenothrust = false; - - if (force == 0) force = 128; - if (distance <= 0) distance = abs(force); - - // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. - if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) - { - sourcenothrust = true; - self->target->flags2 &= ~MF2_NODMGTHRUST; - } - - P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); - P_CheckSplash(self, distance); - - if (sourcenothrust) - { - self->target->flags2 |= MF2_NODMGTHRUST; - } - return 0; -} - //========================================================================== // // A_RadiusDamageSelf diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 4e7499e1b4..72e9b6e092 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3409,20 +3409,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_Pain) return 0; } -// -// A_Detonate -// killough 8/9/98: same as A_Explode, except that the damage is variable -// - -DEFINE_ACTION_FUNCTION(AActor, A_Detonate) -{ - PARAM_SELF_PROLOGUE(AActor); - int damage = self->GetMissileDamage(0, 1); - P_RadiusAttack (self, self->target, damage, damage, self->DamageType, RADF_HURTSOURCE); - P_CheckSplash(self, damage); - return 0; -} - bool CheckBossDeath (AActor *actor) { int i; diff --git a/src/p_local.h b/src/p_local.h index 66a1b304e2..26d6f8b644 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -359,7 +359,6 @@ void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff); // hitsc void P_TraceBleed (int damage, AActor *target); // random direction version bool P_HitFloor (AActor *thing); bool P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false); -void P_CheckSplash(AActor *self, double distance); struct FRailParams { @@ -410,6 +409,7 @@ enum RADF_SOURCEISSPOT = 4, RADF_NODAMAGE = 8, RADF_THRUSTZ = 16, + RADF_OLDRADIUSDAMAGE = 32 }; int P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, FName damageType, int flags, int fulldamagedistance=0); diff --git a/src/p_map.cpp b/src/p_map.cpp index 33916fdb23..fc29afb061 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -6229,7 +6229,7 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom // them far too "active." BossBrains also use the old code // because some user levels require they have a height of 16, // which can make them near impossible to hit with the new code. - if ((flags & RADF_NODAMAGE) || !((bombspot->flags5 | thing->flags5) & MF5_OLDRADIUSDMG)) + if (((flags & RADF_NODAMAGE) || !((bombspot->flags5 | thing->flags5) & MF5_OLDRADIUSDMG)) && !(flags & RADF_OLDRADIUSDAMAGE)) { double points = P_GetRadiusDamage(false, bombspot, thing, bombdamage, bombdistance, fulldamagedistance, bombsource == thing); double check = int(points) * bombdamage; @@ -6306,6 +6306,18 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom return count; } +DEFINE_ACTION_FUNCTION(AActor, RadiusAttack) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(bombsource, AActor); + PARAM_INT(bombdamage); + PARAM_INT(bombdistance); + PARAM_NAME(damagetype); + PARAM_INT(flags); + PARAM_INT(fulldamagedistance); + ACTION_RETURN_INT(P_RadiusAttack(self, bombsource, bombdamage, bombdistance, damagetype, flags, fulldamagedistance)); +} + //========================================================================== // // SECTOR HEIGHT CHANGING diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 9d3b7d931c..6ee0e61267 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -6777,29 +6777,6 @@ DEFINE_ACTION_FUNCTION(AActor, HitFloor) ACTION_RETURN_BOOL(P_HitFloor(self)); } -//--------------------------------------------------------------------------- -// -// P_CheckSplash -// -// Checks for splashes caused by explosions -// -//--------------------------------------------------------------------------- - -void P_CheckSplash(AActor *self, double distance) -{ - sector_t *floorsec; - self->Sector->LowestFloorAt(self, &floorsec); - if (self->Z() <= self->floorz + distance && self->floorsector == floorsec && self->Sector->GetHeightSec() == NULL && floorsec->heightsec == NULL) - { - // Explosion splashes never alert monsters. This is because A_Explode has - // a separate parameter for that so this would get in the way of proper - // behavior. - DVector3 pos = self->PosRelative(floorsec); - pos.Z = self->floorz; - P_HitWater (self, floorsec, pos, false, false); - } -} - //--------------------------------------------------------------------------- // // FUNC P_CheckMissileSpawn diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 1021977e9e..49762d994a 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -964,69 +964,6 @@ class Actor : Thinker native native bool A_LineEffect(int boomspecial = 0, int tag = 0); // End of MBF redundant functions. - - //========================================================================== - // - // old customizable attack functions which use actor parameters. - // - //========================================================================== - - private void DoAttack (bool domelee, bool domissile, int MeleeDamage, Sound MeleeSound, Class MissileType,double MissileHeight) - { - let targ = target; - if (targ == NULL) return; - - A_FaceTarget (); - if (domelee && MeleeDamage>0 && CheckMeleeRange ()) - { - int damage = random[CustomMelee](1, 8) * MeleeDamage; - if (MeleeSound) A_PlaySound (MeleeSound, CHAN_WEAPON); - int newdam = targ.DamageMobj (self, self, damage, 'Melee'); - targ.TraceBleed (newdam > 0 ? newdam : damage, self); - } - else if (domissile && MissileType != NULL) - { - // This seemingly senseless code is needed for proper aiming. - double add = MissileHeight + GetBobOffset() - 32; - AddZ(add); - Actor missile = SpawnMissileXYZ (Pos + (0, 0, 32), targ, MissileType, false); - AddZ(-add); - - if (missile) - { - // automatic handling of seeker missiles - if (missile.bSeekerMissile) - { - missile.tracer = targ; - } - missile.CheckMissileSpawn(radius); - } - } - } - - deprecated("2.3") void A_MeleeAttack() - { - DoAttack(true, false, MeleeDamage, MeleeSound, NULL, 0); - } - - deprecated("2.3") void A_MissileAttack() - { - Class MissileType = MissileName; - DoAttack(false, true, 0, 0, MissileType, MissileHeight); - } - - deprecated("2.3") void A_ComboAttack() - { - Class MissileType = MissileName; - DoAttack(true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); - } - - void A_BasicAttack(int melee_damage, sound melee_sound, class missile_type, double missile_height) - { - DoAttack(true, true, melee_damage, melee_sound, missile_type, missile_height); - } - - native void A_MonsterRail(); native void A_Pain(); native void A_NoBlocking(bool drop = true); @@ -1038,7 +975,6 @@ class Actor : Thinker native native void A_VileChase(); native bool A_CheckForResurrection(); native void A_BossDeath(); - native void A_Detonate(); bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0) { return Level.ExecuteSpecial(special, self, null, false, arg1, arg2, arg3, arg4, arg5); @@ -1110,10 +1046,10 @@ class Actor : Thinker native native void A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", 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); - native void A_RadiusThrust(int force = 128, int distance = -1, int flags = RTF_AFFECTSOURCE, int fullthrustdistance = 0); 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 int GetRadiusDamage(Actor thing, int damage, int distance, int fulldmgdistance = 0, bool oldradiusdmg = false); + native int RadiusAttack(Actor bombsource, int bombdamage, int bombdistance, Name bombmod = 'none', int flags = RADF_HURTSOURCE, int fulldamagedistance = 0); + native void A_Stop(); native void A_Respawn(int flags = 1); native void A_RestoreSpecialPosition(); diff --git a/wadsrc/static/zscript/actor_attacks.txt b/wadsrc/static/zscript/actor_attacks.txt index 44c32d1f98..22a2a4386b 100644 --- a/wadsrc/static/zscript/actor_attacks.txt +++ b/wadsrc/static/zscript/actor_attacks.txt @@ -529,7 +529,180 @@ extend class Actor } } + //--------------------------------------------------------------------------- + // + // P_CheckSplash + // + // Checks for splashes caused by explosions + // + //--------------------------------------------------------------------------- + + void CheckSplash(double distance) + { + double floorh; + sector floorsec; + [floorh, floorsec] = curSector.LowestFloorAt(pos.XY); + + if (pos.Z <= floorz + distance && floorsector == floorsec && curSector.GetHeightSec() == NULL && floorsec.heightsec == NULL) + { + // Explosion splashes never alert monsters. This is because A_Explode has + // a separate parameter for that so this would get in the way of proper + // behavior. + Vector3 pos = PosRelative(floorsec); + pos.Z = floorz; + HitWater (floorsec, pos, false, false); + } + } + + //========================================================================== + // + // Parameterized version of A_Explode + // + //========================================================================== + + int A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldmgdistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff", name damagetype = "none") + { + + if (damage < 0) // get parameters from metadata + { + damage = ExplosionDamage; + distance = ExplosionRadius; + flags = !DontHurtShooter; + alert = false; + } + if (distance <= 0) distance = damage; + + // NailBomb effect, from SMMU but not from its source code: instead it was implemented and + // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html + + if (nails) + { + double ang; + for (int i = 0; i < nails; i++) + { + ang = i*360./nails; + // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim + LineAttack(ang, MISSILERANGE, 0., + //P_AimLineAttack (self, ang, MISSILERANGE), + naildamage, 'Hitscan', pufftype, bMissile ? LAF_TARGETISSOURCE : 0); + } + } + + if (!(flags & XF_EXPLICITDAMAGETYPE) && damagetype == 'None') + { + damagetype = self.DamageType; + } + + int pflags = 0; + if (flags & XF_HURTSOURCE) pflags |= RADF_HURTSOURCE; + if (flags & XF_NOTMISSILE) pflags |= RADF_SOURCEISSPOT; + + int count = RadiusAttack (target, damage, distance, damagetype, pflags, fulldmgdistance); + if (!(flags & XF_NOSPLASH)) CheckSplash(distance); + if (alert && target != NULL && target.player != NULL) + { + SoundAlert(target); + } + return count; + } + + //========================================================================== + // + // A_RadiusThrust + // + //========================================================================== + + void A_RadiusThrust(int force = 128, int distance = -1, int flags = RTF_AFFECTSOURCE, int fullthrustdistance = 0) + { + if (force == 0) force = 128; + if (distance <= 0) distance = abs(force); + + bool nothrust = target.bNoDamageThrust; + // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. + if (!(flags & RTF_NOTMISSILE) && target != NULL) + { + target.bNoDamageThrust = false; + } + + RadiusAttack (target, force, distance, DamageType, flags | RADF_NODAMAGE, fullthrustdistance); + CheckSplash(distance); + target.bNoDamageThrust = nothrust; + } + + //========================================================================== + // + // A_Detonate + // killough 8/9/98: same as A_Explode, except that the damage is variable + // + //========================================================================== + + void A_Detonate() + { + int damage = GetMissileDamage(0, 1); + RadiusAttack (target, damage, damage, DamageType, RADF_HURTSOURCE); + CheckSplash(damage); + } + + //========================================================================== + // + // old customizable attack functions which use actor parameters. + // + //========================================================================== + + private void DoAttack (bool domelee, bool domissile, int MeleeDamage, Sound MeleeSound, Class MissileType,double MissileHeight) + { + let targ = target; + if (targ == NULL) return; + + A_FaceTarget (); + if (domelee && MeleeDamage>0 && CheckMeleeRange ()) + { + int damage = random[CustomMelee](1, 8) * MeleeDamage; + if (MeleeSound) A_PlaySound (MeleeSound, CHAN_WEAPON); + int newdam = targ.DamageMobj (self, self, damage, 'Melee'); + targ.TraceBleed (newdam > 0 ? newdam : damage, self); + } + else if (domissile && MissileType != NULL) + { + // This seemingly senseless code is needed for proper aiming. + double add = MissileHeight + GetBobOffset() - 32; + AddZ(add); + Actor missile = SpawnMissileXYZ (Pos + (0, 0, 32), targ, MissileType, false); + AddZ(-add); + + if (missile) + { + // automatic handling of seeker missiles + if (missile.bSeekerMissile) + { + missile.tracer = targ; + } + missile.CheckMissileSpawn(radius); + } + } + } + + deprecated("2.3") void A_MeleeAttack() + { + DoAttack(true, false, MeleeDamage, MeleeSound, NULL, 0); + } + + deprecated("2.3") void A_MissileAttack() + { + Class MissileType = MissileName; + DoAttack(false, true, 0, 0, MissileType, MissileHeight); + } + + deprecated("2.3") void A_ComboAttack() + { + Class MissileType = MissileName; + DoAttack(true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); + } + + void A_BasicAttack(int melee_damage, sound melee_sound, class missile_type, double missile_height) + { + DoAttack(true, true, melee_damage, melee_sound, missile_type, missile_height); + } - } diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index ccb3f48488..fe2c8a901e 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1247,3 +1247,13 @@ enum SPAC SPAC_PlayerActivate = (SPAC_Cross|SPAC_Use|SPAC_Impact|SPAC_Push|SPAC_AnyCross|SPAC_UseThrough|SPAC_UseBack), }; + +enum RadiusDamageFlags +{ + RADF_HURTSOURCE = 1, + RADF_NOIMPACTDAMAGE = 2, + RADF_SOURCEISSPOT = 4, + RADF_NODAMAGE = 8, + RADF_THRUSTZ = 16, + RADF_OLDRADIUSDAMAGE = 32 +}; From cf9cd583104cbcd9f62acfc3ab549863735ff8b9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 19:59:24 +0100 Subject: [PATCH 23/48] - scriptified ModifyDropAmount as a virtual function hierarchy for Inventory and children. --- src/p_enemy.cpp | 59 +------------------ wadsrc/static/zscript/inventory/ammo.txt | 42 +++++++++++++ wadsrc/static/zscript/inventory/inventory.txt | 25 +++++++- wadsrc/static/zscript/inventory/weapons.txt | 50 ++++++++++++++++ 4 files changed, 119 insertions(+), 57 deletions(-) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 72e9b6e092..a87e0c8127 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3210,64 +3210,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_ActiveSound) //--------------------------------------------------------------------------- void ModifyDropAmount(AInventory *inv, int dropamount) { - auto flagmask = IF_IGNORESKILL; - double dropammofactor = G_SkillProperty(SKILLP_DropAmmoFactor); - // Default drop amount is half of regular amount * regular ammo multiplication - if (dropammofactor == -1) + IFVIRTUALPTR(inv, AInventory, ModifyDropAmount) { - dropammofactor = 0.5; - flagmask = ItemFlag(0); + VMValue params[] = { inv, dropamount }; + VMCall(func, params, 2, nullptr, 0); } - - if (dropamount > 0) - { - if (flagmask != 0 && inv->IsKindOf(NAME_Ammo)) - { - inv->Amount = int(dropamount * dropammofactor); - inv->ItemFlags |= IF_IGNORESKILL; - } - else - { - inv->Amount = dropamount; - } - } - else if (inv->IsKindOf (PClass::FindActor(NAME_Ammo))) - { - // Half ammo when dropped by bad guys. - int amount = inv->IntVar("DropAmount"); - if (amount <= 0) - { - amount = MAX(1, int(inv->Amount * dropammofactor)); - } - inv->Amount = amount; - inv->ItemFlags |= flagmask; - } - else if (inv->IsKindOf (PClass::FindActor(NAME_WeaponGiver))) - { - inv->FloatVar("AmmoFactor") = dropammofactor; - inv->ItemFlags |= flagmask; - } - else if (inv->IsKindOf(NAME_Weapon)) - { - // The same goes for ammo from a weapon. - static_cast(inv)->AmmoGive1 = int(static_cast(inv)->AmmoGive1 * dropammofactor); - static_cast(inv)->AmmoGive2 = int(static_cast(inv)->AmmoGive2 * dropammofactor); - inv->ItemFlags |= flagmask; - } - else if (inv->IsKindOf (PClass::FindClass(NAME_DehackedPickup))) - { - // For weapons and ammo modified by Dehacked we need to flag the item. - inv->BoolVar("droppedbymonster") = true; - } -} - -// todo: make this a scripted virtual function so it can better deal with some of the classes involved. -DEFINE_ACTION_FUNCTION(AInventory, ModifyDropAmount) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_INT(dropamount); - ModifyDropAmount(self, dropamount); - return 0; } //--------------------------------------------------------------------------- diff --git a/wadsrc/static/zscript/inventory/ammo.txt b/wadsrc/static/zscript/inventory/ammo.txt index 092a93846b..b08f396010 100644 --- a/wadsrc/static/zscript/inventory/ammo.txt +++ b/wadsrc/static/zscript/inventory/ammo.txt @@ -184,6 +184,48 @@ class Ammo : Inventory return copy; } + //--------------------------------------------------------------------------- + // + // Modifies the drop amount of this item according to the current skill's + // settings (also called by ADehackedPickup::TryPickup) + // + //--------------------------------------------------------------------------- + + override void ModifyDropAmount(int dropamount) + { + bool ignoreskill = true; + double dropammofactor = G_SkillPropertyFloat(SKILLP_DropAmmoFactor); + // Default drop amount is half of regular amount * regular ammo multiplication + if (dropammofactor == -1) + { + dropammofactor = 0.5; + ignoreskill = false; + } + + if (dropamount > 0) + { + if (ignoreskill) + { + self.Amount = int(dropamount * dropammofactor); + bIgnoreSkill = true; + } + else + { + self.Amount = dropamount; + } + } + else + { + // Half ammo when dropped by bad guys. + int amount = self.DropAmount; + if (amount <= 0) + { + amount = MAX(1, int(self.Amount * dropammofactor)); + } + self.Amount = amount; + bIgnoreSkill = ignoreskill; + } + } } diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index d724c90d9d..b903a09b61 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -52,7 +52,6 @@ class Inventory : Actor native native bool DoRespawn(); native void BecomeItem(); native void BecomePickup(); - native void ModifyDropAmount(int dropamount); native static void PrintPickupMessage (bool localview, String str); States(Actor) @@ -917,6 +916,22 @@ class Inventory : Actor native //=========================================================================== virtual void OnDrop (Actor dropper) {} + + //--------------------------------------------------------------------------- + // + // Modifies the drop amount of this item according to the current skill's + // settings (also called by ADehackedPickup::TryPickup) + // + //--------------------------------------------------------------------------- + + virtual void ModifyDropAmount(int dropamount) + { + if (dropamount > 0) + { + Amount = dropamount; + } + } + } //=========================================================================== @@ -1014,6 +1029,14 @@ class DehackedPickup : Inventory } Super.OnDestroy(); } + + override void ModifyDropAmount(int dropamount) + { + // Must forward the adjustment to the real item. + // dropamount is not relevant here because Dehacked cannot change it. + droppedbymonster = true; + } + } //=========================================================================== diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index dd63a61592..9f4196de2d 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -842,6 +842,33 @@ class Weapon : StateProvider native } + //--------------------------------------------------------------------------- + // + // Modifies the drop amount of this item according to the current skill's + // settings (also called by ADehackedPickup::TryPickup) + // + //--------------------------------------------------------------------------- + override void ModifyDropAmount(int dropamount) + { + bool ignoreskill = true; + double dropammofactor = G_SkillPropertyFloat(SKILLP_DropAmmoFactor); + // Default drop amount is half of regular amount * regular ammo multiplication + if (dropammofactor == -1) + { + dropammofactor = 0.5; + ignoreskill = false; + } + + if (dropamount > 0) + { + self.Amount = dropamount; + } + // Adjust the ammo given by this weapon + AmmoGive1 = int(AmmoGive1 * dropammofactor); + AmmoGive2 = int(AmmoGive2 * dropammofactor); + bIgnoreSkill = ignoreskill; + } + } class WeaponGiver : Weapon @@ -905,6 +932,29 @@ class WeaponGiver : Weapon return false; } + //--------------------------------------------------------------------------- + // + // Modifies the drop amount of this item according to the current skill's + // settings (also called by ADehackedPickup::TryPickup) + // + //--------------------------------------------------------------------------- + + override void ModifyDropAmount(int dropamount) + { + bool ignoreskill = true; + double dropammofactor = G_SkillPropertyFloat(SKILLP_DropAmmoFactor); + // Default drop amount is half of regular amount * regular ammo multiplication + if (dropammofactor == -1) + { + dropammofactor = 0.5; + ignoreskill = false; + } + + AmmoFactor = dropammofactor; + bIgnoreSkill = ignoreskill; + } + + } struct WeaponSlots native From fb91982da23f1d9657778d2e31c5291c9b23757c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 20:32:12 +0100 Subject: [PATCH 24/48] - scriptified APlayerPawn::Die and fixed a few things I encountered while doing it. --- src/d_dehacked.cpp | 2 +- src/d_player.h | 1 - src/g_inventory/a_weapons.h | 4 +- src/g_level.cpp | 11 +++ src/g_levellocals.h | 1 + src/intermission/intermission.cpp | 1 - src/p_actionfunctions.cpp | 3 +- src/p_enemy.cpp | 5 +- src/p_enemy.h | 2 +- src/p_saveg.cpp | 1 + src/p_user.cpp | 82 ---------------------- wadsrc/static/zscript/actor.txt | 2 +- wadsrc/static/zscript/base.txt | 2 + wadsrc/static/zscript/constants.txt | 8 +++ wadsrc/static/zscript/shared/morph.txt | 2 +- wadsrc/static/zscript/shared/player.txt | 92 +++++++++++++++++++++++-- 16 files changed, 117 insertions(+), 102 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 2adc83c924..a6b9ba5a94 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -1690,7 +1690,7 @@ static int PatchWeapon (int weapNum) } else if (stricmp (Line1, "Min ammo") == 0) { - info->MinAmmo1 = val; + info->MinSelAmmo1 = val; } else { diff --git a/src/d_player.h b/src/d_player.h index 774aba83d2..cf2fffa0a3 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -94,7 +94,6 @@ public: virtual bool UseInventory (AInventory *item) override; virtual void MarkPrecacheSounds () const override; virtual void BeginPlay () override; - virtual void Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) override; virtual bool UpdateWaterLevel (bool splash) override; bool ResetAirSupply (bool playgasp = true); diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 747f0bdb35..fcc3b1134f 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -119,9 +119,9 @@ public: TObjPtr SisterWeapon; float FOVScale; int Crosshair; // 0 to use player's crosshair - bool GivenAsMorphWeapon; + bool GivenAsMorphWeapon; // *** only accessed from ZScript. - bool bAltFire; // Set when this weapon's alternate fire is used. + bool bAltFire; // *** only accessed from ZScript. Set when this weapon's alternate fire is used. virtual void MarkPrecacheSounds() const; diff --git a/src/g_level.cpp b/src/g_level.cpp index 775d226fa0..392e1e63e0 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1513,6 +1513,7 @@ void G_InitLevelLocals () level.fogdensity = info->fogdensity; level.outsidefogdensity = info->outsidefogdensity; level.skyfog = info->skyfog; + level.deathsequence = info->deathsequence; level.pixelstretch = info->pixelstretch; @@ -2238,6 +2239,7 @@ DEFINE_FIELD(FLevelLocals, fogdensity) DEFINE_FIELD(FLevelLocals, outsidefogdensity) DEFINE_FIELD(FLevelLocals, skyfog) DEFINE_FIELD(FLevelLocals, pixelstretch) +DEFINE_FIELD(FLevelLocals, deathsequence) DEFINE_FIELD_BIT(FLevelLocals, flags, noinventorybar, LEVEL_NOINVENTORYBAR) DEFINE_FIELD_BIT(FLevelLocals, flags, monsterstelefrag, LEVEL_MONSTERSTELEFRAG) DEFINE_FIELD_BIT(FLevelLocals, flags, actownspecial, LEVEL_ACTOWNSPECIAL) @@ -2304,3 +2306,12 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, ChangeSky) R_InitSkyMap(); return 0; } + +DEFINE_ACTION_FUNCTION(FLevelLocals, StartIntermission) +{ + PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals); + PARAM_NAME(seq); + PARAM_INT(state); + F_StartIntermission(seq, (uint8_t)state); + return 0; +} diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 86050b68cc..ffb4aad613 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -175,6 +175,7 @@ struct FLevelLocals int outsidefogdensity; int skyfog; + FName deathsequence; float pixelstretch; float MusicVolume; diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 30ef3fa400..65f31910e0 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -872,7 +872,6 @@ void F_StartIntermission(FName seq, uint8_t state) } } - //========================================================================== // // Called by main loop. diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index ff375974c3..3dea0a8a71 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -4512,8 +4512,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_DropItem) PARAM_INT(amount); PARAM_INT(chance); - P_DropItem(self, spawntype, amount, chance); - return 0; + ACTION_RETURN_OBJECT(P_DropItem(self, spawntype, amount, chance)); } //=========================================================================== diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index a87e0c8127..0e2968d9b9 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3225,7 +3225,7 @@ void ModifyDropAmount(AInventory *inv, int dropamount) CVAR(Int, sv_dropstyle, 0, CVAR_SERVERINFO | CVAR_ARCHIVE); -AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int chance) +AActor *P_DropItem (AActor *source, PClassActor *type, int dropamount, int chance) { if (type != NULL && pr_dropitem() <= chance) { @@ -3276,9 +3276,8 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c return NULL; } } - return inv; } - // we can't really return an AInventory pointer to a non-inventory item here, can we? + return mo; } } return NULL; diff --git a/src/p_enemy.h b/src/p_enemy.h index 0e917e0af6..4dff6f810a 100644 --- a/src/p_enemy.h +++ b/src/p_enemy.h @@ -53,7 +53,7 @@ bool P_CheckMeleeRange2 (AActor *actor); bool P_Move (AActor *actor); bool P_TryWalk (AActor *actor); void P_NewChaseDir (AActor *actor); -AInventory *P_DropItem (AActor *source, PClassActor *type, int special, int chance); +AActor *P_DropItem (AActor *source, PClassActor *type, int special, int chance); void P_TossItem (AActor *item); bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params); void A_Weave(AActor *self, int xyspeed, int zspeed, double xydist, double zdist); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index fa1e6f1053..5436cfbe7b 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -972,6 +972,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) ("level.fogdensity", level.fogdensity) ("level.outsidefogdensity", level.outsidefogdensity) ("level.skyfog", level.skyfog) + ("level.deathsequence", level.deathsequence) ("level.bodyqueslot", level.bodyqueslot) .Array("level.bodyque", level.bodyque, level.BODYQUESIZE); diff --git a/src/p_user.cpp b/src/p_user.cpp index e70543a8bb..e4f964df39 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1351,88 +1351,6 @@ void APlayerPawn::GiveDefaultInventory () } } -//=========================================================================== -// -// APlayerPawn :: Die -// -//=========================================================================== - -void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) -{ - Super::Die (source, inflictor, dmgflags, MeansOfDeath); - - if (player != NULL && player->mo == this) player->bonuscount = 0; - - if (player != NULL && player->mo != this) - { // Make the real player die, too - player->mo->CallDie (source, inflictor, dmgflags, MeansOfDeath); - } - else - { - if (player != NULL && (dmflags2 & DF2_YES_WEAPONDROP)) - { // Voodoo dolls don't drop weapons - AWeapon *weap = player->ReadyWeapon; - if (weap != NULL) - { - AInventory *item; - - // kgDROP - start - modified copy from a_action.cpp - auto di = weap->GetDropItems(); - - if (di != NULL) - { - while (di != NULL) - { - if (di->Name != NAME_None) - { - PClassActor *ti = PClass::FindActor(di->Name); - if (ti) P_DropItem (player->mo, ti, di->Amount, di->Probability); - } - di = di->Next; - } - } else - // kgDROP - end - if (weap->SpawnState != NULL && - weap->SpawnState != ::GetDefault()->SpawnState) - { - item = P_DropItem (this, weap->GetClass(), -1, 256); - if (item != NULL && item->IsKindOf(NAME_Weapon)) - { - if (weap->AmmoGive1 && weap->Ammo1) - { - static_cast(item)->AmmoGive1 = weap->Ammo1->Amount; - } - if (weap->AmmoGive2 && weap->Ammo2) - { - static_cast(item)->AmmoGive2 = weap->Ammo2->Amount; - } - item->ItemFlags |= IF_IGNORESKILL; - } - } - else - { - item = P_DropItem (this, weap->AmmoType1, -1, 256); - if (item != NULL) - { - item->Amount = weap->Ammo1->Amount; - item->ItemFlags |= IF_IGNORESKILL; - } - item = P_DropItem (this, weap->AmmoType2, -1, 256); - if (item != NULL) - { - item->Amount = weap->Ammo2->Amount; - item->ItemFlags |= IF_IGNORESKILL; - } - } - } - } - if (!multiplayer && level.info->deathsequence != NAME_None) - { - F_StartIntermission(level.info->deathsequence, FSTATE_EndingGame); - } - } -} - //=========================================================================== // // A_PlayerScream diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 49762d994a..3db5669f94 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -1070,7 +1070,7 @@ class Actor : Thinker native 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, 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 Actor 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); native void A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = null, name species = "None", int src = AAPTR_DEFAULT, int inflict = AAPTR_DEFAULT); native void A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = null, name species = "None", int src = AAPTR_DEFAULT, int inflict = AAPTR_DEFAULT); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 0bf5cc175f..67c1893cc8 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -655,6 +655,7 @@ struct LevelLocals native native readonly int outsidefogdensity; native readonly int skyfog; native readonly float pixelstretch; + native name deathsequence; // level_info_t *info cannot be done yet. native String GetUDMFString(int type, int index, Name key); @@ -670,6 +671,7 @@ struct LevelLocals native native bool IsJumpingAllowed() const; native bool IsCrouchingAllowed() const; native bool IsFreelookAllowed() const; + native void StartIntermission(Name type, int state) const; native static clearscope bool IsPointInMap(vector3 p); diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index fe2c8a901e..8a1240f106 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1257,3 +1257,11 @@ enum RadiusDamageFlags RADF_THRUSTZ = 16, RADF_OLDRADIUSDAMAGE = 32 }; + +enum IntermissionSequenceType +{ + FSTATE_EndingGame = 0, + FSTATE_ChangingLevel = 1, + FSTATE_InLevel = 2 +}; + diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index 4c67621252..3a16629155 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -560,7 +560,7 @@ extend class PlayerPawn override Actor, int, int MorphedDeath() { // Voodoo dolls should not unmorph the real player here. - if ((player.mo == self) && + if (player && (player.mo == self) && (player.morphTics) && (player.MorphStyle & MRF_UNDOBYDEATH) && (alternative)) diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 72cee7276c..9747ef1dd2 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -154,7 +154,7 @@ class PlayerPawn : Actor native let invul = Powerup(Spawn("PowerInvulnerable")); invul.EffectTics = 3 * TICRATE; invul.BlendColor = 0; // don't mess with the view - invul.bUndroppable = true; // Don't drop this + invul.bUndroppable = true; // Don't drop self bRespawnInvul = true; // [RH] special effect } } @@ -363,7 +363,7 @@ class PlayerPawn : Actor native // // PROC P_CheckWeaponChange // - // The player can change to another weapon at this time. + // The player can change to another weapon at self time. // [GZ] This was cut from P_CheckWeaponFire. // //--------------------------------------------------------------------------- @@ -537,6 +537,84 @@ class PlayerPawn : Actor native } } + //=========================================================================== + // + // APlayerPawn :: Die + // + //=========================================================================== + + override void Die (Actor source, Actor inflictor, int dmgflags, Name MeansOfDeath) + { + Super.Die (source, inflictor, dmgflags, MeansOfDeath); + + if (player != NULL && player.mo == self) player.bonuscount = 0; + + if (player != NULL && player.mo != self) + { // Make the real player die, too + player.mo.Die (source, inflictor, dmgflags, MeansOfDeath); + } + else + { + if (player != NULL && sv_weapondrop) + { // Voodoo dolls don't drop weapons + let weap = player.ReadyWeapon; + if (weap != NULL) + { + // kgDROP - start - modified copy from a_action.cpp + let di = weap.GetDropItems(); + + if (di != NULL) + { + while (di != NULL) + { + if (di.Name != 'None') + { + class ti = di.Name; + if (ti) A_DropItem (ti, di.Amount, di.Probability); + } + di = di.Next; + } + } + else if (weap.SpawnState != NULL && + weap.SpawnState != GetDefaultByType('Actor').SpawnState) + { + let weapitem = Weapon(A_DropItem (weap.GetClass(), -1, 256)); + if (weapitem) + { + if (weap.AmmoGive1 && weap.Ammo1) + { + weapitem.AmmoGive1 = weap.Ammo1.Amount; + } + if (weap.AmmoGive2 && weap.Ammo2) + { + weapitem.AmmoGive2 = weap.Ammo2.Amount; + } + weapitem.bIgnoreSkill = true; + } + } + else + { + let item = Inventory(A_DropItem (weap.AmmoType1, -1, 256)); + if (item != NULL) + { + item.Amount = weap.Ammo1.Amount; + item.bIgnoreSkill = true; + } + item = Inventory(A_DropItem (weap.AmmoType2, -1, 256)); + if (item != NULL) + { + item.Amount = weap.Ammo2.Amount; + item.bIgnoreSkill = true; + } + } + } + } + if (!multiplayer && level.deathsequence != 'None') + { + level.StartIntermission(level.deathsequence, FSTATE_EndingGame); + } + } + } //---------------------------------------------------------------------------- // // PROC P_CheckFOV @@ -1244,7 +1322,7 @@ class PlayerPawn : Actor native CheckUse(); CheckUndoMorph(); // Cycle psprites. - // Note that after this point the PlayerPawn may have changed due to getting unmorphed so 'self' is no longer safe to use. + // Note that after self point the PlayerPawn may have changed due to getting unmorphed so 'self' is no longer safe to use. player.mo.TickPSprites(); // Other Counters if (player.damagecount) player.damagecount--; @@ -1285,7 +1363,7 @@ class PlayerPawn : Actor native let weapon = player.PendingWeapon; - // If the player has a tome of power, use this weapon's powered up + // If the player has a tome of power, use self weapon's powered up // version, if one is available. if (weapon != null && weapon.SisterWeapon && @@ -1345,7 +1423,7 @@ class PlayerPawn : Actor native weap.Ammo1.GetClass() != ammotype)) continue; - // Don't select it if the Tome is active and this isn't the powered-up version. + // Don't select it if the Tome is active and self isn't the powered-up version. if (tomed && weap.SisterWeapon != NULL && weap.SisterWeapon.bPowered_Up) continue; @@ -1400,7 +1478,7 @@ class PlayerPawn : Actor native // // APlayerPawn :: PickNewWeapon // - // Picks a new weapon for this player. Used mostly for running out of ammo, + // Picks a new weapon for self player. Used mostly for running out of ammo, // but it also works when an ACS script explicitly takes the ready weapon // away or the player picks up some ammo they had previously run out of. // @@ -1632,7 +1710,7 @@ enum EPlayerGender GENDER_OTHER } -struct PlayerInfo native play // this is what internally is known as player_t +struct PlayerInfo native play // self is what internally is known as player_t { // technically engine constants but the only part of the playsim using them is the player. const NOFIXEDCOLORMAP = -1; From c14b7f58d3b6e668425d4bff5303012cd72cf73c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 20:33:00 +0100 Subject: [PATCH 25/48] - scriptified some simple sound functions. --- src/p_enemy.cpp | 45 --------------------------------- wadsrc/static/zscript/actor.txt | 31 +++++++++++++++++++---- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 0e2968d9b9..7f6f376a6d 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3157,51 +3157,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail) return 0; } -DEFINE_ACTION_FUNCTION(AActor, A_Scream) -{ - PARAM_SELF_PROLOGUE(AActor); - if (self->DeathSound) - { - // Check for bosses. - if (self->flags2 & MF2_BOSS) - { - // full volume - S_Sound (self, CHAN_VOICE, self->DeathSound, 1, ATTN_NONE); - } - else - { - S_Sound (self, CHAN_VOICE, self->DeathSound, 1, ATTN_NORM); - } - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_XScream) -{ - PARAM_SELF_PROLOGUE(AActor); - if (self->player) - S_Sound (self, CHAN_VOICE, "*gibbed", 1, ATTN_NORM); - else - S_Sound (self, CHAN_VOICE, "misc/gibbed", 1, ATTN_NORM); - return 0; -} - -//=========================================================================== -// -// A_ActiveSound -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ActiveSound) -{ - PARAM_SELF_PROLOGUE(AActor); - if (self->ActiveSound) - { - S_Sound(self, CHAN_VOICE, self->ActiveSound, 1, ATTN_NORM); - } - return 0; -} - //--------------------------------------------------------------------------- // // Modifies the drop amount of this item according to the current skill's diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 3db5669f94..d4783db057 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -968,10 +968,8 @@ class Actor : Thinker native native void A_Pain(); native void A_NoBlocking(bool drop = true); void A_Fall() { A_NoBlocking(); } - native void A_XScream(); native void A_Look(); native void A_Chase(statelabel melee = null, statelabel missile = null, int flags = 0x40000000); - native void A_Scream(); native void A_VileChase(); native bool A_CheckForResurrection(); native void A_BossDeath(); @@ -980,9 +978,6 @@ class Actor : Thinker native return Level.ExecuteSpecial(special, self, null, false, arg1, arg2, arg3, arg4, arg5); } - - native void A_ActiveSound(); - native void A_FastChase(); native void A_PlayerScream(); native void A_SkullPop(class skulltype = "BloodySkull"); @@ -1151,6 +1146,32 @@ class Actor : Thinker native return ACS_ExecuteWithResult(-int(script), arg1, arg2, arg3, arg4); } + //=========================================================================== + // + // Sounds + // + //=========================================================================== + + void A_Scream() + { + if (DeathSound) + { + A_PlaySound(DeathSound, CHAN_VOICE, 1, false, bBoss? ATTN_NONE : ATTN_NORM); + } + } + + void A_XScream() + { + A_PlaySound(player? Sound("*gibbed") : Sound("misc/gibbed"), CHAN_VOICE); + } + + void A_ActiveSound() + { + if (ActiveSound) + { + A_PlaySound(ActiveSound, CHAN_VOICE); + } + } States(Actor, Overlay, Weapon, Item) { From 3d892d397014da2ac98cec829f5719b062bebe8a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 20:58:33 +0100 Subject: [PATCH 26/48] - scriptified FilterCoopRespawnInventory. --- src/d_player.h | 3 +- src/g_statusbar/sbarinfo_commands.cpp | 5 +- src/p_mobj.cpp | 12 ++- src/p_user.cpp | 121 +----------------------- wadsrc/static/zscript/actor.txt | 1 + wadsrc/static/zscript/shared/player.txt | 107 +++++++++++++++++++++ 6 files changed, 124 insertions(+), 125 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index cf2fffa0a3..ad6635c212 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -101,8 +101,7 @@ public: AWeapon *PickNewWeapon (PClassActor *ammotype); AWeapon *BestWeapon (PClassActor *ammotype); void GiveDeathmatchInventory (); - void FilterCoopRespawnInventory (APlayerPawn *oldplayer); - + void GiveDefaultInventory (); // These are virtual on the script side only. diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index 0eba791b37..5a6da4b313 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -597,8 +597,9 @@ class CommandDrawSwitchableImage : public CommandDrawImage auto armor = statusBar->CPlayer->mo->FindInventory(NAME_BasicArmor); if(armor != NULL) { - bool matches1 = armor->NameVar(NAME_ArmorType).GetIndex() == armorType[0] && EvaluateOperation(conditionalOperator[0], conditionalValue[0], armor->Amount); - bool matches2 = armor->NameVar(NAME_ArmorType).GetIndex() == armorType[1] && EvaluateOperation(conditionalOperator[1], conditionalValue[1], armor->Amount); + auto n = armor->NameVar(NAME_ArmorType).GetIndex(); + bool matches1 = n == armorType[0] && EvaluateOperation(conditionalOperator[0], conditionalValue[0], armor->Amount); + bool matches2 = n == armorType[1] && EvaluateOperation(conditionalOperator[1], conditionalValue[1], armor->Amount); drawAlt = 1; if(conditionAnd) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6ee0e61267..7788c9ca98 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1071,6 +1071,12 @@ void AActor::DestroyAllInventory () } } +DEFINE_ACTION_FUNCTION(AActor, DestroyAllInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + self->DestroyAllInventory(); + return 0; +} //============================================================================ // // AActor :: FirstInv @@ -5711,7 +5717,11 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN) || sv_singleplayerrespawn || !!G_SkillProperty(SKILLP_PlayerRespawn)) && state == PST_REBORN && oldactor != NULL) { // Special inventory handling for respawning in coop - p->mo->FilterCoopRespawnInventory (oldactor); + IFVM(PlayerPawn, FilterCoopRespawnInventory) + { + VMValue params[] = { p->mo, oldactor }; + VMCall(func, params, 2, nullptr, 0); + } } if (oldactor != NULL) { // Remove any inventory left from the old actor. Coop handles diff --git a/src/p_user.cpp b/src/p_user.cpp index e4f964df39..a35c66dacd 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1092,125 +1092,6 @@ void APlayerPawn::GiveDeathmatchInventory() } } -//=========================================================================== -// -// APlayerPawn :: FilterCoopRespawnInventory -// -// When respawning in coop, this function is called to walk through the dead -// player's inventory and modify it according to the current game flags so -// that it can be transferred to the new live player. This player currently -// has the default inventory, and the oldplayer has the inventory at the time -// of death. -// -//=========================================================================== - -void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer) -{ - AInventory *item, *next, *defitem; - - // If we're losing everything, this is really simple. - if (dmflags & DF_COOP_LOSE_INVENTORY) - { - oldplayer->DestroyAllInventory(); - return; - } - - if (dmflags & (DF_COOP_LOSE_KEYS | - DF_COOP_LOSE_WEAPONS | - DF_COOP_LOSE_AMMO | - DF_COOP_HALVE_AMMO | - DF_COOP_LOSE_ARMOR | - DF_COOP_LOSE_POWERUPS)) - { - // Walk through the old player's inventory and destroy or modify - // according to dmflags. - for (item = oldplayer->Inventory; item != NULL; item = next) - { - next = item->Inventory; - - // If this item is part of the default inventory, we never want - // to destroy it, although we might want to copy the default - // inventory amount. - defitem = FindInventory (item->GetClass()); - - if ((dmflags & DF_COOP_LOSE_KEYS) && - defitem == NULL && - item->IsKindOf(NAME_Key)) - { - item->Destroy(); - } - else if ((dmflags & DF_COOP_LOSE_WEAPONS) && - defitem == NULL && - item->IsKindOf(NAME_Weapon)) - { - item->Destroy(); - } - else if ((dmflags & DF_COOP_LOSE_ARMOR) && - item->IsKindOf(NAME_Armor)) - { - if (defitem == NULL) - { - item->Destroy(); - } - else if (item->IsKindOf(NAME_BasicArmor)) - { - item->IntVar(NAME_SavePercent) = defitem->IntVar(NAME_SavePercent); - item->Amount = defitem->Amount; - } - else if (item->IsKindOf(NAME_HexenArmor)) - { - double *SlotsTo = (double*)item->ScriptVar(NAME_Slots, nullptr); - double *SlotsFrom = (double*)defitem->ScriptVar(NAME_Slots, nullptr); - memcpy(SlotsTo, SlotsFrom, 4 * sizeof(double)); - } - } - else if ((dmflags & DF_COOP_LOSE_POWERUPS) && - defitem == NULL && - item->IsKindOf(NAME_PowerupGiver)) - { - item->Destroy(); - } - else if ((dmflags & (DF_COOP_LOSE_AMMO | DF_COOP_HALVE_AMMO)) && - item->IsKindOf(NAME_Ammo)) - { - if (defitem == NULL) - { - if (dmflags & DF_COOP_LOSE_AMMO) - { - // Do NOT destroy the ammo, because a weapon might reference it. - item->Amount = 0; - } - else if (item->Amount > 1) - { - item->Amount /= 2; - } - } - else - { - // When set to lose ammo, you get to keep all your starting ammo. - // When set to halve ammo, you won't be left with less than your starting amount. - if (dmflags & DF_COOP_LOSE_AMMO) - { - item->Amount = defitem->Amount; - } - else if (item->Amount > 1) - { - item->Amount = MAX(item->Amount / 2, defitem->Amount); - } - } - } - } - } - - // Now destroy the default inventory this player is holding and move - // over the old player's remaining inventory. - DestroyAllInventory(); - ObtainInventory (oldplayer); - - player->ReadyWeapon = NULL; - PickNewWeapon (NULL); -} - //=========================================================================== // // APlayerPawn :: GetSoundClass @@ -1350,7 +1231,7 @@ void APlayerPawn::GiveDefaultInventory () VMCall(func, params, 1, nullptr, 0); } } - + //=========================================================================== // // A_PlayerScream diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index d4783db057..c2405b40e9 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -727,6 +727,7 @@ class Actor : Thinker native native void AddInventory(Inventory inv); native void RemoveInventory(Inventory inv); native void ClearInventory(); + protected native void DestroyAllInventory(); // This is not supposed to be called by user code! native bool GiveInventory(class type, int amount, bool givecheat = false); native bool SetInventory(class itemclass, int amount, bool beyondMax = false); native bool TakeInventory(class itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 9747ef1dd2..041d1e49d8 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -615,6 +615,113 @@ class PlayerPawn : Actor native } } } + + //=========================================================================== + // + // APlayerPawn :: FilterCoopRespawnInventory + // + // When respawning in coop, this function is called to walk through the dead + // player's inventory and modify it according to the current game flags so + // that it can be transferred to the new live player. This player currently + // has the default inventory, and the oldplayer has the inventory at the time + // of death. + // + //=========================================================================== + + void FilterCoopRespawnInventory (PlayerPawn oldplayer) + { + // If we're losing everything, this is really simple. + if (sv_cooploseinventory) + { + oldplayer.DestroyAllInventory(); + return; + } + + // Walk through the old player's inventory and destroy or modify + // according to dmflags. + Inventory next; + for (Inventory item = oldplayer.Inv; item != NULL; item = next) + { + next = item.Inv; + + // If this item is part of the default inventory, we never want + // to destroy it, although we might want to copy the default + // inventory amount. + let defitem = FindInventory (item.GetClass()); + + if (sv_cooplosekeys && defitem == NULL && item is 'Key') + { + item.Destroy(); + } + else if (sv_cooploseweapons && defitem == NULL && item is 'Weapon') + { + item.Destroy(); + } + else if (sv_cooplosearmor && item is 'Armor') + { + if (defitem == NULL) + { + item.Destroy(); + } + else if (item is 'BasicArmor') + { + BasicArmor(item).SavePercent = BasicArmor(defitem).SavePercent; + item.Amount = defitem.Amount; + } + else if (item is 'HexenArmor') + { + let to = HexenArmor(item); + let from = HexenArmor(defitem); + to.Slots[0] = from.Slots[0]; + to.Slots[1] = from.Slots[1]; + to.Slots[2] = from.Slots[2]; + to.Slots[3] = from.Slots[3]; + } + } + else if (sv_cooplosepowerups && defitem == NULL && item is 'Powerup') + { + item.Destroy(); + } + else if ((sv_cooploseammo || sv_coophalveammo) && item is 'Ammo') + { + if (defitem == NULL) + { + if (sv_cooploseammo) + { + // Do NOT destroy the ammo, because a weapon might reference it. + item.Amount = 0; + } + else if (item.Amount > 1) + { + item.Amount /= 2; + } + } + else + { + // When set to lose ammo, you get to keep all your starting ammo. + // When set to halve ammo, you won't be left with less than your starting amount. + if (sv_cooploseammo) + { + item.Amount = defitem.Amount; + } + else if (item.Amount > 1) + { + item.Amount = MAX(item.Amount / 2, defitem.Amount); + } + } + } + } + + // Now destroy the default inventory this player is holding and move + // over the old player's remaining inventory. + DestroyAllInventory(); + ObtainInventory (oldplayer); + + player.ReadyWeapon = NULL; + PickNewWeapon (NULL); + } + + //---------------------------------------------------------------------------- // // PROC P_CheckFOV From 337750b874e588f91626b29dd304ac4f5838f407 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 21:25:26 +0100 Subject: [PATCH 27/48] - scriptified BecomeItem and BecomePickup --- src/g_inventory/a_pickups.cpp | 65 ------------------- src/g_inventory/a_pickups.h | 3 - wadsrc/static/zscript/inventory/inventory.txt | 51 ++++++++++++++- 3 files changed, 49 insertions(+), 70 deletions(-) diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 7b7721b486..2ed78aef34 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -200,71 +200,6 @@ bool AInventory::Grind(bool items) return Super::Grind(items); } -//=========================================================================== -// -// AInventory :: BecomeItem -// -// Lets this actor know that it's about to be placed in an inventory. -// -//=========================================================================== - -void AInventory::BecomeItem () -{ - if (!(flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) - { - UnlinkFromWorld (nullptr); - flags |= MF_NOBLOCKMAP|MF_NOSECTOR; - LinkToWorld (nullptr); - } - RemoveFromHash (); - flags &= ~MF_SPECIAL; - ChangeStatNum(STAT_INVENTORY); - // stop all sounds this item is playing. - for(int i = 1;i<=7;i++) S_StopSound(this, i); - SetState (FindState("Held")); -} - -DEFINE_ACTION_FUNCTION(AInventory, BecomeItem) -{ - PARAM_SELF_PROLOGUE(AInventory); - self->BecomeItem(); - return 0; -} - -//=========================================================================== -// -// AInventory :: BecomePickup -// -// Lets this actor know it should wait to be picked up. -// -//=========================================================================== - -void AInventory::BecomePickup () -{ - if (Owner != NULL) - { - Owner->RemoveInventory (this); - } - if (flags & (MF_NOBLOCKMAP|MF_NOSECTOR)) - { - UnlinkFromWorld (nullptr); - flags &= ~(MF_NOBLOCKMAP|MF_NOSECTOR); - LinkToWorld (nullptr); - P_FindFloorCeiling (this); - } - flags = (GetDefault()->flags | MF_DROPPED) & ~MF_COUNTITEM; - renderflags &= ~RF_INVISIBLE; - ChangeStatNum(STAT_DEFAULT); - SetState (SpawnState); -} - -DEFINE_ACTION_FUNCTION(AInventory, BecomePickup) -{ - PARAM_SELF_PROLOGUE(AInventory); - self->BecomePickup(); - return 0; -} - //=========================================================================== // // AInventory :: GetSpeedFactor diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 4cb202f7d2..d831bb3de3 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -86,9 +86,6 @@ public: double GetSpeedFactor(); // virtual on the script side. bool GetNoTeleportFreeze(); // virtual on the script side. - void BecomeItem (); - void BecomePickup (); - bool DoRespawn(); AInventory *PrevItem(); // Returns the item preceding this one in the list. diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index b903a09b61..8131190892 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -50,8 +50,6 @@ class Inventory : Actor native } native bool DoRespawn(); - native void BecomeItem(); - native void BecomePickup(); native static void PrintPickupMessage (bool localview, String str); States(Actor) @@ -145,6 +143,55 @@ class Inventory : Actor native } + //=========================================================================== + // + // AInventory :: BecomeItem + // + // Lets this actor know that it's about to be placed in an inventory. + // + //=========================================================================== + + void BecomeItem () + { + if (!bNoBlockmap || !bNoSector) + { + A_ChangeLinkFlags(1, 1); + } + ChangeTid(0); + bSpecial = false; + ChangeStatNum(STAT_INVENTORY); + // stop all sounds this item is playing. + for(int i = 1;i<=7;i++) A_StopSound(i); + SetState (FindState("Held")); + } + + //=========================================================================== + // + // AInventory :: BecomePickup + // + // Lets this actor know it should wait to be picked up. + // + //=========================================================================== + + void BecomePickup () + { + if (Owner != NULL) + { + Owner.RemoveInventory (self); + } + if (bNoBlockmap || bNoSector) + { + A_ChangeLinkFlags(0, 0); + FindFloorCeiling(); + } + bSpecial = true; + bDropped = true; + bCountItem = false; + bInvisible = false; + ChangeStatNum(STAT_DEFAULT); + SetState (SpawnState); + } + //=========================================================================== // // AInventory :: CreateCopy From b6d0d5008e2379c7ac871728ecd1c48c76d4e5f1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 21:37:00 +0100 Subject: [PATCH 28/48] - change teleport freeze handling to a player property plus virtual override on PlayerPawn for increased configurability. --- src/g_inventory/a_pickups.cpp | 49 ------------------------- src/g_inventory/a_pickups.h | 1 - src/p_teleport.cpp | 11 ++++-- wadsrc/static/zscript/shared/player.txt | 21 ++++++++++- 4 files changed, 28 insertions(+), 54 deletions(-) diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 2ed78aef34..33c33413d5 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -200,55 +200,6 @@ bool AInventory::Grind(bool items) return Super::Grind(items); } -//=========================================================================== -// -// AInventory :: GetSpeedFactor -// -//=========================================================================== - -double AInventory::GetSpeedFactor() -{ - double factor = 1.; - auto self = this; - while (self != nullptr) - { - IFVIRTUALPTR(self, AInventory, GetSpeedFactor) - { - VMValue params[1] = { (DObject*)self }; - double retval; - VMReturn ret(&retval); - VMCall(func, params, 1, &ret, 1); - factor *= retval; - } - self = self->Inventory; - } - return factor; -} - -//=========================================================================== -// -// AInventory :: GetNoTeleportFreeze -// -//=========================================================================== - -bool AInventory::GetNoTeleportFreeze () -{ - auto self = this; - while (self != nullptr) - { - IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze) - { - VMValue params[1] = { (DObject*)self }; - int retval; - VMReturn ret(&retval); - VMCall(func, params, 1, &ret, 1); - if (retval) return true; - } - self = self->Inventory; - } - return false; -} - //=========================================================================== // // AInventory :: Use diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index d831bb3de3..97f94bff0f 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -83,7 +83,6 @@ public: void DepleteOrDestroy (); // virtual on the script side. bool CallUse(bool pickup); // virtual on the script side. PalEntry CallGetBlend(); // virtual on the script side. - double GetSpeedFactor(); // virtual on the script side. bool GetNoTeleportFreeze(); // virtual on the script side. bool DoRespawn(); diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 78f21b741f..7618b6165f 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -203,9 +203,14 @@ bool P_Teleport (AActor *thing, DVector3 pos, DAngle angle, int flags) // [BC] && bHaltVelocity. if (thing->player && ((flags & TELF_DESTFOG) || !(flags & TELF_KEEPORIENTATION)) && !(flags & TELF_KEEPVELOCITY)) { - // Freeze player for about .5 sec - if (thing->Inventory == NULL || !thing->Inventory->GetNoTeleportFreeze()) - thing->reactiontime = 18; + int time = 18; + IFVIRTUALPTR(thing, APlayerPawn, GetTeleportFreezeTime) + { + VMValue param = thing; + VMReturn ret(&time); + VMCall(func, ¶m, 1, &ret, 1); + } + thing->reactiontime = time; } if (thing->flags & MF_MISSILE) { diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 041d1e49d8..478d3e8132 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -52,6 +52,7 @@ class PlayerPawn : Actor native meta Name HealingRadiusType; meta Name InvulMode; + meta int TeleportFreezeTime; property prefix: Player; property HealRadiusType: HealingradiusType; @@ -69,6 +70,7 @@ class PlayerPawn : Actor native property MorphWeapon: MorphWeapon; property FlechetteType: FlechetteType; property Portrait: Portrait; + property TeleportFreezeTime: TeleportFreezeTime; Default { @@ -106,6 +108,7 @@ class PlayerPawn : Actor native Player.FlechetteType "ArtiPoisonBag3"; Player.AirCapacity 1; Player.ViewBob 1; + Player.TeleportFreezeTime 18; Obituary "$OB_MPDEFAULT"; } @@ -1700,6 +1703,23 @@ class PlayerPawn : Actor native } } + //=========================================================================== + // + // + // + //=========================================================================== + + virtual int GetTeleportFreezeTime() + { + if (TeleportFreezeTime <= 0) return 0; + let item = inv; + while (item != null) + { + if (item.GetNoTeleportFreeze()) return 0; + item = item.inv; + } + return TeleportFreezeTime; + } //---------------------------------------------------------------------------- // @@ -2009,7 +2029,6 @@ struct PlayerInfo native play // self is what internally is known as player_t return (mo.ViewHeight + crouchviewdelta - viewheight) / 8; } - } struct PlayerClass native From 51ee623b3bdb9eb6bd7ff3fc7ef9a93891e766c0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 22:03:56 +0100 Subject: [PATCH 29/48] - took the weapon selection logic out of the WeaponSlots data and blocked all direct access to the weapon slots internals This seriously needs to be independent from the data store and better abstracted. More work to come to move this to its proper place. --- src/b_func.cpp | 1 - src/d_net.cpp | 2 +- src/g_game.cpp | 228 +++++++++++++++++++++++++- src/g_inventory/a_pickups.cpp | 39 ----- src/g_inventory/a_pickups.h | 2 - src/g_inventory/a_weapons.cpp | 219 +------------------------ src/g_inventory/a_weapons.h | 48 +++++- src/g_shared/shared_hud.cpp | 8 +- src/g_statusbar/sbarinfo_commands.cpp | 4 +- src/p_mobj.cpp | 9 +- src/p_user.cpp | 4 +- 11 files changed, 286 insertions(+), 278 deletions(-) diff --git a/src/b_func.cpp b/src/b_func.cpp index 6f8191b95d..87eaa66b6e 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -233,7 +233,6 @@ void DBot::Dofire (ticcmd_t *cmd) } } // prediction aiming -shootmissile: Dist = player->mo->Distance2D(enemy); fm = Dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed; bglobal.SetBodyAt(enemy->Pos() + enemy->Vel.XY() * fm * 2, 1); diff --git a/src/d_net.cpp b/src/d_net.cpp index 37969fa1de..4c86a664d3 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2631,7 +2631,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player) int count = ReadByte(stream); if (slot < NUM_WEAPON_SLOTS) { - players[pnum].weapons.Slots[slot].Clear(); + players[pnum].weapons.ClearSlot(slot); } for(i = 0; i < count; ++i) { diff --git a/src/g_game.cpp b/src/g_game.cpp index 27aff3abfa..94b9b5a2a3 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -275,6 +275,226 @@ CCMD (turnspeeds) } } + +//=========================================================================== +// +// FWeaponSlot :: PickWeapon +// +// Picks a weapon from this slot. If no weapon is selected in this slot, +// or the first weapon in this slot is selected, returns the last weapon. +// Otherwise, returns the previous weapon in this slot. This means +// precedence is given to the last weapon in the slot, which by convention +// is probably the strongest. Does not return weapons you have no ammo +// for or which you do not possess. +// +//=========================================================================== + +AWeapon *PickWeapon(player_t *player, int slot, bool checkammo) +{ + int i, j; + + if (player->mo == nullptr) + { + return nullptr; + } + int Size = player->weapons.SlotSize(slot); + // Does this slot even have any weapons? + if (Size == 0) + { + return player->ReadyWeapon; + } + if (player->ReadyWeapon != nullptr) + { + for (i = 0; (unsigned)i < Size; i++) + { + auto weapontype = player->weapons.GetWeapon(slot, i); + if (weapontype == player->ReadyWeapon->GetClass() || + (player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP && + player->ReadyWeapon->SisterWeapon != nullptr && + player->ReadyWeapon->SisterWeapon->GetClass() == weapontype)) + { + for (j = (i == 0 ? Size - 1 : i - 1); + j != i; + j = (j == 0 ? Size - 1 : j - 1)) + { + auto weapontype2 = player->weapons.GetWeapon(slot, j); + AWeapon *weap = static_cast (player->mo->FindInventory(weapontype2)); + + if (weap != nullptr && weap->IsKindOf(NAME_Weapon)) + { + if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false)) + { + return weap; + } + } + } + } + } + } + for (i = Size - 1; i >= 0; i--) + { + auto weapontype = player->weapons.GetWeapon(slot, i); + AWeapon *weap = static_cast (player->mo->FindInventory(weapontype)); + + if (weap != nullptr && weap->IsKindOf(NAME_Weapon)) + { + if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false)) + { + return weap; + } + } + } + return player->ReadyWeapon; +} + +//=========================================================================== +// +// FindMostRecentWeapon +// +// Locates the slot and index for the most recently selected weapon. If the +// player is in the process of switching to a new weapon, that is the most +// recently selected weapon. Otherwise, the current weapon is the most recent +// weapon. +// +//=========================================================================== + +static bool FindMostRecentWeapon(player_t *player, int *slot, int *index) +{ + if (player->PendingWeapon != WP_NOCHANGE) + { + return player->weapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index); + } + else if (player->ReadyWeapon != nullptr) + { + AWeapon *weap = player->ReadyWeapon; + if (!player->weapons.LocateWeapon(weap->GetClass(), slot, index)) + { + // If the current weapon wasn't found and is powered up, + // look for its non-powered up version. + if (weap->WeaponFlags & WIF_POWERED_UP && weap->SisterWeaponType != nullptr) + { + return player->weapons.LocateWeapon(weap->SisterWeaponType, slot, index); + } + return false; + } + return true; + } + else + { + return false; + } +} + +//=========================================================================== +// +// FWeaponSlots :: PickNextWeapon +// +// Returns the "next" weapon for this player. If the current weapon is not +// in a slot, then it just returns that weapon, since there's nothing to +// consider it relative to. +// +//=========================================================================== + +AWeapon *PickNextWeapon(player_t *player) +{ + int startslot, startindex; + int slotschecked = 0; + + if (player->mo == nullptr) + { + return nullptr; + } + if (player->ReadyWeapon == nullptr || FindMostRecentWeapon(player, &startslot, &startindex)) + { + int slot; + int index; + + if (player->ReadyWeapon == nullptr) + { + startslot = NUM_WEAPON_SLOTS - 1; + startindex = player->weapons.SlotSize(startslot) - 1; + } + + slot = startslot; + index = startindex; + do + { + if (++index >= player->weapons.SlotSize(slot)) + { + index = 0; + slotschecked++; + if (++slot >= NUM_WEAPON_SLOTS) + { + slot = 0; + } + } + PClassActor *type = player->weapons.GetWeapon(slot, index); + AWeapon *weap = static_cast(player->mo->FindInventory(type)); + if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false)) + { + return weap; + } + } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); + } + return player->ReadyWeapon; +} + +//=========================================================================== +// +// FWeaponSlots :: PickPrevWeapon +// +// Returns the "previous" weapon for this player. If the current weapon is +// not in a slot, then it just returns that weapon, since there's nothing to +// consider it relative to. +// +//=========================================================================== + +AWeapon *PickPrevWeapon(player_t *player) +{ + int startslot, startindex; + int slotschecked = 0; + + if (player->mo == nullptr) + { + return nullptr; + } + if (player->ReadyWeapon == nullptr || FindMostRecentWeapon(player, &startslot, &startindex)) + { + int slot; + int index; + + if (player->ReadyWeapon == nullptr) + { + startslot = 0; + startindex = 0; + } + + slot = startslot; + index = startindex; + do + { + if (--index < 0) + { + slotschecked++; + if (--slot < 0) + { + slot = NUM_WEAPON_SLOTS - 1; + } + index = player->weapons.SlotSize(slot) - 1; + } + PClassActor *type = player->weapons.GetWeapon(slot, index); + AWeapon *weap = static_cast(player->mo->FindInventory(type)); + if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false)) + { + return weap; + } + } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); + } + return player->ReadyWeapon; +} + + + CCMD (slot) { if (argv.argc() > 1) @@ -283,8 +503,8 @@ CCMD (slot) if (slot < NUM_WEAPON_SLOTS) { - SendItemUse = players[consoleplayer].weapons.Slots[slot].PickWeapon (&players[consoleplayer], - !(dmflags2 & DF2_DONTCHECKAMMO)); + // Needs to be redone + SendItemUse = PickWeapon(&players[consoleplayer], slot, !(dmflags2 & DF2_DONTCHECKAMMO)); } } } @@ -316,7 +536,7 @@ CCMD (turn180) CCMD (weapnext) { - SendItemUse = players[consoleplayer].weapons.PickNextWeapon (&players[consoleplayer]); + SendItemUse = PickNextWeapon (&players[consoleplayer]); // [BC] Option to display the name of the weapon being cycled to. if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { @@ -331,7 +551,7 @@ CCMD (weapnext) CCMD (weapprev) { - SendItemUse = players[consoleplayer].weapons.PickPrevWeapon (&players[consoleplayer]); + SendItemUse = PickPrevWeapon (&players[consoleplayer]); // [BC] Option to display the name of the weapon being cycled to. if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 33c33413d5..ac9cbe330f 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -200,26 +200,6 @@ bool AInventory::Grind(bool items) return Super::Grind(items); } -//=========================================================================== -// -// AInventory :: Use -// -//=========================================================================== - -bool AInventory::CallUse(bool pickup) -{ - IFVIRTUAL(AInventory, Use) - { - VMValue params[2] = { (DObject*)this, pickup }; - int retval; - VMReturn ret(&retval); - VMCall(func, params, 2, &ret, 1); - return !!retval; - } - return false; -} - - //=========================================================================== // // @@ -304,25 +284,6 @@ PalEntry AInventory::CallGetBlend() else return 0; } -//=========================================================================== -// -// AInventory :: PrevItem -// -// Returns the previous item. -// -//=========================================================================== - -AInventory *AInventory::PrevItem () -{ - AInventory *item = Owner->Inventory; - - while (item != NULL && item->Inventory != this) - { - item = item->Inventory; - } - return item; -} - //=========================================================================== // // AInventory :: PrevInv diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 97f94bff0f..3815c6f771 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -81,13 +81,11 @@ public: bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // Wrapper for script function. void DepleteOrDestroy (); // virtual on the script side. - bool CallUse(bool pickup); // virtual on the script side. PalEntry CallGetBlend(); // virtual on the script side. bool GetNoTeleportFreeze(); // virtual on the script side. 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. diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 6408c90c4f..60fb582b94 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -317,73 +317,6 @@ int FWeaponSlot::LocateWeapon(PClassActor *type) return -1; } -//=========================================================================== -// -// FWeaponSlot :: PickWeapon -// -// Picks a weapon from this slot. If no weapon is selected in this slot, -// or the first weapon in this slot is selected, returns the last weapon. -// Otherwise, returns the previous weapon in this slot. This means -// precedence is given to the last weapon in the slot, which by convention -// is probably the strongest. Does not return weapons you have no ammo -// for or which you do not possess. -// -//=========================================================================== - -AWeapon *FWeaponSlot::PickWeapon(player_t *player, bool checkammo) -{ - int i, j; - - if (player->mo == nullptr) - { - return nullptr; - } - // Does this slot even have any weapons? - if (Weapons.Size() == 0) - { - return player->ReadyWeapon; - } - if (player->ReadyWeapon != nullptr) - { - for (i = 0; (unsigned)i < Weapons.Size(); i++) - { - if (Weapons[i].Type == player->ReadyWeapon->GetClass() || - (player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP && - player->ReadyWeapon->SisterWeapon != nullptr && - player->ReadyWeapon->SisterWeapon->GetClass() == Weapons[i].Type)) - { - for (j = (i == 0 ? Weapons.Size() - 1 : i - 1); - j != i; - j = (j == 0 ? Weapons.Size() - 1 : j - 1)) - { - AWeapon *weap = static_cast (player->mo->FindInventory(Weapons[j].Type)); - - if (weap != nullptr && weap->IsKindOf(NAME_Weapon)) - { - if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } - } - } - } - } - for (i = Weapons.Size() - 1; i >= 0; i--) - { - AWeapon *weap = static_cast (player->mo->FindInventory(Weapons[i].Type)); - - if (weap != nullptr && weap->IsKindOf(NAME_Weapon)) - { - if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } - } - return player->ReadyWeapon; -} - //=========================================================================== // // FWeaponSlot :: SetInitialPositions @@ -533,154 +466,6 @@ DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon) return MIN(numret, 3); } -//=========================================================================== -// -// FindMostRecentWeapon -// -// Locates the slot and index for the most recently selected weapon. If the -// player is in the process of switching to a new weapon, that is the most -// recently selected weapon. Otherwise, the current weapon is the most recent -// weapon. -// -//=========================================================================== - -static bool FindMostRecentWeapon(player_t *player, int *slot, int *index) -{ - if (player->PendingWeapon != WP_NOCHANGE) - { - return player->weapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index); - } - else if (player->ReadyWeapon != nullptr) - { - AWeapon *weap = player->ReadyWeapon; - if (!player->weapons.LocateWeapon(weap->GetClass(), slot, index)) - { - // If the current weapon wasn't found and is powered up, - // look for its non-powered up version. - if (weap->WeaponFlags & WIF_POWERED_UP && weap->SisterWeaponType != nullptr) - { - return player->weapons.LocateWeapon(weap->SisterWeaponType, slot, index); - } - return false; - } - return true; - } - else - { - return false; - } -} - -//=========================================================================== -// -// FWeaponSlots :: PickNextWeapon -// -// Returns the "next" weapon for this player. If the current weapon is not -// in a slot, then it just returns that weapon, since there's nothing to -// consider it relative to. -// -//=========================================================================== - -AWeapon *FWeaponSlots::PickNextWeapon(player_t *player) -{ - int startslot, startindex; - int slotschecked = 0; - - if (player->mo == nullptr) - { - return nullptr; - } - if (player->ReadyWeapon == nullptr || FindMostRecentWeapon(player, &startslot, &startindex)) - { - int slot; - int index; - - if (player->ReadyWeapon == nullptr) - { - startslot = NUM_WEAPON_SLOTS - 1; - startindex = Slots[startslot].Size() - 1; - } - - slot = startslot; - index = startindex; - do - { - if (++index >= Slots[slot].Size()) - { - index = 0; - slotschecked++; - if (++slot >= NUM_WEAPON_SLOTS) - { - slot = 0; - } - } - PClassActor *type = Slots[slot].GetWeapon(index); - AWeapon *weap = static_cast(player->mo->FindInventory(type)); - if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } - while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); - } - return player->ReadyWeapon; -} - -//=========================================================================== -// -// FWeaponSlots :: PickPrevWeapon -// -// Returns the "previous" weapon for this player. If the current weapon is -// not in a slot, then it just returns that weapon, since there's nothing to -// consider it relative to. -// -//=========================================================================== - -AWeapon *FWeaponSlots::PickPrevWeapon (player_t *player) -{ - int startslot, startindex; - int slotschecked = 0; - - if (player->mo == nullptr) - { - return nullptr; - } - if (player->ReadyWeapon == nullptr || FindMostRecentWeapon (player, &startslot, &startindex)) - { - int slot; - int index; - - if (player->ReadyWeapon == nullptr) - { - startslot = 0; - startindex = 0; - } - - slot = startslot; - index = startindex; - do - { - if (--index < 0) - { - slotschecked++; - if (--slot < 0) - { - slot = NUM_WEAPON_SLOTS - 1; - } - index = Slots[slot].Size() - 1; - } - PClassActor *type = Slots[slot].GetWeapon(index); - AWeapon *weap = static_cast(player->mo->FindInventory(type)); - if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } - while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); - } - return player->ReadyWeapon; -} - //=========================================================================== // // FWeaponSlots :: AddExtraWeapons @@ -976,10 +761,10 @@ CCMD (setslot) } else if (PlayingKeyConf != nullptr) { - PlayingKeyConf->Slots[slot].Clear(); + PlayingKeyConf->ClearSlot(slot); for (int i = 2; i < argv.argc(); ++i) { - PlayingKeyConf->Slots[slot].AddWeapon(argv[i]); + PlayingKeyConf->AddWeapon(slot, argv[i]); } } else diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index fcc3b1134f..1c4109425d 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -15,7 +15,6 @@ public: bool AddWeapon (const char *type); bool AddWeapon (PClassActor *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 (PClassActor *type); @@ -56,10 +55,9 @@ struct FWeaponSlots FWeaponSlots() { Clear(); } FWeaponSlots(const FWeaponSlots &other); +private: FWeaponSlot Slots[NUM_WEAPON_SLOTS]; - - AWeapon *PickNextWeapon (player_t *player); - AWeapon *PickPrevWeapon (player_t *player); +public: void Clear (); bool LocateWeapon (PClassActor *type, int *const slot, int *const index); @@ -76,7 +74,49 @@ struct FWeaponSlots void AddSlot(int slot, PClassActor *type, bool feedback); void AddSlotDefault(int slot, PClassActor *type, bool feedback); + // Abstract access interface to the slots + void AddWeapon(int slot, PClassActor *type) + { + if (slot >= 0 && slot < NUM_WEAPON_SLOTS) + { + Slots[slot].AddWeapon(type); + } + } + + void AddWeapon(int slot, const char *type) + { + if (slot >= 0 && slot < NUM_WEAPON_SLOTS) + { + Slots[slot].AddWeapon(type); + } + } + + void ClearSlot(int slot) + { + if (slot >= 0 && slot < NUM_WEAPON_SLOTS) + { + Slots[slot].Weapons.Clear(); + } + } + + int SlotSize(int slot) const + { + if (slot >= 0 && slot < NUM_WEAPON_SLOTS) + { + return Slots[slot].Weapons.Size(); + } + return 0; + } + + PClassActor *GetWeapon(int slot, int index) const + { + if (slot >= 0 && slot < NUM_WEAPON_SLOTS && (unsigned)index < Slots[slot].Weapons.Size()) + { + return Slots[slot].GetWeapon(index); + } + return nullptr; + } }; void P_PlaybackKeyConfWeapons(FWeaponSlots *slots); diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index a3b7c2a6de..88fc1ecbf1 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -582,9 +582,9 @@ static int DrawAmmo(player_t *CPlayer, int x, int y) else { // Order ammo by use of weapons in the weapon slots - for (k = 0; k < NUM_WEAPON_SLOTS; k++) for(j = 0; j < CPlayer->weapons.Slots[k].Size(); j++) + for (k = 0; k < NUM_WEAPON_SLOTS; k++) for(j = 0; j < CPlayer->weapons.SlotSize(k); j++) { - PClassActor *weap = CPlayer->weapons.Slots[k].GetWeapon(j); + PClassActor *weap = CPlayer->weapons.GetWeapon(k, j); if (weap) { @@ -782,9 +782,9 @@ static void DrawWeapons(player_t *CPlayer, int x, int y) } // And now everything in the weapon slots back to front - for (k = NUM_WEAPON_SLOTS - 1; k >= 0; k--) for(j = CPlayer->weapons.Slots[k].Size() - 1; j >= 0; j--) + for (k = NUM_WEAPON_SLOTS - 1; k >= 0; k--) for(j = CPlayer->weapons.SlotSize(k) - 1; j >= 0; j--) { - PClassActor *weap = CPlayer->weapons.Slots[k].GetWeapon(j); + PClassActor *weap = CPlayer->weapons.GetWeapon(k, j); if (weap) { inv=CPlayer->mo->FindInventory(weap); diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index 5a6da4b313..a4249e832c 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -527,9 +527,9 @@ class CommandDrawSwitchableImage : public CommandDrawImage if(condition == WEAPONSLOT) //weaponslots { drawAlt = 1; //draw off state until we know we have something. - for (int i = 0; i < statusBar->CPlayer->weapons.Slots[conditionalValue[0]].Size(); i++) + for (int i = 0; i < statusBar->CPlayer->weapons.SlotSize(conditionalValue[0]); i++) { - PClassActor *weap = statusBar->CPlayer->weapons.Slots[conditionalValue[0]].GetWeapon(i); + PClassActor *weap = statusBar->CPlayer->weapons.GetWeapon(conditionalValue[0], i); if(weap == NULL) { continue; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 7788c9ca98..a82342d5f9 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1126,9 +1126,14 @@ bool AActor::UseInventory (AInventory *item) { return false; } - if (!item->CallUse (false)) + + IFVIRTUALPTR(item, AInventory, Use) { - return false; + VMValue params[2] = { item, false }; + int retval; + VMReturn ret(&retval); + VMCall(func, params, 2, &ret, 1); + if (!retval) return false; } if (dmflags2 & DF2_INFINITE_INVENTORY) diff --git a/src/p_user.cpp b/src/p_user.cpp index a35c66dacd..e75865fcdc 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -655,9 +655,9 @@ void player_t::SendPitchLimits() const bool player_t::HasWeaponsInSlot(int slot) const { - for (int i = 0; i < weapons.Slots[slot].Size(); i++) + for (int i = 0; i < weapons.SlotSize(slot); i++) { - PClassActor *weap = weapons.Slots[slot].GetWeapon(i); + PClassActor *weap = weapons.GetWeapon(slot, i); if (weap != NULL && mo->FindInventory(weap)) return true; } return false; From f260709e735cf7ff3355492d90e2c8ba47e4ceec Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 22:22:36 +0100 Subject: [PATCH 30/48] - moved the weapon selection logic to PlayerPawn as overridable virtual functions. --- src/g_game.cpp | 256 +++----------------- src/g_inventory/a_weapons.cpp | 40 ++- src/g_inventory/a_weapons.h | 1 - wadsrc/static/zscript/inventory/weapons.txt | 2 + wadsrc/static/zscript/shared/player.txt | 219 +++++++++++++++++ 5 files changed, 270 insertions(+), 248 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 94b9b5a2a3..e8bddf230e 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -275,236 +275,22 @@ CCMD (turnspeeds) } } - -//=========================================================================== -// -// FWeaponSlot :: PickWeapon -// -// Picks a weapon from this slot. If no weapon is selected in this slot, -// or the first weapon in this slot is selected, returns the last weapon. -// Otherwise, returns the previous weapon in this slot. This means -// precedence is given to the last weapon in the slot, which by convention -// is probably the strongest. Does not return weapons you have no ammo -// for or which you do not possess. -// -//=========================================================================== - -AWeapon *PickWeapon(player_t *player, int slot, bool checkammo) -{ - int i, j; - - if (player->mo == nullptr) - { - return nullptr; - } - int Size = player->weapons.SlotSize(slot); - // Does this slot even have any weapons? - if (Size == 0) - { - return player->ReadyWeapon; - } - if (player->ReadyWeapon != nullptr) - { - for (i = 0; (unsigned)i < Size; i++) - { - auto weapontype = player->weapons.GetWeapon(slot, i); - if (weapontype == player->ReadyWeapon->GetClass() || - (player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP && - player->ReadyWeapon->SisterWeapon != nullptr && - player->ReadyWeapon->SisterWeapon->GetClass() == weapontype)) - { - for (j = (i == 0 ? Size - 1 : i - 1); - j != i; - j = (j == 0 ? Size - 1 : j - 1)) - { - auto weapontype2 = player->weapons.GetWeapon(slot, j); - AWeapon *weap = static_cast (player->mo->FindInventory(weapontype2)); - - if (weap != nullptr && weap->IsKindOf(NAME_Weapon)) - { - if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } - } - } - } - } - for (i = Size - 1; i >= 0; i--) - { - auto weapontype = player->weapons.GetWeapon(slot, i); - AWeapon *weap = static_cast (player->mo->FindInventory(weapontype)); - - if (weap != nullptr && weap->IsKindOf(NAME_Weapon)) - { - if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } - } - return player->ReadyWeapon; -} - -//=========================================================================== -// -// FindMostRecentWeapon -// -// Locates the slot and index for the most recently selected weapon. If the -// player is in the process of switching to a new weapon, that is the most -// recently selected weapon. Otherwise, the current weapon is the most recent -// weapon. -// -//=========================================================================== - -static bool FindMostRecentWeapon(player_t *player, int *slot, int *index) -{ - if (player->PendingWeapon != WP_NOCHANGE) - { - return player->weapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index); - } - else if (player->ReadyWeapon != nullptr) - { - AWeapon *weap = player->ReadyWeapon; - if (!player->weapons.LocateWeapon(weap->GetClass(), slot, index)) - { - // If the current weapon wasn't found and is powered up, - // look for its non-powered up version. - if (weap->WeaponFlags & WIF_POWERED_UP && weap->SisterWeaponType != nullptr) - { - return player->weapons.LocateWeapon(weap->SisterWeaponType, slot, index); - } - return false; - } - return true; - } - else - { - return false; - } -} - -//=========================================================================== -// -// FWeaponSlots :: PickNextWeapon -// -// Returns the "next" weapon for this player. If the current weapon is not -// in a slot, then it just returns that weapon, since there's nothing to -// consider it relative to. -// -//=========================================================================== - -AWeapon *PickNextWeapon(player_t *player) -{ - int startslot, startindex; - int slotschecked = 0; - - if (player->mo == nullptr) - { - return nullptr; - } - if (player->ReadyWeapon == nullptr || FindMostRecentWeapon(player, &startslot, &startindex)) - { - int slot; - int index; - - if (player->ReadyWeapon == nullptr) - { - startslot = NUM_WEAPON_SLOTS - 1; - startindex = player->weapons.SlotSize(startslot) - 1; - } - - slot = startslot; - index = startindex; - do - { - if (++index >= player->weapons.SlotSize(slot)) - { - index = 0; - slotschecked++; - if (++slot >= NUM_WEAPON_SLOTS) - { - slot = 0; - } - } - PClassActor *type = player->weapons.GetWeapon(slot, index); - AWeapon *weap = static_cast(player->mo->FindInventory(type)); - if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); - } - return player->ReadyWeapon; -} - -//=========================================================================== -// -// FWeaponSlots :: PickPrevWeapon -// -// Returns the "previous" weapon for this player. If the current weapon is -// not in a slot, then it just returns that weapon, since there's nothing to -// consider it relative to. -// -//=========================================================================== - -AWeapon *PickPrevWeapon(player_t *player) -{ - int startslot, startindex; - int slotschecked = 0; - - if (player->mo == nullptr) - { - return nullptr; - } - if (player->ReadyWeapon == nullptr || FindMostRecentWeapon(player, &startslot, &startindex)) - { - int slot; - int index; - - if (player->ReadyWeapon == nullptr) - { - startslot = 0; - startindex = 0; - } - - slot = startslot; - index = startindex; - do - { - if (--index < 0) - { - slotschecked++; - if (--slot < 0) - { - slot = NUM_WEAPON_SLOTS - 1; - } - index = player->weapons.SlotSize(slot) - 1; - } - PClassActor *type = player->weapons.GetWeapon(slot, index); - AWeapon *weap = static_cast(player->mo->FindInventory(type)); - if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false)) - { - return weap; - } - } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); - } - return player->ReadyWeapon; -} - - - CCMD (slot) { if (argv.argc() > 1) { int slot = atoi (argv[1]); - if (slot < NUM_WEAPON_SLOTS) + auto mo = players[consoleplayer].mo; + if (slot < NUM_WEAPON_SLOTS && mo) { // Needs to be redone - SendItemUse = PickWeapon(&players[consoleplayer], slot, !(dmflags2 & DF2_DONTCHECKAMMO)); + IFVIRTUALPTR(mo, APlayerPawn, PickWeapon) + { + VMValue param[] = { mo, slot, !(dmflags2 & DF2_DONTCHECKAMMO) }; + VMReturn ret((void**)&SendItemUse); + VMCall(func, param, 3, &ret, 1); + } } } } @@ -536,7 +322,18 @@ CCMD (turn180) CCMD (weapnext) { - SendItemUse = PickNextWeapon (&players[consoleplayer]); + auto mo = players[consoleplayer].mo; + if (mo) + { + // Needs to be redone + IFVIRTUALPTR(mo, APlayerPawn, PickNextWeapon) + { + VMValue param[] = { mo }; + VMReturn ret((void**)&SendItemUse); + VMCall(func, param, 1, &ret, 1); + } + } + // [BC] Option to display the name of the weapon being cycled to. if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { @@ -551,7 +348,18 @@ CCMD (weapnext) CCMD (weapprev) { - SendItemUse = PickPrevWeapon (&players[consoleplayer]); + auto mo = players[consoleplayer].mo; + if (mo) + { + // Needs to be redone + IFVIRTUALPTR(mo, APlayerPawn, PickPrevWeapon) + { + VMValue param[] = { mo }; + VMReturn ret((void**)&SendItemUse); + VMCall(func, param, 1, &ret, 1); + } + } + // [BC] Option to display the name of the weapon being cycled to. if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 60fb582b94..29579f1273 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -204,29 +204,6 @@ void AWeapon::MarkPrecacheSounds() const ReadySound.MarkUsed(); } -//=========================================================================== -// -// AWeapon :: CheckAmmo -// -// Returns true if there is enough ammo to shoot. If not, selects the -// next weapon to use. -// -//=========================================================================== - -bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int ammocount) -{ - IFVIRTUAL(AWeapon, CheckAmmo) - { - VMValue params[] = { (DObject*)this, fireMode, autoSwitch, requireAmmo, ammocount }; - VMReturn ret; - int retval; - ret.IntAt(&retval); - VMCall(func, params, 5, &ret, 1); - return !!retval; - } - return false; -} - /* Weapon slots ***********************************************************/ //=========================================================================== @@ -466,6 +443,23 @@ DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon) return MIN(numret, 3); } +DEFINE_ACTION_FUNCTION(FWeaponSlots, GetWeapon) +{ + PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots); + PARAM_INT(slot); + PARAM_INT(index); + ACTION_RETURN_POINTER(self->GetWeapon(slot, index)); + return 1; +} + +DEFINE_ACTION_FUNCTION(FWeaponSlots, SlotSize) +{ + PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots); + PARAM_INT(slot); + ACTION_RETURN_INT(self->SlotSize(slot)); + return 1; +} + //=========================================================================== // // FWeaponSlots :: AddExtraWeapons diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 1c4109425d..f1252f7294 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -174,7 +174,6 @@ public: AltFire, EitherFire }; - bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1); enum { diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 9f4196de2d..8612af7926 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -961,4 +961,6 @@ struct WeaponSlots native { native bool, int, int LocateWeapon(class weap); native static void SetupWeaponSlots(PlayerPawn pp); + native class GetWeapon(int slot, int index); + native int SlotSize(int slot); } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 478d3e8132..2e1cda0e99 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1721,6 +1721,225 @@ class PlayerPawn : Actor native return TeleportFreezeTime; } + //=========================================================================== + // + // FWeaponSlot :: PickWeapon + // + // Picks a weapon from this slot. If no weapon is selected in this slot, + // or the first weapon in this slot is selected, returns the last weapon. + // Otherwise, returns the previous weapon in this slot. This means + // precedence is given to the last weapon in the slot, which by convention + // is probably the strongest. Does not return weapons you have no ammo + // for or which you do not possess. + // + //=========================================================================== + + virtual Weapon PickWeapon(int slot, bool checkammo) + { + int i, j; + + let player = self.player; + int Size = player.weapons.SlotSize(slot); + // Does this slot even have any weapons? + if (Size == 0) + { + return player.ReadyWeapon; + } + let ReadyWeapon = player.ReadyWeapon; + if (ReadyWeapon != null) + { + for (i = 0; i < Size; i++) + { + let weapontype = player.weapons.GetWeapon(slot, i); + if (weapontype == ReadyWeapon.GetClass() || + (ReadyWeapon.bPOWERED_UP && ReadyWeapon.SisterWeapon != null && ReadyWeapon.SisterWeapon.GetClass() == weapontype)) + { + for (j = (i == 0 ? Size - 1 : i - 1); + j != i; + j = (j == 0 ? Size - 1 : j - 1)) + { + let weapontype2 = player.weapons.GetWeapon(slot, j); + let weap = Weapon(player.mo.FindInventory(weapontype2)); + + if (weap != null) + { + if (!checkammo || weap.CheckAmmo(Weapon.EitherFire, false)) + { + return weap; + } + } + } + } + } + } + for (i = Size - 1; i >= 0; i--) + { + let weapontype = player.weapons.GetWeapon(slot, i); + let weap = Weapon(player.mo.FindInventory(weapontype)); + + if (weap != null) + { + if (!checkammo || weap.CheckAmmo(Weapon.EitherFire, false)) + { + return weap; + } + } + } + return ReadyWeapon; + } + + //=========================================================================== + // + // FindMostRecentWeapon + // + // Locates the slot and index for the most recently selected weapon. If the + // player is in the process of switching to a new weapon, that is the most + // recently selected weapon. Otherwise, the current weapon is the most recent + // weapon. + // + //=========================================================================== + + bool, int, int FindMostRecentWeapon() + { + let player = self.player; + let ReadyWeapon = player.ReadyWeapon; + if (player.PendingWeapon != WP_NOCHANGE) + { + return player.weapons.LocateWeapon(player.PendingWeapon.GetClass()); + } + else if (ReadyWeapon != null) + { + bool found; + int slot; + int index; + [found, slot, index] = player.weapons.LocateWeapon(ReadyWeapon.GetClass()); + if (!found) + { + // If the current weapon wasn't found and is powered up, + // look for its non-powered up version. + if (ReadyWeapon.bPOWERED_UP && ReadyWeapon.SisterWeaponType != null) + { + return player.weapons.LocateWeapon(ReadyWeapon.SisterWeaponType); + } + return false, 0, 0; + } + return true, slot, index; + } + else + { + return false, 0, 0; + } + } + + //=========================================================================== + // + // FWeaponSlots :: PickNextWeapon + // + // Returns the "next" weapon for this player. If the current weapon is not + // in a slot, then it just returns that weapon, since there's nothing to + // consider it relative to. + // + //=========================================================================== + const NUM_WEAPON_SLOTS = 10; + + virtual Weapon PickNextWeapon() + { + let player = self.player; + bool found; + int startslot, startindex; + int slotschecked = 0; + + [found, startslot, startindex] = FindMostRecentWeapon(); + let ReadyWeapon = player.ReadyWeapon; + if (ReadyWeapon == null || found) + { + int slot; + int index; + + if (ReadyWeapon == null) + { + startslot = NUM_WEAPON_SLOTS - 1; + startindex = player.weapons.SlotSize(startslot) - 1; + } + + slot = startslot; + index = startindex; + do + { + if (++index >= player.weapons.SlotSize(slot)) + { + index = 0; + slotschecked++; + if (++slot >= NUM_WEAPON_SLOTS) + { + slot = 0; + } + } + let type = player.weapons.GetWeapon(slot, index); + let weap = Weapon(FindInventory(type)); + if (weap != null && weap.CheckAmmo(Weapon.EitherFire, false)) + { + return weap; + } + } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); + } + return ReadyWeapon; + } + + //=========================================================================== + // + // FWeaponSlots :: PickPrevWeapon + // + // Returns the "previous" weapon for this player. If the current weapon is + // not in a slot, then it just returns that weapon, since there's nothing to + // consider it relative to. + // + //=========================================================================== + + virtual Weapon PickPrevWeapon() + { + let player = self.player; + int startslot, startindex; + bool found; + int slotschecked = 0; + + [found, startslot, startindex] = FindMostRecentWeapon(); + if (player.ReadyWeapon == null || found) + { + int slot; + int index; + + if (player.ReadyWeapon == null) + { + startslot = 0; + startindex = 0; + } + + slot = startslot; + index = startindex; + do + { + if (--index < 0) + { + slotschecked++; + if (--slot < 0) + { + slot = NUM_WEAPON_SLOTS - 1; + } + index = player.weapons.SlotSize(slot) - 1; + } + let type = player.weapons.GetWeapon(slot, index); + let weap = Weapon(FindInventory(type)); + if (weap != null && weap.CheckAmmo(Weapon.EitherFire, false)) + { + return weap; + } + } while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); + } + return player.ReadyWeapon; + } + + //---------------------------------------------------------------------------- // // From 4392b4e96d8a3a4902359abdfb5d15a542f13405 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 22:35:50 +0100 Subject: [PATCH 31/48] - exported the blood spawning part of P_LineAttack as a virtual ZScript function. --- src/p_map.cpp | 31 +++-------------------- wadsrc/static/zscript/actor_attacks.txt | 33 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index fc29afb061..c1d50d4539 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4859,38 +4859,13 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, } if (!(puffDefaults != NULL && puffDefaults->flags3&MF3_BLOODLESSIMPACT)) { - bool bloodsplatter = (t1->flags5 & MF5_BLOODSPLATTER) || - (t1->player != nullptr && t1->player->ReadyWeapon != nullptr && - (t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD)); - - bool axeBlood = (t1->player != nullptr && - t1->player->ReadyWeapon != nullptr && - (t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD)); - - if (!bloodsplatter && !axeBlood && - !(trace.Actor->flags & MF_NOBLOOD) && - !(trace.Actor->flags2 & (MF2_INVULNERABLE | MF2_DORMANT))) + IFVIRTUALPTR(trace.Actor, AActor, SpawnLineAttackBlood) { - P_SpawnBlood(bleedpos, trace.SrcAngleFromTarget, newdam > 0 ? newdam : damage, trace.Actor); + VMValue params[] = { trace.Actor, t1, bleedpos.X, bleedpos.Y, bleedpos.Z, trace.SrcAngleFromTarget.Degrees, damage, newdam }; + VMCall(func, params, countof(params), nullptr, 0); } - if (damage) { - if (bloodsplatter || axeBlood) - { - if (!(trace.Actor->flags&MF_NOBLOOD) && - !(trace.Actor->flags2&(MF2_INVULNERABLE | MF2_DORMANT))) - { - if (axeBlood) - { - P_BloodSplatter2(bleedpos, trace.Actor, trace.SrcAngleFromTarget); - } - if (pr_lineattack() < 192) - { - P_BloodSplatter(bleedpos, trace.Actor, trace.SrcAngleFromTarget); - } - } - } // [RH] Stick blood to walls P_TraceBleed(newdam > 0 ? newdam : damage, trace.HitPos, trace.Actor, trace.SrcAngleFromTarget, pitch); } diff --git a/wadsrc/static/zscript/actor_attacks.txt b/wadsrc/static/zscript/actor_attacks.txt index 22a2a4386b..d5dc1813fa 100644 --- a/wadsrc/static/zscript/actor_attacks.txt +++ b/wadsrc/static/zscript/actor_attacks.txt @@ -705,4 +705,37 @@ extend class Actor } + //========================================================================== + // + // called with the victim as 'self' + // + //========================================================================== + + virtual void SpawnLineAttackBlood(Actor attacker, Vector3 bleedpos, double SrcAngleFromTarget, int originaldamage, int actualdamage) + { + if (!bNoBlood && !bDormant && !bInvulnerable) + { + let player = attacker.player; + let weapon = player? player.ReadyWeapon : null; + let axeBlood = (weapon && weapon.bAxeBlood); + let bloodsplatter = attacker.bBloodSplatter || axeBlood; + + if (!bloodsplatter) + { + SpawnBlood(bleedpos, SrcAngleFromTarget, actualdamage > 0 ? actualdamage : originaldamage); + } + else if (damage) + { + if (axeBlood) + { + BloodSplatter(bleedpos, SrcAngleFromTarget, true); + } + // No else here... + if (random[LineAttack]() < 192) + { + BloodSplatter(bleedpos, SrcAngleFromTarget, false); + } + } + } + } } From ead28db0077a3062be3f4d6b8ebcffc169f70b0c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 22:40:14 +0100 Subject: [PATCH 32/48] - consolidated the 3 nearly identical code fragments handling the weapon's YAdjust for the different renderers into a utility function in DPSprite. --- src/hwrenderer/scene/hw_weapon.cpp | 16 +----------- src/p_pspr.cpp | 27 ++++++++++++++++++++ src/p_pspr.h | 1 + src/polyrenderer/scene/poly_playersprite.cpp | 13 +--------- src/swrenderer/things/r_playersprite.cpp | 13 +--------- 5 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/hwrenderer/scene/hw_weapon.cpp b/src/hwrenderer/scene/hw_weapon.cpp index d0bb20e4d3..171c28647b 100644 --- a/src/hwrenderer/scene/hw_weapon.cpp +++ b/src/hwrenderer/scene/hw_weapon.cpp @@ -433,21 +433,7 @@ bool HUDSprite::GetWeaponRect(HWDrawInfo *di, DPSprite *psp, float sx, float sy, x2 += viewwindowx; // killough 12/98: fix psprite positioning problem - ftexturemid = 100.f - sy - r.top; - - AWeapon * wi = player->ReadyWeapon; - if (wi && wi->YAdjust != 0) - { - float fYAd = wi->YAdjust; - if (screenblocks >= 11) - { - ftexturemid -= fYAd; - } - else - { - ftexturemid -= float(StatusBar->GetDisplacement()) * fYAd; - } - } + ftexturemid = 100.f - sy - r.top - psp->GetYAdjust(screenblocks >= 11); scale = (SCREENHEIGHT*vw) / (SCREENWIDTH * 200.0f); y1 = viewwindowy + vh / 2 - (ftexturemid * scale); diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index f0a1a38564..6634fb7b55 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -44,6 +44,7 @@ #include "cmdlib.h" #include "g_levellocals.h" #include "vm.h" +#include "sbar.h" // MACROS ------------------------------------------------------------------ @@ -1348,6 +1349,32 @@ void DPSprite::OnDestroy() // //------------------------------------------------------------------------ +float DPSprite::GetYAdjust(bool fullscreen) +{ + AWeapon *weapon = dyn_cast(GetCaller()); + if (weapon != nullptr) + { + float fYAd = weapon->YAdjust; + if (fYAd != 0) + { + if (fullscreen) + { + return fYAd; + } + else + { + return StatusBar->GetDisplacement() * fYAd; + } + } + } +} + +//------------------------------------------------------------------------ +// +// +// +//------------------------------------------------------------------------ + ADD_STAT(psprites) { FString out; diff --git a/src/p_pspr.h b/src/p_pspr.h index 2113a854e5..33d46d29c9 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -92,6 +92,7 @@ public: void ResetInterpolation() { oldx = x; oldy = y; } void OnDestroy() override; std::pair GetRenderStyle(FRenderStyle ownerstyle, double owneralpha); + float GetYAdjust(bool fullscreen); double x, y, alpha; double oldx, oldy; diff --git a/src/polyrenderer/scene/poly_playersprite.cpp b/src/polyrenderer/scene/poly_playersprite.cpp index fecbc77ace..ada83a7475 100644 --- a/src/polyrenderer/scene/poly_playersprite.cpp +++ b/src/polyrenderer/scene/poly_playersprite.cpp @@ -296,18 +296,7 @@ void RenderPolyPlayerSprites::RenderSprite(PolyRenderThread *thread, DPSprite *p viewheight == renderTarget->GetHeight() || (renderTarget->GetWidth() > (BASEXCENTER * 2)))) { // Adjust PSprite for fullscreen views - AWeapon *weapon = dyn_cast(pspr->GetCaller()); - if (weapon != nullptr && weapon->YAdjust != 0) - { - if (renderToCanvas || viewheight == renderTarget->GetHeight()) - { - vis.texturemid -= weapon->YAdjust; - } - else - { - vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; - } - } + vis.texturemid -= pspr->GetYAdjust(renderToCanvas || viewheight == renderTarget->GetHeight()); } if (pspr->GetID() < PSP_TARGETCENTER) { // Move the weapon down for 1280x1024. diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index 1a6f52e2db..a51b3b5fda 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -295,18 +295,7 @@ namespace swrenderer viewheight == viewport->RenderTarget->GetHeight() || (viewport->RenderTarget->GetWidth() > (BASEXCENTER * 2)))) { // Adjust PSprite for fullscreen views - AWeapon *weapon = dyn_cast(pspr->GetCaller()); - if (weapon != nullptr && weapon->YAdjust != 0) - { - if (renderToCanvas || viewheight == viewport->RenderTarget->GetHeight()) - { - vis.texturemid -= weapon->YAdjust; - } - else - { - vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; - } - } + vis.texturemid -= pspr->GetYAdjust(renderToCanvas || viewheight == viewport->RenderTarget->GetHeight()); } if (pspr->GetID() < PSP_TARGETCENTER) { // Move the weapon down for 1280x1024. From 8eb4697fbdfe3f2a2091b3f7f1989af4bfb13599 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 23:48:23 +0100 Subject: [PATCH 33/48] - removed the bot related properties from AWeapon. This stuff is now kept locally in the bot code so that it doesn't infest the rest of the engine. And please don't read the new botsupp.txt file as some new means to configure bots! This was merely done to get this data out of the way. The bots are still broken beyond repair and virtually unusable, even if proper data is provided for all weapons. --- src/b_bot.cpp | 101 +++++++++----------- src/b_bot.h | 30 ++++++ src/b_func.cpp | 12 +-- src/b_move.cpp | 4 +- src/b_think.cpp | 12 +-- src/g_inventory/a_weapons.cpp | 6 -- src/g_inventory/a_weapons.h | 9 -- src/scripting/thingdef_data.cpp | 4 +- wadsrc/static/botsupp.txt | 37 +++++++ wadsrc/static/zscript/inventory/weapons.txt | 5 +- 10 files changed, 131 insertions(+), 89 deletions(-) create mode 100644 wadsrc/static/botsupp.txt diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 24a1bfe037..25418acca3 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -47,6 +47,7 @@ #include "d_net.h" #include "serializer.h" #include "d_player.h" +#include "w_wad.h" #include "vm.h" IMPLEMENT_CLASS(DBot, false, true) @@ -246,69 +247,61 @@ CCMD (listbots) // set the bot specific weapon information // This is intentionally not in the weapon definition anymore. + +BotInfoMap BotInfo; + void InitBotStuff() { - static struct BotInit + int lump; + int lastlump = 0; + while (-1 != (lump = Wads.FindLump("BOTSUPP", &lastlump))) { - const char *type; - int movecombatdist; - int weaponflags; - const char *projectile; - } botinits[] = { - - { "Pistol", 25000000, 0, NULL }, - { "Shotgun", 24000000, 0, NULL }, - { "SuperShotgun", 15000000, 0, NULL }, - { "Chaingun", 27000000, 0, NULL }, - { "RocketLauncher", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "Rocket" }, - { "PlasmaRifle", 27000000, 0, "PlasmaBall" }, - { "BFG9000", 10000000, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_BFG, "BFGBall" }, - { "GoldWand", 25000000, 0, NULL }, - { "GoldWandPowered", 25000000, 0, NULL }, - { "Crossbow", 24000000, 0, "CrossbowFX1" }, - { "CrossbowPowered", 24000000, 0, "CrossbowFX2" }, - { "Blaster", 27000000, 0, NULL }, - { "BlasterPowered", 27000000, 0, "BlasterFX1" }, - { "SkullRod", 27000000, 0, "HornRodFX1" }, - { "SkullRodPowered", 27000000, 0, "HornRodFX2" }, - { "PhoenixRod", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "PhoenixFX1" }, - { "Mace", 27000000, WIF_BOT_REACTION_SKILL_THING, "MaceFX2" }, - { "MacePowered", 27000000, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "MaceFX4" }, - { "FWeapHammer", 22000000, 0, "HammerMissile" }, - { "FWeapQuietus", 20000000, 0, "FSwordMissile" }, - { "CWeapStaff", 25000000, 0, "CStaffMissile" }, - { "CWeapFlane", 27000000, 0, "CFlameMissile" }, - { "MWeapWand", 25000000, 0, "MageWandMissile" }, - { "CWeapWraithverge", 22000000, 0, "HolyMissile" }, - { "MWeapFrost", 19000000, 0, "FrostMissile" }, - { "MWeapLightning", 23000000, 0, "LightningFloor" }, - { "MWeapBloodscourge", 20000000, 0, "MageStaffFX2" }, - { "StrifeCrossbow", 24000000, 0, "ElectricBolt" }, - { "StrifeCrossbow2", 24000000, 0, "PoisonBolt" }, - { "AssaultGun", 27000000, 0, NULL }, - { "MiniMissileLauncher", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "MiniMissile" }, - { "FlameThrower", 24000000, 0, "FlameMissile" }, - { "Mauler", 15000000, 0, NULL }, - { "Mauler2", 10000000, 0, "MaulerTorpedo" }, - { "StrifeGrenadeLauncher", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "HEGrenade" }, - { "StrifeGrenadeLauncher2", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "PhosphorousGrenade" }, - }; - - for(unsigned i=0;iIsDescendantOf(NAME_Weapon)) + FScanner sc(lump); + sc.SetCMode(true); + while (sc.GetString()) { - AWeapon *w = (AWeapon*)GetDefaultByType(cls); - if (w != NULL) + PClassActor *wcls = PClass::FindActor(sc.String); + if (wcls != NULL && wcls->IsDescendantOf(NAME_Weapon)) { - w->MoveCombatDist = botinits[i].movecombatdist/65536.; - w->WeaponFlags |= botinits[i].weaponflags; - w->ProjectileType = PClass::FindActor(botinits[i].projectile); + BotInfoData bi = {}; + sc.MustGetStringName(","); + sc.MustGetNumber(); + bi.MoveCombatDist = sc.Number; + while (sc.CheckString(",")) + { + sc.MustGetString(); + if (sc.Compare("BOT_REACTION_SKILL_THING")) + { + bi.flags |= BIF_BOT_REACTION_SKILL_THING; + } + else if (sc.Compare("BOT_EXPLOSIVE")) + { + bi.flags |= BIF_BOT_EXPLOSIVE; + } + else if (sc.Compare("BOT_BFG")) + { + bi.flags |= BIF_BOT_BFG; + } + else + { + PClassActor *cls = PClass::FindActor(sc.String); + bi.projectileType = cls; + if (cls == nullptr) + { + sc.ScriptError("Unknown token %s", sc.String); + } + } + } + BotInfo[wcls->TypeName] = bi; + } + else + { + sc.ScriptError("%s is not a weapon type", sc.String); } } } + // Fixme: Export these, too. static const char *warnbotmissiles[] = { "PlasmaBall", "Ripper", "HornRodFX1" }; for(unsigned i=0;i; + +extern BotInfoMap BotInfo; + +inline BotInfoData GetBotInfo(AWeapon *weap) +{ + if (weap == nullptr) return BotInfoData(); + auto k = BotInfo.CheckKey(weap->GetClass()->TypeName); + if (k) return *k; + return BotInfoData(); +} + //Used to keep all the globally needed variables in nice order. class FCajunMaster { diff --git a/src/b_func.cpp b/src/b_func.cpp index 87eaa66b6e..34a5bd9fdc 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -188,7 +188,7 @@ void DBot::Dofire (ticcmd_t *cmd) //Reaction skill thing. if (first_shot && - !(player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) + !(GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_REACTION_SKILL_THING)) { t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3); } @@ -203,21 +203,21 @@ void DBot::Dofire (ticcmd_t *cmd) Dist = player->mo->Distance2D(enemy, player->mo->Vel.X - enemy->Vel.X, player->mo->Vel.Y - enemy->Vel.Y); //FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go. - if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) + if (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0) { //*4 is for atmosphere, the chainsaws sounding and all.. no_fire = (Dist > DEFMELEERANGE*4); } - else if (player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) + else if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_BFG) { //MAKEME: This should be smarter. if ((pr_botdofire()%200)<=skill.reaction) if(Check_LOS(enemy, SHOOTFOV)) no_fire = false; } - else if (player->ReadyWeapon->ProjectileType != NULL) + else if (GetBotInfo(player->ReadyWeapon).projectileType != NULL) { - if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) + if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) { //Special rules for RL an = FireRox (enemy, cmd); @@ -234,7 +234,7 @@ void DBot::Dofire (ticcmd_t *cmd) } // prediction aiming Dist = player->mo->Distance2D(enemy); - fm = Dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed; + fm = Dist / GetDefaultByType (GetBotInfo(player->ReadyWeapon).projectileType)->Speed; bglobal.SetBodyAt(enemy->Pos() + enemy->Vel.XY() * fm * 2, 1); Angle = player->mo->AngleTo(bglobal.body1); if (Check_LOS (enemy, SHOOTFOV)) diff --git a/src/b_move.cpp b/src/b_move.cpp index 3d3e67ed2c..52ae724dc0 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -345,7 +345,7 @@ void DBot::TurnToAng () if (player->ReadyWeapon != NULL) { - if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) + if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) { if (t_roam && !missile) { //Keep angle that where when shot where decided. @@ -356,7 +356,7 @@ void DBot::TurnToAng () if(enemy) if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns - if(player->ReadyWeapon->ProjectileType == NULL && !(player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) + if(GetBotInfo(player->ReadyWeapon).projectileType == NULL && GetBotInfo(player->ReadyWeapon).MoveCombatDist > 0) if(Check_LOS(enemy, SHOOTFOV+5)) maxturn = 3; } diff --git a/src/b_think.cpp b/src/b_think.cpp index 2c41bcda36..32dd8e6b1b 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -173,9 +173,9 @@ void DBot::ThinkForMove (ticcmd_t *cmd) is (Megasphere) ) || dist < (GETINCOMBAT/4) || - (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) + (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0) ) - && (dist < GETINCOMBAT || (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + && (dist < GETINCOMBAT || (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0)) && Reachable (dest)) #undef is { @@ -185,7 +185,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd) dest = NULL; //To let bot turn right - if (player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + if (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0) player->mo->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. if (!(enemy->flags3 & MF3_ISMONSTER)) @@ -205,7 +205,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd) if (player->ReadyWeapon == NULL || player->mo->Distance2D(enemy) > - player->ReadyWeapon->MoveCombatDist) + GetBotInfo(player->ReadyWeapon).MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; @@ -273,8 +273,8 @@ void DBot::ThinkForMove (ticcmd_t *cmd) { if (enemy->player) { - if (((enemy->player->ReadyWeapon != NULL && enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>skill.isp) && player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + if (((enemy->player->ReadyWeapon != NULL && GetBotInfo(enemy->player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) || + (pr_botmove()%100)>skill.isp) && (GetBotInfo(player->ReadyWeapon).MoveCombatDist != 0)) dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. else //hide while t_fight, but keep view at enemy. Angle = player->mo->AngleTo(enemy); diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 29579f1273..0d18cbcc63 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -68,12 +68,9 @@ 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) @@ -184,9 +181,6 @@ void AWeapon::Serialize(FSerializer &arc) ("ammotype1", AmmoType1, def->AmmoType1) ("ammotype2", AmmoType2, def->AmmoType2) ("sisterweapontype", SisterWeaponType, def->SisterWeaponType) - ("projectiletype", ProjectileType, def->ProjectileType) - ("altprojectiletype", AltProjectileType, def->AltProjectileType) - ("movecombatdist", MoveCombatDist, def->MoveCombatDist) */ } diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index f1252f7294..62097b6333 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -142,11 +142,8 @@ public: 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 PClassActor *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. @@ -210,11 +207,5 @@ enum 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 }; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index f5d10a4172..c88262298d 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -456,9 +456,7 @@ static FFlagDef WeaponFlagDefs[] = DEFINE_FLAG(WIF, WIMPY_WEAPON, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, POWERED_UP, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, STAFF2_KICKBACK, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF_BOT, EXPLOSIVE, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, MELEEWEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF_BOT, BFG, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, CHEATNOTWEAPON, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, NO_AUTO_SWITCH, AWeapon, WeaponFlags), DEFINE_FLAG(WIF, AMMO_CHECKBOTH, AWeapon, WeaponFlags), @@ -469,6 +467,8 @@ static FFlagDef WeaponFlagDefs[] = DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), + DEFINE_DUMMY_FLAG(BFG, true), + DEFINE_DUMMY_FLAG(EXPLOSIVE, true), }; diff --git a/wadsrc/static/botsupp.txt b/wadsrc/static/botsupp.txt new file mode 100644 index 0000000000..730fb056a2 --- /dev/null +++ b/wadsrc/static/botsupp.txt @@ -0,0 +1,37 @@ +// This is just a straight dump of an internal table. Contents are: Weapon type, distance coefficient, optional flags and an optional projectile type. +Pistol, 25000000 +Shotgun, 24000000 +SuperShotgun, 15000000 +Chaingun, 27000000 +RocketLauncher, 18350080, BOT_REACTION_SKILL_THING, BOT_EXPLOSIVE, Rocket +PlasmaRifle, 27000000, PlasmaBall +BFG9000, 10000000, BOT_REACTION_SKILL_THING, BOT_BFG, BFGBall +GoldWand, 25000000 +GoldWandPowered, 25000000 +Crossbow, 24000000, CrossbowFX1 +CrossbowPowered, 24000000, CrossbowFX2 +Blaster, 27000000 +BlasterPowered, 27000000, BlasterFX1 +SkullRod, 27000000, HornRodFX1 +SkullRodPowered, 27000000, HornRodFX2 +PhoenixRod, 18350080, BOT_REACTION_SKILL_THING, BOT_EXPLOSIVE, PhoenixFX1 +Mace, 27000000, BOT_REACTION_SKILL_THING, MaceFX2 +MacePowered, 27000000, BOT_REACTION_SKILL_THING, BOT_EXPLOSIVE, MaceFX4 +FWeapHammer, 22000000, HammerMissile +FWeapQuietus, 20000000, FSwordMissile +CWeapStaff, 25000000, CStaffMissile +CWeapFlame, 27000000, CFlameMissile +MWeapWand, 25000000, MageWandMissile +CWeapWraithverge, 22000000, HolyMissile +MWeapFrost, 19000000, FrostMissile +MWeapLightning, 23000000, LightningFloor +MWeapBloodscourge, 20000000, MageStaffFX2 +StrifeCrossbow, 24000000, ElectricBolt +StrifeCrossbow2, 24000000, PoisonBolt +AssaultGun, 27000000 +MiniMissileLauncher, 18350080, BOT_REACTION_SKILL_THING, BOT_EXPLOSIVE, MiniMissile +FlameThrower, 24000000, FlameMissile +Mauler, 15000000 +Mauler2, 10000000, MaulerTorpedo +StrifeGrenadeLauncher, 18350080, BOT_REACTION_SKILL_THING, BOT_EXPLOSIVE, HEGrenade +StrifeGrenadeLauncher2, 18350080, BOT_REACTION_SKILL_THING, BOT_EXPLOSIVE, PhosphorousGrenade diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 8612af7926..1832e2d218 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -13,17 +13,14 @@ class Weapon : StateProvider native native uint WeaponFlags; native class AmmoType1, AmmoType2; // Types of ammo used by self 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 self weapon + native int MinAmmo1, MinAmmo2; // not used anywhere. 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 self 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. From b75ee1027a46cea9fad4c95c9278f4ede5eacbef Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 23:51:09 +0100 Subject: [PATCH 34/48] - a little bit of cleanup on some code that repeatedly accessed some fields in AWeapon and produced far too many search results when looking for this. --- src/ct_chat.cpp | 16 +++++++++------- src/g_statusbar/sbarinfo.cpp | 6 ++++++ src/g_statusbar/sbarinfo_commands.cpp | 8 ++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 85e8f0c234..c32a673039 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -375,6 +375,8 @@ static bool DoSubstitution (FString &out, const char *in) { player_t *player = &players[consoleplayer]; AWeapon *weapon = player->ReadyWeapon; + auto ammo1 = weapon ? weapon->Ammo1 : nullptr; + auto ammo2 = weapon ? weapon->Ammo2 : nullptr; const char *a, *b; a = in; @@ -427,10 +429,10 @@ static bool DoSubstitution (FString &out, const char *in) } else { - out.AppendFormat("%d", weapon->Ammo1 != NULL ? weapon->Ammo1->Amount : 0); - if (weapon->Ammo2 != NULL) + out.AppendFormat("%d", ammo1 != NULL ? ammo1->Amount : 0); + if (ammo2 != NULL) { - out.AppendFormat("/%d", weapon->Ammo2->Amount); + out.AppendFormat("/%d", ammo2->Amount); } } } @@ -439,16 +441,16 @@ static bool DoSubstitution (FString &out, const char *in) { if (strnicmp(a, "ammo", 4) == 0) { - if (weapon == NULL || weapon->Ammo1 == NULL) + if (ammo1 == NULL) { out += "no ammo"; } else { - out.AppendFormat("%s", weapon->Ammo1->GetClass()->TypeName.GetChars()); - if (weapon->Ammo2 != NULL) + out.AppendFormat("%s", ammo1->GetClass()->TypeName.GetChars()); + if (ammo2 != NULL) { - out.AppendFormat("/%s", weapon->Ammo2->GetClass()->TypeName.GetChars()); + out.AppendFormat("/%s", ammo2->GetClass()->TypeName.GetChars()); } } } diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index 9c3c710173..e0dc3e294c 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -1467,6 +1467,12 @@ public: return TRANSLATION(TRANSLATION_Players, int(CPlayer - players)); } + PClassActor *AmmoType(int no) const + { + auto w = StatusBar->CPlayer->ReadyWeapon; + return w == nullptr ? nullptr : (no == 1 ? w->AmmoType1 : w->AmmoType2); + } + AInventory *ammo1, *ammo2; int ammocount1, ammocount2; AInventory *armor; diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index a4249e832c..d282986679 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -1874,7 +1874,7 @@ class CommandUsesAmmo : public SBarInfoNegatableFlowControl { SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); - SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL), block, statusBar); + SetTruth(statusBar->AmmoType(1) || statusBar->AmmoType(2), block, statusBar); } }; @@ -1891,7 +1891,7 @@ class CommandUsesSecondaryAmmo : public CommandUsesAmmo { SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); - SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2, block, statusBar); + SetTruth(statusBar->AmmoType(2) && statusBar->AmmoType(2) != statusBar->AmmoType(1), block, statusBar); } }; @@ -3247,8 +3247,8 @@ class CommandWeaponAmmo : public SBarInfoNegatableFlowControl if(statusBar->CPlayer->ReadyWeapon != NULL) { - const PClass *AmmoType1 = statusBar->CPlayer->ReadyWeapon->AmmoType1; - const PClass *AmmoType2 = statusBar->CPlayer->ReadyWeapon->AmmoType2; + const PClass *AmmoType1 = statusBar->AmmoType(1); + const PClass *AmmoType2 = statusBar->AmmoType(2); bool usesammo1 = (AmmoType1 != NULL); bool usesammo2 = (AmmoType2 != NULL); //if(!usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. From 34b7e5f435fd3e3a05cb81f6d2b3fb90ec709d71 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 00:23:03 +0100 Subject: [PATCH 35/48] - scriptified P_BobWeapon as a virtual function on PlayerPawn. --- src/d_player.h | 1 + src/p_pspr.cpp | 103 +++------------------- src/p_user.cpp | 7 ++ src/scripting/vm/vm.h | 6 ++ wadsrc/static/zscript/constants.txt | 9 ++ wadsrc/static/zscript/shared/player.txt | 112 ++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 93 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index ad6635c212..6099b57aea 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -151,6 +151,7 @@ public: // [SP] ViewBob Multiplier double ViewBob; + double curBob; // Former class properties that were moved into the object to get rid of the meta class. FName SoundClass; // Sound class diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 6634fb7b55..400b34d52a 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -579,101 +579,17 @@ void P_BringUpWeapon (player_t *player) void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac) { - static float curbob; - double xx[2], yy[2]; - - AWeapon *weapon; - float bobtarget; - - weapon = player->ReadyWeapon; - - if (weapon == nullptr || weapon->WeaponFlags & WIF_DONTBOB) + IFVIRTUALPTR(player->mo, APlayerPawn, BobWeapon) { - *x = *y = 0; + VMValue param[] = { player->mo, ticfrac }; + DVector2 result; + VMReturn ret(&result); + VMCall(func, param, 2, &ret, 1); + *x = (float)result.X; + *y = (float)result.Y; return; } - - // [XA] Get the current weapon's bob properties. - int bobstyle = weapon->BobStyle; - float BobSpeed = (weapon->BobSpeed * 128); - float Rangex = weapon->BobRangeX; - float Rangey = weapon->BobRangeY; - - for (int i = 0; i < 2; i++) - { - // Bob the weapon based on movement speed. ([SP] And user's bob speed setting) - FAngle angle = (BobSpeed * player->userinfo.GetWBobSpeed() * 35 / - TICRATE*(level.time - 1 + i)) * (360.f / 8192.f); - - // [RH] Smooth transitions between bobbing and not-bobbing frames. - // This also fixes the bug where you can "stick" a weapon off-center by - // shooting it when it's at the peak of its swing. - bobtarget = float((player->WeaponState & WF_WEAPONBOBBING) ? player->bob : 0.); - if (curbob != bobtarget) - { - if (fabsf(bobtarget - curbob) <= 1) - { - curbob = bobtarget; - } - else - { - float zoom = MAX(1.f, fabsf(curbob - bobtarget) / 40); - if (curbob > bobtarget) - { - curbob -= zoom; - } - else - { - curbob += zoom; - } - } - } - - if (curbob != 0) - { - //[SP] Added in decorate player.viewbob checks - float bobx = float(player->bob * Rangex * (float)player->mo->ViewBob); - float boby = float(player->bob * Rangey * (float)player->mo->ViewBob); - switch (bobstyle) - { - case AWeapon::BobNormal: - xx[i] = bobx * angle.Cos(); - yy[i] = boby * fabsf(angle.Sin()); - break; - - case AWeapon::BobInverse: - xx[i] = bobx*angle.Cos(); - yy[i] = boby * (1.f - fabsf(angle.Sin())); - break; - - case AWeapon::BobAlpha: - xx[i] = bobx * angle.Sin(); - yy[i] = boby * fabsf(angle.Sin()); - break; - - case AWeapon::BobInverseAlpha: - xx[i] = bobx * angle.Sin(); - yy[i] = boby * (1.f - fabsf(angle.Sin())); - break; - - case AWeapon::BobSmooth: - xx[i] = bobx*angle.Cos(); - yy[i] = 0.5f * (boby * (1.f - ((angle * 2).Cos()))); - break; - - case AWeapon::BobInverseSmooth: - xx[i] = bobx*angle.Cos(); - yy[i] = 0.5f * (boby * (1.f + ((angle * 2).Cos()))); - } - } - else - { - xx[i] = 0; - yy[i] = 0; - } - } - *x = (float)(xx[0] * (1. - ticfrac) + xx[1] * ticfrac); - *y = (float)(yy[0] * (1. - ticfrac) + yy[1] * ticfrac); + *x = *y = 0; } //============================================================================ @@ -1363,10 +1279,11 @@ float DPSprite::GetYAdjust(bool fullscreen) } else { - return StatusBar->GetDisplacement() * fYAd; + return (float)StatusBar->GetDisplacement() * fYAd; } } } + return 0; } //------------------------------------------------------------------------ diff --git a/src/p_user.cpp b/src/p_user.cpp index e75865fcdc..50da7fe9a7 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -780,6 +780,12 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, GetNoAutostartMap) ACTION_RETURN_INT(self->userinfo.GetNoAutostartMap()); } +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetWBobSpeed) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_FLOAT(self->userinfo.GetWBobSpeed()); +} + //=========================================================================== // @@ -2278,6 +2284,7 @@ DEFINE_FIELD(APlayerPawn, AirCapacity) DEFINE_FIELD(APlayerPawn, FlechetteType) DEFINE_FIELD(APlayerPawn, DamageFade) DEFINE_FIELD(APlayerPawn, ViewBob) +DEFINE_FIELD(APlayerPawn, curBob) DEFINE_FIELD(APlayerPawn, FullHeight) DEFINE_FIELD(APlayerPawn, SoundClass) DEFINE_FIELD(APlayerPawn, Face) diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 030388efde..aabff3c2d4 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -177,6 +177,11 @@ struct VMReturn Location = loc; RegType = REGT_FLOAT; } + void Vec2At(DVector2 *loc) + { + Location = loc; + RegType = REGT_FLOAT | REGT_MULTIREG2; + } void StringAt(FString *loc) { Location = loc; @@ -190,6 +195,7 @@ struct VMReturn VMReturn() { } VMReturn(int *loc) { IntAt(loc); } VMReturn(double *loc) { FloatAt(loc); } + VMReturn(DVector2 *loc) { Vec2At(loc); } VMReturn(FString *loc) { StringAt(loc); } VMReturn(void **loc) { PointerAt(loc); } }; diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 8a1240f106..7931c40af3 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1265,3 +1265,12 @@ enum IntermissionSequenceType FSTATE_InLevel = 2 }; +enum Bobbing +{ + Bob_Normal, + Bob_Inverse, + Bob_Alpha, + Bob_InverseAlpha, + Bob_Smooth, + Bob_InverseSmooth +}; diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 2e1cda0e99..998b1ce6b9 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -49,6 +49,7 @@ class PlayerPawn : Actor native native color DamageFade; // [CW] Fades for when you are being damaged. native double ViewBob; // [SP] ViewBob Multiplier native double FullHeight; + native double curBob; meta Name HealingRadiusType; meta Name InvulMode; @@ -1939,6 +1940,116 @@ class PlayerPawn : Actor native return player.ReadyWeapon; } + //============================================================================ + // + // P_BobWeapon + // + // [RH] Moved this out of A_WeaponReady so that the weapon can bob every + // tic and not just when A_WeaponReady is called. Not all weapons execute + // A_WeaponReady every tic, and it looks bad if they don't bob smoothly. + // + // [XA] Added new bob styles and exposed bob properties. Thanks, Ryan Cordell! + // [SP] Added new user option for bob speed + // + //============================================================================ + + virtual Vector2 BobWeapon (double ticfrac) + { + Vector2 p1, p2, r; + Vector2 result; + + float bobtarget; + + let player = self.player; + if (!player) return (0, 0); + let weapon = player.ReadyWeapon; + + if (weapon == null || weapon.bDontBob) + { + return (0, 0); + } + + // [XA] Get the current weapon's bob properties. + int bobstyle = weapon.BobStyle; + double BobSpeed = (weapon.BobSpeed * 128); + double Rangex = weapon.BobRangeX; + double Rangey = weapon.BobRangeY; + + for (int i = 0; i < 2; i++) + { + // Bob the weapon based on movement speed. ([SP] And user's bob speed setting) + double angle = (BobSpeed * player.GetWBobSpeed() * 35 / TICRATE*(level.time - 1 + i)) * (360. / 8192.); + + // [RH] Smooth transitions between bobbing and not-bobbing frames. + // This also fixes the bug where you can "stick" a weapon off-center by + // shooting it when it's at the peak of its swing. + bobtarget = double((player.WeaponState & WF_WEAPONBOBBING) ? player.bob : 0.); + if (curbob != bobtarget) + { + if (abs(bobtarget - curbob) <= 1) + { + curbob = bobtarget; + } + else + { + double zoom = MAX(1., abs(curbob - bobtarget) / 40); + if (curbob > bobtarget) + { + curbob -= zoom; + } + else + { + curbob += zoom; + } + } + } + + if (curbob != 0) + { + //[SP] Added in decorate player.viewbob checks + double bobx = (player.bob * Rangex * ViewBob); + double boby = (player.bob * Rangey * ViewBob); + switch (bobstyle) + { + case Bob_Normal: + r.X = bobx * cos(angle); + r.Y = boby * abs(sin(angle)); + break; + + case Bob_Inverse: + r.X = bobx*cos(angle); + r.Y = boby * (1. - abs(sin(angle))); + break; + + case Bob_Alpha: + r.X = bobx * sin(angle); + r.Y = boby * abs(sin(angle)); + break; + + case Bob_InverseAlpha: + r.X = bobx * sin(angle); + r.Y = boby * (1. - abs(sin(angle))); + break; + + case Bob_Smooth: + r.X = bobx*cos(angle); + r.Y = 0.5f * (boby * (1. - (cos(angle * 2)))); + break; + + case Bob_InverseSmooth: + r.X = bobx*cos(angle); + r.Y = 0.5f * (boby * (1. + (cos(angle * 2)))); + } + } + else + { + r = (0, 0); + } + if (i == 0) p1 = r; else p2 = r; + } + return p1 * (1. - ticfrac) + p2 * ticfrac; + } + //---------------------------------------------------------------------------- // @@ -2169,6 +2280,7 @@ struct PlayerInfo native play // self is what internally is known as player_t native int GetTeam() const; native float GetAutoaim() const; native bool GetNoAutostartMap() const; + native double GetWBobSpeed() const; native void SetFOV(float fov); native bool GetClassicFlight() const; native clearscope bool HasWeaponsInSlot(int slot) const; From ae27acb9443430dde49e2a2e318906176080482b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 00:54:13 +0100 Subject: [PATCH 36/48] - scriptified A_WeaponReady and its subfunctions. --- src/p_pspr.cpp | 120 ------------------ .../zscript/inventory/stateprovider.txt | 3 +- wadsrc/static/zscript/inventory/weapons.txt | 104 +++++++++++++++ 3 files changed, 105 insertions(+), 122 deletions(-) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 400b34d52a..88ffca329d 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -93,8 +93,6 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO); // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static FRandom pr_wpnreadysnd ("WpnReadySnd"); - static const FGenericButtons ButtonChecks[] = { { WRF_AllowZoom, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom }, @@ -592,124 +590,6 @@ void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac) *x = *y = 0; } -//============================================================================ -// -// PROC A_WeaponReady -// -// Readies a weapon for firing or bobbing with its three ancillary functions, -// DoReadyWeaponToSwitch(), DoReadyWeaponToFire() and DoReadyWeaponToBob(). -// [XA] Added DoReadyWeaponToReload() and DoReadyWeaponToZoom() -// -//============================================================================ - -void DoReadyWeaponToSwitch (AActor *self, bool switchable) -{ - // Prepare for switching action. - player_t *player; - if (self && (player = self->player)) - { - if (switchable) - { - player->WeaponState |= WF_WEAPONSWITCHOK | WF_REFIRESWITCHOK; - } - else - { - // WF_WEAPONSWITCHOK is automatically cleared every tic by P_SetPsprite(). - player->WeaponState &= ~WF_REFIRESWITCHOK; - } - } -} - -void DoReadyWeaponDisableSwitch (AActor *self, INTBOOL disable) -{ - // Discard all switch attempts? - player_t *player; - if (self && (player = self->player)) - { - if (disable) - { - player->WeaponState |= WF_DISABLESWITCH; - player->WeaponState &= ~WF_REFIRESWITCHOK; - } - else - { - player->WeaponState &= ~WF_DISABLESWITCH; - } - } -} - -void DoReadyWeaponToFire (AActor *self, bool prim, bool alt) -{ - player_t *player; - AWeapon *weapon; - - if (!self || !(player = self->player) || !(weapon = player->ReadyWeapon)) - { - return; - } - - // Change player from attack state - if (self->InStateSequence(self->state, self->MissileState) || - self->InStateSequence(self->state, self->MeleeState)) - { - static_cast(self)->PlayIdle (); - } - - // Play ready sound, if any. - if (weapon->ReadySound && player->GetPSprite(PSP_WEAPON)->GetState() == weapon->FindState(NAME_Ready)) - { - if (!(weapon->WeaponFlags & WIF_READYSNDHALF) || pr_wpnreadysnd() < 128) - { - S_Sound (self, CHAN_WEAPON, weapon->ReadySound, 1, ATTN_NORM); - } - } - - // Prepare for firing action. - player->WeaponState |= ((prim ? WF_WEAPONREADY : 0) | (alt ? WF_WEAPONREADYALT : 0)); - return; -} - -void DoReadyWeaponToBob (AActor *self) -{ - if (self && self->player && self->player->ReadyWeapon) - { - // Prepare for bobbing action. - self->player->WeaponState |= WF_WEAPONBOBBING; - self->player->GetPSprite(PSP_WEAPON)->x = 0; - self->player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP; - } -} - -void DoReadyWeaponToGeneric(AActor *self, int paramflags) -{ - int flags = 0; - - for (size_t i = 0; i < countof(ButtonChecks); ++i) - { - if (paramflags & ButtonChecks[i].ReadyFlag) - { - flags |= ButtonChecks[i].StateFlag; - } - } - if (self != NULL && self->player != NULL) - { - self->player->WeaponState |= flags; - } -} - -DEFINE_ACTION_FUNCTION(AStateProvider, A_WeaponReady) -{ - PARAM_ACTION_PROLOGUE(AStateProvider); - PARAM_INT(flags); - - DoReadyWeaponToSwitch(self, !(flags & WRF_NoSwitch)); - if ((flags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self, !(flags & WRF_NoPrimary), !(flags & WRF_NoSecondary)); - if (!(flags & WRF_NoBob)) DoReadyWeaponToBob(self); - DoReadyWeaponToGeneric(self, flags); - DoReadyWeaponDisableSwitch(self, flags & WRF_DisableSwitch); - return 0; -} - //--------------------------------------------------------------------------- // // PROC P_CheckWeaponButtons diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index 03cfd9b8d0..3f5a88c558 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -1,8 +1,6 @@ class StateProvider : Inventory native { - action native void A_WeaponReady(int flags = 0); - //========================================================================== // // State jump function @@ -446,6 +444,7 @@ class CustomInventory : StateProvider deprecated("2.3") action void A_Lower() {} deprecated("2.3") action void A_Raise() {} deprecated("2.3") action void A_CheckReload() {} + deprecated("3.7") action void A_WeaponReady(int flags = 0) {} // this was somehow missed in 2.3 ... native bool CallStateChain (Actor actor, State state); //=========================================================================== diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 1832e2d218..d07239f4a6 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -233,6 +233,110 @@ class Weapon : StateProvider native return; } + //============================================================================ + // + // PROC A_WeaponReady + // + // Readies a weapon for firing or bobbing with its three ancillary functions, + // DoReadyWeaponToSwitch(), DoReadyWeaponToFire() and DoReadyWeaponToBob(). + // [XA] Added DoReadyWeaponToReload() and DoReadyWeaponToZoom() + // + //============================================================================ + + static void DoReadyWeaponToSwitch (PlayerInfo player, bool switchable) + { + // Prepare for switching action. + if (switchable) + { + player.WeaponState |= WF_WEAPONSWITCHOK | WF_REFIRESWITCHOK; + } + else + { + // WF_WEAPONSWITCHOK is automatically cleared every tic by P_SetPsprite(). + player.WeaponState &= ~WF_REFIRESWITCHOK; + } + } + + static void DoReadyWeaponDisableSwitch (PlayerInfo player, int disable) + { + // Discard all switch attempts? + if (disable) + { + player.WeaponState |= WF_DISABLESWITCH; + player.WeaponState &= ~WF_REFIRESWITCHOK; + } + else + { + player.WeaponState &= ~WF_DISABLESWITCH; + } + } + + static void DoReadyWeaponToFire (PlayerPawn pawn, bool prim, bool alt) + { + let player = pawn.player; + let weapon = player.ReadyWeapon; + if (!weapon) + { + return; + } + + // Change player from attack state + if (pawn.InStateSequence(pawn.curstate, pawn.MissileState) || + pawn.InStateSequence(pawn.curstate, pawn.MeleeState)) + { + pawn.PlayIdle (); + } + + // Play ready sound, if any. + if (weapon.ReadySound && player.GetPSprite(PSP_WEAPON).curState == weapon.FindState('Ready')) + { + if (!weapon.bReadySndHalf || random[WpnReadySnd]() < 128) + { + pawn.A_PlaySound(weapon.ReadySound, CHAN_WEAPON); + } + } + + // Prepare for firing action. + player.WeaponState |= ((prim ? WF_WEAPONREADY : 0) | (alt ? WF_WEAPONREADYALT : 0)); + return; + } + + static void DoReadyWeaponToBob (PlayerInfo player) + { + if (player.ReadyWeapon) + { + // Prepare for bobbing action. + player.WeaponState |= WF_WEAPONBOBBING; + let pspr = player.GetPSprite(PSP_WEAPON); + pspr.x = 0; + pspr.y = WEAPONTOP; + } + } + + static int GetButtonStateFlags(int flags) + { + // Rewritten for efficiency and clarity + int outflags = 0; + if (flags & WRF_AllowZoom) outflags |= WF_WEAPONZOOMOK; + if (flags & WRF_AllowReload) outflags |= WF_WEAPONRELOADOK; + if (flags & WRF_AllowUser1) outflags |= WF_USER1OK; + if (flags & WRF_AllowUser2) outflags |= WF_USER2OK; + if (flags & WRF_AllowUser3) outflags |= WF_USER3OK; + if (flags & WRF_AllowUser4) outflags |= WF_USER4OK; + return outflags; + } + + action void A_WeaponReady(int flags = 0) + { + if (!player) return; + DoReadyWeaponToSwitch(player, !(flags & WRF_NoSwitch)); + if ((flags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(player.mo, !(flags & WRF_NoPrimary), !(flags & WRF_NoSecondary)); + if (!(flags & WRF_NoBob)) DoReadyWeaponToBob(player); + + player.WeaponState |= GetButtonStateFlags(flags); + DoReadyWeaponDisableSwitch(player, flags & WRF_DisableSwitch); + } + //--------------------------------------------------------------------------- // // PROC A_CheckReload From e856e3c830026cb87af7093a51a56e67110ebc29 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 01:01:34 +0100 Subject: [PATCH 37/48] - moved the kickback code in P_DamageMobj into a subfunction. This is a first attempt to reduce the complexity of that 600+ lines monstrosity, and also a good first target for scriptification. --- src/p_interaction.cpp | 143 ++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 148f0a4d40..33f131a4d2 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -908,6 +908,80 @@ void P_AutoUseStrifeHealth (player_t *player) ================== */ +void ApplyKickback(AActor *target, AActor *inflictor, AActor *source, int damage, DAngle angle, FName mod, int flags) +{ + DAngle ang; + int kickback; + double thrust; + + if (inflictor && inflictor->projectileKickback) + kickback = inflictor->projectileKickback; + else if (!source || !source->player || !source->player->ReadyWeapon) + kickback = gameinfo.defKickback; + else + kickback = source->player->ReadyWeapon->Kickback; + + kickback = int(kickback * G_SkillProperty(SKILLP_KickbackFactor)); + if (kickback) + { + AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF)) ? source : inflictor; + + if (flags & DMG_USEANGLE) + { + ang = angle; + } + else if (origin->X() == target->X() && origin->Y() == target->Y()) + { + // If the origin and target are in exactly the same spot, choose a random direction. + // (Most likely cause is from telefragging somebody during spawning because they + // haven't moved from their spawn spot at all.) + ang = pr_kickbackdir.GenRand_Real2() * 360.; + } + else + { + ang = origin->AngleTo(target); + } + + thrust = mod == NAME_MDK ? 10 : 32; + if (target->Mass > 0) + { + thrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., thrust); + } + + // Don't apply ultra-small damage thrust + if (thrust < 0.01) thrust = 0; + + // make fall forwards sometimes + if ((damage < 40) && (damage > target->health) + && (target->Z() - origin->Z() > 64) + && (pr_damagemobj() & 1) + // [RH] But only if not too fast and not flying + && thrust < 10 + && !(target->flags & MF_NOGRAVITY) + && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)) + ) + { + ang += 180.; + thrust *= 4; + } + if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF) + && source->player->ReadyWeapon != NULL && + (source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK)) + { + // Staff power level 2 + target->Thrust(ang, 10); + if (!(target->flags & MF_NOGRAVITY)) + { + target->Vel.Z += 5.; + } + } + else + { + target->Thrust(ang, thrust); + } + } +} + static inline bool MustForcePain(AActor *target, AActor *inflictor) { return (inflictor && (inflictor->flags6 & MF6_FORCEPAIN)); @@ -1188,74 +1262,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da && !(target->flags7 & MF7_DONTTHRUST) && (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST))) { - int kickback; - - if (inflictor && inflictor->projectileKickback) - kickback = inflictor->projectileKickback; - else if (!source || !source->player || !source->player->ReadyWeapon) - kickback = gameinfo.defKickback; - else - kickback = source->player->ReadyWeapon->Kickback; - - kickback = int(kickback * G_SkillProperty(SKILLP_KickbackFactor)); - if (kickback) - { - AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor; - - if (flags & DMG_USEANGLE) - { - ang = angle; - } - else if (origin->X() == target->X() && origin->Y() == target->Y()) - { - // If the origin and target are in exactly the same spot, choose a random direction. - // (Most likely cause is from telefragging somebody during spawning because they - // haven't moved from their spawn spot at all.) - ang = pr_kickbackdir.GenRand_Real2() * 360.; - } - else - { - ang = origin->AngleTo(target); - } - - thrust = mod == NAME_MDK ? 10 : 32; - if (target->Mass > 0) - { - thrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., thrust); - } - - // Don't apply ultra-small damage thrust - if (thrust < 0.01) thrust = 0; - - // make fall forwards sometimes - if ((damage < 40) && (damage > target->health) - && (target->Z() - origin->Z() > 64) - && (pr_damagemobj()&1) - // [RH] But only if not too fast and not flying - && thrust < 10 - && !(target->flags & MF_NOGRAVITY) - && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)) - ) - { - ang += 180.; - thrust *= 4; - } - if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF) - && source->player->ReadyWeapon != NULL && - (source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK)) - { - // Staff power level 2 - target->Thrust(ang, 10); - if (!(target->flags & MF_NOGRAVITY)) - { - target->Vel.Z += 5.; - } - } - else - { - target->Thrust(ang, thrust); - } - } + ApplyKickback(target, inflictor, source, damage, angle, mod, flags); } // [RH] Avoid friendly fire if enabled From 460c400315778d3c5a614216c2b498f1afcaad4c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 01:12:45 +0100 Subject: [PATCH 38/48] - scriptified ApplyKickback. --- src/gi.cpp | 1 + src/p_interaction.cpp | 83 ++------------------- wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/actor_interaction.txt | 79 ++++++++++++++++++++ wadsrc/static/zscript/base.txt | 1 + wadsrc/static/zscript/constants.txt | 1 + 6 files changed, 88 insertions(+), 78 deletions(-) create mode 100644 wadsrc/static/zscript/actor_interaction.txt diff --git a/src/gi.cpp b/src/gi.cpp index 38aa76d1f3..45deb93ea0 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -62,6 +62,7 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_coop) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_dm) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mSliderColor) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, telefogheight) +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, defKickback) const char *GameNames[17] = diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 33f131a4d2..fd7f53fbe4 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -68,7 +68,6 @@ FRandom pr_damagemobj ("ActorTakeDamage"); static FRandom pr_lightning ("LightningDamage"); static FRandom pr_poison ("PoisonDamage"); static FRandom pr_switcher ("SwitchTarget"); -static FRandom pr_kickbackdir ("KickbackDir"); CVAR (Bool, cl_showsprees, true, CVAR_ARCHIVE) CVAR (Bool, cl_showmultikills, true, CVAR_ARCHIVE) @@ -908,80 +907,6 @@ void P_AutoUseStrifeHealth (player_t *player) ================== */ -void ApplyKickback(AActor *target, AActor *inflictor, AActor *source, int damage, DAngle angle, FName mod, int flags) -{ - DAngle ang; - int kickback; - double thrust; - - if (inflictor && inflictor->projectileKickback) - kickback = inflictor->projectileKickback; - else if (!source || !source->player || !source->player->ReadyWeapon) - kickback = gameinfo.defKickback; - else - kickback = source->player->ReadyWeapon->Kickback; - - kickback = int(kickback * G_SkillProperty(SKILLP_KickbackFactor)); - if (kickback) - { - AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF)) ? source : inflictor; - - if (flags & DMG_USEANGLE) - { - ang = angle; - } - else if (origin->X() == target->X() && origin->Y() == target->Y()) - { - // If the origin and target are in exactly the same spot, choose a random direction. - // (Most likely cause is from telefragging somebody during spawning because they - // haven't moved from their spawn spot at all.) - ang = pr_kickbackdir.GenRand_Real2() * 360.; - } - else - { - ang = origin->AngleTo(target); - } - - thrust = mod == NAME_MDK ? 10 : 32; - if (target->Mass > 0) - { - thrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., thrust); - } - - // Don't apply ultra-small damage thrust - if (thrust < 0.01) thrust = 0; - - // make fall forwards sometimes - if ((damage < 40) && (damage > target->health) - && (target->Z() - origin->Z() > 64) - && (pr_damagemobj() & 1) - // [RH] But only if not too fast and not flying - && thrust < 10 - && !(target->flags & MF_NOGRAVITY) - && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)) - ) - { - ang += 180.; - thrust *= 4; - } - if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF) - && source->player->ReadyWeapon != NULL && - (source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK)) - { - // Staff power level 2 - target->Thrust(ang, 10); - if (!(target->flags & MF_NOGRAVITY)) - { - target->Vel.Z += 5.; - } - } - else - { - target->Thrust(ang, thrust); - } - } -} - static inline bool MustForcePain(AActor *target, AActor *inflictor) { return (inflictor && (inflictor->flags6 & MF6_FORCEPAIN)); @@ -997,9 +922,7 @@ static inline bool isFakePain(AActor *target, AActor *inflictor, int damage) // the damage was cancelled. static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags, DAngle angle, bool& needevent) { - DAngle ang; player_t *player = NULL; - double thrust; int temp; int painchance = 0; FState * woundstate = NULL; @@ -1262,7 +1185,11 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da && !(target->flags7 & MF7_DONTTHRUST) && (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST))) { - ApplyKickback(target, inflictor, source, damage, angle, mod, flags); + IFVIRTUALPTR(target, AActor, ApplyKickback) + { + VMValue params[] = { target, inflictor, source, damage, angle.Degrees, mod.GetIndex(), flags }; + VMCall(func, params, countof(params), nullptr, 0); + } } // [RH] Avoid friendly fire if enabled diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 6c9df6c72c..d04f99ae0c 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -7,6 +7,7 @@ version "3.7" #include "zscript/actor.txt" #include "zscript/actor_attacks.txt" #include "zscript/actor_checks.txt" +#include "zscript/actor_interaction.txt" #include "zscript/events.txt" #include "zscript/destructible.txt" #include "zscript/level_compatibility.txt" diff --git a/wadsrc/static/zscript/actor_interaction.txt b/wadsrc/static/zscript/actor_interaction.txt new file mode 100644 index 0000000000..51c48deacf --- /dev/null +++ b/wadsrc/static/zscript/actor_interaction.txt @@ -0,0 +1,79 @@ +extend class Actor +{ + + virtual void ApplyKickback(Actor inflictor, Actor source, int damage, double angle, Name mod, int flags) + { + double ang; + int kickback; + double thrust; + + if (inflictor && inflictor.projectileKickback) + kickback = inflictor.projectileKickback; + else if (!source || !source.player || !source.player.ReadyWeapon) + kickback = gameinfo.defKickback; + else + kickback = source.player.ReadyWeapon.Kickback; + + kickback = int(kickback * G_SkillPropertyFloat(SKILLP_KickbackFactor)); + if (kickback) + { + Actor origin = (source && (flags & DMG_INFLICTOR_IS_PUFF)) ? source : inflictor; + + if (flags & DMG_USEANGLE) + { + ang = angle; + } + else if (origin.pos.xy == pos.xy) + { + // If the origin and target are in exactly the same spot, choose a random direction. + // (Most likely cause is from telefragging somebody during spawning because they + // haven't moved from their spawn spot at all.) + ang = frandom[Kickback](0., 360.); + } + else + { + ang = origin.AngleTo(self); + } + + thrust = mod == 'MDK' ? 10 : 32; + if (Mass > 0) + { + thrust = clamp((damage * 0.125 * kickback) / Mass, 0., thrust); + } + + // Don't apply ultra-small damage thrust + if (thrust < 0.01) thrust = 0; + + // make fall forwards sometimes + if ((damage < 40) && (damage > health) + && (pos.Z - origin.pos.Z > 64) + && (random[Kickback]() & 1) + // [RH] But only if not too fast and not flying + && thrust < 10 + && !bNoGravity + && !bNoForwardFall + && (inflictor == NULL || !inflictor.bNoForwardFall) + ) + { + ang += 180.; + thrust *= 4; + } + if (source && source.player && (flags & DMG_INFLICTOR_IS_PUFF) + && source.player.ReadyWeapon != NULL && (source.player.ReadyWeapon.bSTAFF2_KICKBACK)) + { + // Staff power level 2 + Thrust(10, ang); + if (!bNoGravity) + { + Vel.Z += 5.; + } + } + else + { + Thrust(thrust, ang); + } + } + } + + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 67c1893cc8..9a8baf9906 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -367,6 +367,7 @@ struct GameInfoStruct native native bool intermissioncounter; native Name mSliderColor; native double telefogheight; + native int defKickback; } class Object native diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 7931c40af3..9ea4375c69 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1001,6 +1001,7 @@ enum EFSkillProperty // floating point properties SKILLP_Aggressiveness, SKILLP_MonsterHealth, SKILLP_FriendlyHealth, + SKILLP_KickbackFactor, }; enum EWeaponPos From bc86ec4c5167f0226a0b61a140ba4344a905033c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 01:14:50 +0100 Subject: [PATCH 39/48] - removed the less-parameters versions of P_SpawnPlayerMissile, because there was only one native call left to them. --- src/d_net.cpp | 2 +- src/p_local.h | 3 --- src/p_mobj.cpp | 14 -------------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 4c86a664d3..4c9f09fb30 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2335,7 +2335,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player) { if (GetDefaultByType (typeinfo)->flags & MF_MISSILE) { - P_SpawnPlayerMissile (source, typeinfo); + P_SpawnPlayerMissile (source, 0, 0, 0, typeinfo, source->Angles.Yaw); } else { diff --git a/src/p_local.h b/src/p_local.h index 26d6f8b644..dcc6828075 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -131,9 +131,6 @@ AActor *P_SpawnMissileAngleZSpeed(AActor *source, double z, PClassActor *type, D AActor *P_SpawnMissileZAimed(AActor *source, double z, AActor *dest, PClassActor *type); -AActor *P_SpawnPlayerMissile (AActor* source, PClassActor *type); -AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type, DAngle angle); - AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, PClassActor *type, DAngle angle, FTranslatedLineTarget *pLineTarget = NULL, AActor **MissileActor = NULL, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a82342d5f9..089fd1972c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -7287,20 +7287,6 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnSubMissile) ================ */ -AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type) -{ - if (source == NULL) - { - return NULL; - } - return P_SpawnPlayerMissile (source, 0, 0, 0, type, source->Angles.Yaw); -} - -AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type, DAngle angle) -{ - return P_SpawnPlayerMissile (source, 0, 0, 0, type, angle); -} - AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, PClassActor *type, DAngle angle, FTranslatedLineTarget *pLineTarget, AActor **pMissileActor, bool nofreeaim, bool noautoaim, int aimflags) From f218e95c4d53b22295c9ae697709ec43985ee3c8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 01:26:19 +0100 Subject: [PATCH 40/48] - scriptified cht_Takeweaps. --- src/g_game.cpp | 32 ++++++++++-------- src/m_cheat.cpp | 33 ++++++------------- src/m_cheat.h | 1 + wadsrc/static/zscript/shared/player_cheat.txt | 24 ++++++++++++++ 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index e8bddf230e..eff1b7a3c6 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -793,19 +793,28 @@ void G_BuildTiccmd (ticcmd_t *cmd) //[Graf Zahl] This really helps if the mouse update rate can't be increased! CVAR (Bool, smooth_mouse, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +static int LookAdjust(int look) +{ + look <<= 16; + if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead. + players[consoleplayer].ReadyWeapon != NULL) // No adjustment if no weapon. + { + auto scale = players[consoleplayer].ReadyWeapon->FOVScale; + if (scale > 0) // No adjustment if it is non-positive. + { + look = int(look * scale); + } + } + return look; +} + void G_AddViewPitch (int look, bool mouse) { if (gamestate == GS_TITLELEVEL) { return; } - look <<= 16; - if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead. - players[consoleplayer].ReadyWeapon != NULL && // No adjustment if no weapon. - players[consoleplayer].ReadyWeapon->FOVScale > 0) // No adjustment if it is non-positive. - { - look = int(look * players[consoleplayer].ReadyWeapon->FOVScale); - } + look = LookAdjust(look); if (!level.IsFreelookAllowed()) { LocalViewPitch = 0; @@ -845,14 +854,9 @@ void G_AddViewAngle (int yaw, bool mouse) if (gamestate == GS_TITLELEVEL) { return; + } - yaw <<= 16; - if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead. - players[consoleplayer].ReadyWeapon != NULL && // No adjustment if no weapon. - players[consoleplayer].ReadyWeapon->FOVScale > 0) // No adjustment if it is non-positive. - { - yaw = int(yaw * players[consoleplayer].ReadyWeapon->FOVScale); - } + yaw = LookAdjust(yaw); LocalViewAngle -= yaw; if (yaw != 0) { diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index bcaccc6fba..2935605a25 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -416,29 +416,7 @@ void cht_DoCheat (player_t *player, int cheat) break; case CHT_TAKEWEAPS: - if (player->morphTics || player->mo == NULL || player->mo->health <= 0) - { - return; - } - { - // Take away all weapons that are either non-wimpy or use ammo. - AInventory **invp = &player->mo->Inventory, **lastinvp; - for (item = *invp; item != NULL; item = *invp) - { - lastinvp = invp; - invp = &(*invp)->Inventory; - if (item->IsKindOf(NAME_Weapon)) - { - AWeapon *weap = static_cast (item); - if (!(weap->WeaponFlags & WIF_WIMPY_WEAPON) || - weap->AmmoType1 != NULL) - { - item->Destroy (); - invp = lastinvp; - } - } - } - } + cht_Takeweaps(player); msg = GStrings("TXT_CHEATIDKFA"); break; @@ -613,6 +591,15 @@ void cht_Take (player_t *player, const char *name, int amount) } } +void cht_Takeweaps(player_t *player) +{ + IFVIRTUALPTR(player->mo, APlayerPawn, CheatTakeWeaps) + { + VMValue params[3] = { player->mo }; + VMCall(func, params, 1, nullptr, 0); + } +} + class DSuicider : public DThinker { DECLARE_CLASS(DSuicider, DThinker) diff --git a/src/m_cheat.h b/src/m_cheat.h index 6701bddc47..a6bb5ab687 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -16,5 +16,6 @@ void cht_Take (player_t *player, const char *item, int amount=1); void cht_SetInv(player_t *player, const char *item, int amount = 1, bool beyondMax = false); void cht_Suicide (player_t *player); FString cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo); +void cht_Takeweaps(player_t *player); #endif diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt index 1a222386e8..8c10b5898e 100644 --- a/wadsrc/static/zscript/shared/player_cheat.txt +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -426,4 +426,28 @@ extend class PlayerPawn return ""; } + virtual void CheatTakeWeaps() + { + if (player.morphTics || health <= 0) + { + return; + } + // Do not mass-delete directly from the linked list. That can cause problems. + Array collect; + // Take away all weapons that are either non-wimpy or use ammo. + for(let item = Inv; item; item = item.Inv) + { + let weap = Weapon(item); + if (weap && (!weap.bWimpy_Weapon || weap.AmmoType1 != null)) + { + collect.Push(item); + } + } + // Destroy them in a second loop. We have to look out for indirect destructions here as will happen with powered up weapons. + for(int i = 0; i < collect.Size(); i++) + { + let item = collect[i]; + if (item) item.Destroy(); + } + } } From 8fa16b6c30eb4d45552b30f7336afd40f7a076aa Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 07:21:02 +0100 Subject: [PATCH 41/48] - some cleanup on the weapon slot interface. This really shouldn't make any decisions from directly reading weapon class defaults. --- src/d_dehacked.cpp | 2 +- src/g_inventory/a_weapons.cpp | 28 ++++++++++++--------- src/g_inventory/a_weapons.h | 6 ++--- src/scripting/thingdef_properties.cpp | 9 ------- wadsrc/static/zscript/inventory/weapons.txt | 17 +++++++++++-- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index a6b9ba5a94..a04ab2240f 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -3095,7 +3095,7 @@ void FinishDehPatch () } else { - weap->WeaponFlags |= WIF_DEHAMMO; + weap->bDehAmmo = true; weap->AmmoUse1 = 0; // to allow proper checks in CheckAmmo we have to find the first attack pointer in the Fire sequence // and set its default ammo use as the weapon's AmmoUse1. diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 0d18cbcc63..f9e6357564 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -83,9 +83,8 @@ DEFINE_FIELD(AWeapon, FOVScale) DEFINE_FIELD(AWeapon, Crosshair) DEFINE_FIELD(AWeapon, GivenAsMorphWeapon) DEFINE_FIELD(AWeapon, bAltFire) -DEFINE_FIELD(AWeapon, SlotNumber) DEFINE_FIELD(AWeapon, WeaponFlags) -DEFINE_FIELD_BIT(AWeapon, WeaponFlags, bDehAmmo, WIF_DEHAMMO) +DEFINE_FIELD(AWeapon, bDehAmmo) //=========================================================================== // @@ -482,18 +481,23 @@ void FWeaponSlots::AddExtraWeapons() { continue; } - auto weapdef = ((AWeapon*)GetDefaultByType(cls)); - auto gf = cls->ActorInfo()->GameFilter; - if ((gf == GAME_Any || (gf & gameinfo.gametype)) && - cls->ActorInfo()->Replacement == nullptr && // Replaced weapons don't get slotted. - !(weapdef->WeaponFlags & WIF_POWERED_UP) && - !LocateWeapon(cls, nullptr, nullptr) // Don't duplicate it if it's already present. - ) + if (LocateWeapon(cls, nullptr, nullptr)) // Do we already have it? Don't add it again. { - int slot = weapdef->SlotNumber; - if ((unsigned)slot < NUM_WEAPON_SLOTS) + continue; + } + auto weapdef = ((AWeapon*)GetDefaultByType(cls)); + + // Let the weapon decide for itself if it wants to get added to a slot. + IFVIRTUALPTR(weapdef, AWeapon, CheckAddToSlots) + { + VMValue param = weapdef; + int slot = -1, slotpriority; + VMReturn rets[]{ &slot, &slotpriority }; + VMCall(func, ¶m, 1, rets, 2); + + if (slot >= 0 && slot < NUM_WEAPON_SLOTS) { - FWeaponSlot::WeaponInfo info = { cls, weapdef->SlotPriority }; + FWeaponSlot::WeaponInfo info = { cls, slotpriority }; Slots[slot].Weapons.Push(info); } } diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 62097b6333..1a7bfb0ded 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -148,8 +148,6 @@ public: 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. - int SlotNumber; - int SlotPriority; // In-inventory instance variables TObjPtr Ammo1, Ammo2; @@ -159,6 +157,7 @@ public: bool GivenAsMorphWeapon; // *** only accessed from ZScript. bool bAltFire; // *** only accessed from ZScript. Set when this weapon's alternate fire is used. + bool bDehAmmo; virtual void MarkPrecacheSounds() const; @@ -202,8 +201,7 @@ enum 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_DEHAMMO = 0x00010000, 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) diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 522a569eea..8701f8f4ee 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -1203,15 +1203,6 @@ DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon) defaults->BobStyle = styles[match]; } -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY(slotpriority, F, Weapon) -{ - PROP_DOUBLE_PARM(i, 0); - defaults->SlotPriority = int(i*65536); -} - //========================================================================== // //========================================================================== diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index d07239f4a6..879063a634 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -31,8 +31,10 @@ class Weapon : StateProvider native 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; - native readonly int SlotNumber; + native readonly bool bDehAmmo; // 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 + meta int SlotNumber; + meta int SlotPriority; property AmmoGive: AmmoGive1; property AmmoGive1: AmmoGive1; @@ -55,6 +57,7 @@ class Weapon : StateProvider native property BobRangeX: BobRangeX; property BobRangeY: BobRangeY; property SlotNumber: SlotNumber; + property SlotPriority: SlotPriority; Default { @@ -74,6 +77,16 @@ class Weapon : StateProvider native SHTG E 0 A_Light0; Stop; } + + + virtual int, int CheckAddToSlots() + { + if (GetReplacement(GetClass()) == null && !bPowered_Up) + { + return SlotNumber, SlotPriority*65536; + } + return -1, 0; + } virtual State GetReadyState () { From 70121799046e74bb742c8c0a914005a46d8d612e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 07:43:05 +0100 Subject: [PATCH 42/48] - moved MarkPrecacheSounds completely to the script side and added native support to make this a usable feature. --- src/actor.h | 2 -- src/d_player.h | 1 - src/g_inventory/a_pickups.cpp | 12 ----------- src/g_inventory/a_pickups.h | 1 - src/g_inventory/a_weapons.cpp | 12 ----------- src/g_inventory/a_weapons.h | 2 -- src/p_mobj.cpp | 20 ------------------- src/p_user.cpp | 10 +++++----- src/s_advsound.cpp | 18 +++++++++++++---- src/s_sound.cpp | 5 ----- wadsrc/static/zscript/actor.txt | 13 +++++++++++- wadsrc/static/zscript/base.txt | 1 + wadsrc/static/zscript/inventory/inventory.txt | 11 ++++++++++ wadsrc/static/zscript/inventory/weapons.txt | 12 +++++++++++ wadsrc/static/zscript/shared/player.txt | 13 ++++++++++++ .../static/zscript/shared/soundsequence.txt | 8 ++++++++ 16 files changed, 76 insertions(+), 65 deletions(-) diff --git a/src/actor.h b/src/actor.h index bd6b51a4da..b6d67cd184 100644 --- a/src/actor.h +++ b/src/actor.h @@ -685,8 +685,6 @@ public: void LevelSpawned(); // Called after BeginPlay if this actor was spawned by the world 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); void CallActivate(AActor *activator); diff --git a/src/d_player.h b/src/d_player.h index 6099b57aea..1008f837d4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -92,7 +92,6 @@ public: 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 bool UpdateWaterLevel (bool splash) override; diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index ac9cbe330f..1399915ee6 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -161,18 +161,6 @@ bool AInventory::Massacre() return false; } -//=========================================================================== -// -// AInventory :: MarkPrecacheSounds -// -//=========================================================================== - -void AInventory::MarkPrecacheSounds() const -{ - Super::MarkPrecacheSounds(); - PickupSound.MarkUsed(); -} - //=========================================================================== // // AInventory :: Grind diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 3815c6f771..7e4ad03f1d 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -72,7 +72,6 @@ public: virtual void Finalize(FStateDefinitions &statedef) override; virtual void Serialize(FSerializer &arc) override; - virtual void MarkPrecacheSounds() const override; virtual void OnDestroy() override; virtual void Tick() override; virtual bool Massacre() override; diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index f9e6357564..8758a132df 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -184,18 +184,6 @@ void AWeapon::Serialize(FSerializer &arc) } -//=========================================================================== -// -// AWeapon :: MarkPrecacheSounds -// -//=========================================================================== - -void AWeapon::MarkPrecacheSounds() const -{ - Super::MarkPrecacheSounds(); - UpSound.MarkUsed(); - ReadySound.MarkUsed(); -} /* Weapon slots ***********************************************************/ diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 1a7bfb0ded..4c498f7c24 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -159,8 +159,6 @@ public: bool bAltFire; // *** only accessed from ZScript. Set when this weapon's alternate fire is used. bool bDehAmmo; - virtual void MarkPrecacheSounds() const; - void Finalize(FStateDefinitions &statedef) override; void Serialize(FSerializer &arc) override; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 089fd1972c..56b19a5de5 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5310,26 +5310,6 @@ void AActor::CallPostBeginPlay() E_WorldThingSpawned(this); } -void AActor::MarkPrecacheSounds() const -{ - SeeSound.MarkUsed(); - AttackSound.MarkUsed(); - PainSound.MarkUsed(); - DeathSound.MarkUsed(); - ActiveSound.MarkUsed(); - UseSound.MarkUsed(); - BounceSound.MarkUsed(); - WallBounceSound.MarkUsed(); - CrushPainSound.MarkUsed(); -} - -DEFINE_ACTION_FUNCTION(AActor, MarkPrecacheSounds) -{ - PARAM_SELF_PROLOGUE(AActor); - self->MarkPrecacheSounds(); - return 0; -} - bool AActor::isFast() { if (flags5&MF5_ALWAYSFAST) return true; diff --git a/src/p_user.cpp b/src/p_user.cpp index 50da7fe9a7..9a8463c9d0 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -833,16 +833,16 @@ void APlayerPawn::Serialize(FSerializer &arc) //=========================================================================== // -// APlayerPawn :: MarkPrecacheSounds +// APlayerPawn :: MarkPlayerSounds // //=========================================================================== -void APlayerPawn::MarkPrecacheSounds() const +DEFINE_ACTION_FUNCTION(APlayerPawn, MarkPlayerSounds) { - Super::MarkPrecacheSounds(); - S_MarkPlayerSounds(GetSoundClass()); + PARAM_SELF_PROLOGUE(APlayerPawn); + S_MarkPlayerSounds(self->GetSoundClass()); + return 0; } - //=========================================================================== // // APlayerPawn :: BeginPlay diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index f0ec171c69..0be53d7e9e 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2232,18 +2232,20 @@ void AAmbientSound::Serialize(FSerializer &arc) //========================================================================== // -// AmbientSound :: MarkPrecacheSounds +// AmbientSound :: MarkAmbientSounds // //========================================================================== -void AAmbientSound::MarkPrecacheSounds() const +DEFINE_ACTION_FUNCTION(AAmbientSound, MarkAmbientSounds) { - Super::MarkPrecacheSounds(); - FAmbientSound *ambient = Ambients.CheckKey(args[0]); + PARAM_SELF_PROLOGUE(AAmbientSound); + + FAmbientSound *ambient = Ambients.CheckKey(self->args[0]); if (ambient != NULL) { ambient->sound.MarkUsed(); } + return 0; } //========================================================================== @@ -2458,3 +2460,11 @@ void S_ParseMusInfo() } +DEFINE_ACTION_FUNCTION(DObject, MarkSound) +{ + PARAM_PROLOGUE; + PARAM_SOUND(sound_id); + sound_id.MarkUsed(); + return 0; +} + diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 4ba1d476e7..3eec90d39d 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -504,14 +504,9 @@ void S_PrecacheLevel () { IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds) { - // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { actor }; VMCall(func, params, 1, nullptr, 0); } - else - { - actor->MarkPrecacheSounds(); - } } for (auto snd : gameinfo.PrecachedSounds) { diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index c2405b40e9..3c2eec9301 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -420,6 +420,18 @@ class Actor : Thinker native native clearscope static Vector2 RotateVector(Vector2 vec, double angle); native clearscope static double Normalize180(double ang); + virtual void MarkPrecacheSounds() + { + MarkSound(SeeSound); + MarkSound(AttackSound); + MarkSound(PainSound); + MarkSound(DeathSound); + MarkSound(ActiveSound); + MarkSound(UseSound); + MarkSound(BounceSound); + MarkSound(WallBounceSound); + MarkSound(CrushPainSound); + } bool IsPointerEqual(int ptr_select1, int ptr_select2) { @@ -439,7 +451,6 @@ class Actor : Thinker native virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0, Name MeansOfDeath = 'none'); virtual native bool Slam(Actor victim); virtual native void Touch(Actor toucher); - virtual native void MarkPrecacheSounds(); native void Substitute(Actor replacement); // Called by PIT_CheckThing to check if two actors actually can collide. diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 9a8baf9906..3aec2fef7b 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -385,6 +385,7 @@ class Object native native static void S_ResumeSound (bool notsfx); native static bool S_ChangeMusic(String music_name, int order = 0, bool looping = true, bool force = false); native static float S_GetLength(Sound sound_id); + native static void MarkSound(Sound snd); native static uint BAM(double angle); native static void SetMusicVolume(float vol); native static uint MSTime(); diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 8131190892..839e3aa71f 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -74,6 +74,17 @@ class Inventory : Actor native Stop; } + //=========================================================================== + // + // AInventory :: MarkPrecacheSounds + // + //=========================================================================== + + override void MarkPrecacheSounds() + { + Super.MarkPrecacheSounds(); + MarkSound(PickupSound); + } //=========================================================================== // diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 879063a634..b03edbde71 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -78,6 +78,18 @@ class Weapon : StateProvider native Stop; } + //=========================================================================== + // + // AWeapon :: MarkPrecacheSounds + // + //=========================================================================== + + override void MarkPrecacheSounds() + { + Super.MarkPrecacheSounds(); + MarkSound(UpSound); + MarkSound(ReadySound); + } virtual int, int CheckAddToSlots() { diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 998b1ce6b9..a213e2c0c6 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -113,6 +113,18 @@ class PlayerPawn : Actor native Obituary "$OB_MPDEFAULT"; } + //=========================================================================== + // + // APlayerPawn :: MarkPrecacheSounds + // + //=========================================================================== + + override void MarkPrecacheSounds() + { + Super.MarkPrecacheSounds(); + MarkPlayerSounds(); + } + //---------------------------------------------------------------------------- // // @@ -2065,6 +2077,7 @@ class PlayerPawn : Actor native native void CheckEnvironment(); native void CheckUse(); native void CheckWeaponButtons(); + native void MarkPlayerSounds(); } class PlayerChunk : PlayerPawn diff --git a/wadsrc/static/zscript/shared/soundsequence.txt b/wadsrc/static/zscript/shared/soundsequence.txt index c5e2b26041..e7c3f8454a 100644 --- a/wadsrc/static/zscript/shared/soundsequence.txt +++ b/wadsrc/static/zscript/shared/soundsequence.txt @@ -66,6 +66,14 @@ class AmbientSound : Actor native +NOSECTOR +DONTSPLASH } + + native void MarkAmbientSounds(); + + override void MarkPrecacheSounds() + { + Super.MarkPrecacheSounds(); + MarkAmbientSounds(); + } } class AmbientSoundNoGravity : AmbientSound From 00a48b09e57cd8cedcffd2caea6cf852e4dd9f3d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 08:16:18 +0100 Subject: [PATCH 43/48] - moved the 'Finalize' methods back into a single function in the parser code. It has been like this initially but was changed when ZDoom gained an overly complicated polymorphic class descriptor object that required a lot of support code. All these complications have long been removed but these methods remained. Since they prevent a class from being moved to the script side entirely they had to be removed. This was the last major blocker to make Weapon a purely scripted class, the only remaining native method is Serialize which is of no concern for the coming work. --- src/actor.h | 1 - src/g_inventory/a_pickups.cpp | 6 --- src/g_inventory/a_pickups.h | 1 - src/g_inventory/a_weapons.cpp | 39 -------------- src/g_inventory/a_weapons.h | 1 - src/info.cpp | 23 -------- src/p_local.h | 1 + src/scripting/decorate/thingdef_parse.cpp | 2 +- src/scripting/thingdef.cpp | 65 +++++++++++++++++++++++ src/scripting/thingdef.h | 1 + src/scripting/zscript/zcc_compile.cpp | 2 +- wadsrc/static/zscript/constants.txt | 1 + 12 files changed, 70 insertions(+), 73 deletions(-) diff --git a/src/actor.h b/src/actor.h index b6d67cd184..ffcae818c6 100644 --- a/src/actor.h +++ b/src/actor.h @@ -640,7 +640,6 @@ public: AActor &operator= (const AActor &other); ~AActor (); - virtual void Finalize(FStateDefinitions &statedef); virtual void OnDestroy() override; virtual void Serialize(FSerializer &arc) override; virtual void PostSerialize() override; diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 1399915ee6..a63030a3bf 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -54,12 +54,6 @@ EXTERN_CVAR(Bool, sv_unlimited_pickup) -void AInventory::Finalize(FStateDefinitions &statedef) -{ - Super::Finalize(statedef); - flags |= MF_SPECIAL; -} - IMPLEMENT_CLASS(AInventory, false, true) IMPLEMENT_POINTERS_START(AInventory) diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 7e4ad03f1d..ca11da98d4 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -70,7 +70,6 @@ class AInventory : public AActor HAS_OBJECT_POINTERS public: - virtual void Finalize(FStateDefinitions &statedef) override; virtual void Serialize(FSerializer &arc) override; virtual void OnDestroy() override; virtual void Tick() override; diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 8758a132df..94e65a33c7 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -101,45 +101,6 @@ TMap Weapons_hton; static int ntoh_cmp(const void *a, const void *b); -//=========================================================================== -// -// -// -//=========================================================================== - -void AWeapon::Finalize(FStateDefinitions &statedef) -{ - Super::Finalize(statedef); - FState *ready = FindState(NAME_Ready); - FState *select = FindState(NAME_Select); - FState *deselect = FindState(NAME_Deselect); - FState *fire = FindState(NAME_Fire); - auto TypeName = GetClass()->TypeName; - - // Consider any weapon without any valid state abstract and don't output a warning - // This is for creating base classes for weapon groups that only set up some properties. - if (ready || select || deselect || fire) - { - if (!ready) - { - I_Error("Weapon %s doesn't define a ready state.", TypeName.GetChars()); - } - if (!select) - { - I_Error("Weapon %s doesn't define a select state.", TypeName.GetChars()); - } - if (!deselect) - { - I_Error("Weapon %s doesn't define a deselect state.", TypeName.GetChars()); - } - if (!fire) - { - I_Error("Weapon %s doesn't define a fire state.", TypeName.GetChars()); - } - } -} - - //=========================================================================== // // AWeapon :: Serialize diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 4c498f7c24..39179999f8 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -159,7 +159,6 @@ public: bool bAltFire; // *** only accessed from ZScript. Set when this weapon's alternate fire is used. bool bDehAmmo; - void Finalize(FStateDefinitions &statedef) override; void Serialize(FSerializer &arc) override; enum diff --git a/src/info.cpp b/src/info.cpp index cfa6b067b1..00a66b45b4 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -365,29 +365,6 @@ bool PClassActor::SetReplacement(FName replaceName) return true; } -//========================================================================== -// -// PClassActor :: Finalize -// -// Installs the parsed states and does some sanity checking -// -//========================================================================== - -void AActor::Finalize(FStateDefinitions &statedef) -{ - try - { - statedef.FinishStates(GetClass()); - } - catch (CRecoverableError &) - { - statedef.MakeStateDefines(nullptr); - throw; - } - statedef.InstallStates(GetClass(), this); - statedef.MakeStateDefines(nullptr); -} - //========================================================================== // // PClassActor :: RegisterIDs diff --git a/src/p_local.h b/src/p_local.h index dcc6828075..8e4c415a20 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -316,6 +316,7 @@ enum // P_AimLineAttack flags ALF_CHECKCONVERSATION = 8, ALF_NOFRIENDS = 16, ALF_PORTALRESTRICT = 32, // only work through portals with a global offset (to be used for stuff that cannot remember the calculated FTranslatedLineTarget info) + ALF_NOWEAPONCHECK = 64, // ignore NOAUTOAIM flag on a player's weapon. }; enum // P_LineAttack flags diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index eb553028d3..d3d6335516 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -1187,7 +1187,7 @@ static void ParseActor(FScanner &sc, PNamespace *ns) } try { - GetDefaultByType(info)->Finalize(bag.statedef); + FinalizeClass(info, bag.statedef); } catch (CRecoverableError &err) { diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 95206801ef..4ca9a2eb59 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -51,6 +51,8 @@ #include "v_text.h" #include "backend/codegen.h" #include "stats.h" +#include "info.h" +#include "thingdef.h" // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void InitThingdef(); @@ -60,6 +62,69 @@ void InitThingdef(); static TMap StateSourceLines; static FScriptPosition unknownstatesource("unknown file", 0); + +//========================================================================== +// +// PClassActor :: Finalize +// +// Installs the parsed states and does some sanity checking +// +//========================================================================== + +void FinalizeClass(PClass *ccls, FStateDefinitions &statedef) +{ + if (!ccls->IsDescendantOf(NAME_Actor)) return; + auto cls = static_cast(ccls); + try + { + statedef.FinishStates(cls); + } + catch (CRecoverableError &) + { + statedef.MakeStateDefines(nullptr); + throw; + } + auto def = GetDefaultByType(cls); + statedef.InstallStates(cls, def); + statedef.MakeStateDefines(nullptr); + + if (cls->IsDescendantOf(NAME_Inventory)) + { + def->flags |= MF_SPECIAL; + } + + if (cls->IsDescendantOf(NAME_Weapon)) + { + FState *ready = def->FindState(NAME_Ready); + FState *select = def->FindState(NAME_Select); + FState *deselect = def->FindState(NAME_Deselect); + FState *fire = def->FindState(NAME_Fire); + auto TypeName = cls->TypeName; + + // Consider any weapon without any valid state abstract and don't output a warning + // This is for creating base classes for weapon groups that only set up some properties. + if (ready || select || deselect || fire) + { + if (!ready) + { + I_Error("Weapon %s doesn't define a ready state.", TypeName.GetChars()); + } + if (!select) + { + I_Error("Weapon %s doesn't define a select state.", TypeName.GetChars()); + } + if (!deselect) + { + I_Error("Weapon %s doesn't define a deselect state.", TypeName.GetChars()); + } + if (!fire) + { + I_Error("Weapon %s doesn't define a fire state.", TypeName.GetChars()); + } + } + } +} + //========================================================================== // // Saves the state's source lines for error messages during postprocessing diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 0d59dce5cc..b26ddd1d78 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -68,6 +68,7 @@ struct FFlagDef int varflags; }; +void FinalizeClass(PClass *cls, FStateDefinitions &statedef); FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict = false); void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int index); bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index a5b8d875aa..ab31ee05fa 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -3105,7 +3105,7 @@ void ZCCCompiler::CompileStates() } try { - GetDefaultByType(c->ClassType())->Finalize(statedef); + FinalizeClass(c->ClassType(), statedef); } catch (CRecoverableError &err) { diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 9ea4375c69..a1248b35c8 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -884,6 +884,7 @@ enum EAimFlags ALF_CHECKCONVERSATION = 8, ALF_NOFRIENDS = 16, ALF_PORTALRESTRICT = 32, // only work through portals with a global offset (to be used for stuff that cannot remember the calculated FTranslatedLineTarget info) + ALF_NOWEAPONCHECK = 64, // ignore NOAUTOAIM flag on a player's weapon. } enum ELineAttackFlags From b5c4ab8c47796d5e5e6a5ad2ddfaee73f6e7a438 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 08:17:37 +0100 Subject: [PATCH 44/48] - removed all direct access to AWeapon's members to prepare moving this class fully to the script side. Disregarding UI-side and setup-related calls there's unfortunately still 6 places in the native game code which require direct access. --- src/b_think.cpp | 6 ++-- src/ct_chat.cpp | 4 +-- src/d_dehacked.cpp | 36 ++++++++++++--------- src/g_game.cpp | 6 ++-- src/g_inventory/a_weapons.cpp | 2 ++ src/g_inventory/a_weapons.h | 34 ++++++++----------- src/g_shared/shared_hud.cpp | 11 ++++--- src/g_statusbar/sbarinfo.cpp | 18 +++++------ src/g_statusbar/sbarinfo_commands.cpp | 2 +- src/g_statusbar/shared_sbar.cpp | 2 +- src/namedef.h | 18 +++++++++++ src/p_conversation.cpp | 2 +- src/p_enemy.cpp | 2 +- src/p_map.cpp | 2 +- src/p_mobj.cpp | 2 +- src/p_pspr.cpp | 6 ++-- src/scripting/thingdef_properties.cpp | 10 +++--- wadsrc/static/zscript/inventory/weapons.txt | 8 ++--- 18 files changed, 95 insertions(+), 76 deletions(-) diff --git a/src/b_think.cpp b/src/b_think.cpp index 32dd8e6b1b..2099c90459 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -369,8 +369,10 @@ void DBot::WhatToGet (AActor *item) { if (!weapgiveammo) return; - if ((heldWeapon->Ammo1 == NULL || heldWeapon->Ammo1->Amount >= heldWeapon->Ammo1->MaxAmount) && - (heldWeapon->Ammo2 == NULL || heldWeapon->Ammo2->Amount >= heldWeapon->Ammo2->MaxAmount)) + auto ammo1 = heldWeapon->PointerVar(NAME_Ammo1); + auto ammo2 = heldWeapon->PointerVar(NAME_Ammo2); + if ((ammo1 == NULL || ammo1->Amount >= ammo1->MaxAmount) && + (ammo2 == NULL || ammo2->Amount >= ammo2->MaxAmount)) { return; } diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index c32a673039..9f131ab8a8 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -375,8 +375,8 @@ static bool DoSubstitution (FString &out, const char *in) { player_t *player = &players[consoleplayer]; AWeapon *weapon = player->ReadyWeapon; - auto ammo1 = weapon ? weapon->Ammo1 : nullptr; - auto ammo2 = weapon ? weapon->Ammo2 : nullptr; + auto ammo1 = weapon ? weapon->PointerVar(NAME_Ammo1) : nullptr; + auto ammo2 = weapon ? weapon->PointerVar(NAME_Ammo2) : nullptr; const char *a, *b; a = in; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index a04ab2240f..92b3630cd4 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -1583,13 +1583,15 @@ static int PatchAmmo (int ammoNum) else if (type->IsDescendantOf (RUNTIME_CLASS(AWeapon))) { AWeapon *defWeap = (AWeapon *)GetDefaultByType (type); - if (defWeap->AmmoType1 == ammoType) + if (defWeap->PointerVar(NAME_AmmoType1) == ammoType) { - defWeap->AmmoGive1 = Scale (defWeap->AmmoGive1, *per, oldclip); + auto &AmmoGive1 = defWeap->IntVar(NAME_AmmoGive1); + AmmoGive1 = Scale (AmmoGive1, *per, oldclip); } - if (defWeap->AmmoType2 == ammoType) + if (defWeap->PointerVar(NAME_AmmoType2) == ammoType) { - defWeap->AmmoGive2 = Scale (defWeap->AmmoGive2, *per, oldclip); + auto &AmmoGive2 = defWeap->IntVar(NAME_AmmoGive2); + AmmoGive2 = Scale (AmmoGive2, *per, oldclip); } } } @@ -1655,13 +1657,15 @@ static int PatchWeapon (int weapNum) { val = 5; } - info->AmmoType1 = AmmoNames[val]; - if (info->AmmoType1 != NULL) + auto &AmmoType = info->PointerVar(NAME_AmmoType1); + AmmoType = AmmoNames[val]; + if (AmmoType != nullptr) { - info->AmmoGive1 = ((AInventory*)GetDefaultByType (info->AmmoType1))->Amount * 2; - if (info->AmmoUse1 == 0) + info->IntVar(NAME_AmmoGive1) = ((AInventory*)GetDefaultByType (AmmoType))->Amount * 2; + auto &AmmoUse = info->IntVar(NAME_AmmoUse1); + if (AmmoUse == 0) { - info->AmmoUse1 = 1; + AmmoUse = 1; } } } @@ -1685,12 +1689,12 @@ static int PatchWeapon (int weapNum) } else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0) { - info->AmmoUse1 = val; + info->IntVar(NAME_AmmoUse1) = val; info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means) } else if (stricmp (Line1, "Min ammo") == 0) { - info->MinSelAmmo1 = val; + info->IntVar(NAME_MinSelAmmo1) = val; } else { @@ -1698,9 +1702,9 @@ static int PatchWeapon (int weapNum) } } - if (info->AmmoType1 == NULL) + if (info->PointerVar(NAME_AmmoType1) == nullptr) { - info->AmmoUse1 = 0; + info->IntVar(NAME_AmmoUse1) = 0; } if (patchedStates) @@ -3095,8 +3099,8 @@ void FinishDehPatch () } else { - weap->bDehAmmo = true; - weap->AmmoUse1 = 0; + weap->BoolVar(NAME_bDehAmmo) = true; + weap->IntVar(NAME_AmmoUse1) = 0; // to allow proper checks in CheckAmmo we have to find the first attack pointer in the Fire sequence // and set its default ammo use as the weapon's AmmoUse1. @@ -3123,7 +3127,7 @@ void FinishDehPatch () found = true; int use = AmmoPerAttacks[j].ammocount; if (use < 0) use = deh.BFGCells; - weap->AmmoUse1 = use; + weap->IntVar(NAME_AmmoUse1) = use; break; } } diff --git a/src/g_game.cpp b/src/g_game.cpp index eff1b7a3c6..1c4304f510 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -799,7 +799,7 @@ static int LookAdjust(int look) if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead. players[consoleplayer].ReadyWeapon != NULL) // No adjustment if no weapon. { - auto scale = players[consoleplayer].ReadyWeapon->FOVScale; + auto scale = players[consoleplayer].ReadyWeapon->FloatVar(NAME_FOVScale); if (scale > 0) // No adjustment if it is non-positive. { look = int(look * scale); @@ -1301,8 +1301,8 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) item = next; } if (p->ReadyWeapon != NULL && - p->ReadyWeapon->WeaponFlags&WIF_POWERED_UP && - p->PendingWeapon == p->ReadyWeapon->SisterWeapon) + p->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_POWERED_UP && + p->PendingWeapon == p->ReadyWeapon->PointerVar(NAME_SisterWeapon)) { // Unselect powered up weapons if the unpowered counterpart is pending p->ReadyWeapon=p->PendingWeapon; diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 94e65a33c7..86819f75e7 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -55,6 +55,7 @@ IMPLEMENT_POINTERS_START(AWeapon) IMPLEMENT_POINTER(SisterWeapon) IMPLEMENT_POINTERS_END +/* DEFINE_FIELD(AWeapon, AmmoType1) DEFINE_FIELD(AWeapon, AmmoType2) DEFINE_FIELD(AWeapon, AmmoGive1) @@ -85,6 +86,7 @@ DEFINE_FIELD(AWeapon, GivenAsMorphWeapon) DEFINE_FIELD(AWeapon, bAltFire) DEFINE_FIELD(AWeapon, WeaponFlags) DEFINE_FIELD(AWeapon, bDehAmmo) +*/ //=========================================================================== // diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 39179999f8..8fb67f8d53 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -139,7 +139,7 @@ public: 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) + double YAdjust; // For viewing the weapon fullscreen (visual only so no need to be a double) FSoundIDNoInit UpSound, ReadySound; // Sounds when coming up and idle PClassActor *SisterWeaponType; // Another weapon to pick up with this one int SelectionOrder; // Lower-numbered weapons get picked first @@ -152,32 +152,24 @@ public: // In-inventory instance variables TObjPtr Ammo1, Ammo2; TObjPtr SisterWeapon; - float FOVScale; + double FOVScale; int Crosshair; // 0 to use player's crosshair - bool GivenAsMorphWeapon; // *** only accessed from ZScript. + bool GivenAsMorphWeapon; - bool bAltFire; // *** only accessed from ZScript. Set when this weapon's alternate fire is used. + bool bAltFire; bool bDehAmmo; void Serialize(FSerializer &arc) override; +}; - enum - { - PrimaryFire, - AltFire, - EitherFire - }; - - enum - { - BobNormal, - BobInverse, - BobAlpha, - BobInverseAlpha, - BobSmooth, - BobInverseSmooth - }; - +enum class EBobStyle +{ + BobNormal, + BobInverse, + BobAlpha, + BobInverseAlpha, + BobSmooth, + BobInverseSmooth }; enum diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 88fc1ecbf1..92b664baa8 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -512,7 +512,7 @@ static void AddAmmoToList(AWeapon * weapdef) for (int i = 0; i < 2; i++) { - auto ti = i == 0 ? weapdef->AmmoType1 : weapdef->AmmoType2; + auto ti = weapdef->PointerVar(i == 0 ? NAME_AmmoType1 : NAME_AmmoType2); if (ti) { auto ammodef = (AInventory*)GetDefaultByType(ti); @@ -643,7 +643,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y) FTextureID icon = !AltIcon.isNull()? AltIcon : inv->Icon; if (!icon.isValid()) continue; - double trans= (wi && (type==wi->AmmoType1 || type==wi->AmmoType2)) ? 0.75 : 0.375; + double trans= (wi && (type==wi->PointerVar(NAME_AmmoType1) || type==wi->PointerVar(NAME_AmmoType2))) ? 0.75 : 0.375; int maxammo = inv->MaxAmount; int ammo = ammoitem? ammoitem->Amount : 0; @@ -741,13 +741,14 @@ static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AWeapon * weapon) double trans; // Powered up weapons and inherited sister weapons are not displayed. - if (weapon->WeaponFlags & WIF_POWERED_UP) return; - if (weapon->SisterWeapon && weapon->IsKindOf(weapon->SisterWeapon->GetClass())) return; + if (weapon->IntVar(NAME_WeaponFlags) & WIF_POWERED_UP) return; + auto SisterWeapon = weapon->PointerVar(NAME_SisterWeapon); + if (SisterWeapon && weapon->IsKindOf(SisterWeapon->GetClass())) return; trans=0.4; if (CPlayer->ReadyWeapon) { - if (weapon==CPlayer->ReadyWeapon || weapon==CPlayer->ReadyWeapon->SisterWeapon) trans = 0.85; + if (weapon==CPlayer->ReadyWeapon || SisterWeapon == CPlayer->ReadyWeapon) trans = 0.85; } FTextureID picnum = GetInventoryIcon(weapon, DI_ALTICONFIRST); diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index e0dc3e294c..147f32439a 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -1039,22 +1039,22 @@ public: } wrapper->ForceHUDScale(script->huds[hud]->ForceScaled()); - if (CPlayer->ReadyWeapon != NULL) + if (CPlayer->ReadyWeapon != nullptr) { - ammo1 = CPlayer->ReadyWeapon->Ammo1; - ammo2 = CPlayer->ReadyWeapon->Ammo2; - if (ammo1 == NULL) + ammo1 = CPlayer->ReadyWeapon->PointerVar(NAME_Ammo1); + ammo2 = CPlayer->ReadyWeapon->PointerVar(NAME_Ammo2); + if (ammo1 == nullptr) { ammo1 = ammo2; - ammo2 = NULL; + ammo2 = nullptr; } } else { - ammo1 = ammo2 = NULL; + ammo1 = ammo2 = nullptr; } - ammocount1 = ammo1 != NULL ? ammo1->Amount : 0; - ammocount2 = ammo2 != NULL ? ammo2->Amount : 0; + ammocount1 = ammo1 != nullptr ? ammo1->Amount : 0; + ammocount2 = ammo2 != nullptr ? ammo2->Amount : 0; //prepare ammo counts armor = CPlayer->mo->FindInventory(NAME_BasicArmor); @@ -1470,7 +1470,7 @@ public: PClassActor *AmmoType(int no) const { auto w = StatusBar->CPlayer->ReadyWeapon; - return w == nullptr ? nullptr : (no == 1 ? w->AmmoType1 : w->AmmoType2); + return w == nullptr ? nullptr : (w->PointerVar(no == 1 ? NAME_AmmoType1 : NAME_AmmoType2)); } AInventory *ammo1, *ammo2; diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index d282986679..2c94970f5c 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -245,7 +245,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl else if(type == AMMO1) { auto ammo = statusBar->ammo1; - if(ammo != NULL) + if(ammo != NULL) GetIcon(ammo); } else if(type == AMMO2) diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index e3430f2cfd..52e280d83f 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -175,7 +175,7 @@ void ST_LoadCrosshair(bool alwaysload) players[consoleplayer].camera->player != NULL && players[consoleplayer].camera->player->ReadyWeapon != NULL) { - num = players[consoleplayer].camera->player->ReadyWeapon->Crosshair; + num = players[consoleplayer].camera->player->ReadyWeapon->IntVar(NAME_Crosshair); } if (num == 0) { diff --git a/src/namedef.h b/src/namedef.h index 6dc22c1132..c40b16ca60 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -988,3 +988,21 @@ xx(snd_resampler) xx(ScriptUtil) xx(SetMarineWeapon) xx(SetMarineSprite) + +// Weapon member fields that need direct access +xx(Ammo1) +xx(Ammo2) +xx(AmmoType1) +xx(AmmoType2) +xx(AmmoGive1) +xx(AmmoGive2) +xx(AmmoUse1) +xx(SisterWeapon) +xx(BobStyle) +xx(Kickback) +xx(MinSelAmmo1) +xx(bDehAmmo) +xx(FOVScale) +xx(YAdjust) +xx(Crosshair) +xx(WeaponFlags) \ No newline at end of file diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index c577a4c2c3..d0e67ca7af 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -965,7 +965,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply if (item->GetClass()->TypeName == NAME_FlameThrower) { // The flame thrower gives less ammo when given in a dialog - static_cast(item)->AmmoGive1 = 40; + item->IntVar(NAME_AmmoGive1) = 40; } item->flags |= MF_DROPPED; if (!item->CallTryPickup(player->mo)) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 7f6f376a6d..a54603d8f6 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1029,7 +1029,7 @@ void P_NewChaseDir(AActor * actor) { // melee range of player weapon is a parameter of the action function and cannot be checked here. // Add a new weapon property? - ismeleeattacker = ((target->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) && dist < 192); + ismeleeattacker = ((target->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_MELEEWEAPON) && dist < 192); } if (ismeleeattacker) { diff --git a/src/p_map.cpp b/src/p_map.cpp index c1d50d4539..e6b2e33c96 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4459,7 +4459,7 @@ DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLin { // [BB] Disable autoaim on weapons with WIF_NOAUTOAIM. AWeapon *weapon = t1->player->ReadyWeapon; - if (weapon && (weapon->WeaponFlags & WIF_NOAUTOAIM)) + if ((weapon && (weapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM)) && !(flags & ALF_NOWEAPONCHECK)) { vrange = 0.5; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 56b19a5de5..5e555e1db9 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -7284,7 +7284,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, DAngle vrange = nofreeaim ? 35. : 0.; if (!pLineTarget) pLineTarget = &scratch; - if (source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM) || noautoaim)) + if (!(flags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim)) { // Keep exactly the same angle and pitch as the player's own aim an = angle; diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 88ffca329d..82434f1483 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -1150,16 +1150,16 @@ float DPSprite::GetYAdjust(bool fullscreen) AWeapon *weapon = dyn_cast(GetCaller()); if (weapon != nullptr) { - float fYAd = weapon->YAdjust; + auto fYAd = weapon->FloatVar(NAME_YAdjust); if (fYAd != 0) { if (fullscreen) { - return fYAd; + return (float)fYAd; } else { - return (float)StatusBar->GetDisplacement() * fYAd; + return (float)(StatusBar->GetDisplacement() * fYAd); } } } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 8701f8f4ee..0c5f836b8b 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -1181,7 +1181,7 @@ DEFINE_CLASS_PROPERTY(pickupannouncerentry, S, Inventory) //========================================================================== DEFINE_CLASS_PROPERTY(defaultkickback, 0, Weapon) { - defaults->Kickback = gameinfo.defKickback; + defaults->IntVar(NAME_Kickback) = gameinfo.defKickback; } //========================================================================== @@ -1190,9 +1190,9 @@ DEFINE_CLASS_PROPERTY(defaultkickback, 0, Weapon) DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon) { static const char *names[] = { "Normal", "Inverse", "Alpha", "InverseAlpha", "Smooth", "InverseSmooth", NULL }; - static const int styles[] = { AWeapon::BobNormal, - AWeapon::BobInverse, AWeapon::BobAlpha, AWeapon::BobInverseAlpha, - AWeapon::BobSmooth, AWeapon::BobInverseSmooth, }; + static const EBobStyle styles[] = { EBobStyle::BobNormal, + EBobStyle::BobInverse, EBobStyle::BobAlpha, EBobStyle::BobInverseAlpha, + EBobStyle::BobSmooth, EBobStyle::BobInverseSmooth, }; PROP_STRING_PARM(id, 0); int match = MatchString(id, names); if (match < 0) @@ -1200,7 +1200,7 @@ DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon) I_Error("Unknown bobstyle %s", id); match = 0; } - defaults->BobStyle = styles[match]; + defaults->IntVar(NAME_BobStyle) = (int)styles[match]; } //========================================================================== diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index b03edbde71..e98dba8e8a 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -10,13 +10,13 @@ class Weapon : StateProvider native const ZOOM_INSTANT = 1; const ZOOM_NOSCALETURNING = 2; - native uint WeaponFlags; + deprecated("3.7") native uint WeaponFlags; // not to be used directly. native class AmmoType1, AmmoType2; // Types of ammo used by self weapon native int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon - native int MinAmmo1, MinAmmo2; // not used anywhere. + deprecated("3.7") native int MinAmmo1, MinAmmo2; // not used anywhere and thus deprecated. 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 double YAdjust; // For viewing the weapon fullscreen native sound UpSound, ReadySound; // Sounds when coming up and idle native class SisterWeaponType; // Another weapon to pick up with self one native int SelectionOrder; // Lower-numbered weapons get picked first @@ -27,7 +27,7 @@ class Weapon : StateProvider native 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 double 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. From 70d3c31551e6bac569c2e37097d5ba8fe12e8030 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 09:29:12 +0100 Subject: [PATCH 45/48] - allow defining flags in the script declaration of a class and do that for Weapon. --- src/g_inventory/a_weapons.cpp | 2 - src/p_mobj.cpp | 2 +- src/sc_man_scanner.re | 1 + src/sc_man_tokens.h | 1 + src/scripting/symbols.cpp | 23 ++++++ src/scripting/symbols.h | 3 +- src/scripting/thingdef_data.cpp | 67 +++++++++++----- src/scripting/zscript/ast.cpp | 12 +++ src/scripting/zscript/zcc-parse.lemon | 12 +++ src/scripting/zscript/zcc_compile.cpp | 89 ++++++++++++++++++--- src/scripting/zscript/zcc_compile.h | 2 + src/scripting/zscript/zcc_parser.cpp | 1 + src/scripting/zscript/zcc_parser.h | 7 ++ wadsrc/static/zscript/inventory/weapons.txt | 20 +++++ 14 files changed, 207 insertions(+), 35 deletions(-) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 86819f75e7..94e65a33c7 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -55,7 +55,6 @@ IMPLEMENT_POINTERS_START(AWeapon) IMPLEMENT_POINTER(SisterWeapon) IMPLEMENT_POINTERS_END -/* DEFINE_FIELD(AWeapon, AmmoType1) DEFINE_FIELD(AWeapon, AmmoType2) DEFINE_FIELD(AWeapon, AmmoGive1) @@ -86,7 +85,6 @@ DEFINE_FIELD(AWeapon, GivenAsMorphWeapon) DEFINE_FIELD(AWeapon, bAltFire) DEFINE_FIELD(AWeapon, WeaponFlags) DEFINE_FIELD(AWeapon, bDehAmmo) -*/ //=========================================================================== // diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 5e555e1db9..1fd612acfc 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -7284,7 +7284,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, DAngle vrange = nofreeaim ? 35. : 0.; if (!pLineTarget) pLineTarget = &scratch; - if (!(flags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim)) + if (!(aimflags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim)) { // Keep exactly the same angle and pitch as the player's own aim an = angle; diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 3ef66b7609..c4fcf63997 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -179,6 +179,7 @@ std2: 'none' { RET(TK_None); } 'auto' { RET(TK_Auto); } 'property' { RET(TK_Property); } + 'flagdef' { RET(ParseVersion >= MakeVersion(3, 7, 0)? TK_FlagDef : TK_Identifier); } 'native' { RET(TK_Native); } 'var' { RET(TK_Var); } 'out' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Out : TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index dfe49518ae..82dac84d87 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -67,6 +67,7 @@ xx(TK_Long, "'long'") xx(TK_ULong, "'ulong'") xx(TK_Void, "'void'") xx(TK_Struct, "'struct'") +xx(TK_FlagDef, "'flagdef'") xx(TK_Property, "'property'") xx(TK_Class, "'class'") xx(TK_Enum, "'enum'") diff --git a/src/scripting/symbols.cpp b/src/scripting/symbols.cpp index 335edd552e..fbb06e9008 100644 --- a/src/scripting/symbols.cpp +++ b/src/scripting/symbols.cpp @@ -196,6 +196,29 @@ PProperty::PProperty(FName name, TArray &fields) Variables = std::move(fields); } +/* PProperty *****************************************************************/ + +IMPLEMENT_CLASS(PPropFlag, false, false) + +//========================================================================== +// +// PField - Default Constructor +// +//========================================================================== + +PPropFlag::PPropFlag() + : PSymbol(NAME_None) +{ +} + +PPropFlag::PPropFlag(FName name, PField * field, int bitValue, bool forDecorate) + : PSymbol(name) +{ + Offset = field; + bitval = bitValue; + decorateOnly = forDecorate; +} + //========================================================================== // // diff --git a/src/scripting/symbols.h b/src/scripting/symbols.h index bc6de46b64..954edc85a7 100644 --- a/src/scripting/symbols.h +++ b/src/scripting/symbols.h @@ -109,10 +109,11 @@ class PPropFlag : public PSymbol { DECLARE_CLASS(PPropFlag, PSymbol); public: - PPropFlag(FName name, PField *offset, int bitval); + PPropFlag(FName name, PField *offset, int bitval, bool decorateonly); PField *Offset; int bitval; + bool decorateOnly; protected: PPropFlag(); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index c88262298d..575a689c3b 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -445,30 +445,10 @@ static FFlagDef InventoryFlagDefs[] = static FFlagDef WeaponFlagDefs[] = { // Weapon flags - DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, READYSNDHALF, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, DONTBOB, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, AXEBLOOD, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NOALERT, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, AMMO_OPTIONAL, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, ALT_AMMO_OPTIONAL, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, PRIMARY_USES_BOTH, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, WIMPY_WEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, POWERED_UP, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, STAFF2_KICKBACK, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, MELEEWEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, CHEATNOTWEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NO_AUTO_SWITCH, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, AMMO_CHECKBOTH, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags), - DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), - DEFINE_DUMMY_FLAG(BFG, true), - DEFINE_DUMMY_FLAG(EXPLOSIVE, true), + DEFINE_DUMMY_FLAG(BFG, false), + DEFINE_DUMMY_FLAG(EXPLOSIVE, false), }; @@ -511,6 +491,8 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int }; #define NUM_FLAG_LISTS (countof(FlagLists)) +static FFlagDef forInternalFlags; + //========================================================================== // // Find a flag by name using a binary search @@ -548,6 +530,47 @@ static FFlagDef *FindFlag (FFlagDef *flags, int numflags, const char *flag) FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict) { + + if (part2 == nullptr) + { + FStringf internalname("@flagdef@%s", part1); + FName name(internalname, true); + if (name != NAME_None) + { + auto field = dyn_cast(type->FindSymbol(name, true)); + if (field != nullptr && (!strict || !field->decorateOnly)) + { + forInternalFlags.fieldsize = 4; + forInternalFlags.name = ""; + forInternalFlags.flagbit = 1 << field->bitval; + forInternalFlags.structoffset = field->Offset->Offset; + forInternalFlags.varflags = 0; + return &forInternalFlags; + } + } + } + else + { + FStringf internalname("@flagdef@%s.%s", part1, part2); + FName name(internalname, true); + if (name != NAME_None) + { + auto field = dyn_cast(type->FindSymbol(name, true)); + if (field != nullptr) + { + forInternalFlags.fieldsize = 4; + forInternalFlags.name = ""; + forInternalFlags.flagbit = 1 << field->bitval; + forInternalFlags.structoffset = field->Offset->Offset; + forInternalFlags.varflags = 0; + return &forInternalFlags; + } + } + } + + // Not found. Try the internal flag definitions. + + FFlagDef *def; if (part2 == NULL) diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 7731735030..99453901d2 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -348,6 +348,17 @@ static void PrintProperty(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintFlagDef(FLispString &out, ZCC_TreeNode *node) +{ + ZCC_FlagDef *snode = (ZCC_FlagDef *)node; + out.Break(); + out.Open("flagdef"); + out.AddName(snode->NodeName); + out.AddName(snode->RefName); + out.AddInt(snode->BitValue); + out.Close(); +} + static void PrintStaticArrayState(FLispString &out, ZCC_TreeNode *node) { auto *snode = (ZCC_StaticArrayStatement *)node; @@ -959,6 +970,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintExprClassCast, PrintStaticArrayState, PrintProperty, + PrintFlagDef, }; FString ZCC_PrintAST(ZCC_TreeNode *root) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 97b63198aa..83da141005 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -313,6 +313,7 @@ class_innards(X) ::= . { X = NULL; } class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); } %type property_def{ZCC_Property *} +%type flag_def{ZCC_FlagDef *} %type struct_def{ZCC_Struct *} %type enum_def {ZCC_Enum *} %type states_def {ZCC_States *} @@ -325,6 +326,7 @@ class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ } +class_member(X) ::= flag_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ } @@ -344,6 +346,16 @@ property_def(X) ::= PROPERTY(T) IDENTIFIER(A) COLON identifier_list(B) SEMICOLON X = def; } +flag_def(X) ::= FLAGDEF(T) IDENTIFIER(A) COLON IDENTIFIER(B) COMMA INTCONST(C) SEMICOLON. +{ + NEW_AST_NODE(FlagDef,def,T); + def->NodeName = A.Name(); + def->RefName = B.Name(); + def->BitValue = C.Int; + X = def; +} + + identifier_list(X) ::= IDENTIFIER(A). { NEW_AST_NODE(Identifier,id,A); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ab31ee05fa..ae36d3589a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -175,6 +175,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) cls->Properties.Push(static_cast(node)); break; + case AST_FlagDef: + cls->FlagDefs.Push(static_cast(node)); + break; + case AST_VarDeclarator: cls->Fields.Push(static_cast(node)); break; @@ -1375,6 +1379,10 @@ void ZCCCompiler::CompileAllProperties() { if (c->Properties.Size() > 0) CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName); + + if (c->FlagDefs.Size() > 0) + CompileFlagDefs(c->ClassType(), c->FlagDefs, c->Type()->TypeName); + } } @@ -1415,20 +1423,83 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper fields.Push(f); id = (ZCC_Identifier*)id->SiblingNext; } while (id != p->Body); + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); + else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); + + fields.ShrinkToFit(); + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) + { + Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } } + } + return true; +} - FString qualifiedname; - // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. - // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. - FName name = FName(p->NodeName); - if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); - else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== - fields.ShrinkToFit(); - if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) +bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + Error(Properties[0], "Flags can only be defined for actors"); + return false; + } + for (auto p : Properties) + { + PField *field; + FName referenced = FName(p->RefName); + + if (FName(p->NodeName) == FName("prefix") && Wads.GetLumpFile(Lump) == 0) { - Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. + prefix = referenced; } + else + { + field = dyn_cast(type->FindSymbol(referenced, true)); + if (field == nullptr) + { + Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); + } + if (!field->Type->isInt() || field->Type->Size != 4) + { + Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); + } + + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + for (int i = 0; i < 2; i++) + { + if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars()); + else + { + if (prefix == NAME_None) continue; + qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars()); + } + + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None))) + { + Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + + type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); + } } return true; } diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 9a4a0af8b2..238b5431c7 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -54,6 +54,7 @@ struct ZCC_ClassWork : public ZCC_StructWork TArray Defaults; TArray States; TArray Properties; + TArray FlagDefs; ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n) { @@ -110,6 +111,7 @@ private: bool CompileFields(PContainerType *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); void CompileAllProperties(); bool CompileProperties(PClass *type, TArray &Properties, FName prefix); + bool CompileFlagDefs(PClass *type, TArray &FlagDefs, FName prefix); 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, PContainerType *cls); diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 9093b7b516..fde8e1f3ff 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -150,6 +150,7 @@ static void InitTokenMap() TOKENDEF ('}', ZCC_RBRACE); TOKENDEF (TK_Struct, ZCC_STRUCT); TOKENDEF (TK_Property, ZCC_PROPERTY); + TOKENDEF (TK_FlagDef, ZCC_FLAGDEF); TOKENDEF (TK_Transient, ZCC_TRANSIENT); TOKENDEF (TK_Enum, ZCC_ENUM); TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 1089b2ed8b..272d7e1413 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -134,6 +134,7 @@ enum EZCCTreeNodeType AST_ClassCast, AST_StaticArrayStatement, AST_Property, + AST_FlagDef, NUM_AST_NODE_TYPES }; @@ -226,6 +227,12 @@ struct ZCC_Property : ZCC_NamedNode ZCC_TreeNode *Body; }; +struct ZCC_FlagDef : ZCC_NamedNode +{ + ENamedName RefName; + int BitValue; +}; + struct ZCC_Class : ZCC_Struct { ZCC_Identifier *ParentName; diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index e98dba8e8a..58b2a071d2 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -59,6 +59,26 @@ class Weapon : StateProvider native property SlotNumber: SlotNumber; property SlotPriority: SlotPriority; + flagdef NoAutoFire: WeaponFlags, 0; // weapon does not autofire + flagdef ReadySndHalf: WeaponFlags, 1; // ready sound is played ~1/2 the time + flagdef DontBob: WeaponFlags, 2; // don't bob the weapon + flagdef AxeBlood: WeaponFlags, 3; // weapon makes axe blood on impact + flagdef NoAlert: WeaponFlags, 4; // weapon does not alert monsters + flagdef Ammo_Optional: WeaponFlags, 5; // weapon can use ammo but does not require it + flagdef Alt_Ammo_Optional: WeaponFlags, 6; // alternate fire can use ammo but does not require it + flagdef Primary_Uses_Both: WeaponFlags, 7; // primary fire uses both ammo + flagdef Alt_Uses_Both: WeaponFlags, 8; // alternate fire uses both ammo + flagdef Wimpy_Weapon:WeaponFlags, 9; // change away when ammo for another weapon is replenished + flagdef Powered_Up: WeaponFlags, 10; // this is a tome-of-power'ed version of its sister + flagdef Ammo_CheckBoth: WeaponFlags, 11; // check for both primary and secondary fire before switching it off + flagdef No_Auto_Switch: WeaponFlags, 12; // never switch to this weapon when it's picked up + flagdef Staff2_Kickback: WeaponFlags, 13; // the powered-up Heretic staff has special kickback + flagdef NoAutoaim: WeaponFlags, 14; // this weapon never uses autoaim (useful for ballistic projectiles) + flagdef MeleeWeapon: WeaponFlags, 15; // melee weapon. Used by monster AI with AVOIDMELEE. + flagdef NoDeathDeselect: WeaponFlags, 16; // Don't jump to the Deselect state when the player dies + flagdef NoDeathInput: WeaponFlags, 17; // The weapon cannot be fired/reloaded/whatever when the player is dead + flagdef CheatNotWeapon: WeaponFlags, 18; // Give cheat considers this not a weapon (used by Sigil) + Default { Inventory.PickupSound "misc/w_pkup"; From d6b781312c62b1128ea775c6a3368e43ae165138 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 10:00:55 +0100 Subject: [PATCH 46/48] - removed all remaining native components of the weapon class. --- src/b_bot.h | 3 +- src/b_think.cpp | 4 +- src/ct_chat.cpp | 2 +- src/d_dehacked.cpp | 72 ++++++++++------- src/d_player.h | 12 +-- src/dobject.h | 2 + src/dobjtype.cpp | 4 +- src/fragglescript/t_func.cpp | 2 +- src/g_inventory/a_weapons.cpp | 89 +-------------------- src/g_inventory/a_weapons.h | 35 -------- src/g_shared/shared_hud.cpp | 16 ++-- src/g_statusbar/sbar.h | 2 - src/g_statusbar/sbarinfo_commands.cpp | 4 +- src/m_cheat.cpp | 2 +- src/p_actionfunctions.cpp | 4 +- src/p_map.cpp | 2 +- src/p_mobj.cpp | 2 +- src/p_pspr.cpp | 13 +-- src/p_user.cpp | 4 +- src/scripting/thingdef.cpp | 5 +- src/scripting/thingdef_data.cpp | 31 +------ src/scripting/thingdef_properties.cpp | 6 +- src/scripting/zscript/zcc_compile.cpp | 3 +- src/version.h | 4 +- wadsrc/static/zscript/inventory/weapons.txt | 60 +++++++------- 25 files changed, 130 insertions(+), 253 deletions(-) diff --git a/src/b_bot.h b/src/b_bot.h index 285216f0c4..f2cfdee81e 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -50,7 +50,6 @@ #define MMAXSELECT 100 //Maximum number of monsters that can be selected at a time. struct FCheckPosition; -class AWeapon; struct botskill_t { @@ -100,7 +99,7 @@ using BotInfoMap = TMap; extern BotInfoMap BotInfo; -inline BotInfoData GetBotInfo(AWeapon *weap) +inline BotInfoData GetBotInfo(AInventory *weap) { if (weap == nullptr) return BotInfoData(); auto k = BotInfo.CheckKey(weap->GetClass()->TypeName); diff --git a/src/b_think.cpp b/src/b_think.cpp index 2099c90459..c335a34024 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -362,9 +362,9 @@ void DBot::WhatToGet (AActor *item) if (item->IsKindOf(NAME_Weapon)) { // FIXME - AWeapon *heldWeapon; + AInventory *heldWeapon; - heldWeapon = dyn_cast(player->mo->FindInventory(item->GetClass())); + heldWeapon = player->mo->FindInventory(item->GetClass()); if (heldWeapon != NULL) { if (!weapgiveammo) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 9f131ab8a8..5f8a8e1267 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -374,7 +374,7 @@ static void ShoveChatStr (const char *str, uint8_t who) static bool DoSubstitution (FString &out, const char *in) { player_t *player = &players[consoleplayer]; - AWeapon *weapon = player->ReadyWeapon; + auto weapon = player->ReadyWeapon; auto ammo1 = weapon ? weapon->PointerVar(NAME_Ammo1) : nullptr; auto ammo2 = weapon ? weapon->PointerVar(NAME_Ammo2) : nullptr; const char *a, *b; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 92b3630cd4..506e49ff2e 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -761,8 +761,8 @@ void SetDehParams(FState *state, int codepointer) // Let's identify the codepointer we're dealing with. PFunction *sym; - sym = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(FName(MBFCodePointers[codepointer].name), true)); - if (sym == NULL) return; + sym = dyn_cast(PClass::FindActor(NAME_Weapon)->FindSymbol(FName(MBFCodePointers[codepointer].name), true)); + if (sym == NULL ) return; if (codepointer < 0 || (unsigned)codepointer >= countof(MBFCodePointerFactories)) { @@ -1580,9 +1580,9 @@ static int PatchAmmo (int ammoNum) defaultAmmo->MaxAmount = *max; defaultAmmo->Amount = Scale (defaultAmmo->Amount, *per, oldclip); } - else if (type->IsDescendantOf (RUNTIME_CLASS(AWeapon))) + else if (type->IsDescendantOf (NAME_Weapon)) { - AWeapon *defWeap = (AWeapon *)GetDefaultByType (type); + auto defWeap = GetDefaultByType (type); if (defWeap->PointerVar(NAME_AmmoType1) == ammoType) { auto &AmmoGive1 = defWeap->IntVar(NAME_AmmoGive1); @@ -1603,9 +1603,8 @@ static int PatchAmmo (int ammoNum) static int PatchWeapon (int weapNum) { int result; - PClassActor *type = NULL; - uint8_t dummy[sizeof(AWeapon)]; - AWeapon *info = (AWeapon *)&dummy; + PClassActor *type = nullptr; + AInventory *info = nullptr; bool patchedStates = false; FStateDefinitions statedef; @@ -1614,7 +1613,7 @@ static int PatchWeapon (int weapNum) type = WeaponNames[weapNum]; if (type != NULL) { - info = (AWeapon *)GetDefaultByType (type); + info = (AInventory*)GetDefaultByType (type); DPrintf (DMSG_SPAMMY, "Weapon %d\n", weapNum); } } @@ -1657,15 +1656,18 @@ static int PatchWeapon (int weapNum) { val = 5; } - auto &AmmoType = info->PointerVar(NAME_AmmoType1); - AmmoType = AmmoNames[val]; - if (AmmoType != nullptr) + if (info) { - info->IntVar(NAME_AmmoGive1) = ((AInventory*)GetDefaultByType (AmmoType))->Amount * 2; - auto &AmmoUse = info->IntVar(NAME_AmmoUse1); - if (AmmoUse == 0) + auto &AmmoType = info->PointerVar(NAME_AmmoType1); + AmmoType = AmmoNames[val]; + if (AmmoType != nullptr) { - AmmoUse = 1; + info->IntVar(NAME_AmmoGive1) = ((AInventory*)GetDefaultByType(AmmoType))->Amount * 2; + auto &AmmoUse = info->IntVar(NAME_AmmoUse1); + if (AmmoUse == 0) + { + AmmoUse = 1; + } } } } @@ -1680,7 +1682,7 @@ static int PatchWeapon (int weapNum) const FDecalTemplate *decal = DecalLibrary.GetDecalByName (Line2); if (decal != NULL) { - info->DecalGenerator = const_cast (decal); + if (info) info->DecalGenerator = const_cast (decal); } else { @@ -1689,12 +1691,15 @@ static int PatchWeapon (int weapNum) } else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0) { - info->IntVar(NAME_AmmoUse1) = val; - info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means) + if (info) + { + info->IntVar(NAME_AmmoUse1) = val; + info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means) + } } else if (stricmp (Line1, "Min ammo") == 0) { - info->IntVar(NAME_MinSelAmmo1) = val; + if (info) info->IntVar(NAME_MinSelAmmo1) = val; } else { @@ -1702,14 +1707,17 @@ static int PatchWeapon (int weapNum) } } - if (info->PointerVar(NAME_AmmoType1) == nullptr) + if (info) { - info->IntVar(NAME_AmmoUse1) = 0; - } + if (info->PointerVar(NAME_AmmoType1) == nullptr) + { + info->IntVar(NAME_AmmoUse1) = 0; + } - if (patchedStates) - { - statedef.InstallStates(type, info); + if (patchedStates) + { + statedef.InstallStates(type, info); + } } return result; @@ -2121,7 +2129,7 @@ static int PatchCodePtrs (int dummy) // This skips the action table and goes directly to the internal symbol table // DEH compatible functions are easy to recognize. - PFunction *sym = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(symname, true)); + PFunction *sym = dyn_cast(PClass::FindActor(NAME_Weapon)->FindSymbol(symname, true)); if (sym == NULL) { Printf(TEXTCOLOR_RED "Frame %d: Unknown code pointer '%s'\n", frame, Line2); @@ -2728,6 +2736,7 @@ static bool LoadDehSupp () sc.OpenLumpNum(lump); sc.SetCMode(true); + auto wcls = PClass::FindActor(NAME_Weapon); while (sc.GetString()) { if (sc.Compare("ActionList")) @@ -2742,11 +2751,11 @@ static bool LoadDehSupp () } else { - // all relevant code pointers are either defined in AWeapon - // or AActor so this will find all of them. + // all relevant code pointers are either defined in Weapon + // or Actor so this will find all of them. FString name = "A_"; name << sc.String; - PFunction *sym = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(name, true)); + PFunction *sym = dyn_cast(wcls->FindSymbol(name, true)); if (sym == NULL) { sc.ScriptError("Unknown code pointer '%s'", sc.String); @@ -3088,9 +3097,10 @@ void FinishDehPatch () // Now it gets nasty: We have to fiddle around with the weapons' ammo use info to make Doom's original // ammo consumption work as intended. + auto wcls = PClass::FindActor(NAME_Weapon); for(unsigned i = 0; i < WeaponNames.Size(); i++) { - AWeapon *weap = (AWeapon*)GetDefaultByType(WeaponNames[i]); + AInventory *weap = (AInventory*)GetDefaultByType(WeaponNames[i]); bool found = false; if (weap->flags6 & MF6_INTRYMOVE) { @@ -3119,7 +3129,7 @@ void FinishDehPatch () { if (AmmoPerAttacks[j].ptr == nullptr) { - auto p = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(AmmoPerAttacks[j].func, true)); + auto p = dyn_cast(wcls->FindSymbol(AmmoPerAttacks[j].func, true)); if (p != nullptr) AmmoPerAttacks[j].ptr = p->Variants[0].Implementation; } if (state->ActionFunc == AmmoPerAttacks[j].ptr) diff --git a/src/d_player.h b/src/d_player.h index 1008f837d4..67c409ba33 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -97,8 +97,8 @@ public: bool ResetAirSupply (bool playgasp = true); int GetMaxHealth(bool withupgrades = false) const; - AWeapon *PickNewWeapon (PClassActor *ammotype); - AWeapon *BestWeapon (PClassActor *ammotype); + AInventory *PickNewWeapon (PClassActor *ammotype); + AInventory *BestWeapon (PClassActor *ammotype); void GiveDeathmatchInventory (); void GiveDefaultInventory (); @@ -231,7 +231,7 @@ enum // 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. -extern AWeapon *WP_NOCHANGE; +extern AInventory *WP_NOCHANGE; #define MAXPLAYERNAME 15 @@ -417,8 +417,8 @@ public: uint8_t spreecount = 0; // [RH] Keep track of killing sprees uint16_t WeaponState = 0; - AWeapon *ReadyWeapon = nullptr; - AWeapon *PendingWeapon = nullptr; // WP_NOCHANGE if not changing + AInventory *ReadyWeapon = nullptr; + AInventory *PendingWeapon = nullptr; // WP_NOCHANGE if not changing TObjPtr psprites = nullptr; // view sprites (gun, etc) int cheats = 0; // bit flags @@ -443,7 +443,7 @@ public: PClassActor *MorphedPlayerClass = nullptr; // [MH] (for SBARINFO) class # for this player instance when morphed int MorphStyle = 0; // which effects to apply for this player instance when morphed PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to MorphPlayer) - TObjPtr PremorphWeapon = nullptr; // ready weapon before morphing + TObjPtr PremorphWeapon = nullptr; // ready weapon before morphing int chickenPeck = 0; // chicken peck countdown int jumpTics = 0; // delay the next jump for a moment bool onground = 0; // Identifies if this player is on the ground or other object diff --git a/src/dobject.h b/src/dobject.h index fb561e2c6f..ff885d4cab 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -426,6 +426,8 @@ template T *dyn_cast(DObject *p) return NULL; } + + template const T *dyn_cast(const DObject *p) { return dyn_cast(const_cast(p)); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index e3dfd232a3..990382016f 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -75,7 +75,7 @@ bool PClass::bVMOperational; // that does not work anymore. WP_NOCHANGE needs to point to a vaild object to work as intended. // This Object does not need to be garbage collected, though, but it needs to provide the proper structure so that the // GC can process it. -AWeapon *WP_NOCHANGE; +AInventory *WP_NOCHANGE; DEFINE_GLOBAL(WP_NOCHANGE); @@ -231,7 +231,7 @@ void PClass::StaticInit () // WP_NOCHANGE must point to a valid object, although it does not need to be a weapon. // A simple DObject is enough to give the GC the ability to deal with it, if subjected to it. - WP_NOCHANGE = (AWeapon*)Create(); + WP_NOCHANGE = (AInventory*)Create(); WP_NOCHANGE->Release(); } diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index c16896f318..b26f54d0ec 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -2713,7 +2713,7 @@ void FParser::SF_PlayerSelectedWeapon() return; } - players[playernum].PendingWeapon = (AWeapon*)players[playernum].mo->FindInventory(ti); + players[playernum].PendingWeapon = (AInventory*)players[playernum].mo->FindInventory(ti); } t_return.type = svt_int; diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 94e65a33c7..0e2f1b6ab2 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -47,45 +47,6 @@ #include "serializer.h" #include "vm.h" -IMPLEMENT_CLASS(AWeapon, false, true) - -IMPLEMENT_POINTERS_START(AWeapon) - IMPLEMENT_POINTER(Ammo1) - IMPLEMENT_POINTER(Ammo2) - IMPLEMENT_POINTER(SisterWeapon) -IMPLEMENT_POINTERS_END - -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, SelectionOrder) -DEFINE_FIELD(AWeapon, MinSelAmmo1) -DEFINE_FIELD(AWeapon, MinSelAmmo2) -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(AWeapon, WeaponFlags) -DEFINE_FIELD(AWeapon, bDehAmmo) - //=========================================================================== // // @@ -101,50 +62,6 @@ TMap Weapons_hton; static int ntoh_cmp(const void *a, const void *b); -//=========================================================================== -// -// AWeapon :: Serialize -// -//=========================================================================== - -void AWeapon::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - auto def = (AWeapon*)GetDefault(); - arc("weaponflags", WeaponFlags, def->WeaponFlags) - ("ammogive1", AmmoGive1, def->AmmoGive1) - ("ammogive2", AmmoGive2, def->AmmoGive2) - ("minammo1", MinAmmo1, def->MinAmmo1) - ("minammo2", MinAmmo2, def->MinAmmo2) - ("ammouse1", AmmoUse1, def->AmmoUse1) - ("ammouse2", AmmoUse2, def->AmmoUse2) - ("kickback", Kickback, Kickback) - ("yadjust", YAdjust, def->YAdjust) - ("upsound", UpSound, def->UpSound) - ("readysound", ReadySound, def->ReadySound) - ("selectionorder", SelectionOrder, def->SelectionOrder) - ("ammo1", Ammo1) - ("ammo2", Ammo2) - ("sisterweapon", SisterWeapon) - ("givenasmorphweapon", GivenAsMorphWeapon, def->GivenAsMorphWeapon) - ("altfire", bAltFire, def->bAltFire) - ("reloadcounter", ReloadCounter, def->ReloadCounter) - ("bobstyle", BobStyle, def->BobStyle) - ("bobspeed", BobSpeed, def->BobSpeed) - ("bobrangex", BobRangeX, def->BobRangeX) - ("bobrangey", BobRangeY, def->BobRangeY) - ("fovscale", FOVScale, def->FOVScale) - ("crosshair", Crosshair, def->Crosshair) - ("minselammo1", MinSelAmmo1, def->MinSelAmmo1) - ("minselammo2", MinSelAmmo2, def->MinSelAmmo2); - /* these can never change - ("ammotype1", AmmoType1, def->AmmoType1) - ("ammotype2", AmmoType2, def->AmmoType2) - ("sisterweapontype", SisterWeaponType, def->SisterWeaponType) - */ - -} - /* Weapon slots ***********************************************************/ @@ -376,7 +293,7 @@ bool FWeaponSlots::LocateWeapon (PClassActor *type, int *const slot, int *const DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon) { PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots); - PARAM_CLASS(weap, AWeapon); + PARAM_CLASS(weap, AInventory); int slot = 0, index = 0; bool retv = self->LocateWeapon(weap, &slot, &index); if (numret >= 1) ret[0].SetInt(retv); @@ -434,10 +351,10 @@ void FWeaponSlots::AddExtraWeapons() { continue; } - auto weapdef = ((AWeapon*)GetDefaultByType(cls)); + auto weapdef = ((AInventory*)GetDefaultByType(cls)); // Let the weapon decide for itself if it wants to get added to a slot. - IFVIRTUALPTR(weapdef, AWeapon, CheckAddToSlots) + IFVIRTUALPTRNAME(weapdef, NAME_Weapon, CheckAddToSlots) { VMValue param = weapdef; int slot = -1, slotpriority; diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 8fb67f8d53..ac2c9f14d6 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -2,7 +2,6 @@ #include "a_pickups.h" class PClassActor; -class AWeapon; class APlayerPawn; class FWeaponSlot @@ -128,40 +127,6 @@ void P_WriteDemoWeaponsChunk(uint8_t **demo); void P_ReadDemoWeaponsChunk(uint8_t **demo); -class AWeapon : public AStateProvider -{ - DECLARE_CLASS(AWeapon, AStateProvider) - HAS_OBJECT_POINTERS -public: - uint32_t WeaponFlags; - PClassActor *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; - double YAdjust; // For viewing the weapon fullscreen (visual only so no need to be a double) - FSoundIDNoInit UpSound, ReadySound; // Sounds when coming up and idle - PClassActor *SisterWeaponType; // Another weapon to pick up with this one - int SelectionOrder; // Lower-numbered weapons get picked first - int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo - 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; - double FOVScale; - int Crosshair; // 0 to use player's crosshair - bool GivenAsMorphWeapon; - - bool bAltFire; - bool bDehAmmo; - - void Serialize(FSerializer &arc) override; -}; - enum class EBobStyle { BobNormal, diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 92b664baa8..6f6ddf53d7 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -507,7 +507,7 @@ static int DrawKeys(player_t * CPlayer, int x, int y) //--------------------------------------------------------------------------- static TArray orderedammos; -static void AddAmmoToList(AWeapon * weapdef) +static void AddAmmoToList(AInventory * weapdef) { for (int i = 0; i < 2; i++) @@ -570,7 +570,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y) char buf[256]; AInventory *inv; - AWeapon *wi=CPlayer->ReadyWeapon; + auto wi=CPlayer->ReadyWeapon; orderedammos.Clear(); @@ -593,7 +593,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y) if (hud_showammo > 1 || CPlayer->mo->FindInventory(weap)) { - AddAmmoToList((AWeapon*)GetDefaultByType(weap)); + AddAmmoToList((AInventory*)GetDefaultByType(weap)); } } } @@ -603,7 +603,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y) { if (inv->IsKindOf(NAME_Weapon)) { - AddAmmoToList((AWeapon*)inv); + AddAmmoToList(inv); } } } @@ -736,7 +736,7 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetInventoryIcon) return MIN(numret, 2); } -static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AWeapon * weapon) +static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AInventory * weapon) { double trans; @@ -776,9 +776,9 @@ static void DrawWeapons(player_t *CPlayer, int x, int y) for(inv = CPlayer->mo->Inventory; inv; inv = inv->Inventory) { if (inv->IsKindOf(NAME_Weapon) && - !CPlayer->weapons.LocateWeapon(static_cast(inv)->GetClass(), NULL, NULL)) + !CPlayer->weapons.LocateWeapon(inv->GetClass(), NULL, NULL)) { - DrawOneWeapon(CPlayer, x, y, static_cast(inv)); + DrawOneWeapon(CPlayer, x, y, inv); } } @@ -791,7 +791,7 @@ static void DrawWeapons(player_t *CPlayer, int x, int y) inv=CPlayer->mo->FindInventory(weap); if (inv) { - DrawOneWeapon(CPlayer, x, y, static_cast(inv)); + DrawOneWeapon(CPlayer, x, y, inv); } } } diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h index 9f7bbb4cc7..f38e06d6b4 100644 --- a/src/g_statusbar/sbar.h +++ b/src/g_statusbar/sbar.h @@ -52,8 +52,6 @@ enum EHudState HUD_AltHud // Used for passing through popups to the alt hud }; -class AWeapon; - bool ST_IsTimeVisible(); bool ST_IsLatencyVisible(); diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index 2c94970f5c..38373f328f 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -2882,7 +2882,7 @@ class CommandIsSelected : public SBarInfoNegatableFlowControl if(weapon[i] == NULL || !weapon[i]->IsDescendantOf(NAME_Weapon)) { sc.ScriptMessage("'%s' is not a type of weapon.", sc.String); - weapon[i] = RUNTIME_CLASS(AWeapon); + weapon[i] = PClass::FindClass(NAME_Weapon); } if(sc.CheckToken(',')) @@ -3035,7 +3035,7 @@ class CommandHasWeaponPiece : public SBarInfoCommandFlowControl if (weapon == NULL || !weapon->IsDescendantOf(NAME_Weapon)) //must be a weapon { sc.ScriptMessage("%s is not a kind of weapon.", sc.String); - weapon = RUNTIME_CLASS(AWeapon); + weapon = PClass::FindClass(NAME_Weapon); } sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 2935605a25..f9c0f6783a 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -480,7 +480,7 @@ void cht_DoCheat (player_t *player, int cheat) } else { - player->PendingWeapon = static_cast (item); + player->PendingWeapon = item; } } } diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 3dea0a8a71..9d20b5bc56 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1874,7 +1874,7 @@ enum SW_Flags DEFINE_ACTION_FUNCTION(AActor, A_SelectWeapon) { PARAM_SELF_PROLOGUE(AActor); - PARAM_CLASS(cls, AWeapon); + PARAM_CLASS(cls, AInventory); PARAM_INT(flags); bool selectPriority = !!(flags & SWF_SELECTPRIORITY); @@ -1884,7 +1884,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SelectWeapon) ACTION_RETURN_BOOL(false); } - AWeapon *weaponitem = static_cast(self->FindInventory(cls)); + auto weaponitem = self->FindInventory(cls); if (weaponitem != NULL && weaponitem->IsKindOf(NAME_Weapon)) { diff --git a/src/p_map.cpp b/src/p_map.cpp index e6b2e33c96..eeee54bc6d 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4458,7 +4458,7 @@ DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLin else { // [BB] Disable autoaim on weapons with WIF_NOAUTOAIM. - AWeapon *weapon = t1->player->ReadyWeapon; + auto weapon = t1->player->ReadyWeapon; if ((weapon && (weapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM)) && !(flags & ALF_NOWEAPONCHECK)) { vrange = 0.5; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 1fd612acfc..4d5e345233 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -804,7 +804,7 @@ bool AActor::GiveInventory(PClassActor *type, int amount, bool givecheat) if (type == nullptr || !type->IsDescendantOf(RUNTIME_CLASS(AInventory))) return false; - AWeapon *savedPendingWeap = player != NULL ? player->PendingWeapon : NULL; + auto savedPendingWeap = player != NULL ? player->PendingWeapon : NULL; bool hadweap = player != NULL ? player->ReadyWeapon != NULL : true; AInventory *item; diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 82434f1483..222ab95236 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -604,7 +604,7 @@ static void P_CheckWeaponButtons (player_t *player) { return; } - AWeapon *weapon = player->ReadyWeapon; + auto weapon = player->ReadyWeapon; if (weapon == nullptr) { return; @@ -1057,12 +1057,13 @@ void player_t::DestroyPSprites() // //------------------------------------------------------------------------------------ -void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int index) +void P_SetSafeFlash(AInventory *weapon, player_t *player, FState *flashstate, int index) { + auto wcls = PClass::FindActor(NAME_Weapon); if (flashstate != nullptr) { PClassActor *cls = weapon->GetClass(); - while (cls != RUNTIME_CLASS(AWeapon)) + while (cls != wcls) { if (cls->OwnsState(flashstate)) { @@ -1100,7 +1101,7 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i DEFINE_ACTION_FUNCTION(_PlayerInfo, SetSafeFlash) { PARAM_SELF_STRUCT_PROLOGUE(player_t); - PARAM_OBJECT_NOT_NULL(weapon, AWeapon); + PARAM_OBJECT_NOT_NULL(weapon, AInventory); PARAM_POINTER(state, FState); PARAM_INT(index); P_SetSafeFlash(weapon, self, state, index); @@ -1147,8 +1148,8 @@ void DPSprite::OnDestroy() float DPSprite::GetYAdjust(bool fullscreen) { - AWeapon *weapon = dyn_cast(GetCaller()); - if (weapon != nullptr) + auto weapon = GetCaller(); + if (weapon != nullptr && weapon->IsKindOf(NAME_Weapon)) { auto fYAd = weapon->FloatVar(NAME_YAdjust); if (fYAd != 0) diff --git a/src/p_user.cpp b/src/p_user.cpp index 9a8463c9d0..1a9ef4b1fb 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1057,9 +1057,9 @@ bool APlayerPawn::UseInventory (AInventory *item) // //=========================================================================== -AWeapon *APlayerPawn::PickNewWeapon(PClassActor *ammotype) +AInventory *APlayerPawn::PickNewWeapon(PClassActor *ammotype) { - AWeapon *best = nullptr; + AInventory *best = nullptr; IFVM(PlayerPawn, DropWeapon) { VMValue param = player->mo; diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 4ca9a2eb59..186f5275ab 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -310,9 +310,10 @@ static void CheckForUnsafeStates(PClassActor *obj) TMap checked; ENamedName *test; - if (obj->IsDescendantOf(NAME_Weapon)) + auto cwtype = PClass::FindActor(NAME_Weapon); + if (obj->IsDescendantOf(cwtype)) { - if (obj->Size == RUNTIME_CLASS(AWeapon)->Size) return; // This class cannot have user variables. + if (obj->Size == cwtype->Size) return; // This class cannot have user variables. test = weaponstates; } else diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 575a689c3b..a4dfbc16c5 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -442,17 +442,6 @@ static FFlagDef InventoryFlagDefs[] = DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP), }; -static FFlagDef WeaponFlagDefs[] = -{ - // Weapon flags - DEFINE_DUMMY_FLAG(NOLMS, false), - DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), - DEFINE_DUMMY_FLAG(BFG, false), - DEFINE_DUMMY_FLAG(EXPLOSIVE, false), -}; - - - static FFlagDef PlayerPawnFlagDefs[] = { // PlayerPawn flags @@ -485,7 +474,6 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int { &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(ADynamicLight),DynLightFlagDefs, countof(DynLightFlagDefs), 3 }, }; @@ -542,8 +530,8 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo { forInternalFlags.fieldsize = 4; forInternalFlags.name = ""; - forInternalFlags.flagbit = 1 << field->bitval; - forInternalFlags.structoffset = field->Offset->Offset; + forInternalFlags.flagbit = field->bitval >= 0? 1 << field->bitval : DEPF_UNUSED; + forInternalFlags.structoffset = field->bitval >= 0 ? field->Offset->Offset : -1; forInternalFlags.varflags = 0; return &forInternalFlags; } @@ -560,8 +548,8 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo { forInternalFlags.fieldsize = 4; forInternalFlags.name = ""; - forInternalFlags.flagbit = 1 << field->bitval; - forInternalFlags.structoffset = field->Offset->Offset; + forInternalFlags.flagbit = field->bitval >= 0 ? 1 << field->bitval : DEPF_UNUSED; + forInternalFlags.structoffset = field->bitval >= 0 ? field->Offset->Offset : -1; forInternalFlags.varflags = 0; return &forInternalFlags; } @@ -994,17 +982,6 @@ void InitThingdef() void SynthesizeFlagFields() { - // These are needed for inserting the flag symbols - /* - NewClassType(RUNTIME_CLASS(DObject)); - NewClassType(RUNTIME_CLASS(DThinker)); - NewClassType(RUNTIME_CLASS(AActor)); - NewClassType(RUNTIME_CLASS(AInventory)); - NewClassType(RUNTIME_CLASS(AStateProvider)); - NewClassType(RUNTIME_CLASS(AWeapon)); - NewClassType(RUNTIME_CLASS(APlayerPawn)); - NewClassType(RUNTIME_CLASS(ADynamicLight)); - */ // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. for (auto &fl : FlagLists) { diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 0c5f836b8b..4db78bdf11 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -1179,7 +1179,7 @@ DEFINE_CLASS_PROPERTY(pickupannouncerentry, S, Inventory) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(defaultkickback, 0, Weapon) +DEFINE_SCRIPTED_PROPERTY(defaultkickback, 0, Weapon) { defaults->IntVar(NAME_Kickback) = gameinfo.defKickback; } @@ -1187,7 +1187,7 @@ DEFINE_CLASS_PROPERTY(defaultkickback, 0, Weapon) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon) +DEFINE_SCRIPTED_PROPERTY(bobstyle, S, Weapon) { static const char *names[] = { "Normal", "Inverse", "Alpha", "InverseAlpha", "Smooth", "InverseSmooth", NULL }; static const EBobStyle styles[] = { EBobStyle::BobNormal, @@ -1206,7 +1206,7 @@ DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(preferredskin, S, Weapon) +DEFINE_SCRIPTED_PROPERTY(preferredskin, S, Weapon) { PROP_STRING_PARM(str, 0); // NoOp - only for Skulltag compatibility diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ae36d3589a..512a2f801b 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1498,7 +1498,8 @@ bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray &Propertie } } - type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); + if (p->BitValue >= 0) + type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); } } return true; diff --git a/src/version.h b/src/version.h index 61c6ceff8e..de314043b7 100644 --- a/src/version.h +++ b/src/version.h @@ -87,11 +87,11 @@ const char *GetVersionString(); #define SAVEGAME_EXT "zds" // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 4551 +#define MINSAVEVER 4553 // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4552 +#define SAVEVER 4553 // This is so that derivates can use the same savegame versions without worrying about engine compatibility #define GAMESIG "GZDOOM" diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 58b2a071d2..eabe72292f 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -1,4 +1,4 @@ -class Weapon : StateProvider native +class Weapon : StateProvider { enum EFireMode { @@ -10,29 +10,29 @@ class Weapon : StateProvider native const ZOOM_INSTANT = 1; const ZOOM_NOSCALETURNING = 2; - deprecated("3.7") native uint WeaponFlags; // not to be used directly. - native class AmmoType1, AmmoType2; // Types of ammo used by self weapon - native int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon - deprecated("3.7") native int MinAmmo1, MinAmmo2; // not used anywhere and thus deprecated. - native int AmmoUse1, AmmoUse2; // How much ammo to use with each shot - native int Kickback; - native double YAdjust; // For viewing the weapon fullscreen - native sound UpSound, ReadySound; // Sounds when coming up and idle - native class SisterWeaponType; // Another weapon to pick up with self one - native int SelectionOrder; // Lower-numbered weapons get picked first - native int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo - 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 double 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; // 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 + deprecated("3.7") uint WeaponFlags; // not to be used directly. + class AmmoType1, AmmoType2; // Types of ammo used by self weapon + int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon + deprecated("3.7") int MinAmmo1, MinAmmo2; // not used anywhere and thus deprecated. + int AmmoUse1, AmmoUse2; // How much ammo to use with each shot + int Kickback; + double YAdjust; // For viewing the weapon fullscreen + sound UpSound, ReadySound; // Sounds when coming up and idle + class SisterWeaponType; // Another weapon to pick up with self one + int SelectionOrder; // Lower-numbered weapons get picked first + int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo + 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. + Ammo Ammo1, Ammo2; // In-inventory instance variables + Weapon SisterWeapon; + double FOVScale; + int Crosshair; // 0 to use player's crosshair + bool GivenAsMorphWeapon; + bool bAltFire; // Set when this weapon's alternate fire is used. + readonly bool bDehAmmo; // 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 meta int SlotNumber; meta int SlotPriority; @@ -79,6 +79,12 @@ class Weapon : StateProvider native flagdef NoDeathInput: WeaponFlags, 17; // The weapon cannot be fired/reloaded/whatever when the player is dead flagdef CheatNotWeapon: WeaponFlags, 18; // Give cheat considers this not a weapon (used by Sigil) + // no-op flags + flagdef NoLMS: WeaponFlags, -1; + flagdef Allow_With_Respawn_Invul: WeaponFlags, -1; + flagdef BFG: WeaponFlags, -1; + flagdef Explosive: WeaponFlags, -1; + Default { Inventory.PickupSound "misc/w_pkup"; @@ -100,7 +106,7 @@ class Weapon : StateProvider native //=========================================================================== // - // AWeapon :: MarkPrecacheSounds + // Weapon :: MarkPrecacheSounds // //=========================================================================== @@ -856,7 +862,7 @@ class Weapon : StateProvider native //=========================================================================== // - // AWeapon :: CheckAmmo + // Weapon :: CheckAmmo // // Returns true if there is enough ammo to shoot. If not, selects the // next weapon to use. @@ -934,7 +940,7 @@ class Weapon : StateProvider native //=========================================================================== // - // AWeapon :: DepleteAmmo + // Weapon :: DepleteAmmo // // Use up some of the weapon's ammo. Returns true if the ammo was successfully // depleted. If checkEnough is false, then the ammo will always be depleted, From f4789bdefc511ee222c2324ee5f2586cb77b1d11 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 10:06:00 +0100 Subject: [PATCH 47/48] - fixed handling of dummy flags. --- src/scripting/thingdef_data.cpp | 8 ++++---- src/scripting/zscript/zcc_compile.cpp | 20 ++++++++++++-------- wadsrc/static/zscript/inventory/weapons.txt | 8 ++++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index a4dfbc16c5..210fb7cfc3 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -530,8 +530,8 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo { forInternalFlags.fieldsize = 4; forInternalFlags.name = ""; - forInternalFlags.flagbit = field->bitval >= 0? 1 << field->bitval : DEPF_UNUSED; - forInternalFlags.structoffset = field->bitval >= 0 ? field->Offset->Offset : -1; + forInternalFlags.flagbit = field->Offset? 1 << field->bitval : DEPF_UNUSED; + forInternalFlags.structoffset = field->Offset? (int)field->Offset->Offset : -1; forInternalFlags.varflags = 0; return &forInternalFlags; } @@ -548,8 +548,8 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo { forInternalFlags.fieldsize = 4; forInternalFlags.name = ""; - forInternalFlags.flagbit = field->bitval >= 0 ? 1 << field->bitval : DEPF_UNUSED; - forInternalFlags.structoffset = field->bitval >= 0 ? field->Offset->Offset : -1; + forInternalFlags.flagbit = field->Offset ? 1 << field->bitval : DEPF_UNUSED; + forInternalFlags.structoffset = field->Offset ? (int)field->Offset->Offset : -1; forInternalFlags.varflags = 0; return &forInternalFlags; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 512a2f801b..52ab582faf 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1468,15 +1468,19 @@ bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray &Propertie } else { - field = dyn_cast(type->FindSymbol(referenced, true)); - if (field == nullptr) + if (referenced != NAME_None) { - Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); - } - if (!field->Type->isInt() || field->Type->Size != 4) - { - Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); + field = dyn_cast(type->FindSymbol(referenced, true)); + if (field == nullptr) + { + Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); + } + if (!field->Type->isInt() || field->Type->Size != 4) + { + Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); + } } + else field = nullptr; FString qualifiedname; @@ -1498,7 +1502,7 @@ bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray &Propertie } } - if (p->BitValue >= 0) + if (field != nullptr) type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); } } diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index eabe72292f..add0409124 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -80,10 +80,10 @@ class Weapon : StateProvider flagdef CheatNotWeapon: WeaponFlags, 18; // Give cheat considers this not a weapon (used by Sigil) // no-op flags - flagdef NoLMS: WeaponFlags, -1; - flagdef Allow_With_Respawn_Invul: WeaponFlags, -1; - flagdef BFG: WeaponFlags, -1; - flagdef Explosive: WeaponFlags, -1; + flagdef NoLMS: none, 0; + flagdef Allow_With_Respawn_Invul: none, 0; + flagdef BFG: none, 0; + flagdef Explosive: none, 0; Default { From 43d434b071bbb17598fb7ab2b4b2690927dfbe51 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 25 Nov 2018 10:09:06 +0100 Subject: [PATCH 48/48] - removed AStateProvider from native code. The only place still referencing it was CallStateChain, so this has been made a static function now instead of a class method. --- src/g_inventory/a_pickups.cpp | 7 ------- src/g_inventory/a_pickups.h | 7 ------- src/namedef.h | 1 + src/p_actionfunctions.cpp | 20 +++++++++---------- src/scripting/thingdef.cpp | 4 ++-- .../zscript/inventory/stateprovider.txt | 2 +- 6 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index a63030a3bf..0823fa7a1c 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -401,10 +401,3 @@ CCMD (targetinv) "the NOBLOCKMAP flag or have height/radius of 0.\n"); } -//=========================================================================== -//=========================================================================== - - - -IMPLEMENT_CLASS(AStateProvider, false, false) - diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index ca11da98d4..74c5071c68 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -103,11 +103,4 @@ public: FSoundIDNoInit PickupSound; }; -class AStateProvider : public AInventory -{ - DECLARE_CLASS (AStateProvider, AInventory) -public: - bool CallStateChain(AActor *actor, FState *state); -}; - #endif //__A_PICKUPS_H__ diff --git a/src/namedef.h b/src/namedef.h index c40b16ca60..fbb3056d62 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -201,6 +201,7 @@ xx(PowerDrain) xx(Reflection) xx(CustomInventory) xx(Inventory) +xx(StateProvider) xx(CallTryPickup) xx(QuestItem25) xx(QuestItem28) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 9d20b5bc56..e8dc860e94 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -98,7 +98,7 @@ FRandom pr_cajump("CustomJump"); extern TArray actionParams; // this can use the same storage as CallAction -bool AStateProvider::CallStateChain (AActor *actor, FState *state) +static bool CallStateChain (AActor *self, AActor *actor, FState *state) { INTBOOL result = false; int counter = 0; @@ -111,7 +111,7 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) ret[0].PointerAt((void **)&nextstate); ret[1].IntAt(&retval); - FState *savedstate = this->state; + FState *savedstate = self->state; while (state != NULL) { @@ -121,7 +121,7 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) return false; } - this->state = state; + self->state = state; nextstate = NULL; // assume no jump if (state->ActionFunc != NULL) @@ -170,7 +170,7 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) try { - state->CheckCallerType(actor, this); + state->CheckCallerType(actor, self); if (state->ActionFunc->DefaultArgs.Size() > 0) { @@ -187,7 +187,7 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) } if (state->ActionFunc->ImplicitArgs == 3) { - actionParams[index + 1] = this; + actionParams[index + 1] = self; actionParams[index + 2] = VMValue(&stp); } @@ -196,14 +196,14 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) } else { - VMValue params[3] = { actor, this, VMValue(&stp) }; + VMValue params[3] = { actor, self, VMValue(&stp) }; VMCallAction(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); } } catch (CVMAbortException &err) { err.MaybePrintMessage(); - err.stacktrace.AppendFormat("Called from state %s in inventory state chain in %s\n", FState::StaticGetStateName(state).GetChars(), GetClass()->TypeName.GetChars()); + err.stacktrace.AppendFormat("Called from state %s in inventory state chain in %s\n", FState::StaticGetStateName(state).GetChars(), self->GetClass()->TypeName.GetChars()); throw; } @@ -231,16 +231,16 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) } state = nextstate; } - this->state = savedstate; + self->state = savedstate; return !!result; } DEFINE_ACTION_FUNCTION(ACustomInventory, CallStateChain) { - PARAM_SELF_PROLOGUE(AStateProvider); + PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT(affectee, AActor); PARAM_POINTER(state, FState); - ACTION_RETURN_BOOL(self->CallStateChain(affectee, state)); + ACTION_RETURN_BOOL(CallStateChain(self, affectee, state)); } //========================================================================== diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 186f5275ab..287a499089 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -324,7 +324,7 @@ static void CheckForUnsafeStates(PClassActor *obj) if (obj->Size == citype->Size) return; // This class cannot have user variables. test = pickupstates; } - else return; // something else derived from AStateProvider. We do not know what this may be. + else return; // something else derived from StateProvider. We do not know what this may be. } for (; *test != NAME_None; test++) @@ -483,7 +483,7 @@ void LoadActors() CheckStates(ti); - if (ti->bDecorateClass && ti->IsDescendantOf(RUNTIME_CLASS(AStateProvider))) + if (ti->bDecorateClass && ti->IsDescendantOf(NAME_StateProvider)) { // either a DECORATE based weapon or CustomInventory. // These are subject to relaxed rules for user variables in states. diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index 3f5a88c558..28024fe443 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -1,5 +1,5 @@ -class StateProvider : Inventory native +class StateProvider : Inventory { //========================================================================== //