static void reset_game() { /* Set the starting level to demo.level */ current_level = asset_get(P("./levels/demo.level")); level_score = 0; level_time = 0.0; /* New main character entity */ character* main_char = entity_get("main_char"); main_char->position = vec2_mul( vec2_new(20, 20), TILE_SIZE); main_char->velocity = vec2_zero(); /* We can create multiple entities using a name format string like printf */ entities_new("coin_id_%i", COIN_COUNT, coin); /* Get an array of pointers to all coin entities */ coin* coins[COIN_COUNT]; entities_get(coins, NULL, coin); /* Set all the coin initial positions */ for(int i = 0; i < COIN_COUNT; i++) { coins[i]->position = vec2_mul(coin_positions[i], TILE_SIZE); } /* Deactivate victory and new game UI elements */ ui_button* victory = ui_elem_get("victory"); ui_button* new_game = ui_elem_get("new_game"); victory->active = false; new_game->active = false; }
static int _llfunc_vec2_mul(lua_State *L) { vec2 *v = (vec2*)userdata_get_or_die(L, 1); float k = (float)luaL_checknumber(L, 2); vec2 *r = (vec2*)userdata_get_or_new(L, 3, sizeof(vec2)); vec2_mul(v, k, r); return 1; }
Vec2 game_unit_to_pixels(Vec2 p) { Vec2 hw = vec2_scalar_mul(game_get_window_size(), 0.5f); p = vec2_mul(p, hw); p = vec2(p.x + hw.x, p.y - hw.y); return p; }
static void vertex_shader(const vertex_t *in, vs_to_fs_t *out) { // decode vertex const vec3_t pc = { 0.5f, 0.5f, 0.5f }, nc = { 1.0f, 1.0f, 1.0f }; const float pi = 1.0f / 65535.0f, ni = 2.0f / 255.0f, uvi = 1.0f / 255.0f; vec3_t position = vec3_sub(vec3_mul(vec3(in->x, in->y, in->z), pi), pc); vec3_t normal = vec3_sub(vec3_mul(vec3(in->nx, in->ny, in->nz), ni), nc); vec2_t uv = vec2_mul(vec2(in->u, in->v), uvi); // transform vertex out->position = mat4_transform_position(mvp, position); out->normal = mat4_transform_vector(mv, normal); out->uv = uv; }
void raycast_init(t_env *e) { float cam; t_vec2 dir_pow; cam = (2 * e->ray.x / (float)e->win_w) - 1; e->ray.hit = 0; e->ray.pos = e->map.pos; e->ray.dir.x = e->cam.dir.x + e->cam.pln.x * cam; e->ray.dir.y = e->cam.dir.y + e->cam.pln.y * cam; e->ray.map = vec2i(e->ray.pos); dir_pow = vec2_mul(e->ray.dir, e->ray.dir); e->ray.a.x = sqrt(1 + dir_pow.y / dir_pow.x); e->ray.a.y = sqrt(1 + dir_pow.x / dir_pow.y); }
static bool line_line_intersection(vec2 a0, vec2 a1, vec2 b0, vec2 b1) { #if 1 // Implemented based on http://stackoverflow.com/a/17198094/4354008 vec2 v = vec2_sub(a0, a1); vec2 normal = vec2_new(v.y, -v.x); vec2 v0 = vec2_sub(b0, a0); vec2 v1 = vec2_sub(b1, a0); float proj0 = vec2_dot(v0, normal); float proj1 = vec2_dot(v1, normal); if ((proj0 == 0) || (proj1 == 0)) return true; // TODO: float_sign() if ((proj0 > 0) && (proj1 < 0)) return true; if ((proj0 < 0) && (proj1 > 0)) return true; return false; #else vec2 intersection = vec2_zero(); vec2 b = vec2_sub(a1, a0); vec2 d = vec2_sub(b1, b0); float b_dot_p_perp = (b.x * d.y) - (b.y * d.x); if (b_dot_p_perp == 0) return false; vec2 c = vec2_sub(b0, a0); float t = ((c.x * d.y) - (c.y * d.x)) / b_dot_p_perp; if ((t < 0) || (t > 1)) return false; float u = ((c.x * b.y) - (c.y * b.x)) / b_dot_p_perp; if ((u < 0) || (u > 1)) return false; // TODO: make this an output parameter intersection = vec2_add(a0, vec2_mul(b, t)); return true; #endif }
static void fire_projectile(struct GameState *game_state, struct Ship *source, struct Ship *target, int32 damage) { ASSERT(source != target); source->fire_cooldown_timer = source->fire_cooldown; struct Projectile *projectile = create_projectile(game_state); projectile->owner = source->id; projectile->team = source->team; projectile->damage = damage; projectile->position = source->position; projectile->size = vec2_new(0.1f, 0.1f); vec2 direction = vec2_normalize(vec2_sub(target->position, projectile->position)); projectile->velocity = vec2_mul(direction, 5.0f); }
void tick_game(struct GameMemory *memory, struct Input *input, uint32 screen_width, uint32 screen_height, float dt) { struct GameState *game_state = (struct GameState *)memory->game_memory; struct RenderBuffer *render_buffer = (struct RenderBuffer *)memory->render_memory; clear_render_buffer(render_buffer); for (uint32 i = 0; i < game_state->visibility_graph.vertex_count; ++i) { vec2 vertex = game_state->visibility_graph.vertices[i]; draw_world_quad_buffered(render_buffer, vertex, vec2_scalar(0.1f), vec4_zero(), vec3_new(0, 1, 1)); } for (uint32 i = 0; i < game_state->visibility_graph.edge_count; ++i) { uint32 *edge = &game_state->visibility_graph.edges[i][0]; vec2 p0 = game_state->visibility_graph.vertices[edge[0]]; vec2 p1 = game_state->visibility_graph.vertices[edge[1]]; draw_world_line_buffered(render_buffer, p0, p1, vec3_new(1, 1, 0)); } if (mouse_down(MOUSE_LEFT, input)) { struct AABB box = calc_mouse_selection_box(input, MOUSE_LEFT); vec2 size = vec2_sub(box.max, box.min); vec2 bottom_left = vec2_new(box.min.x, box.max.y); vec2 bottom_right = vec2_new(box.max.x, box.max.y); vec2 top_left = vec2_new(box.min.x, box.min.y); vec2 top_right = vec2_new(box.max.x, box.min.y); const uint32 outline_thickness = 1; draw_screen_quad_buffered(render_buffer, top_left, vec2_new(size.x, outline_thickness), vec4_zero(), vec3_new(1, 1, 0)); draw_screen_quad_buffered(render_buffer, bottom_left, vec2_new(size.x, outline_thickness), vec4_zero(), vec3_new(1, 1, 0)); draw_screen_quad_buffered(render_buffer, top_left, vec2_new(outline_thickness, size.y), vec4_zero(), vec3_new(1, 1, 0)); draw_screen_quad_buffered(render_buffer, top_right, vec2_new(outline_thickness, size.y), vec4_zero(), vec3_new(1, 1, 0)); } if (mouse_released(MOUSE_LEFT, input)) { // Clear previous selection. game_state->selected_ship_count = 0; struct AABB screen_selection_box = calc_mouse_selection_box(input, MOUSE_LEFT); // Because screen space y-values are inverted, these two results have conflicting // y-values, i.e. screen_to_world_min.y is actually the maximum y. These values // are then properly min/maxed and stored correctly in world_selection_box. vec2 screen_to_world_min = screen_to_world_coords(screen_selection_box.min, &game_state->camera, screen_width, screen_height); vec2 screen_to_world_max = screen_to_world_coords(screen_selection_box.max, &game_state->camera, screen_width, screen_height); struct AABB world_selection_box; world_selection_box.min = min_vec2(screen_to_world_min, screen_to_world_max); world_selection_box.max = max_vec2(screen_to_world_min, screen_to_world_max); // Add colliding ships to the selection list. // TODO: optimize, spatial grid hash? for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; struct AABB ship_aabb = aabb_from_transform(ship->position, ship->size); vec2 center = vec2_div(vec2_add(ship_aabb.min, ship_aabb.max), 2.0f); if (aabb_aabb_intersection(world_selection_box, ship_aabb)) game_state->selected_ships[game_state->selected_ship_count++] = ship->id; } if (game_state->selected_ship_count > 0) fprintf(stderr, "selected %u ships\n", game_state->selected_ship_count); } if (mouse_down(MOUSE_RIGHT, input)) { vec2 target_position = screen_to_world_coords(input->mouse_position, &game_state->camera, screen_width, screen_height); for (uint32 i = 0; i < game_state->selected_ship_count; ++i) { uint32 id = game_state->selected_ships[i]; struct Ship *ship = get_ship_by_id(game_state, id); ship->target_position = target_position; ship->move_order = true; } } // Outline selected ships. for (uint32 i = 0; i < game_state->selected_ship_count; ++i) { uint32 id = game_state->selected_ships[i]; struct Ship *ship = get_ship_by_id(game_state, id); draw_world_quad_buffered(render_buffer, ship->position, vec2_mul(ship->size, 1.1f), vec4_zero(), vec3_new(0, 1, 0)); if (ship->move_order) draw_world_quad_buffered(render_buffer, ship->target_position, vec2_new(0.5f, 0.5f), vec4_zero(), vec3_new(0, 1, 0)); } // Handle move orders. for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; if (ship->move_order) { vec2 direction = vec2_zero(); /* if (vec2_length2(direction) < FLOAT_EPSILON) direction = vec2_normalize(vec2_sub(ship->target_position, ship->position)); */ ship->move_velocity = vec2_mul(direction, 2.0f); /* vec2 direction = vec2_normalize(vec2_sub(ship->target_position, ship->position)); ship->move_velocity = vec2_mul(direction, 2.0f); */ if (vec2_distance2(ship->position, ship->target_position) < 0.1f) { ship->move_velocity = vec2_zero(); ship->move_order = false; } } } tick_camera(input, &game_state->camera, dt); tick_combat(game_state, dt); tick_physics(game_state, dt); }
static void tick_physics(struct GameState *game_state, float dt) { // Projectile kinematics. for (uint32 i = 0; i < game_state->projectile_count; ++i) { struct Projectile *projectile = &game_state->projectiles[i]; // r = r0 + (v*t) + (a*t^2)/2 projectile->position = vec2_add(projectile->position, vec2_mul(projectile->velocity, dt)); } // Ship kinematics. for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; vec2 move_acceleration = vec2_zero(); // v = v0 + (a*t) ship->move_velocity = vec2_add(ship->move_velocity, vec2_mul(move_acceleration, dt)); // r = r0 + (v*t) + (a*t^2)/2 ship->position = vec2_add(vec2_add(ship->position, vec2_mul(ship->move_velocity, dt)), vec2_div(vec2_mul(move_acceleration, dt * dt), 2.0f)); } // // TODO: spatial hashing // // Projectile collision. for (uint32 i = 0; i < game_state->projectile_count; ++i) { struct Projectile *projectile = &game_state->projectiles[i]; struct AABB projectile_aabb = aabb_from_transform(projectile->position, projectile->size); // Projectile-building collision. for (uint32 j = 0; j < game_state->building_count; ++j) { struct Building *building = &game_state->buildings[j]; struct AABB building_aabb = aabb_from_transform(building->position, building->size); if (aabb_aabb_intersection(projectile_aabb, building_aabb)) { // TODO: damage building if not friendly destroy_projectile(game_state, projectile); break; } } // NOTE: owner may be dead and destroyed at this point, so checking for NULL might be required. struct Ship *owner = get_ship_by_id(game_state, projectile->owner); // Projectile-ship collision. for (uint32 j = 0; j < game_state->ship_count; ++j) { struct Ship *ship = &game_state->ships[j]; if (ship->id == projectile->owner) continue; #if 0 // Allow projectiles to pass through teammates. if (ship->team == projectile->team) continue; #endif struct AABB ship_aabb = aabb_from_transform(ship->position, ship->size); if (aabb_aabb_intersection(projectile_aabb, ship_aabb)) { // Disable friendly fire. if (ship->team != projectile->team) damage_ship(game_state, ship, projectile->damage); destroy_projectile(game_state, projectile); break; } } } // TODO: optimize // Ship collision. if (game_state->ship_count >= 2) { for (uint32 i = 0; i < game_state->ship_count - 1; ++i) { struct Ship *a = &game_state->ships[i]; struct AABB a_aabb = aabb_from_transform(a->position, a->size); vec2 a_center = vec2_div(vec2_add(a_aabb.min, a_aabb.max), 2.0f); vec2 a_half_extents = vec2_div(vec2_sub(a_aabb.max, a_aabb.min), 2.0f); // Ship-building collision. for (uint32 j = 0; j < game_state->building_count; ++j) { struct Building *building = &game_state->buildings[j]; struct AABB b_aabb = aabb_from_transform(building->position, building->size); if (aabb_aabb_intersection(a_aabb, b_aabb)) { vec2 b_center = vec2_div(vec2_add(b_aabb.min, b_aabb.max), 2.0f); vec2 b_half_extents = vec2_div(vec2_sub(b_aabb.max, b_aabb.min), 2.0f); vec2 intersection = vec2_sub(vec2_abs(vec2_sub(b_center, a_center)), vec2_add(a_half_extents, b_half_extents)); if (intersection.x > intersection.y) { a->move_velocity.x = 0.0f; if (a->position.x < building->position.x) a->position.x += intersection.x/2.0f; else a->position.x -= intersection.x/2.0f; } else { a->move_velocity.y = 0.0f; if (a->position.y < building->position.y) a->position.y += intersection.y/2.0f; else a->position.y -= intersection.y/2.0f; } } } // Ship-ship collision. for (uint32 j = i + 1; j < game_state->ship_count; ++j) { struct Ship *b = &game_state->ships[j]; struct AABB b_aabb = aabb_from_transform(b->position, b->size); if (aabb_aabb_intersection(a_aabb, b_aabb)) { vec2 b_center = vec2_div(vec2_add(b_aabb.min, b_aabb.max), 2.0f); vec2 b_half_extents = vec2_div(vec2_sub(b_aabb.max, b_aabb.min), 2.0f); vec2 intersection = vec2_sub(vec2_abs(vec2_sub(b_center, a_center)), vec2_add(a_half_extents, b_half_extents)); if (intersection.x > intersection.y) { if (abs_float(a->move_velocity.x) > abs_float(b->move_velocity.x)) a->move_velocity.x = 0.0f; else b->move_velocity.x = 0.0f; if (a->position.x < b->position.x) { a->position.x += intersection.x/2.0f; b->position.x -= intersection.x/2.0f; } else { a->position.x -= intersection.x/2.0f; b->position.x += intersection.x/2.0f; } } else { if (abs_float(a->move_velocity.y) > abs_float(b->move_velocity.y)) a->move_velocity.y = 0.0f; else b->move_velocity.y = 0.0f; if (a->position.y < b->position.y) { a->position.y += intersection.y/2.0f; b->position.y -= intersection.y/2.0f; } else { a->position.y -= intersection.y/2.0f; b->position.y += intersection.y/2.0f; } } } } } } }
static void tick_camera(struct Input *input, struct Camera *camera, float dt) { // // move // vec2 camera_up = vec2_direction(camera->rotation); vec2 camera_right = vec2_direction(camera->rotation - PI_OVER_TWO); vec2 move_acceleration = vec2_zero(); if (key_down(GLFW_KEY_W, input)) move_acceleration = vec2_add(move_acceleration, camera_up); if (key_down(GLFW_KEY_A, input)) move_acceleration = vec2_add(move_acceleration, vec2_negate(camera_right)); if (key_down(GLFW_KEY_S, input)) move_acceleration = vec2_add(move_acceleration, vec2_negate(camera_up)); if (key_down(GLFW_KEY_D, input)) move_acceleration = vec2_add(move_acceleration, camera_right); if (vec2_length2(move_acceleration) > FLOAT_EPSILON) { const float acceleration_magnitude = camera->zoom * 10.0f; move_acceleration = vec2_normalize(move_acceleration); move_acceleration = vec2_mul(move_acceleration, acceleration_magnitude); // v = v0 + (a*t) camera->move_velocity = vec2_add(camera->move_velocity, vec2_mul(move_acceleration, dt)); // Clamp move velocity. const float max_move_speed = 5.0f * sqrtf(camera->zoom); if (vec2_length2(camera->move_velocity) > (max_move_speed * max_move_speed)) camera->move_velocity = vec2_mul(vec2_normalize(camera->move_velocity), max_move_speed); } // Number of seconds required for friction to stop movement completely. // (because velocity is sampled each frame, actual stop time longer than this) float stop_time = 0.1f; float inv_stop_time = 1.0f / stop_time; if (move_acceleration.x == 0.0f) camera->move_velocity.x -= camera->move_velocity.x * inv_stop_time * dt; if (move_acceleration.y == 0.0f) camera->move_velocity.y -= camera->move_velocity.y * inv_stop_time * dt; // r = r0 + (v0*t) + (a*t^2)/2 camera->position = vec2_add(vec2_add(camera->position, vec2_mul(camera->move_velocity, dt)), vec2_div(vec2_mul(move_acceleration, dt * dt), 2.0f)); // // zoom // float zoom_acceleration = 0.0f; if (key_down(GLFW_KEY_SPACE, input)) zoom_acceleration += 1.0f; if (key_down(GLFW_KEY_LEFT_CONTROL, input)) zoom_acceleration -= 1.0f; zoom_acceleration *= camera->zoom * 50.0f; if (zoom_acceleration == 0.0f) camera->zoom_velocity -= camera->zoom_velocity * (1.0f / 0.1f) * dt; // v = v0 + (a*t) camera->zoom_velocity += zoom_acceleration * dt; // r = r0 + (v0*t) + (a*t^2)/2 camera->zoom += (camera->zoom_velocity * dt) + (zoom_acceleration * dt * dt) / 2.0f; // Clamp zoom velocity. const float max_zoom_speed = 3.0f * camera->zoom; camera->zoom_velocity = clamp_float(camera->zoom_velocity, -max_zoom_speed, max_zoom_speed); float unclamped_zoom = camera->zoom; camera->zoom = clamp_float(camera->zoom, 1.0f, 1000.0f); if (camera->zoom != unclamped_zoom) camera->zoom_velocity = 0.0f; }