From 21a1d5ffc80517c45e3f76dc541b8bb98bde402e Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <c.oelckers@users.noreply.github.com>
Date: Thu, 17 Nov 2016 00:44:43 +0100
Subject: [PATCH] - scriptified Hexen's Wraith and parts of the Spike.

---
 src/CMakeLists.txt                     |   1 -
 src/g_hexen/a_hexenmisc.cpp            |   1 -
 src/g_hexen/a_spike.cpp                | 101 +---------
 src/g_hexen/a_wraith.cpp               | 258 -------------------------
 src/g_shared/a_debris.cpp              |  24 ---
 src/g_shared/a_sharedglobal.h          |   1 -
 src/p_enemy.cpp                        |  41 ----
 src/p_enemy.h                          |   3 -
 src/p_mobj.cpp                         |   6 +-
 wadsrc/static/zscript/actor.txt        |  62 ++++++
 wadsrc/static/zscript/hexen/spike.txt  |  77 +++++++-
 wadsrc/static/zscript/hexen/wraith.txt | 223 ++++++++++++++++++++-
 12 files changed, 354 insertions(+), 444 deletions(-)
 delete mode 100644 src/g_hexen/a_wraith.cpp

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f1fc1bab54..c5f307d56f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -900,7 +900,6 @@ set( NOT_COMPILED_SOURCE_FILES
 	g_hexen/a_spike.cpp
 	g_hexen/a_summon.cpp
 	g_hexen/a_teleportother.cpp
-	g_hexen/a_wraith.cpp
 	g_strife/a_acolyte.cpp
 	g_strife/a_alienspectres.cpp
 	g_strife/a_coin.cpp
diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp
index 9003405e49..21227e8486 100644
--- a/src/g_hexen/a_hexenmisc.cpp
+++ b/src/g_hexen/a_hexenmisc.cpp
@@ -52,4 +52,3 @@
 #include "a_spike.cpp"
 #include "a_summon.cpp"
 #include "a_teleportother.cpp"
-#include "a_wraith.cpp"
diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp
index 9b2cb5d6e2..be28e38dfd 100644
--- a/src/g_hexen/a_spike.cpp
+++ b/src/g_hexen/a_spike.cpp
@@ -20,28 +20,13 @@ static FRandom pr_thrustraise ("ThrustRaise");
 class AThrustFloor : public AActor
 {
 	DECLARE_CLASS (AThrustFloor, AActor)
-	HAS_OBJECT_POINTERS
 public:
 	
-	void Serialize(FSerializer &arc);
-
 	void Activate (AActor *activator);
 	void Deactivate (AActor *activator);
-
-	TObjPtr<AActor> DirtClump;
 };
 
-IMPLEMENT_CLASS(AThrustFloor, false, true, false, false)
-
-IMPLEMENT_POINTERS_START(AThrustFloor)
-	IMPLEMENT_POINTER(DirtClump)
-IMPLEMENT_POINTERS_END
-
-void AThrustFloor::Serialize(FSerializer &arc)
-{
-	Super::Serialize (arc);
-	arc("dirtclump", DirtClump);
-}
+IMPLEMENT_CLASS(AThrustFloor, false, false, false, false)
 
 void AThrustFloor::Activate (AActor *activator)
 {
@@ -68,90 +53,6 @@ void AThrustFloor::Deactivate (AActor *activator)
 	}
 }
 
-//===========================================================================
-//
-// Thrust floor stuff
-//
-// Thrust Spike Variables
-//		DirtClump	pointer to dirt clump actor
-//		special2	speed of raise
-//		args[0]		0 = lowered,  1 = raised
-//		args[1]		0 = normal,   1 = bloody
-//===========================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_ThrustInitUp)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	self->special2 = 5;	// Raise speed
-	self->args[0] = 1;		// Mark as up
-	self->Floorclip = 0;
-	self->flags = MF_SOLID;
-	self->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP;
-	self->special1 = 0L;
-	return 0;
-}
-
-DEFINE_ACTION_FUNCTION(AActor, A_ThrustInitDn)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	self->special2 = 5;	// Raise speed
-	self->args[0] = 0;		// Mark as down
-	self->Floorclip = self->GetDefault()->Height;
-	self->flags = 0;
-	self->flags2 = MF2_NOTELEPORT|MF2_FLOORCLIP;
-	self->renderflags = RF_INVISIBLE;
-	static_cast<AThrustFloor *>(self)->DirtClump =
-		Spawn("DirtClump", self->Pos(), ALLOW_REPLACE);
-	return 0;
-}
-
-
-DEFINE_ACTION_FUNCTION(AActor, A_ThrustRaise)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	AThrustFloor *actor = static_cast<AThrustFloor *>(self);
-
-	if (A_RaiseMobj (actor, self->special2))
-	{	// Reached it's target height
-		actor->args[0] = 1;
-		if (actor->args[1])
-			actor->SetState (actor->FindState ("BloodThrustInit2"), true);
-		else
-			actor->SetState (actor->FindState ("ThrustInit2"), true);
-	}
-
-	// Lose the dirt clump
-	if ((actor->Floorclip < actor->Height) && actor->DirtClump)
-	{
-		actor->DirtClump->Destroy ();
-		actor->DirtClump = NULL;
-	}
-
-	// Spawn some dirt
-	if (pr_thrustraise()<40)
-		P_SpawnDirt (actor, actor->radius);
-	actor->special2++;							// Increase raise speed
-	return 0;
-}
-
-DEFINE_ACTION_FUNCTION(AActor, A_ThrustLower)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	if (A_SinkMobj (self, 6))
-	{
-		self->args[0] = 0;
-		if (self->args[1])
-			self->SetState (self->FindState ("BloodThrustInit1"), true);
-		else
-			self->SetState (self->FindState ("ThrustInit1"), true);
-	}
-	return 0;
-}
-
 DEFINE_ACTION_FUNCTION(AActor, A_ThrustImpale)
 {
 	PARAM_SELF_PROLOGUE(AActor);
diff --git a/src/g_hexen/a_wraith.cpp b/src/g_hexen/a_wraith.cpp
deleted file mode 100644
index fcd0d75f34..0000000000
--- a/src/g_hexen/a_wraith.cpp
+++ /dev/null
@@ -1,258 +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 "a_sharedglobal.h"
-#include "vm.h"
-*/
-
-static FRandom pr_stealhealth ("StealHealth");
-static FRandom pr_wraithfx2 ("WraithFX2");
-static FRandom pr_wraithfx3 ("WraithFX3");
-static FRandom pr_wraithfx4 ("WraithFX4");
-
-//============================================================================
-//
-// A_WraithInit
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithInit)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	self->AddZ(48);
-
-	// [RH] Make sure the wraith didn't go into the ceiling
-	if (self->Top() > self->ceilingz)
-	{
-		self->SetZ(self->ceilingz - self->Height);
-	}
-
-	self->WeaveIndexZ = 0;			// index into floatbob
-	return 0;
-}
-
-//============================================================================
-//
-// A_WraithRaiseInit
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithRaiseInit)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	self->renderflags &= ~RF_INVISIBLE;
-	self->flags2 &= ~MF2_NONSHOOTABLE;
-	self->flags3 &= ~MF3_DONTBLAST;
-	self->flags |= MF_SHOOTABLE|MF_SOLID;
-	self->Floorclip = self->Height;
-	return 0;
-}
-
-//============================================================================
-//
-// A_WraithRaise
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithRaise)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	if (A_RaiseMobj (self, 2))
-	{
-		// Reached it's target height
-		// [RH] Once a buried wraith is fully raised, it should be
-		// morphable, right?
-		self->flags3 &= ~(MF3_DONTMORPH|MF3_SPECIALFLOORCLIP);
-		self->SetState (self->FindState("Chase"));
-		// [RH] Reset PainChance to a normal wraith's.
-		self->PainChance = GetDefaultByName ("Wraith")->PainChance;
-	}
-
-	P_SpawnDirt (self, self->radius);
-	return 0;
-}
-
-//============================================================================
-//
-// A_WraithMelee
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithMelee)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	int amount;
-
-	// Steal health from target and give to self
-	if (self->CheckMeleeRange() && (pr_stealhealth()<220))
-	{
-		amount = pr_stealhealth.HitDice (2);
-		P_DamageMobj (self->target, self, self, amount, NAME_Melee);
-		self->health += amount;
-	}
-	return 0;
-}
-
-//============================================================================
-//
-// A_WraithFX2 - spawns sparkle tail of missile
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithFX2)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	AActor *mo;
-	DAngle angle;
-	int i;
-
-	for (i = 2; i; --i)
-	{
-		mo = Spawn ("WraithFX2", self->Pos(), ALLOW_REPLACE);
-		if(mo)
-		{
-			angle = pr_wraithfx2() * (360 / 1024.f);
-			if (pr_wraithfx2() >= 128)
-			{
-				angle = -angle;
-			}
-			angle += self->Angles.Yaw;
-			mo->Vel.X = ((pr_wraithfx2() / 512.) + 1) * angle.Cos();
-			mo->Vel.Y = ((pr_wraithfx2() / 512.) + 1) * angle.Sin();
-			mo->Vel.Z = 0;
-			mo->target = self;
-			mo->Floorclip = 10;
-		}
-	}
-	return 0;
-}
-
-//============================================================================
-//
-// A_WraithFX3
-//
-// Spawn an FX3 around the self during attacks
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithFX3)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	AActor *mo;
-	int numdropped = pr_wraithfx3() % 15;
-
-	while (numdropped-- > 0)
-	{
-		double xo = (pr_wraithfx3() - 128) / 32.;
-		double yo = (pr_wraithfx3() - 128) / 32.;
-		double zo = pr_wraithfx3() / 64.;
-
-		mo = Spawn("WraithFX3", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
-		if (mo)
-		{
-			mo->floorz = self->floorz;
-			mo->ceilingz = self->ceilingz;
-			mo->target = self;
-		}
-	}
-	return 0;
-}
-
-//============================================================================
-//
-// A_WraithFX4
-//
-// Spawn an FX4 during movement
-//
-//============================================================================
-
-void A_WraithFX4 (AActor *self)
-{
-	AActor *mo;
-	int chance = pr_wraithfx4();
-	bool spawn4, spawn5;
-
-	if (chance < 10)
-	{
-		spawn4 = true;
-		spawn5 = false;
-	}
-	else if (chance < 20)
-	{
-		spawn4 = false;
-		spawn5 = true;
-	}
-	else if (chance < 25)
-	{
-		spawn4 = true;
-		spawn5 = true;
-	}
-	else
-	{
-		spawn4 = false;
-		spawn5 = false;
-	}
-
-	if (spawn4)
-	{
-		double xo = (pr_wraithfx4() - 128) / 16.;
-		double yo = (pr_wraithfx4() - 128) / 16.;
-		double zo = (pr_wraithfx4() / 64.);
-
-		mo = Spawn ("WraithFX4", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
-		if (mo)
-		{
-			mo->floorz = self->floorz;
-			mo->ceilingz = self->ceilingz;
-			mo->target = self;
-		}
-	}
-	if (spawn5)
-	{
-		double xo = (pr_wraithfx4() - 128) / 32.;
-		double yo = (pr_wraithfx4() - 128) / 32.;
-		double zo = (pr_wraithfx4() / 64.);
-
-		mo = Spawn ("WraithFX5", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
-		if (mo)
-		{
-			mo->floorz = self->floorz;
-			mo->ceilingz = self->ceilingz;
-			mo->target = self;
-		}
-	}
-}
-
-//============================================================================
-//
-// A_WraithChase
-//
-//============================================================================
-
-DEFINE_ACTION_FUNCTION(AActor, A_WraithChase)
-{
-	PARAM_SELF_PROLOGUE(AActor);
-
-	int weaveindex = self->WeaveIndexZ;
-	self->AddZ(BobSin(weaveindex));
-	self->WeaveIndexZ = (weaveindex + 2) & 63;
-//	if (self->Floorclip > 0)
-//	{
-//		P_SetMobjState(self, S_WRAITH_RAISE2);
-//		return;
-//	}
-	A_Chase (stack, self);
-	A_WraithFX4 (self);
-	return 0;
-}
diff --git a/src/g_shared/a_debris.cpp b/src/g_shared/a_debris.cpp
index 3be3c22409..d3d69d7f6b 100644
--- a/src/g_shared/a_debris.cpp
+++ b/src/g_shared/a_debris.cpp
@@ -3,8 +3,6 @@
 #include "m_random.h"
 #include "m_fixed.h"
 
-static FRandom pr_dirt ("SpawnDirt");
-
 // Stained glass ------------------------------------------------------------
 
 class AGlassShard : public AActor
@@ -27,25 +25,3 @@ public:
 
 IMPLEMENT_CLASS(AGlassShard, false, false, false, false)
 
-// Dirt stuff
-
-void P_SpawnDirt (AActor *actor, double radius)
-{
-	PClassActor *dtype = NULL;
-	AActor *mo;
-
-	double zo = pr_dirt() / 128. + 1;
-	DVector3 pos = actor->Vec3Angle(radius, pr_dirt() * (360./256), zo);
-
-	char fmt[8];
-	mysnprintf(fmt, countof(fmt), "Dirt%d", 1 + pr_dirt()%6);
-	dtype = PClass::FindActor(fmt);
-	if (dtype)
-	{
-		mo = Spawn (dtype, pos, ALLOW_REPLACE);
-		if (mo)
-		{
-			mo->Vel.Z = pr_dirt() / 64.;
-		}
-	}
-}
diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h
index c00cd8cf0c..65f9d90b63 100644
--- a/src/g_shared/a_sharedglobal.h
+++ b/src/g_shared/a_sharedglobal.h
@@ -10,7 +10,6 @@ struct side_t;
 struct F3DFloor;
 class DBaseDecal;
 
-void P_SpawnDirt (AActor *actor, double radius);
 class DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, double x, double y, double z, DAngle angle, double tracedist, bool permanent);
 
 class DBaseDecal : public DThinker
diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp
index d36a8de9d8..6bb96ef30c 100644
--- a/src/p_enemy.cpp
+++ b/src/p_enemy.cpp
@@ -3468,47 +3468,6 @@ int P_Massacre ()
 	return killcount;
 }
 
-//
-// A_SinkMobj
-// Sink a mobj incrementally into the floor
-//
-
-bool A_SinkMobj (AActor *actor, double speed)
-{
-	if (actor->Floorclip < actor->Height)
-	{
-		actor->Floorclip += speed;
-		return false;
-	}
-	return true;
-}
-
-//
-// A_RaiseMobj
-// Raise a mobj incrementally from the floor to 
-// 
-
-bool A_RaiseMobj (AActor *actor, double speed)
-{
-	bool done = true;
-
-	// Raise a mobj from the ground
-	if (actor->Floorclip > 0)
-	{
-		actor->Floorclip -= speed;
-		if (actor->Floorclip <= 0)
-		{
-			actor->Floorclip = 0;
-			done = true;
-		}
-		else
-		{
-			done = false;
-		}
-	}
-	return done;		// Reached target height
-}
-
 DEFINE_ACTION_FUNCTION(AActor, A_ClassBossHealth)
 {
 	PARAM_SELF_PROLOGUE(AActor);
diff --git a/src/p_enemy.h b/src/p_enemy.h
index 7f4424fd6f..c4a966344b 100644
--- a/src/p_enemy.h
+++ b/src/p_enemy.h
@@ -75,9 +75,6 @@ void A_Chase(VMFrameStack *stack, AActor *self);
 void A_FaceTarget(AActor *actor);
 void A_Face(AActor *self, AActor *other, DAngle max_turn = 0., DAngle max_pitch = 270., DAngle ang_offset = 0., DAngle pitch_offset = 0., int flags = 0, double z_add = 0);
 
-bool A_RaiseMobj (AActor *, double speed);
-bool A_SinkMobj (AActor *, double speed);
-
 bool CheckBossDeath (AActor *);
 int P_Massacre ();
 bool P_CheckMissileRange (AActor *actor);
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index abbd057d73..9c36e861cb 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -204,8 +204,8 @@ void AActor::InitNativeFields()
 	meta->AddNativeField("fillcolor",			TypeColor,		myoffsetof(AActor, fillcolor));
 	meta->AddNativeField("Sector",				TypeSector,		myoffsetof(AActor, Sector));
 	//meta->AddNativeField("Subsector",			TypeSubsector,	myoffsetof(AActor, subsector));
-	meta->AddNativeField(NAME_CeilingZ,			TypeFloat64,	myoffsetof(AActor, ceilingz), VARF_ReadOnly);
-	meta->AddNativeField(NAME_FloorZ,			TypeFloat64,	myoffsetof(AActor, floorz), VARF_ReadOnly);
+	meta->AddNativeField(NAME_CeilingZ,			TypeFloat64,	myoffsetof(AActor, ceilingz));
+	meta->AddNativeField(NAME_FloorZ,			TypeFloat64,	myoffsetof(AActor, floorz));
 	meta->AddNativeField("DropoffZ",			TypeFloat64,	myoffsetof(AActor, dropoffz), VARF_ReadOnly);
 	meta->AddNativeField("floorsector",			TypeSector,		myoffsetof(AActor, floorsector));
 	meta->AddNativeField("floorpic",			TypeSInt32,		myoffsetof(AActor, floorpic));	// Do we need a variable type 'texture' to do this?
@@ -225,7 +225,7 @@ void AActor::InitNativeFields()
 	meta->AddNativeField("specialf1",			TypeFloat64,	myoffsetof(AActor, specialf1));
 	meta->AddNativeField("specialf2",			TypeFloat64,	myoffsetof(AActor, specialf2));
 	meta->AddNativeField("weaponspecial",		TypeSInt32,		myoffsetof(AActor, weaponspecial));
-	meta->AddNativeField(NAME_Health,			TypeSInt32,		myoffsetof(AActor, health), VARF_ReadOnly);
+	meta->AddNativeField(NAME_Health,			TypeSInt32,		myoffsetof(AActor, health));
 	meta->AddNativeField("movedir",				TypeUInt8,		myoffsetof(AActor, movedir));
 	meta->AddNativeField("visdir",				TypeSInt8,		myoffsetof(AActor, visdir));
 	meta->AddNativeField("movecount",			TypeSInt16,		myoffsetof(AActor, movecount));
diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt
index 7b6b63672d..f20a0b19ec 100644
--- a/wadsrc/static/zscript/actor.txt
+++ b/wadsrc/static/zscript/actor.txt
@@ -268,6 +268,68 @@ class Actor : Thinker native
 		DamageMobj(null, null, health, damagetype, DMG_FORCED);
 	}
 
+	void SpawnDirt (double radius)
+	{
+		class<Actor> dtype;
+		switch (random[Dirt](0, 5))
+		{
+			case 0: dtype = "Dirt1"; break;
+			case 1: dtype = "Dirt2"; break;
+			case 2: dtype = "Dirt3"; break;
+			case 3: dtype = "Dirt4"; break;
+			case 4: dtype = "Dirt5"; break;
+			default: dtype = "Dirt6"; break;
+		}
+
+		double zo = random[Dirt]() / 128. + 1;
+		Vector3 pos = Vec3Angle(radius, random[Dirt]() * (360./256), zo);
+
+		Actor mo = Spawn (dtype, pos, ALLOW_REPLACE);
+		if (mo)
+		{
+			mo.Vel.Z = random[Dirt]() / 64.;
+		}
+	}
+	
+	//
+	// A_SinkMobj
+	// Sink a mobj incrementally into the floor
+	//
+
+	bool SinkMobj (double speed)
+	{
+		if (Floorclip < Height)
+		{
+			Floorclip += speed;
+			return false;
+		}
+		return true;
+	}
+
+	//
+	// A_RaiseMobj
+	// Raise a mobj incrementally from the floor to 
+	// 
+
+	bool RaiseMobj (double speed)
+	{
+		// Raise a mobj from the ground
+		if (Floorclip > 0)
+		{
+			Floorclip -= speed;
+			if (Floorclip <= 0)
+			{
+				Floorclip = 0;
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+		return true;
+	}
+	
 
 	native void A_Face(Actor faceto, float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0);
 
diff --git a/wadsrc/static/zscript/hexen/spike.txt b/wadsrc/static/zscript/hexen/spike.txt
index 58f763ccc7..8d418d55a5 100644
--- a/wadsrc/static/zscript/hexen/spike.txt
+++ b/wadsrc/static/zscript/hexen/spike.txt
@@ -26,11 +26,7 @@ class ThrustFloor : Actor native
 		Height 128;
 	}
 
-	native void A_ThrustRaise();
 	native void A_ThrustImpale();
-	native void A_ThrustLower();
-	native void A_ThrustInitDn();
-	native void A_ThrustInitUp();
 
 	States
 	{
@@ -81,6 +77,79 @@ class ThrustFloor : Actor native
 		TSPK B 2 A_ThrustImpale;
 		Loop;
 	}
+	
+	//===========================================================================
+	//
+	// Thrust floor stuff
+	//
+	// Thrust Spike Variables
+	//		master		pointer to dirt clump actor
+	//		special2	speed of raise
+	//		args[0]		0 = lowered,  1 = raised
+	//		args[1]		0 = normal,   1 = bloody
+	//===========================================================================
+
+	void A_ThrustInitUp()
+	{
+		special2 = 5;	// Raise speed
+		args[0] = 1;		// Mark as up
+		Floorclip = 0;
+		bSolid = true;
+		bNoTeleport = true;
+		bFloorClip = true;
+		special1 = 0;
+	}
+
+	void A_ThrustInitDn()
+	{
+		special2 = 5;	// Raise speed
+		args[0] = 0;		// Mark as down
+		Floorclip = Default.Height;
+		bSolid = false;
+		bNoTeleport = true;
+		bFloorClip = true;
+		bInvisible = true;
+		master = Spawn("DirtClump", Pos, ALLOW_REPLACE);
+	}
+
+
+	void A_ThrustRaise()
+	{
+		if (RaiseMobj (special2))
+		{	// Reached it's target height
+			args[0] = 1;
+			if (args[1])
+				SetStateLabel ("BloodThrustInit2", true);
+			else
+				SetStateLabel ("ThrustInit2", true);
+		}
+
+		// Lose the dirt clump
+		if ((Floorclip < Height) && master)
+		{
+			master.Destroy ();
+			master = null;
+		}
+
+		// Spawn some dirt
+		if (random[Thrustraise]()<40)
+			SpawnDirt (radius);
+		special2++;							// Increase raise speed
+	}
+
+	void A_ThrustLower()
+	{
+		if (SinkMobj (6))
+		{
+			args[0] = 0;
+			if (args[1])
+				SetStateLabel ("BloodThrustInit1", true);
+			else
+				SetStateLabel ("ThrustInit1", true);
+		}
+	}
+
+	
 }
 
 // Spike up -----------------------------------------------------------------
diff --git a/wadsrc/static/zscript/hexen/wraith.txt b/wadsrc/static/zscript/hexen/wraith.txt
index 86df72d94e..42a81ae6c5 100644
--- a/wadsrc/static/zscript/hexen/wraith.txt
+++ b/wadsrc/static/zscript/hexen/wraith.txt
@@ -23,11 +23,6 @@ class Wraith : Actor
 		Obituary "$OB_WRAITH";
 	}
 
-	native void A_WraithInit();
-	native void A_WraithChase();
-	native void A_WraithFX3();
-	native void A_WraithMelee();
-
 	States
 	{
 	Spawn:
@@ -78,6 +73,150 @@ class Wraith : Actor
 		WRT2 I 1 A_FreezeDeathChunks;
 		Wait;
 	}
+	
+	//============================================================================
+	//
+	// A_WraithInit
+	//
+	//============================================================================
+
+	void A_WraithInit()
+	{
+		AddZ(48);
+
+		// [RH] Make sure the wraith didn't go into the ceiling
+		if (pos.z + height > ceilingz)
+		{
+			SetZ(ceilingz - Height);
+		}
+
+		WeaveIndexZ = 0;			// index into floatbob
+	}
+
+
+	//============================================================================
+	//
+	// A_WraithChase
+	//
+	//============================================================================
+
+	void A_WraithChase()
+	{
+		int weaveindex = WeaveIndexZ;
+		AddZ(BobSin(weaveindex));
+		WeaveIndexZ = (weaveindex + 2) & 63;
+		A_Chase ();
+		A_WraithFX4 ();
+	}
+
+	//============================================================================
+	//
+	// A_WraithFX3
+	//
+	// Spawn an FX3 around the wraith during attacks
+	//
+	//============================================================================
+
+	void A_WraithFX3()
+	{
+		int numdropped = random[WraithFX3](0,14);
+
+		while (numdropped-- > 0)
+		{
+			double xo = (random[WraithFX3]() - 128) / 32.;
+			double yo = (random[WraithFX3]() - 128) / 32.;
+			double zo = random[WraithFX3]() / 64.;
+
+			Actor mo = Spawn("WraithFX3", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
+			if (mo)
+			{
+				mo.floorz = floorz;
+				mo.ceilingz = ceilingz;
+				mo.target = self;
+			}
+		}
+	}
+
+	//============================================================================
+	//
+	// A_WraithFX4
+	//
+	// Spawn an FX4 during movement
+	//
+	//============================================================================
+
+	void A_WraithFX4 ()
+	{
+		int chance = random[WraithFX4]();
+		bool spawn4, spawn5;
+
+		if (chance < 10)
+		{
+			spawn4 = true;
+			spawn5 = false;
+		}
+		else if (chance < 20)
+		{
+			spawn4 = false;
+			spawn5 = true;
+		}
+		else if (chance < 25)
+		{
+			spawn4 = true;
+			spawn5 = true;
+		}
+		else
+		{
+			spawn4 = false;
+			spawn5 = false;
+		}
+
+		if (spawn4)
+		{
+			double xo = (random[WraithFX4]() - 128) / 16.;
+			double yo = (random[WraithFX4]() - 128) / 16.;
+			double zo = (random[WraithFX4]() / 64.);
+
+			Actor mo = Spawn ("WraithFX4", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
+			if (mo)
+			{
+				mo.floorz = floorz;
+				mo.ceilingz = ceilingz;
+				mo.target = self;
+			}
+		}
+		if (spawn5)
+		{
+			double xo = (random[WraithFX4]() - 128) / 32.;
+			double yo = (random[WraithFX4]() - 128) / 32.;
+			double zo = (random[WraithFX4]() / 64.);
+
+			Actor mo = Spawn ("WraithFX5", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
+			if (mo)
+			{
+				mo.floorz = floorz;
+				mo.ceilingz = ceilingz;
+				mo.target = self;
+			}
+		}
+	}
+
+	//============================================================================
+	//
+	// A_WraithMelee
+	//
+	//============================================================================
+
+	void A_WraithMelee()
+	{
+		// Steal health from target and give to self
+		if (CheckMeleeRange() && (random[StealHealth]()<220))
+		{
+			int amount = random[StealHealth](1, 8) * 2;
+			target.DamageMobj (self, self, amount, 'Melee');
+			health += amount;
+		}
+	}
 }
 
 // Buried wraith ------------------------------------------------------------
@@ -97,8 +236,6 @@ class WraithBuried : Wraith
 		PainChance 0;
 	}
 	
-	native void A_WraithRaiseInit();
-	native void A_WraithRaise();
 	
 	States
 	{
@@ -113,6 +250,47 @@ class WraithBuried : Wraith
 	Chase:
 		Goto Super::See;
 	}
+	
+	//============================================================================
+	//
+	// A_WraithRaiseInit
+	//
+	//============================================================================
+
+	void A_WraithRaiseInit()
+	{
+		bInvisible = false;
+		bNonShootable = false;
+		bDontBlast = false;
+		bShootable = true;
+		bSolid = true;
+		Floorclip = Height;
+	}
+
+	//============================================================================
+	//
+	// A_WraithRaise
+	//
+	//============================================================================
+
+	void A_WraithRaise()
+	{
+		if (RaiseMobj (2))
+		{
+			// Reached it's target height
+			// [RH] Once a buried wraith is fully raised, it should be
+			// morphable, right?
+			bDontMorph = false;
+			bSpecialFloorClip = false;
+			SetStateLabel ("Chase");
+			// [RH] Reset PainChance to a normal wraith's.
+			PainChance = GetDefaultByType("Wraith").PainChance;
+		}
+
+		SpawnDirt (radius);
+	}
+
+	
 }
 
 // Wraith FX 1 --------------------------------------------------------------
@@ -133,7 +311,6 @@ class WraithFX1 : Actor
 		DeathSound "WraithMissileExplode";
 	}
 
-	native void A_WraithFX2();
 
 	States
 	{
@@ -150,6 +327,36 @@ class WraithFX1 : Actor
 		WRBL I 3 Bright;
 		Stop;
 	}
+	
+	//============================================================================
+	//
+	// A_WraithFX2 - spawns sparkle tail of missile
+	//
+	//============================================================================
+
+	void A_WraithFX2()
+	{
+		for (int i = 2; i; --i)
+		{
+			Actor mo = Spawn ("WraithFX2", Pos, ALLOW_REPLACE);
+			if(mo)
+			{
+				double newangle = random[WraithFX2]() * (360 / 1024.f);
+				if (random[WraithFX2]() >= 128)
+				{
+					newangle = -newangle;
+				}
+				newangle += angle;
+				mo.Vel.X = ((random[WraithFX2]() / 512.) + 1) * cos(newangle);
+				mo.Vel.Y = ((random[WraithFX2]() / 512.) + 1) * sin(newangle);
+				mo.Vel.Z = 0;
+				mo.target = self;
+				mo.Floorclip = 10;
+			}
+		}
+	}
+
+	
 }
 
 // Wraith FX 2 --------------------------------------------------------------