- 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
parent c2f7ed7f1c
commit 8dba322775
16 changed files with 570 additions and 521 deletions

View file

@ -759,7 +759,7 @@ public:
virtual FName GetSpecies(); virtual FName GetSpecies();
// set translation // set translation
void SetTranslation(const char *trname); void SetTranslation(FName trname);
double GetBobOffset(double ticfrac = 0) const double GetBobOffset(double ticfrac = 0) const
{ {

View file

@ -46,305 +46,6 @@ void P_DSparilTeleport (AActor *actor);
extern bool P_AutoUseChaosDevice (player_t *player); 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 ---------------------------------------------------------- // --- Phoenix Rod ----------------------------------------------------------
class APhoenixRod : public AWeapon class APhoenixRod : public AWeapon

View file

@ -764,3 +764,22 @@ xx(a)
xx(r) xx(r)
xx(g) xx(g)
xx(b) 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) DEFINE_ACTION_FUNCTION(AActor, A_SetTranslation)
{ {
PARAM_SELF_PROLOGUE(AActor); PARAM_SELF_PROLOGUE(AActor);
PARAM_STRING(trname); PARAM_NAME(trname);
self->SetTranslation(trname); self->SetTranslation(trname);
return 0; return 0;

View file

@ -1089,6 +1089,14 @@ AInventory *AActor::GiveInventoryType (PClassActor *type)
return item; return item;
} }
DEFINE_ACTION_FUNCTION(AActor, GiveInventoryType)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(type, AInventory);
ACTION_RETURN_OBJECT(self->GiveInventoryType(type));
}
//============================================================================ //============================================================================
// //
// AActor :: GiveAmmo // 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 // an empty string resets to the default
Translation = GetDefault()->Translation; Translation = GetDefault()->Translation;
@ -7341,6 +7350,12 @@ DEFINE_ACTION_FUNCTION(AActor, RestoreDamage)
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(AActor, PlayerNumber)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_INT(self->player ? int(self->player - players) : 0);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// //
// DropItem handling // 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)) if ((flags & FFCF_NOPORTALS) || sec->PortalBlocksMovement(ceiling) || planeheight >= sec->GetPortalPlaneZ(ceiling))
{ // Use sector's floor { // Use sector's ceiling
if (resultffloor) *resultffloor = NULL; if (resultffloor) *resultffloor = NULL;
if (resultsec) *resultsec = sec; if (resultsec) *resultsec = sec;
return realceil; 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) double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, double steph, sector_t **resultsec, F3DFloor **resultffloor)
{ {
sector_t *sec = this; sector_t *sec = this;

View file

@ -1196,20 +1196,37 @@ void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayer
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
static TMap<FName, int> customTranslationMap; static TMap<FName, int> customTranslationMap;
int R_FindCustomTranslation(const char *name) int R_FindCustomTranslation(FName name)
{ {
if (name == nullptr) switch (name)
{ {
return -1; case NAME_Ice:
}
// Ice is a special case which will remain in its original slot. // Ice is a special case which will remain in its original slot.
if (!stricmp(name, "Ice"))
{
return TRANSLATION(TRANSLATION_Standard, 7); return TRANSLATION(TRANSLATION_Standard, 7);
}
else if (!stricmp(name, "None")) case NAME_None:
{
return 0; 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)); int *t = customTranslationMap.CheckKey(FName(name, true));
return (t != nullptr)? *t : -1; return (t != nullptr)? *t : -1;

View file

@ -110,7 +110,7 @@ extern TArray<PalEntry> BloodTranslationColors;
int CreateBloodTranslation(PalEntry color); int CreateBloodTranslation(PalEntry color);
int R_FindCustomTranslation(const char *name); int R_FindCustomTranslation(FName name);
void R_ParseTrnslate(); void R_ParseTrnslate();

View file

@ -7609,7 +7609,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
{ {
bool writable; bool writable;
ArgList[i] = ArgList[i]->Resolve(ctx); // nust be resolved before the address is requested. 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]->RequestAddress(ctx, &writable);
ArgList[i]->ValueType = NewPointer(ArgList[i]->ValueType); ArgList[i]->ValueType = NewPointer(ArgList[i]->ValueType);

View file

@ -701,6 +701,10 @@ void InitThingdef()
auto sptr = NewPointer(sstruct); auto sptr = NewPointer(sstruct);
sstruct->AddNativeField("soundtarget", TypeActor, myoffsetof(sector_t, SoundTarget)); 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 // set up a variable for the global level data structure
PStruct *lstruct = NewNativeStruct("LevelLocals", nullptr); PStruct *lstruct = NewNativeStruct("LevelLocals", nullptr);
PField *levelf = new PField("level", lstruct, VARF_Native | VARF_Static, (intptr_t)&level); PField *levelf = new PField("level", lstruct, VARF_Native | VARF_Static, (intptr_t)&level);
@ -708,8 +712,9 @@ void InitThingdef()
// set up a variable for the DEH data // set up a variable for the DEH data
PStruct *dstruct = NewNativeStruct("DehInfo", nullptr); PStruct *dstruct = NewNativeStruct("DehInfo", nullptr);
PField *dehi = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh); PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh);
GlobalSymbols.AddSymbol(dehi);
GlobalSymbols.AddSymbol(dehf);
// set up a variable for the global players array. // set up a variable for the global players array.
PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr);

View file

@ -102,6 +102,7 @@ zscript/heretic/weaponcrossbow.txt
zscript/heretic/weapongauntlets.txt zscript/heretic/weapongauntlets.txt
zscript/heretic/weaponmace.txt zscript/heretic/weaponmace.txt
zscript/heretic/weaponblaster.txt zscript/heretic/weaponblaster.txt
zscript/heretic/weaponskullrod.txt
zscript/hexen/baseweapons.txt zscript/hexen/baseweapons.txt
zscript/hexen/korax.txt zscript/hexen/korax.txt

View file

@ -5,6 +5,7 @@ class Actor : Thinker native
const ONCEILINGZ = 2147483647.0; const ONCEILINGZ = 2147483647.0;
const FLOATRANDZ = ONCEILINGZ-1; const FLOATRANDZ = ONCEILINGZ-1;
const TELEFRAG_DAMAGE = 1000000; const TELEFRAG_DAMAGE = 1000000;
const MinVel = 1./65536;
// flags are not defined here, the native fields for those get synthesized from the internal tables. // flags are not defined here, the native fields for those get synthesized from the internal tables.
@ -263,6 +264,7 @@ class Actor : Thinker native
virtual native int DoSpecialDamage (Actor target, int damage, Name damagetype); virtual native int DoSpecialDamage (Actor target, int damage, Name damagetype);
virtual native bool UseInventory(Inventory item); virtual native bool UseInventory(Inventory item);
native void AdjustPlayerAngle(FTranslatedLineTarget t); native void AdjustPlayerAngle(FTranslatedLineTarget t);
native static readonly<Actor> GetDefaultByType(class<Actor> cls); native static readonly<Actor> GetDefaultByType(class<Actor> cls);
native static double GetDefaultSpeed(class<Actor> type); native static double GetDefaultSpeed(class<Actor> type);
@ -273,6 +275,7 @@ class Actor : Thinker native
native bool GiveBody (int num, int max=0); native bool GiveBody (int num, int max=0);
native bool HitFloor(); native bool HitFloor();
native bool isTeammate(Actor other); native bool isTeammate(Actor other);
native int PlayerNumber();
native void RestoreDamage(); native void RestoreDamage();
native int SpawnHealth(); native int SpawnHealth();
@ -342,6 +345,7 @@ class Actor : Thinker native
// DECORATE compatible functions // DECORATE compatible functions
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false); native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false);
native Inventory GiveInventoryType(class<Inventory> itemtype);
native int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT); native int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT);
native double GetDistance(bool checkz, int ptr = AAPTR_TARGET); native double GetDistance(bool checkz, int ptr = AAPTR_TARGET);
native double GetAngle(int flags, int ptr = AAPTR_DEFAULT); native double GetAngle(int flags, int ptr = AAPTR_DEFAULT);
@ -720,7 +724,7 @@ class Actor : Thinker native
native int A_ClearOverlays(int sstart = 0, int sstop = 0, bool safety = true); native int A_ClearOverlays(int sstart = 0, int sstop = 0, bool safety = true);
native bool A_CopySpriteFrame(int from, int to, int flags = 0); native bool A_CopySpriteFrame(int from, int to, int flags = 0);
native bool A_SetVisibleRotation(double anglestart = 0, double angleend = 0, double pitchstart = 0, double pitchend = 0, int flags = 0, int ptr = AAPTR_DEFAULT); native bool A_SetVisibleRotation(double anglestart = 0, double angleend = 0, double pitchstart = 0, double pitchend = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
native void A_SetTranslation(string transname); native void A_SetTranslation(name transname);
native void A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0); native void A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0);
native void A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); native void A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0);

View file

@ -122,6 +122,12 @@ struct State native
native bool bDehacked; native bool bDehacked;
} }
struct Sector native struct F3DFloor native
{ {
} }
struct Sector native
{
native double, Sector, F3DFloor NextHighestCeilingAt(double x, double y, double bottomz, double topz, int flags = 0);
}

View file

@ -954,3 +954,31 @@ enum EWeaponPos
WEAPONBOTTOM = 128, WEAPONBOTTOM = 128,
WEAPONTOP = 32 WEAPONTOP = 32
} }
enum ETranslationTable
{
TRANSLATION_Invalid,
TRANSLATION_Players,
TRANSLATION_PlayersExtra,
TRANSLATION_Standard,
TRANSLATION_LevelScripted,
TRANSLATION_Decals,
TRANSLATION_PlayerCorpses,
TRANSLATION_Decorate,
TRANSLATION_Blood,
TRANSLATION_RainPillar,
TRANSLATION_Custom,
};
enum EFindFloorCeiling
{
FFCF_ONLYSPAWNPOS = 1,
FFCF_SAMESECTOR = 2,
FFCF_ONLY3DFLOORS = 4, // includes 3D midtexes
FFCF_3DRESTRICT = 8, // ignore 3D midtexes and floors whose floorz are above thing's z
FFCF_NOPORTALS = 16, // ignore portals (considers them impassable.)
FFCF_NOFLOOR = 32,
FFCF_NOCEILING = 64,
FFCF_RESTRICTEDPORTAL = 128, // current values in the iterator's return are through a restricted portal type (i.e. some features are blocked.)
FFCF_NODROPOFF = 256, // Caller does not need a dropoff (saves some time when checking portals)
};

View file

@ -8,206 +8,6 @@ class HereticWeapon : Weapon
} }
// Skull (Horn) Rod ---------------------------------------------------------
class SkullRod : HereticWeapon
{
Default
{
Weapon.SelectionOrder 200;
Weapon.AmmoUse1 1;
Weapon.AmmoGive1 50;
Weapon.YAdjust 15;
Weapon.AmmoType1 "SkullRodAmmo";
Weapon.SisterWeapon "SkullRodPowered";
Inventory.PickupMessage "$TXT_WPNSKULLROD";
Tag "$TAG_SKULLROD";
}
action native void A_FireSkullRodPL1();
States
{
Spawn:
WSKL A -1;
Stop;
Ready:
HROD A 1 A_WeaponReady;
Loop;
Deselect:
HROD A 1 A_Lower;
Loop;
Select:
HROD A 1 A_Raise;
Loop;
Fire:
HROD AB 4 A_FireSkullRodPL1;
HROD B 0 A_ReFire;
Goto Ready;
}
}
class SkullRodPowered : SkullRod
{
Default
{
+WEAPON.POWERED_UP
Weapon.AmmoUse1 5;
Weapon.AmmoGive1 0;
Weapon.SisterWeapon "SkullRod";
Tag "$TAG_SKULLRODP";
}
action native void A_FireSkullRodPL2();
States
{
Fire:
HROD C 2;
HROD D 3;
HROD E 2;
HROD F 3;
HROD G 4 A_FireSkullRodPL2;
HROD F 2;
HROD E 3;
HROD D 2;
HROD C 2 A_ReFire;
Goto Ready;
}
}
// Horn Rod FX 1 ------------------------------------------------------------
class HornRodFX1 : Actor
{
Default
{
Radius 12;
Height 8;
Speed 22;
Damage 3;
Projectile;
+WINDTHRUST
-NOBLOCKMAP
RenderStyle "Add";
SeeSound "weapons/hornrodshoot";
DeathSound "weapons/hornrodhit";
Obituary "$OB_MPSKULLROD";
}
States
{
Spawn:
FX00 AB 6 BRIGHT;
Loop;
Death:
FX00 HI 5 BRIGHT;
FX00 JK 4 BRIGHT;
FX00 LM 3 BRIGHT;
Stop;
}
}
// Horn Rod FX 2 ------------------------------------------------------------
class HornRodFX2 : Actor
{
Default
{
Radius 12;
Height 8;
Speed 22;
Damage 10;
Health 140;
Projectile;
RenderStyle "Add";
SeeSound "weapons/hornrodpowshoot";
DeathSound "weapons/hornrodpowhit";
Obituary "$OB_MPPSKULLROD";
}
native void A_AddPlayerRain();
native void A_HideInCeiling();
native void A_SkullRodStorm();
States
{
Spawn:
FX00 C 3 BRIGHT;
FX00 D 3 BRIGHT A_SeekerMissile(10, 30);
FX00 E 3 BRIGHT;
FX00 F 3 BRIGHT A_SeekerMissile(10, 30);
Loop;
Death:
FX00 H 5 BRIGHT A_AddPlayerRain;
FX00 I 5 BRIGHT;
FX00 J 4 BRIGHT;
FX00 KLM 3 BRIGHT;
FX00 G 1 A_HideInCeiling;
FX00 G 1 A_SkullRodStorm;
Wait;
}
override int DoSpecialDamage (Actor target, int damage, Name damagetype)
{
Sorcerer2 s2 = Sorcerer2(target);
if (s2 != null && random[HornRodFX2]() < 96)
{ // D'Sparil teleports away
s2.DSparilTeleport ();
return -1;
}
return damage;
}
}
// Rain pillar 1 ------------------------------------------------------------
class RainPillar : Actor native
{
Default
{
Radius 5;
Height 12;
Speed 12;
Damage 5;
Mass 5;
Projectile;
-ACTIVATEPCROSS
-ACTIVATEIMPACT
RenderStyle "Add";
Obituary "$OB_MPPSKULLROD";
}
native void A_RainImpact();
States
{
Spawn:
FX22 A -1 BRIGHT;
Stop;
Death:
FX22 B 4 BRIGHT A_RainImpact;
FX22 CDEF 4 BRIGHT;
Stop;
NotFloor:
FX22 GHI 4 BRIGHT;
Stop;
}
}
// Rain tracker "inventory" item --------------------------------------------
class RainTracker : Inventory native
{
Default
{
+INVENTORY.UNDROPPABLE
}
}
// Phoenix Rod -------------------------------------------------------------- // Phoenix Rod --------------------------------------------------------------
class PhoenixRod : Weapon native class PhoenixRod : Weapon native

View file

@ -0,0 +1,425 @@
// Skull (Horn) Rod ---------------------------------------------------------
class SkullRod : HereticWeapon
{
Default
{
Weapon.SelectionOrder 200;
Weapon.AmmoUse1 1;
Weapon.AmmoGive1 50;
Weapon.YAdjust 15;
Weapon.AmmoType1 "SkullRodAmmo";
Weapon.SisterWeapon "SkullRodPowered";
Inventory.PickupMessage "$TXT_WPNSKULLROD";
Tag "$TAG_SKULLROD";
}
States
{
Spawn:
WSKL A -1;
Stop;
Ready:
HROD A 1 A_WeaponReady;
Loop;
Deselect:
HROD A 1 A_Lower;
Loop;
Select:
HROD A 1 A_Raise;
Loop;
Fire:
HROD AB 4 A_FireSkullRodPL1;
HROD B 0 A_ReFire;
Goto Ready;
}
//----------------------------------------------------------------------------
//
// PROC A_FireSkullRodPL1
//
//----------------------------------------------------------------------------
action void A_FireSkullRodPL1()
{
if (player == null)
{
return;
}
Weapon weapon = player.ReadyWeapon;
if (weapon != null)
{
if (!weapon.DepleteAmmo (weapon.bAltFire))
return;
}
Actor mo = SpawnPlayerMissile ("HornRodFX1");
// Randomize the first frame
if (mo && random[FireSkullRod]() > 128)
{
mo.SetState (mo.CurState.NextState);
}
}
}
class SkullRodPowered : SkullRod
{
Default
{
+WEAPON.POWERED_UP
Weapon.AmmoUse1 5;
Weapon.AmmoGive1 0;
Weapon.SisterWeapon "SkullRod";
Tag "$TAG_SKULLRODP";
}
States
{
Fire:
HROD C 2;
HROD D 3;
HROD E 2;
HROD F 3;
HROD G 4 A_FireSkullRodPL2;
HROD F 2;
HROD E 3;
HROD D 2;
HROD C 2 A_ReFire;
Goto Ready;
}
//----------------------------------------------------------------------------
//
// 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.
//
//----------------------------------------------------------------------------
action void A_FireSkullRodPL2()
{
FTranslatedLineTarget t;
if (player == null)
{
return;
}
Weapon weapon = player.ReadyWeapon;
if (weapon != null)
{
if (!weapon.DepleteAmmo (weapon.bAltFire))
return;
}
// Use MissileActor instead of the first return value from P_SpawnPlayerMissile
// because we need to give info to it, even if it exploded immediately.
Actor mo, MissileActor;
[mo, MissileActor] = SpawnPlayerMissile ("HornRodFX2", angle, pLineTarget: t);
if (MissileActor != null)
{
if (t.linetarget && !t.unlinked)
{
MissileActor.tracer = t.linetarget;
}
MissileActor.A_PlaySound ("weapons/hornrodpowshoot", CHAN_WEAPON);
}
}
}
// Horn Rod FX 1 ------------------------------------------------------------
class HornRodFX1 : Actor
{
Default
{
Radius 12;
Height 8;
Speed 22;
Damage 3;
Projectile;
+WINDTHRUST
-NOBLOCKMAP
RenderStyle "Add";
SeeSound "weapons/hornrodshoot";
DeathSound "weapons/hornrodhit";
Obituary "$OB_MPSKULLROD";
}
States
{
Spawn:
FX00 AB 6 BRIGHT;
Loop;
Death:
FX00 HI 5 BRIGHT;
FX00 JK 4 BRIGHT;
FX00 LM 3 BRIGHT;
Stop;
}
}
// Horn Rod FX 2 ------------------------------------------------------------
class HornRodFX2 : Actor
{
Default
{
Radius 12;
Height 8;
Speed 22;
Damage 10;
Health 140;
Projectile;
RenderStyle "Add";
SeeSound "weapons/hornrodpowshoot";
DeathSound "weapons/hornrodpowhit";
Obituary "$OB_MPPSKULLROD";
}
States
{
Spawn:
FX00 C 3 BRIGHT;
FX00 D 3 BRIGHT A_SeekerMissile(10, 30);
FX00 E 3 BRIGHT;
FX00 F 3 BRIGHT A_SeekerMissile(10, 30);
Loop;
Death:
FX00 H 5 BRIGHT A_AddPlayerRain;
FX00 I 5 BRIGHT;
FX00 J 4 BRIGHT;
FX00 KLM 3 BRIGHT;
FX00 G 1 A_HideInCeiling;
FX00 G 1 A_SkullRodStorm;
Wait;
}
override int DoSpecialDamage (Actor target, int damage, Name damagetype)
{
Sorcerer2 s2 = Sorcerer2(target);
if (s2 != null && random[HornRodFX2]() < 96)
{ // D'Sparil teleports away
s2.DSparilTeleport ();
return -1;
}
return damage;
}
//----------------------------------------------------------------------------
//
// PROC A_AddPlayerRain
//
//----------------------------------------------------------------------------
void A_AddPlayerRain()
{
RainTracker tracker;
if (target == null || target.health <= 0)
{ // Shooter is dead or nonexistant
return;
}
tracker = RainTracker(target.FindInventory("RainTracker"));
// 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 = RainTracker(target.GiveInventoryType("RainTracker"));
}
// Add rain mobj to list
if (tracker.Rain1)
{
tracker.Rain2 = self;
}
else
{
tracker.Rain1 = self;
}
ActiveSound = "misc/rain";
}
//----------------------------------------------------------------------------
//
// PROC A_HideInCeiling
//
//----------------------------------------------------------------------------
void A_HideInCeiling()
{
// This no longer hides in the ceiling. It just makes the actor invisible and keeps it in place.
// We need its actual position to determine the correct ceiling height in A_SkullRodStorm.
bInvisible = true;
bSolid = false;
bMissile = false;
Vel = (0,0,0);
}
//----------------------------------------------------------------------------
//
// PROC A_SkullRodStorm
//
//----------------------------------------------------------------------------
void A_SkullRodStorm()
{
static const Name translations[] =
{
"RainPillar1", "RainPillar2", "RainPillar3", "RainPillar4",
"RainPillar5", "RainPillar6", "RainPillar7", "RainPillar8"
};
if (health-- == 0)
{
A_StopSound (CHAN_BODY);
if (target == null)
{ // Player left the game
Destroy ();
return;
}
RainTracker tracker = RainTracker(target.FindInventory("RainTracker"));
if (tracker != null)
{
if (tracker.Rain1 == self)
{
tracker.Rain1 = null;
}
else if (tracker.Rain2 == self)
{
tracker.Rain2 = null;
}
}
Destroy ();
return;
}
if (Random[SkullRodStorm]() < 25)
{ // Fudge rain frequency
return;
}
double xo = ((Random[SkullRodStorm]() & 127) - 64);
double yo = ((Random[SkullRodStorm]() & 127) - 64);
Vector3 spawnpos = Vec2OffsetZ(xo, yo, pos.z);
Actor mo = Spawn("RainPillar", spawnpos, ALLOW_REPLACE);
if (!mo) return;
// Find the ceiling above the spawn location. This may come from 3D floors but will not reach through portals.
// (should probably be fixed for portals, too.)
double newz = mo.CurSector.NextHighestCeilingAt(mo.pos.x, mo.pos.y, mo.pos.z, mo.pos.z, FFCF_NOPORTALS) - mo.height;
mo.SetZ(newz);
if (multiplayer && target.player)
{
mo.A_SetTranslation(translations[target.PlayerNumber()]);
}
mo.target = target;
mo.Vel.X = MinVel; // Force collision detection
mo.Vel.Z = -mo.Speed;
mo.CheckMissileSpawn (radius);
if (ActiveSound > 0) A_PlaySound(ActiveSound, CHAN_BODY, 1, true);
}
}
// Rain pillar 1 ------------------------------------------------------------
class RainPillar : Actor
{
Default
{
Radius 5;
Height 12;
Speed 12;
Damage 5;
Mass 5;
Projectile;
-ACTIVATEPCROSS
-ACTIVATEIMPACT
RenderStyle "Add";
Obituary "$OB_MPPSKULLROD";
}
States
{
Spawn:
FX22 A -1 BRIGHT;
Stop;
Death:
FX22 B 4 BRIGHT A_RainImpact;
FX22 CDEF 4 BRIGHT;
Stop;
NotFloor:
FX22 GHI 4 BRIGHT;
Stop;
}
//----------------------------------------------------------------------------
//
// PROC A_RainImpact
//
//----------------------------------------------------------------------------
void A_RainImpact()
{
if (pos.z > floorz)
{
SetStateLabel("NotFloor");
}
else if (random[RainImpact]() < 40)
{
HitFloor ();
}
}
// Rain pillar 1 ------------------------------------------------------------
int DoSpecialDamage (Actor target, int damage, Name damagetype)
{
if (target.bBoss)
{ // Decrease damage for bosses
damage = random[RainDamage](1, 8);
}
return damage;
}
}
// Rain tracker "inventory" item --------------------------------------------
class RainTracker : Inventory
{
Actor Rain1, Rain2;
Default
{
+INVENTORY.UNDROPPABLE
}
}