ref: 7cfaff0c7b618e8150bd97ad43fca9899f4a5514
dir: /src/wipeout/ship_ai.c/
#include "../mem.h" #include "../input.h" #include "../system.h" #include "../utils.h" #include "object.h" #include "track.h" #include "ship.h" #include "weapon.h" #include "hud.h" #include "droid.h" #include "camera.h" #include "ship_ai.h" #include "game.h" vec3_t ship_ai_strat_hold_center(ship_t *self, track_face_t *face); vec3_t ship_ai_strat_hold_right(ship_t *self, track_face_t *face); vec3_t ship_ai_strat_hold_left(ship_t *self, track_face_t *face); vec3_t ship_ai_strat_block(ship_t *self, track_face_t *face); vec3_t ship_ai_strat_avoid(ship_t *self, track_face_t *face); vec3_t ship_ai_strat_avoid_other(ship_t *self, track_face_t *face); vec3_t ship_ai_strat_zig_zag(ship_t *self, track_face_t *face); void ship_ai_update_intro(ship_t *self) { self->temp_target = self->position; self->update_func = ship_ai_update_intro_await_go; self->sfx_engine_thrust = sfx_reserve_loop(SFX_ENGINE_REMOTE); sfx_set_position(self->sfx_engine_thrust, self->position, self->velocity, 0.1); } void ship_ai_update_intro_await_go(ship_t *self) { self->position.y = self->temp_target.y + sin(self->update_timer * (80.0 + self->pilot * 3.0) * 30.0 * M_PI * 2.0 / 4096.0) * 32; self->update_timer -= system_tick(); if (self->update_timer <= UPDATE_TIME_GO) { self->update_func = ship_ai_update_race; } } vec3_t ship_ai_strat_hold_left(ship_t *self, track_face_t *face) { vec3_t fv1 = face->tris[0].vertices[1].pos; vec3_t fv2 = face->tris[0].vertices[0].pos; return vec3_mulf(vec3_sub(fv1, fv2), 0.5); } vec3_t ship_ai_strat_hold_right(ship_t *self, track_face_t *face) { vec3_t fv1 = face->tris[0].vertices[0].pos; vec3_t fv2 = face->tris[0].vertices[1].pos; return vec3_mulf(vec3_sub(fv1, fv2), 0.5); } vec3_t ship_ai_strat_hold_center(ship_t *self, track_face_t *face) { return vec3(0, 0, 0); } vec3_t ship_ai_strat_block(ship_t *self, track_face_t *face) { if (flags_is(g.ships[g.pilot].flags, SHIP_LEFT_SIDE)) { return ship_ai_strat_hold_left(self, face); } else { return ship_ai_strat_hold_right(self, face); } } vec3_t ship_ai_strat_avoid(ship_t *self, track_face_t *face) { if (flags_is(g.ships[g.pilot].flags, SHIP_LEFT_SIDE)) { return ship_ai_strat_hold_right(self, face); } else { return ship_ai_strat_hold_left(self, face); } } vec3_t ship_ai_strat_avoid_other(ship_t *self, track_face_t *face) { int min_section_num = 100; ship_t *avoid_ship; for (int i = 0; i < NUM_PILOTS; i++) { if (i != self->pilot) { int section_diff = g.ships[i].total_section_num - self->total_section_num; if (min_section_num < section_diff) { min_section_num = section_diff; avoid_ship = &g.ships[i]; } } } if (avoid_ship && min_section_num < 10 && min_section_num > -2) { if (flags_is(avoid_ship->flags, SHIP_LEFT_SIDE)) { return ship_ai_strat_hold_right(self, face); } else { return ship_ai_strat_hold_left(self, face); } } return vec3(0, 0, 0); } vec3_t ship_ai_strat_zig_zag(ship_t *self, track_face_t *face) { int update_count = (self->update_timer * 30)/50; if (update_count % 2) { return ship_ai_strat_hold_right(self, face); } else { return ship_ai_strat_hold_left(self, face); } } void ship_ai_update_race(ship_t *self) { vec3_t offset_vector = vec3(0, 0, 0); ship_t *player = &(g.ships[g.pilot]); if (self->ebolt_timer > 0) { self->ebolt_timer -= system_tick(); } if (self->ebolt_timer <= 0) { flags_rm(self->flags, SHIP_ELECTROED); } int behind_speed = def.circuts[g.circut].settings[g.race_class].behind_speed; if (flags_not(self->flags, SHIP_FLYING)) { // Find First track base section track_face_t *face = track_section_get_base_face(self->section); int section_diff = self->total_section_num - player->total_section_num; flags_rm(self->flags, SHIP_JUST_IN_FRONT); if (self == player) { self->update_strat_func = ship_ai_strat_avoid_other; if (self->remote_thrust_max > self->speed) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } else { // Make global DPA decisions , these will effect the craft in // relation to your race position // Accelerate remote ships away at start, start_accelerate_count set in // InitShipData and is an exponential progression if (self->start_accelerate_timer > 0) { self->start_accelerate_timer -= system_tick(); self->update_timer = 0; self->update_strat_func = ship_ai_strat_avoid; if ((self->remote_thrust_max + 1200) > self->speed) { self->speed += (self->remote_thrust_mag + 150) * 30 * system_tick(); } } // Ship has been left WELL BEHIND; set it to avoid // other ships and update its speed as normal else if (section_diff < -10) { // Ship behind, AVOID self->update_timer = 0; self->update_strat_func = ship_ai_strat_avoid; // If ship has been well passed, increase its speed to allow // it to make a challenge when the player fouls up if (((self->remote_thrust_max + behind_speed) > self->speed)) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } // Ship is JUST AHEAD else if ((section_diff <= 4) && (section_diff > 0)) { // Ship close by, beware does not account for lapped opponents yet flags_add(self->flags, SHIP_JUST_IN_FRONT); if (self->update_timer <= 0) { // Make New Decision int chance = rand_int(0, 64); // 12 self->update_timer = UPDATE_TIME_JUST_FRONT; if (self->fight_back) { // Ship wants to make life difficult if ((chance < 40) || (self->weapon_type == WEAPON_TYPE_NONE)) { // Ship will try to block you self->update_strat_func = ship_ai_strat_block; } else if ((chance >= 40) && (chance < 52)) { // Ship will attempt to drop mines in your path self->update_strat_func = ship_ai_strat_block; if (flags_not(self->flags, SHIP_SHIELDED) && flags_is(self->flags, SHIP_RACING)) { sfx_play(SFX_VOICE_MINES); self->weapon_type = WEAPON_TYPE_MINE; weapons_fire_delayed(self, self->weapon_type); } } else if ((chance >= 52) && (chance < 64)) { // Ship will raise its shield self->update_strat_func = ship_ai_strat_block; if (flags_not(self->flags, SHIP_SHIELDED)) { self->weapon_type = WEAPON_TYPE_SHIELD; weapons_fire(self, self->weapon_type); } } } else { // Let the first ships be easy to pass self->update_strat_func = ship_ai_strat_avoid; } } self->update_timer -= system_tick(); if (flags_is(self->flags, SHIP_OVERTAKEN)) { // If ship has just overtaken, slow it down to a reasonable speed if ((self->remote_thrust_max + behind_speed) > self->speed) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } else { // Increase the speed of any craft just in front slightly if (((self->remote_thrust_max + (behind_speed >> 1)) > self->speed)) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } } // Ship is JUST BEHIND; we must decided if and how many times it 'should have a go back' else if ((section_diff >= -10) && (section_diff <= 0)) { // Ship just behind, MAKE DECISION if (self->update_timer <= 0) { // Make New Decision self->update_timer = UPDATE_TIME_JUST_BEHIND; if (self->fight_back) { // Ship wants you to say "Outside Now!" if (self->weapon_type == WEAPON_TYPE_NONE) { self->update_strat_func = ship_ai_strat_avoid; flags_add(self->flags, SHIP_OVERTAKEN); } else { int chance = rand_int(0, 64); if (chance < 48) { self->update_strat_func = ship_ai_strat_block; } else if ((chance >= 40) && (chance < 54)) { self->update_strat_func = ship_ai_strat_avoid; flags_rm(self->flags, SHIP_OVERTAKEN); if (flags_not(self->flags, SHIP_SHIELDED) && flags_is(self->flags, SHIP_RACING)) { sfx_play(SFX_VOICE_ROCKETS); self->weapon_type = WEAPON_TYPE_ROCKET; weapons_fire_delayed(self, self->weapon_type); } } else if ((chance >= 54) && (chance < 60)) { self->update_strat_func = ship_ai_strat_avoid; flags_rm(self->flags, SHIP_OVERTAKEN); if (flags_not(self->flags, SHIP_SHIELDED) && flags_is(self->flags, SHIP_RACING)) { sfx_play(SFX_VOICE_MISSILE); self->weapon_type = WEAPON_TYPE_MISSILE; self->weapon_target = &g.ships[g.pilot]; weapons_fire_delayed(self, self->weapon_type); } } else if ((chance >= 60) && (chance < 64)) { self->update_strat_func = ship_ai_strat_avoid; flags_rm(self->flags, SHIP_OVERTAKEN); if (flags_not(self->flags, SHIP_SHIELDED) && flags_is(self->flags, SHIP_RACING)) { sfx_play(SFX_VOICE_SHOCKWAVE); self->weapon_type = WEAPON_TYPE_EBOLT; self->weapon_target = &g.ships[g.pilot]; weapons_fire_delayed(self, self->weapon_type); } } } } else { // If ship destined to be tail-ender then slow down self->remote_thrust_max = 2100 ; self->remote_thrust_mag = 25; self->speed = 2100 ; self->update_strat_func = ship_ai_strat_avoid; flags_rm(self->flags, SHIP_OVERTAKEN); } } for (int i = 0; i < NUM_PILOTS; i++) { // If another ship is just in front pass fight on if (flags_is(g.ships[i].flags, SHIP_JUST_IN_FRONT)) { self->update_strat_func = ship_ai_strat_avoid; flags_rm(self->flags, SHIP_OVERTAKEN); } } self->update_timer -= system_tick(); if (flags_is(self->flags, SHIP_OVERTAKEN)) { if ((self->remote_thrust_max + 700) > self->speed) { self->speed += self->remote_thrust_mag * 2 * 30 * system_tick(); } } else { if (((self->remote_thrust_max + behind_speed) > self->speed)) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } } // Ship is WELL AHEAD; we must slow the opponent to // give the weaker player a chance to catch up else if (section_diff > (NUM_PILOTS - self->position_rank) * 15 && section_diff < 150) { self->speed += self->remote_thrust_mag * 0.5 * 30 * system_tick(); if (self->speed > self->remote_thrust_max * 0.5) { self->speed = self->remote_thrust_max * 0.5; } self->update_timer = 0; self->update_strat_func = ship_ai_strat_hold_center; } // Ship is TOO FAR AHEAD else if (section_diff >= 150) { // Ship too far ahead, let it continue self->update_timer = 0; self->update_strat_func = ship_ai_strat_avoid; if ((self->remote_thrust_max > self->speed)) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } // Ship is IN SIGHT else if ((section_diff <= 10) && (section_diff > 4)) { // Ship close by, beware does not account for lapped opponents yet if (self->update_timer <= 0) { // Make New Decision int chance = rand_int(0, 5); self->update_timer = UPDATE_TIME_IN_SIGHT; switch (chance) { case 0: self->update_strat_func = ship_ai_strat_hold_center; break; case 1: self->update_strat_func = ship_ai_strat_hold_left; break; case 2: self->update_strat_func = ship_ai_strat_hold_right; break; case 3: self->update_strat_func = ship_ai_strat_block; break; case 4: self->update_strat_func = ship_ai_strat_zig_zag; break; } } self->update_timer -= system_tick(); if ((self->remote_thrust_max > self->speed)) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } // End of DPA control options // Ship is JUST OUT OF SIGHT else { self->update_timer = 0; self->update_strat_func = ship_ai_strat_hold_center; if ((self->remote_thrust_max > self->speed)) { self->speed += self->remote_thrust_mag * 30 * system_tick(); } } } if (!self->update_strat_func) { self->update_strat_func = ship_ai_strat_hold_center; } offset_vector = (self->update_strat_func)(self, face); // Make decision as to which path the craft will take at a junction section_t *section = self->section->prev; for (int i = 0; i < 4; i++) { section = section->next; } if (section->junction) { if (flags_is(section->junction->flags, SECTION_JUNCTION_START)) { int chance = rand_int(0, 2); if (chance == 0) { flags_add(self->flags, SHIP_JUNCTION_LEFT); } else { flags_rm(self->flags, SHIP_JUNCTION_LEFT); } } } section = self->section->prev; for (int i = 0; i < 4; i++) { if (section->junction) { if (flags_is(section->junction->flags, SECTION_JUNCTION_START)) { if (flags_is(self->flags, SHIP_JUNCTION_LEFT)) { section = section->junction; } else { section = section->next; } } else { section = section->next; } } else { section = section->next; } } section_t *next = section->next; section = self->section; // General routines - Non decision based // Bleed off speed as orientation changes self->speed -= fabsf(self->speed * self->angular_velocity.y) * 4 / (M_PI * 2) * system_tick(); // >> 14 self->speed -= fabsf(self->speed * self->angular_velocity.x) * 4 / (M_PI * 2) * system_tick(); // >> 14 // If remote has gone over boost if (flags_is(face->flags, FACE_BOOST) && (self->update_strat_func == ship_ai_strat_hold_left || self->update_strat_func == ship_ai_strat_hold_center)) { self->speed += 200 * 30 * system_tick(); } face++; if (flags_is(face->flags, FACE_BOOST) && (self->update_strat_func == ship_ai_strat_hold_right || self->update_strat_func == ship_ai_strat_hold_center)) { self->speed += 200 * 30 * system_tick(); } vec3_t track_target; if (flags_is(self->section->flags, SECTION_JUMP)) { // Cure for ships getting stuck on hump lip track_target = vec3_sub(self->section->center, self->section->prev->center); } else { track_target = vec3_sub(next->center, section->center); } float gap_length = vec3_len(track_target); track_target = vec3_mulf(track_target, self->speed / gap_length); vec3_t path1 = vec3_add(section->center, offset_vector); vec3_t path2 = vec3_add(next->center, offset_vector); vec3_t best_path = vec3_project_to_ray(self->position, path2, path1); self->acceleration = vec3_add(track_target, vec3_mulf(vec3_sub(best_path, self->position), 0.5)); vec3_t face_point = face->tris[0].vertices[0].pos; float height = vec3_distance_to_plane(self->position, face_point, face->normal); if (height < 50) { height = 50; } self->acceleration = vec3_add(self->acceleration, vec3_mulf(vec3_sub( vec3_mulf(face->normal, (SHIP_TRACK_FLOAT * SHIP_TRACK_MAGNET) / height), vec3_mulf(face->normal, SHIP_TRACK_MAGNET) ), 16.0)); self->velocity = vec3_add(self->velocity, vec3_mulf(self->acceleration, 30 * system_tick())); float xy_dist = sqrt(track_target.x * track_target.x + track_target.z * track_target.z); self->angular_velocity.x = wrap_angle(-atan2(track_target.y, xy_dist) - self->angle.x) * (1.0/16.0) * 30; self->angular_velocity.y = (wrap_angle(-atan2(track_target.x, track_target.z) - self->angle.y) * (1.0/16.0)) * 30 + self->turn_rate_from_hit; } // Ship is SHIP_FLYING else { section_t *section = self->section->next->next; section_t *next = section->next; self->update_strat_func = ship_ai_strat_hold_center; offset_vector = (self->update_strat_func)(self, NULL); if (self->remote_thrust_max > self->speed) { self->speed += self->remote_thrust_mag ; } self->speed -= fabsf(self->speed * self->angular_velocity.y) * (4 * M_PI * 2) * system_tick(); vec3_t track_target = vec3_sub(next->center, section->center); float gap_length = vec3_len(track_target); track_target.x = (track_target.x * self->speed) / gap_length; track_target.z = (track_target.z * self->speed) / gap_length; track_target.y = 500; vec3_t best_path = vec3_project_to_ray(self->position, next->center, self->section->center); self->acceleration = vec3( (track_target.x + ((best_path.x - self->position.x) * 0.5)), track_target.y, (track_target.z + ((best_path.z - self->position.z) * 0.5)) ); self->velocity = vec3_add(self->velocity, vec3_mulf(self->acceleration, 30 * system_tick())); self->angular_velocity.x = -0.3 - self->angle.x * 30; self->angular_velocity.y = wrap_angle(-atan2(track_target.x, track_target.z) - self->angle.y) * (1.0/16.0) * 30; } self->angular_velocity.z += (self->angular_velocity.y * 2.0 - self->angular_velocity.z * 0.5) * 30 * system_tick(); self->turn_rate_from_hit -= self->turn_rate_from_hit * 0.125 * 30 * system_tick(); self->angle = vec3_add(self->angle, vec3_mulf(self->angular_velocity, system_tick())); self->angle.z -= self->angle.z * 0.125 * 30 * system_tick(); self->angle = vec3_wrap_angle(self->angle); self->velocity = vec3_sub(self->velocity, vec3_mulf(self->velocity, 0.125 * 30 * system_tick())); self->position = vec3_add(self->position, vec3_mulf(self->velocity, 0.015625 * 30 * system_tick())); if (flags_is(self->flags, SHIP_ELECTROED)) { self->position = vec3_add(self->position, vec3(rand_float(-20, 20), rand_float(-20, 20), rand_float(-20, 20))); if (rand_int(0, 50) == 0) { self->speed -= self->speed * 0.5 * 30 * system_tick(); } } sfx_set_position(self->sfx_engine_thrust, self->position, self->velocity, 0.5); }