diff options
Diffstat (limited to 'src/game.c')
-rw-r--r-- | src/game.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..41f9b1c --- /dev/null +++ b/src/game.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2018 Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <SDL.h> +#include <SDL_mixer.h> +#include <stdarg.h> /* vsnprintf */ + +#include "aabb.h" +#include "whale.h" +#include "whale-svg.h" +#include "macros.h" +#include "stage.h" +#include "svg-node.h" + +/* the game */ + +typedef enum whale_game_fsm_t { + WHALE_GAME_SETTING_UP, + WHALE_GAME_STARTING, + WHALE_GAME_TARGETING, + WHALE_GAME_LAUNCHING, + WHALE_GAME_FLYING, + WHALE_GAME_INSIDE, + WHALE_GAME_OUTSIDE, + WHALE_GAME_LEAVE +} whale_game_fsm_t; + +static Mix_Chunk *launch_sfx, *crash_sfx, *winner_sfx; +static stage_node_t *bobby, *crater, *astro, *planets; +static unsigned bobby_armed; +static whale_game_fsm_t state; + +void whale_game_event(whale_t *whale, SDL_Event *ev) +{ + stage_t *stage = whale_get_stage(whale); + + switch (ev->type) { + case SDL_KEYDOWN: + switch (ev->key.keysym.sym) { + case SDLK_ESCAPE: + state = WHALE_GAME_LEAVE; + break; + + case SDLK_SPACE: + if (state == WHALE_GAME_INSIDE || state == WHALE_GAME_OUTSIDE) { + /* resetup game */ + state = WHALE_GAME_SETTING_UP; + } + + default: + break; + } + + break; + + case SDL_MOUSEBUTTONDOWN: + if (state == WHALE_GAME_TARGETING) { + if (!bobby_armed) + bobby_armed = whale_ticks(whale, WHALE_TICKS_TIMER); + } else if (state == WHALE_GAME_INSIDE || state == WHALE_GAME_OUTSIDE) { + /* resetup game */ + state = WHALE_GAME_SETTING_UP; + } + break; + + case SDL_MOUSEBUTTONUP: + if (state == WHALE_GAME_TARGETING) { + if (bobby_armed) { + /* launch bobby */ + Mix_PlayChannel(-1, launch_sfx, 0); + state = WHALE_GAME_LAUNCHING; + } + } + break; + + case SDL_MOUSEMOTION: + if (state == WHALE_GAME_TARGETING) { + /* convert the mouse X, Y coordinates into an angle, it's + * trivial since the whale is always in the bottom left + */ + int width, height; + double angle; + v2f_t v; + + stage_get_output_size(stage, &width, &height); + + v.x = (float)ev->motion.x; + v.y = (float)height - ev->motion.y; + + v = v2f_normalize(&v); + angle = -atan2f(v.y, v.x) * 57.4712f /* rad2deg */; + + stage_node_set_angle(stage, bobby, angle); + } + break; + + default: + break; + } +} + +#if 0 +stage_node_t * text_node(stage_t *stage, stage_node_t *node, const char *name, int layer, aabb_t aabb, int width, int height, int x, int y, const char *anchor, int font_size, const char *fmt, ...) +{ + char buf[2048]; + char str[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(str, sizeof(str), fmt, ap); + va_end(ap); + + snprintf(buf, sizeof(buf), + "<svg width=\"%i\" height=\"%i\"><g><text x=\"%i\" y=\"%i\" style=\"font-size:%i\" text-anchor=\"%s\">%s</text></g></svg>", + width, height, x, y, font_size, anchor, str); + if (!node) { + node = svg_node_new_buffer(stage, name, buf, strlen(buf), layer, aabb); + fatal_if(!node, "Unable to create quesiton svg \"%s\"", name); + } else { + svg_node_replace_buffer(stage, node, name, buf, strlen(buf)); + } + + return node; +} +#endif + + +stage_node_t * meter_node(stage_t *stage, const aabb_t aabb, float level) +{ + /* TODO: power meter */ +} + + +void whale_game_update(whale_t *whale) +{ + static const aabb_t bobby_end_aabb = {{-1.f, -1.f}, {-.8f, -.8f}}; + static aabb_t crater_end_aabb = {{0.f, -1.f}, {0.f, -.7f}}; + static aabb_t bobby_start_aabb, crater_start_aabb; + static int bobby_keyboard_armed, prev; + static whale_svg_t *bobby_svg, *dead_svg; + static int initialized; + static v2f_t bobby_vector; + stage_t *stage = whale_get_stage(whale); + unsigned now = whale_ticks(whale, WHALE_TICKS_TIMER); + + if (!initialized) { + bobby = stage_node_lookup_name(stage, "bobby"); + fatal_if(!bobby, "Unable to lookup whale node"); + stage_node_get_object(stage, bobby, (void **)&bobby_svg); /* XXX FIXME: NOO */ + + dead_svg = whale_svg_new_file("assets/dead.svg"); + fatal_if(!dead_svg, "Unable to load dead bobby svg"); + + crater = stage_node_lookup_name(stage, "crater"); + fatal_if(!crater, "Unable to lookup crater node"); + + astro = stage_node_lookup_name(stage, "astro"); + fatal_if(!astro, "Unable to lookup astro node"); + + planets = svg_node_new_file(stage, "planets", "assets/planets.svg", 0, (aabb_t){{-1.f, -.7f}, {1.f, 1.f}}); + fatal_if(!planets, "Unable to load planets svg"); + stage_node_set_active(stage, planets); + + stage_node_set_alpha(stage, bobby, 1.0f); + stage_node_set_alpha(stage, crater, 1.0f); + + winner_sfx = Mix_LoadWAV("assets/winner.wav"); + fatal_if(!winner_sfx, "Unable to load winner sfx"); + + launch_sfx = Mix_LoadWAV("assets/launch.wav"); + fatal_if(!launch_sfx, "Unable to load launch sfx"); + + crash_sfx = Mix_LoadWAV("assets/crash.wav"); + fatal_if(!crash_sfx, "Unable to load crash sfx"); + + initialized = 1; + } + + switch (state) { + case WHALE_GAME_SETTING_UP: { + float crater_x; + + stage_node_set_angle(stage, bobby, 0); + stage_node_get_aabb(stage, bobby, &bobby_start_aabb); + stage_node_get_aabb(stage, crater, &crater_start_aabb); + stage_node_set_object(stage, bobby, bobby_svg); + + /* place crater destination randomly along the bottom */ + crater_x = ((float)rand()) / RAND_MAX * .6f; + crater_end_aabb.min.x = crater_x - .2f; + crater_end_aabb.max.x = crater_x + .2f; + + bobby_keyboard_armed = bobby_armed = 0; + whale_ticks_reset(whale, WHALE_TICKS_TIMER); + prev = now = whale_ticks(whale, WHALE_TICKS_TIMER); + state++; + break; + } + + case WHALE_GAME_STARTING: + if (now < 500) { + float t = ((float)now) * (1.0f / 500.0f); + aabb_t aabb; + + t *= t; + + aabb = aabb_lerp(&bobby_start_aabb, &bobby_end_aabb, t); + stage_node_set_aabb(stage, bobby, &aabb); + + aabb = aabb_lerp(&crater_start_aabb, &crater_end_aabb, t); + stage_node_set_aabb(stage, crater, &aabb); + + if (astro) { /* XXX this is ugly; astro is serving as a "first start" flag */ + stage_node_set_alpha(stage, astro, 1.f - t); + stage_node_set_alpha(stage, planets, t); + } + } else { + stage_node_set_aabb(stage, bobby, &bobby_end_aabb); + stage_node_set_aabb(stage, crater, &crater_end_aabb); + + if (astro) { + astro = stage_node_free(stage, astro); + stage_node_set_alpha(stage, planets, 1.f); /* XXX: see above comment about astro abuse */ + } + + state++; + } + break; + + case WHALE_GAME_TARGETING: { + const Uint8 *key_state = SDL_GetKeyboardState(NULL); + float t = ((float)(now - prev)) * .05f; /* scale simulation things according to time passed */ + + if (key_state[SDL_SCANCODE_LEFT] || key_state[SDL_SCANCODE_A]) { + double angle; + + stage_node_get_angle(stage, bobby, &angle); + angle += -3.f * t; + stage_node_set_angle(stage, bobby, angle); + } + + if (key_state[SDL_SCANCODE_RIGHT] || key_state[SDL_SCANCODE_D]) { + double angle; + + stage_node_get_angle(stage, bobby, &angle); + angle += 3.f * t; + stage_node_set_angle(stage, bobby, angle); + } + + if (key_state[SDL_SCANCODE_SPACE]) { + /* arm bobby */ + if (!bobby_keyboard_armed) { + bobby_keyboard_armed = 1; + bobby_armed = now; + } + } else if (bobby_keyboard_armed) { + /* launch bobby */ + Mix_PlayChannel(-1, launch_sfx, 0); + state = WHALE_GAME_LAUNCHING; + } + break; + } + + case WHALE_GAME_LAUNCHING: { + double angle; + double power = (now - bobby_armed) * (1.0f / 5000); + + /* convert angle and power into a vector */ + stage_node_get_angle(stage, bobby, &angle); + angle *= -(M_PI / 180.f); /* to radians */ + bobby_vector.x = cos(angle) * power; + bobby_vector.y = sin(angle) * power; + + state++; + break; + } + + case WHALE_GAME_FLYING: { + float t = ((float)(now - prev)) * .05f; /* scale simulation things according to time passed */ + double angle; + v2f_t move; + aabb_t aabb; + + move = v2f_mult_scalar(&bobby_vector, t); + + /* spin the bobby while he's flying */ + stage_node_get_angle(stage, bobby, &angle); + angle += 4.0f * t; + stage_node_set_angle(stage, bobby, angle); + + /* move him on his trajectory */ + /* XXX: this is pretty silly, but it's the interfaces I have at the moment */ + stage_node_get_aabb(stage, bobby, &aabb); + aabb.min = v2f_add(&aabb.min, &move); + aabb.max = v2f_add(&aabb.max, &move); + stage_node_set_aabb(stage, bobby, &aabb); + + /* check if we're inside the crater (crater aabb is .4f wide) */ + /* FIXME: I should really add some generic aabb overlap test type stuff to aabb.h */ + /* FIXME XXX: yes I know the collision detection is janky/ad-hoc, there was no time! */ + if (aabb.min.x > (crater_end_aabb.min.x + .08f) && + aabb.max.x < (crater_end_aabb.max.x - .08f) && + aabb.min.y < (crater_end_aabb.max.y - .06f)) { + + state = WHALE_GAME_INSIDE; + Mix_PlayChannel(-1, winner_sfx, 0); + + } else if (aabb.min.y < -1.f) { /* or if we're in the ground */ + + state = WHALE_GAME_OUTSIDE; + Mix_PlayChannel(-1, crash_sfx, 0); + stage_node_set_object(stage, bobby, dead_svg); + } + + /* alter trajectory w/gravity */ + bobby_vector.y += -.01f * t; + break; + } + + case WHALE_GAME_INSIDE: { + aabb_t aabb; + + /* bobby simming inside crater lake */ + aabb.min.x = crater_end_aabb.min.x + .1f; + aabb.max.x = crater_end_aabb.max.x - .1f; + aabb.min.y = crater_end_aabb.max.y - .2f; + aabb.max.y = crater_end_aabb.max.y; + stage_node_set_aabb(stage, bobby, &aabb); + + stage_node_set_angle(stage, bobby, cos((double)now * .001) * 10.0); + + break; + } + + case WHALE_GAME_OUTSIDE: + break; + + case WHALE_GAME_LEAVE: + whale_set_context(whale, WHALE_CONTEXT_CREDITS); + break; + } + + /* last 20 seconds of song are credits */ + if (whale_ticks(whale, WHALE_TICKS_MUSIC) > 206000) + whale_set_context(whale, WHALE_CONTEXT_CREDITS); + + prev = now; +} |