mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-11 07:12:02 +00:00
- 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:
parent
36f559ecb7
commit
3dd323ac0d
9 changed files with 424 additions and 493 deletions
|
@ -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;
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue