-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPXEngine.jack
364 lines (285 loc) · 10.7 KB
/
PXEngine.jack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Physics Engine
// Handles the movement and collisions of physical objects
class PXEngine {
field Settings settings;
field ObjectManager OM;
field AdvancedMath mathEngine;
constructor PXEngine new(Settings gameSettings, AdvancedMath newmx, ObjectManager newOM) {
var int i;
let settings=gameSettings;
let OM=newOM;
let mathEngine=newmx;
return this;
}
method void applyAcceleration(Attributes att, int accelerationValue, int direction) {
// Adds an acceleration to the existing acceleration
var int xComp, yComp;
var int previousX, previousY;
do att.backup();
do att.setNeedsUpdate(true);
let xComp = (mathEngine.sin100(direction)/10 * accelerationValue) /10;
let yComp = (mathEngine.cos100(direction)/10 * accelerationValue*-1) /10;
let previousX = att.getAx();
let previousY = att.getAy();
do att.setAcceleration(previousX+xComp, previousY+yComp);
return;
}
method void applyCartesianAcceleration(Attributes att, int xComp, int yComp) {
// Adds an acceleration to the existing acceleration, using x and y components to
// skip the sine lookup
var int previousX, previousY;
do att.backup();
do att.setNeedsUpdate(true);
let previousX = att.getAx();
let previousY = att.getAy();
do att.setAcceleration(previousX+xComp, previousY+yComp);
return;
}
method void applyRotation(Attributes att, int rotation) {
do att.backup();
do att.setNeedsUpdate(true);
let rotation = rotation + att.getRotation();
do att.setRotation(rotation);
return;
}
method void stepForward(int timeDelta) {
// applies accelerations and velocitys to physical objects
// over a period of timeDelta
var int i, x1, y1, x2, y2;
var Bullet thisBullet;
var int friction10;
var Attributes currentAttributes;
var Player playerSpecimen;
// we're going to divide by a modifier here because a 10 millisecond
// frame delay might not be what we want for a physics time delta.
let timeDelta = timeDelta / settings.getphysicsTimeStepModifier();
let friction10=settings.getSpaceFriction() *timeDelta;
// do the players
let i = 0;
while( ~(OM.getPlayer(i)=0) ) {
let playerSpecimen=OM.getPlayer(i);
let currentAttributes = playerSpecimen.getAttributes();
// Apply accelerations
let x1 = currentAttributes.getAx();
let y1 = currentAttributes.getAy();
let x2 = currentAttributes.getVx();
let y2 = currentAttributes.getVy();
// but only apply them if there is something to apply
if ( ~(x1=0) | ~(x2=0) | ~(y1=0) | ~(y2=0) ){
// first, apply space friction
let x2= ((x2*1*friction10)/10);
let y2= ((y2*1*friction10)/10);
do currentAttributes.backup();
do currentAttributes.setNeedsUpdate(true); // redraw on next frame
do currentAttributes.setVelocity( (x1*timeDelta) + x2,
(y1*timeDelta) + y2 );
// and reset accelerations now that we've applied them.
do currentAttributes.setAcceleration(0,0);
// Apply velocitys
let x1 = currentAttributes.getPx();
let y1 = currentAttributes.getPy();
// x2 and y2 already have velocitys assigned
do currentAttributes.setCoordinates( (x2*timeDelta) + x1,
(y2*timeDelta) + y1 );
}
let i=i+1;
}
// now do the bullets
let i = 0;
while( ~(OM.getBullet(i)=0)) {
let currentAttributes = OM.getBulletAttributes(i);
// apply gravitation field accelerations
do applyGravitationalField(currentAttributes);
// Apply accelerations to velocities
let x1 = currentAttributes.getAx();
let y1 = currentAttributes.getAy();
let x2 = currentAttributes.getVx();
let y2 = currentAttributes.getVy();
// no space friction for bullets!
do currentAttributes.backup();
do currentAttributes.setNeedsUpdate(true); // redraw on next frame
do currentAttributes.setVelocity( (x1*timeDelta) + x2,
(y1*timeDelta) + y2 );
// and reset accelerations now that we've applied them.
do currentAttributes.setAcceleration(0,0);
// Apply velocitys
let x1 = currentAttributes.getPx();
let y1 = currentAttributes.getPy();
// x2 and y2 already have velocitys assigned
do currentAttributes.setCoordinates( (x2*timeDelta) + x1,
(y2*timeDelta) + y1 );
let i=i+1;
}
return;
}
method bool detectCollision(bool executeKill) {
// returns true if there are overlapping objects.
// when executeKill is true, this function will set the
// isDead flag on every attribute that is party to a collision
var int i,j, res, dist;
var int x1, y1, x2, y2;
var int r1, r2;
var bool collisionOccured;
var Attributes att1, att2;
let i=0;
let att1 = OM.getAttributes(i);
let collisionOccured = false;
// iterate throuh all objects.
while (~(att1=0) ) {
let x1=att1.getPx();
let y1=att1.getPy();
let r1=att1.getRadius();
let j=0;
let att2 = OM.getAttributes(j);
// check against all other objects (skipping itself)
// also, skip dead objects.
while( ~(att2=0) & ~(j=i) ) {
let x2=att2.getPx();
let y2=att2.getPy();
let r2=att2.getRadius();
if ( ~(att1.getIsDead()) & ~(att2.getIsDead()) ) {
let dist = getDistance(x1,y1,x2,y2,r1+r2);
if (dist < (r1+r2)) {
let collisionOccured=true;
if (executeKill) {
// add velocities together to make cooler explosions. (but not for gravity sinks)
if (~(att1.getType()=2) & ~(att2.getType()=2) ) {
do att2.setVelocity( att1.getVx() + att2.getVx(), att1.getVy() + att2.getVy() );
}
// dont kill gravity sinks
if (~(att1.getType()=2)) { do att1.setIsDead(true); }
if (~(att2.getType()=2)) { do att2.setIsDead(true); }
}
}
}
let j=j+1;
let att2=OM.getAttributes(j);
} // next j
// Now check if the object it outside the game bounds.
// x1 and y1 are still the object from the outside loop.
// x2 and y2 are now being overwritten to study the screen bounds.
let res=settings.getworldResolutionX();
let x1=x1/res;
let res=settings.getworldResolutionY();
let y1=y1/res;
let x2 = 0;
if ((x1-r1)<x2 & (~att1.getIsDead()) ){
let collisionOccured=true;
if (executeKill){ do att1.setIsDead(true); }
}
let x2 = settings.getscreenSizeX();
if ((x1+r1)>x2 & (~att1.getIsDead()) ){
let collisionOccured=true;
if (executeKill){ do att1.setIsDead(true); }
}
let y2 = 0;
if ((y1-r1)<y2 & (~att1.getIsDead()) ){
let collisionOccured=true;
if (executeKill){ do att1.setIsDead(true); }
}
let y2 = settings.getscreenSizeY();
if ((y1+r1)>y2 & (~att1.getIsDead()) ){
let collisionOccured=true;
if (executeKill){ do att1.setIsDead(true); }
}
// i++
let i=i+1;
let att1=OM.getAttributes(i);
}
//return false;
return collisionOccured;
}
method bool withinDistanceOfAnotherPlayer(Player passedPlayer, int distance) {
var int i;
var bool isWithinDistance;
var Player comparisonPlayer;
var Attributes att1, att2;
let isWithinDistance = false;
let att1 = passedPlayer.getAttributes();
let i=0;
let comparisonPlayer = OM.getPlayer(i);
while (~(comparisonPlayer=0) & (~(comparisonPlayer = passedPlayer)))
{
let comparisonPlayer = OM.getPlayer(i);
let att2 = comparisonPlayer.getAttributes();
if ( getDistance( att1.getPx(), att1.getPy(),
att2.getPx(), att2.getPy(),distance+1 ) < distance ) {
let isWithinDistance = true;
}
let i=i+1;
let comparisonPlayer = OM.getPlayer(i);
}
return isWithinDistance;
}
method int getDistance(int x1,int y1,int x2,int y2, int envelope) {
// gets the distance between two cartesian coordinates.
// To prevent overflows when squaring large numbers, we only
// calculate the actual distance if the two points are close to the
// envelope
var int ratio;
let x1 = Math.abs(x1-x2);
let y1 = Math.abs(y1-y2);
// now x1 and y1 are the x and y components of the distance
// convert to pixel space, since that's what units our radii are in.
let x1 = x1 / settings.getworldResolutionX();
let y1 = y1 / settings.getworldResolutionY();
// To avoid overflow, we divide by ten until our calculations
// can be done in 15 bits and then remultiply after the calculation
let ratio=1;
// the biggest distances we can calculate are 128 so the function
// scales down the request
while ( (x1>128) | (y1>128) )
{
let x1 = x1/2;
let y1 = y1/2;
let ratio = ratio*2.8;
}
//do Output.printString("<");
//do Output.printInt(x1);
//do Output.printString(",");
//do Output.printInt(y1);
//do Output.printString(">");
let x1 = Math.sqrt( (x1*x1) + (y1*y1) );
// now x1 has the total distance calculation
let x1=x1*ratio;
return x1;
}
method void applyGravitationalField(Attributes att) {
// Applies acceleration to a specific object based on the
// proximity to gravitational sinks
var int i, m1, m2,d,f;
var int x1,y1,x2,y2;
var int xComp, yComp;
var Attributes currentAttributes;
var GravitySink currentGS;
let m1 = att.getMass();
let x1=att.getPx();
let y1 = att.getPy();
let i = 0;
while(~(OM.getGravitySink(i)=0) & (i<settings.getMaxGravitySinks()) ) {
let currentGS = OM.getGravitySink(i);
let currentAttributes = currentGS.getAttributes();
let x2=currentAttributes.getPx();
let y2=currentAttributes.getPy();
let m2 = currentAttributes.getMass();
// if the distance between two objects is > 10000 then it's negligable
let d= getDistance(x1,y1,x2,y2,1000);
// Universal gravitational formula: F=(m1*m2)/d
let f = (m1*m2)/d;
// now we're going to skip a few sine calculations by realizing that
// Fx=F*sin(theta) and sin(theta) = Dx/D, which are two values we know.
let xComp = f*( (x2-x1)/settings.getworldResolutionX()/d); // x component
let yComp = f*( (y2-y1)/settings.getworldResolutionY()/d); // y component
// now apply this gravitational pull to our object
do applyCartesianAcceleration(att, xComp,yComp);
let x2=att.getAx();
let y2 = att.getAy();
let i=i+1;
}
return;
}
method void dispose() {
do Memory.deAlloc(mathEngine);
do Memory.deAlloc(this);
return; }
}