0
|
1 /*
|
|
2 * Copyright (c) 2009, Mason Green (zzzzrrr)
|
|
3 *
|
|
4 * All rights reserved.
|
|
5 *
|
|
6 * Redistribution and use in source and binary forms, with or without modification,
|
|
7 * are permitted provided that the following conditions are met:
|
|
8 *
|
|
9 * * Redistributions of source code must retain the above copyright notice,
|
|
10 * this list of conditions and the following disclaimer.
|
|
11 * * Redistributions in binary form must reproduce the above copyright notice,
|
|
12 * this list of conditions and the following disclaimer in the documentation
|
|
13 * and/or other materials provided with the distribution.
|
|
14 * * Neither the name of the polygonal nor the names of its contributors may be
|
|
15 * used to endorse or promote products derived from this software without specific
|
|
16 * prior written permission.
|
|
17 *
|
|
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29 */
|
2
|
30 module openmelee.melee;
|
0
|
31
|
5
|
32 import tango.io.Stdout: Stdout;
|
|
33
|
0
|
34 import Integer = tango.text.convert.Integer;
|
|
35 import tango.math.Math;
|
|
36 import tango.math.random.Kiss;
|
|
37
|
|
38 import xf.hybrid.Event;
|
|
39 import xf.input.KeySym;
|
|
40 import xf.omg.core.LinearAlgebra;
|
|
41
|
|
42 public import blaze.all;
|
|
43
|
2
|
44 import openmelee.boundaryListener;
|
|
45 import openmelee.contactListener;
|
|
46 import openmelee.ship;
|
|
47 import openmelee.urQuan;
|
|
48 import openmelee.orz;
|
6
|
49 import openmelee.planet;
|
0
|
50
|
|
51 // Cursor scale factor
|
|
52 const CURSORSIZE = 0.05f;
|
|
53
|
|
54 const INIT_SPAWN_SIZE = 0.5f;
|
|
55
|
|
56 // Dragging stuffs
|
|
57 const BUNGEE_K = 1.5f;
|
|
58 // Damping factor for dragging
|
|
59 const DRAGDAMP = 20.0f;
|
|
60
|
|
61 // Size of hinges
|
|
62 const HINGE_RADIUS = 0.05f;
|
|
63
|
|
64 // Smallest allowed dimension
|
|
65 const MIN_DIMENSION = 0.1;
|
|
66
|
|
67 // Water stuffs
|
|
68 const MAX_PARTICLES = 10000;
|
|
69 const WATER_BOUNCE = 0.01;
|
|
70 const WATER_FRICTION = 0.5;
|
|
71 const WATER_RADIUS = 0.05f;
|
|
72 const MAX_CIRCLE_RES = 32;
|
|
73
|
|
74 const k_maxContactPoints = 2048;
|
|
75
|
|
76 enum ContactState {
|
|
77 e_contactAdded,
|
|
78 e_contactPersisted,
|
|
79 e_contactRemoved
|
|
80 }
|
|
81
|
|
82 struct ContactPoint {
|
|
83 bzShape shape1;
|
|
84 bzShape shape2;
|
|
85 bzVec2 normal;
|
|
86 bzVec2 position;
|
|
87 bzVec2 velocity;
|
|
88 bzContactID id;
|
|
89 ContactState state;
|
|
90 }
|
|
91
|
|
92 // Melee settings. Some can be controlled in the GUI.
|
|
93 struct Settings {
|
4
|
94 float hz = 60;
|
0
|
95 int velocityIterations = 8;
|
|
96 int positionIterations = 2;
|
|
97 bool drawShapes = true;
|
|
98 bool drawJoints = true;
|
|
99 bool drawControllers;
|
|
100 bool drawCoreShapes;
|
5
|
101 bool drawAABBs;
|
0
|
102 bool drawOBBs;
|
|
103 bool drawPairs;
|
|
104 bool drawContactPoints;
|
|
105 bool drawContactNormals;
|
|
106 bool drawContactForces;
|
|
107 bool drawFrictionForces;
|
|
108 bool drawCOMs;
|
|
109 bool drawStats;
|
|
110 bool enableWarmStarting;
|
|
111 bool enableTOI;
|
|
112 }
|
|
113
|
|
114 // Dirty, dirty hack for communicating config changes to Main
|
|
115 // TODO: Harass h3 to add .changed to hybrid so this isn't necessary
|
|
116 struct ConfigChange (T)
|
|
117 {
|
|
118 protected
|
|
119 {
|
|
120 bool _pending = false;
|
|
121 T _value;
|
|
122 }
|
|
123
|
|
124 T value()
|
|
125 {
|
|
126 _pending = false;
|
|
127 return _value;
|
|
128 }
|
|
129
|
|
130 void value(T change)
|
|
131 {
|
|
132 _value = change;
|
|
133 _pending = true;
|
|
134 }
|
|
135
|
|
136 bool pending()
|
|
137 {
|
|
138 return _pending;
|
|
139 }
|
|
140
|
|
141 T opAssign(T change)
|
|
142 {
|
|
143 value(change);
|
|
144 return _value;
|
|
145 }
|
|
146
|
|
147 bool opEquals(T other)
|
|
148 {
|
|
149 return _value == other;
|
|
150 }
|
|
151 }
|
|
152
|
|
153 T randomRange(T = int) (T min, T max)
|
|
154 {
|
|
155 return min + Kiss.instance.natural() % (max + 1 - min);
|
|
156 }
|
|
157
|
|
158 class Melee
|
|
159 {
|
|
160
|
|
161 this(Settings *settings)
|
|
162 {
|
|
163 this.settings = settings;
|
|
164 spawnRect = vec2(INIT_SPAWN_SIZE, INIT_SPAWN_SIZE);
|
|
165 // bzWorld boundary callback
|
|
166 m_boundaryListener = new BoundaryListener(this);
|
|
167 // bzContact callback
|
|
168 m_contactListener = new ContactListener(this);
|
5
|
169 init();
|
0
|
170 }
|
|
171
|
|
172 void init() {
|
|
173 // Define world boundaries
|
6
|
174 worldAABB.lowerBound.set(-100.0f, -100.0f);
|
|
175 worldAABB.upperBound.set(100.0f, 100.0f);
|
0
|
176 world = new bzWorld(worldAABB, gravity, allowSleep);
|
|
177 world.boundaryListener = m_boundaryListener;
|
|
178 world.contactListener = m_contactListener;
|
|
179 viewCenter = vec2(10, 10);
|
|
180 ship1 = new Orz(world);
|
|
181 ship2 = new UrQuan(world);
|
6
|
182 auto planet = new Planet(world);
|
0
|
183 }
|
|
184
|
|
185 void drag()
|
|
186 {
|
|
187
|
|
188 }
|
|
189
|
|
190 EventHandling onClick(MouseButtonEvent e)
|
|
191 {
|
|
192 return EventHandling.Stop;
|
|
193 }
|
|
194
|
|
195 EventHandling onKey(KeyboardEvent e)
|
|
196 {
|
5
|
197 // Key pressed
|
0
|
198 if (e.down) {
|
|
199 switch (e.keySym) {
|
5
|
200 case KeySym.space:
|
|
201 settings.drawAABBs = !settings.drawAABBs;
|
|
202 break;
|
0
|
203 case KeySym.Escape:
|
|
204 quit = true;
|
|
205 break;
|
5
|
206 case KeySym.Up:
|
|
207 thrust = true;
|
|
208 break;
|
|
209 case KeySym.Left:
|
|
210 ship1.turnLeft();
|
|
211 break;
|
|
212 case KeySym.Right:
|
|
213 ship1.turnRight();
|
|
214 break;
|
|
215 case KeySym.Down:
|
|
216 break;
|
0
|
217 default:
|
|
218 break;
|
|
219 }
|
|
220 // Key released
|
|
221 } else {
|
5
|
222 if(e.keySym == KeySym.Up) {
|
0
|
223 thrust = false;
|
5
|
224 } else if (e.keySym == KeySym.Left || e.keySym == KeySym.Right) {
|
0
|
225 ship1.rBody.angularVelocity = 0.0f;
|
|
226 }
|
|
227 }
|
|
228 return EventHandling.Stop;
|
|
229 }
|
|
230
|
|
231 // Mouse move
|
|
232 EventHandling onMove(MouseMoveEvent e)
|
|
233 {
|
|
234 return EventHandling.Stop;
|
|
235 }
|
|
236
|
|
237 EventHandling onDT(TimeUpdateEvent e)
|
|
238 {
|
|
239 return EventHandling.Continue;
|
|
240 }
|
|
241
|
|
242 EventHandling onMouseEnter(MouseEnterEvent e)
|
|
243 {
|
|
244 return EventHandling.Continue;
|
|
245 }
|
|
246
|
|
247 EventHandling onMouseLeave(MouseLeaveEvent e)
|
|
248 {
|
|
249 return EventHandling.Continue;
|
|
250 }
|
|
251
|
|
252 protected
|
|
253 {
|
|
254 const bzVec2 gravity = bzVec2(0.0f, 0.0f);
|
|
255 bool allowSleep = false;
|
|
256
|
|
257 vec2 spawnRect;
|
|
258
|
|
259 vec2[] drawing;
|
|
260
|
|
261 vec2i screenSize = vec2i.zero;
|
|
262 vec2 mousePos = vec2.zero;
|
|
263
|
|
264 bool scaling = false;
|
|
265 bool full = false;
|
|
266
|
|
267 vec2 spawnStart;
|
|
268
|
|
269 bool preserveBullet = false;
|
|
270
|
|
271 float waterDelta = 0;
|
|
272 }
|
|
273
|
|
274 void boundaryViolated(bzBody rBody)
|
|
275 {
|
5
|
276 float x,y;
|
|
277
|
|
278 if(rBody.position.x > worldAABB.upperBound.x) {
|
|
279 x = worldAABB.lowerBound.x + 5;
|
|
280 rBody.position = bzVec2(x, rBody.position.y);
|
|
281 } else if (rBody.position.x < worldAABB.lowerBound.x) {
|
|
282 x = worldAABB.upperBound.x - 5;
|
|
283 rBody.position = bzVec2(x, rBody.position.y);
|
|
284 } else if (rBody.position.y > worldAABB.upperBound.y) {
|
|
285 y = worldAABB.lowerBound.y + 5;
|
|
286 rBody.position = bzVec2(rBody.position.x, y);
|
|
287 } else if(rBody.position.y < worldAABB.lowerBound.y) {
|
|
288 y = worldAABB.upperBound.y - 5;
|
|
289 rBody.position = bzVec2(rBody.position.x, y);
|
|
290 }
|
0
|
291 }
|
|
292
|
|
293 bool quit;
|
|
294
|
|
295 // Ortho view zoom
|
|
296 float zoom = 40;
|
|
297 int pointCount;
|
|
298 vec2 viewCenter;
|
|
299
|
|
300 bzWorld world;
|
|
301 Settings *settings;
|
|
302
|
|
303 // bzWorld boundary listener. Destroy bodies that leave world bzAABB
|
|
304 bzBoundaryListener m_boundaryListener;
|
|
305 bzContactListener m_contactListener;
|
|
306 ContactPoint[k_maxContactPoints] points;
|
|
307
|
|
308 ConfigChange!(vec2) editChange;
|
|
309 bool thrust;
|
2
|
310 bzAABB worldAABB;
|
|
311
|
0
|
312 Ship ship1;
|
|
313 Ship ship2;
|
|
314 }
|
|
315
|
|
316 // Utility functions
|
|
317 bzVec2 toBlaze(vec2 vec)
|
|
318 {
|
|
319 auto ret = bzVec2(vec.x, vec.y);
|
|
320 return ret;
|
|
321 }
|
|
322
|
|
323 vec2 rotate(vec2 point, float rad)
|
|
324 {
|
|
325 return vec2(cos(rad) * point.x - sin(rad) * point.y, sin(rad) * point.x + cos(rad) * point.y);
|
|
326 }
|
|
327
|
|
328 class Select
|
|
329 {
|
|
330 bool select;
|
|
331 }
|