- scriptified Heretic's mace.

- fixed: FxAssignSelf did not the correct number of registers for vector operations.
- fixed a few asserts in vector2 instructions.
- turned the virtual AActor::HitFloor method into a flag MF7_SMASHABLE. The only use of this function was to kill Hexen's pottery when they hit the floor, and this looks like something that can be exposed to modders less clumsily.
This commit is contained in:
Christoph Oelckers 2016-11-24 13:45:43 +01:00
parent 3a059cbfd6
commit 3f5bf88d69
15 changed files with 529 additions and 595 deletions

View file

@ -385,6 +385,7 @@ enum ActorFlag7
MF7_USEKILLSCRIPTS = 0x00800000, // [JM] Use "KILL" Script on death if not forced by GameInfo.
MF7_NOKILLSCRIPTS = 0x01000000, // [JM] No "KILL" Script on death whatsoever, even if forced by GameInfo.
MF7_SPRITEANGLE = 0x02000000, // [MC] Utilize the SpriteAngle property and lock the rotation to the degrees specified.
MF7_SMASHABLE = 0x04000000, // dies if hitting the floor.
};
// --- mobj.renderflags ---
@ -636,9 +637,6 @@ public:
// Made a metadata property so no longer virtual
void Howl ();
// Actor just hit the floor
virtual void HitFloor ();
// plays bouncing sound
void PlayBounceSound(bool onfloor);

View file

@ -45,349 +45,6 @@ void P_DSparilTeleport (AActor *actor);
extern bool P_AutoUseChaosDevice (player_t *player);
// --- Mace -----------------------------------------------------------------
#define MAGIC_JUNK 1234
// Mace FX4 -----------------------------------------------------------------
class AMaceFX4 : public AActor
{
DECLARE_CLASS (AMaceFX4, AActor)
public:
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
};
IMPLEMENT_CLASS(AMaceFX4, false, false, false, false)
int AMaceFX4::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
if ((target->flags2 & MF2_BOSS) || (target->flags3 & MF3_DONTSQUASH) || target->IsTeammate (this->target))
{ // Don't allow cheap boss kills and don't instagib teammates
return damage;
}
else if (target->player)
{ // Player specific checks
if (target->player->mo->flags2 & MF2_INVULNERABLE)
{ // Can't hurt invulnerable players
return -1;
}
if (P_AutoUseChaosDevice (target->player))
{ // Player was saved using chaos device
return -1;
}
}
return TELEFRAG_DAMAGE; // Something's gonna die
}
//----------------------------------------------------------------------------
//
// PROC A_FireMacePL1B
//
//----------------------------------------------------------------------------
void FireMacePL1B (AActor *actor)
{
AActor *ball;
player_t *player;
if (NULL == (player = actor->player))
{
return;
}
AWeapon *weapon = player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
ball = Spawn("MaceFX2", actor->PosPlusZ(28 - actor->Floorclip), ALLOW_REPLACE);
ball->Vel.Z = 2 - player->mo->Angles.Pitch.TanClamped();
ball->target = actor;
ball->Angles.Yaw = actor->Angles.Yaw;
ball->AddZ(ball->Vel.Z);
ball->VelFromAngle();
ball->Vel += actor->Vel.XY()/2;
S_Sound (ball, CHAN_BODY, "weapons/maceshoot", 1, ATTN_NORM);
P_CheckMissileSpawn (ball, actor->radius);
}
//----------------------------------------------------------------------------
//
// PROC A_FireMacePL1
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_FireMacePL1)
{
PARAM_ACTION_PROLOGUE(AActor);
AActor *ball;
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
if (pr_maceatk() < 28)
{
FireMacePL1B(self);
return 0;
}
AWeapon *weapon = player->ReadyWeapon;
if (weapon != nullptr)
{
if (!weapon->DepleteAmmo(weapon->bAltFire))
return 0;
player->GetPSprite(PSP_WEAPON)->x = ((pr_maceatk() & 3) - 2);
player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP + (pr_maceatk() & 3);
}
ball = P_SpawnPlayerMissile(self, PClass::FindActor("MaceFX1"), self->Angles.Yaw + (((pr_maceatk() & 7) - 4) * (360. / 256)));
if (ball)
{
ball->special1 = 16; // tics till dropoff
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MacePL1Check
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MacePL1Check)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->special1 == 0)
{
return 0;
}
self->special1 -= 4;
if (self->special1 > 0)
{
return 0;
}
self->special1 = 0;
self->flags &= ~MF_NOGRAVITY;
self->Gravity = 1. / 8;;
// [RH] Avoid some precision loss by scaling the velocity directly
#if 0
// This is the original code, for reference.
a.ngle_t ang = self->ang>>ANGLETOF.INESHIFT;
self->velx = F.ixedMul(7*F.RACUNIT, f.inecosine[ang]);
self->vely = F.ixedMul(7*F.RACUNIT, f.inesine[ang]);
#else
double velscale = 7 / self->Vel.XY().Length();
self->Vel.X *= velscale;
self->Vel.Y *= velscale;
#endif
self->Vel.Z *= 0.5;
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MaceBallImpact
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact)
{
PARAM_SELF_PROLOGUE(AActor);
if ((self->health != MAGIC_JUNK) && (self->flags & MF_INBOUNCE))
{ // Bounce
self->health = MAGIC_JUNK;
self->Vel.Z *= 0.75;
self->BounceFlags = BOUNCE_None;
self->SetState (self->SpawnState);
S_Sound (self, CHAN_BODY, "weapons/macebounce", 1, ATTN_NORM);
}
else
{ // Explode
self->Vel.Zero();
self->flags |= MF_NOGRAVITY;
self->Gravity = 1;
S_Sound (self, CHAN_BODY, "weapons/macehit", 1, ATTN_NORM);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_MaceBallImpact2
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact2)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *tiny;
if ((self->Z() <= self->floorz) && P_HitFloor (self))
{ // Landed in some sort of liquid
self->Destroy ();
return 0;
}
if (self->flags & MF_INBOUNCE)
{
if (self->Vel.Z < 2)
{
goto boom;
}
// Bounce
self->Vel.Z *= 0.75;
self->SetState (self->SpawnState);
tiny = Spawn("MaceFX3", self->Pos(), ALLOW_REPLACE);
tiny->target = self->target;
tiny->Angles.Yaw = self->Angles.Yaw + 90.;
tiny->VelFromAngle(self->Vel.Z - 1.);
tiny->Vel += { self->Vel.X * .5, self->Vel.Y * .5, self->Vel.Z };
P_CheckMissileSpawn (tiny, self->radius);
tiny = Spawn("MaceFX3", self->Pos(), ALLOW_REPLACE);
tiny->target = self->target;
tiny->Angles.Yaw = self->Angles.Yaw - 90.;
tiny->VelFromAngle(self->Vel.Z - 1.);
tiny->Vel += { self->Vel.X * .5, self->Vel.Y * .5, self->Vel.Z };
P_CheckMissileSpawn (tiny, self->radius);
}
else
{ // Explode
boom:
self->Vel.Zero();
self->flags |= MF_NOGRAVITY;
self->BounceFlags = BOUNCE_None;
self->Gravity = 1;
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_FireMacePL2
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_FireMacePL2)
{
PARAM_ACTION_PROLOGUE(AActor);
AActor *mo;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
AWeapon *weapon = player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
mo = P_SpawnPlayerMissile (self, 0,0,0, RUNTIME_CLASS(AMaceFX4), self->Angles.Yaw, &t);
if (mo)
{
mo->Vel += self->Vel.XY();
mo->Vel.Z = 2 - player->mo->Angles.Pitch.TanClamped();
if (t.linetarget && !t.unlinked)
{
mo->tracer = t.linetarget;
}
}
S_Sound (self, CHAN_WEAPON, "weapons/maceshoot", 1, ATTN_NORM);
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_DeathBallImpact
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
AActor *target;
DAngle ang = 0.;
bool newAngle;
FTranslatedLineTarget t;
if ((self->Z() <= self->floorz) && P_HitFloor (self))
{ // Landed in some sort of liquid
self->Destroy ();
return 0;
}
if (self->flags & MF_INBOUNCE)
{
if (self->Vel.Z < 2)
{
goto boom;
}
// Bounce
newAngle = false;
target = self->tracer;
if (target)
{
if (!(target->flags&MF_SHOOTABLE))
{ // Target died
self->tracer = NULL;
}
else
{ // Seek
ang = self->AngleTo(target);
newAngle = true;
}
}
else
{ // Find new target
ang = 0.;
for (i = 0; i < 16; i++)
{
P_AimLineAttack (self, ang, 640., &t, 0., ALF_NOFRIENDS|ALF_PORTALRESTRICT, NULL, self->target);
if (t.linetarget && self->target != t.linetarget)
{
self->tracer = t.linetarget;
ang = t.angleFromSource;
newAngle = true;
break;
}
ang += 22.5;
}
}
if (newAngle)
{
self->Angles.Yaw = ang;
self->VelFromAngle();
}
self->SetState (self->SpawnState);
S_Sound (self, CHAN_BODY, "weapons/macestop", 1, ATTN_NORM);
}
else
{ // Explode
boom:
self->Vel.Zero();
self->flags |= MF_NOGRAVITY;
self->Gravity = 1;
S_Sound (self, CHAN_BODY, "weapons/maceexplode", 1, ATTN_NORM);
}
return 0;
}
// Blaster FX 1 -------------------------------------------------------------

View file

@ -32,21 +32,6 @@ void A_PotteryExplode (AActor *);
void A_PotteryChooseBit (AActor *);
void A_PotteryCheck (AActor *);
class APottery1 : public AActor
{
DECLARE_CLASS (APottery1, AActor)
public:
void HitFloor ();
};
IMPLEMENT_CLASS(APottery1, false, false, false, false)
void APottery1::HitFloor ()
{
Super::HitFloor ();
P_DamageMobj (this, NULL, NULL, 25, NAME_None);
}
//============================================================================
//
// A_PotteryExplode

View file

@ -61,21 +61,3 @@ bool AArtiTeleport::Use (bool pickup)
return true;
}
//---------------------------------------------------------------------------
//
// FUNC P_AutoUseChaosDevice
//
//---------------------------------------------------------------------------
bool P_AutoUseChaosDevice (player_t *player)
{
AInventory *arti = player->mo->FindInventory(PClass::FindActor("ArtiTeleport"));
if (arti != NULL)
{
player->mo->UseInventory (arti);
player->health = player->mo->health = (player->health+1)/2;
return true;
}
return false;
}

View file

@ -209,6 +209,7 @@ xx(XDeath)
xx(Burn)
//xx(Ice) // already defined above
xx(Disintegrate)
xx(Smash)
// Weapon animator names.
xx(Select)

View file

@ -948,6 +948,14 @@ bool AActor::UseInventory (AInventory *item)
return true;
}
DEFINE_ACTION_FUNCTION(AActor, UseInventory)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(item, AInventory);
self->VMSuperCall();
ACTION_RETURN_BOOL(self->UseInventory(item));
}
//===========================================================================
//
// AActor :: DropInventory
@ -2741,7 +2749,10 @@ void P_ZMovement (AActor *mo, double oldfloorz)
return;
}
// Let the actor do something special for hitting the floor
mo->HitFloor ();
if (mo->flags7 & MF7_SMASHABLE)
{
P_DamageMobj(mo, nullptr, nullptr, mo->health, NAME_Smash);
}
if (mo->player)
{
if (mo->player->jumpTics < 0 || mo->Vel.Z < minvel)
@ -3230,10 +3241,6 @@ void AActor::Howl ()
}
}
void AActor::HitFloor ()
{
}
bool AActor::Slam (AActor *thing)
{
flags &= ~MF_SKULLFLY;
@ -5945,6 +5952,12 @@ bool P_HitFloor (AActor *thing)
return P_HitWater (thing, m->m_sector, pos);
}
DEFINE_ACTION_FUNCTION(AActor, HitFloor)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_BOOL(P_HitFloor(self));
}
//---------------------------------------------------------------------------
//
// P_CheckSplash
@ -6543,6 +6556,13 @@ bool AActor::IsTeammate (AActor *other)
return false;
}
DEFINE_ACTION_FUNCTION(AActor, isTeammate)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(other, AActor);
ACTION_RETURN_BOOL(self->IsTeammate(other));
}
//==========================================================================
//
// AActor :: GetSpecies

View file

@ -2323,7 +2323,7 @@ ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build)
ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it
if (!pointer.Target)
{
ExpEmit out(build, ValueType->GetRegType());
ExpEmit out(build, ValueType->GetRegType(), ValueType->GetRegCount());
if (Assignment->IsBitWrite != -1)
{
build->Emit(OP_LBIT, out.RegNum, pointer.RegNum, 1 << Assignment->IsBitWrite);

View file

@ -294,6 +294,7 @@ static FFlagDef ActorFlagDefs[]=
DEFINE_FLAG(MF7, USEKILLSCRIPTS, AActor, flags7),
DEFINE_FLAG(MF7, NOKILLSCRIPTS, AActor, flags7),
DEFINE_FLAG(MF7, SPRITEANGLE, AActor, flags7),
DEFINE_FLAG(MF7, SMASHABLE, AActor, flags7),
// Effect flags
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),

View file

@ -217,7 +217,7 @@ begin:
reg.atag[a] = ATAG_GENERIC;
NEXTOP;
OP(LV2):
ASSERTF(a+2); ASSERTA(B); ASSERTKD(C);
ASSERTF(a+1); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
{
auto v = (double *)ptr;
@ -226,7 +226,7 @@ begin:
}
NEXTOP;
OP(LV2_R):
ASSERTF(a+2); ASSERTA(B); ASSERTD(C);
ASSERTF(a+1); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
{
auto v = (double *)ptr;
@ -331,7 +331,7 @@ begin:
*(void **)ptr = reg.a[B];
NEXTOP;
OP(SV2):
ASSERTA(a); ASSERTF(B+2); ASSERTKD(C);
ASSERTA(a); ASSERTF(B+1); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
{
auto v = (double *)ptr;
@ -340,7 +340,7 @@ begin:
}
NEXTOP;
OP(SV2_R):
ASSERTA(a); ASSERTF(B+2); ASSERTD(C);
ASSERTA(a); ASSERTF(B+1); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
{
auto v = (double *)ptr;

View file

@ -39,6 +39,7 @@ VMEXPORTED_NATIVES_START
VMEXPORTED_NATIVES_FUNC(Activate)
VMEXPORTED_NATIVES_FUNC(Deactivate)
VMEXPORTED_NATIVES_FUNC(DoSpecialDamage)
VMEXPORTED_NATIVES_FUNC(UseInventory)
VMEXPORTED_NATIVES_END
@ -213,6 +214,26 @@ public:
return retval;
}
}
bool UseInventory(AInventory *item)
{
if (this->ObjectFlags & OF_SuperCall)
{
this->ObjectFlags &= ~OF_SuperCall;
return ExportedNatives<T>::Get()->template UseInventory<bool, T>(this, item);
}
else
{
VINDEX(AActor, UseInventory);
// Without the type cast this picks the 'void *' assignment...
VMValue params[2] = { (DObject*)this, (DObject*)item };
VMReturn ret;
VMFrameStack stack;
int retval;
ret.IntAt(&retval);
stack.Call(VFUNC, params, 2, &ret, 1, nullptr);
return !!retval;
}
}
};
@ -245,6 +266,7 @@ VMEXPORT_NATIVES_START(AActor, DThinker)
VMEXPORT_NATIVES_FUNC(Activate)
VMEXPORT_NATIVES_FUNC(Deactivate)
VMEXPORT_NATIVES_FUNC(DoSpecialDamage)
VMEXPORT_NATIVES_FUNC(UseInventory)
VMEXPORT_NATIVES_END(AActor)
/*

View file

@ -99,6 +99,7 @@ zscript/heretic/weaponstaff.txt
zscript/heretic/weaponwand.txt
zscript/heretic/weaponcrossbow.txt
zscript/heretic/weapongauntlets.txt
zscript/heretic/weaponmace.txt
zscript/hexen/baseweapons.txt
zscript/hexen/korax.txt

View file

@ -238,6 +238,7 @@ class Actor : Thinker native
virtual native void Activate(Actor activator);
virtual native void Deactivate(Actor activator);
virtual native int DoSpecialDamage (Actor target, int damage, Name damagetype);
virtual native bool UseInventory(Inventory item);
native void AdjustPlayerAngle(FTranslatedLineTarget t);
native static readonly<Actor> GetDefaultByType(class<Actor> cls);
@ -247,6 +248,8 @@ class Actor : Thinker native
native double GetBobOffset(double frac = 0);
native void ClearCounters();
native bool GiveBody (int num, int max=0);
native bool HitFloor();
native bool isTeammate(Actor other);
native void RestoreDamage();
native int SpawnHealth();

View file

@ -7,210 +7,6 @@ class HereticWeapon : Weapon
}
}
// The mace itself ----------------------------------------------------------
class Mace : HereticWeapon
{
Default
{
Weapon.SelectionOrder 1400;
Weapon.AmmoUse 1;
Weapon.AmmoGive1 50;
Weapon.YAdjust 15;
Weapon.AmmoType "MaceAmmo";
Weapon.SisterWeapon "MacePowered";
Inventory.PickupMessage "$TXT_WPNMACE";
Tag "$TAG_MACE";
}
action native void A_FireMacePL1();
States
{
Spawn:
WMCE A -1;
Stop;
Ready:
MACE A 1 A_WeaponReady;
Loop;
Deselect:
MACE A 1 A_Lower;
Loop;
Select:
MACE A 1 A_Raise;
Loop;
Fire:
MACE B 4;
Hold:
MACE CDEF 3 A_FireMacePL1;
MACE C 4 A_ReFire;
MACE DEFB 4;
Goto Ready;
}
}
class MacePowered : Mace
{
Default
{
+WEAPON.POWERED_UP
Weapon.AmmoUse 5;
Weapon.AmmoGive 0;
Weapon.SisterWeapon "Mace";
Tag "$TAG_MACEP";
}
action native void A_FireMacePL2();
States
{
Fire:
Hold:
MACE B 4;
MACE D 4 A_FireMacePL2;
MACE B 4;
MACE A 8 A_ReFire;
Goto Ready;
}
}
// Mace FX1 -----------------------------------------------------------------
class MaceFX1 : Actor
{
Default
{
Radius 8;
Height 6;
Speed 20;
Damage 2;
Projectile;
+THRUGHOST
BounceType "HereticCompat";
SeeSound "weapons/maceshoot";
Obituary "$OB_MPMACE";
}
native void A_MacePL1Check();
native void A_MaceBallImpact();
States
{
Spawn:
FX02 AB 4 A_MacePL1Check;
Loop;
Death:
FX02 F 4 BRIGHT A_MaceBallImpact;
FX02 GHIJ 4 BRIGHT;
Stop;
}
}
// Mace FX2 -----------------------------------------------------------------
class MaceFX2 : MaceFX1
{
Default
{
Speed 10;
Damage 6;
Gravity 0.125;
-NOGRAVITY
SeeSound "";
}
native void A_MaceBallImpact2();
States
{
Spawn:
FX02 CD 4;
Loop;
Death:
FX02 F 4 A_MaceBallImpact2;
goto Super::Death+1;
}
}
// Mace FX3 -----------------------------------------------------------------
class MaceFX3 : MaceFX1
{
Default
{
Speed 7;
Damage 4;
-NOGRAVITY;
Gravity 0.125;
}
States
{
Spawn:
FX02 AB 4;
Loop;
}
}
// Mace FX4 -----------------------------------------------------------------
class MaceFX4 : Actor native
{
Default
{
Radius 8;
Height 6;
Speed 7;
Damage 18;
Gravity 0.125;
Projectile;
-NOGRAVITY
+TELESTOMP
+THRUGHOST
-NOTELEPORT
BounceType "HereticCompat";
SeeSound "";
Obituary "$OB_MPPMACE";
}
native void A_DeathBallImpact();
States
{
Spawn:
FX02 E 99;
Loop;
Death:
FX02 C 4 A_DeathBallImpact;
FX02 GHIJ 4 BRIGHT;
Stop;
}
}
// Mace spawn spot ----------------------------------------------------------
class MaceSpawner : SpecialSpot
{
Default
{
+NOSECTOR
+NOBLOCKMAP
}
States
{
Spawn:
TNT1 A 1;
TNT1 A -1 A_SpawnSingleItem("Mace", 64, 64, 0);
Stop;
}
}
// Blaster ------------------------------------------------------------------
class Blaster : HereticWeapon

View file

@ -0,0 +1,468 @@
// The mace itself ----------------------------------------------------------
class Mace : HereticWeapon
{
Default
{
Weapon.SelectionOrder 1400;
Weapon.AmmoUse 1;
Weapon.AmmoGive1 50;
Weapon.YAdjust 15;
Weapon.AmmoType "MaceAmmo";
Weapon.SisterWeapon "MacePowered";
Inventory.PickupMessage "$TXT_WPNMACE";
Tag "$TAG_MACE";
}
States
{
Spawn:
WMCE A -1;
Stop;
Ready:
MACE A 1 A_WeaponReady;
Loop;
Deselect:
MACE A 1 A_Lower;
Loop;
Select:
MACE A 1 A_Raise;
Loop;
Fire:
MACE B 4;
Hold:
MACE CDEF 3 A_FireMacePL1;
MACE C 4 A_ReFire;
MACE DEFB 4;
Goto Ready;
}
//----------------------------------------------------------------------------
//
// PROC A_FireMacePL1
//
//----------------------------------------------------------------------------
action void A_FireMacePL1()
{
if (player == null)
{
return;
}
Weapon weapon = player.ReadyWeapon;
if (weapon != null)
{
if (!weapon.DepleteAmmo (weapon.bAltFire))
return;
}
if (random[MaceAtk]() < 28)
{
Actor ball = Spawn("MaceFX2", Pos + (0, 0, 28 - Floorclip), ALLOW_REPLACE);
ball.Vel.Z = 2 - clamp(tan(pitch), -5, 5);
ball.target = self;
ball.angle = self.angle;
ball.AddZ(ball.Vel.Z);
ball.VelFromAngle();
ball.Vel += Vel.xy / 2;
ball.A_PlaySound ("weapons/maceshoot", CHAN_BODY);
ball.CheckMissileSpawn (radius);
}
else
{
player.GetPSprite(PSP_WEAPON).x = ((random[MaceAtk]() & 3) - 2);
player.GetPSprite(PSP_WEAPON).y = WEAPONTOP + (random[MaceAtk]() & 3);
Actor ball = SpawnPlayerMissile("MaceFX1", angle + (((random[MaceAtk]() & 7) - 4) * (360. / 256)));
if (ball)
{
ball.special1 = 16; // tics till dropoff
}
}
}
}
class MacePowered : Mace
{
Default
{
+WEAPON.POWERED_UP
Weapon.AmmoUse 5;
Weapon.AmmoGive 0;
Weapon.SisterWeapon "Mace";
Tag "$TAG_MACEP";
}
States
{
Fire:
Hold:
MACE B 4;
MACE D 4 A_FireMacePL2;
MACE B 4;
MACE A 8 A_ReFire;
Goto Ready;
}
//----------------------------------------------------------------------------
//
// PROC A_FireMacePL2
//
//----------------------------------------------------------------------------
action void A_FireMacePL2()
{
FTranslatedLineTarget t;
if (player == null)
{
return;
}
Weapon weapon = player.ReadyWeapon;
if (weapon != null)
{
if (!weapon.DepleteAmmo (weapon.bAltFire))
return;
}
Actor mo = SpawnPlayerMissile ("MaceFX4", angle, pLineTarget:t);
if (mo)
{
mo.Vel.xy += Vel.xy;
mo.Vel.Z = 2 - clamp(tan(pitch), -5, 5);
if (t.linetarget && !t.unlinked)
{
mo.tracer = t.linetarget;
}
}
A_PlaySound ("weapons/maceshoot", CHAN_WEAPON);
}
}
// Mace FX1 -----------------------------------------------------------------
class MaceFX1 : Actor
{
const MAGIC_JUNK = 1234;
Default
{
Radius 8;
Height 6;
Speed 20;
Damage 2;
Projectile;
+THRUGHOST
BounceType "HereticCompat";
SeeSound "weapons/maceshoot";
Obituary "$OB_MPMACE";
}
States
{
Spawn:
FX02 AB 4 A_MacePL1Check;
Loop;
Death:
FX02 F 4 BRIGHT A_MaceBallImpact;
FX02 GHIJ 4 BRIGHT;
Stop;
}
//----------------------------------------------------------------------------
//
// PROC A_MacePL1Check
//
//----------------------------------------------------------------------------
void A_MacePL1Check()
{
if (special1 == 0) return;
special1 -= 4;
if (special1 > 0) return;
special1 = 0;
bNoGravity = false;
Gravity = 1. / 8;
// [RH] Avoid some precision loss by scaling the velocity directly
double velscale = 7 / Vel.XY.Length();
Vel.XY *= velscale;
Vel.Z *= 0.5;
}
//----------------------------------------------------------------------------
//
// PROC A_MaceBallImpact
//
//----------------------------------------------------------------------------
void A_MaceBallImpact()
{
if ((health != MAGIC_JUNK) && bInFloat)
{ // Bounce
health = MAGIC_JUNK;
Vel.Z *= 0.75;
bBounceOnFloors = bBounceOnCeilings = false;
SetState (SpawnState);
A_PlaySound ("weapons/macebounce", CHAN_BODY);
}
else
{ // Explode
Vel = (0,0,0);
bNoGravity = true;
Gravity = 1;
A_PlaySound ("weapons/macehit", CHAN_BODY);
}
}
}
// Mace FX2 -----------------------------------------------------------------
class MaceFX2 : MaceFX1
{
Default
{
Speed 10;
Damage 6;
Gravity 0.125;
-NOGRAVITY
SeeSound "";
}
States
{
Spawn:
FX02 CD 4;
Loop;
Death:
FX02 F 4 A_MaceBallImpact2;
goto Super::Death+1;
}
//----------------------------------------------------------------------------
//
// PROC A_MaceBallImpact2
//
//----------------------------------------------------------------------------
void A_MaceBallImpact2()
{
if ((pos.Z <= floorz) && HitFloor ())
{ // Landed in some sort of liquid
Destroy ();
return;
}
if (bInFloat)
{
if (Vel.Z >= 2)
{
// Bounce
Vel.Z *= 0.75;
SetState (SpawnState);
Actor tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE);
tiny.target = target;
tiny.angle = angle + 90.;
tiny.VelFromAngle(Vel.Z - 1.);
tiny.Vel += (Vel.XY * .5, Vel.Z);
tiny.CheckMissileSpawn (radius);
tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE);
tiny.target = target;
tiny.angle = angle - 90.;
tiny.VelFromAngle(Vel.Z - 1.);
tiny.Vel += (Vel.XY * .5, Vel.Z);
tiny.CheckMissileSpawn (radius);
return;
}
}
Vel = (0,0,0);
bNoGravity = true;
bBounceOnFloors = bBounceOnCeilings = false;
Gravity = 1;
}
}
// Mace FX3 -----------------------------------------------------------------
class MaceFX3 : MaceFX1
{
Default
{
Speed 7;
Damage 4;
-NOGRAVITY;
Gravity 0.125;
}
States
{
Spawn:
FX02 AB 4;
Loop;
}
}
// Mace FX4 -----------------------------------------------------------------
class MaceFX4 : Actor
{
Default
{
Radius 8;
Height 6;
Speed 7;
Damage 18;
Gravity 0.125;
Projectile;
-NOGRAVITY
+TELESTOMP
+THRUGHOST
-NOTELEPORT
BounceType "HereticCompat";
SeeSound "";
Obituary "$OB_MPPMACE";
}
States
{
Spawn:
FX02 E 99;
Loop;
Death:
FX02 C 4 A_DeathBallImpact;
FX02 GHIJ 4 BRIGHT;
Stop;
}
//---------------------------------------------------------------------------
//
// FUNC P_AutoUseChaosDevice
//
//---------------------------------------------------------------------------
private bool AutoUseChaosDevice (PlayerInfo player)
{
Inventory arti = player.mo.FindInventory("ArtiTeleport");
if (arti != null)
{
player.mo.UseInventory (arti);
player.health = player.mo.health = (player.health+1)/2;
return true;
}
return false;
}
//----------------------------------------------------------------------------
//
// PROC DoSpecialDamage
//
//----------------------------------------------------------------------------
override int DoSpecialDamage (Actor target, int damage, Name damagetype)
{
if (target.bBoss || target.bDontSquash || target.IsTeammate (self.target))
{ // Don't allow cheap boss kills and don't instagib teammates
return damage;
}
else if (target.player)
{ // Player specific checks
if (target.player.mo.bInvulnerable)
{ // Can't hurt invulnerable players
return -1;
}
if (AutoUseChaosDevice (target.player))
{ // Player was saved using chaos device
return -1;
}
}
return TELEFRAG_DAMAGE; // Something's gonna die
}
//----------------------------------------------------------------------------
//
// PROC A_DeathBallImpact
//
//----------------------------------------------------------------------------
void A_DeathBallImpact()
{
FTranslatedLineTarget t;
if ((pos.Z <= floorz) && HitFloor ())
{ // Landed in some sort of liquid
Destroy ();
return;
}
if (bInFloat)
{
if (Vel.Z >= 2)
{
// Bounce
bool newAngle = false;
double ang = 0;
if (tracer)
{
if (!tracer.bShootable)
{ // Target died
tracer = null;
}
else
{ // Seek
ang = AngleTo(tracer);
newAngle = true;
}
}
else
{ // Find new target
ang = 0.;
for (int i = 0; i < 16; i++)
{
AimLineAttack (ang, 640., t, 0., ALF_NOFRIENDS|ALF_PORTALRESTRICT, null, target);
if (t.linetarget && target != t.linetarget)
{
tracer = t.linetarget;
ang = t.angleFromSource;
newAngle = true;
break;
}
ang += 22.5;
}
}
if (newAngle)
{
angle = ang;
VelFromAngle();
}
SetState (SpawnState);
A_PlaySound ("weapons/macestop", CHAN_BODY);
return;
}
}
Vel = (0,0,0);
bNoGravity = true;
Gravity = 1;
A_PlaySound ("weapons/maceexplode", CHAN_BODY);
}
}
// Mace spawn spot ----------------------------------------------------------
class MaceSpawner : SpecialSpot
{
Default
{
+NOSECTOR
+NOBLOCKMAP
}
States
{
Spawn:
TNT1 A 1;
TNT1 A -1 A_SpawnSingleItem("Mace", 64, 64, 0);
Stop;
}
}

View file

@ -83,14 +83,14 @@ class TreeDestructible : Actor
// Pottery1 ------------------------------------------------------------------
class Pottery1 : Actor native
class Pottery1 : Actor
{
Default
{
Health 15;
Speed 10;
Height 32;
+SOLID +SHOOTABLE +NOBLOOD +DROPOFF
+SOLID +SHOOTABLE +NOBLOOD +DROPOFF +SMASHABLE
+SLIDESONWALLS +PUSHABLE +TELESTOMP +CANPASS
+NOICEDEATH
}