mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-11 07:11:54 +00:00
Merge remote-tracking branch 'gzdoom/master' into qzdoom
This commit is contained in:
commit
1c4e0c6385
24 changed files with 955 additions and 799 deletions
|
@ -1210,10 +1210,8 @@ set (PCH_SOURCES
|
|||
g_shared/a_action.cpp
|
||||
g_shared/a_decals.cpp
|
||||
g_shared/a_flashfader.cpp
|
||||
g_shared/a_fountain.cpp
|
||||
g_shared/a_lightning.cpp
|
||||
g_shared/a_morph.cpp
|
||||
g_shared/a_movingcamera.cpp
|
||||
g_shared/a_quake.cpp
|
||||
g_shared/a_randomspawner.cpp
|
||||
g_shared/a_skies.cpp
|
||||
|
|
|
@ -1014,12 +1014,13 @@ public:
|
|||
double FloatSpeed;
|
||||
|
||||
int sprite; // used to find patch_t and flip value
|
||||
BYTE frame; // sprite frame to draw
|
||||
uint8_t frame; // sprite frame to draw
|
||||
uint8_t effects; // [RH] see p_effect.h
|
||||
uint8_t fountaincolor; // Split out of 'effect' to have easier access.
|
||||
DVector2 Scale; // Scaling values; 1 is normal size
|
||||
FRenderStyle RenderStyle; // Style to draw this actor with
|
||||
ActorRenderFlags renderflags; // Different rendering flags
|
||||
FTextureID picnum; // Draw this instead of sprite if valid
|
||||
DWORD effects; // [RH] see p_effect.h
|
||||
double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
|
||||
DWORD fillcolor; // Color to draw when STYLE_Shaded
|
||||
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
** a_fountain.cpp
|
||||
** Actors that make particle fountains
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "actor.h"
|
||||
#include "info.h"
|
||||
#include "p_effect.h"
|
||||
#include "doomdata.h"
|
||||
|
||||
class AParticleFountain : public AActor
|
||||
{
|
||||
DECLARE_CLASS (AParticleFountain, AActor)
|
||||
public:
|
||||
void PostBeginPlay ();
|
||||
void Activate (AActor *activator);
|
||||
void Deactivate (AActor *activator);
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(AParticleFountain, false, false)
|
||||
|
||||
void AParticleFountain::PostBeginPlay ()
|
||||
{
|
||||
Super::PostBeginPlay ();
|
||||
if (!(SpawnFlags & MTF_DORMANT))
|
||||
CallActivate (NULL);
|
||||
}
|
||||
|
||||
void AParticleFountain::Activate (AActor *activator)
|
||||
{
|
||||
Super::Activate (activator);
|
||||
effects &= ~FX_FOUNTAINMASK;
|
||||
effects |= health << FX_FOUNTAINSHIFT;
|
||||
}
|
||||
|
||||
void AParticleFountain::Deactivate (AActor *activator)
|
||||
{
|
||||
Super::Deactivate (activator);
|
||||
effects &= ~FX_FOUNTAINMASK;
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
|
|||
morphed->flags |= actor->flags & (MF_SHADOW|MF_NOGRAVITY);
|
||||
morphed->flags2 |= actor->flags2 & MF2_FLY;
|
||||
morphed->flags3 |= actor->flags3 & MF3_GHOST;
|
||||
AActor *eflash = Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
|
||||
AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
|
||||
actor->player = nullptr;
|
||||
actor->alternative = morphed;
|
||||
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
|
||||
|
@ -128,7 +128,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
|
|||
p->MorphedPlayerClass = spawntype;
|
||||
|
||||
p->MorphStyle = style;
|
||||
p->MorphExitFlash = (exit_flash) ? exit_flash : RUNTIME_CLASS(ATeleportFog);
|
||||
p->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog");
|
||||
p->health = morphed->health;
|
||||
p->mo = morphed;
|
||||
p->Vel.X = p->Vel.Y = 0;
|
||||
|
@ -416,7 +416,7 @@ bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int st
|
|||
|
||||
morphed->UnmorphTime = level.time + ((duration) ? duration : MORPHTICS) + pr_morphmonst();
|
||||
morphed->MorphStyle = style;
|
||||
morphed->MorphExitFlash = (exit_flash) ? exit_flash : RUNTIME_CLASS(ATeleportFog);
|
||||
morphed->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog");
|
||||
morphed->FlagsSave = actor->flags & ~MF_JUSTHIT;
|
||||
morphed->special = actor->special;
|
||||
memcpy (morphed->args, actor->args, sizeof(actor->args));
|
||||
|
@ -434,7 +434,7 @@ bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int st
|
|||
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
|
||||
actor->flags |= MF_UNMORPHED;
|
||||
actor->renderflags |= RF_INVISIBLE;
|
||||
AActor *eflash = Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
|
||||
AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
|
||||
if (eflash)
|
||||
eflash->target = morphed;
|
||||
return true;
|
||||
|
|
|
@ -1,650 +0,0 @@
|
|||
/*
|
||||
** a_movingcamera.cpp
|
||||
** Cameras that move and related neat stuff
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "actor.h"
|
||||
#include "info.h"
|
||||
#include "p_local.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "doomstat.h"
|
||||
#include "serializer.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
/*
|
||||
== InterpolationPoint: node along a camera's path
|
||||
==
|
||||
== args[0] = pitch
|
||||
== args[1] = time (in octics) to get here from previous node
|
||||
== args[2] = time (in octics) to stay here before moving to next node
|
||||
== args[3] = low byte of next node's tid
|
||||
== args[4] = high byte of next node's tid
|
||||
*/
|
||||
|
||||
class AInterpolationPoint : public AActor
|
||||
{
|
||||
DECLARE_CLASS (AInterpolationPoint, AActor)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
void BeginPlay ();
|
||||
void HandleSpawnFlags ();
|
||||
void Tick () {} // Nodes do no thinking
|
||||
AInterpolationPoint *ScanForLoop ();
|
||||
void FormChain ();
|
||||
|
||||
|
||||
void Serialize(FSerializer &arc);
|
||||
|
||||
TObjPtr<AInterpolationPoint> Next;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(AInterpolationPoint, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(AInterpolationPoint)
|
||||
IMPLEMENT_POINTER(Next)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
void AInterpolationPoint::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc("next", Next);
|
||||
}
|
||||
|
||||
void AInterpolationPoint::BeginPlay ()
|
||||
{
|
||||
Super::BeginPlay ();
|
||||
Next = NULL;
|
||||
}
|
||||
|
||||
void AInterpolationPoint::HandleSpawnFlags ()
|
||||
{
|
||||
// Spawn flags mean nothing to an interpolation point
|
||||
}
|
||||
|
||||
void AInterpolationPoint::FormChain ()
|
||||
{
|
||||
if (flags & MF_AMBUSH)
|
||||
return;
|
||||
|
||||
flags |= MF_AMBUSH;
|
||||
|
||||
TActorIterator<AInterpolationPoint> iterator (args[3] + 256 * args[4]);
|
||||
Next = iterator.Next ();
|
||||
|
||||
if (Next == this) // Don't link to self
|
||||
Next = iterator.Next ();
|
||||
|
||||
if (Next == NULL && (args[3] | args[4]))
|
||||
Printf ("Can't find target for camera node %d\n", tid);
|
||||
|
||||
Angles.Pitch = (double)clamp<int>((signed char)args[0], -89, 89);
|
||||
|
||||
if (Next != NULL)
|
||||
Next->FormChain ();
|
||||
}
|
||||
|
||||
// Return the node (if any) where a path loops, relative to this one.
|
||||
AInterpolationPoint *AInterpolationPoint::ScanForLoop ()
|
||||
{
|
||||
AInterpolationPoint *node = this;
|
||||
while (node->Next && node->Next != this && node->special1 == 0)
|
||||
{
|
||||
node->special1 = 1;
|
||||
node = node->Next;
|
||||
}
|
||||
return node->Next == this ? node : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
== InterpolationSpecial: Holds a special to execute when a
|
||||
== PathFollower reaches an InterpolationPoint of the same TID.
|
||||
*/
|
||||
|
||||
class AInterpolationSpecial : public AActor
|
||||
{
|
||||
DECLARE_CLASS (AInterpolationSpecial, AActor)
|
||||
public:
|
||||
void Tick () {} // Does absolutely nothing itself
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(AInterpolationSpecial, false, false)
|
||||
|
||||
/*
|
||||
== PathFollower: something that follows a camera path
|
||||
== Base class for some moving cameras
|
||||
==
|
||||
== args[0] = low byte of first node in path's tid
|
||||
== args[1] = high byte of first node's tid
|
||||
== args[2] = bit 0 = follow a linear path (rather than curved)
|
||||
== bit 1 = adjust angle
|
||||
== bit 2 = adjust pitch
|
||||
== bit 3 = aim in direction of motion
|
||||
==
|
||||
== Also uses:
|
||||
== target = first node in path
|
||||
== lastenemy = node prior to first node (if looped)
|
||||
*/
|
||||
|
||||
class APathFollower : public AActor
|
||||
{
|
||||
DECLARE_CLASS (APathFollower, AActor)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
void BeginPlay ();
|
||||
void PostBeginPlay ();
|
||||
void Tick ();
|
||||
void Activate (AActor *activator);
|
||||
void Deactivate (AActor *activator);
|
||||
protected:
|
||||
double Splerp (double p1, double p2, double p3, double p4);
|
||||
double Lerp (double p1, double p2);
|
||||
virtual bool Interpolate ();
|
||||
virtual void NewNode ();
|
||||
|
||||
|
||||
void Serialize(FSerializer &arc);
|
||||
|
||||
bool bActive, bJustStepped;
|
||||
TObjPtr<AInterpolationPoint> PrevNode, CurrNode;
|
||||
float Time; // Runs from 0.0 to 1.0 between CurrNode and CurrNode->Next
|
||||
int HoldTime;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(APathFollower, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(APathFollower)
|
||||
IMPLEMENT_POINTER(PrevNode)
|
||||
IMPLEMENT_POINTER(CurrNode)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
void APathFollower::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc("active", bActive)
|
||||
("juststepped", bJustStepped)
|
||||
("prevnode", PrevNode)
|
||||
("currnode", CurrNode)
|
||||
("time", Time)
|
||||
("holdtime", HoldTime);
|
||||
}
|
||||
|
||||
// Interpolate between p2 and p3 along a Catmull-Rom spline
|
||||
// http://research.microsoft.com/~hollasch/cgindex/curves/catmull-rom.html
|
||||
double APathFollower::Splerp (double p1, double p2, double p3, double p4)
|
||||
{
|
||||
double t = Time;
|
||||
double res = 2*p2;
|
||||
res += (p3 - p1) * Time;
|
||||
t *= Time;
|
||||
res += (2*p1 - 5*p2 + 4*p3 - p4) * t;
|
||||
t *= Time;
|
||||
res += (3*p2 - 3*p3 + p4 - p1) * t;
|
||||
return 0.5f * res;
|
||||
}
|
||||
|
||||
// Linearly interpolate between p1 and p2
|
||||
double APathFollower::Lerp (double p1, double p2)
|
||||
{
|
||||
return p1 + Time * (p2 - p1);
|
||||
}
|
||||
|
||||
void APathFollower::BeginPlay ()
|
||||
{
|
||||
Super::BeginPlay ();
|
||||
PrevNode = CurrNode = NULL;
|
||||
bActive = false;
|
||||
}
|
||||
|
||||
void APathFollower::PostBeginPlay ()
|
||||
{
|
||||
// Find first node of path
|
||||
TActorIterator<AInterpolationPoint> iterator (args[0] + 256 * args[1]);
|
||||
AInterpolationPoint *node = iterator.Next ();
|
||||
AInterpolationPoint *prevnode;
|
||||
|
||||
target = node;
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
Printf ("PathFollower %d: Can't find interpolation pt %d\n",
|
||||
tid, args[0] + 256 * args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the path has enough nodes
|
||||
node->FormChain ();
|
||||
if (args[2] & 1)
|
||||
{ // linear path; need 2 nodes
|
||||
if (node->Next == NULL)
|
||||
{
|
||||
Printf ("PathFollower %d: Path needs at least 2 nodes\n", tid);
|
||||
return;
|
||||
}
|
||||
lastenemy = NULL;
|
||||
}
|
||||
else
|
||||
{ // spline path; need 4 nodes
|
||||
if (node->Next == NULL ||
|
||||
node->Next->Next == NULL ||
|
||||
node->Next->Next->Next == NULL)
|
||||
{
|
||||
Printf ("PathFollower %d: Path needs at least 4 nodes\n", tid);
|
||||
return;
|
||||
}
|
||||
// If the first node is in a loop, we can start there.
|
||||
// Otherwise, we need to start at the second node in the path.
|
||||
prevnode = node->ScanForLoop ();
|
||||
if (prevnode == NULL || prevnode->Next != node)
|
||||
{
|
||||
lastenemy = target;
|
||||
target = node->Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastenemy = prevnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APathFollower::Deactivate (AActor *activator)
|
||||
{
|
||||
bActive = false;
|
||||
}
|
||||
|
||||
void APathFollower::Activate (AActor *activator)
|
||||
{
|
||||
if (!bActive)
|
||||
{
|
||||
CurrNode = barrier_cast<AInterpolationPoint *>(target);
|
||||
PrevNode = barrier_cast<AInterpolationPoint *>(lastenemy);
|
||||
|
||||
if (CurrNode != NULL)
|
||||
{
|
||||
NewNode ();
|
||||
SetOrigin (CurrNode->Pos(), false);
|
||||
Time = 0.f;
|
||||
HoldTime = 0;
|
||||
bJustStepped = true;
|
||||
bActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APathFollower::Tick ()
|
||||
{
|
||||
if (!bActive)
|
||||
return;
|
||||
|
||||
if (bJustStepped)
|
||||
{
|
||||
bJustStepped = false;
|
||||
if (CurrNode->args[2])
|
||||
{
|
||||
HoldTime = level.time + CurrNode->args[2] * TICRATE / 8;
|
||||
SetXYZ(CurrNode->Pos());
|
||||
}
|
||||
}
|
||||
|
||||
if (HoldTime > level.time)
|
||||
return;
|
||||
|
||||
// Splines must have a previous node.
|
||||
if (PrevNode == NULL && !(args[2] & 1))
|
||||
{
|
||||
bActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// All paths must have a current node.
|
||||
if (CurrNode->Next == NULL)
|
||||
{
|
||||
bActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Interpolate ())
|
||||
{
|
||||
Time += float(8.f / ((double)CurrNode->args[1] * (double)TICRATE));
|
||||
if (Time > 1.f)
|
||||
{
|
||||
Time -= 1.f;
|
||||
bJustStepped = true;
|
||||
PrevNode = CurrNode;
|
||||
CurrNode = CurrNode->Next;
|
||||
if (CurrNode != NULL)
|
||||
NewNode ();
|
||||
if (CurrNode == NULL || CurrNode->Next == NULL)
|
||||
CallDeactivate (this);
|
||||
if ((args[2] & 1) == 0 && CurrNode->Next->Next == NULL)
|
||||
CallDeactivate (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APathFollower::NewNode ()
|
||||
{
|
||||
TActorIterator<AInterpolationSpecial> iterator (CurrNode->tid);
|
||||
AInterpolationSpecial *spec;
|
||||
|
||||
while ( (spec = iterator.Next ()) )
|
||||
{
|
||||
P_ExecuteSpecial(spec->special, NULL, NULL, false, spec->args[0],
|
||||
spec->args[1], spec->args[2], spec->args[3], spec->args[4]);
|
||||
}
|
||||
}
|
||||
|
||||
bool APathFollower::Interpolate ()
|
||||
{
|
||||
DVector3 dpos(0, 0, 0);
|
||||
FLinkContext ctx;
|
||||
|
||||
if ((args[2] & 8) && Time > 0.f)
|
||||
{
|
||||
dpos = Pos();
|
||||
}
|
||||
|
||||
if (CurrNode->Next==NULL) return false;
|
||||
|
||||
UnlinkFromWorld (&ctx);
|
||||
DVector3 newpos;
|
||||
if (args[2] & 1)
|
||||
{ // linear
|
||||
newpos.X = Lerp(CurrNode->X(), CurrNode->Next->X());
|
||||
newpos.Y = Lerp(CurrNode->Y(), CurrNode->Next->Y());
|
||||
newpos.Z = Lerp(CurrNode->Z(), CurrNode->Next->Z());
|
||||
}
|
||||
else
|
||||
{ // spline
|
||||
if (CurrNode->Next->Next==NULL) return false;
|
||||
|
||||
newpos.X = Splerp(PrevNode->X(), CurrNode->X(), CurrNode->Next->X(), CurrNode->Next->Next->X());
|
||||
newpos.Y = Splerp(PrevNode->Y(), CurrNode->Y(), CurrNode->Next->Y(), CurrNode->Next->Next->Y());
|
||||
newpos.Z = Splerp(PrevNode->Z(), CurrNode->Z(), CurrNode->Next->Z(), CurrNode->Next->Next->Z());
|
||||
}
|
||||
SetXYZ(newpos);
|
||||
LinkToWorld (&ctx);
|
||||
|
||||
if (args[2] & 6)
|
||||
{
|
||||
if (args[2] & 8)
|
||||
{
|
||||
if (args[2] & 1)
|
||||
{ // linear
|
||||
dpos.X = CurrNode->Next->X() - CurrNode->X();
|
||||
dpos.Y = CurrNode->Next->Y() - CurrNode->Y();
|
||||
dpos.Z = CurrNode->Next->Z() - CurrNode->Z();
|
||||
}
|
||||
else if (Time > 0.f)
|
||||
{ // spline
|
||||
dpos = newpos - dpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
int realarg = args[2];
|
||||
args[2] &= ~(2|4|8);
|
||||
Time += 0.1f;
|
||||
dpos = newpos;
|
||||
Interpolate ();
|
||||
Time -= 0.1f;
|
||||
args[2] = realarg;
|
||||
dpos = newpos - dpos;
|
||||
newpos -= dpos;
|
||||
SetXYZ(newpos);
|
||||
}
|
||||
if (args[2] & 2)
|
||||
{ // adjust yaw
|
||||
Angles.Yaw = dpos.Angle();
|
||||
}
|
||||
if (args[2] & 4)
|
||||
{ // adjust pitch; use floats for precision
|
||||
double dist = dpos.XY().Length();
|
||||
Angles.Pitch = dist != 0.f ? VecToAngle(dist, -dpos.Z) : 0.;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args[2] & 2)
|
||||
{ // interpolate angle
|
||||
DAngle angle1 = CurrNode->Angles.Yaw.Normalized180();
|
||||
DAngle angle2 = angle1 + deltaangle(angle1, CurrNode->Next->Angles.Yaw);
|
||||
Angles.Yaw = Lerp(angle1.Degrees, angle2.Degrees);
|
||||
}
|
||||
if (args[2] & 1)
|
||||
{ // linear
|
||||
if (args[2] & 4)
|
||||
{ // interpolate pitch
|
||||
Angles.Pitch = Lerp(CurrNode->Angles.Pitch.Degrees, CurrNode->Next->Angles.Pitch.Degrees);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // spline
|
||||
if (args[2] & 4)
|
||||
{ // interpolate pitch
|
||||
Angles.Pitch = Splerp(PrevNode->Angles.Pitch.Degrees, CurrNode->Angles.Pitch.Degrees,
|
||||
CurrNode->Next->Angles.Pitch.Degrees, CurrNode->Next->Next->Angles.Pitch.Degrees);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
== ActorMover: Moves any actor along a camera path
|
||||
==
|
||||
== Same as PathFollower, except
|
||||
== args[2], bit 7: make nonsolid
|
||||
== args[3] = tid of thing to move
|
||||
==
|
||||
== also uses:
|
||||
== tracer = thing to move
|
||||
*/
|
||||
|
||||
class AActorMover : public APathFollower
|
||||
{
|
||||
DECLARE_CLASS (AActorMover, APathFollower)
|
||||
public:
|
||||
void BeginPlay();
|
||||
void PostBeginPlay ();
|
||||
void Activate (AActor *activator);
|
||||
void Deactivate (AActor *activator);
|
||||
protected:
|
||||
bool Interpolate ();
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(AActorMover, false, false)
|
||||
|
||||
void AActorMover::BeginPlay()
|
||||
{
|
||||
ChangeStatNum(STAT_ACTORMOVER);
|
||||
}
|
||||
|
||||
void AActorMover::PostBeginPlay ()
|
||||
{
|
||||
Super::PostBeginPlay ();
|
||||
|
||||
TActorIterator<AActor> iterator (args[3]);
|
||||
tracer = iterator.Next ();
|
||||
|
||||
if (tracer == NULL)
|
||||
{
|
||||
Printf ("ActorMover %d: Can't find target %d\n", tid, args[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
special1 = tracer->flags;
|
||||
special2 = tracer->flags2;
|
||||
}
|
||||
}
|
||||
|
||||
bool AActorMover::Interpolate ()
|
||||
{
|
||||
if (tracer == NULL)
|
||||
return true;
|
||||
|
||||
if (Super::Interpolate ())
|
||||
{
|
||||
double savedz = tracer->Z();
|
||||
tracer->SetZ(Z());
|
||||
if (!P_TryMove (tracer, Pos(), true))
|
||||
{
|
||||
tracer->SetZ(savedz);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args[2] & 2)
|
||||
tracer->Angles.Yaw = Angles.Yaw;
|
||||
if (args[2] & 4)
|
||||
tracer->Angles.Pitch = Angles.Pitch;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AActorMover::Activate (AActor *activator)
|
||||
{
|
||||
if (tracer == NULL || bActive)
|
||||
return;
|
||||
|
||||
Super::Activate (activator);
|
||||
special1 = tracer->flags;
|
||||
special2 = tracer->flags2;
|
||||
tracer->flags |= MF_NOGRAVITY;
|
||||
if (args[2] & 128)
|
||||
{
|
||||
FLinkContext ctx;
|
||||
tracer->UnlinkFromWorld (&ctx);
|
||||
tracer->flags |= MF_NOBLOCKMAP;
|
||||
tracer->flags &= ~MF_SOLID;
|
||||
tracer->LinkToWorld (&ctx);
|
||||
}
|
||||
if (tracer->flags3 & MF3_ISMONSTER)
|
||||
{
|
||||
tracer->flags2 |= MF2_INVULNERABLE | MF2_DORMANT;
|
||||
}
|
||||
// Don't let the renderer interpolate between the actor's
|
||||
// old position and its new position.
|
||||
Interpolate ();
|
||||
tracer->ClearInterpolation();
|
||||
}
|
||||
|
||||
void AActorMover::Deactivate (AActor *activator)
|
||||
{
|
||||
if (bActive)
|
||||
{
|
||||
Super::Deactivate (activator);
|
||||
if (tracer != NULL)
|
||||
{
|
||||
FLinkContext ctx;
|
||||
tracer->UnlinkFromWorld (&ctx);
|
||||
tracer->flags = ActorFlags::FromInt (special1);
|
||||
tracer->LinkToWorld (&ctx);
|
||||
tracer->flags2 = ActorFlags2::FromInt (special2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
== MovingCamera: Moves any actor along a camera path
|
||||
==
|
||||
== Same as PathFollower, except
|
||||
== args[3] = tid of thing to look at (0 if none)
|
||||
==
|
||||
== Also uses:
|
||||
== tracer = thing to look at
|
||||
*/
|
||||
|
||||
class AMovingCamera : public APathFollower
|
||||
{
|
||||
DECLARE_CLASS (AMovingCamera, APathFollower)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
void PostBeginPlay ();
|
||||
|
||||
|
||||
void Serialize(FSerializer &arc);
|
||||
protected:
|
||||
bool Interpolate ();
|
||||
|
||||
TObjPtr<AActor> Activator;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(AMovingCamera, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(AMovingCamera)
|
||||
IMPLEMENT_POINTER(Activator)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
void AMovingCamera::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc("activator", Activator);
|
||||
}
|
||||
|
||||
void AMovingCamera::PostBeginPlay ()
|
||||
{
|
||||
Super::PostBeginPlay ();
|
||||
|
||||
Activator = NULL;
|
||||
if (args[3] != 0)
|
||||
{
|
||||
TActorIterator<AActor> iterator (args[3]);
|
||||
tracer = iterator.Next ();
|
||||
if (tracer == NULL)
|
||||
{
|
||||
Printf ("MovingCamera %d: Can't find thing %d\n", tid, args[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AMovingCamera::Interpolate ()
|
||||
{
|
||||
if (tracer == NULL)
|
||||
return Super::Interpolate ();
|
||||
|
||||
if (Super::Interpolate ())
|
||||
{
|
||||
Angles.Yaw = AngleTo(tracer, true);
|
||||
|
||||
if (args[2] & 4)
|
||||
{ // Also aim camera's pitch;
|
||||
DVector3 diff = Pos() - tracer->PosPlusZ(tracer->Height / 2);
|
||||
double dist = diff.XY().Length();
|
||||
Angles.Pitch = dist != 0.f ? VecToAngle(dist, diff.Z) : 0.;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -76,13 +76,6 @@ private:
|
|||
DImpactDecal();
|
||||
};
|
||||
|
||||
class ATeleportFog : public AActor
|
||||
{
|
||||
DECLARE_CLASS (ATeleportFog, AActor)
|
||||
public:
|
||||
void PostBeginPlay ();
|
||||
};
|
||||
|
||||
class ASkyViewpoint : public AActor
|
||||
{
|
||||
DECLARE_CLASS (ASkyViewpoint, AActor)
|
||||
|
|
|
@ -108,10 +108,10 @@ DEFINE_CLASS_PROPERTY(type, S, DynamicLight)
|
|||
// which is controlled by flags
|
||||
//
|
||||
//==========================================================================
|
||||
IMPLEMENT_CLASS (ADynamicLight, false, false)
|
||||
IMPLEMENT_CLASS (AVavoomLight, false, false)
|
||||
IMPLEMENT_CLASS (AVavoomLightWhite, false, false)
|
||||
IMPLEMENT_CLASS (AVavoomLightColor, false, false)
|
||||
IMPLEMENT_CLASS(ADynamicLight, false, false)
|
||||
IMPLEMENT_CLASS(AVavoomLight, false, false)
|
||||
IMPLEMENT_CLASS(AVavoomLightWhite, false, false)
|
||||
IMPLEMENT_CLASS(AVavoomLightColor, false, false)
|
||||
|
||||
void AVavoomLight::BeginPlay ()
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "p_lnspec.h"
|
||||
#include "p_local.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "r_sky.h"
|
||||
#include "p_effect.h"
|
||||
#include "po_man.h"
|
||||
|
|
|
@ -349,6 +349,7 @@ xx(VisibleStartAngle)
|
|||
xx(VisibleStartPitch)
|
||||
xx(VisibleEndAngle)
|
||||
xx(VisibleEndPitch)
|
||||
xx(Format)
|
||||
|
||||
// Various actor names which are used internally
|
||||
xx(MapSpot)
|
||||
|
@ -699,6 +700,7 @@ xx(BuiltinFindSingleNameState)
|
|||
xx(BuiltinHandleRuntimeState)
|
||||
xx(BuiltinGetDefault)
|
||||
xx(BuiltinClassCast)
|
||||
xx(BuiltinFormat)
|
||||
xx(Damage)
|
||||
|
||||
// basic type names
|
||||
|
|
|
@ -358,7 +358,7 @@ void P_RunEffects ()
|
|||
|
||||
while ( (actor = iterator.Next ()) )
|
||||
{
|
||||
if (actor->effects)
|
||||
if (actor->effects || actor->fountaincolor)
|
||||
{
|
||||
// Only run the effect if the actor is potentially visible
|
||||
int rnum = pnum + actor->Sector->Index();
|
||||
|
@ -494,7 +494,7 @@ void P_RunEffect (AActor *actor, int effects)
|
|||
|
||||
P_DrawSplash2 (6, pos, moveangle + 180, 2, 2);
|
||||
}
|
||||
if (effects & FX_FOUNTAINMASK)
|
||||
if (actor->fountaincolor)
|
||||
{
|
||||
// Particle fountain
|
||||
|
||||
|
@ -508,7 +508,7 @@ void P_RunEffect (AActor *actor, int effects)
|
|||
&black, &grey3,
|
||||
&grey4, &white
|
||||
};
|
||||
int color = (effects & FX_FOUNTAINMASK) >> 15;
|
||||
int color = actor->fountaincolor*2;
|
||||
MakeFountain (actor, *fountainColors[color], *fountainColors[color+1]);
|
||||
}
|
||||
if (effects & FX_RESPAWNINVUL)
|
||||
|
|
|
@ -40,16 +40,6 @@
|
|||
#define FX_RESPAWNINVUL 0x00000020
|
||||
#define FX_VISIBILITYPULSE 0x00000040
|
||||
|
||||
#define FX_FOUNTAINMASK 0x00070000
|
||||
#define FX_FOUNTAINSHIFT 16
|
||||
#define FX_REDFOUNTAIN 0x00010000
|
||||
#define FX_GREENFOUNTAIN 0x00020000
|
||||
#define FX_BLUEFOUNTAIN 0x00030000
|
||||
#define FX_YELLOWFOUNTAIN 0x00040000
|
||||
#define FX_PURPLEFOUNTAIN 0x00050000
|
||||
#define FX_BLACKFOUNTAIN 0x00060000
|
||||
#define FX_WHITEFOUNTAIN 0x00070000
|
||||
|
||||
struct subsector_t;
|
||||
|
||||
// [RH] Particle details
|
||||
|
|
|
@ -396,7 +396,8 @@ bool AActor::FixMapthingPos()
|
|||
DEFINE_ACTION_FUNCTION(AActor, UnlinkFromWorld)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->UnlinkFromWorld(nullptr); // fixme
|
||||
PARAM_POINTER_DEF(ctx, FLinkContext);
|
||||
self->UnlinkFromWorld(ctx); // fixme
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -530,7 +531,8 @@ void AActor::LinkToWorld(FLinkContext *ctx, bool spawningmapthing, sector_t *sec
|
|||
DEFINE_ACTION_FUNCTION(AActor, LinkToWorld)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->LinkToWorld(nullptr); // fixme
|
||||
PARAM_POINTER_DEF(ctx, FLinkContext);
|
||||
self->LinkToWorld(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -314,6 +314,7 @@ DEFINE_FIELD(AActor, MissileState)
|
|||
DEFINE_FIELD(AActor, ConversationRoot)
|
||||
DEFINE_FIELD(AActor, Conversation)
|
||||
DEFINE_FIELD(AActor, DecalGenerator)
|
||||
DEFINE_FIELD(AActor, fountaincolor)
|
||||
|
||||
DEFINE_FIELD(PClassActor, Obituary)
|
||||
DEFINE_FIELD(PClassActor, HitObituary)
|
||||
|
@ -366,6 +367,7 @@ void AActor::Serialize(FSerializer &arc)
|
|||
A("lastlookpn", LastLookPlayerNumber)
|
||||
("lastlookactor", LastLookActor)
|
||||
A("effects", effects)
|
||||
A("fountaincolor", fountaincolor)
|
||||
A("alpha", Alpha)
|
||||
A("fillcolor", fillcolor)
|
||||
A("sector", Sector)
|
||||
|
@ -7752,6 +7754,13 @@ DEFINE_ACTION_FUNCTION(AActor, RotateVector)
|
|||
ACTION_RETURN_VEC2(DVector2(x, y).Rotated(angle));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, Normalize180)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_ANGLE(angle);
|
||||
ACTION_RETURN_FLOAT(angle.Normalized180().Degrees);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, DistanceBySpeed)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
|
|
@ -275,6 +275,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t
|
|||
("linked_floor", p.e->Linked.Floor.Sectors)
|
||||
("linked_ceiling", p.e->Linked.Ceiling.Sectors)
|
||||
("colormap", p.ColorMap, def->ColorMap)
|
||||
("gravity", p.gravity, def->gravity)
|
||||
.Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0])
|
||||
.Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1])
|
||||
("scrolls", scroll, nul)
|
||||
|
|
|
@ -1455,7 +1455,7 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt)
|
|||
|
||||
bool sector_t::TriggerSectorActions(AActor *thing, int activation)
|
||||
{
|
||||
auto act = SecActTarget;
|
||||
AActor *act = SecActTarget;
|
||||
bool res = false;
|
||||
|
||||
while (act != nullptr)
|
||||
|
@ -1464,7 +1464,7 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt)
|
|||
|
||||
IFVIRTUALPTRNAME(act, "SectorAction", TriggerAction)
|
||||
{
|
||||
VMValue params[3] = { act, thing, activation };
|
||||
VMValue params[3] = { (DObject *)act, thing, activation };
|
||||
VMReturn ret;
|
||||
int didit;
|
||||
ret.IntAt(&didit);
|
||||
|
|
|
@ -48,28 +48,6 @@ extern void P_CalcHeight (player_t *player);
|
|||
|
||||
CVAR (Bool, telezoom, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
|
||||
IMPLEMENT_CLASS(ATeleportFog, false, false)
|
||||
|
||||
void ATeleportFog::PostBeginPlay ()
|
||||
{
|
||||
Super::PostBeginPlay ();
|
||||
S_Sound (this, CHAN_BODY, "misc/teleport", 1, ATTN_NORM);
|
||||
switch (gameinfo.gametype)
|
||||
{
|
||||
case GAME_Hexen:
|
||||
case GAME_Heretic:
|
||||
SetState(FindState(NAME_Raven));
|
||||
break;
|
||||
|
||||
case GAME_Strife:
|
||||
SetState(FindState(NAME_Strife));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// P_SpawnTeleportFog
|
||||
|
|
|
@ -7115,6 +7115,14 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
// [ZZ] string formatting function
|
||||
if (MethodName == NAME_Format)
|
||||
{
|
||||
FxExpression *x = new FxFormat(ArgList, ScriptPosition);
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
|
||||
int min, max, special;
|
||||
if (MethodName == NAME_ACS_NamedExecuteWithResult || MethodName == NAME_CallACS)
|
||||
{
|
||||
|
@ -8341,6 +8349,279 @@ ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build)
|
|||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxFormat::FxFormat(FArgumentList &args, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_Format, pos)
|
||||
{
|
||||
EmitTail = false;
|
||||
ArgList = std::move(args);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxFormat::~FxFormat()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PPrototype *FxFormat::ReturnProto()
|
||||
{
|
||||
EmitTail = true;
|
||||
return FxExpression::ReturnProto();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxFormat::Resolve(FCompileContext& ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
|
||||
for (int i = 0; i < ArgList.Size(); i++)
|
||||
{
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// first argument should be a string
|
||||
if (!i && ArgList[i]->ValueType != TypeString)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "String was expected for format");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ArgList[i]->ValueType == TypeName ||
|
||||
ArgList[i]->ValueType == TypeSound)
|
||||
{
|
||||
FxExpression* x = new FxStringCast(ArgList[i]);
|
||||
x = x->Resolve(ctx);
|
||||
if (x == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
ArgList[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
ValueType = TypeString;
|
||||
return this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static int BuiltinFormat(VMValue *args, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
|
||||
{
|
||||
assert(args[0].Type == REGT_STRING);
|
||||
FString fmtstring = args[0].s().GetChars();
|
||||
|
||||
// note: we don't need a real printf format parser.
|
||||
// enough to simply find the subtitution tokens and feed them to the real printf after checking types.
|
||||
// https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification
|
||||
FString output;
|
||||
bool in_fmt = false;
|
||||
FString fmt_current;
|
||||
int argnum = 1;
|
||||
int argauto = 1;
|
||||
// % = starts
|
||||
// [0-9], -, +, \s, 0, #, . continue
|
||||
// %, s, d, i, u, fF, eE, gG, xX, o, c, p, aA terminate
|
||||
// various type flags are not supported. not like stuff like 'hh' modifier is to be used in the VM.
|
||||
// the only combination that is parsed locally is %n$...
|
||||
bool haveargnums = false;
|
||||
for (int i = 0; i < fmtstring.Len(); i++)
|
||||
{
|
||||
char c = fmtstring[i];
|
||||
if (in_fmt)
|
||||
{
|
||||
if ((c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '+' || (c == ' ' && fmt_current[fmt_current.Len() - 1] != ' ') || c == '#' || c == '.')
|
||||
{
|
||||
fmt_current += c;
|
||||
}
|
||||
else if (c == '$') // %number$format
|
||||
{
|
||||
if (!haveargnums && argauto > 1)
|
||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||
FString argnumstr = fmt_current.Mid(1);
|
||||
if (!argnumstr.IsInt()) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for argument number, got '%s'.", argnumstr.GetChars());
|
||||
argnum = argnumstr.ToLong();
|
||||
if (argnum < 1 || argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format (tried to access argument %d, %d total).", argnum, numparam);
|
||||
fmt_current = "%";
|
||||
haveargnums = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt_current += c;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
// string
|
||||
case 's':
|
||||
{
|
||||
if (argnum < 0 && haveargnums)
|
||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||
in_fmt = false;
|
||||
// fail if something was found, but it's not a string
|
||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||
if (args[argnum].Type != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars());
|
||||
// append
|
||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].s().GetChars());
|
||||
if (!haveargnums) argnum = ++argauto;
|
||||
else argnum = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// pointer
|
||||
case 'p':
|
||||
{
|
||||
if (argnum < 0 && haveargnums)
|
||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||
in_fmt = false;
|
||||
// fail if something was found, but it's not a string
|
||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||
if (args[argnum].Type != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars());
|
||||
// append
|
||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].a);
|
||||
if (!haveargnums) argnum = ++argauto;
|
||||
else argnum = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// int formats (including char)
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'c':
|
||||
{
|
||||
if (argnum < 0 && haveargnums)
|
||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||
in_fmt = false;
|
||||
// fail if something was found, but it's not an int
|
||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||
if (args[argnum].Type != REGT_INT &&
|
||||
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
|
||||
// append
|
||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToInt());
|
||||
if (!haveargnums) argnum = ++argauto;
|
||||
else argnum = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// double formats
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'g':
|
||||
case 'G':
|
||||
case 'a':
|
||||
case 'A':
|
||||
{
|
||||
if (argnum < 0 && haveargnums)
|
||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||
in_fmt = false;
|
||||
// fail if something was found, but it's not a float
|
||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||
if (args[argnum].Type != REGT_INT &&
|
||||
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
|
||||
// append
|
||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToDouble());
|
||||
if (!haveargnums) argnum = ++argauto;
|
||||
else argnum = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// invalid character
|
||||
output += fmt_current;
|
||||
in_fmt = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '%')
|
||||
{
|
||||
if (i + 1 < fmtstring.Len() && fmtstring[i + 1] == '%')
|
||||
{
|
||||
output += '%';
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_fmt = true;
|
||||
fmt_current = "%";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_RETURN_STRING(output);
|
||||
}
|
||||
|
||||
ExpEmit FxFormat::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
// Call DecoRandom to generate a random number.
|
||||
VMFunction *callfunc;
|
||||
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinFormat, BuiltinFormat);
|
||||
|
||||
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
|
||||
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
|
||||
callfunc = ((PSymbolVMFunction *)sym)->Function;
|
||||
|
||||
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
|
||||
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
|
||||
|
||||
for (int i = 0; i < ArgList.Size(); i++)
|
||||
EmitParameter(build, ArgList[i], ScriptPosition);
|
||||
build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), ArgList.Size(), 1);
|
||||
|
||||
if (EmitTail)
|
||||
{
|
||||
ExpEmit call;
|
||||
call.Final = true;
|
||||
return call;
|
||||
}
|
||||
|
||||
ExpEmit out(build, REGT_STRING);
|
||||
build->Emit(OP_RESULT, 0, REGT_STRING, out.RegNum);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
@ -255,6 +255,7 @@ enum EFxType
|
|||
EFX_MemberFunctionCall,
|
||||
EFX_ActionSpecialCall,
|
||||
EFX_FlopFunctionCall,
|
||||
EFX_Format,
|
||||
EFX_VMFunctionCall,
|
||||
EFX_Sequence,
|
||||
EFX_CompoundStatement,
|
||||
|
@ -1532,7 +1533,27 @@ public:
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxFlopFunctionCall
|
||||
// FxFormat
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxFormat : public FxExpression
|
||||
{
|
||||
FArgumentList ArgList;
|
||||
bool EmitTail;
|
||||
|
||||
public:
|
||||
|
||||
FxFormat(FArgumentList &args, const FScriptPosition &pos);
|
||||
~FxFormat();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
PPrototype *ReturnProto();
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVectorBuiltin
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
@ -1551,7 +1572,7 @@ public:
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxFlopFunctionCall
|
||||
// FxGetClass
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
@ -1569,7 +1590,7 @@ public:
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxFlopFunctionCall
|
||||
// FxGetDefaultByType
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
|
|
@ -199,6 +199,7 @@ enum EVMAbortException
|
|||
X_ARRAY_OUT_OF_BOUNDS,
|
||||
X_DIVISION_BY_ZERO,
|
||||
X_BAD_SELF,
|
||||
X_FORMAT_ERROR
|
||||
};
|
||||
|
||||
class CVMAbortException : public CDoomError
|
||||
|
|
|
@ -549,6 +549,10 @@ CVMAbortException::CVMAbortException(EVMAbortException reason, const char *morei
|
|||
AppendMessage("invalid self pointer.");
|
||||
break;
|
||||
|
||||
case X_FORMAT_ERROR:
|
||||
AppendMessage("string format failed.");
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
size_t len = strlen(m_Message);
|
||||
|
|
|
@ -28,6 +28,12 @@ struct FCheckPosition
|
|||
native void ClearLastRipped();
|
||||
}
|
||||
|
||||
struct LinkContext
|
||||
{
|
||||
voidptr sector_list; // really msecnode but that's not exported yet.
|
||||
voidptr render_list;
|
||||
}
|
||||
|
||||
|
||||
class Actor : Thinker native
|
||||
{
|
||||
|
@ -182,6 +188,7 @@ class Actor : Thinker native
|
|||
native State MeleeState;
|
||||
native State MissileState;
|
||||
native voidptr /*DecalBase*/ DecalGenerator;
|
||||
native uint8 fountaincolor;
|
||||
|
||||
native meta String Obituary; // Player was killed by this actor
|
||||
native meta String HitObituary; // Player was killed by this actor in melee
|
||||
|
@ -281,6 +288,7 @@ class Actor : Thinker native
|
|||
native static double absangle(double ang1, double ang2);
|
||||
native static Vector2 AngleToVector(double angle, double length = 1);
|
||||
native static Vector2 RotateVector(Vector2 vec, double angle);
|
||||
native static double Normalize180(double ang);
|
||||
|
||||
|
||||
bool IsPointerEqual(int ptr_select1, int ptr_select2)
|
||||
|
@ -432,8 +440,8 @@ class Actor : Thinker native
|
|||
native state FindState(statelabel st, bool exact = false);
|
||||
bool SetStateLabel(statelabel st, bool nofunction = false) { return SetState(FindState(st), nofunction); }
|
||||
native action state ResolveState(statelabel st); // this one, unlike FindState, is context aware.
|
||||
native void LinkToWorld();
|
||||
native void UnlinkFromWorld();
|
||||
native void LinkToWorld(LinkContext ctx = null);
|
||||
native void UnlinkFromWorld(out LinkContext ctx = null);
|
||||
native bool CanSeek(Actor target);
|
||||
native double AngleTo(Actor target, bool absolute = false);
|
||||
native void AddZ(double zadd, bool moving = true);
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
class ParticleFountain : Actor native
|
||||
class ParticleFountain : Actor
|
||||
{
|
||||
enum EColor
|
||||
{
|
||||
REDFOUNTAIN = 1,
|
||||
GREENFOUNTAIN = 2,
|
||||
BLUEFOUNTAIN = 3,
|
||||
YELLOWFOUNTAIN = 4,
|
||||
PURPLEFOUNTAIN = 5,
|
||||
BLACKFOUNTAIN = 6,
|
||||
WHITEFOUNTAIN = 7
|
||||
}
|
||||
|
||||
default
|
||||
{
|
||||
Height 0;
|
||||
|
@ -7,13 +18,32 @@ class ParticleFountain : Actor native
|
|||
+NOGRAVITY
|
||||
+INVISIBLE
|
||||
}
|
||||
|
||||
override void PostBeginPlay ()
|
||||
{
|
||||
Super.PostBeginPlay ();
|
||||
if (!(SpawnFlags & MTF_DORMANT))
|
||||
Activate (null);
|
||||
}
|
||||
|
||||
override void Activate (Actor activator)
|
||||
{
|
||||
Super.Activate (activator);
|
||||
fountaincolor = health;
|
||||
}
|
||||
|
||||
override void Deactivate (Actor activator)
|
||||
{
|
||||
Super.Deactivate (activator);
|
||||
fountaincolor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class RedParticleFountain : ParticleFountain
|
||||
{
|
||||
default
|
||||
{
|
||||
Health 1;
|
||||
Health REDFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +51,7 @@ class GreenParticleFountain : ParticleFountain
|
|||
{
|
||||
default
|
||||
{
|
||||
Health 2;
|
||||
Health GREENFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +59,7 @@ class BlueParticleFountain : ParticleFountain
|
|||
{
|
||||
default
|
||||
{
|
||||
Health 3;
|
||||
Health BLUEFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +67,7 @@ class YellowParticleFountain : ParticleFountain
|
|||
{
|
||||
default
|
||||
{
|
||||
Health 4;
|
||||
Health YELLOWFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +75,7 @@ class PurpleParticleFountain : ParticleFountain
|
|||
{
|
||||
default
|
||||
{
|
||||
Health 5;
|
||||
Health PURPLEFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +83,7 @@ class BlackParticleFountain : ParticleFountain
|
|||
{
|
||||
default
|
||||
{
|
||||
Health 6;
|
||||
Health BLACKFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +91,6 @@ class WhiteParticleFountain : ParticleFountain
|
|||
{
|
||||
default
|
||||
{
|
||||
Health 7;
|
||||
Health WHITEFOUNTAIN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,51 @@
|
|||
class InterpolationPoint : Actor native
|
||||
/*
|
||||
** a_movingcamera.cpp
|
||||
** Cameras that move and related neat stuff
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, self list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, self list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from self software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
/*
|
||||
== InterpolationPoint: node along a camera's path
|
||||
==
|
||||
== args[0] = pitch
|
||||
== args[1] = time (in octics) to get here from previous node
|
||||
== args[2] = time (in octics) to stay here before moving to next node
|
||||
== args[3] = low byte of next node's tid
|
||||
== args[4] = high byte of next node's tid
|
||||
*/
|
||||
class InterpolationPoint : Actor
|
||||
{
|
||||
|
||||
InterpolationPoint Next;
|
||||
|
||||
default
|
||||
{
|
||||
+NOBLOCKMAP
|
||||
|
@ -7,9 +53,61 @@ class InterpolationPoint : Actor native
|
|||
+DONTSPLASH
|
||||
RenderStyle "None";
|
||||
}
|
||||
|
||||
override void BeginPlay ()
|
||||
{
|
||||
Super.BeginPlay ();
|
||||
Next = null;
|
||||
}
|
||||
|
||||
void FormChain ()
|
||||
{
|
||||
let me = self;
|
||||
|
||||
while (me != null)
|
||||
{
|
||||
if (me.bAmbush) return;
|
||||
|
||||
me.bAmbush = true;
|
||||
|
||||
let iterator = ActorIterator.Create(me.args[3] + 256 * me.args[4], "InterpolationPoint");
|
||||
me.Next = InterpolationPoint(iterator.Next ());
|
||||
|
||||
if (me.Next == me) // Don't link to self
|
||||
me.Next = InterpolationPoint(iterator.Next ());
|
||||
|
||||
int pt = (me.args[0] << 24) >> 24; // this is for truncating the value to a byte, presumably because some old WAD needs it...
|
||||
me.Pitch = clamp(pt, -89, 89);
|
||||
|
||||
if (me.Next == null && (me.args[3] | me.args[4]))
|
||||
{
|
||||
A_Log("Can't find target for camera node " .. me.tid);
|
||||
}
|
||||
|
||||
me = me.Next;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the node (if any) where a path loops, relative to self one.
|
||||
InterpolationPoint ScanForLoop ()
|
||||
{
|
||||
InterpolationPoint node = self;
|
||||
while (node.Next && node.Next != self && node.special1 == 0)
|
||||
{
|
||||
node.special1 = 1;
|
||||
node = node.Next;
|
||||
}
|
||||
return node.Next == self ? node : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InterpolationSpecial : Actor native
|
||||
/*
|
||||
== InterpolationSpecial: Holds a special to execute when a
|
||||
== PathFollower reaches an InterpolationPoint of the same TID.
|
||||
*/
|
||||
|
||||
class InterpolationSpecial : Actor
|
||||
{
|
||||
default
|
||||
{
|
||||
|
@ -18,9 +116,28 @@ class InterpolationSpecial : Actor native
|
|||
+NOGRAVITY
|
||||
+DONTSPLASH
|
||||
}
|
||||
|
||||
override void Tick () {} // Does absolutely nothing itself
|
||||
|
||||
}
|
||||
|
||||
class PathFollower : Actor native
|
||||
/*
|
||||
== PathFollower: something that follows a camera path
|
||||
== Base class for some moving cameras
|
||||
==
|
||||
== args[0] = low byte of first node in path's tid
|
||||
== args[1] = high byte of first node's tid
|
||||
== args[2] = bit 0 = follow a linear path (rather than curved)
|
||||
== bit 1 = adjust angle
|
||||
== bit 2 = adjust pitch
|
||||
== bit 3 = aim in direction of motion
|
||||
==
|
||||
== Also uses:
|
||||
== target = first node in path
|
||||
== lastenemy = node prior to first node (if looped)
|
||||
*/
|
||||
|
||||
class PathFollower : Actor
|
||||
{
|
||||
default
|
||||
{
|
||||
|
@ -29,18 +146,436 @@ class PathFollower : Actor native
|
|||
+NOGRAVITY
|
||||
+DONTSPLASH
|
||||
}
|
||||
|
||||
bool bActive, bJustStepped;
|
||||
InterpolationPoint PrevNode, CurrNode;
|
||||
double Time; // Runs from 0.0 to 1.0 between CurrNode and CurrNode.Next
|
||||
int HoldTime;
|
||||
|
||||
// Interpolate between p2 and p3 along a Catmull-Rom spline
|
||||
// http://research.microsoft.com/~hollasch/cgindex/curves/catmull-rom.html
|
||||
double Splerp (double p1, double p2, double p3, double p4)
|
||||
{
|
||||
double t = Time;
|
||||
double res = 2*p2;
|
||||
res += (p3 - p1) * Time;
|
||||
t *= Time;
|
||||
res += (2*p1 - 5*p2 + 4*p3 - p4) * t;
|
||||
t *= Time;
|
||||
res += (3*p2 - 3*p3 + p4 - p1) * t;
|
||||
return 0.5 * res;
|
||||
}
|
||||
|
||||
// Linearly interpolate between p1 and p2
|
||||
double Lerp (double p1, double p2)
|
||||
{
|
||||
return p1 + Time * (p2 - p1);
|
||||
}
|
||||
|
||||
override void BeginPlay ()
|
||||
{
|
||||
Super.BeginPlay ();
|
||||
PrevNode = CurrNode = null;
|
||||
bActive = false;
|
||||
}
|
||||
|
||||
override void PostBeginPlay ()
|
||||
{
|
||||
// Find first node of path
|
||||
let iterator = ActorIterator.Create(args[0] + 256 * args[1], "InterpolationPoint");
|
||||
let node = InterpolationPoint(iterator.Next ());
|
||||
InterpolationPoint prevnode;
|
||||
|
||||
target = node;
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
A_Log ("PathFollower " .. tid .. ": Can't find interpolation pt " .. args[0] + 256 * args[1] .. "%d\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the path has enough nodes
|
||||
node.FormChain ();
|
||||
if (args[2] & 1)
|
||||
{ // linear path; need 2 nodes
|
||||
if (node.Next == null)
|
||||
{
|
||||
A_Log ("PathFollower " .. tid .. ": Path needs at least 2 nodes\n");
|
||||
return;
|
||||
}
|
||||
lastenemy = null;
|
||||
}
|
||||
else
|
||||
{ // spline path; need 4 nodes
|
||||
if (node.Next == null ||
|
||||
node.Next.Next == null ||
|
||||
node.Next.Next.Next == null)
|
||||
{
|
||||
A_Log ("PathFollower " .. tid .. ": Path needs at least 4 nodes\n");
|
||||
return;
|
||||
}
|
||||
// If the first node is in a loop, we can start there.
|
||||
// Otherwise, we need to start at the second node in the path.
|
||||
prevnode = node.ScanForLoop ();
|
||||
if (prevnode == null || prevnode.Next != node)
|
||||
{
|
||||
lastenemy = target;
|
||||
target = node.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastenemy = prevnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void Deactivate (Actor activator)
|
||||
{
|
||||
bActive = false;
|
||||
}
|
||||
|
||||
override void Activate (Actor activator)
|
||||
{
|
||||
if (!bActive)
|
||||
{
|
||||
CurrNode = InterpolationPoint(target);
|
||||
PrevNode = InterpolationPoint(lastenemy);
|
||||
|
||||
if (CurrNode != null)
|
||||
{
|
||||
NewNode ();
|
||||
SetOrigin (CurrNode.Pos, false);
|
||||
Time = 0.;
|
||||
HoldTime = 0;
|
||||
bJustStepped = true;
|
||||
bActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void Tick ()
|
||||
{
|
||||
if (!bActive)
|
||||
return;
|
||||
|
||||
if (bJustStepped)
|
||||
{
|
||||
bJustStepped = false;
|
||||
if (CurrNode.args[2])
|
||||
{
|
||||
HoldTime = level.time + CurrNode.args[2] * TICRATE / 8;
|
||||
SetXYZ(CurrNode.Pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (HoldTime > level.time)
|
||||
return;
|
||||
|
||||
// Splines must have a previous node.
|
||||
if (PrevNode == null && !(args[2] & 1))
|
||||
{
|
||||
bActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// All paths must have a current node.
|
||||
if (CurrNode.Next == null)
|
||||
{
|
||||
bActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Interpolate ())
|
||||
{
|
||||
Time += (8. / (CurrNode.args[1] * TICRATE));
|
||||
if (Time > 1.)
|
||||
{
|
||||
Time -= 1.;
|
||||
bJustStepped = true;
|
||||
PrevNode = CurrNode;
|
||||
CurrNode = CurrNode.Next;
|
||||
if (CurrNode != null)
|
||||
NewNode ();
|
||||
if (CurrNode == null || CurrNode.Next == null)
|
||||
Deactivate (self);
|
||||
if ((args[2] & 1) == 0 && CurrNode.Next.Next == null)
|
||||
Deactivate (self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NewNode ()
|
||||
{
|
||||
let iterator = ActorIterator.Create(CurrNode.tid, "InterpolationSpecial");
|
||||
InterpolationSpecial spec;
|
||||
|
||||
while ( (spec = InterpolationSpecial(iterator.Next ())) )
|
||||
{
|
||||
A_CallSpecial(spec.special, spec.args[0], spec.args[1], spec.args[2], spec.args[3], spec.args[4]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool Interpolate ()
|
||||
{
|
||||
Vector3 dpos = (0, 0, 0);
|
||||
LinkContext ctx;
|
||||
|
||||
if ((args[2] & 8) && Time > 0)
|
||||
{
|
||||
dpos = Pos;
|
||||
}
|
||||
|
||||
if (CurrNode.Next==null) return false;
|
||||
|
||||
UnlinkFromWorld (ctx);
|
||||
Vector3 newpos;
|
||||
if (args[2] & 1)
|
||||
{ // linear
|
||||
newpos.X = Lerp(CurrNode.pos.X, CurrNode.Next.pos.X);
|
||||
newpos.Y = Lerp(CurrNode.pos.Y, CurrNode.Next.pos.Y);
|
||||
newpos.Z = Lerp(CurrNode.pos.Z, CurrNode.Next.pos.Z);
|
||||
}
|
||||
else
|
||||
{ // spline
|
||||
if (CurrNode.Next.Next==null) return false;
|
||||
|
||||
newpos.X = Splerp(PrevNode.pos.X, CurrNode.pos.X, CurrNode.Next.pos.X, CurrNode.Next.Next.pos.X);
|
||||
newpos.Y = Splerp(PrevNode.pos.Y, CurrNode.pos.Y, CurrNode.Next.pos.Y, CurrNode.Next.Next.pos.Y);
|
||||
newpos.Z = Splerp(PrevNode.pos.Z, CurrNode.pos.Z, CurrNode.Next.pos.Z, CurrNode.Next.Next.pos.Z);
|
||||
}
|
||||
SetXYZ(newpos);
|
||||
LinkToWorld (ctx);
|
||||
|
||||
if (args[2] & 6)
|
||||
{
|
||||
if (args[2] & 8)
|
||||
{
|
||||
if (args[2] & 1)
|
||||
{ // linear
|
||||
dpos.X = CurrNode.Next.pos.X - CurrNode.pos.X;
|
||||
dpos.Y = CurrNode.Next.pos.Y - CurrNode.pos.Y;
|
||||
dpos.Z = CurrNode.Next.pos.Z - CurrNode.pos.Z;
|
||||
}
|
||||
else if (Time > 0)
|
||||
{ // spline
|
||||
dpos = newpos - dpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
int realarg = args[2];
|
||||
args[2] &= ~(2|4|8);
|
||||
Time += 0.1;
|
||||
dpos = newpos;
|
||||
Interpolate ();
|
||||
Time -= 0.1;
|
||||
args[2] = realarg;
|
||||
dpos = newpos - dpos;
|
||||
newpos -= dpos;
|
||||
SetXYZ(newpos);
|
||||
}
|
||||
if (args[2] & 2)
|
||||
{ // adjust yaw
|
||||
Angle = VectorAngle(dpos.X, dpos.Y);
|
||||
}
|
||||
if (args[2] & 4)
|
||||
{ // adjust pitch;
|
||||
double dist = dpos.XY.Length();
|
||||
Pitch = dist != 0 ? VectorAngle(dist, -dpos.Z) : 0.;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args[2] & 2)
|
||||
{ // interpolate angle
|
||||
double angle1 = Normalize180(CurrNode.angle);
|
||||
double angle2 = angle1 + deltaangle(angle1, CurrNode.Next.angle);
|
||||
angle = Lerp(angle1, angle2);
|
||||
}
|
||||
if (args[2] & 1)
|
||||
{ // linear
|
||||
if (args[2] & 4)
|
||||
{ // interpolate pitch
|
||||
Pitch = Lerp(CurrNode.Pitch, CurrNode.Next.Pitch);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // spline
|
||||
if (args[2] & 4)
|
||||
{ // interpolate pitch
|
||||
Pitch = Splerp(PrevNode.Pitch, CurrNode.Pitch,
|
||||
CurrNode.Next.Pitch, CurrNode.Next.Next.Pitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ActorMover : PathFollower native
|
||||
/*
|
||||
== ActorMover: Moves any actor along a camera path
|
||||
==
|
||||
== Same as PathFollower, except
|
||||
== args[2], bit 7: make nonsolid
|
||||
== args[3] = tid of thing to move
|
||||
==
|
||||
== also uses:
|
||||
== tracer = thing to move
|
||||
*/
|
||||
|
||||
class ActorMover : PathFollower
|
||||
{
|
||||
override void BeginPlay()
|
||||
{
|
||||
ChangeStatNum(STAT_ACTORMOVER);
|
||||
}
|
||||
|
||||
override void PostBeginPlay ()
|
||||
{
|
||||
Super.PostBeginPlay ();
|
||||
|
||||
let iterator = ActorIterator.Create(args[3]);
|
||||
tracer = iterator.Next ();
|
||||
|
||||
if (tracer == null)
|
||||
{
|
||||
A_Log("ActorMover " .. tid .. ": Can't find target " .. args[3] .. "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
special1 = tracer.bNoGravity + (tracer.bNoBlockmap<<1) + (tracer.bSolid<<2) + (tracer.bInvulnerable<<4) + (tracer.bDormant<<8);
|
||||
}
|
||||
}
|
||||
|
||||
override bool Interpolate ()
|
||||
{
|
||||
if (tracer == null)
|
||||
return true;
|
||||
|
||||
if (Super.Interpolate ())
|
||||
{
|
||||
double savedz = tracer.pos.Z;
|
||||
tracer.SetZ(pos.Z);
|
||||
if (!tracer.TryMove (Pos.XY, true))
|
||||
{
|
||||
tracer.SetZ(savedz);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args[2] & 2)
|
||||
tracer.angle = angle;
|
||||
if (args[2] & 4)
|
||||
tracer.Pitch = Pitch;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override void Activate (Actor activator)
|
||||
{
|
||||
if (tracer == null || bActive)
|
||||
return;
|
||||
|
||||
Super.Activate (activator);
|
||||
let tracer = self.tracer;
|
||||
special1 = tracer.bNoGravity + (tracer.bNoBlockmap<<1) + (tracer.bSolid<<2) + (tracer.bInvulnerable<<4) + (tracer.bDormant<<8);
|
||||
bNoGravity = true;
|
||||
if (args[2] & 128)
|
||||
{
|
||||
LinkContext ctx;
|
||||
tracer.UnlinkFromWorld (ctx);
|
||||
bNoBlockmap = true;
|
||||
bSolid = false;
|
||||
tracer.LinkToWorld (ctx);
|
||||
}
|
||||
if (tracer.bIsMonster)
|
||||
{
|
||||
bInvulnerable = true;
|
||||
bDormant = true;
|
||||
}
|
||||
// Don't let the renderer interpolate between the actor's
|
||||
// old position and its new position.
|
||||
Interpolate ();
|
||||
tracer.ClearInterpolation();
|
||||
}
|
||||
|
||||
override void Deactivate (Actor activator)
|
||||
{
|
||||
if (bActive)
|
||||
{
|
||||
Super.Deactivate (activator);
|
||||
let tracer = self.tracer;
|
||||
if (tracer != null)
|
||||
{
|
||||
LinkContext ctx;
|
||||
tracer.UnlinkFromWorld (ctx);
|
||||
tracer.bNoGravity = (special1 & 1);
|
||||
tracer.bNoBlockmap = !!(special1 & 2);
|
||||
tracer.bSolid = !!(special1 & 4);
|
||||
tracer.bInvulnerable = !!(special1 & 8);
|
||||
tracer.bDormant = !!(special1 & 16);
|
||||
tracer.LinkToWorld (ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MovingCamera : PathFollower native
|
||||
/*
|
||||
== MovingCamera: Moves any actor along a camera path
|
||||
==
|
||||
== Same as PathFollower, except
|
||||
== args[3] = tid of thing to look at (0 if none)
|
||||
==
|
||||
== Also uses:
|
||||
== tracer = thing to look at
|
||||
*/
|
||||
|
||||
class MovingCamera : PathFollower
|
||||
{
|
||||
Actor activator;
|
||||
default
|
||||
{
|
||||
CameraHeight 0;
|
||||
}
|
||||
|
||||
override void PostBeginPlay ()
|
||||
{
|
||||
Super.PostBeginPlay ();
|
||||
|
||||
Activator = null;
|
||||
if (args[3] != 0)
|
||||
{
|
||||
let iterator = ActorIterator.Create(args[3]);
|
||||
tracer = iterator.Next ();
|
||||
if (tracer == null)
|
||||
{
|
||||
A_Log("MovingCamera " .. tid .. ": Can't find thing " .. args[3] .. "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override bool Interpolate ()
|
||||
{
|
||||
if (tracer == null)
|
||||
return Super.Interpolate ();
|
||||
|
||||
if (Super.Interpolate ())
|
||||
{
|
||||
angle = AngleTo(tracer, true);
|
||||
|
||||
if (args[2] & 4)
|
||||
{ // Also aim camera's pitch;
|
||||
Vector3 diff = Pos - tracer.Pos - (0, 0, tracer.Height / 2);
|
||||
double dist = diff.XY.Length();
|
||||
Pitch = dist != 0 ? VectorAngle(dist, diff.Z) : 0.;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
class TeleportFog : Actor native
|
||||
class TeleportFog : Actor
|
||||
{
|
||||
default
|
||||
{
|
||||
|
@ -22,6 +22,27 @@ class TeleportFog : Actor native
|
|||
TFOG ABCDEFEDCB 6 Bright;
|
||||
Stop;
|
||||
}
|
||||
|
||||
override void PostBeginPlay ()
|
||||
{
|
||||
Super.PostBeginPlay ();
|
||||
A_PlaySound ("misc/teleport", CHAN_BODY);
|
||||
switch (gametype())
|
||||
{
|
||||
case GAME_Hexen:
|
||||
case GAME_Heretic:
|
||||
SetStateLabel("Raven");
|
||||
break;
|
||||
|
||||
case GAME_Strife:
|
||||
SetStateLabel("Strife");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue