gzdoom-gles/src/g_shared/a_bridge.cpp
Christoph Oelckers d86f03e2e0 - reverted most of the last commit after realizing that trying to manage constructing/destructing per variable is not going to work because it'd require some extensive exception management in the compiled VM code.
- instead add a list of SpecialInits to VMScriptFunction so this can be done transparently when setting up and popping the stack frame. The only drawback is that this requires permanent allocation of stack objects for the entire lifetime of a function but this is a relatively small tradeoff for significantly reduced maintenance work throughout.
- removed most #include "vm.h", because nearly all files already pull this in through dobject.h.
2016-11-17 13:10:19 +01:00

168 lines
4.4 KiB
C++

#include "actor.h"
#include "info.h"
#include "gi.h"
#include "m_random.h"
static FRandom pr_orbit ("Orbit");
// Custom bridge --------------------------------------------------------
/*
args[0]: Bridge radius, in mapunits
args[1]: Bridge height, in mapunits
args[2]: Amount of bridge balls (if 0: Doom bridge)
args[3]: Rotation speed of bridge balls, in byte angle per seconds, sorta:
Since an arg is only a byte, it can only go from 0 to 255, while ZDoom's
BAM go from 0 to 65535. Plus, it needs to be able to go either way. So,
up to 128, it goes counterclockwise; 129-255 is clockwise, substracting
256 from it to get the angle. A few example values:
0: Hexen default
11: 15° / seconds
21: 30° / seconds
32: 45° / seconds
64: 90° / seconds
128: 180° / seconds
192: -90° / seconds
223: -45° / seconds
233: -30° / seconds
244: -15° / seconds
This value only matters if args[2] is not zero.
args[4]: Rotation radius of bridge balls, in bridge radius %.
If 0, use Hexen default: ORBIT_RADIUS, regardless of bridge radius.
This value only matters if args[2] is not zero.
*/
class ACustomBridge : public AActor
{
DECLARE_CLASS (ACustomBridge, AActor)
public:
void BeginPlay ();
void Destroy();
};
IMPLEMENT_CLASS(ACustomBridge, false, false, false, false)
void ACustomBridge::BeginPlay ()
{
if (args[2]) // Hexen bridge if there are balls
{
SetState(SeeState);
radius = args[0] ? args[0] : 32;
Height = args[1] ? args[1] : 2;
}
else // No balls? Then a Doom bridge.
{
radius = args[0] ? args[0] : 36;
Height = args[1] ? args[1] : 4;
RenderStyle = STYLE_Normal;
}
}
void ACustomBridge::Destroy()
{
// Hexen originally just set a flag to make the bridge balls remove themselves in A_BridgeOrbit.
// But this is not safe with custom bridge balls that do not necessarily call that function.
// So the best course of action is to look for all bridge balls here and destroy them ourselves.
TThinkerIterator<AActor> it;
AActor *thing;
while ((thing = it.Next()))
{
if (thing->target == this)
{
thing->Destroy();
}
}
Super::Destroy();
}
// Action functions for the non-Doom bridge --------------------------------
#define ORBIT_RADIUS 15
// New bridge stuff
// Parent
// special1 true == removing from world
//
// Child
// target pointer to center mobj
// angle angle of ball
DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->target == NULL)
{ // Don't crash if somebody spawned this into the world
// independantly of a Bridge actor.
return 0;
}
// Set default values
// Every five tics, Hexen moved the ball 3/256th of a revolution.
DAngle rotationspeed = 45./32*3/5;
double rotationradius = ORBIT_RADIUS;
// If the bridge is custom, set non-default values if any.
// Set angular speed; 1--128: counterclockwise rotation ~=1--180°; 129--255: clockwise rotation ~= 180--1°
if (self->target->args[3] > 128) rotationspeed = 45./32 * (self->target->args[3]-256) / TICRATE;
else if (self->target->args[3] > 0) rotationspeed = 45./32 * (self->target->args[3]) / TICRATE;
// Set rotation radius
if (self->target->args[4]) rotationradius = ((self->target->args[4] * self->target->radius) / 100);
self->Angles.Yaw += rotationspeed;
self->SetOrigin(self->target->Vec3Angle(rotationradius, self->Angles.Yaw, 0), true);
self->floorz = self->target->floorz;
self->ceilingz = self->target->ceilingz;
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_BridgeInit)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS_DEF(balltype, AActor);
AActor *ball;
if (balltype == NULL)
{
balltype = PClass::FindActor("BridgeBall");
}
DAngle startangle = pr_orbit() * (360./256.);
// Spawn triad into world -- may be more than a triad now.
int ballcount = self->args[2]==0 ? 3 : self->args[2];
for (int i = 0; i < ballcount; i++)
{
ball = Spawn(balltype, self->Pos(), ALLOW_REPLACE);
ball->Angles.Yaw = startangle + (45./32) * (256/ballcount) * i;
ball->target = self;
CALL_ACTION(A_BridgeOrbit, ball);
}
return 0;
}
// Invisible bridge --------------------------------------------------------
class AInvisibleBridge : public AActor
{
DECLARE_CLASS (AInvisibleBridge, AActor)
public:
void BeginPlay ();
};
IMPLEMENT_CLASS(AInvisibleBridge, false, false, false, false)
void AInvisibleBridge::BeginPlay ()
{
Super::BeginPlay ();
if (args[0])
radius = args[0];
if (args[1])
Height = args[1];
}