Revert "Remove ZPolyobject due to licensing concerns."
This reverts commit f134375f7e.
Library is now MIT licensed and can be included.
This commit is contained in:
parent
564c99782a
commit
0fbbb91b8e
15 changed files with 566 additions and 20 deletions
7
zscript/swwm_Polyobjects/LICENSE.txt
Normal file
7
zscript/swwm_Polyobjects/LICENSE.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright (c) 2020 Mykola Ambartsumov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
48
zscript/swwm_Polyobjects/PolyobjectEffector.zs
Normal file
48
zscript/swwm_Polyobjects/PolyobjectEffector.zs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
class swwm_PolyobjectEffector: Thinker abstract
|
||||
{
|
||||
// Base abstract class for Polyobject Effectors
|
||||
// Polyobject Effectors affect how a polyobject behaves.
|
||||
// Polyobject Effectors contain a pointer to the next Effector of a polyobject,
|
||||
// forming a circular linked list.
|
||||
// To add an effector to a polyobject, call AddEffector() on a PolyobjectHandle.
|
||||
// To remove an effector, simply call Destroy() on it.
|
||||
|
||||
swwm_PolyobjectHandle Polyobject;
|
||||
swwm_PolyobjectEffector Next;
|
||||
|
||||
// OnAdd() is called once after adding the effector to a PolyobjectHandle
|
||||
virtual void OnAdd()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// PolyTick() is called every tic by a PolyobjectHandle
|
||||
virtual void PolyTick()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
override void OnDestroy()
|
||||
{
|
||||
swwm_PolyobjectEffector e = Polyobject.EffectorList;
|
||||
if (e != NULL)
|
||||
{
|
||||
// Find previous effector
|
||||
while (e && e.Next != self)
|
||||
{
|
||||
e = e.Next;
|
||||
}
|
||||
|
||||
// Link previous effector to the next effector
|
||||
e.Next = Next;
|
||||
|
||||
// Check if this effector is the last one
|
||||
if (e == self)
|
||||
{
|
||||
// Polyobject has no other effectors, set EffectorList to NULL
|
||||
Polyobject.EffectorList = NULL;
|
||||
}
|
||||
}
|
||||
Super.OnDestroy();
|
||||
}
|
||||
}
|
||||
359
zscript/swwm_Polyobjects/PolyobjectHandle.zs
Normal file
359
zscript/swwm_Polyobjects/PolyobjectHandle.zs
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
123
zscript/swwm_Polyobjects/PolyobjectMapPostprocessor.zs
Normal file
123
zscript/swwm_Polyobjects/PolyobjectMapPostprocessor.zs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// Creates a PolyobjectHandle for every polyobject in the map
|
||||
class swwm_PolyobjectHandlePostProcessor: LevelPostProcessor
|
||||
{
|
||||
protected void Apply(Name checksum, String mapname)
|
||||
{
|
||||
Array<int> pobjnums;
|
||||
Array<swwm_PolyobjectHandle> pobjhandles;
|
||||
|
||||
// Make sure initialization doesn't happen when reentering a map
|
||||
if (Level.Time > 0)
|
||||
return;
|
||||
|
||||
// Look for Polyobject StartSpots and create a handle for each
|
||||
for (uint i = 0; i < GetThingCount(); i++)
|
||||
{
|
||||
// Ignore every thing that isn't a Polyobject StartSpot
|
||||
int ednum = GetThingEdNum(i);
|
||||
// [MK] hotfix for this to recognize hexen polyobjects
|
||||
if (gameinfo.gametype&GAME_Hexen)
|
||||
{
|
||||
if (ednum == 3001) ednum = swwm_PolyobjectHandle.POTYP_NORMAL;
|
||||
else if (ednum == 3002) ednum = swwm_PolyobjectHandle.POTYP_CRUSH;
|
||||
}
|
||||
if (ednum < swwm_PolyobjectHandle.POTYP_NORMAL || ednum > swwm_PolyobjectHandle.POTYP_HURT)
|
||||
continue;
|
||||
|
||||
// Create a PolyobjectHandle
|
||||
swwm_PolyobjectHandle handle = swwm_PolyobjectHandle.Create();
|
||||
|
||||
// Get polyobject number from StartSpot angle
|
||||
handle.PolyobjectNum = GetThingAngle(i);
|
||||
|
||||
// Store StartSpot position
|
||||
Vector3 pos = GetThingPos(i);
|
||||
handle.StartSpotPos = pos.xy;
|
||||
handle.z = pos.z;
|
||||
|
||||
// Store StartSpot type (normal, crush, hurt)
|
||||
handle.Type = ednum;
|
||||
|
||||
// Append polyobject number and corresponding handle to the respective arrays
|
||||
pobjnums.Push(handle.PolyobjectNum);
|
||||
pobjhandles.Push(handle);
|
||||
}
|
||||
|
||||
// Look for Polyobj_StartLine/Polyobj_ExplicitLine lines
|
||||
for (int i = 0; i < Level.Lines.Size(); i++)
|
||||
{
|
||||
Line line = Level.Lines[i];
|
||||
|
||||
// Ignore every line that doesn't have a Polyobj_StartLine or Polyobj_ExplicitLine
|
||||
// line special
|
||||
if (line.Special != Polyobj_StartLine && line.Special != Polyobj_ExplicitLine)
|
||||
continue;
|
||||
|
||||
// Get polyobject number
|
||||
// (Args[0] for both Polyobj_StartLine and Polyobj_ExplicitLine)
|
||||
int pobjnum = line.Args[0];
|
||||
|
||||
// Find the array index of the corresponding handle
|
||||
int pobjhandleindex = pobjnums.Find(pobjnum);
|
||||
if (pobjhandleindex >= pobjnums.Size())
|
||||
continue; // Polyobject doesn't have a corresponding StartSpot
|
||||
|
||||
swwm_PolyobjectHandle handle = pobjhandles[pobjhandleindex];
|
||||
|
||||
// Get mirror polyobject number
|
||||
// (Args[1] for Polyobj_StartLine, Args[2] for Polyobj_ExplicitLine)
|
||||
int mirrorpobjnum = line.Special == Polyobj_StartLine ? line.Args[1] : line.Args[2];
|
||||
if (mirrorpobjnum != 0)
|
||||
{
|
||||
// Find the array index of the mirror polyobject handle
|
||||
int mirrorpobjhandleindex = pobjnums.Find(mirrorpobjnum);
|
||||
if (mirrorpobjhandleindex < pobjnums.Size())
|
||||
{
|
||||
// Mirror polyobject handle exists, store it
|
||||
handle.Mirror = pobjhandles[mirrorpobjhandleindex];
|
||||
}
|
||||
}
|
||||
|
||||
// Get sound sequence number and store it
|
||||
// (Args[2] for Polyobj_StartLine, Args[3] for Polyobj_ExplicitLine)
|
||||
int soundseq = line.Special == Polyobj_StartLine ? line.Args[2] : line.Args[3];
|
||||
handle.SoundSequenceNum = soundseq;
|
||||
|
||||
// Store the line
|
||||
handle.StartLine = line;
|
||||
|
||||
// [MK] the library doesn't store ALL lines belonging to the polyobject, but we need them
|
||||
handle.Lines.Push(line);
|
||||
|
||||
// [MK] collect all connected lines if this is Polyobj_StartLine
|
||||
if ( line.Special != Polyobj_StartLine )
|
||||
continue;
|
||||
|
||||
bool newlines;
|
||||
do
|
||||
{
|
||||
newlines = false;
|
||||
for (int j = 0; j < Level.Lines.Size(); j++)
|
||||
{
|
||||
Line linea = Level.Lines[j];
|
||||
if (handle.Lines.Find(linea) < handle.Lines.Size())
|
||||
continue;
|
||||
bool nomatches = true;
|
||||
for (int k = 0; k < handle.Lines.Size(); k++)
|
||||
{
|
||||
Line lineb = handle.Lines[k];
|
||||
if ((linea.v1 != lineb.v1) && (linea.v1 != lineb.v2) && (linea.v2 != lineb.v1) && (linea.v2 != lineb.v2))
|
||||
continue;
|
||||
nomatches = false;
|
||||
break;
|
||||
}
|
||||
if (nomatches)
|
||||
continue;
|
||||
newlines = true;
|
||||
handle.Lines.Push(linea);
|
||||
}
|
||||
}
|
||||
while (newlines);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
zscript/swwm_Polyobjects/Polyobjects.zs
Normal file
3
zscript/swwm_Polyobjects/Polyobjects.zs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#include "zscript/swwm_Polyobjects/PolyobjectHandle.zs"
|
||||
#include "zscript/swwm_Polyobjects/PolyobjectMapPostprocessor.zs"
|
||||
#include "zscript/swwm_Polyobjects/PolyobjectEffector.zs"
|
||||
Loading…
Add table
Add a link
Reference in a new issue