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