From 3dd323ac0d34abf8d0cd0517e566c2df26453174 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 27 Nov 2016 12:52:54 +0100 Subject: [PATCH] - scriptified the Flechette. Not fully tested yet. - fixed issues with the refactoring of the recent commits. This one starts again. - added builtins for TextureID. Note about builtins: Currently they are just hacked into the compiler backend. They really should be made part of the respective types to keep matters clean and allow more widespread use of builtins to create more efficient code. --- src/g_hexen/a_flechette.cpp | 455 --------------------- src/g_hexen/a_hexenmisc.cpp | 5 +- src/g_hexen/a_hexenspecialdecs.cpp | 14 - src/g_shared/a_pickups.cpp | 12 +- src/namedef.h | 5 + src/scripting/codegeneration/codegen.cpp | 46 +++ wadsrc/static/zscript/actor.txt | 4 +- wadsrc/static/zscript/hexen/flechette.txt | 375 ++++++++++++++++- wadsrc/static/zscript/shared/inventory.txt | 1 + 9 files changed, 424 insertions(+), 493 deletions(-) delete mode 100644 src/g_hexen/a_flechette.cpp diff --git a/src/g_hexen/a_flechette.cpp b/src/g_hexen/a_flechette.cpp deleted file mode 100644 index 77917c402..000000000 --- a/src/g_hexen/a_flechette.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/* -#include "actor.h" -#include "info.h" -#include "a_pickups.h" -#include "a_artifacts.h" -#include "gstrings.h" -#include "p_local.h" -#include "s_sound.h" -#include "m_random.h" -#include "a_action.h" -#include "a_hexenglobal.h" -#include "w_wad.h" -#include "vm.h" -#include "g_level.h" -*/ - -EXTERN_CVAR(Bool, sv_unlimited_pickup) - -static FRandom pr_poisonbag ("PoisonBag"); -static FRandom pr_poisoncloud ("PoisonCloud"); -static FRandom pr_poisoncloudd ("PoisonCloudDamage"); - -DECLARE_ACTION(A_CheckThrowBomb) - -// Poison Bag Artifact (Flechette) ------------------------------------------ - -IMPLEMENT_CLASS(AArtiPoisonBag, false, false) - -// Poison Bag 1 (The Cleric's) ---------------------------------------------- - -class AArtiPoisonBag1 : public AArtiPoisonBag -{ - DECLARE_CLASS (AArtiPoisonBag1, AArtiPoisonBag) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiPoisonBag1, false, false) - -bool AArtiPoisonBag1::Use (bool pickup) -{ - AActor *mo = Spawn("PoisonBag", Owner->Vec3Offset( - 16 * Owner->Angles.Yaw.Cos(), - 24 * Owner->Angles.Yaw.Sin(), - -Owner->Floorclip + 8), ALLOW_REPLACE); - if (mo) - { - mo->target = Owner; - return true; - } - return false; -} - -// Poison Bag 2 (The Mage's) ------------------------------------------------ - -class AArtiPoisonBag2 : public AArtiPoisonBag -{ - DECLARE_CLASS (AArtiPoisonBag2, AArtiPoisonBag) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiPoisonBag2, false, false) - -bool AArtiPoisonBag2::Use (bool pickup) -{ - AActor *mo = Spawn("FireBomb", Owner->Vec3Offset( - 16 * Owner->Angles.Yaw.Cos(), - 24 * Owner->Angles.Yaw.Sin(), - -Owner->Floorclip + 8), ALLOW_REPLACE); - if (mo) - { - mo->target = Owner; - return true; - } - return false; -} - -// Poison Bag 3 (The Fighter's) --------------------------------------------- - -class AArtiPoisonBag3 : public AArtiPoisonBag -{ - DECLARE_CLASS (AArtiPoisonBag3, AArtiPoisonBag) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiPoisonBag3, false, false) - -bool AArtiPoisonBag3::Use (bool pickup) -{ - AActor *mo; - - mo = Spawn("ThrowingBomb", Owner->PosPlusZ(35. - Owner->Floorclip + (Owner->player? Owner->player->crouchoffset : 0)), ALLOW_REPLACE); - if (mo) - { - mo->Angles.Yaw = Owner->Angles.Yaw + (((pr_poisonbag() & 7) - 4) * (360./256.)); - - /* Original flight code from Hexen - * mo->momz = 4*F.RACUNIT+((player->lookdir)<<(F.RACBITS-4)); - * mo->z += player->lookdir<<(F.RACBITS-4); - * P_ThrustMobj(mo, mo->angle, mo->info->speed); - * mo->momx += player->mo->momx>>1; - * mo->momy += player->mo->momy>>1; - */ - - // When looking straight ahead, it uses a z velocity of 4 while the xy velocity - // is as set by the projectile. To accommodate this with a proper trajectory, we - // aim the projectile ~20 degrees higher than we're looking at and increase the - // speed we fire at accordingly. - DAngle orgpitch = -Owner->Angles.Pitch; - DAngle modpitch = clamp(-Owner->Angles.Pitch + 20, -89., 89.); - DAngle angle = mo->Angles.Yaw; - double speed = DVector2(mo->Speed, 4.).Length(); - double xyscale = speed * modpitch.Cos(); - - mo->Vel.Z = speed * modpitch.Sin(); - mo->Vel.X = xyscale * angle.Cos() + Owner->Vel.X / 2; - mo->Vel.Y = xyscale * angle.Sin() + Owner->Vel.Y / 2; - mo->AddZ(mo->Speed * orgpitch.Sin()); - - mo->target = Owner; - mo->tics -= pr_poisonbag()&3; - P_CheckMissileSpawn(mo, Owner->radius); - return true; - } - return false; -} - -// Poison Bag 4 (Generic Giver) ---------------------------------------------- - -class AArtiPoisonBagGiver : public AArtiPoisonBag -{ - DECLARE_CLASS (AArtiPoisonBagGiver, AArtiPoisonBag) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiPoisonBagGiver, false, false) - -bool AArtiPoisonBagGiver::Use (bool pickup) -{ - PClassActor *missiletype = PClass::FindActor(this->GetClass()->MissileName); - if (missiletype != NULL) - { - AActor *mo = Spawn (missiletype, Owner->Pos(), ALLOW_REPLACE); - if (mo != NULL) - { - if (mo->IsKindOf(RUNTIME_CLASS(AInventory))) - { - AInventory *inv = static_cast(mo); - if (inv->CallTryPickup(Owner)) - return true; - } - mo->Destroy(); // Destroy if not inventory or couldn't be picked up - } - } - return false; -} - -// Poison Bag 5 (Generic Thrower) ---------------------------------------------- - -class AArtiPoisonBagShooter : public AArtiPoisonBag -{ - DECLARE_CLASS (AArtiPoisonBagShooter, AArtiPoisonBag) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiPoisonBagShooter, false, false) - -bool AArtiPoisonBagShooter::Use (bool pickup) -{ - PClassActor *missiletype = PClass::FindActor(this->GetClass()->MissileName); - if (missiletype != NULL) - { - AActor *mo = P_SpawnPlayerMissile(Owner, missiletype); - if (mo != NULL) - { - // automatic handling of seeker missiles - if (mo->flags2 & MF2_SEEKERMISSILE) - { - mo->tracer = Owner->target; - } - return true; - } - } - return false; -} - -//============================================================================ -// -// GetFlechetteType -// -//============================================================================ - -PClassActor *GetFlechetteType(AActor *other) -{ - PClassActor *spawntype = NULL; - if (other->IsKindOf(RUNTIME_CLASS(APlayerPawn))) - { - spawntype = static_cast(other)->FlechetteType; - } - if (spawntype == NULL) - { - // default fallback if nothing valid defined. - spawntype = RUNTIME_CLASS(AArtiPoisonBag3); - } - return spawntype; -} - -//============================================================================ -// -// AArtiPoisonBag :: HandlePickup -// -//============================================================================ - -bool AArtiPoisonBag::HandlePickup (AInventory *item) -{ - // Only do special handling when picking up the base class - if (item->GetClass() != RUNTIME_CLASS(AArtiPoisonBag)) - { - return Super::HandlePickup (item); - } - - if (GetClass() == GetFlechetteType(Owner)) - { - if (Amount < MaxAmount || sv_unlimited_pickup) - { - Amount += item->Amount; - if (Amount > MaxAmount && !sv_unlimited_pickup) - { - Amount = MaxAmount; - } - item->ItemFlags |= IF_PICKUPGOOD; - } - return true; - } - if (Inventory != NULL) - { - return Inventory->HandlePickup (item); - } - return false; -} - -//============================================================================ -// -// AArtiPoisonBag :: CreateCopy -// -//============================================================================ - -AInventory *AArtiPoisonBag::CreateCopy (AActor *other) -{ - // Only the base class gets special handling - if (GetClass() != RUNTIME_CLASS(AArtiPoisonBag)) - { - return Super::CreateCopy (other); - } - - AInventory *copy; - PClassActor *spawntype = GetFlechetteType(other); - copy = static_cast(Spawn (spawntype)); - copy->Amount = Amount; - copy->MaxAmount = MaxAmount; - GoAwayAndDie (); - return copy; -} - -//============================================================================ -// -// AArtiPoisonBag :: BeginPlay -// -//============================================================================ - -void AArtiPoisonBag::BeginPlay () -{ - Super::BeginPlay (); - // If a subclass's specific icon is not defined, let it use the base class's. - if (!Icon.isValid()) - { - AInventory *defbag; - // Why doesn't this work? - //defbag = GetDefault(); - defbag = (AInventory *)GetDefaultByType (RUNTIME_CLASS(AArtiPoisonBag)); - Icon = defbag->Icon; - } -} - -// Poison Cloud ------------------------------------------------------------- - -class APoisonCloud : public AActor -{ - DECLARE_CLASS (APoisonCloud, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); - void BeginPlay (); -}; - -IMPLEMENT_CLASS(APoisonCloud, false, false) - -void APoisonCloud::BeginPlay () -{ - Vel.X = MinVel; // missile objects must move to impact other objects - special1 = 24+(pr_poisoncloud()&7); - special2 = 0; -} - -int APoisonCloud::DoSpecialDamage (AActor *victim, int damage, FName damagetype) -{ - if (victim->player) - { - bool mate = (target != NULL && victim->player != target->player && victim->IsTeammate (target)); - bool dopoison; - - if (!mate) - { - dopoison = victim->player->poisoncount < 4; - } - else - { - dopoison = victim->player->poisoncount < (int)(4. * level.teamdamage); - } - - if (dopoison) - { - int damage = 15 + (pr_poisoncloudd()&15); - if (mate) - { - damage = (int)(damage * level.teamdamage); - } - // Handle passive damage modifiers (e.g. PowerProtection) - if (victim->Inventory != NULL) - { - victim->Inventory->ModifyDamage(damage, damagetype, damage, true); - } - // Modify with damage factors - damage = victim->ApplyDamageFactor(damagetype, damage); - if (damage > 0) - { - P_PoisonDamage (victim->player, this, - 15+(pr_poisoncloudd()&15), false); // Don't play painsound - - // If successful, play the poison sound. - if (P_PoisonPlayer (victim->player, this, this->target, 50)) - S_Sound (victim, CHAN_VOICE, "*poison", 1, ATTN_NORM); - } - } - return -1; - } - else if (!(victim->flags3 & MF3_ISMONSTER)) - { // only damage monsters/players with the poison cloud - return -1; - } - return damage; -} - -//=========================================================================== -// -// A_PoisonBagInit -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagInit) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - - mo = Spawn (self->PosPlusZ(28.), ALLOW_REPLACE); - if (mo) - { - mo->target = self->target; - } - return 0; -} - -//=========================================================================== -// -// A_PoisonBagCheck -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagCheck) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (--self->special1 <= 0) - { - self->SetState (self->FindState ("Death")); - } - else - { - return 0; - } - return 0; -} - -//=========================================================================== -// -// A_PoisonBagDamage -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagDamage) -{ - PARAM_SELF_PROLOGUE(AActor); - - int bobIndex; - - P_RadiusAttack (self, self->target, 4, 40, self->DamageType, RADF_HURTSOURCE); - bobIndex = self->special2; - self->AddZ(BobSin(bobIndex) / 16); - self->special2 = (bobIndex + 1) & 63; - return 0; -} - -//=========================================================================== -// -// A_CheckThrowBomb -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_CheckThrowBomb) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (--self->health <= 0) - { - self->SetState (self->FindState(NAME_Death)); - } - return 0; -} - -//=========================================================================== -// -// A_CheckThrowBomb2 -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_CheckThrowBomb2) -{ - PARAM_SELF_PROLOGUE(AActor); - - // [RH] Check using actual velocity, although the vel.z < 2 check still stands - if (self->Vel.Z < 2 && self->Vel.LengthSquared() < (9./4.)) - { - self->SetState (self->SpawnState + 6); - self->SetZ(self->floorz); - self->Vel.Z = 0; - self->BounceFlags = BOUNCE_None; - self->flags &= ~MF_MISSILE; - } - CALL_ACTION(A_CheckThrowBomb, self); - return 0; -} diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index 3223575c8..ae4399526 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -1,11 +1,10 @@ -#include "actor.h" +#include "d_player.h" #include "info.h" #include "p_local.h" #include "s_sound.h" #include "a_action.h" #include "m_random.h" #include "a_sharedglobal.h" -#include "a_hexenglobal.h" #include "i_system.h" #include "gi.h" #include "g_level.h" @@ -22,9 +21,9 @@ #include "p_maputl.h" #include "p_spec.h" #include "serializer.h" +#include "a_pickups.h" // Include all the Hexen stuff here to reduce compile time -#include "a_flechette.cpp" #include "a_flies.cpp" #include "a_heresiarch.cpp" #include "a_hexenspecialdecs.cpp" diff --git a/src/g_hexen/a_hexenspecialdecs.cpp b/src/g_hexen/a_hexenspecialdecs.cpp index 1745e6efc..899d9326d 100644 --- a/src/g_hexen/a_hexenspecialdecs.cpp +++ b/src/g_hexen/a_hexenspecialdecs.cpp @@ -264,20 +264,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_LeafCheck) return 0; } -//=========================================================================== -// -// A_PoisonShroom -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_PoisonShroom) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->tics = 128 + (pr_shroom() << 1); - return 0; -} - //=========================================================================== // // A_SoAExplode - Suit of Armor Explode diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index a57d90bcd..2172d2a0c 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -729,7 +729,7 @@ bool AInventory::CallHandlePickup(AInventory *item) auto self = this; while (self != nullptr) { - IFVIRTUAL(AActor, HandlePickup) + IFVIRTUAL(AInventory, HandlePickup) { // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)self, (DObject*)item }; @@ -794,6 +794,14 @@ void AInventory::GoAwayAndDie () } } +DEFINE_ACTION_FUNCTION(AInventory, GoAwayAndDie) +{ + PARAM_SELF_PROLOGUE(AInventory); + self->GoAwayAndDie(); + return 0; +} + + //=========================================================================== // // AInventory :: CreateCopy @@ -831,7 +839,7 @@ DEFINE_ACTION_FUNCTION(AInventory, CreateCopy) AInventory *AInventory::CallCreateCopy(AActor *other) { - IFVIRTUAL(AActor, CreateCopy) + IFVIRTUAL(AInventory, CreateCopy) { VMValue params[2] = { (DObject*)this, (DObject*)other }; VMReturn ret; diff --git a/src/namedef.h b/src/namedef.h index 779c2e132..413b68124 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -746,6 +746,11 @@ xx(StateLabel) xx(SpriteID) xx(TextureID) xx(Overlay) +xx(IsValid) +xx(IsNull) +xx(Exists) +xx(SetInvalid) +xx(SetNull) xx(A_Punch) xx(A_FirePistol) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 1e06f5751..72d79647f 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -7192,6 +7192,52 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } } + // Note: These builtins would better be relegated to the actual type objects, instead of polluting this file, but that's a task for later. + + // Texture builtins. + if (Self->ValueType == TypeTextureID) + { + if (MethodName == NAME_IsValid || MethodName == NAME_IsNull || MethodName == NAME_Exists || MethodName == NAME_SetInvalid || MethodName == NAME_SetNull) + { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } + // No need to create a dedicated node here, all builtins map directly to trivial operations. + Self->ValueType = TypeSInt32; // all builtins treat the texture index as integer. + FxExpression *x; + switch (MethodName) + { + case NAME_IsValid: + x = new FxCompareRel('>', Self, new FxConstant(0, ScriptPosition)); + break; + + case NAME_IsNull: + x = new FxCompareEq(TK_Eq, Self, new FxConstant(0, ScriptPosition)); + break; + + case NAME_Exists: + x = new FxCompareRel(TK_Geq, Self, new FxConstant(0, ScriptPosition)); + break; + + case NAME_SetInvalid: + x = new FxAssign(Self, new FxConstant(-1, ScriptPosition)); + break; + + case NAME_SetNull: + x = new FxAssign(Self, new FxConstant(0, ScriptPosition)); + break; + } + Self = nullptr; + SAFE_RESOLVE(x, ctx); + if (MethodName == NAME_SetInvalid || MethodName == NAME_SetNull) x->ValueType = TypeVoid; // override the default type of the assignment operator. + delete this; + return x; + } + } + if (Self->IsVector()) { // handle builtins: Vectors got 2: Length and Unit. diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 64a62a822..884bcc8a5 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -303,8 +303,8 @@ class Actor : Thinker native native Actor, Actor SpawnPlayerMissile(class type, double angle = 0, double x = 0, double y = 0, double z = 0, out FTranslatedLineTarget pLineTarget = null, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0); native void SpawnTeleportFog(Vector3 pos, bool beforeTele, bool setTarget); native Actor RoughMonsterSearch(int distance, bool onlyseekable = false); - native int ApplyDamageFactor(FName damagetype, int damage); - native int GetModifiedDamage(FName damagetype, int damage, bool passive); + native int ApplyDamageFactor(Name damagetype, int damage); + native int GetModifiedDamage(Name damagetype, int damage, bool passive); void A_Light(int extralight) { if (player) player.extralight = clamp(extralight, -20, 20); } void A_Light0() { if (player) player.extralight = 0; } diff --git a/wadsrc/static/zscript/hexen/flechette.txt b/wadsrc/static/zscript/hexen/flechette.txt index c02477064..f8f6a0b8a 100644 --- a/wadsrc/static/zscript/hexen/flechette.txt +++ b/wadsrc/static/zscript/hexen/flechette.txt @@ -10,8 +10,6 @@ class PoisonBag : Actor +NOBLOCKMAP +NOGRAVITY } - native void A_PoisonBagInit(); - States { Spawn: @@ -21,6 +19,21 @@ class PoisonBag : Actor PSBG C 1 A_PoisonBagInit; Stop; } + + //=========================================================================== + // + // A_PoisonBagInit + // + //=========================================================================== + + void A_PoisonBagInit() + { + Actor mo = Spawn("PoisonCloud", (pos.xy, 28), ALLOW_REPLACE); + if (mo) + { + mo.target = target; + } + } } // Fire Bomb (Flechette used by Mage) --------------------------------------- @@ -74,9 +87,6 @@ class ThrowingBomb : Actor DeathSound "FlechetteExplode"; } - native void A_CheckThrowBomb(); - native void A_CheckThrowBomb2(); - States { Spawn: @@ -84,6 +94,7 @@ class ThrowingBomb : Actor THRW BCDE 3 A_CheckThrowBomb; THRW F 3 A_CheckThrowBomb2; Loop; + Tail: THRW G 6 A_CheckThrowBomb; THRW F 4 A_CheckThrowBomb; THRW H 6 A_CheckThrowBomb; @@ -102,11 +113,46 @@ class ThrowingBomb : Actor CFCF Z 3 Bright; Stop; } + + //=========================================================================== + // + // A_CheckThrowBomb + // + //=========================================================================== + + void A_CheckThrowBomb() + { + if (--health <= 0) + { + SetStateLabel("Death"); + } + } + + //=========================================================================== + // + // A_CheckThrowBomb2 + // + //=========================================================================== + + void A_CheckThrowBomb2() + { + // [RH] Check using actual velocity, although the vel.z < 2 check still stands + if (Vel.Z < 2 && Vel.Length() < 1.5) + { + SetStateLabel("Tail"); + SetZ(floorz); + Vel.Z = 0; + ClearBounce(); + bMissile = false; + } + A_CheckThrowBomb(); + } + } // Poison Bag Artifact (Flechette) ------------------------------------------ -class ArtiPoisonBag : Inventory native +class ArtiPoisonBag : Inventory { Default { @@ -125,64 +171,262 @@ class ArtiPoisonBag : Inventory native PSBG A -1; Stop; } + + //============================================================================ + // + // AArtiPoisonBag :: BeginPlay + // + //============================================================================ + + override void BeginPlay () + { + Super.BeginPlay (); + // If a subclass's specific icon is not defined, let it use the base class's. + if (!Icon.isValid()) + { + Inventory defbag = Inventory(GetDefaultByType("ArtiPoisonBag")); + Icon = defbag.Icon; + } + } + + //============================================================================ + // + // GetFlechetteType + // + //============================================================================ + + private class GetFlechetteType(Actor other) + { + class spawntype = null; + PlayerPawn pp = PlayerPawn(other); + if (pp) + { + spawntype = pp.FlechetteType; + } + if (spawntype == null) + { + // default fallback if nothing valid defined. + spawntype = "ArtiPoisonBag3"; + } + return spawntype; + } + + //============================================================================ + // + // AArtiPoisonBag :: HandlePickup + // + //============================================================================ + + override bool HandlePickup (Inventory item) + { + // Only do special handling when picking up the base class + if (item.GetClass() != "ArtiPoisonBag") + { + return Super.HandlePickup (item); + } + + if (GetClass() == GetFlechetteType(Owner)) + { + if (Amount < MaxAmount || sv_unlimited_pickup) + { + Amount += item.Amount; + if (Amount > MaxAmount && !sv_unlimited_pickup) + { + Amount = MaxAmount; + } + item.bPickupGood = true; + } + return true; + } + return false; + } + + //============================================================================ + // + // AArtiPoisonBag :: CreateCopy + // + //============================================================================ + + override Inventory CreateCopy (Actor other) + { + // Only the base class gets special handling + if (GetClass() != "ArtiPoisonBag") + { + return Super.CreateCopy (other); + } + + class spawntype = GetFlechetteType(other); + Inventory copy = Inventory(Spawn (spawntype)); + copy.Amount = Amount; + copy.MaxAmount = MaxAmount; + GoAwayAndDie (); + return copy; + } } // Poison Bag 1 (The Cleric's) ---------------------------------------------- -class ArtiPoisonBag1 : ArtiPoisonBag native +class ArtiPoisonBag1 : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB1"; Tag "$TAG_ARTIPOISONBAG1"; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("PoisonBag", Owner.Vec3Offset( + 16 * cos(Owner.angle), + 24 * sin(Owner.angle), + -Owner.Floorclip + 8), ALLOW_REPLACE); + if (mo) + { + mo.target = Owner; + return true; + } + return false; + } + } // Poison Bag 2 (The Mage's) ------------------------------------------------ -class ArtiPoisonBag2 : ArtiPoisonBag native +class ArtiPoisonBag2 : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB2"; Tag "$TAG_ARTIPOISONBAG2"; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("FireBomb", Owner.Vec3Offset( + 16 * cos(Owner.angle), + 24 * sin(Owner.angle), + -Owner.Floorclip + 8), ALLOW_REPLACE); + if (mo) + { + mo.target = Owner; + return true; + } + return false; + } + } // Poison Bag 3 (The Fighter's) --------------------------------------------- -class ArtiPoisonBag3 : ArtiPoisonBag native +class ArtiPoisonBag3 : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB3"; Tag "$TAG_ARTIPOISONBAG3"; } + + override bool Use (bool pickup) + { + Actor mo = Spawn("ThrowingBomb", Owner.Pos + (0,0,35. - Owner.Floorclip + (Owner.player? Owner.player.crouchoffset : 0)), ALLOW_REPLACE); + if (mo) + { + mo.angle = Owner.angle + (((random[PoisonBag]() & 7) - 4) * (360./256.)); + + /* Original flight code from Hexen + * mo.momz = 4*F.RACUNIT+((player.lookdir)<<(F.RACBITS-4)); + * mo.z += player.lookdir<<(F.RACBITS-4); + * P_ThrustMobj(mo, mo.ang, mo.info.speed); + * mo.momx += player.mo.momx>>1; + * mo.momy += player.mo.momy>>1; + */ + + // When looking straight ahead, it uses a z velocity of 4 while the xy velocity + // is as set by the projectile. To accommodate self with a proper trajectory, we + // aim the projectile ~20 degrees higher than we're looking at and increase the + // speed we fire at accordingly. + double orgpitch = -Owner.Pitch; + double modpitch = clamp(-Owner.Pitch + 20, -89., 89.); + double ang = mo.angle; + double speed = (mo.Speed, 4.).Length(); + double xyscale = speed * cos(modpitch); + + mo.Vel.Z = speed * sin(modpitch); + mo.Vel.X = xyscale * cos(ang) + Owner.Vel.X / 2; + mo.Vel.Y = xyscale * sin(ang) + Owner.Vel.Y / 2; + mo.AddZ(mo.Speed * sin(modpitch)); + + mo.target = Owner; + mo.tics -= random[PoisonBag]()&3; + mo.CheckMissileSpawn(Owner.radius); + return true; + } + return false; + } + + } // Poison Bag 4 (Custom Giver) ---------------------------------------------- -class ArtiPoisonBagGiver : ArtiPoisonBag native +class ArtiPoisonBagGiver : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB4"; } + + override bool Use (bool pickup) + { + Class missiletype = MissileName; + if (missiletype != null) + { + Actor mo = Spawn (missiletype, Owner.Pos, ALLOW_REPLACE); + if (mo != null) + { + Inventory inv = Inventory(mo); + if (inv && inv.CallTryPickup(Owner)) return true; + mo.Destroy(); // Destroy if not inventory or couldn't be picked up + } + } + return false; + } + } // Poison Bag 5 (Custom Thrower) -------------------------------------------- -class ArtiPoisonBagShooter : ArtiPoisonBag native +class ArtiPoisonBagShooter : ArtiPoisonBag { Default { Inventory.Icon "ARTIPSB5"; } + + override bool Use (bool pickup) + { + Class missiletype = MissileName; + if (missiletype != null) + { + Actor mo = Owner.SpawnPlayerMissile(missiletype); + if (mo != null) + { + // automatic handling of seeker missiles + if (mo.bSeekerMissile) + { + mo.tracer = Owner.target; + } + return true; + } + } + return false; + } + } // Poison Cloud ------------------------------------------------------------- -class PoisonCloud : Actor native +class PoisonCloud : Actor { Default { @@ -198,9 +442,6 @@ class PoisonCloud : Actor native DamageType "PoisonCloud"; } - native void A_PoisonBagDamage(); - native void A_PoisonBagCheck(); - States { Spawn: @@ -215,6 +456,97 @@ class PoisonCloud : Actor native PSBG FD 6; Stop; } + + //=========================================================================== + // + // + // + //=========================================================================== + + override void BeginPlay () + { + Vel.X = MinVel; // missile objects must move to impact other objects + special1 = 24+(random[PoisonCloud]() & 7); + special2 = 0; + } + + //=========================================================================== + // + // + // + //=========================================================================== + + override int DoSpecialDamage (Actor victim, int damage, Name damagetype) + { + if (victim.player) + { + bool mate = (target != null && victim.player != target.player && victim.IsTeammate (target)); + bool dopoison; + + if (!mate) + { + dopoison = victim.player.poisoncount < 4; + } + else + { + dopoison = victim.player.poisoncount < (int)(4. * level.teamdamage); + } + + if (dopoison) + { + int damage = 15 + (random[PoisonCloud]()&15); + if (mate) + { + damage = (int)(damage * level.teamdamage); + } + // Handle passive damage modifiers (e.g. PowerProtection) + damage = victim.GetModifiedDamage(damagetype, damage, true); + // Modify with damage factors + damage = victim.ApplyDamageFactor(damagetype, damage); + if (damage > 0) + { + victim.player.PoisonDamage (self, 15 + (random[PoisonCloud]() & 15), false); // Don't play painsound + + // If successful, play the poison sound. + if (victim.player.PoisonPlayer (self, self.target, 50)) + victim.A_PlaySound ("*poison", CHAN_VOICE); + } + } + return -1; + } + else if (!victim.bIsMonster) + { // only damage monsters/players with the poison cloud + return -1; + } + return damage; + } + + //=========================================================================== + // + // A_PoisonBagCheck + // + //=========================================================================== + + void A_PoisonBagCheck() + { + if (--special1 <= 0) + { + SetStateLabel("Death"); + } + } + + //=========================================================================== + // + // A_PoisonBagDamage + // + //=========================================================================== + + void A_PoisonBagDamage() + { + A_Explode(4, 40); + AddZ(BobSin(special2) / 16); + special2 = (special2 + 1) & 63; + } } // Poison Shroom ------------------------------------------------------------ @@ -238,8 +570,6 @@ class ZPoisonShroom : PoisonBag DeathSound "PoisonShroomDeath"; } - native void A_PoisonShroom(); - States { Spawn: @@ -255,5 +585,16 @@ class ZPoisonShroom : PoisonBag SHRM F -1; Stop; } + + //=========================================================================== + // + // A_PoisonShroom + // + //=========================================================================== + + void A_PoisonShroom() + { + tics = 128 + (random[PoisonShroom]() << 1); + } } diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt index dab317ea6..25c94cb87 100644 --- a/wadsrc/static/zscript/shared/inventory.txt +++ b/wadsrc/static/zscript/shared/inventory.txt @@ -30,6 +30,7 @@ class Inventory : Actor native virtual native bool HandlePickup(Inventory item); virtual native Inventory CreateCopy(Actor other); + native void GoAwayAndDie(); // These are regular functions for the item itself. private native void A_RestoreSpecialDoomThing();