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;
|
0
|
49
|
|
50 // Cursor scale factor
|
|
51 const CURSORSIZE = 0.05f;
|
|
52
|
|
53 const INIT_SPAWN_SIZE = 0.5f;
|
|
54
|
|
55 // Dragging stuffs
|
|
56 const BUNGEE_K = 1.5f;
|
|
57 // Damping factor for dragging
|
|
58 const DRAGDAMP = 20.0f;
|
|
59
|
|
60 // Size of hinges
|
|
61 const HINGE_RADIUS = 0.05f;
|
|
62
|
|
63 // Smallest allowed dimension
|
|
64 const MIN_DIMENSION = 0.1;
|
|
65
|
|
66 // Water stuffs
|
|
67 const MAX_PARTICLES = 10000;
|
|
68 const WATER_BOUNCE = 0.01;
|
|
69 const WATER_FRICTION = 0.5;
|
|
70 const WATER_RADIUS = 0.05f;
|
|
71 const MAX_CIRCLE_RES = 32;
|
|
72
|
|
73 const k_maxContactPoints = 2048;
|
|
74
|
|
75 enum ContactState {
|
|
76 e_contactAdded,
|
|
77 e_contactPersisted,
|
|
78 e_contactRemoved
|
|
79 }
|
|
80
|
|
81 struct ContactPoint {
|
|
82 bzShape shape1;
|
|
83 bzShape shape2;
|
|
84 bzVec2 normal;
|
|
85 bzVec2 position;
|
|
86 bzVec2 velocity;
|
|
87 bzContactID id;
|
|
88 ContactState state;
|
|
89 }
|
|
90
|
|
91 // Melee settings. Some can be controlled in the GUI.
|
|
92 struct Settings {
|
4
|
93 float hz = 60;
|
0
|
94 int velocityIterations = 8;
|
|
95 int positionIterations = 2;
|
|
96 bool drawShapes = true;
|
|
97 bool drawJoints = true;
|
|
98 bool drawControllers;
|
|
99 bool drawCoreShapes;
|
5
|
100 bool drawAABBs;
|
0
|
101 bool drawOBBs;
|
|
102 bool drawPairs;
|
|
103 bool drawContactPoints;
|
|
104 bool drawContactNormals;
|
|
105 bool drawContactForces;
|
|
106 bool drawFrictionForces;
|
|
107 bool drawCOMs;
|
|
108 bool drawStats;
|
|
109 bool enableWarmStarting;
|
|
110 bool enableTOI;
|
|
111 }
|
|
112
|
|
113 // Dirty, dirty hack for communicating config changes to Main
|
|
114 // TODO: Harass h3 to add .changed to hybrid so this isn't necessary
|
|
115 struct ConfigChange (T)
|
|
116 {
|
|
117 protected
|
|
118 {
|
|
119 bool _pending = false;
|
|
120 T _value;
|
|
121 }
|
|
122
|
|
123 T value()
|
|
124 {
|
|
125 _pending = false;
|
|
126 return _value;
|
|
127 }
|
|
128
|
|
129 void value(T change)
|
|
130 {
|
|
131 _value = change;
|
|
132 _pending = true;
|
|
133 }
|
|
134
|
|
135 bool pending()
|
|
136 {
|
|
137 return _pending;
|
|
138 }
|
|
139
|
|
140 T opAssign(T change)
|
|
141 {
|
|
142 value(change);
|
|
143 return _value;
|
|
144 }
|
|
145
|
|
146 bool opEquals(T other)
|
|
147 {
|
|
148 return _value == other;
|
|
149 }
|
|
150 }
|
|
151
|
|
152 T randomRange(T = int) (T min, T max)
|
|
153 {
|
|
154 return min + Kiss.instance.natural() % (max + 1 - min);
|
|
155 }
|
|
156
|
|
157 class Melee
|
|
158 {
|
|
159
|
|
160 this(Settings *settings)
|
|
161 {
|
|
162 this.settings = settings;
|
|
163 spawnRect = vec2(INIT_SPAWN_SIZE, INIT_SPAWN_SIZE);
|
|
164 // bzWorld boundary callback
|
|
165 m_boundaryListener = new BoundaryListener(this);
|
|
166 // bzContact callback
|
|
167 m_contactListener = new ContactListener(this);
|
5
|
168 init();
|
0
|
169 }
|
|
170
|
|
171 void init() {
|
|
172 // Define world boundaries
|
2
|
173 worldAABB.lowerBound.set(-75.0f, -75.0f);
|
|
174 worldAABB.upperBound.set(75.0f, 75.0f);
|
0
|
175 world = new bzWorld(worldAABB, gravity, allowSleep);
|
|
176 world.boundaryListener = m_boundaryListener;
|
|
177 world.contactListener = m_contactListener;
|
|
178 viewCenter = vec2(10, 10);
|
|
179 ship1 = new Orz(world);
|
|
180 ship2 = new UrQuan(world);
|
|
181 }
|
|
182
|
|
183 void drag()
|
|
184 {
|
|
185
|
|
186 }
|
|
187
|
|
188 EventHandling onClick(MouseButtonEvent e)
|
|
189 {
|
|
190 return EventHandling.Stop;
|
|
191 }
|
|
192
|
|
193 EventHandling onKey(KeyboardEvent e)
|
|
194 {
|
5
|
195 // Key pressed
|
0
|
196 if (e.down) {
|
|
197 switch (e.keySym) {
|
5
|
198 case KeySym.space:
|
|
199 settings.drawAABBs = !settings.drawAABBs;
|
|
200 break;
|
0
|
201 case KeySym.Escape:
|
|
202 quit = true;
|
|
203 break;
|
5
|
204 case KeySym.Up:
|
|
205 thrust = true;
|
|
206 break;
|
|
207 case KeySym.Left:
|
|
208 ship1.turnLeft();
|
|
209 break;
|
|
210 case KeySym.Right:
|
|
211 ship1.turnRight();
|
|
212 break;
|
|
213 case KeySym.Down:
|
|
214 break;
|
0
|
215 default:
|
|
216 break;
|
|
217 }
|
|
218 // Key released
|
|
219 } else {
|
5
|
220 if(e.keySym == KeySym.Up) {
|
0
|
221 thrust = false;
|
5
|
222 } else if (e.keySym == KeySym.Left || e.keySym == KeySym.Right) {
|
0
|
223 ship1.rBody.angularVelocity = 0.0f;
|
|
224 }
|
|
225 }
|
|
226 return EventHandling.Stop;
|
|
227 }
|
|
228
|
|
229 // Mouse move
|
|
230 EventHandling onMove(MouseMoveEvent e)
|
|
231 {
|
|
232 return EventHandling.Stop;
|
|
233 }
|
|
234
|
|
235 EventHandling onDT(TimeUpdateEvent e)
|
|
236 {
|
|
237 return EventHandling.Continue;
|
|
238 }
|
|
239
|
|
240 EventHandling onMouseEnter(MouseEnterEvent e)
|
|
241 {
|
|
242 return EventHandling.Continue;
|
|
243 }
|
|
244
|
|
245 EventHandling onMouseLeave(MouseLeaveEvent e)
|
|
246 {
|
|
247 return EventHandling.Continue;
|
|
248 }
|
|
249
|
|
250 protected
|
|
251 {
|
|
252 const bzVec2 gravity = bzVec2(0.0f, 0.0f);
|
|
253 bool allowSleep = false;
|
|
254
|
|
255 vec2 spawnRect;
|
|
256
|
|
257 vec2[] drawing;
|
|
258
|
|
259 vec2i screenSize = vec2i.zero;
|
|
260 vec2 mousePos = vec2.zero;
|
|
261
|
|
262 bool scaling = false;
|
|
263 bool full = false;
|
|
264
|
|
265 vec2 spawnStart;
|
|
266
|
|
267 bool preserveBullet = false;
|
|
268
|
|
269 float waterDelta = 0;
|
|
270 }
|
|
271
|
|
272 void boundaryViolated(bzBody rBody)
|
|
273 {
|
5
|
274 float x,y;
|
|
275
|
|
276 if(rBody.position.x > worldAABB.upperBound.x) {
|
|
277 x = worldAABB.lowerBound.x + 5;
|
|
278 rBody.position = bzVec2(x, rBody.position.y);
|
|
279 } else if (rBody.position.x < worldAABB.lowerBound.x) {
|
|
280 x = worldAABB.upperBound.x - 5;
|
|
281 rBody.position = bzVec2(x, rBody.position.y);
|
|
282 } else if (rBody.position.y > worldAABB.upperBound.y) {
|
|
283 y = worldAABB.lowerBound.y + 5;
|
|
284 rBody.position = bzVec2(rBody.position.x, y);
|
|
285 } else if(rBody.position.y < worldAABB.lowerBound.y) {
|
|
286 y = worldAABB.upperBound.y - 5;
|
|
287 rBody.position = bzVec2(rBody.position.x, y);
|
|
288 }
|
0
|
289 }
|
|
290
|
|
291 bool quit;
|
|
292
|
|
293 // Ortho view zoom
|
|
294 float zoom = 40;
|
|
295 int pointCount;
|
|
296 vec2 viewCenter;
|
|
297
|
|
298 bzWorld world;
|
|
299 Settings *settings;
|
|
300
|
|
301 // bzWorld boundary listener. Destroy bodies that leave world bzAABB
|
|
302 bzBoundaryListener m_boundaryListener;
|
|
303 bzContactListener m_contactListener;
|
|
304 ContactPoint[k_maxContactPoints] points;
|
|
305
|
|
306 ConfigChange!(vec2) editChange;
|
|
307 bool thrust;
|
2
|
308 bzAABB worldAABB;
|
|
309
|
0
|
310 Ship ship1;
|
|
311 Ship ship2;
|
|
312 }
|
|
313
|
|
314 // Utility functions
|
|
315 bzVec2 toBlaze(vec2 vec)
|
|
316 {
|
|
317 auto ret = bzVec2(vec.x, vec.y);
|
|
318 return ret;
|
|
319 }
|
|
320
|
|
321 vec2 rotate(vec2 point, float rad)
|
|
322 {
|
|
323 return vec2(cos(rad) * point.x - sin(rad) * point.y, sin(rad) * point.x + cos(rad) * point.y);
|
|
324 }
|
|
325
|
|
326 class Select
|
|
327 {
|
|
328 bool select;
|
|
329 }
|