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