1 #include "actor.h"
2 #include "info.h"
3 #include "gi.h"
4 #include "m_random.h"
5 #include "thingdef/thingdef.h"
6 
7 static FRandom pr_orbit ("Orbit");
8 
9 
10 // Custom bridge --------------------------------------------------------
11 /*
12 	args[0]: Bridge radius, in mapunits
13 	args[1]: Bridge height, in mapunits
14 	args[2]: Amount of bridge balls (if 0: Doom bridge)
15 	args[3]: Rotation speed of bridge balls, in byte angle per seconds, sorta:
16 		Since an arg is only a byte, it can only go from 0 to 255, while ZDoom's
17 		BAM go from 0 to 65535. Plus, it needs to be able to go either way. So,
18 		up to 128, it goes counterclockwise; 129-255 is clockwise, substracting
19 		256 from it to get the angle. A few example values:
20 		  0: Hexen default
21 	     11:  15� / seconds
22 		 21:  30� / seconds
23 		 32:  45� / seconds
24 		 64:  90� / seconds
25 		128: 180� / seconds
26 		192: -90� / seconds
27 		223: -45� / seconds
28 		233: -30� / seconds
29 		244: -15� / seconds
30 		This value only matters if args[2] is not zero.
31 	args[4]: Rotation radius of bridge balls, in bridge radius %.
32 		If 0, use Hexen default: ORBIT_RADIUS, regardless of bridge radius.
33 		This value only matters if args[2] is not zero.
34 */
35 
36 class ACustomBridge : public AActor
37 {
38 	DECLARE_CLASS (ACustomBridge, AActor)
39 public:
40 	void BeginPlay ();
41 	void Destroy();
42 };
43 
IMPLEMENT_CLASS(ACustomBridge)44 IMPLEMENT_CLASS(ACustomBridge)
45 
46 void ACustomBridge::BeginPlay ()
47 {
48 	if (args[2]) // Hexen bridge if there are balls
49 	{
50 		SetState(SeeState);
51 		radius = args[0] ? args[0] << FRACBITS : 32 * FRACUNIT;
52 		height = args[1] ? args[1] << FRACBITS : 2 * FRACUNIT;
53 	}
54 	else // No balls? Then a Doom bridge.
55 	{
56 		radius = args[0] ? args[0] << FRACBITS : 36 * FRACUNIT;
57 		height = args[1] ? args[1] << FRACBITS : 4 * FRACUNIT;
58 		RenderStyle = STYLE_Normal;
59 	}
60 }
61 
Destroy()62 void ACustomBridge::Destroy()
63 {
64 	// Hexen originally just set a flag to make the bridge balls remove themselves in A_BridgeOrbit.
65 	// But this is not safe with custom bridge balls that do not necessarily call that function.
66 	// So the best course of action is to look for all bridge balls here and destroy them ourselves.
67 
68 	TThinkerIterator<AActor> it;
69 	AActor *thing;
70 
71 	while ((thing = it.Next()))
72 	{
73 		if (thing->target == this)
74 		{
75 			thing->Destroy();
76 		}
77 	}
78 	Super::Destroy();
79 }
80 
81 // Action functions for the non-Doom bridge --------------------------------
82 
83 #define ORBIT_RADIUS	15
84 
85 // New bridge stuff
86 //	Parent
87 //		special1	true == removing from world
88 //
89 //	Child
90 //		target		pointer to center mobj
91 //		angle		angle of ball
92 
DEFINE_ACTION_FUNCTION(AActor,A_BridgeOrbit)93 DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit)
94 {
95 	if (self->target == NULL)
96 	{ // Don't crash if somebody spawned this into the world
97 	  // independantly of a Bridge actor.
98 		return;
99 	}
100 	// Set default values
101 	// Every five tics, Hexen moved the ball 3/256th of a revolution.
102 	int rotationspeed  = ANGLE_45/32*3/5;
103 	int rotationradius = ORBIT_RADIUS * FRACUNIT;
104 	// If the bridge is custom, set non-default values if any.
105 
106 	// Set angular speed; 1--128: counterclockwise rotation ~=1--180�; 129--255: clockwise rotation ~= 180--1�
107 	if (self->target->args[3] > 128) rotationspeed = ANGLE_45/32 * (self->target->args[3]-256) / TICRATE;
108 	else if (self->target->args[3] > 0) rotationspeed = ANGLE_45/32 * (self->target->args[3]) / TICRATE;
109 	// Set rotation radius
110 	if (self->target->args[4]) rotationradius = ((self->target->args[4] * self->target->radius) / 100);
111 
112 	self->angle += rotationspeed;
113 	self->SetOrigin(self->target->Vec3Angle(rotationradius, self->angle, 0), true);
114 	self->floorz = self->target->floorz;
115 	self->ceilingz = self->target->ceilingz;
116 }
117 
118 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_BridgeInit)119 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BridgeInit)
120 {
121 	angle_t startangle;
122 	AActor *ball;
123 
124 	ACTION_PARAM_START(1);
125 	ACTION_PARAM_CLASS(balltype, 0);
126 
127 	if (balltype == NULL) balltype = PClass::FindClass("BridgeBall");
128 
129 	startangle = pr_orbit() << 24;
130 
131 	// Spawn triad into world -- may be more than a triad now.
132 	int ballcount = self->args[2]==0 ? 3 : self->args[2];
133 
134 	for (int i = 0; i < ballcount; i++)
135 	{
136 		ball = Spawn(balltype, self->Pos(), ALLOW_REPLACE);
137 		ball->angle = startangle + (ANGLE_45/32) * (256/ballcount) * i;
138 		ball->target = self;
139 		CALL_ACTION(A_BridgeOrbit, ball);
140 	}
141 }
142 
143 
144 // Invisible bridge --------------------------------------------------------
145 
146 class AInvisibleBridge : public AActor
147 {
148 	DECLARE_CLASS (AInvisibleBridge, AActor)
149 public:
150 	void BeginPlay ();
151 };
152 
IMPLEMENT_CLASS(AInvisibleBridge)153 IMPLEMENT_CLASS(AInvisibleBridge)
154 
155 void AInvisibleBridge::BeginPlay ()
156 {
157 	Super::BeginPlay ();
158 	if (args[0])
159 		radius = args[0] << FRACBITS;
160 	if (args[1])
161 		height = args[1] << FRACBITS;
162 }
163 
164