- scriptified Heretic's Skull Rod.
- Took the opportunity and fixed the logic for the Skull Rod's rain spawner. The old code which was part of the 3D floor submission was unable to work with portals at all. The new approach no longer tries to hide the dead projectile in the ceiling, it leaves it where it is and changes a few flags, so that its z-position can be used as reference to get the actual ceiling. This works for line portals, but for sector portals still requires some changes to sector_t::NextHighestCeilingAt to work, but at least this can be made to work unlike the old code. - added names for the player-related translations to A_SetTranslation. - fixed: Failure to resolve a function argument was checked for, too late. - made the parameter for A_SetTranslation a name instead of a string, because it is more efficient. We do not need full strings here.
This commit is contained in:
parent
c2f7ed7f1c
commit
8dba322775
16 changed files with 569 additions and 520 deletions
|
|
@ -759,7 +759,7 @@ public:
|
|||
virtual FName GetSpecies();
|
||||
|
||||
// set translation
|
||||
void SetTranslation(const char *trname);
|
||||
void SetTranslation(FName trname);
|
||||
|
||||
double GetBobOffset(double ticfrac = 0) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,305 +46,6 @@ void P_DSparilTeleport (AActor *actor);
|
|||
extern bool P_AutoUseChaosDevice (player_t *player);
|
||||
|
||||
|
||||
// --- Skull rod ------------------------------------------------------------
|
||||
|
||||
|
||||
// Rain pillar 1 ------------------------------------------------------------
|
||||
|
||||
class ARainPillar : public AActor
|
||||
{
|
||||
DECLARE_CLASS (ARainPillar, AActor)
|
||||
public:
|
||||
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(ARainPillar, false, false)
|
||||
|
||||
int ARainPillar::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
||||
{
|
||||
if (target->flags2 & MF2_BOSS)
|
||||
{ // Decrease damage for bosses
|
||||
damage = (pr_rp() & 7) + 1;
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
// Rain tracker "inventory" item --------------------------------------------
|
||||
|
||||
class ARainTracker : public AInventory
|
||||
{
|
||||
DECLARE_CLASS (ARainTracker, AInventory)
|
||||
public:
|
||||
|
||||
void Serialize(FSerializer &arc);
|
||||
TObjPtr<AActor> Rain1, Rain2;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(ARainTracker, false, false)
|
||||
|
||||
void ARainTracker::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc("rain1", Rain1)
|
||||
("rain2", Rain2);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_FireSkullRodPL1
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL1)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
|
||||
AActor *mo;
|
||||
player_t *player;
|
||||
|
||||
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, PClass::FindActor("HornRodFX1"));
|
||||
// Randomize the first frame
|
||||
if (mo && pr_fsr1() > 128)
|
||||
{
|
||||
mo->SetState (mo->state->GetNextState());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_FireSkullRodPL2
|
||||
//
|
||||
// The special2 field holds the player number that shot the rain missile.
|
||||
// The special1 field holds the id of the rain sound.
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL2)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
|
||||
player_t *player;
|
||||
AActor *MissileActor;
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (NULL == (player = self->player))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
AWeapon *weapon = player->ReadyWeapon;
|
||||
if (weapon != NULL)
|
||||
{
|
||||
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
||||
return 0;
|
||||
}
|
||||
P_SpawnPlayerMissile (self, 0,0,0, PClass::FindActor("HornRodFX2"), self->Angles.Yaw, &t, &MissileActor);
|
||||
// Use MissileActor instead of the return value from
|
||||
// P_SpawnPlayerMissile because we need to give info to the mobj
|
||||
// even if it exploded immediately.
|
||||
if (MissileActor != NULL)
|
||||
{
|
||||
MissileActor->special2 = (int)(player - players);
|
||||
if (t.linetarget && !t.unlinked)
|
||||
{
|
||||
MissileActor->tracer = t.linetarget;
|
||||
}
|
||||
S_Sound (MissileActor, CHAN_WEAPON, "weapons/hornrodpowshoot", 1, ATTN_NORM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_AddPlayerRain
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_AddPlayerRain)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
ARainTracker *tracker;
|
||||
|
||||
if (self->target == NULL || self->target->health <= 0)
|
||||
{ // Shooter is dead or nonexistant
|
||||
return 0;
|
||||
}
|
||||
|
||||
tracker = self->target->FindInventory<ARainTracker> ();
|
||||
|
||||
// They player is only allowed two rainstorms at a time. Shooting more
|
||||
// than that will cause the oldest one to terminate.
|
||||
if (tracker != NULL)
|
||||
{
|
||||
if (tracker->Rain1 && tracker->Rain2)
|
||||
{ // Terminate an active rain
|
||||
if (tracker->Rain1->health < tracker->Rain2->health)
|
||||
{
|
||||
if (tracker->Rain1->health > 16)
|
||||
{
|
||||
tracker->Rain1->health = 16;
|
||||
}
|
||||
tracker->Rain1 = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tracker->Rain2->health > 16)
|
||||
{
|
||||
tracker->Rain2->health = 16;
|
||||
}
|
||||
tracker->Rain2 = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tracker = static_cast<ARainTracker *> (self->target->GiveInventoryType (RUNTIME_CLASS(ARainTracker)));
|
||||
}
|
||||
// Add rain mobj to list
|
||||
if (tracker->Rain1)
|
||||
{
|
||||
tracker->Rain2 = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
tracker->Rain1 = self;
|
||||
}
|
||||
self->special1 = S_FindSound ("misc/rain");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_SkullRodStorm
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_SkullRodStorm)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
AActor *mo;
|
||||
ARainTracker *tracker;
|
||||
|
||||
if (self->health-- == 0)
|
||||
{
|
||||
S_StopSound (self, CHAN_BODY);
|
||||
if (self->target == NULL)
|
||||
{ // Player left the game
|
||||
self->Destroy ();
|
||||
return 0;
|
||||
}
|
||||
tracker = self->target->FindInventory<ARainTracker> ();
|
||||
if (tracker != NULL)
|
||||
{
|
||||
if (tracker->Rain1 == self)
|
||||
{
|
||||
tracker->Rain1 = NULL;
|
||||
}
|
||||
else if (tracker->Rain2 == self)
|
||||
{
|
||||
tracker->Rain2 = NULL;
|
||||
}
|
||||
}
|
||||
self->Destroy ();
|
||||
return 0;
|
||||
}
|
||||
if (pr_storm() < 25)
|
||||
{ // Fudge rain frequency
|
||||
return 0;
|
||||
}
|
||||
double xo = ((pr_storm() & 127) - 64);
|
||||
double yo = ((pr_storm() & 127) - 64);
|
||||
DVector3 pos = self->Vec2OffsetZ(xo, yo, ONCEILINGZ);
|
||||
mo = Spawn<ARainPillar> (pos, ALLOW_REPLACE);
|
||||
// We used bouncecount to store the 3D floor index in A_HideInCeiling
|
||||
if (!mo) return 0;
|
||||
if (mo->Sector->PortalGroup != self->Sector->PortalGroup)
|
||||
{
|
||||
// spawning this through a portal will never work right so abort right away.
|
||||
mo->Destroy();
|
||||
return 0;
|
||||
}
|
||||
if (self->bouncecount >= 0 && (unsigned)self->bouncecount < self->Sector->e->XFloor.ffloors.Size())
|
||||
pos.Z = self->Sector->e->XFloor.ffloors[self->bouncecount]->bottom.plane->ZatPoint(mo);
|
||||
else
|
||||
pos.Z = self->Sector->ceilingplane.ZatPoint(mo);
|
||||
int moceiling = P_Find3DFloor(NULL, pos, false, false, pos.Z);
|
||||
if (moceiling >= 0) mo->SetZ(pos.Z - mo->Height);
|
||||
mo->Translation = multiplayer ? TRANSLATION(TRANSLATION_RainPillar,self->special2) : 0;
|
||||
mo->target = self->target;
|
||||
mo->Vel.X = MinVel; // Force collision detection
|
||||
mo->Vel.Z = -mo->Speed;
|
||||
mo->special2 = self->special2; // Transfer player number
|
||||
P_CheckMissileSpawn (mo, self->radius);
|
||||
if (self->special1 != -1 && !S_IsActorPlayingSomething (self, CHAN_BODY, -1))
|
||||
{
|
||||
S_Sound (self, CHAN_BODY|CHAN_LOOP, self->special1, 1, ATTN_NORM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_RainImpact
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_RainImpact)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
if (self->Z() > self->floorz)
|
||||
{
|
||||
self->SetState (self->FindState("NotFloor"));
|
||||
}
|
||||
else if (pr_impact() < 40)
|
||||
{
|
||||
P_HitFloor (self);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// PROC A_HideInCeiling
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_HideInCeiling)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
// We use bouncecount to store the 3D floor index
|
||||
double foo;
|
||||
for (int i = self->Sector->e->XFloor.ffloors.Size() - 1; i >= 0; i--)
|
||||
{
|
||||
F3DFloor * rover = self->Sector->e->XFloor.ffloors[i];
|
||||
if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue;
|
||||
|
||||
if ((foo = rover->bottom.plane->ZatPoint(self)) >= self->Top())
|
||||
{
|
||||
self->SetZ(foo + 4, false);
|
||||
self->bouncecount = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
self->bouncecount = -1;
|
||||
self->SetZ(self->ceilingz + 4, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- Phoenix Rod ----------------------------------------------------------
|
||||
|
||||
class APhoenixRod : public AWeapon
|
||||
|
|
|
|||
|
|
@ -764,3 +764,22 @@ xx(a)
|
|||
xx(r)
|
||||
xx(g)
|
||||
xx(b)
|
||||
|
||||
// Special translation names
|
||||
xx(RainPillar1)
|
||||
xx(RainPillar2)
|
||||
xx(RainPillar3)
|
||||
xx(RainPillar4)
|
||||
xx(RainPillar5)
|
||||
xx(RainPillar6)
|
||||
xx(RainPillar7)
|
||||
xx(RainPillar8)
|
||||
|
||||
xx(Player1)
|
||||
xx(Player2)
|
||||
xx(Player3)
|
||||
xx(Player4)
|
||||
xx(Player5)
|
||||
xx(Player6)
|
||||
xx(Player7)
|
||||
xx(Player8)
|
||||
|
|
|
|||
|
|
@ -6802,7 +6802,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetVisibleRotation)
|
|||
DEFINE_ACTION_FUNCTION(AActor, A_SetTranslation)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_STRING(trname);
|
||||
PARAM_NAME(trname);
|
||||
|
||||
self->SetTranslation(trname);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1089,6 +1089,14 @@ AInventory *AActor::GiveInventoryType (PClassActor *type)
|
|||
return item;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, GiveInventoryType)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_CLASS(type, AInventory);
|
||||
ACTION_RETURN_OBJECT(self->GiveInventoryType(type));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// AActor :: GiveAmmo
|
||||
|
|
@ -7098,9 +7106,10 @@ int AActor::ApplyDamageFactor(FName damagetype, int damage) const
|
|||
}
|
||||
|
||||
|
||||
void AActor::SetTranslation(const char *trname)
|
||||
void AActor::SetTranslation(FName trname)
|
||||
{
|
||||
if (*trname == 0)
|
||||
// There is no constant for the empty name...
|
||||
if (trname.GetChars()[0] == 0)
|
||||
{
|
||||
// an empty string resets to the default
|
||||
Translation = GetDefault()->Translation;
|
||||
|
|
@ -7341,6 +7350,12 @@ DEFINE_ACTION_FUNCTION(AActor, RestoreDamage)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, PlayerNumber)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
ACTION_RETURN_INT(self->player ? int(self->player - players) : 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// DropItem handling
|
||||
|
|
|
|||
|
|
@ -960,7 +960,7 @@ double sector_t::NextHighestCeilingAt(double x, double y, double bottomz, double
|
|||
}
|
||||
}
|
||||
if ((flags & FFCF_NOPORTALS) || sec->PortalBlocksMovement(ceiling) || planeheight >= sec->GetPortalPlaneZ(ceiling))
|
||||
{ // Use sector's floor
|
||||
{ // Use sector's ceiling
|
||||
if (resultffloor) *resultffloor = NULL;
|
||||
if (resultsec) *resultsec = sec;
|
||||
return realceil;
|
||||
|
|
@ -976,6 +976,34 @@ double sector_t::NextHighestCeilingAt(double x, double y, double bottomz, double
|
|||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Sector, NextHighestCeilingAt)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
PARAM_FLOAT(bottomz);
|
||||
PARAM_FLOAT(topz);
|
||||
PARAM_INT_DEF(flags);
|
||||
sector_t *resultsec;
|
||||
F3DFloor *resultff;
|
||||
double resultheight = self->NextHighestCeilingAt(x, y, bottomz, topz, flags, &resultsec, &resultff);
|
||||
|
||||
if (numret > 2)
|
||||
{
|
||||
ret[2].SetPointer(resultff, ATAG_GENERIC);
|
||||
numret = 3;
|
||||
}
|
||||
if (numret > 1)
|
||||
{
|
||||
ret[1].SetPointer(resultsec, ATAG_GENERIC);
|
||||
}
|
||||
if (numret > 0)
|
||||
{
|
||||
ret[0].SetFloat(resultheight);
|
||||
}
|
||||
return numret;
|
||||
}
|
||||
|
||||
double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, double steph, sector_t **resultsec, F3DFloor **resultffloor)
|
||||
{
|
||||
sector_t *sec = this;
|
||||
|
|
|
|||
|
|
@ -1196,20 +1196,37 @@ void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayer
|
|||
//----------------------------------------------------------------------------
|
||||
static TMap<FName, int> customTranslationMap;
|
||||
|
||||
int R_FindCustomTranslation(const char *name)
|
||||
int R_FindCustomTranslation(FName name)
|
||||
{
|
||||
if (name == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Ice is a special case which will remain in its original slot.
|
||||
if (!stricmp(name, "Ice"))
|
||||
switch (name)
|
||||
{
|
||||
case NAME_Ice:
|
||||
// Ice is a special case which will remain in its original slot.
|
||||
return TRANSLATION(TRANSLATION_Standard, 7);
|
||||
}
|
||||
else if (!stricmp(name, "None"))
|
||||
{
|
||||
|
||||
case NAME_None:
|
||||
return 0;
|
||||
|
||||
case NAME_RainPillar1:
|
||||
case NAME_RainPillar2:
|
||||
case NAME_RainPillar3:
|
||||
case NAME_RainPillar4:
|
||||
case NAME_RainPillar5:
|
||||
case NAME_RainPillar6:
|
||||
case NAME_RainPillar7:
|
||||
case NAME_RainPillar8:
|
||||
return TRANSLATION(TRANSLATION_RainPillar, name.GetIndex() - NAME_RainPillar1);
|
||||
|
||||
case NAME_Player1:
|
||||
case NAME_Player2:
|
||||
case NAME_Player3:
|
||||
case NAME_Player4:
|
||||
case NAME_Player5:
|
||||
case NAME_Player6:
|
||||
case NAME_Player7:
|
||||
case NAME_Player8:
|
||||
return TRANSLATION(TRANSLATION_Players, name.GetIndex() - NAME_Player1);
|
||||
|
||||
}
|
||||
int *t = customTranslationMap.CheckKey(FName(name, true));
|
||||
return (t != nullptr)? *t : -1;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ extern TArray<PalEntry> BloodTranslationColors;
|
|||
|
||||
int CreateBloodTranslation(PalEntry color);
|
||||
|
||||
int R_FindCustomTranslation(const char *name);
|
||||
int R_FindCustomTranslation(FName name);
|
||||
void R_ParseTrnslate();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7609,7 +7609,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
bool writable;
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx); // nust be resolved before the address is requested.
|
||||
if (ArgList[i]->ValueType != TypeNullPtr)
|
||||
if (ArgList[i] != nullptr && ArgList[i]->ValueType != TypeNullPtr)
|
||||
{
|
||||
ArgList[i]->RequestAddress(ctx, &writable);
|
||||
ArgList[i]->ValueType = NewPointer(ArgList[i]->ValueType);
|
||||
|
|
|
|||
|
|
@ -700,6 +700,10 @@ void InitThingdef()
|
|||
PStruct *sstruct = NewNativeStruct("Sector", nullptr);
|
||||
auto sptr = NewPointer(sstruct);
|
||||
sstruct->AddNativeField("soundtarget", TypeActor, myoffsetof(sector_t, SoundTarget));
|
||||
|
||||
// expose the glibal Multiplayer variable.
|
||||
PField *multif = new PField("multiplayer", TypeBool, VARF_Native | VARF_ReadOnly | VARF_Static, (intptr_t)&multiplayer);
|
||||
GlobalSymbols.AddSymbol(multif);
|
||||
|
||||
// set up a variable for the global level data structure
|
||||
PStruct *lstruct = NewNativeStruct("LevelLocals", nullptr);
|
||||
|
|
@ -708,8 +712,9 @@ void InitThingdef()
|
|||
|
||||
// set up a variable for the DEH data
|
||||
PStruct *dstruct = NewNativeStruct("DehInfo", nullptr);
|
||||
PField *dehi = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh);
|
||||
GlobalSymbols.AddSymbol(dehi);
|
||||
PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh);
|
||||
|
||||
GlobalSymbols.AddSymbol(dehf);
|
||||
|
||||
// set up a variable for the global players array.
|
||||
PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue