- 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:
Christoph Oelckers 2016-11-25 16:05:03 +01:00
commit 8dba322775
16 changed files with 569 additions and 520 deletions

View file

@ -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
{

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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);

View file

@ -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);