- scriptified Hexen's Bloodscourge and Serpent.

- merged the FrontBlock searcher for the Bloodscourge into RoughMonsterSearch. This also fixes the bug that the searcher was not initialized properly for the MageBoss.
This commit is contained in:
Christoph Oelckers 2016-11-28 00:49:10 +01:00
parent f9a1388066
commit ebd2c27e0a
17 changed files with 507 additions and 620 deletions

View file

@ -860,8 +860,6 @@ set( NOT_COMPILED_SOURCE_FILES
sc_man_scanner.h
sc_man_scanner.re
g_hexen/a_heresiarch.cpp
g_hexen/a_magestaff.cpp
g_hexen/a_serpent.cpp
g_hexen/a_spike.cpp
g_hexen/a_teleportother.cpp
g_strife/a_acolyte.cpp

View file

@ -25,7 +25,5 @@
// Include all the Hexen stuff here to reduce compile time
#include "a_heresiarch.cpp"
#include "a_magestaff.cpp"
#include "a_serpent.cpp"
#include "a_spike.cpp"
#include "a_teleportother.cpp"

View file

@ -1,260 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "m_random.h"
#include "s_sound.h"
#include "a_hexenglobal.h"
#include "gstrings.h"
#include "a_weaponpiece.h"
#include "vm.h"
#include "doomstat.h"
*/
class AMageWeapon : public AWeapon
{
DECLARE_CLASS (AMageWeapon, AWeapon);
public:
};
IMPLEMENT_CLASS(AMageWeapon, false, false)
static FRandom pr_mstafftrack ("MStaffTrack");
static FRandom pr_bloodscourgedrop ("BloodScourgeDrop");
void A_MStaffTrack (AActor *);
void A_DropBloodscourgePieces (AActor *);
void A_MStaffAttack (AActor *actor);
void A_MStaffPalette (AActor *actor);
static AActor *FrontBlockCheck (AActor *mo, int index, void *);
static divline_t BlockCheckLine;
//==========================================================================
// The Mages's Staff (Bloodscourge) -----------------------------------------
class AMWeapBloodscourge : public AMageWeapon
{
DECLARE_CLASS (AMWeapBloodscourge, AMageWeapon)
public:
void Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("mstaffcount", MStaffCount);
}
PalEntry GetBlend ()
{
if (paletteflash & PF_HEXENWEAPONS)
{
if (MStaffCount == 3)
return PalEntry(128, 100, 73, 0);
else if (MStaffCount == 2)
return PalEntry(128, 125, 92, 0);
else if (MStaffCount == 1)
return PalEntry(128, 150, 110, 0);
else
return PalEntry(0, 0, 0, 0);
}
else
{
return PalEntry (MStaffCount * 128 / 3, 151, 110, 0);
}
}
BYTE MStaffCount;
};
IMPLEMENT_CLASS(AMWeapBloodscourge, false, false)
// Mage Staff FX2 (Bloodscourge) --------------------------------------------
class AMageStaffFX2 : public AActor
{
DECLARE_CLASS(AMageStaffFX2, AActor)
public:
bool SpecialBlastHandling (AActor *source, double strength);
};
IMPLEMENT_CLASS(AMageStaffFX2, false, false)
bool AMageStaffFX2::SpecialBlastHandling (AActor *source, double strength)
{
// Reflect to originator
tracer = target;
target = source;
return true;
}
//============================================================================
//============================================================================
//
// MStaffSpawn
//
//============================================================================
void MStaffSpawn (AActor *pmo, DAngle angle, AActor *alttarget)
{
AActor *mo;
FTranslatedLineTarget t;
mo = P_SpawnPlayerMissile (pmo, 0, 0, 8, RUNTIME_CLASS(AMageStaffFX2), angle, &t);
if (mo)
{
mo->target = pmo;
if (t.linetarget && !t.unlinked)
mo->tracer = t.linetarget;
else
mo->tracer = alttarget;
}
}
//============================================================================
//
// A_MStaffAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MStaffAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
AMWeapBloodscourge *weapon = static_cast<AMWeapBloodscourge *> (self->player->ReadyWeapon);
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return 0;
}
angle = self->Angles.Yaw;
// [RH] Let's try and actually track what the player aimed at
P_AimLineAttack (self, angle, PLAYERMISSILERANGE, &t, 32.);
if (t.linetarget == NULL)
{
BlockCheckLine.x = self->X();
BlockCheckLine.y = self->Y();
BlockCheckLine.dx = -angle.Sin();
BlockCheckLine.dy = -angle.Cos();
t.linetarget = P_BlockmapSearch (self, 10, FrontBlockCheck);
}
MStaffSpawn (self, angle, t.linetarget);
MStaffSpawn (self, angle-5, t.linetarget);
MStaffSpawn (self, angle+5, t.linetarget);
S_Sound (self, CHAN_WEAPON, "MageStaffFire", 1, ATTN_NORM);
weapon->MStaffCount = 3;
return 0;
}
//============================================================================
//
// A_MStaffPalette
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MStaffPalette)
{
PARAM_ACTION_PROLOGUE(AActor);
if (self->player != NULL)
{
AMWeapBloodscourge *weapon = static_cast<AMWeapBloodscourge *> (self->player->ReadyWeapon);
if (weapon != NULL && weapon->MStaffCount != 0)
{
weapon->MStaffCount--;
}
}
return 0;
}
//============================================================================
//
// A_MStaffTrack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MStaffTrack)
{
PARAM_SELF_PROLOGUE(AActor);
if ((self->tracer == 0) && (pr_mstafftrack()<50))
{
self->tracer = P_RoughMonsterSearch (self, 10, true);
}
P_SeekerMissile(self, 2, 10);
return 0;
}
//============================================================================
//
// FrontBlockCheck
//
// [RH] Like RoughBlockCheck, but it won't return anything behind a line.
//
//============================================================================
static AActor *FrontBlockCheck (AActor *mo, int index, void *)
{
FBlockNode *link;
for (link = blocklinks[index]; link != NULL; link = link->NextActor)
{
if (link->Me != mo)
{
if (P_PointOnDivlineSide(link->Me->X(), link->Me->Y(), &BlockCheckLine) == 0 &&
mo->IsOkayToAttack (link->Me))
{
return link->Me;
}
}
}
return NULL;
}
//============================================================================
//
// MStaffSpawn2 - for use by mage class boss
//
//============================================================================
void MStaffSpawn2 (AActor *actor, DAngle angle)
{
AActor *mo;
mo = P_SpawnMissileAngleZ (actor, actor->Z()+40, RUNTIME_CLASS(AMageStaffFX2), angle, 0.);
if (mo)
{
mo->target = actor;
mo->tracer = P_BlockmapSearch (mo, 10, FrontBlockCheck);
}
}
//============================================================================
//
// A_MStaffAttack2 - for use by mage class boss
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MageAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
{
return 0;
}
DAngle angle = self->Angles.Yaw;
MStaffSpawn2(self, angle);
MStaffSpawn2(self, angle - 5);
MStaffSpawn2(self, angle + 5);
S_Sound(self, CHAN_WEAPON, "MageStaffFire", 1, ATTN_NORM);
return 0;
}

View file

@ -1,311 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "p_enemy.h"
#include "a_action.h"
#include "m_random.h"
#include "p_terrain.h"
#include "vm.h"
*/
static FRandom pr_serpentchase ("SerpentChase");
static FRandom pr_serpenthump ("SerpentHump");
static FRandom pr_serpentattack ("SerpentAttack");
static FRandom pr_serpentmeattack ("SerpentMeAttack");
static FRandom pr_serpentgibs ("SerpentGibs");
static FRandom pr_delaygib ("DelayGib");
//============================================================================
//
// A_SerpentUnHide
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentUnHide)
{
PARAM_SELF_PROLOGUE(AActor);
self->renderflags &= ~RF_INVISIBLE;
self->Floorclip = 24;
return 0;
}
//============================================================================
//
// A_SerpentHide
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentHide)
{
PARAM_SELF_PROLOGUE(AActor);
self->renderflags |= RF_INVISIBLE;
self->Floorclip = 0;
return 0;
}
//============================================================================
//
// A_SerpentRaiseHump
//
// Raises the hump above the surface by raising the floorclip level
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentRaiseHump)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip -= 4;
return 0;
}
//============================================================================
//
// A_SerpentLowerHump
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentLowerHump)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip += 4;
return 0;
}
//============================================================================
//
// A_SerpentHumpDecide
//
// Decided whether to hump up, or if the mobj is a serpent leader,
// to missile attack
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentHumpDecide)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->MissileState != NULL)
{
if (pr_serpenthump() > 30)
{
return 0;
}
else if (pr_serpenthump() < 40)
{ // Missile attack
self->SetState (self->MeleeState);
return 0;
}
}
else if (pr_serpenthump() > 3)
{
return 0;
}
if (!self->CheckMeleeRange ())
{ // The hump shouldn't occur when within melee range
if (self->MissileState != NULL && pr_serpenthump() < 128)
{
self->SetState (self->MeleeState);
}
else
{
self->SetState (self->FindState ("Hump"));
S_Sound (self, CHAN_BODY, "SerpentActive", 1, ATTN_NORM);
}
}
return 0;
}
//============================================================================
//
// A_SerpentCheckForAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentCheckForAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
{
return 0;
}
if (self->MissileState != NULL)
{
if (!self->CheckMeleeRange ())
{
self->SetState (self->FindState ("Attack"));
return 0;
}
}
if (P_CheckMeleeRange2 (self))
{
self->SetState (self->FindState ("Walk"));
}
else if (self->CheckMeleeRange ())
{
if (pr_serpentattack() < 32)
{
self->SetState (self->FindState ("Walk"));
}
else
{
self->SetState (self->FindState ("Attack"));
}
}
return 0;
}
//============================================================================
//
// A_SerpentChooseAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentChooseAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target || self->CheckMeleeRange())
{
return 0;
}
if (self->MissileState != NULL)
{
self->SetState (self->MissileState);
}
return 0;
}
//============================================================================
//
// A_SerpentMeleeAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentMeleeAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
{
return 0;
}
if (self->CheckMeleeRange ())
{
int damage = pr_serpentmeattack.HitDice (5);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
S_Sound (self, CHAN_BODY, "SerpentMeleeHit", 1, ATTN_NORM);
}
if (pr_serpentmeattack() < 96)
{
CALL_ACTION(A_SerpentCheckForAttack, self);
}
return 0;
}
//============================================================================
//
// A_SerpentSpawnGibs
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentSpawnGibs)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
static const char *GibTypes[] =
{
"SerpentGib3",
"SerpentGib2",
"SerpentGib1"
};
for (int i = countof(GibTypes)-1; i >= 0; --i)
{
double x = (pr_serpentgibs() - 128) / 16.;
double y = (pr_serpentgibs() - 128) / 16.;
mo = Spawn (GibTypes[i], self->Vec2OffsetZ(x, y, self->floorz + 1), ALLOW_REPLACE);
if (mo)
{
mo->Vel.X = (pr_serpentgibs() - 128) / 1024.f;
mo->Vel.Y = (pr_serpentgibs() - 128) / 1024.f;
mo->Floorclip = 6;
}
}
return 0;
}
//============================================================================
//
// A_FloatGib
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_FloatGib)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip -= 1;
return 0;
}
//============================================================================
//
// A_SinkGib
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SinkGib)
{
PARAM_SELF_PROLOGUE(AActor);
self->Floorclip += 1;;
return 0;
}
//============================================================================
//
// A_DelayGib
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_DelayGib)
{
PARAM_SELF_PROLOGUE(AActor);
self->tics -= pr_delaygib()>>2;
return 0;
}
//============================================================================
//
// A_SerpentHeadCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SerpentHeadCheck)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->Z() <= self->floorz)
{
if (Terrains[P_GetThingFloorType(self)].IsLiquid)
{
P_HitFloor (self);
self->SetState (NULL);
}
else
{
self->SetState (self->FindState(NAME_Death));
}
}
return 0;
}

View file

@ -365,6 +365,12 @@ bool P_CheckMeleeRange2 (AActor *actor)
return true;
}
DEFINE_ACTION_FUNCTION(AActor, CheckMeleeRange2)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_INT(P_CheckMeleeRange2(self));
}
//=============================================================================
//

View file

@ -238,7 +238,7 @@ enum PCM
AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params = NULL);
AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false);
AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false, bool frontonly = false);
//
// P_MAP

View file

@ -45,7 +45,6 @@
#include "templates.h"
#include "po_man.h"
static AActor *RoughBlockCheck (AActor *mo, int index, void *);
sector_t *P_PointInSectorBuggy(double x, double y);
int P_VanillaPointOnDivlineSide(double x, double y, const divline_t* line);
@ -1685,19 +1684,6 @@ FPathTraverse::~FPathTraverse()
// distance is in MAPBLOCKUNITS
//===========================================================================
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;
@ -1787,6 +1773,13 @@ AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, in
return NULL;
}
struct BlockCheckInfo
{
bool onlyseekable;
bool frontonly;
divline_t frontline;
};
//===========================================================================
//
// RoughBlockCheck
@ -1795,14 +1788,19 @@ AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, in
static AActor *RoughBlockCheck (AActor *mo, int index, void *param)
{
bool onlyseekable = param != NULL;
BlockCheckInfo *info = (BlockCheckInfo *)param;
FBlockNode *link;
for (link = blocklinks[index]; link != NULL; link = link->NextActor)
{
if (link->Me != mo)
{
if (onlyseekable && !mo->CanSeek(link->Me))
if (info->onlyseekable && !mo->CanSeek(link->Me))
{
continue;
}
if (info->frontonly && P_PointOnDivlineSide(link->Me->X(), link->Me->Y(), &info->frontline) != 0)
{
continue;
}
@ -1815,6 +1813,30 @@ static AActor *RoughBlockCheck (AActor *mo, int index, void *param)
return NULL;
}
AActor *P_RoughMonsterSearch(AActor *mo, int distance, bool onlyseekable, bool frontonly)
{
BlockCheckInfo info;
info.onlyseekable = onlyseekable;
if ((info.frontonly = frontonly))
{
info.frontline.x = mo->X();
info.frontline.y = mo->Y();
info.frontline.dx = -mo->Angles.Yaw.Sin();
info.frontline.dy = -mo->Angles.Yaw.Cos();
}
return P_BlockmapSearch(mo, distance, RoughBlockCheck, (void *)&info);
}
DEFINE_ACTION_FUNCTION(AActor, RoughMonsterSearch)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(distance);
PARAM_BOOL_DEF(onlyseekable);
PARAM_BOOL_DEF(frontonly);
ACTION_RETURN_OBJECT(P_RoughMonsterSearch(self, distance, onlyseekable, frontonly));
}
//==========================================================================
//
// [RH] LinkToWorldForMapThing

View file

@ -5908,6 +5908,13 @@ int P_GetThingFloorType (AActor *thing)
}
}
DEFINE_ACTION_FUNCTION(AActor, GetFloorTerrain)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_POINTER(&Terrains[P_GetThingFloorType(self)]);
}
//---------------------------------------------------------------------------
//
// FUNC P_HitWater

View file

@ -724,3 +724,18 @@ FName P_GetTerrainName(int terrainnum)
}
}
DEFINE_FIELD_NAMED(FTerrainDef, Name, TerrainName)
DEFINE_FIELD(FTerrainDef, Splash)
DEFINE_FIELD(FTerrainDef, DamageAmount)
DEFINE_FIELD(FTerrainDef, DamageMOD)
DEFINE_FIELD(FTerrainDef, DamageTimeMask)
DEFINE_FIELD(FTerrainDef, FootClip)
DEFINE_FIELD(FTerrainDef, StepVolume)
DEFINE_FIELD(FTerrainDef, WalkStepTics)
DEFINE_FIELD(FTerrainDef, RunStepTics)
DEFINE_FIELD(FTerrainDef, LeftStepSound)
DEFINE_FIELD(FTerrainDef, RightStepSound)
DEFINE_FIELD(FTerrainDef, IsLiquid)
DEFINE_FIELD(FTerrainDef, AllowProtection)
DEFINE_FIELD(FTerrainDef, Friction)
DEFINE_FIELD(FTerrainDef, MoveFactor)

View file

@ -45,6 +45,7 @@
#include "autosegs.h"
#include "p_maputl.h"
#include "gi.h"
#include "p_terrain.h"
static TArray<FPropertyInfo*> properties;
static TArray<AFuncDesc> AFTable;

View file

@ -1095,6 +1095,7 @@ void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self);
#define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_STATE); return 1; } return 0; } while(0)
#define ACTION_RETURN_POINTER(v) do { void *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_GENERIC); return 1; } return 0; } while(0)
#define ACTION_RETURN_OBJECT(v) do { auto state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_OBJECT); return 1; } return 0; } while(0)
#define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0)
#define ACTION_RETURN_VEC2(v) do { DVector2 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector2(u); return 1; } return 0; } while(0)

View file

@ -288,6 +288,7 @@ class Actor : Thinker native
native void SetFriendPlayer(PlayerInfo player);
native void NoiseAlert(Actor emitter, bool splash = false, double maxdist = 0);
native void ClearBounce();
native TerrainDef GetFloorTerrain();
native void ExplodeMissile(line lin = null, Actor target = null);
native void RestoreDamage();
@ -310,7 +311,7 @@ class Actor : Thinker native
native Actor SpawnMissileAngleZSpeed (double z, class<Actor> type, double angle, double vz, double speed, Actor owner = null, bool checkspawn = true);
native Actor, Actor SpawnPlayerMissile(class<Actor> 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);
native Actor RoughMonsterSearch(int distance, bool onlyseekable = false, bool frontonly = false);
native int ApplyDamageFactor(Name damagetype, int damage);
native int GetModifiedDamage(Name damagetype, int damage, bool passive);
@ -329,6 +330,7 @@ class Actor : Thinker native
native void SetIdle(bool nofunction = false);
native bool CheckMeleeRange();
native bool CheckMeleeRange2();
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);
native Actor, int LineAttack(double angle, double distance, double pitch, int damage, Name damageType, class<Actor> pufftype, int flags = 0, out FTranslatedLineTarget victim = null);

View file

@ -247,4 +247,23 @@ struct Wads
}
native static int CheckNumForName(string name, int ns, int wadnum = -1, bool exact = false);
}
}
struct TerrainDef native
{
native Name TerrainName;
native int Splash;
native int DamageAmount;
native Name DamageMOD;
native int DamageTimeMask;
native double FootClip;
native float StepVolume;
native int WalkStepTics;
native int RunStepTics;
native Sound LeftStepSound;
native Sound RightStepSound;
native bool IsLiquid;
native bool AllowProtection;
native double Friction;
native double MoveFactor;
};

View file

@ -19,7 +19,7 @@ class ClericWeapon : Weapon
}
}
class MageWeapon : Weapon native
class MageWeapon : Weapon
{
Default
{

View file

@ -18,8 +18,6 @@ class MageBoss : Actor
Obituary "$OB_MBOSS";
}
native void A_MageAttack();
States
{
Spawn:
@ -83,4 +81,39 @@ class MageBoss : Actor
FDTH V 4 Bright;
Stop;
}
//============================================================================
//
// MStaffSpawn2 - for use by mage class boss
//
//============================================================================
void MStaffSpawn2 (double angle)
{
Actor mo = SpawnMissileAngleZ (pos.z + 40, "MageStaffFX2", angle, 0.);
if (mo)
{
mo.target = self;
mo.tracer = RoughMonsterSearch(10, true, true);
}
}
//============================================================================
//
// A_MStaffAttack2 - for use by mage class boss
//
//============================================================================
void A_MageAttack()
{
if (target == NULL)
{
return;
}
MStaffSpawn2(angle);
MStaffSpawn2(angle - 5);
MStaffSpawn2(angle + 5);
A_PlaySound("MageStaffFire", CHAN_WEAPON);
}
}

View file

@ -76,8 +76,10 @@ class BloodscourgeDrop : Actor
// The Mages's Staff (Bloodscourge) -----------------------------------------
class MWeapBloodscourge : MageWeapon native
class MWeapBloodscourge : MageWeapon
{
int MStaffCount;
Default
{
Health 3;
@ -97,9 +99,6 @@ class MWeapBloodscourge : MageWeapon native
Tag "$TAG_MWEAPBLOODSCOURGE";
}
action native void A_MStaffAttack();
action native void A_MStaffPalette();
States
{
Spawn:
@ -123,11 +122,104 @@ class MWeapBloodscourge : MageWeapon native
MSTF J 5 Offset (0, 36);
Goto Ready;
}
//============================================================================
//
//
//
//============================================================================
override Color GetBlend ()
{
if (paletteflash & PF_HEXENWEAPONS)
{
if (MStaffCount == 3)
return Color(128, 100, 73, 0);
else if (MStaffCount == 2)
return Color(128, 125, 92, 0);
else if (MStaffCount == 1)
return Color(128, 150, 110, 0);
else
return Color(0, 0, 0, 0);
}
else
{
return Color (MStaffCount * 128 / 3, 151, 110, 0);
}
}
//============================================================================
//
// MStaffSpawn
//
//============================================================================
private action void MStaffSpawn (double angle, Actor alttarget)
{
FTranslatedLineTarget t;
Actor mo = SpawnPlayerMissile ("MageStaffFX2", angle, pLineTarget:t);
if (mo)
{
mo.target = self;
if (t.linetarget && !t.unlinked)
mo.tracer = t.linetarget;
else
mo.tracer = alttarget;
}
}
//============================================================================
//
// A_MStaffAttack
//
//============================================================================
action void A_MStaffAttack()
{
FTranslatedLineTarget t;
if (player == null)
{
return;
}
Weapon weapon = player.ReadyWeapon;
if (weapon != NULL)
{
if (!weapon.DepleteAmmo (weapon.bAltFire))
return;
}
// [RH] Let's try and actually track what the player aimed at
AimLineAttack (angle, PLAYERMISSILERANGE, t, 32.);
if (t.linetarget == NULL)
{
t.linetarget = RoughMonsterSearch(10, true, true);
}
MStaffSpawn (angle, t.linetarget);
MStaffSpawn (angle-5, t.linetarget);
MStaffSpawn (angle+5, t.linetarget);
A_PlaySound ("MageStaffFire", CHAN_WEAPON);
invoker.MStaffCount = 3;
}
//============================================================================
//
// A_MStaffPalette
//
//============================================================================
action void A_MStaffPalette()
{
if (invoker.MStaffCount > 0) invoker.MStaffCount--;
}
}
// Mage Staff FX2 (Bloodscourge) --------------------------------------------
class MageStaffFX2 : Actor native
class MageStaffFX2 : Actor
{
Default
{
@ -143,7 +235,6 @@ class MageStaffFX2 : Actor native
Obituary "$OB_MPMWEAPBLOODSCOURGE";
}
native void A_MStaffTrack();
States
{
@ -158,6 +249,12 @@ class MageStaffFX2 : Actor native
Stop;
}
//============================================================================
//
//
//
//============================================================================
override int SpecialMissileHit (Actor victim)
{
if (victim != target && !victim.player && !victim.bBoss)
@ -168,5 +265,26 @@ class MageStaffFX2 : Actor native
return -1;
}
override bool SpecialBlastHandling (Actor source, double strength)
{
// Reflect to originator
tracer = target;
target = source;
return true;
}
//============================================================================
//
// A_MStaffTrack
//
//============================================================================
void A_MStaffTrack()
{
if (tracer == null && random[MStaffTrack]() < 50)
{
tracer = RoughMonsterSearch (10, true);
}
A_SeekerMissile(2, 10);
}
}

View file

@ -24,16 +24,6 @@ class Serpent : Actor
HitObituary "$OB_SERPENTHIT";
}
native void A_SerpentHumpDecide();
native void A_SerpentHide();
native void A_SerpentCheckForAttack();
native void A_SerpentSpawnGibs();
native void A_SerpentUnHide();
native void A_SerpentRaiseHump();
native void A_SerpentLowerHump();
native void A_SerpentChooseAttack();
native void A_SerpentMeleeAttack();
States
{
Spawn:
@ -99,6 +89,203 @@ class Serpent : Actor
SSPT N 5 A_SerpentMeleeAttack;
Goto Dive;
}
//============================================================================
//
// A_SerpentUnHide
//
//============================================================================
void A_SerpentUnHide()
{
bInvisible = false;
Floorclip = 24;
}
//============================================================================
//
// A_SerpentHide
//
//============================================================================
void A_SerpentHide()
{
bInvisible = true;
Floorclip = 0;
}
//============================================================================
//
// A_SerpentRaiseHump
//
// Raises the hump above the surface by raising the floorclip level
//============================================================================
void A_SerpentRaiseHump()
{
Floorclip -= 4;
}
//============================================================================
//
// A_SerpentLowerHump
//
//============================================================================
void A_SerpentLowerHump()
{
Floorclip += 4;
}
//============================================================================
//
// A_SerpentHumpDecide
//
// Decided whether to hump up, or if the mobj is a serpent leader,
// to missile attack
//============================================================================
void A_SerpentHumpDecide()
{
if (MissileState != NULL)
{
if (random[SerpentHump]() > 30)
{
return;
}
else if (random[SerpentHump]() < 40)
{ // Missile attack
SetState (MeleeState);
return;
}
}
else if (random[SerpentHump]() > 3)
{
return;
}
if (!CheckMeleeRange ())
{ // The hump shouldn't occur when within melee range
if (MissileState != NULL && random[SerpentHump]() < 128)
{
SetState (MeleeState);
}
else
{
SetStateLabel("Hump");
A_PlaySound ("SerpentActive", CHAN_BODY);
}
}
}
//============================================================================
//
// A_SerpentCheckForAttack
//
//============================================================================
void A_SerpentCheckForAttack()
{
if (!target)
{
return;
}
if (MissileState != NULL)
{
if (!CheckMeleeRange ())
{
SetStateLabel ("Attack");
return;
}
}
if (CheckMeleeRange2 ())
{
SetStateLabel ("Walk");
}
else if (CheckMeleeRange ())
{
if (random[SerpentAttack]() < 32)
{
SetStateLabel ("Walk");
}
else
{
SetStateLabel ("Attack");
}
}
}
//============================================================================
//
// A_SerpentChooseAttack
//
//============================================================================
void A_SerpentChooseAttack()
{
if (!target || CheckMeleeRange())
{
return;
}
if (MissileState != NULL)
{
SetState (MissileState);
}
}
//============================================================================
//
// A_SerpentMeleeAttack
//
//============================================================================
void A_SerpentMeleeAttack()
{
if (!target)
{
return;
}
if (CheckMeleeRange ())
{
int damage = random[SerpentAttack](1, 8) * 5;
int newdam = target.DamageMobj (self, self, damage, 'Melee');
target.TraceBleed (newdam > 0 ? newdam : damage, self);
A_PlaySound ("SerpentMeleeHit", CHAN_BODY);
}
if (random[SerpentAttack]() < 96)
{
A_SerpentCheckForAttack();
}
}
//============================================================================
//
// A_SerpentSpawnGibs
//
//============================================================================
void A_SerpentSpawnGibs()
{
static const class<Actor> GibTypes[] =
{
"SerpentGib3",
"SerpentGib2",
"SerpentGib1"
};
for (int i = 2; i >= 0; --i)
{
double x = (random[SerpentGibs]() - 128) / 16.;
double y = (random[SerpentGibs]() - 128) / 16.;
Actor mo = Spawn (GibTypes[i], Vec2OffsetZ(x, y, floorz + 1), ALLOW_REPLACE);
if (mo)
{
mo.Vel.X = (random[SerpentGibs]() - 128) / 1024.f;
mo.Vel.Y = (random[SerpentGibs]() - 128) / 1024.f;
mo.Floorclip = 6;
}
}
}
}
// Serpent Leader -----------------------------------------------------------
@ -159,8 +346,6 @@ class SerpentHead : Actor
+NOBLOCKMAP
}
native void A_SerpentHeadCheck();
States
{
Spawn:
@ -170,6 +355,28 @@ class SerpentHead : Actor
SSXD S -1;
Loop;
}
//============================================================================
//
// A_SerpentHeadCheck
//
//============================================================================
void A_SerpentHeadCheck()
{
if (pos.z <= floorz)
{
if (GetFloorTerrain().IsLiquid)
{
HitFloor ();
Destroy();
}
else
{
SetStateLabel ("NAME_Death");
}
}
}
}
// Serpent Gib 1 ------------------------------------------------------------
@ -183,10 +390,6 @@ class SerpentGib1 : Actor
+NOBLOCKMAP +NOGRAVITY
}
native void A_FloatGib();
native void A_DelayGib();
native void A_SinkGib();
States
{
Spawn:
@ -199,6 +402,41 @@ class SerpentGib1 : Actor
SSXD QQQ 8 A_SinkGib;
Stop;
}
//============================================================================
//
// A_FloatGib
//
//============================================================================
void A_FloatGib()
{
Floorclip -= 1;
}
//============================================================================
//
// A_SinkGib
//
//============================================================================
void A_SinkGib()
{
Floorclip += 1;
}
//============================================================================
//
// A_DelayGib
//
//============================================================================
void A_DelayGib()
{
tics -= random[DelayGib]() >> 2;
}
}
// Serpent Gib 2 ------------------------------------------------------------