mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 21:11:39 +00:00
- 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:
parent
3a059cbfd6
commit
3f5bf88d69
15 changed files with 529 additions and 595 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 -------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -209,6 +209,7 @@ xx(XDeath)
|
|||
xx(Burn)
|
||||
//xx(Ice) // already defined above
|
||||
xx(Disintegrate)
|
||||
xx(Smash)
|
||||
|
||||
// Weapon animator names.
|
||||
xx(Select)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
468
wadsrc/static/zscript/heretic/weaponmace.txt
Normal file
468
wadsrc/static/zscript/heretic/weaponmace.txt
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue