summaryrefslogtreecommitdiff
path: root/src/game.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/game.c')
-rw-r--r--src/game.c361
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;
+}
© All Rights Reserved