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