- 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.
This commit is contained in:
Christoph Oelckers 2016-11-27 12:52:54 +01:00
parent 36f559ecb7
commit 3dd323ac0d
9 changed files with 424 additions and 493 deletions

View file

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

View file

@ -1,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"

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -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.

View file

@ -303,8 +303,8 @@ class Actor : Thinker native
native Actor, Actor SpawnPlayerMissile(class<Actor> 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; }

View file

@ -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<Actor> GetFlechetteType(Actor other)
{
class<Actor> 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<Actor> 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<Actor> 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<Actor> 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);
}
}

View file

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