- 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
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)
/*