/* * GRacer * * Copyright (C) 1999 Takashi Matsuda * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include #include "gr_world.h" #include #include #include #include "lap.h" #define DEFAULT_MIN_INTERVAL 0.02 typedef struct { GrRef ref; GrVehicle *vehicle; GrSList *triggers; } GrWorldEntry; static void gr_world_free (GrWorld *world) { if (world->course_data) gr_DECREF (world->course_data); while (world->vehicles) { gr_DECREF(world->vehicles->data); world->vehicles = gr_slist_remove (world->vehicles); } free (world); } static void gr_world_entry_free (GrWorldEntry *entry) { if (entry) { while (entry->triggers) { gr_DECREF(entry->triggers->data); entry->triggers = gr_slist_remove (entry->triggers); } } free (entry); } GrWorld * gr_world_new (void) { GrWorld *world = gr_new0 (GrWorld, 1); assert (world != NULL); gr_FREE_FUNC (world, gr_world_free); world->minimam_interval = DEFAULT_MIN_INTERVAL; return world; } static void gr_world_check_trigger_real (GrWorld *world, GrTrigger *trigger, GrVehicle *vehicle, int side) { if (trigger->condition & GR_TRIGGER_CLINE) { if (trigger->cline != vehicle->cinfo.current.cline) return; } if (trigger->condition & GR_CLINE_MASK) { if ((trigger->condition & GR_CLINE_MASK) != (vehicle->cinfo.res & GR_CLINE_MASK)) return; } if (trigger->condition & GR_TRIGGER_LAP_PLUS) { if ((vehicle->cinfo.res & GR_CLINE_MASK) != GR_CLINE_A_TO_B) return; if (vehicle->lap >= vehicle->lap_count) return; } if (trigger->condition & GR_TRIGGER_LAP) { if (trigger->lap != vehicle->lap) return; } if (trigger->condition & GR_TRIGGER_SPEED_LT) { if (trigger->speed_lt <= vehicle->state[side].rpm) return; } if (trigger->condition & GR_TRIGGER_SPEED_GT) { if (trigger->speed_gt >= vehicle->state[side].rpm) return; } if (trigger->condition & GR_TRIGGER_RPM_LT) { if (trigger->rpm_lt <= vehicle->state[side].rpm) return; } if (trigger->condition & GR_TRIGGER_RPM_GT) { if (trigger->rpm_gt >= vehicle->state[side].rpm) return; } (*trigger->callback) (world, vehicle, trigger); } static void gr_world_check_trigger (GrWorld *world, int side) { GrTrigger *trigger; GrWorldEntry *entry; GrVehicle *vehicle; GrSList *list = world->vehicles; GrSList *tlist; gr_FOREACH (list, entry) { vehicle = entry->vehicle; vehicle->state[side].spd = gr_vertex_length (&vehicle->body.state[side].spd); vehicle->cinfo.prev = vehicle->cinfo.current; vehicle->cinfo.current.pos = vehicle->body.state[0].pos; gr_course_check_control_line (world->course_data, &vehicle->cinfo); if (gr_cline_prev_region (vehicle->cinfo.res) != gr_cline_current_region (vehicle->cinfo.res)) { vehicle->time = (world->current_time - world->previous_time) * vehicle->cinfo.cross_ratio + world->previous_time; } switch (vehicle->cinfo.res) { case GR_CLINE_A_TO_B: vehicle->lap_count ++; break; case GR_CLINE_B_TO_A: vehicle->lap_count --; break; } tlist = entry->triggers; gr_FOREACH (tlist, trigger) { gr_world_check_trigger_real (world, trigger, vehicle, side); } tlist = world->triggers; gr_FOREACH (tlist, trigger) { gr_world_check_trigger_real (world, trigger, vehicle, side); } if (vehicle->lap < vehicle->lap_count) { vehicle->lap = vehicle->lap_count; } } } static GrSList * remove_trigger (GrSList *list, GrTrigger *trigger) { GrSList *tmp; if (list == NULL) { return NULL; } else if (list->data == trigger) { gr_DECREF(trigger); return gr_slist_remove (list); } else { tmp = list; while (tmp->next != NULL) { if (tmp->next->data == trigger) { gr_DECREF(trigger); tmp->next = gr_slist_remove (tmp->next); break; } tmp = tmp->next; } return list; } } void gr_world_remove_trigger (GrWorld *world, GrTrigger *trigger) { GrSList *list; GrWorldEntry *entry; if (trigger->vehicle) { list = world->vehicles; gr_FOREACH (list, entry) { if (entry->vehicle == trigger->vehicle) { entry->triggers = remove_trigger (entry->triggers, trigger); break; } } } else { world->triggers = remove_trigger (world->triggers, trigger); } } void gr_world_remove_triggers_of_vehicle (GrWorld *world, GrVehicle *vehicle) { GrSList *list; GrWorldEntry *entry; list = world->vehicles; gr_FOREACH (list, entry) { if (entry->vehicle == vehicle) { while (entry->triggers) { gr_DECREF(entry->triggers->data); entry->triggers = gr_slist_remove (entry->triggers); } break; } } } void gr_world_remove_triggers (GrWorld *world) { GrSList *list; GrWorldEntry *entry; list = world->vehicles; gr_FOREACH (list, entry) { gr_world_remove_triggers_of_vehicle (world, entry->vehicle); } while (world->triggers) { gr_DECREF(world->triggers->data); world->triggers = gr_slist_remove (world->triggers); } } void gr_world_add_trigger (GrWorld *world, GrTrigger *trigger) { GrSList *list; GrWorldEntry *entry; if (trigger->vehicle) { list = world->vehicles; gr_FOREACH (list, entry) { if (entry->vehicle == trigger->vehicle) { entry->triggers = gr_slist_prepend (entry->triggers, trigger); gr_INCREF (trigger); return; } } } else { world->triggers = gr_slist_prepend (world->triggers, trigger); gr_INCREF (trigger); } } void gr_world_do_simulate (GrWorld *world, double interval) { int i, loop; GrVehicle *vehicle; GrWorldEntry *entry; GrSList *list; double h; loop = ceil (interval / (world->minimam_interval * 2.0)); h = interval / (loop * 2); for (i=0; ivehicles; gr_FOREACH (list, entry) { vehicle = entry->vehicle; gr_vehicle_integral (vehicle, world->course_data, 0, 1, h); gr_vehicle_integral (vehicle, world->course_data, 1, 0, h); } } list = world->vehicles; gr_FOREACH (list, entry) { vehicle = entry->vehicle; if (vehicle->body.state[0].rot.c.x < RAD(-45)) { vehicle->body.state[0].rot.c.x = RAD(-45); } else if (vehicle->body.state[0].rot.c.x > RAD(45)) { vehicle->body.state[0].rot.c.x = RAD(45); } if (vehicle->body.state[0].rot.c.y < RAD(-45)) { vehicle->body.state[0].rot.c.y = RAD(-45); } else if (vehicle->body.state[0].rot.c.y > RAD(45)) { vehicle->body.state[0].rot.c.y = RAD(45); } } world->previous_time = world->current_time; world->current_time += interval; gr_world_check_trigger (world, 0); } void gr_world_add_vehicle (GrWorld *world, GrVehicle *vehicle) { GrSList *list; GrWorldEntry *entry; list = world->vehicles; gr_FOREACH (list, entry) { assert (entry->vehicle != vehicle); } entry = gr_new0 (GrWorldEntry, 1); gr_FREE_FUNC (entry, gr_world_entry_free); entry->vehicle = vehicle; gr_INCREF(vehicle); world->vehicles = gr_slist_prepend (world->vehicles, entry); } void gr_world_remove_vehicle (GrWorld *world, GrVehicle *vehicle) { GrSList *list; GrWorldEntry *entry; if (!world->vehicles) return; list = world->vehicles; entry = list->data; if (entry->vehicle == vehicle) { gr_DECREF(vehicle); gr_DECREF(entry); world->vehicles = gr_slist_remove (world->vehicles); } else { while (list->next != NULL) { entry = list->next->data; if (entry->vehicle == vehicle) { gr_DECREF(vehicle); gr_DECREF(entry); list->next = gr_slist_remove (list->next); return; } } } } void gr_world_set_course (GrWorld *world, GrCourse *course) { if (world->course_data && world->course_data != course) { gr_DECREF (world->course_data); } world->course_data = course; }