1 /*
2  *   GRacer
3  *
4  *   Copyright (C) 1999 Takashi Matsuda <matsu@users.sourceforge.net>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  */
21 
22 #include <unistd.h>
23 #include <assert.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include "gr_world.h"
27 #include <common/gr_memory.h>
28 #include <common/gr_debug.h>
29 #include <common/gr_angle.h>
30 #include "lap.h"
31 
32 #define DEFAULT_MIN_INTERVAL		0.02
33 
34 typedef struct {
35   GrRef ref;
36 
37   GrVehicle *vehicle;
38   GrSList *triggers;
39 } GrWorldEntry;
40 
41 
42 static void
gr_world_free(GrWorld * world)43 gr_world_free (GrWorld *world)
44 {
45   if (world->course_data)
46     gr_DECREF (world->course_data);
47 
48   while (world->vehicles) {
49     gr_DECREF(world->vehicles->data);
50     world->vehicles = gr_slist_remove (world->vehicles);
51   }
52 
53   free (world);
54 }
55 
56 
57 static void
gr_world_entry_free(GrWorldEntry * entry)58 gr_world_entry_free (GrWorldEntry *entry)
59 {
60   if (entry) {
61     while (entry->triggers) {
62       gr_DECREF(entry->triggers->data);
63       entry->triggers = gr_slist_remove (entry->triggers);
64     }
65   }
66   free (entry);
67 }
68 
69 
70 GrWorld *
gr_world_new(void)71 gr_world_new (void)
72 {
73   GrWorld *world = gr_new0 (GrWorld, 1);
74   assert (world != NULL);
75 
76   gr_FREE_FUNC (world, gr_world_free);
77 
78   world->minimam_interval = DEFAULT_MIN_INTERVAL;
79 
80   return world;
81 }
82 
83 
84 static void
gr_world_check_trigger_real(GrWorld * world,GrTrigger * trigger,GrVehicle * vehicle,int side)85 gr_world_check_trigger_real (GrWorld *world,
86 			     GrTrigger *trigger,
87 			     GrVehicle *vehicle,
88 			     int side)
89 {
90   if (trigger->condition & GR_TRIGGER_CLINE) {
91     if (trigger->cline != vehicle->cinfo.current.cline)
92       return;
93   }
94 
95   if (trigger->condition & GR_CLINE_MASK) {
96     if ((trigger->condition & GR_CLINE_MASK) !=
97 	(vehicle->cinfo.res & GR_CLINE_MASK))
98       return;
99   }
100 
101   if (trigger->condition & GR_TRIGGER_LAP_PLUS) {
102     if ((vehicle->cinfo.res & GR_CLINE_MASK) != GR_CLINE_A_TO_B)
103       return;
104 
105     if (vehicle->lap >= vehicle->lap_count)
106       return;
107   }
108 
109   if (trigger->condition & GR_TRIGGER_LAP) {
110     if (trigger->lap != vehicle->lap)
111       return;
112   }
113 
114   if (trigger->condition & GR_TRIGGER_SPEED_LT) {
115     if (trigger->speed_lt <= vehicle->state[side].rpm)
116       return;
117   }
118 
119   if (trigger->condition & GR_TRIGGER_SPEED_GT) {
120     if (trigger->speed_gt >= vehicle->state[side].rpm)
121       return;
122   }
123 
124   if (trigger->condition & GR_TRIGGER_RPM_LT) {
125     if (trigger->rpm_lt <= vehicle->state[side].rpm)
126       return;
127   }
128 
129   if (trigger->condition & GR_TRIGGER_RPM_GT) {
130     if (trigger->rpm_gt >= vehicle->state[side].rpm)
131       return;
132   }
133 
134   (*trigger->callback) (world, vehicle, trigger);
135 }
136 
137 
138 static void
gr_world_check_trigger(GrWorld * world,int side)139 gr_world_check_trigger (GrWorld *world, int side)
140 {
141   GrTrigger *trigger;
142   GrWorldEntry *entry;
143   GrVehicle *vehicle;
144   GrSList *list = world->vehicles;
145   GrSList *tlist;
146 
147   gr_FOREACH (list, entry) {
148     vehicle = entry->vehicle;
149 
150     vehicle->state[side].spd =
151       gr_vertex_length (&vehicle->body.state[side].spd);
152 
153     vehicle->cinfo.prev = vehicle->cinfo.current;
154     vehicle->cinfo.current.pos = vehicle->body.state[0].pos;
155     gr_course_check_control_line (world->course_data, &vehicle->cinfo);
156 
157     if (gr_cline_prev_region (vehicle->cinfo.res) !=
158 	gr_cline_current_region (vehicle->cinfo.res)) {
159       vehicle->time = (world->current_time - world->previous_time)
160 		      * vehicle->cinfo.cross_ratio + world->previous_time;
161     }
162     switch (vehicle->cinfo.res) {
163     case GR_CLINE_A_TO_B:
164       vehicle->lap_count ++;
165       break;
166 
167     case GR_CLINE_B_TO_A:
168       vehicle->lap_count --;
169       break;
170     }
171 
172     tlist = entry->triggers;
173     gr_FOREACH (tlist, trigger) {
174       gr_world_check_trigger_real (world, trigger, vehicle, side);
175     }
176 
177     tlist = world->triggers;
178     gr_FOREACH (tlist, trigger) {
179       gr_world_check_trigger_real (world, trigger, vehicle, side);
180     }
181 
182     if (vehicle->lap < vehicle->lap_count) {
183       vehicle->lap = vehicle->lap_count;
184     }
185   }
186 }
187 
188 
189 static GrSList *
remove_trigger(GrSList * list,GrTrigger * trigger)190 remove_trigger (GrSList *list, GrTrigger *trigger)
191 {
192   GrSList *tmp;
193 
194   if (list == NULL) {
195     return NULL;
196   } else if (list->data == trigger) {
197     gr_DECREF(trigger);
198     return gr_slist_remove (list);
199   } else {
200     tmp = list;
201     while (tmp->next != NULL) {
202       if (tmp->next->data == trigger) {
203 	gr_DECREF(trigger);
204 	tmp->next = gr_slist_remove (tmp->next);
205 	break;
206       }
207       tmp = tmp->next;
208     }
209     return list;
210   }
211 }
212 
213 void
gr_world_remove_trigger(GrWorld * world,GrTrigger * trigger)214 gr_world_remove_trigger (GrWorld *world, GrTrigger *trigger)
215 {
216   GrSList *list;
217   GrWorldEntry *entry;
218 
219   if (trigger->vehicle) {
220     list = world->vehicles;
221     gr_FOREACH (list, entry) {
222       if (entry->vehicle == trigger->vehicle) {
223 	entry->triggers = remove_trigger (entry->triggers, trigger);
224 	break;
225       }
226     }
227   } else {
228     world->triggers = remove_trigger (world->triggers, trigger);
229   }
230 }
231 
232 void
gr_world_remove_triggers_of_vehicle(GrWorld * world,GrVehicle * vehicle)233 gr_world_remove_triggers_of_vehicle (GrWorld *world, GrVehicle *vehicle)
234 {
235   GrSList *list;
236   GrWorldEntry *entry;
237 
238   list = world->vehicles;
239   gr_FOREACH (list, entry) {
240     if (entry->vehicle == vehicle) {
241       while (entry->triggers) {
242 	gr_DECREF(entry->triggers->data);
243 	entry->triggers = gr_slist_remove (entry->triggers);
244       }
245       break;
246     }
247   }
248 }
249 
250 void
gr_world_remove_triggers(GrWorld * world)251 gr_world_remove_triggers (GrWorld *world)
252 {
253   GrSList *list;
254   GrWorldEntry *entry;
255 
256   list = world->vehicles;
257   gr_FOREACH (list, entry) {
258     gr_world_remove_triggers_of_vehicle (world, entry->vehicle);
259   }
260 
261   while (world->triggers) {
262     gr_DECREF(world->triggers->data);
263     world->triggers = gr_slist_remove (world->triggers);
264   }
265 }
266 
267 void
gr_world_add_trigger(GrWorld * world,GrTrigger * trigger)268 gr_world_add_trigger (GrWorld *world, GrTrigger *trigger)
269 {
270   GrSList *list;
271   GrWorldEntry *entry;
272 
273   if (trigger->vehicle) {
274     list = world->vehicles;
275     gr_FOREACH (list, entry) {
276       if (entry->vehicle == trigger->vehicle) {
277 	entry->triggers = gr_slist_prepend (entry->triggers, trigger);
278 	gr_INCREF (trigger);
279 	return;
280       }
281     }
282   } else {
283     world->triggers = gr_slist_prepend (world->triggers, trigger);
284     gr_INCREF (trigger);
285   }
286 }
287 
288 void
gr_world_do_simulate(GrWorld * world,double interval)289 gr_world_do_simulate (GrWorld *world, double interval) {
290   int i, loop;
291   GrVehicle *vehicle;
292   GrWorldEntry *entry;
293   GrSList *list;
294   double h;
295 
296   loop = ceil (interval / (world->minimam_interval * 2.0));
297   h = interval / (loop * 2);
298 
299   for (i=0; i<loop; i++) {
300     list = world->vehicles;
301     gr_FOREACH (list, entry) {
302       vehicle = entry->vehicle;
303 
304       gr_vehicle_integral (vehicle, world->course_data, 0, 1, h);
305       gr_vehicle_integral (vehicle, world->course_data, 1, 0, h);
306     }
307   }
308   list = world->vehicles;
309   gr_FOREACH (list, entry) {
310     vehicle = entry->vehicle;
311 
312     if (vehicle->body.state[0].rot.c.x < RAD(-45)) {
313       vehicle->body.state[0].rot.c.x = RAD(-45);
314     } else if (vehicle->body.state[0].rot.c.x > RAD(45)) {
315       vehicle->body.state[0].rot.c.x = RAD(45);
316     }
317 
318     if (vehicle->body.state[0].rot.c.y < RAD(-45)) {
319       vehicle->body.state[0].rot.c.y = RAD(-45);
320     } else if (vehicle->body.state[0].rot.c.y > RAD(45)) {
321       vehicle->body.state[0].rot.c.y = RAD(45);
322     }
323   }
324 
325   world->previous_time = world->current_time;
326   world->current_time += interval;
327 
328   gr_world_check_trigger (world, 0);
329 }
330 
331 void
gr_world_add_vehicle(GrWorld * world,GrVehicle * vehicle)332 gr_world_add_vehicle (GrWorld *world, GrVehicle *vehicle)
333 {
334   GrSList *list;
335   GrWorldEntry *entry;
336 
337   list = world->vehicles;
338   gr_FOREACH (list, entry) {
339     assert (entry->vehicle != vehicle);
340   }
341 
342   entry = gr_new0 (GrWorldEntry, 1);
343   gr_FREE_FUNC (entry, gr_world_entry_free);
344 
345   entry->vehicle = vehicle;
346   gr_INCREF(vehicle);
347 
348   world->vehicles = gr_slist_prepend (world->vehicles, entry);
349 }
350 
351 void
gr_world_remove_vehicle(GrWorld * world,GrVehicle * vehicle)352 gr_world_remove_vehicle (GrWorld *world, GrVehicle *vehicle)
353 {
354   GrSList *list;
355   GrWorldEntry *entry;
356 
357   if (!world->vehicles)
358     return;
359 
360   list = world->vehicles;
361   entry = list->data;
362   if (entry->vehicle == vehicle) {
363     gr_DECREF(vehicle);
364     gr_DECREF(entry);
365     world->vehicles = gr_slist_remove (world->vehicles);
366   } else {
367     while (list->next != NULL) {
368       entry = list->next->data;
369       if (entry->vehicle == vehicle) {
370 	gr_DECREF(vehicle);
371 	gr_DECREF(entry);
372 	list->next = gr_slist_remove (list->next);
373 	return;
374       }
375     }
376   }
377 }
378 
379 void
gr_world_set_course(GrWorld * world,GrCourse * course)380 gr_world_set_course (GrWorld *world, GrCourse *course)
381 {
382   if (world->course_data && world->course_data != course) {
383     gr_DECREF (world->course_data);
384   }
385   world->course_data = course;
386 }
387