From 7385cd70c04f471c6a3e5c2a4daba6463359d4c2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 26 Nov 2016 01:14:47 +0100 Subject: [PATCH] - scriptified the Minotaur. Interesting tidbit: The damage calculation in P_MinotaurSlam had been incorrect for the Heretic version since the friendly Hexen Dark Servant was added, but nobody ever noticed in 14 years... --- src/CMakeLists.txt | 5 - src/actor.h | 2 + src/g_hexen/a_hexenmisc.cpp | 2 - src/g_hexen/a_summon.cpp | 85 ---- src/g_raven/a_minotaur.cpp | 623 ----------------------- src/g_raven/ravenshared.h | 29 -- src/g_shared/a_morph.cpp | 2 +- src/g_strife/a_alienspectres.cpp | 2 +- src/g_strife/a_strifestuff.cpp | 2 +- src/p_interaction.cpp | 26 +- src/p_map.cpp | 16 +- src/p_maputl.cpp | 8 + src/p_mobj.cpp | 47 +- src/p_user.cpp | 2 +- src/scripting/codegeneration/codegen.cpp | 79 ++- src/scripting/thingdef_data.cpp | 13 +- src/w_wad.cpp | 9 + wadsrc/static/zscript/actor.txt | 7 + wadsrc/static/zscript/base.txt | 31 ++ wadsrc/static/zscript/constants.txt | 14 + wadsrc/static/zscript/hexen/summon.txt | 68 ++- wadsrc/static/zscript/raven/minotaur.txt | 551 +++++++++++++++++++- 22 files changed, 834 insertions(+), 789 deletions(-) delete mode 100644 src/g_hexen/a_summon.cpp delete mode 100644 src/g_raven/a_minotaur.cpp delete mode 100644 src/g_raven/ravenshared.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bca0e9371..95db06b2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -825,7 +825,6 @@ file( GLOB HEADER_FILES ${EXTRA_HEADER_DIRS} fragglescript/*.h g_hexen/*.h - g_raven/*.h g_shared/*.h g_strife/*.h intermission/*.h @@ -878,7 +877,6 @@ set( NOT_COMPILED_SOURCE_FILES g_hexen/a_magestaff.cpp g_hexen/a_serpent.cpp g_hexen/a_spike.cpp - g_hexen/a_summon.cpp g_hexen/a_teleportother.cpp g_strife/a_acolyte.cpp g_strife/a_alienspectres.cpp @@ -1147,7 +1145,6 @@ set (PCH_SOURCES wi_stuff.cpp zstrformat.cpp g_hexen/a_hexenmisc.cpp - g_raven/a_minotaur.cpp g_strife/a_strifestuff.cpp g_strife/strife_sbar.cpp g_shared/a_action.cpp @@ -1300,7 +1297,6 @@ endif() target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma ) include_directories( . g_hexen - g_raven g_strife g_shared oplsynth @@ -1432,7 +1428,6 @@ source_group("External\\RapidJSON" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_D source_group("Externak\\SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+") source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fragglescript/.+") source_group("Games\\Hexen Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_hexen/.+") -source_group("Games\\Raven Shared" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_raven/.+") source_group("Games\\Strife Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_strife/.+") source_group("Intermission" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/intermission/.+") source_group("Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/menu/.+") diff --git a/src/actor.h b/src/actor.h index bdb5241d1..f6020d292 100644 --- a/src/actor.h +++ b/src/actor.h @@ -628,6 +628,7 @@ public: // Called when actor dies virtual void Die (AActor *source, AActor *inflictor, int dmgflags = 0); + void CallDie(AActor *source, AActor *inflictor, int dmgflags = 0); // Perform some special damage action. Returns the amount of damage to do. // Returning -1 signals the damage routine to exit immediately @@ -660,6 +661,7 @@ public: // Actor had MF_SKULLFLY set and rammed into something // Returns false to stop moving and true to keep moving virtual bool Slam (AActor *victim); + bool CallSlam(AActor *victim); // Called by PIT_CheckThing() and needed for some Hexen things. // Returns -1 for normal behavior, 0 to return false, and 1 to return true. diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index f98305cba..f442df65e 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -15,7 +15,6 @@ #include "p_lnspec.h" #include "p_terrain.h" #include "m_bbox.h" -#include "ravenshared.h" #include "v_palette.h" #include "g_game.h" #include "p_blockmap.h" @@ -47,5 +46,4 @@ #include "a_magestaff.cpp" #include "a_serpent.cpp" #include "a_spike.cpp" -#include "a_summon.cpp" #include "a_teleportother.cpp" diff --git a/src/g_hexen/a_summon.cpp b/src/g_hexen/a_summon.cpp deleted file mode 100644 index 58e50eeaf..000000000 --- a/src/g_hexen/a_summon.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* -#include "info.h" -#include "a_pickups.h" -#include "a_artifacts.h" -#include "gstrings.h" -#include "p_local.h" -#include "s_sound.h" -#include "ravenshared.h" -#include "vm.h" -#include "g_level.h" -*/ - -void A_Summon (AActor *); - -// Dark Servant Artifact ---------------------------------------------------- - -class AArtiDarkServant : public AInventory -{ - DECLARE_CLASS (AArtiDarkServant, AInventory) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiDarkServant, false, false) - -//============================================================================ -// -// Activate the summoning artifact -// -//============================================================================ - -bool AArtiDarkServant::Use (bool pickup) -{ - AActor *mo = P_SpawnPlayerMissile (Owner, PClass::FindActor("SummoningDoll")); - if (mo) - { - mo->target = Owner; - mo->tracer = Owner; - mo->Vel.Z = 5; - } - return true; -} - -//============================================================================ -// -// A_Summon -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_Summon) -{ - PARAM_SELF_PROLOGUE(AActor); - - AMinotaurFriend *mo; - - mo = Spawn(self->Pos(), ALLOW_REPLACE); - if (mo) - { - if (P_TestMobjLocation(mo) == false || !self->tracer) - { // Didn't fit - change back to artifact - mo->Destroy(); - AActor *arti = Spawn(self->Pos(), ALLOW_REPLACE); - if (arti) arti->flags |= MF_DROPPED; - return 0; - } - - mo->StartTime = level.maptime; - if (self->tracer->flags & MF_CORPSE) - { // Master dead - mo->tracer = NULL; // No master - } - else - { - mo->tracer = self->tracer; // Pointer to master - AInventory *power = Spawn(); - power->CallTryPickup(self->tracer); - mo->SetFriendPlayer(self->tracer->player); - } - - // Make smoke puff - Spawn("MinotaurSmoke", self->Pos(), ALLOW_REPLACE); - S_Sound(self, CHAN_VOICE, mo->ActiveSound, 1, ATTN_NORM); - } - return 0; -} diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp deleted file mode 100644 index cd0ab2669..000000000 --- a/src/g_raven/a_minotaur.cpp +++ /dev/null @@ -1,623 +0,0 @@ -#include "actor.h" -#include "info.h" -#include "m_random.h" -#include "s_sound.h" -#include "p_local.h" -#include "p_enemy.h" -#include "ravenshared.h" -#include "a_action.h" -#include "gi.h" -#include "w_wad.h" -#include "vm.h" -#include "g_level.h" -#include "doomstat.h" -#include "a_pickups.h" -#include "d_player.h" -#include "serializer.h" -#include "vm.h" - -#define MAULATORTICS (25*35) - -static FRandom pr_minotauratk1 ("MinotaurAtk1"); -static FRandom pr_minotaurdecide ("MinotaurDecide"); -static FRandom pr_atk ("MinotaurAtk2"); -static FRandom pr_minotauratk3 ("MinotaurAtk3"); -static FRandom pr_fire ("MntrFloorFire"); -static FRandom pr_minotaurslam ("MinotaurSlam"); -static FRandom pr_minotaurroam ("MinotaurRoam"); -static FRandom pr_minotaurchase ("MinotaurChase"); - -void P_MinotaurSlam (AActor *source, AActor *target); - -DECLARE_ACTION(A_MinotaurLook) - -IMPLEMENT_CLASS(AMinotaur, false, false) - -void AMinotaur::Tick () -{ - Super::Tick (); - - // The unfriendly Minotaur (Heretic's) is invulnerable while charging - if (!(flags5 & MF5_SUMMONEDMONSTER)) - { - // Get MF_SKULLFLY bit and shift it so it matches MF2_INVULNERABLE - DWORD flying = (flags & MF_SKULLFLY) << 3; - if ((flags2 & MF2_INVULNERABLE) != flying) - { - flags2 ^= MF2_INVULNERABLE; - } - } -} - -bool AMinotaur::Slam (AActor *thing) -{ - // Slamming minotaurs shouldn't move non-creatures - if (!(thing->flags3&MF3_ISMONSTER) && !thing->player) - { - return false; - } - return Super::Slam (thing); -} - -int AMinotaur::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - damage = Super::DoSpecialDamage (target, damage, damagetype); - if ((damage != -1) && (flags & MF_SKULLFLY)) - { // Slam only when in charge mode - P_MinotaurSlam (this, target); - return -1; - } - return damage; -} - -// Minotaur Friend ---------------------------------------------------------- - -IMPLEMENT_CLASS(AMinotaurFriend, false, false) - -void AMinotaurFriend::BeginPlay () -{ - Super::BeginPlay (); - StartTime = -1; -} - -void AMinotaurFriend::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("starttime", StartTime); -} - -void AMinotaurFriend::Die (AActor *source, AActor *inflictor, int dmgflags) -{ - Super::Die (source, inflictor, dmgflags); - - if (tracer && tracer->health > 0 && tracer->player) - { - // Search thinker list for minotaur - TThinkerIterator iterator; - AMinotaurFriend *mo; - - while ((mo = iterator.Next()) != NULL) - { - if (mo->health <= 0) continue; - // [RH] Minotaurs can't be morphed, so this isn't needed - //if (!(mo->flags&MF_COUNTKILL)) continue; // for morphed minotaurs - if (mo->flags&MF_CORPSE) continue; - if (mo->StartTime >= 0 && (level.maptime - StartTime) >= MAULATORTICS) continue; - if (mo->tracer != NULL && mo->tracer->player == tracer->player) break; - } - - if (mo == NULL) - { - AInventory *power = tracer->FindInventory(PClass::FindActor("PowerMinotaur")); - if (power != NULL) - { - power->Destroy (); - } - } - } -} - -// Action functions for the minotaur ---------------------------------------- - -//---------------------------------------------------------------------------- -// -// PROC A_MinotaurAtk1 -// -// Melee attack. -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (Wads.CheckNumForName ("MNTRF1", ns_sprites) < 0 && - Wads.CheckNumForName ("MNTRF0", ns_sprites) < 0) - self->SetState(self->FindState ("FadeOut")); - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk1) -{ - PARAM_SELF_PROLOGUE(AActor); - - player_t *player; - - if (!self->target) - { - return 0; - } - S_Sound (self, CHAN_WEAPON, "minotaur/melee", 1, ATTN_NORM); - if (self->CheckMeleeRange()) - { - int damage = pr_minotauratk1.HitDice (4); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - if ((player = self->target->player) != NULL && - player->mo == self->target) - { // Squish the player - player->deltaviewheight = -16; - } - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MinotaurDecide -// -// Choose a missile attack. -// -//---------------------------------------------------------------------------- - -#define MNTR_CHARGE_SPEED (13.) - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDecide) -{ - PARAM_SELF_PROLOGUE(AActor); - - bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER); - AActor *target; - double dist; - - target = self->target; - if (!target) - { - return 0; - } - if (!friendly) - { - S_Sound (self, CHAN_WEAPON, "minotaur/sight", 1, ATTN_NORM); - } - dist = self->Distance2D(target); - if (target->Top() > self->Z() - && target->Top() < self->Top() - && dist < (friendly ? 16*64. : 8*64.) - && dist > 1*64. - && pr_minotaurdecide() < 150) - { // Charge attack - // Don't call the state function right away - self->SetState (self->FindState ("Charge"), true); - self->flags |= MF_SKULLFLY; - if (!friendly) - { // Heretic's Minotaur is invulnerable during charge attack - self->flags2 |= MF2_INVULNERABLE; - } - A_FaceTarget (self); - self->VelFromAngle(MNTR_CHARGE_SPEED); - self->special1 = TICRATE/2; // Charge duration - } - else if (target->Z() == target->floorz - && dist < 9*64. - && pr_minotaurdecide() < (friendly ? 100 : 220)) - { // Floor fire attack - self->SetState (self->FindState ("Hammer")); - self->special2 = 0; - } - else - { // Swing attack - A_FaceTarget (self); - // Don't need to call P_SetMobjState because the current state - // falls through to the swing attack - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MinotaurCharge -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurCharge) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *puff; - - if (self->target == NULL) - { - return 0; - } - if (self->special1 > 0) - { - PClassActor *type; - - if (gameinfo.gametype == GAME_Heretic) - { - type = PClass::FindActor("PhoenixPuff"); - } - else - { - type = PClass::FindActor("PunchPuff"); - } - puff = Spawn (type, self->Pos(), ALLOW_REPLACE); - puff->Vel.Z = 2; - self->special1--; - } - else - { - self->flags &= ~MF_SKULLFLY; - self->flags2 &= ~MF2_INVULNERABLE; - self->SetState (self->SeeState); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MinotaurAtk2 -// -// Swing attack. -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk2) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - DAngle angle; - double vz; - double z; - bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER); - - if (self->target == NULL) - { - return 0; - } - S_Sound (self, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM); - if (self->CheckMeleeRange()) - { - int damage; - damage = pr_atk.HitDice (friendly ? 3 : 5); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - return 0; - } - z = self->Z() + 40; - PClassActor *fx = PClass::FindActor("MinotaurFX1"); - if (fx) - { - mo = P_SpawnMissileZ (self, z, self->target, fx); - if (mo != NULL) - { -// S_Sound (mo, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM); - vz = mo->Vel.Z; - angle = mo->Angles.Yaw; - P_SpawnMissileAngleZ (self, z, fx, angle-(45./8), vz); - P_SpawnMissileAngleZ (self, z, fx, angle+(45./8), vz); - P_SpawnMissileAngleZ (self, z, fx, angle-(45./16), vz); - P_SpawnMissileAngleZ (self, z, fx, angle+(45./16), vz); - } - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MinotaurAtk3 -// -// Floor fire attack. -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk3) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - player_t *player; - bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER); - - if (!self->target) - { - return 0; - } - S_Sound (self, CHAN_VOICE, "minotaur/attack3", 1, ATTN_NORM); - if (self->CheckMeleeRange()) - { - int damage; - - damage = pr_minotauratk3.HitDice (friendly ? 3 : 5); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - if ((player = self->target->player) != NULL && - player->mo == self->target) - { // Squish the player - player->deltaviewheight = -16; - } - } - else - { - if (self->Floorclip > 0 && (i_compatflags & COMPATF_MINOTAUR)) - { - // only play the sound. - S_Sound (self, CHAN_WEAPON, "minotaur/fx2hit", 1, ATTN_NORM); - } - else - { - mo = P_SpawnMissile (self, self->target, PClass::FindActor("MinotaurFX2")); - if (mo != NULL) - { - S_Sound (mo, CHAN_WEAPON, "minotaur/attack1", 1, ATTN_NORM); - } - } - } - if (pr_minotauratk3() < 192 && self->special2 == 0) - { - self->SetState (self->FindState ("HammerLoop")); - self->special2 = 1; - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_MntrFloorFire -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MntrFloorFire) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - - self->SetZ(self->floorz); - double x = pr_fire.Random2() / 64.; - double y = pr_fire.Random2() / 64.; - - mo = Spawn("MinotaurFX3", self->Vec2OffsetZ(x, y, self->floorz), ALLOW_REPLACE); - mo->target = self->target; - mo->Vel.X = MinVel; // Force block checking - P_CheckMissileSpawn (mo, self->radius); - return 0; -} - -//--------------------------------------------------------------------------- -// -// FUNC P_MinotaurSlam -// -//--------------------------------------------------------------------------- - -void P_MinotaurSlam (AActor *source, AActor *target) -{ - DAngle angle; - double thrust; - int damage; - - angle = source->AngleTo(target); - thrust = 16 + pr_minotaurslam() / 64.; - target->VelFromAngle(thrust, angle); - damage = pr_minotaurslam.HitDice (static_cast(source) ? 4 : 6); - int newdam = P_DamageMobj (target, NULL, NULL, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, target, angle, 0.); - if (target->player) - { - target->reactiontime = 14+(pr_minotaurslam()&7); - } -} - -//---------------------------------------------------------------------------- -// -// Minotaur variables -// -// special1 charge duration countdown -// special2 internal to minotaur AI -// StartTime minotaur start time -// tracer pointer to player that spawned it -//---------------------------------------------------------------------------- - -//---------------------------------------------------------------------------- -// -// A_MinotaurRoam -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurRoam) -{ - PARAM_SELF_PROLOGUE(AActor); - - // In case pain caused him to skip his fade in. - self->RenderStyle = STYLE_Normal; - - if (self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend))) - { - AMinotaurFriend *self1 = static_cast (self); - - if (self1->StartTime >= 0 && (level.maptime - self1->StartTime) >= MAULATORTICS) - { - P_DamageMobj (self1, NULL, NULL, TELEFRAG_DAMAGE, NAME_None); - return 0; - } - } - - if (pr_minotaurroam() < 30) - CALL_ACTION(A_MinotaurLook, self); // adjust to closest target - - if (pr_minotaurroam() < 6) - { - //Choose new direction - self->movedir = pr_minotaurroam() % 8; - FaceMovementDirection (self); - } - if (!P_Move(self)) - { - // Turn - if (pr_minotaurroam() & 1) - self->movedir = (self->movedir + 1) % 8; - else - self->movedir = (self->movedir + 7) % 8; - FaceMovementDirection (self); - } - return 0; -} - - -//---------------------------------------------------------------------------- -// -// PROC A_MinotaurLook -// -// Look for enemy of player -//---------------------------------------------------------------------------- -#define MINOTAUR_LOOK_DIST (16*54.) - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurLook) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (!self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend))) - { - CALL_ACTION(A_Look, self); - return 0; - } - - AActor *mo = NULL; - player_t *player; - double dist; - int i; - AActor *master = self->tracer; - - self->target = NULL; - if (deathmatch) // Quick search for players - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) continue; - player = &players[i]; - mo = player->mo; - if (mo == master) continue; - if (mo->health <= 0) continue; - dist = self->Distance2D(mo); - if (dist > MINOTAUR_LOOK_DIST) continue; - self->target = mo; - break; - } - } - - if (!self->target) // Near player monster search - { - if (master && (master->health>0) && (master->player)) - mo = P_RoughMonsterSearch(master, 20); - else - mo = P_RoughMonsterSearch(self, 20); - self->target = mo; - } - - if (!self->target) // Normal monster search - { - FActorIterator iterator (0); - - while ((mo = iterator.Next()) != NULL) - { - if (!(mo->flags3 & MF3_ISMONSTER)) continue; - if (mo->health <= 0) continue; - if (!(mo->flags & MF_SHOOTABLE)) continue; - dist = self->Distance2D(mo); - if (dist > MINOTAUR_LOOK_DIST) continue; - if ((mo == master) || (mo == self)) continue; - if ((mo->flags5 & MF5_SUMMONEDMONSTER) && (mo->tracer == master)) continue; - self->target = mo; - break; // Found actor to attack - } - } - - if (self->target) - { - self->SetState (self->SeeState, true); - } - else - { - self->SetState (self->FindState ("Roam"), true); - } - return 0; -} - -DEFINE_ACTION_FUNCTION(AActor, A_MinotaurChase) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (!self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend))) - { - A_Chase (stack, self); - return 0; - } - - AMinotaurFriend *self1 = static_cast (self); - - // In case pain caused him to skip his fade in. - self1->RenderStyle = STYLE_Normal; - - if (self1->StartTime >= 0 && (level.maptime - self1->StartTime) >= MAULATORTICS) - { - P_DamageMobj (self1, NULL, NULL, TELEFRAG_DAMAGE, NAME_None); - return 0; - } - - if (pr_minotaurchase() < 30) - CALL_ACTION(A_MinotaurLook, self1); // adjust to closest target - - if (!self1->target || (self1->target->health <= 0) || - !(self1->target->flags&MF_SHOOTABLE)) - { // look for a new target - self1->SetIdle(); - return 0; - } - - FaceMovementDirection (self1); - self1->reactiontime = 0; - - // Melee attack - if (self1->MeleeState && self1->CheckMeleeRange ()) - { - if (self1->AttackSound) - { - S_Sound (self1, CHAN_WEAPON, self1->AttackSound, 1, ATTN_NORM); - } - self1->SetState (self1->MeleeState); - return 0; - } - - // Missile attack - if (self1->MissileState && P_CheckMissileRange(self1)) - { - self1->SetState (self1->MissileState); - return 0; - } - - // chase towards target - if (!P_Move (self1)) - { - P_NewChaseDir (self1); - FaceMovementDirection (self1); - } - - // Active sound - if (pr_minotaurchase() < 6) - { - self1->PlayActiveSound (); - } - return 0; -} - diff --git a/src/g_raven/ravenshared.h b/src/g_raven/ravenshared.h deleted file mode 100644 index 3c6019ae7..000000000 --- a/src/g_raven/ravenshared.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __RAVENSHARED_H__ -#define __RAVENSHARED_H__ - -class AActor; - -class AMinotaur : public AActor -{ - DECLARE_CLASS (AMinotaur, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); - -public: - bool Slam (AActor *); - void Tick (); -}; - -class AMinotaurFriend : public AMinotaur -{ - DECLARE_CLASS (AMinotaurFriend, AMinotaur) -public: - int StartTime; - - void Die (AActor *source, AActor *inflictor, int dmgflags); - void BeginPlay (); - - void Serialize(FSerializer &arc); -}; - -#endif //__RAVENSHARED_H__ diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 5ab1e3eb9..2f986c201 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -704,7 +704,7 @@ void AMorphedMonster::Die (AActor *source, AActor *inflictor, int dmgflags) if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED)) { UnmorphedMe->health = health; - UnmorphedMe->Die (source, inflictor, dmgflags); + UnmorphedMe->CallDie (source, inflictor, dmgflags); } } diff --git a/src/g_strife/a_alienspectres.cpp b/src/g_strife/a_alienspectres.cpp index a1c20355f..194bfd9e0 100644 --- a/src/g_strife/a_alienspectres.cpp +++ b/src/g_strife/a_alienspectres.cpp @@ -136,7 +136,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath) if (oracle->health > 0) { oracle->health = 0; - oracle->Die (self, self); + oracle->CallDie (self, self); } } player->GiveInventoryType (QuestItemClasses[22]); diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 7cccdd3f3..6815b1d49 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -119,7 +119,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_GetHurt) } if (self->health <= 0) { - self->Die (self->target, self->target); + self->CallDie (self->target, self->target); } return 0; } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index aece79edc..8c28c46b3 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -58,6 +58,7 @@ #include "d_net.h" #include "d_netinf.h" #include "a_morph.h" +#include "virtual.h" static FRandom pr_obituary ("Obituary"); static FRandom pr_botrespawn ("BotRespawn"); @@ -342,7 +343,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l } } - realthis->Die(source, inflictor, dmgflags); + realthis->CallDie(source, inflictor, dmgflags); } return; } @@ -761,7 +762,26 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) } } +DEFINE_ACTION_FUNCTION(AActor, Die) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(source, AActor); + PARAM_OBJECT(inflictor, AActor); + PARAM_INT(dmgflags); + self->Die(source, inflictor, dmgflags); + return 0; +} +void AActor::CallDie(AActor *source, AActor *inflictor, int dmgflags) +{ + IFVIRTUAL(AActor, Die) + { + VMValue params[4] = { (DObject*)this, source, inflictor, dmgflags }; + VMFrameStack stack; + stack.Call(func, params, 4, nullptr, 0, nullptr); + } + else return Die(source, inflictor, dmgflags); +} //--------------------------------------------------------------------------- @@ -1443,7 +1463,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, source = source->tracer; } } - target->Die (source, inflictor, flags); + target->CallDie (source, inflictor, flags); return damage; } } @@ -1794,7 +1814,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, target->DamageType = player->poisontype; } } - target->Die(source, source); + target->CallDie(source, source); return; } } diff --git a/src/p_map.cpp b/src/p_map.cpp index f1fa55cf9..e6265be8c 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1245,7 +1245,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch // Check for skulls slamming into things if (tm.thing->flags & MF_SKULLFLY) { - bool res = tm.thing->Slam(tm.thing->BlockingMobj); + bool res = tm.thing->CallSlam(tm.thing->BlockingMobj); tm.thing->BlockingMobj = NULL; return res; } @@ -3278,7 +3278,7 @@ bool FSlide::BounceWall(AActor *mo) if (mo->flags & MF_MISSILE) P_ExplodeMissile(mo, line, NULL); else - mo->Die(NULL, NULL); + mo->CallDie(NULL, NULL); return true; } @@ -4611,6 +4611,18 @@ void P_TraceBleed(int damage, AActor *target, DAngle angle, DAngle pitch) P_TraceBleed(damage, target->PosPlusZ(target->Height/2), target, angle, pitch); } +DEFINE_ACTION_FUNCTION(AActor, TraceBleedAngle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(damage); + PARAM_FLOAT(angle); + PARAM_FLOAT(pitch); + + P_TraceBleed(damage, self, angle, pitch); + return 0; +} + + //========================================================================== // // diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 0a1d831d8..5bb3bea0f 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -1690,6 +1690,14 @@ AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable) return P_BlockmapSearch (mo, distance, RoughBlockCheck, (void *)onlyseekable); } +DEFINE_ACTION_FUNCTION(AActor, RoughMonsterSearch) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(distance); + PARAM_BOOL_DEF(onlyseekable); + ACTION_RETURN_OBJECT(P_RoughMonsterSearch(self, distance, onlyseekable)); +} + AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params) { int blockX; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d845846db..559efda82 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -49,7 +49,6 @@ #include "p_acs.h" #include "cmdlib.h" #include "decallib.h" -#include "ravenshared.h" #include "a_action.h" #include "a_keys.h" #include "p_conversation.h" @@ -1787,7 +1786,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane) if (flags & MF_MISSILE) P_ExplodeMissile(this, NULL, NULL); else - Die(NULL, NULL); + CallDie(NULL, NULL); return true; } if (!(BounceFlags & BOUNCE_CanBounceWater)) @@ -1814,7 +1813,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane) if (flags & MF_MISSILE) P_ExplodeMissile(this, NULL, NULL); else - Die(NULL, NULL); + CallDie(NULL, NULL); return true; } @@ -3313,6 +3312,31 @@ bool AActor::Slam (AActor *thing) return false; // stop moving } +DEFINE_ACTION_FUNCTION(AActor, Slam) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(thing, AActor); + ACTION_RETURN_BOOL(self->Slam(thing)); +} + +bool AActor::CallSlam(AActor *thing) +{ + IFVIRTUAL(AActor, Slam) + { + VMValue params[2] = { (DObject*)this, thing }; + VMReturn ret; + VMFrameStack stack; + int retval; + ret.IntAt(&retval); + stack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + + } + else return Slam(thing); +} + + + bool AActor::SpecialBlastHandling (AActor *source, double strength) { return true; @@ -5495,7 +5519,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) else mobj->health = -mthing->health; if (mthing->health == 0) - mobj->Die(NULL, NULL); + mobj->CallDie(NULL, NULL); else if (mthing->health != 1) mobj->StartHealth = mobj->health; @@ -6902,6 +6926,14 @@ void AActor::SetIdle(bool nofunction) SetState(idle, nofunction); } +DEFINE_ACTION_FUNCTION(AActor, SetIdle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL_DEF(nofunction); + self->SetIdle(nofunction); + return 0; +} + int AActor::SpawnHealth() const { int defhealth = StartHealth ? StartHealth : GetDefault()->health; @@ -7351,6 +7383,13 @@ DEFINE_ACTION_FUNCTION(AActor, PlayerNumber) ACTION_RETURN_INT(self->player ? int(self->player - players) : 0); } +DEFINE_ACTION_FUNCTION(AActor, SetFriendPlayer) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_POINTER(player, player_t); + self->SetFriendPlayer(player); + return 0; +} //---------------------------------------------------------------------------- // // DropItem handling diff --git a/src/p_user.cpp b/src/p_user.cpp index 03df6a0ca..f28bf9f42 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1446,7 +1446,7 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags) if (player != NULL && player->mo != this) { // Make the real player die, too - player->mo->Die (source, inflictor, dmgflags); + player->mo->CallDie (source, inflictor, dmgflags); } else { diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index ab0bb96e1..8a5eddd2d 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -185,6 +185,16 @@ FxLocalVariableDeclaration *FCompileContext::FindLocalVariable(FName name) } } +static PStruct *FindStructType(FName name) +{ + PStruct *ccls = PClass::FindClass(name); + if (ccls == nullptr) + { + ccls = dyn_cast(TypeTable.FindType(RUNTIME_CLASS(PStruct), 0, (intptr_t)name, nullptr)); + if (ccls == nullptr) ccls = dyn_cast(TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), 0, (intptr_t)name, nullptr)); + } + return ccls; +} //========================================================================== // // ExpEmit @@ -244,7 +254,7 @@ static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCall // //========================================================================== -static bool AreCompatiblePointerTypes(PType *dest, PType *source) +static bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompare = false) { if (dest->IsKindOf(RUNTIME_CLASS(PPointer)) && source->IsKindOf(RUNTIME_CLASS(PPointer))) { @@ -252,12 +262,13 @@ static bool AreCompatiblePointerTypes(PType *dest, PType *source) auto fromtype = static_cast(source); auto totype = static_cast(dest); if (fromtype == nullptr) return true; - if (totype->IsConst && !fromtype->IsConst) return false; + if (!forcompare && totype->IsConst && !fromtype->IsConst) return false; if (fromtype == totype) return true; if (fromtype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)) && totype->PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { auto fromcls = static_cast(fromtype->PointedType); auto tocls = static_cast(totype->PointedType); + if (forcompare && tocls->IsDescendantOf(fromcls)) return true; return (fromcls->IsDescendantOf(tocls)); } } @@ -2211,6 +2222,14 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) } // Both types are the same so this is ok. } + else if (Right->ValueType->IsA(RUNTIME_CLASS(PNativeStruct)) && Base->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(Base->ValueType)->PointedType == Right->ValueType) + { + // allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers. + // For all other types this is not needed. Structs are not assignable and classes can only exist as references. + bool writable; + Right->RequestAddress(ctx, &writable); + Right->ValueType = Base->ValueType; + } else { // pass it to FxTypeCast for complete handling. @@ -3267,7 +3286,7 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) else if (left->ValueType->GetRegType() == REGT_POINTER && right->ValueType->GetRegType() == REGT_POINTER) { if (left->ValueType != right->ValueType && right->ValueType != TypeNullPtr && left->ValueType != TypeNullPtr && - !AreCompatiblePointerTypes(left->ValueType, right->ValueType)) + !AreCompatiblePointerTypes(left->ValueType, right->ValueType, true)) { goto error; } @@ -5691,12 +5710,48 @@ FxMemberIdentifier::~FxMemberIdentifier() FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) { + PStruct *ccls = nullptr; CHECKRESOLVED(); + if (Object->ExprType == EFX_Identifier) + { + // If the left side is a class name for a static member function call it needs to be resolved manually + // because the resulting value type would cause problems in nearly every other place where identifiers are being used. + ccls = FindStructType(static_cast(Object)->Identifier); + if (ccls != nullptr) static_cast(Object)->noglobal = true; + } + SAFE_RESOLVE(Object, ctx); - // allow accessing the color chanels by mapping the type to a matching struct which defines them. - if (Object->ValueType == TypeColor) + // check for class or struct constants if the left side is a type name. + if (Object->ValueType == TypeError) + { + if (ccls != nullptr) + { + if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) + { + PSymbol *sym; + if ((sym = ccls->Symbols.FindSymbol(Identifier, true)) != nullptr) + { + if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) + { + ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s.%s' as constant\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + delete this; + return FxConstant::MakeConstant(sym, ScriptPosition); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Unable to access '%s.%s' in a static context\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + delete this; + return nullptr; + } + } + } + } + } + + // allow accessing the color channels by mapping the type to a matching struct which defines them. + if (Object->ValueType == TypeColor) Object->ValueType = TypeColorStruct; if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) @@ -6635,12 +6690,12 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) if (!start.Konst) { - ExpEmit indexwork = indexv.Fixed ? ExpEmit(build, indexv.RegType) : indexv; int shiftbits = 0; while (1u << shiftbits < arraytype->ElementSize) { shiftbits++; } + ExpEmit indexwork = indexv.Fixed && arraytype->ElementSize > 1 ? ExpEmit(build, indexv.RegType) : indexv; if (1u << shiftbits == arraytype->ElementSize) { if (shiftbits > 0) @@ -7079,7 +7134,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) bool staticonly = false; bool novirtual = false; - PClass *ccls = nullptr; + PStruct *ccls = nullptr; for (auto a : ArgList) { @@ -7093,10 +7148,10 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (Self->ExprType == EFX_Identifier) { - ccls = PClass::FindClass(static_cast(Self)->Identifier); // If the left side is a class name for a static member function call it needs to be resolved manually // because the resulting value type would cause problems in nearly every other place where identifiers are being used. - if (ccls != nullptr)static_cast(Self)->noglobal = true; + ccls = FindStructType(static_cast(Self)->Identifier); + if (ccls != nullptr) static_cast(Self)->noglobal = true; } SAFE_RESOLVE(Self, ctx); @@ -7105,17 +7160,13 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ccls != nullptr) { - if (ccls->bExported) + if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) { cls = ccls; staticonly = true; goto isresolved; } } - else - { - // Todo: static struct members need to work as well. - } } if (Self->ExprType == EFX_Super) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 1b93e22a5..a6c9ff9f1 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -43,6 +43,7 @@ #include "d_player.h" #include "p_effect.h" #include "autosegs.h" +#include "gi.h" static TArray properties; static TArray AFTable; @@ -721,7 +722,11 @@ void InitThingdef() pstruct->Size = sizeof(player_t); pstruct->Align = alignof(player_t); PArray *parray = NewArray(pstruct, MAXPLAYERS); - PField *playerf = new PField("players", pstruct, VARF_Native | VARF_Static, (intptr_t)&players); + PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players); + GlobalSymbols.AddSymbol(playerf); + + parray = NewArray(TypeBool, MAXPLAYERS); + playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame); GlobalSymbols.AddSymbol(playerf); @@ -803,3 +808,9 @@ void InitThingdef() } } + +DEFINE_ACTION_FUNCTION(DObject, GameType) +{ + PARAM_PROLOGUE; + ACTION_RETURN_INT(gameinfo.gametype); +} diff --git a/src/w_wad.cpp b/src/w_wad.cpp index da369144a..41833fc24 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -501,6 +501,15 @@ int FWadCollection::CheckNumForName (const char *name, int space, int wadnum, bo return i != NULL_INDEX ? i : -1; } +DEFINE_ACTION_FUNCTION(_Wads, CheckNumForName) +{ + PARAM_PROLOGUE; + PARAM_STRING(name); + PARAM_INT(ns); + PARAM_INT_DEF(wadnum); + PARAM_BOOL_DEF(exact); + ACTION_RETURN_INT(Wads.CheckNumForName(name, ns, wadnum, exact)); +} //========================================================================== // // W_GetNumForName diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 45b44f767..a006c79ed 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -262,6 +262,8 @@ class Actor : Thinker native virtual native void Activate(Actor activator); virtual native void Deactivate(Actor activator); virtual native int DoSpecialDamage (Actor target, int damage, Name damagetype); + virtual native void Die(Actor source, Actor inflictor, int dmgflags); + virtual native bool Slam(Actor victim); virtual native bool UseInventory(Inventory item); @@ -276,6 +278,7 @@ class Actor : Thinker native native bool HitFloor(); native bool isTeammate(Actor other); native int PlayerNumber(); + native void SetFriendPlayer(PlayerInfo player); native void RestoreDamage(); native int SpawnHealth(); @@ -296,6 +299,7 @@ class Actor : Thinker native native Actor SpawnMissileAngleZSpeed (double z, class type, double angle, double vz, double speed, Actor owner = null, bool checkspawn = true); native Actor, Actor SpawnPlayerMissile(class type, double angle = 0, double x = 0, double y = 0, double z = 0, out FTranslatedLineTarget pLineTarget = null, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0); native void SpawnTeleportFog(Vector3 pos, bool beforeTele, bool setTarget); + native Actor RoughMonsterSearch(int distance, bool onlyseekable = false); void A_Light(int extralight) { if (player) player.extralight = clamp(extralight, -20, 20); } void A_Light0() { if (player) player.extralight = 0; } @@ -308,6 +312,9 @@ class Actor : Thinker native native bool Teleport(Vector3 pos, double angle, int flags); native void TraceBleed(int damage, Actor missile); + native void TraceBleedAngle(int damage, double angle, double pitch); + + native void SetIdle(bool nofunction = false); native bool CheckMeleeRange(); native int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0); native double AimLineAttack(double angle, double distance, out FTranslatedLineTarget pLineTarget = null, double vrange = 0., int flags = 0, Actor target = null, Actor friender = null); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 6d0e77795..c91b7aaa4 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -7,6 +7,7 @@ class Object native native static double G_SkillPropertyFloat(int p); native static vector3, int G_PickDeathmatchStart(); native static vector3, int G_PickPlayerStart(int pnum, int flags = 0); + native static int GameType(); /*virtual*/ native void Destroy(); } @@ -133,3 +134,33 @@ struct Sector native native double, Sector, F3DFloor NextHighestCeilingAt(double x, double y, double bottomz, double topz, int flags = 0); } +struct Wads +{ + enum WadNamespace + { + ns_hidden = -1, + + ns_global = 0, + ns_sprites, + ns_flats, + ns_colormaps, + ns_acslibrary, + ns_newtextures, + ns_bloodraw, + ns_bloodsfx, + ns_bloodmisc, + ns_strifevoices, + ns_hires, + ns_voxels, + + ns_specialzipdirectory, + ns_sounds, + ns_patches, + ns_graphics, + ns_music, + + ns_firstskin, + } + + native static int CheckNumForName(string name, int ns, int wadnum = -1, bool exact = false); +} \ No newline at end of file diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index d87ad338a..eadf30bc7 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -993,3 +993,17 @@ enum ETeleport TELF_ROTATEBOOM = 32, TELF_ROTATEBOOMINVERSE = 64, }; + +enum EGameType +{ + GAME_Any = 0, + GAME_Doom = 1, + GAME_Heretic = 2, + GAME_Hexen = 4, + GAME_Strife = 8, + GAME_Chex = 16, //Chex is basically Doom, but we need to have a different set of actors. + + GAME_Raven = GAME_Heretic|GAME_Hexen, + GAME_DoomChex = GAME_Doom|GAME_Chex, + GAME_DoomStrifeChex = GAME_Doom|GAME_Strife|GAME_Chex +} diff --git a/wadsrc/static/zscript/hexen/summon.txt b/wadsrc/static/zscript/hexen/summon.txt index 06ceb7e17..c9e9b9295 100644 --- a/wadsrc/static/zscript/hexen/summon.txt +++ b/wadsrc/static/zscript/hexen/summon.txt @@ -1,7 +1,7 @@ // Dark Servant Artifact ---------------------------------------------------- -class ArtiDarkServant : Inventory native +class ArtiDarkServant : Inventory { Default { @@ -23,6 +23,26 @@ class ArtiDarkServant : Inventory native SUMN A 350; Loop; } + + //============================================================================ + // + // Activate the summoning artifact + // + //============================================================================ + + override bool Use (bool pickup) + { + Actor mo = Owner.SpawnPlayerMissile ("SummoningDoll"); + if (mo) + { + mo.target = Owner; + mo.tracer = Owner; + mo.Vel.Z = 5; + } + return true; + } + + } // Summoning Doll ----------------------------------------------------------- @@ -36,8 +56,6 @@ class SummoningDoll : Actor +NOTELEPORT } - native void A_Summon(); - States { Spawn: @@ -48,6 +66,49 @@ class SummoningDoll : Actor SUMN A 4 A_Summon; Stop; } + + //============================================================================ + // + // A_Summon + // + //============================================================================ + + void A_Summon() + { + Actor mo = Spawn("MinotaurFriend", pos, ALLOW_REPLACE); + if (mo) + { + if (mo.TestMobjLocation() == false || !tracer) + { // Didn't fit - change back to artifact + mo.Destroy(); + Actor arti = Spawn("ArtiDarkServant", Pos, ALLOW_REPLACE); + if (arti) arti.bDropped = true; + return; + } + + // Careful! The Minotaur might have been replaced + // so only set the time if we got a genuine one. + MinotaurFriend m = MinotaurFriend(mo); + if (m) m.StartTime = level.maptime; + + if (tracer.bCorpse) + { // Master dead + mo.tracer = null; // No master + } + else + { + mo.tracer = tracer; // Pointer to master + Inventory power = Inventory(Spawn("PowerMinotaur")); + power.CallTryPickup(tracer); + mo.SetFriendPlayer(tracer.player); + } + + // Make smoke puff + Spawn("MinotaurSmoke", Pos, ALLOW_REPLACE); + A_PlaySound(mo.ActiveSound, CHAN_VOICE); + } + } + } // Minotaur Smoke ----------------------------------------------------------- @@ -69,3 +130,4 @@ class MinotaurSmoke : Actor Stop; } } + diff --git a/wadsrc/static/zscript/raven/minotaur.txt b/wadsrc/static/zscript/raven/minotaur.txt index 9bbbe81a0..fa40cbec1 100644 --- a/wadsrc/static/zscript/raven/minotaur.txt +++ b/wadsrc/static/zscript/raven/minotaur.txt @@ -1,5 +1,9 @@ -class Minotaur : Actor native +class Minotaur : Actor { + const MAULATORTICS = 25 * TICRATE; + const MNTR_CHARGE_SPEED =13.; + const MINOTAUR_LOOK_DIST = 16*54.; + Default { Health 3000; @@ -26,16 +30,6 @@ class Minotaur : Actor native DropItem "PhoenixRodAmmo", 84, 10; } - native void A_MinotaurDecide(); - native void A_MinotaurAtk1(); - native void A_MinotaurAtk2(); - native void A_MinotaurAtk3(); - native void A_MinotaurCharge(); - native void A_MinotaurLook(); - native void A_MinotaurRoam(); - native void A_MinotaurChase(); - native void A_MinotaurDeath(); - States { Spawn: @@ -101,10 +95,478 @@ class Minotaur : Actor native MNTR E 10 A_BossDeath; Stop; } + + //--------------------------------------------------------------------------- + // + // FUNC P_MinotaurSlam + // + //--------------------------------------------------------------------------- + + void MinotaurSlam (Actor target) + { + double ang = AngleTo(target); + double thrust = 16 + random[MinotaurSlam]() / 64.; + target.VelFromAngle(ang, thrust); + int damage = random[MinotaurSlam](1, 8) * (bSummonedMonster? 4 : 6); + int newdam = target.DamageMobj (null, null, damage, 'Melee'); + target.TraceBleedAngle (newdam > 0 ? newdam : damage, ang, 0.); + if (target.player) + { + target.reactiontime = random[MinotaurSlam](14, 21); + } + } + + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + override void Tick () + { + Super.Tick (); + + // The unfriendly Minotaur (Heretic's) is invulnerable while charging + if (!bSummonedMonster) + { + bInvulnerable = bSkullFly; + } + } + + override bool Slam (Actor thing) + { + // Slamming minotaurs shouldn't move non-creatures + if (!thing.bIsMonster && !thing.player) + { + return false; + } + return Super.Slam (thing); + } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + damage = Super.DoSpecialDamage (target, damage, damagetype); + if (damage != -1 && bSkullFly) + { // Slam only when in charge mode + MinotaurSlam (target); + return -1; + } + return damage; + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurAtk1 + // + // Melee attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurAtk1() + { + if (!target) + { + return; + } + A_PlaySound ("minotaur/melee", CHAN_WEAPON); + if (CheckMeleeRange()) + { + int damage = random[MinotaurAtk1](1, 8) * 4; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + PlayerInfo player = target.player; + if (player != null && player.mo == target) + { // Squish the player + player.deltaviewheight = -16; + } + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurDecide + // + // Choose a missile attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurDecide() + { + bool friendly = bSummonedMonster; + + if (!target) + { + return; + } + if (!friendly) + { + A_PlaySound ("minotaur/sight", CHAN_WEAPON); + } + double dist = Distance2D(target); + if (target.pos.z + target.height > pos.z + && target.pos.z + target.height < pos.z + height + && dist < (friendly ? 16*64. : 8*64.) + && dist > 1*64. + && random[MinotaurDecide]() < 150) + { // Charge attack + // Don't call the state function right away + SetStateLabel("Charge", true); + bSkullFly = true; + if (!friendly) + { // Heretic's Minotaur is invulnerable during charge attack + bInvulnerable = true; + } + A_FaceTarget (); + VelFromAngle(MNTR_CHARGE_SPEED); + special1 = TICRATE/2; // Charge duration + } + else if (target.pos.z == target.floorz + && dist < 9*64. + && random[MinotaurDecide]() < (friendly ? 100 : 220)) + { // Floor fire attack + SetStateLabel("Hammer"); + special2 = 0; + } + else + { // Swing attack + A_FaceTarget (); + // Don't need to call P_SetMobjState because the current state + // falls through to the swing attack + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurCharge + // + //---------------------------------------------------------------------------- + + void A_MinotaurCharge() + { + if (target == null) + { + return; + } + if (special1 > 0) + { + Class type; + + //if (gameinfo.gametype == GAME_Heretic) + if (gametype() == GAME_Heretic) + { + type = "PhoenixPuff"; + } + else + { + type = "PunchPuff"; + } + Actor puff = Spawn (type, Pos, ALLOW_REPLACE); + puff.Vel.Z = 2; + special1--; + } + else + { + bSkullFly = false; + bInvulnerable = false; + SetState (SeeState); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurAtk2 + // + // Swing attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurAtk2() + { + bool friendly = bSummonedMonster; + + if (target == null) + { + return; + } + A_PlaySound ("minotaur/attack2", CHAN_WEAPON); + if (CheckMeleeRange()) + { + int damage = random[MinotaurAtk2](1, 8) * (friendly ? 3 : 5); + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + double z = pos.z + 40; + Class fx = "MinotaurFX1"; + Actor mo = SpawnMissileZ (z, target, fx); + if (mo != null) + { +// S_Sound (mo, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM); + double vz = mo.Vel.Z; + double ang = mo.angle; + SpawnMissileAngleZ (z, fx, ang-(45./8), vz); + SpawnMissileAngleZ (z, fx, ang+(45./8), vz); + SpawnMissileAngleZ (z, fx, ang-(45./16), vz); + SpawnMissileAngleZ (z, fx, ang+(45./16), vz); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurAtk3 + // + // Floor fire attack. + // + //---------------------------------------------------------------------------- + + void A_MinotaurAtk3() + { + bool friendly = bSummonedMonster; + + if (!target) + { + return; + } + A_PlaySound ("minotaur/attack3", CHAN_VOICE); + if (CheckMeleeRange()) + { + int damage = random[MinotaurAtk3](1, 8) * (friendly ? 3 : 5); + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + PlayerInfo player = target.player; + if (player != null && player.mo == target) + { // Squish the player + player.deltaviewheight = -16; + } + } + else + { + if (Floorclip > 0 && compat_minotaur) + { + // only play the sound. + A_PlaySound ("minotaur/fx2hit", CHAN_WEAPON); + } + else + { + Actor mo = SpawnMissile (target, "MinotaurFX2"); + if (mo != null) + { + mo.A_PlaySound ("minotaur/attack1", CHAN_WEAPON); + } + } + } + if (random[MinotaurAtk3]() < 192 && special2 == 0) + { + SetStateLabel ("HammerLoop"); + special2 = 1; + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurDeath + // + //---------------------------------------------------------------------------- + + void A_MinotaurDeath() + { + if (Wads.CheckNumForName ("MNTRF1", Wads.ns_sprites) < 0 && + Wads.CheckNumForName ("MNTRF0", Wads.ns_sprites) < 0) + SetStateLabel("FadeOut"); + } + + + //---------------------------------------------------------------------------- + // + // A_MinotaurRoam + // + //---------------------------------------------------------------------------- + + void A_MinotaurRoam() + { + // In case pain caused him to skip his fade in. + A_SetRenderStyle(1, STYLE_Normal); + + MinotaurFriend mf = MinotaurFriend(self); + if (mf) + { + if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS) + { + DamageMobj (null, null, TELEFRAG_DAMAGE, 'None'); + return; + } + } + + if (random[MinotaurRoam]() < 30) + A_MinotaurLook(); // adjust to closest target + + if (random[MinotaurRoam]() < 6) + { + //Choose new direction + movedir = random[MinotaurRoam]() % 8; + FaceMovementDirection (); + } + if (!MonsterMove()) + { + // Turn + if (random[MinotaurRoam]() & 1) + movedir = (movedir + 1) % 8; + else + movedir = (movedir + 7) % 8; + FaceMovementDirection (); + } + } + + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurLook + // + // Look for enemy of player + //---------------------------------------------------------------------------- + + void A_MinotaurLook() + { + if (!(self is "MinotaurFriend")) + { + A_Look(); + return; + } + + Actor mo = null; + PlayerInfo player; + double dist; + Actor master = tracer; + + target = null; + if (deathmatch) // Quick search for players + { + for (int i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + player = players[i]; + mo = player.mo; + if (mo == master) continue; + if (mo.health <= 0) continue; + dist = Distance2D(mo); + if (dist > MINOTAUR_LOOK_DIST) continue; + target = mo; + break; + } + } + + if (!target) // Near player monster search + { + if (master && (master.health > 0) && (master.player)) + mo = master.RoughMonsterSearch(20); + else + mo = RoughMonsterSearch(20); + target = mo; + } + + if (!target) // Normal monster search + { + ThinkerIterator it = ThinkerIterator.Create("Actor"); + + while ((mo = Actor(it.Next())) != null) + { + if (!mo.bIsMonster) continue; + if (mo.health <= 0) continue; + if (!mo.bShootable) continue; + dist = Distance2D(mo); + if (dist > MINOTAUR_LOOK_DIST) continue; + if (mo == master || mo == self) continue; + if (mo.bSummonedMonster && mo.tracer == master) continue; + target = mo; + break; // Found actor to attack + } + } + + if (target) + { + SetState (SeeState, true); + } + else + { + SetStateLabel ("Roam", true); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_MinotaurChase + // + //---------------------------------------------------------------------------- + + void A_MinotaurChase() + { + MinotaurFriend mf = MinotaurFriend(self); + if (!mf) + { + A_Chase(); + return; + } + + + // In case pain caused him to skip his fade in. + A_SetRenderStyle(1, STYLE_Normal); + + if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS) + { + DamageMobj (null, null, TELEFRAG_DAMAGE, 'None'); + return; + } + + if (random[MinotaurChase]() < 30) + A_MinotaurLook(); // adjust to closest target + + if (!target || (target.health <= 0) || !target.bShootable) + { // look for a new target + SetIdle(); + return; + } + + FaceMovementDirection (); + reactiontime = 0; + + // Melee attack + if (MeleeState && CheckMeleeRange ()) + { + if (AttackSound) + { + A_PlaySound (AttackSound, CHAN_WEAPON); + } + SetState (MeleeState); + return; + } + + // Missile attack + if (MissileState && CheckMissileRange()) + { + SetState (MissileState); + return; + } + + // chase towards target + if (!MonsterMove ()) + { + NewChaseDir (); + FaceMovementDirection (); + } + + // Active sound + if (random[MinotaurChase]() < 6) + { + PlayActiveSound (); + } + } } -class MinotaurFriend : Minotaur native +class MinotaurFriend : Minotaur { + int StartTime; + Default { Health 2500; @@ -132,6 +594,51 @@ class MinotaurFriend : Minotaur native Death: Goto FadeOut; } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + override void BeginPlay () + { + Super.BeginPlay (); + StartTime = -1; + } + + override void Die (Actor source, Actor inflictor, int dmgflags) + { + Super.Die (source, inflictor, dmgflags); + + if (tracer && tracer.health > 0 && tracer.player) + { + // Search thinker list for minotaur + ThinkerIterator it = ThinkerIterator.Create("MinotaurFriend"); + MinotaurFriend mo; + + while ((mo = MinotaurFriend(it.Next())) != null) + { + if (mo.health <= 0) continue; + // [RH] Minotaurs can't be morphed, so this isn't needed + //if (!(mo.flags&MF_COUNTKILL)) continue; // for morphed minotaurs + if (mo.bCorpse) continue; + if (mo.StartTime >= 0 && (level.maptime - StartTime) >= MAULATORTICS) continue; + if (mo.tracer != null && mo.tracer.player == tracer.player) break; + } + + if (mo == null) + { + Inventory power = tracer.FindInventory("PowerMinotaur"); + if (power != null) + { + power.Destroy (); + } + } + } + } + + } // Minotaur FX 1 ------------------------------------------------------------ @@ -179,8 +686,6 @@ class MinotaurFX2 : MinotaurFX1 DeathSound "minotaur/fx2hit"; } - native void A_MntrFloorFire(); - states { Spawn: @@ -191,6 +696,24 @@ class MinotaurFX2 : MinotaurFX1 FX13 JKLM 4 Bright; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_MntrFloorFire + // + //---------------------------------------------------------------------------- + + void A_MntrFloorFire() + { + SetZ(floorz); + double x = Random2[MntrFloorFire]() / 64.; + double y = Random2[MntrFloorFire]() / 64.; + + Actor mo = Spawn("MinotaurFX3", Vec2OffsetZ(x, y, floorz), ALLOW_REPLACE); + mo.target = target; + mo.Vel.X = MinVel; // Force block checking + mo.CheckMissileSpawn (radius); + } } // Minotaur FX 3 ------------------------------------------------------------