3 define PI=3.14159265358979323846;
4 define BOX_TOLERANCE=0.5;
6 define max_version_code_supported=0;
7 define version_name="0.0";
8 //This string should be no longer than 15 characters
12 //Coordinates are in units of pixels
13 //Velocity is in units of pixels per frame
14 //A frame is defined as 1/LOGICAL_FPS seconds, or one iteration of g_frame()
16 define LOGICAL_FPS=512;
17 //is really 512, but this is for testing
18 //define DRAWN_FPS=60;
20 enum GameLineType {regular,speed,slow,decor};
21 enum GameFloorOrCeiling {floor,ceiling,both};
23 enum GameVehicleType {none, ball};
28 GameFloorOrCeiling floor_or_ceiling;
31 //complicated vehicles that can't be described by a single location/velocity need something
32 // so the camera will center on it.
37 void OnSerialize(IOChannel channel)
40 channel.Put(location);
41 channel.Put(velocity);
44 void OnUnserialize(IOChannel channel)
48 GameVehicleType vtype;
52 GameBall::OnUnserialize(class(GameBall), this, channel);
56 this = GameVehicle { };
61 channel.Get(location);
62 channel.Get(velocity);
65 virtual void Update(Game game);
67 class GameBall : GameVehicle {
73 double angle_velocity;
75 void OnSerialize(IOChannel channel)
77 GameVehicle::OnSerialize(channel);
79 channel.Put(elasticity);
81 channel.Put(angle_velocity);
84 void OnUnserialize(IOChannel channel)
87 GameVehicle::OnUnserialize(channel);
89 channel.Get(elasticity);
91 channel.Get(angle_velocity);
94 //support backtracing if ball is within box for line
95 void Update(Game game) {
96 //TODO: check for collision with other balls
99 BallLineReturn soonest = {INF,{0,0}};
100 GameLine *soonest_line;
102 BallLineReturn bl_result;
103 uint max_collisions_per_frame=16;
106 double x0 = location.x - radius;
107 double y0 = location.y - radius;
108 double x1 = location.x + radius;
109 double y1 = location.y + radius;
119 game.FindLinesInBox(x0,y0,x1,y1, true);
121 for (i:game.linesInBox)
125 //TODO: check for floor/ceiling/decor
128 vb = {i.x1-i.x0,i.y1-i.y0};
129 vp = {i.x0-location.x,i.y0-location.y};
131 BallLineCollide(bl_result,radius,velocity,vb,vp);
134 //TODO: Find a better way to deal with the teleport problem when ball sits in weird situations
135 // Currently, I'm just filtering out outrageously low negative ts (negative ts don't exactly make sense, but they keep the ball from falling through lines)
136 //if (t<=time_left && t<soonest.t) {
137 if (t>-100.0 && t<=time_left && t<soonest.t) {
140 //printf("Collide with line %ld Q=(%f,%f,%f,%f) L=(%f,%f,%f,%f)\n", soonest_line-game.lines.array, x0,y0,x1,y1, );
145 Vector2D v_parallel, v_normal;
146 Vector2D v_parallel_normalized;
148 double elasticity_cur;
149 bool clockcont = false; //if positive angle velocity contributes to speed toward parallel of ball velocity
151 //printf("COLLIDE! (location=(%f, %f), p = <%f, %f>)\n",location.x, location.y, soonest_line->x0-location.x,soonest_line->y0-location.y);
154 // PrintLn("Negative time ", soonest.t);
155 //if (soonest.t>=0.0 && soonest.t<0.01)
156 // PrintLn("Negative time after subtract", soonest.t);
158 soonest.t-=0.01; //don't fall all the way in, or we'll have multiple collisions after this
160 //compensate for the time taken to get to the collision
161 location.x += velocity.x*soonest.t;
162 location.y += velocity.y*soonest.t;
163 time_left -= soonest.t;
165 //decompose into parallel and normal vectors (with relation to line of collision)
166 v_parallel = soonest.s;
167 dp = DotProduct(velocity, soonest.s);
170 v_normal.Sub(velocity, v_parallel);
172 /* //elastic collision: v' = vp-e*vn;
173 v_normal.x *= elasticity;
174 v_normal.y *= elasticity;
175 velocity.Sub(v_parallel,v_normal);
178 if (v_normal.y!=0.0 && v_parallel.x!=0.0) {
179 if ((v_normal.y>0 && v_parallel.x>0) || (v_normal.y<0 && v_parallel.x<0))
182 if ((v_normal.x<0 && v_parallel.y>0) || (v_normal.x>0 && v_parallel.y<0))
186 //we need a reference to this before v_normal gets zeroed if elasticity_cur is 0.
187 v_parallel_normalized = {-v_normal.y, v_normal.x};
188 v_parallel_normalized.Normalize();
190 elasticity_cur = -elasticity;
191 if (soonest_line->type != regular)
192 elasticity_cur *= 0.3; //to make speed lines easier to use
194 //elastic collision: multiply the normal by negative elasticity
195 v_normal.x *= elasticity_cur;
196 v_normal.y *= elasticity_cur;
198 //an attempt at the effects of angular velocity and friction
200 double gs; //goal velocity squared
201 double g; //goal velocity
202 double gsfa; //goal velocity contributed by angular velocity
203 bool gneg = false; //g is negative
204 double dampener = 0.01;
206 gs = 3*DotProduct(v_parallel,v_parallel);
207 gsfa = 2*angle_velocity*angle_velocity*PI*radius*radius*radius;
209 if (angle_velocity<0)
215 gs /= 3+(2*PI*radius*dampener);
223 g *= 0.9993; //energy loss
224 v_parallel = v_parallel_normalized;
226 v_parallel.x = -v_parallel.x;
227 v_parallel.y = -v_parallel.y;
232 angle_velocity = g/radius;
234 angle_velocity = -g/radius;
237 velocity.Add(v_parallel, v_normal);
239 //at last, we'll handle speed/slow lines by simply changing the angular velocity
240 if (soonest_line->type == slow)
241 angle_velocity -= 0.001;
242 else if (soonest_line->type == speed)
243 angle_velocity += 0.001;
246 location.Add(location,velocity);
247 if (!max_collisions_per_frame--)
249 } while (soonest_line);
250 velocity.Add(velocity, game.gravity);
251 angle += angle_velocity;
252 if (angle<0 || angle>=2*3.1415926535897932384626)
253 angle = fmod(angle,2*3.1415926535897932384626); //prevents angle from accumulating an insanely high or low value
260 Array<GameLine> lines {};
261 Array<GameLine> linesInBox {};
262 Array<uint> linesInBoxIndices {};
263 Array<GameVehicle> vehicles {};
266 uint version_code; //which version of OpenRider to act like
268 version_code = max_version_code_supported;
270 ~Game() { FreeAll(); }
274 //we don't need to worry about the other things because they are automatically freed
277 void OnUnserialize(IOChannel channel)
281 channel.Get(gravity);
283 channel.Get(vehicles);
286 void OnSerialize(IOChannel channel)
288 channel.Put(gravity);
290 channel.Put(vehicles);
294 gravity = {0, 0.002};
300 version_code = max_version_code_supported;
302 void ResetBall(void) {
307 vehicles[0] = GameBall {
308 location = {-300+32,-200+32};
309 velocity = {0.0, 0.0};
311 elasticity = 0.3; //if this is 0, the ball rolls the wrong way for some reason
313 angle_velocity = 0.0;
324 void FrameMulti(uint count) {
328 //parameters are the camera's field of vision or bigger
329 void DrawFrame(double x0, double y0, double x1, double y1) {
330 FindLinesInBox(x0,y0,x1,y1, true);
335 DrawBall(master, (GameBall)i);
339 void AddLineSeries(double c_array[],uint count) {
345 lines.Add({c[0],c[1],c[2],c[3],regular,floor});
348 //returns true if any lines were erased
349 bool EraseAtBox(double x0, double y0, double x1, double y1) {
351 FindLinesInBox(x0,y0,x1,y1, false);
352 //the linesInBoxIndices array is assumed to be sorted
353 for (i:linesInBoxIndices) {
355 //FindLinesInBox only gives us an estimate of lines in the box, so we will do a finer check here
356 if (!LineReallyInBox(lines[i].x0, lines[i].y0, lines[i].x1, lines[i].y1, x0,y0,x1,y1))
358 //lines.Remove((IteratorPointer)(lines.array+i-erasures));
359 memmove(lines.array+r, lines.array+r+1, (lines.size-r-1)*sizeof(*lines.array));
362 lines.size -= erasures;
366 //used for efficiency, not for precision
367 //This only checks for lines in box by looking at the bounding boxes of lines in question
368 void FindLinesInBox(double x0, double y0, double x1, double y1, bool tolerance) {
369 #define Swap(a,b) {tmp=a;a=b;b=tmp;}
381 linesInBox.minAllocSize = lines.count;
382 linesInBoxIndices.minAllocSize = lines.count;
383 linesInBox.RemoveAll();
384 linesInBoxIndices.RemoveAll();
386 double lx0=i.x0,ly0=i.y0,lx1=i.x1,ly1=i.y1;
391 if (lx0>x1 || ly0>y1 || x0>lx1 || y0>ly1)
394 linesInBoxIndices.Add(&i - lines.array);
399 virtual void Instance::DrawLine(GameLine line);
400 virtual void Instance::DrawBall(GameBall ball);
406 void g_frame_multi(uint count);
407 void g_draw_frame(void);
409 void g_add_line(double x0,double y0,double x1,double y1,char type,char floor_or_ceiling);