diff --git a/src/actor.h b/src/actor.h index 36fcc5de4..bdb5241d1 100644 --- a/src/actor.h +++ b/src/actor.h @@ -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 { diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index 4e2d62f1d..7ec036413 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -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 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 (); - - // 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 (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 (); - 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 (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 diff --git a/src/namedef.h b/src/namedef.h index b7365b2b2..779c2e132 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -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) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 24c98f86c..95bcb3e25 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -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; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index cde2eb0b4..f956d8911 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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 diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 5ed4cd144..e93d718cd 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -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; diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index 983baea58..3689d1c0f 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -1196,20 +1196,37 @@ void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayer //---------------------------------------------------------------------------- static TMap 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; diff --git a/src/r_data/r_translate.h b/src/r_data/r_translate.h index 4ca2f203f..1680f4df0 100644 --- a/src/r_data/r_translate.h +++ b/src/r_data/r_translate.h @@ -110,7 +110,7 @@ extern TArray BloodTranslationColors; int CreateBloodTranslation(PalEntry color); -int R_FindCustomTranslation(const char *name); +int R_FindCustomTranslation(FName name); void R_ParseTrnslate(); diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index a34e12f7d..908850e3e 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 3f7f27e43..1b93e22a5 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -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); diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 3507e45a8..7144c64e5 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -102,6 +102,7 @@ zscript/heretic/weaponcrossbow.txt zscript/heretic/weapongauntlets.txt zscript/heretic/weaponmace.txt zscript/heretic/weaponblaster.txt +zscript/heretic/weaponskullrod.txt zscript/hexen/baseweapons.txt zscript/hexen/korax.txt diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 3eb855007..1b13d05ff 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -5,6 +5,7 @@ class Actor : Thinker native const ONCEILINGZ = 2147483647.0; const FLOATRANDZ = ONCEILINGZ-1; const TELEFRAG_DAMAGE = 1000000; + const MinVel = 1./65536; // 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 bool UseInventory(Inventory item); + native void AdjustPlayerAngle(FTranslatedLineTarget t); native static readonly GetDefaultByType(class cls); native static double GetDefaultSpeed(class type); @@ -273,6 +275,7 @@ class Actor : Thinker native native bool GiveBody (int num, int max=0); native bool HitFloor(); native bool isTeammate(Actor other); + native int PlayerNumber(); native void RestoreDamage(); native int SpawnHealth(); @@ -342,6 +345,7 @@ class Actor : Thinker native // DECORATE compatible functions native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native Inventory FindInventory(class itemtype, bool subclass = false); + native Inventory GiveInventoryType(class itemtype); native int CountInv(class itemtype, int ptr_select = AAPTR_DEFAULT); native double GetDistance(bool checkz, int ptr = AAPTR_TARGET); 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 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 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_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 160c1c514..13aa480a9 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -122,6 +122,12 @@ struct State native 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); +} + diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 68b81dc2b..70f8abed0 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -953,4 +953,32 @@ enum EWeaponPos { WEAPONBOTTOM = 128, WEAPONTOP = 32 -} \ No newline at end of file +} + +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) +}; diff --git a/wadsrc/static/zscript/heretic/hereticweaps.txt b/wadsrc/static/zscript/heretic/hereticweaps.txt index c3c641b4e..1b84d13fe 100644 --- a/wadsrc/static/zscript/heretic/hereticweaps.txt +++ b/wadsrc/static/zscript/heretic/hereticweaps.txt @@ -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 -------------------------------------------------------------- class PhoenixRod : Weapon native diff --git a/wadsrc/static/zscript/heretic/weaponskullrod.txt b/wadsrc/static/zscript/heretic/weaponskullrod.txt new file mode 100644 index 000000000..a8d77e1d8 --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponskullrod.txt @@ -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 + } +}