- Additional dialogue lines for MAP11 and MAP20. - Officially support Ultimate Doom 2. - Localizable dialogue speaker names.
359 lines
9.1 KiB
Text
359 lines
9.1 KiB
Text
class swwm_PolyobjectHandle: Thinker
|
|
{
|
|
// This thinker keeps track of a polyobject's position, angle, movement speed etc.
|
|
// Instances of this thinker should only be created by the included map postprocessor
|
|
|
|
enum EPolyobjType
|
|
{
|
|
POTYP_NORMAL = 9301, // Normal StartSpot
|
|
POTYP_CRUSH = 9302, // Crush StartSpot
|
|
POTYP_HURT = 9303 // Hurt StartSpot
|
|
}
|
|
|
|
// Polyobject Number
|
|
int PolyobjectNum;
|
|
|
|
// Line defining the polyobject (Polyobj_StartLine, or one of Polyobj_ExplicitLine)
|
|
Line StartLine;
|
|
|
|
// [MK] All lines belonging to the polyobject
|
|
Array<Line> Lines;
|
|
|
|
// Initial angle of StartLine
|
|
double StartAngle;
|
|
|
|
// Last tic angle of StartLine
|
|
double LastAngle;
|
|
|
|
// Starting positions of StartLine vertices
|
|
Vector2[2] VertexStartingPos;
|
|
|
|
// Last tic positions of StartLine vertices
|
|
Vector2[2] VertexLastPos;
|
|
|
|
// Last tic position of the polyobject
|
|
Vector2 LastPos;
|
|
|
|
// SoundSequence number
|
|
int SoundSequenceNum;
|
|
|
|
// StartSpot position
|
|
Vector2 StartSpotPos;
|
|
|
|
// For bounds checking
|
|
double z;
|
|
|
|
// Sector the polyobject spawns in ()
|
|
Sector StartSector;
|
|
|
|
// Polyobject type (normal, crush, hurt)
|
|
EPolyobjType Type;
|
|
|
|
// Mirror Polyobject
|
|
swwm_PolyobjectHandle Mirror;
|
|
|
|
// Circular linked list of attached Polyobject Effectors
|
|
swwm_PolyobjectEffector EffectorList;
|
|
|
|
// Whether the initalization has finished
|
|
bool IsInitialized;
|
|
|
|
// Creates a PolyobjectHandle
|
|
static swwm_PolyobjectHandle Create()
|
|
{
|
|
swwm_PolyobjectHandle po = swwm_PolyobjectHandle(new('swwm_PolyobjectHandle'));
|
|
// Sets the underlying Thinker StatNum to 127 so that LastPos, LastAngle etc. get
|
|
// updated after all the other thinkers.
|
|
po.ChangeStatNum(127);
|
|
|
|
return po;
|
|
}
|
|
|
|
// Returns a PolyobjectHandle corresponding to the provided polyobject number
|
|
// Returns NULL if no such handler exists.
|
|
static swwm_PolyobjectHandle FindPolyobj(int pobjnum)
|
|
{
|
|
swwm_PolyobjectHandle po;
|
|
let it = ThinkerIterator.Create('swwm_PolyobjectHandle');
|
|
while ((po = swwm_PolyobjectHandle(it.Next())) != NULL)
|
|
{
|
|
if (po.PolyobjectNum == pobjnum)
|
|
return po;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Adds effector to the end of current effector list
|
|
void AddEffector(swwm_PolyobjectEffector effector)
|
|
{
|
|
|
|
effector.Polyobject = self;
|
|
|
|
// If effector list is empty, new effector becomes head of the list
|
|
if (EffectorList == NULL)
|
|
{
|
|
EffectorList = effector;
|
|
effector.Next = effector;
|
|
}
|
|
else
|
|
{
|
|
swwm_PolyobjectEffector e = EffectorList;
|
|
// Go through every effector until the last item
|
|
while (e.Next != EffectorList)
|
|
{
|
|
e = e.Next;
|
|
}
|
|
effector.Next = EffectorList;
|
|
e.Next = effector;
|
|
}
|
|
if (IsInitialized)
|
|
{
|
|
// If we're initialized, run the OnAdd effect immediately
|
|
effector.OnAdd();
|
|
}
|
|
}
|
|
|
|
// Finds effector of specified class, and returns it
|
|
swwm_PolyobjectEffector FindEffector(class<swwm_PolyobjectEffector> effectorclass)
|
|
{
|
|
// No effectors? Nothing to find then
|
|
if (EffectorList == NULL)
|
|
return NULL;
|
|
|
|
// Go through each effector
|
|
swwm_PolyobjectEffector e = EffectorList;
|
|
do
|
|
{
|
|
|
|
// Effector is of specified class, return it
|
|
if (e is effectorclass)
|
|
return e;
|
|
|
|
e = e.Next;
|
|
}
|
|
while (e != EffectorList);
|
|
return NULL;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
// Initialization shouldn't happen when reentering the map
|
|
if (Level.Time > 0)
|
|
return;
|
|
|
|
// Map has no lines corresponding to this polyobject, destroy the handle
|
|
if (StartLine == NULL)
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
|
|
// Using polyobject linedefs is the only way to track polyobject movements.
|
|
// All geometric calculations will be done relative to StartLine.
|
|
|
|
// Store initial position and angle
|
|
StartAngle = VectorAngle(StartLine.Delta.x, StartLine.Delta.y);
|
|
VertexStartingPos[0] = VertexLastPos[0] = StartLine.v1.p;
|
|
VertexStartingPos[1] = VertexLastPos[1] = StartLine.v2.p;
|
|
|
|
// Now that the map is loaded, it's safe to call effectors' OnAdd() methods
|
|
swwm_PolyobjectEffector e = EffectorList;
|
|
if (e != NULL)
|
|
{
|
|
do
|
|
{
|
|
e.OnAdd();
|
|
e = e.Next;
|
|
}
|
|
while (e != EffectorList);
|
|
}
|
|
|
|
// Done initializing
|
|
IsInitialized = true;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
// Call PolyTick() for each effector
|
|
if (EffectorList != NULL)
|
|
{
|
|
swwm_PolyobjectEffector e = EffectorList;
|
|
do
|
|
{
|
|
e.PolyTick();
|
|
e = e.Next;
|
|
}
|
|
while (e != EffectorList);
|
|
}
|
|
|
|
// Store current position/angle to be used during the next tic
|
|
VertexLastPos[0] = StartLine.v1.p;
|
|
VertexLastPos[1] = StartLine.v2.p;
|
|
LastAngle = GetAngle();
|
|
LastPos = GetPos();
|
|
}
|
|
|
|
override void OnDestroy()
|
|
{
|
|
// Clean up effectors first
|
|
swwm_PolyobjectEffector e = EffectorList;
|
|
if (e != NULL)
|
|
{
|
|
swwm_PolyobjectEffector next;
|
|
do
|
|
{
|
|
next = e.Next;
|
|
e.Destroy();
|
|
e = next;
|
|
}
|
|
while (e != EffectorList);
|
|
}
|
|
}
|
|
|
|
Sector GetSector()
|
|
{
|
|
if (StartSector == NULL)
|
|
{
|
|
Vector2 SpotPos = StartSpotPos;
|
|
|
|
// Sometimes if StartSpot lies on a one-sided linedef, its position is considered
|
|
// out of bounds by GZDoom, which makes Level.PointInSector() produce unexpected
|
|
// results. In that case, we need to compensate.
|
|
if (!Level.IsPointInLevel((SpotPos, z)))
|
|
{
|
|
// Look at points in a 5x5 square around the StartSpot
|
|
for (int x = -2; x <= 2; x++)
|
|
{
|
|
for (int y = -2; y <= 2; y++)
|
|
{
|
|
SpotPos = StartSpotPos + (x, y);
|
|
|
|
if (Level.IsPointInLevel((SpotPos, z)))
|
|
{
|
|
// Found a point within bounds, should be good enough
|
|
StartSector = Level.PointInSector(SpotPos);
|
|
break;
|
|
}
|
|
}
|
|
if (StartSector)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StartSector = Level.PointInSector(SpotPos);
|
|
}
|
|
}
|
|
return StartSector;
|
|
}
|
|
|
|
// Returns initial StartSpot position
|
|
Vector2 GetOrigin()
|
|
{
|
|
return StartSpotPos;
|
|
}
|
|
|
|
// Returns current polyobject angle
|
|
double GetAngle()
|
|
{
|
|
double lineangle = VectorAngle(StartLine.Delta.x, StartLine.Delta.y);
|
|
return Actor.DeltaAngle(StartAngle, lineangle);
|
|
}
|
|
|
|
// Returns current polyobject startspot position
|
|
Vector2 GetPos()
|
|
{
|
|
let spotdelta = StartSpotPos - VertexStartingPos[0];
|
|
return StartLine.v1.p + Actor.RotateVector(spotdelta, GetAngle());
|
|
}
|
|
|
|
// Returns polyobject coordinates relative to the startspot
|
|
Vector2 GetPosDelta()
|
|
{
|
|
return GetPos() - StartSpotPos;
|
|
}
|
|
|
|
// Returns last polyobject angle
|
|
double GetLastAngle()
|
|
{
|
|
return LastAngle;
|
|
}
|
|
|
|
// Returns last polyobject startspot position
|
|
Vector2 GetLastPos()
|
|
{
|
|
return LastPos;
|
|
}
|
|
|
|
// Returns last coordinates relative to the startspot
|
|
Vector2 GetLastPosDelta()
|
|
{
|
|
return LastPos - StartSpotPos;
|
|
}
|
|
|
|
// Returns current polyobject velocity
|
|
Vector2 GetVel()
|
|
{
|
|
return StartLine.v1.p - VertexLastPos[0];
|
|
}
|
|
|
|
// Returns current polyobject rotation speed
|
|
double GetRotationSpeed()
|
|
{
|
|
return GetAngle() - LastAngle;
|
|
}
|
|
|
|
// Returns whether the polyobject has moved from its spawn position
|
|
bool IsAtOrigin()
|
|
{
|
|
return (VertexStartingPos[0] == StartLine.v1.p && VertexStartingPos[1] == StartLine.v2.p);
|
|
}
|
|
|
|
// Returns whether the polyobject is in motion
|
|
bool IsMoving()
|
|
{
|
|
return (GetPos() != GetLastPos() || GetAngle() != GetLastAngle());
|
|
}
|
|
|
|
// Moves the polyobject to specified location, with specified speed, and plays the
|
|
// specified sound of its sound sequence
|
|
// (i.e. for a door sound sequence, sndseqmode 0 plays the open sound, 1 plays the
|
|
// closing sound)
|
|
void MoveTo(Actor activator, Vector2 dest, int Speed, int sndseqmode = 0)
|
|
{
|
|
// Stop any polyobject movement
|
|
Level.ExecuteSpecial(Polyobj_Stop, activator, StartLine, Line.Front, PolyobjectNum);
|
|
// Move the polyobject
|
|
Level.ExecuteSpecial(Polyobj_OR_MoveTo, activator, StartLine, Line.Front, PolyobjectNum,
|
|
Speed, int(dest.x), int(dest.y));
|
|
|
|
// Polyobj_OR_MoveTo ignores the sound sequence set by the polyobject.
|
|
// Play the sound sequence manually inside the sector containing the polyobject.
|
|
if (SoundSequenceNum)
|
|
{
|
|
GetSector().StartSoundSequenceID(CHAN_AUTO, SoundSequenceNum, SeqNode.DOOR, sndseqmode, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Class for iterating over polyobjects
|
|
class swwm_PolyobjectIterator: Object
|
|
{
|
|
private ThinkerIterator it;
|
|
static swwm_PolyobjectIterator Create()
|
|
{
|
|
let it = New('swwm_PolyobjectIterator');
|
|
it.it = ThinkerIterator.Create('swwm_PolyobjectHandle');
|
|
return it;
|
|
}
|
|
|
|
swwm_PolyobjectHandle Next()
|
|
{
|
|
return swwm_PolyobjectHandle(it.Next());
|
|
}
|
|
|
|
void Reinit()
|
|
{
|
|
it.Reinit();
|
|
}
|
|
}
|