float ai_navmesh_distance(NavMesh* navmesh, Vector2 p1, Vector2 p2) { assert(navmesh); uint navpoint1 = ai_nearest_navpoint(navmesh, p1); uint navpoint2 = ai_nearest_navpoint(navmesh, p2); float distance = vec2_length(vec2_sub(p1, navmesh->navpoints[navpoint1])); distance += vec2_length(vec2_sub(p2, navmesh->navpoints[navpoint2])); uint idx = IDX_2D(navpoint1, navpoint2, navmesh->n_nodes); distance += navmesh->distance[idx]; return distance; }
static int jarhead_hit(struct foe_jarhead *f, const vector2 *hit) { const float radius = foe_data[f->common.type].radius; if (vec2_distance_squared(hit, &f->common.pos) < radius*radius) { if (f->state == JARHEAD_ALIVE) { if (irand(3) == 0) { foe_common_gen_explosion(&f->common, 1.f); kill_foe(&f->common); } else { vector2 s = ship.pos; f->state = JARHEAD_EXPLODING; vec2_mul_scalar(&s, .1f); vec2_sub_from(&f->common.dir, &s); f->hit_dir = f->common.dir; vec2_mul_scalar(&f->hit_dir, .02f/vec2_length(&f->hit_dir)); f->common.angle_speed *= -4.f; f->ttl = JARHEAD_TTL_AFTER_HIT; f->spin_angle = .2f + .2f*frand(); if (irand(2)) f->spin_angle = -f->spin_angle; } foe_common_bump_score(&f->common); } return 1; } return 0; }
static void foe_common_spawn(struct foe_common *f, const vector2 *source, int type) { vector2 *pos = &f->pos; vector2 *dir = &f->dir; float speed; f->used = 1; *pos = *source; speed = .05f + .05f*frand(); dir->x = frand() - .5f; dir->y = frand() - .5f; vec2_mul_scalar(dir, speed/vec2_length(dir)); f->tics = 0; f->type = type; f->angle = 0; f->angle_speed = 5.f*frand(); f->rot_axis.x = frand() - .5f; f->rot_axis.y = frand() - .5f; f->rot_axis.z = frand() - .5f; vec3_normalize(&f->rot_axis); foe_data[f->type].create_fn((foe *)f); }
static int ninja_hit(struct foe_ninja *f, const vector2 *hit) { int r = 0; const float radius = foe_data[FOE_NINJA].radius; if (vec2_distance_squared(hit, &f->common.pos) < radius*radius) { r = 1; if (--f->hit_count <= 0) { foe_common_gen_explosion(&f->common, 1.f); kill_foe(&f->common); foe_common_bump_score(&f->common); } else { vector2 s = ship.pos; f->state = NINJA_RECOVERING; vec2_mul_scalar(&s, .15f); vec2_sub_from(&f->common.dir, &s); f->hit_dir = f->common.dir; vec2_mul_scalar(&f->hit_dir, .03f/vec2_length(&f->hit_dir)); f->common.angle_speed *= -4.f; f->recover_ttl = NINJA_RECOVER_TTL; f->spin_angle = .2f + .2f*frand(); if (irand(2)) f->spin_angle = -f->spin_angle; } } return r; }
Vector2 touch_joystick(TexHandle tex, uint layer, const RectF* back_src, const RectF* nub_src, const Vector2* pos) { assert(back_src); assert(nub_src); assert(pos); Color nub_color = COLOR_WHITE; float back_width = rectf_width(back_src); float back_height = rectf_height(back_src); // Draw static back RectF dest = { pos->x - back_width / 2.0f, pos->y - back_height / 2.0f, 0.0f, 0.0f }; video_draw_rect(tex, layer, back_src, &dest, COLOR_WHITE); Vector2 touch; if(!_get_touch(pos, joystick_area_radius, &touch)) { touch = vec2(0.0f, 0.0f); } else { touch = vec2_sub(touch, *pos); nub_color = pressed_color; } float nub_width = rectf_width(nub_src); float nub_height = rectf_width(nub_src); assert(feql(back_width, back_height) && feql(nub_width, nub_height)); float back_radius = back_width / 2.0f; float nub_radius = nub_width / 2.0f; float move_radius = back_radius - nub_radius; assert(move_radius > 4.0f); float offset_len = vec2_length(touch); if(offset_len > move_radius) { touch = vec2_scale(touch, move_radius / offset_len); offset_len = move_radius; nub_color = COLOR_WHITE; } // Draw nub dest.left = pos->x + touch.x - nub_width / 2.0f; dest.top = pos->y + touch.y - nub_height / 2.0f; video_draw_rect(tex, layer, nub_src, &dest, nub_color); if(offset_len < joystick_deadzone_radius) return vec2(0.0f, 0.0f); float norm_len = normalize(offset_len, joystick_deadzone_radius, move_radius); return vec2_scale(touch, norm_len / offset_len); }
void vec2_normalize(vec2_t* v0, vec2_t* out) { double length = vec2_length(v0); if (length < 0.000001) return; out->x = v0->x/length; out->y = v0->y/length; }
static void get_foe_pos_for_border_generator(struct foe_generator *generator, vector2 *pos, int foe_type) { const int border = generator->border; const vector2 *p0 = &cur_arena->verts[border]; const vector2 *p1 = &cur_arena->verts[(border + 1)%cur_arena->nverts]; vector2 n; float t; n.x = -(p1->y - p0->y); n.y = p1->x - p0->x; vec2_mul_scalar(&n, 1.5*foe_data[foe_type].radius/vec2_length(&n)); t = frand(); pos->x = p0->x + t*(p1->x - p0->x) + n.x; pos->y = p0->y + t*(p1->y - p0->y) + n.y; }
static void brute_update(struct foe_brute *f) { vector2 *dir = &f->common.dir; vector2 s = ship.pos; vec2_sub_from(&s, &f->common.pos); vec2_mul_scalar(&s, .004f); vec2_add_to(dir, &s); foe_common_update(&f->common); hit_ship(&f->common.pos, foe_data[FOE_BRUTE].radius); if (vec2_length(dir) > foe_data[FOE_BRUTE].max_speed); vec2_mul_scalar(dir, .7); if (f->common.angle_speed > BRUTE_MAX_ANGLE_SPEED) f->common.angle_speed *= .7; if (brute_is_recovering(f)) foe_add_trail(&f->common.pos, &f->common.dir, PAL_YELLOW); }
static int brute_hit(struct foe_brute *f, const vector2 *hit) { const vector2 *pos = &f->common.pos; const float radius = foe_data[FOE_BRUTE].radius; if (vec2_distance_squared(hit, pos) < radius*radius) { if (--f->hit_count == 0) { foe_common_gen_explosion(&f->common, 1.3f); kill_foe(&f->common); foe_common_bump_score(&f->common); } else { vector2 s, os; float t; f->last_hit_tic = gc.level_tics; s = f->common.pos; vec2_sub_from(&s, &ship.pos); vec2_normalize(&s); vec2_set(&os, -s.y, s.x); t = 5.f*(frand() - .5f); s.x += t*os.x; s.y += t*os.y; vec2_mul_scalar(&s, .3f/vec2_length(&s)); vec2_add_to(&f->common.dir, &s); f->common.angle_speed *= 2; } return 1; } return 0; }
static void evolved_duck_draw(struct foe_evolved_duck *f) { float u, du; float t, s, hs; int i, j, k; glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_LIGHTING); glShadeModel(GL_FLAT); glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindTexture(GL_TEXTURE_2D, gl_texture_id(f->trail_texture_id)); glDisable(GL_DEPTH_TEST); s = vec2_length(&f->common.dir); hs = .5f*foe_data[f->common.type].max_speed; if (s < hs) t = 0.f; else t = (s - hs)/hs; glColor4f(t, t, t, t); glBegin(GL_TRIANGLE_STRIP); if (f->num_trail_segs > 1) { vector2 d, n, *from, *to; /* head */ j = f->trail_seg_head; k = f->trail_seg_head - 1; if (k < 0) k = MAX_TRAIL_SEGS - 1; from = &f->trail_seg[j]; to = &f->trail_seg[k]; d.x = -(to->y - from->y); d.y = to->x - from->x; #define HS 3.f #define HL 1.5 vec2_mul_scalar(&d, .5f*HL/vec2_length(&d)); assert(from->x == f->common.pos.x); assert(from->y == f->common.pos.y); n.x = from->x + HS*f->common.dir.x; n.y = from->y + HS*f->common.dir.y; glTexCoord2f(0, 0); glVertex3f(n.x - d.x, n.y - d.y, 0); glTexCoord2f(1, 0); glVertex3f(n.x + d.x, n.y + d.y, 0); /* tail */ /* draw tail */ #define START_U .15f du = (1.f - START_U)/f->num_trail_segs; u = START_U; j = f->trail_seg_head; for (i = 0; i < f->num_trail_segs - 1; i++) { vector2 d, *from, *to; k = j - 1; if (k < 0) k = MAX_TRAIL_SEGS - 1; from = &f->trail_seg[j]; to = &f->trail_seg[k]; d.x = -(to->y - from->y); d.y = to->x - from->x; if (i < f->num_trail_segs - 2) { vector2 *next_to; int l = k - 1; if (l < 0) l = MAX_TRAIL_SEGS - 1; next_to = &f->trail_seg[l]; d.x += -(next_to->y - to->y); d.y += next_to->x - to->x; } assert(vec2_length_squared(&d) > 0.f); vec2_mul_scalar(&d, .5f*HL/vec2_length(&d)); glTexCoord2f(0, u); glVertex3f(from->x - d.x, from->y - d.y, 0.f); glTexCoord2f(1, u); glVertex3f(from->x + d.x, from->y + d.y, 0.f); j = k; u += du; } } glEnd(); glPopAttrib(); { float dir_angle = f->common.angle = 180.f + 360.f*atan2(-f->common.dir.x, f->common.dir.y)/ 2.f/M_PI; glPushMatrix(); glTranslatef(f->common.pos.x, f->common.pos.y, 0.f); glRotatef(dir_angle, 0, 0, 1); glRotatef(f->common.angle, 0, 1, 0); if (f->common.tics < SPAWN_TICS) { static const struct rgb white = { 0.f, 1.f, 1.f }; float s = (float)f->common.tics/SPAWN_TICS; mesh_render_modulate_color(foe_meshes[f->common.type], &white, s); } else { mesh_render(foe_meshes[f->common.type]); } glPopMatrix(); } }
NavMesh ai_precalc_navmesh(DArray geometry, DArray platforms, float width, float height) { NavMesh res; ai_precalc_bounds(width, height); DArray navpoints = _gen_navpoints(geometry, platforms); DArray edges = _gen_edges(navpoints, geometry); Vector2* navpoints_v = DARRAY_DATA_PTR(navpoints, Vector2); Edge* edges_e = DARRAY_DATA_PTR(edges, Edge); uint n = navpoints.size; res.n_nodes = n; res.navpoints = MEM_ALLOC(sizeof(Vector2) * n); res.neighbour_count = MEM_ALLOC(sizeof(uint) * n); res.neighbours_start = MEM_ALLOC(sizeof(uint) * n); float* adjacency = MEM_ALLOC(sizeof(float) * n*n); res.distance = MEM_ALLOC(sizeof(float) * n*n); res.radius = MEM_ALLOC(sizeof(float) * n); res.nn_grid_count = MEM_ALLOC(sizeof(uint) * nn_grid_cells); res.nn_grid_start = MEM_ALLOC(sizeof(uint) * nn_grid_cells); memset(res.neighbour_count, 0, sizeof(uint) * n); memset(adjacency, 0, sizeof(float) * n*n); memset(res.distance, 0, sizeof(float) * n*n); memset(res.nn_grid_count, 0, sizeof(uint) * nn_grid_cells); memset(res.nn_grid_start, 0, sizeof(uint) * nn_grid_cells); for(uint i = 0; i < navpoints.size; ++i) { res.navpoints[i] = navpoints_v[i]; res.radius[i] = ai_wall_distance(navpoints_v[i], geometry); } for(uint i = 0; i < edges.size; ++i) { float dist = vec2_length(vec2_sub( navpoints_v[edges_e[i].v1], navpoints_v[edges_e[i].v2])); adjacency[IDX_2D(edges_e[i].v1, edges_e[i].v2, n)] = dist; adjacency[IDX_2D(edges_e[i].v2, edges_e[i].v1, n)] = dist; res.neighbour_count[edges_e[i].v1]++; res.neighbour_count[edges_e[i].v2]++; } // Pracalc node neighbour lists uint total_neighbours = res.neighbour_count[0]; res.neighbours_start[0] = 0; for(uint i = 1; i < n; ++i) { res.neighbours_start[i] = res.neighbours_start[i-1] + res.neighbour_count[i-1]; total_neighbours += res.neighbour_count[i]; } res.neighbours = MEM_ALLOC(sizeof(uint) * total_neighbours); for(uint i = 0; i < n; ++i) { uint idx = res.neighbours_start[i]; for(uint j = 0; j < n; ++j) { if(adjacency[IDX_2D(i, j, n)] > 0.0f) res.neighbours[idx++] = j; } assert(idx == res.neighbours_start[i] + res.neighbour_count[i]); } memcpy(res.distance, adjacency, sizeof(float) * n*n); // Change zero distances to infinities for(uint i = 0; i < n*n; ++i) if(res.distance[i] == 0.0f) res.distance[i] = 10000000.0f; // Floyd-Warshall for(uint i = 0; i < n; ++i) for(uint j = 0; j < n; ++j) for(uint k = 0; k < n; ++k) res.distance[IDX_2D(j, k, n)] = MIN(res.distance[IDX_2D(j, k, n)], res.distance[IDX_2D(j, i, n)]+res.distance[IDX_2D(i, k, n)]); // Precalc nearest-navpoint grid DArray grid_cell; DArray nn_grid; res.nn_grid_start[0] = 0; nn_grid = darray_create(sizeof(uint), 0); for(uint i = 0; i < nn_grid_cells; ++i) { grid_cell = darray_create(sizeof(uint), 0); for(uint j = 0; j < nn_samples; ++j) { Vector2 p = _rand_point_in_nn_grid_cell(i); uint nearest_navpoint = _nearest_navpoint(p, geometry, navpoints); if(nearest_navpoint >= n) // Point was inside wall, skip continue; bool unique = true; uint* existing_navpoints = DARRAY_DATA_PTR(grid_cell, uint); for(uint k = 0; k < grid_cell.size; ++k) if(existing_navpoints[k] == nearest_navpoint) unique = false; if(unique) darray_append(&grid_cell, &nearest_navpoint); } res.nn_grid_count[i] = grid_cell.size; if(i > 0) res.nn_grid_start[i] = res.nn_grid_start[i-1] + res.nn_grid_count[i-1]; uint* cell_navpoints = DARRAY_DATA_PTR(grid_cell, uint); darray_append_multi(&nn_grid, cell_navpoints, grid_cell.size); darray_free(&grid_cell); } // Hack, works properly as long as darray_free only does // MEM_FREE(darray.data); res.nn_grid = (uint*)nn_grid.data; darray_free(&navpoints); darray_free(&edges); MEM_FREE(adjacency); _precalc_vis(geometry, &res); return res; }
VEC2 vec2_normalize(VEC2 v) { return vec2_divide(v, vec2_length(v)); }