Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/space-dragon.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2014-10-09 20:40:32 +0400
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2014-10-09 20:40:32 +0400
commit7465f2ff304bb8cc898478d0db0a9226382a43b5 (patch)
tree0ddbb4e25656ea4a12cde9ad57be047a205287bb
First commit, version 1.2
-rw-r--r--appinfo.json29
-rw-r--r--resources/images/icon.pngbin0 -> 255 bytes
-rw-r--r--resources/images/ship.pngbin0 -> 204 bytes
-rw-r--r--src/game.c419
-rw-r--r--src/game.h13
-rw-r--r--src/space-dragon.c8
6 files changed, 469 insertions, 0 deletions
diff --git a/appinfo.json b/appinfo.json
new file mode 100644
index 0000000..ef7801c
--- /dev/null
+++ b/appinfo.json
@@ -0,0 +1,29 @@
+{
+ "uuid": "130049db-8847-4d94-b619-58ced0e537eb",
+ "shortName": "Space Dragon",
+ "longName": "Space Dragon",
+ "companyName": "Cluster",
+ "versionCode": 3,
+ "versionLabel": "1.2",
+ "watchapp": {
+ "watchface": false
+ },
+ "appKeys": {
+ "dummy": 0
+ },
+ "resources": {
+ "media": [
+ {
+ "file": "images/ship.png",
+ "name": "IMAGE_SHIP",
+ "type": "png"
+ },
+ {
+ "file": "images/icon.png",
+ "menuIcon": true,
+ "name": "IMAGE_ICON",
+ "type": "png"
+ }
+ ]
+ }
+}
diff --git a/resources/images/icon.png b/resources/images/icon.png
new file mode 100644
index 0000000..44f81d6
--- /dev/null
+++ b/resources/images/icon.png
Binary files differ
diff --git a/resources/images/ship.png b/resources/images/ship.png
new file mode 100644
index 0000000..ad86c30
--- /dev/null
+++ b/resources/images/ship.png
Binary files differ
diff --git a/src/game.c b/src/game.c
new file mode 100644
index 0000000..4600e1b
--- /dev/null
+++ b/src/game.c
@@ -0,0 +1,419 @@
+#include <pebble.h>
+#include "game.h"
+
+static Window *s_window;
+static Layer *gfx_layer;
+static AppTimer *timer;
+static GBitmap *ship_bitmap;
+
+const int SHIP_WIDTH = 10;
+const int SHIP_HEIGHT = 15;
+const int SCREEN_WIDTH = 144;
+const int SCREEN_HEIGHT = 168;
+const int UPDATE_INTERVAL = 25;
+const int METEOR_START_INTERVAL = 40;
+
+static long int time_ticks = 0;
+static float ship_x;
+static float ship_y;
+static float speed_x = 0;
+static float speed_y = 0;
+static int acc_x = 0;
+static int acc_y = 0;
+static int meteor_interval;
+static int score;
+static int hi_score = 0;
+static Meteor* first_meteor = NULL;
+static bool started = false;
+static bool game_over = true;
+static long int game_over_stop_time = 0;
+static bool paused = false;
+static bool show_debug = false;
+
+// More meteors!
+static void add_meteor()
+{
+ Meteor* new_meteor = malloc(sizeof(Meteor));
+ new_meteor->size = 5 + (rand() % 15);
+
+ // left-right or up-down?
+ if (rand() % 2)
+ {
+ new_meteor->x = rand() % SCREEN_WIDTH;
+ new_meteor->speed_x = 0.2 * (1 + (rand() % 5));
+ if ((new_meteor->x > SCREEN_WIDTH / 2) && (new_meteor->speed_x > 0))
+ new_meteor->speed_x *= -1;
+
+ new_meteor->speed_y = 0.5 * (1 + (rand() % 5));
+
+ // Top or bottom side?
+ if (rand() % 2)
+ {
+ new_meteor->y = SCREEN_HEIGHT + new_meteor->size;
+ new_meteor->speed_y *= -1;
+ }
+ else
+ new_meteor->y = -new_meteor->size;
+ } else {
+ new_meteor->y = rand() % SCREEN_HEIGHT;
+ new_meteor->speed_y = 0.2 * (1 + (rand() % 5));
+ if ((new_meteor->y > SCREEN_HEIGHT / 2) && (new_meteor->speed_y > 0))
+ new_meteor->speed_y *= -1;
+
+ new_meteor->speed_x = 0.5 * (1 + (rand() % 5));
+
+ // Left or right side?
+ if (rand() % 2)
+ {
+ new_meteor->x = SCREEN_WIDTH + new_meteor->size;
+ new_meteor->speed_x *= -1;
+ }
+ else
+ new_meteor->x = -new_meteor->size;
+ }
+
+ new_meteor->next = NULL;
+
+ if (first_meteor == NULL)
+ first_meteor = new_meteor;
+ else {
+ Meteor* meteor = first_meteor;
+ while (meteor->next)
+ {
+ meteor = meteor->next;
+ }
+ meteor->next = new_meteor;
+ }
+}
+
+// Is point part of meteor?
+static bool is_part_of_meteor(int x, int y, int max_r, Meteor* meteor)
+{
+ int r = meteor->size/2 + max_r;
+ int dist_x = x - meteor->x;
+ int dist_y = y - meteor->y;
+ return dist_x * dist_x + dist_y * dist_y < r * r;
+}
+
+// Checks ship and meteor interception
+static bool is_boom(Meteor* meteor)
+{
+ return is_part_of_meteor(ship_x, ship_y, 7, meteor);
+}
+
+// Update meteors data
+static void update_meteors()
+{
+ // Moving meteors
+ Meteor* meteor = first_meteor;
+ while (meteor)
+ {
+ meteor->x += meteor->speed_x;
+ meteor->y += meteor->speed_y;
+ // Game over?
+ if (!game_over && is_boom(meteor))
+ {
+ vibes_short_pulse();
+ game_over_stop_time = time_ticks + (500 / UPDATE_INTERVAL); // Some more time to rumble...
+ //reset_game();
+ }
+ meteor = meteor->next;
+ }
+
+ // Removing unused meteors
+ Meteor* prev = NULL;
+ meteor = first_meteor;
+ while (meteor)
+ {
+ Meteor* next = meteor->next;
+ if ((meteor->x < -meteor->size) || (meteor->y < -meteor->size) ||
+ (meteor->x > SCREEN_WIDTH + meteor->size) || (meteor->y > SCREEN_HEIGHT + meteor->size))
+ {
+ if (!game_over && !game_over_stop_time)
+ {
+ // Increasing score
+ score += 10;
+ if (score > hi_score) hi_score = score;
+ // More score - more meteors!
+ if (meteor_interval > 30)
+ meteor_interval -= 2;
+ else if ((score % 100 == 0) && (meteor_interval > 20))
+ meteor_interval -= 1;
+ else if ((score % 300 == 0) && (meteor_interval > 15))
+ meteor_interval -= 1;
+ else if ((score % 400 == 0) && (meteor_interval > 10))
+ meteor_interval -= 1;
+ else if ((score % 500 == 0) && (meteor_interval > 5))
+ meteor_interval -= 1;
+ }
+
+ free(meteor);
+ if (prev != NULL)
+ prev->next = next;
+ else first_meteor = next;
+ }
+ else prev = meteor;
+ meteor = next;
+ }
+}
+
+// Updating game data
+void update_timer(void* data)
+{
+ // Reschedule timer
+ timer = app_timer_register(UPDATE_INTERVAL, (AppTimerCallback) update_timer, NULL);
+
+ // Schedule redraw
+ layer_mark_dirty(gfx_layer);
+
+ // Game over?
+ if (game_over_stop_time && (time_ticks >= game_over_stop_time))
+ {
+ // Saving hi-score
+ if (!game_over)
+ persist_write_int(0, hi_score);
+ game_over = true;
+ }
+
+ // Paused? Nah.
+ if (paused)
+ return;
+
+ time_ticks++;
+
+ // More meteors!
+ if (((time_ticks >= 5000 / UPDATE_INTERVAL) || (hi_score >= 200) // Newbie? Time for some tutorial
+ || !started) // Title screen? Lets go!
+ && (time_ticks % meteor_interval == 0)) // Or just time is come
+ add_meteor();
+
+ // Using accelerometer data to control ship
+ AccelData acc;
+ accel_service_peek(&acc);
+ acc_x = acc.x;
+ acc_y = acc.y;
+
+ // Moving ship
+ speed_x = acc_x / 32;
+ speed_y = -acc_y / 32;
+ ship_x += speed_x;
+ ship_y += speed_y;
+
+ // Some limits
+ if (ship_x < 0) ship_x = 0;
+ if (ship_x > SCREEN_WIDTH) ship_x = SCREEN_WIDTH;
+ if (ship_y < 0) ship_y = 0;
+ if (ship_y > SCREEN_HEIGHT) ship_y = SCREEN_HEIGHT;
+
+ // Moving meteors, updating, etc.
+ update_meteors();
+}
+
+static void game_draw(Layer *layer, GContext *ctx)
+{
+ graphics_context_set_text_color(ctx, GColorBlack);
+
+ // Draw ship
+ if (started && !game_over)
+ {
+ //graphics_context_set_fill_color(ctx, GColorBlack);
+ //graphics_fill_rect(ctx, GRect(ship_x - SHIP_WIDTH / 2, ship_y - SHIP_HEIGHT / 2, SHIP_WIDTH, SHIP_HEIGHT), 3, GCornersAll);
+ graphics_draw_bitmap_in_rect(ctx, ship_bitmap, GRect(ship_x - SHIP_WIDTH / 2, ship_y - SHIP_HEIGHT / 2, SHIP_WIDTH, SHIP_HEIGHT));
+ }
+
+ // Draw all meteors
+ Meteor* meteor = first_meteor;
+ int meteor_count = 0;
+ while (meteor)
+ {
+ graphics_fill_circle(ctx, (GPoint) { .x = meteor->x, .y = meteor->y }, meteor->size / 2);
+ meteor_count++;
+ meteor = meteor->next;
+ }
+
+ // Score
+ if (started)
+ {
+ char score_text[20];
+ snprintf(score_text, sizeof(score_text), "SCORE: %.5d", score);
+ GFont *score_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD);
+ graphics_draw_text(ctx, score_text, score_font, (GRect) { .origin = {0, 0}, .size = {140, 30} }, GTextOverflowModeWordWrap, GTextAlignmentRight, NULL);
+ }
+
+ // Some tutorial
+ if (started)
+ {
+ GFont *tutorial_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD);
+ if (time_ticks < 3000 / UPDATE_INTERVAL && hi_score < 200)
+ graphics_draw_text(ctx, "Tilt your watches to control the spaceship", tutorial_font, (GRect) { .origin = {0, 130}, .size = {144, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+ else if (time_ticks < 6000 / UPDATE_INTERVAL && hi_score < 200)
+ graphics_draw_text(ctx, "Avoid asteroids!", tutorial_font, (GRect) { .origin = {0, 130}, .size = {144, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+ else if (time_ticks < 9000 / UPDATE_INTERVAL && hi_score < 200)
+ graphics_draw_text(ctx, "Good luck!", tutorial_font, (GRect) { .origin = {0, 130}, .size = {144, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+ }
+
+ if (!started) // Title screen
+ {
+ graphics_context_set_stroke_color(ctx, GColorBlack);
+ graphics_draw_rect(ctx, GRect(20, 36, 103, 102));
+ graphics_context_set_fill_color(ctx, GColorWhite);
+ graphics_fill_rect(ctx, GRect(21, 37, 101, 100), 0, GCornerNone);
+
+ GFont *hi_score_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD);
+ char hi_score_text[30];
+ snprintf(hi_score_text, sizeof(hi_score_text), "HI SCORE: %.5d", hi_score);
+ graphics_draw_text(ctx, hi_score_text, hi_score_font, (GRect) { .origin = {25, 38}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+
+ GFont *title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD);
+ graphics_draw_text(ctx, "SPACE DRAGON", title_font, (GRect) { .origin = {25, 60}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+
+ GFont *version_font = fonts_get_system_font(FONT_KEY_GOTHIC_14);
+ graphics_draw_text(ctx, VERSION, version_font, (GRect) { .origin = {25, 103}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentRight, NULL);
+
+ GFont *copyright_font = fonts_get_system_font(FONT_KEY_GOTHIC_14);
+ graphics_draw_text(ctx, "(c) Cluster, 2014", copyright_font, (GRect) { .origin = {25, 118}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+
+ }
+ else if (game_over) // "GAME OVER" box
+ {
+ graphics_context_set_stroke_color(ctx, GColorBlack);
+ graphics_draw_rect(ctx, GRect(20, 46, 103, 82));
+ graphics_context_set_fill_color(ctx, GColorWhite);
+ graphics_fill_rect(ctx, GRect(21, 47, 101, 80), 0, GCornerNone);
+
+ // Flash hi-score if beaten
+ if (!hi_score || (score < hi_score) || ((time_ticks / 10) % 2))
+ {
+ GFont *hi_score_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD);
+ char hi_score_text[30];
+ snprintf(hi_score_text, sizeof(hi_score_text), "HI SCORE: %.5d", hi_score);
+ graphics_draw_text(ctx, hi_score_text, hi_score_font, (GRect) { .origin = {25, 48}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+ }
+
+ GFont *game_over_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD);
+ graphics_draw_text(ctx, "GAME OVER", game_over_font, (GRect) { .origin = {25, 65}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+
+ GFont *game_over_comment_font = fonts_get_system_font(FONT_KEY_GOTHIC_14);
+ graphics_draw_text(ctx, "PRESS ANY BUTTON", game_over_comment_font, (GRect) { .origin = {25, 92}, .size = {94, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+
+ }
+ else if (paused) // "PAUSED" box
+ {
+ graphics_context_set_stroke_color(ctx, GColorBlack);
+ graphics_draw_rect(ctx, GRect(44, 76, 55, 16));
+ graphics_context_set_fill_color(ctx, GColorWhite);
+ graphics_fill_rect(ctx, GRect(45, 77, 53, 14), 0, GCornerNone);
+ GFont *paused_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD);
+ graphics_draw_text(ctx, "PAUSED", paused_font, (GRect) { .origin = {0, 75}, .size = {144, 30} }, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
+ }
+
+ // Some debug info
+ if (show_debug)
+ {
+ GFont *debug_font = fonts_get_system_font(FONT_KEY_GOTHIC_14);
+ char debug_text[200];
+ snprintf(debug_text, sizeof(debug_text), "AccX: %.5d\r\nAccY: %.5d\r\nSpeedX: %d\r\nSpeedY: %d\r\nShipX: %d\r\nShipY: %d\r\nMeteors: %d\r\nInterval: %d\r\nTime: %d", acc_x, acc_y, (int)speed_x, (int)speed_y, (int)ship_x, (int)ship_y, meteor_count, meteor_interval, (int)time_ticks);
+ graphics_draw_text(ctx, debug_text, debug_font, (GRect) { .origin = {0, 0}, .size = {144, 168} }, GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL);
+ }
+}
+
+static void destroy_ui(void) {
+ window_destroy(s_window);
+ layer_destroy(gfx_layer);
+ gbitmap_destroy(ship_bitmap);
+}
+
+static void free_meteors()
+{
+ Meteor* meteor = first_meteor;
+ while (meteor)
+ {
+ Meteor* next = meteor->next;
+ free(meteor);
+ meteor = next;
+ }
+ first_meteor = NULL;
+}
+
+static void handle_window_unload(Window* window) {
+ app_timer_cancel(timer); // Cancel timer
+ accel_data_service_unsubscribe(); // Disabling accelerometer
+ app_focus_service_unsubscribe(); // Unsubscribing from focus
+ free_meteors(); // Free memory from meteors
+ destroy_ui(); // Free memory from UI
+ persist_write_int(0, hi_score); // Saving hi score
+}
+
+void app_in_focus_callback(bool in_focus)
+{
+ if (!in_focus && started && !game_over) paused = true;
+}
+
+
+static void click_handler(ClickRecognizerRef recognizer, void *context)
+{
+ if (!started || game_over) reset_game();
+ else paused = !paused;
+}
+
+static void config_provider(void *context)
+{
+ window_single_click_subscribe(BUTTON_ID_UP, click_handler);
+ window_single_click_subscribe(BUTTON_ID_SELECT, click_handler);
+ window_single_click_subscribe(BUTTON_ID_DOWN, click_handler);
+}
+
+void reset_game()
+{
+ srand(time(NULL));
+ free_meteors();
+ time_ticks = 0;
+ ship_x = SCREEN_WIDTH / 2;
+ ship_y = SCREEN_HEIGHT / 2;
+ meteor_interval = METEOR_START_INTERVAL;
+ score = 0;
+ game_over_stop_time = 0;
+ game_over = false;
+ paused = false;
+ started = true;
+}
+
+void show_game() {
+ reset_game();
+ game_over = true;
+ started = false;
+ meteor_interval = 10;
+
+ // Reading hi-score from memory
+ if (persist_exists(0))
+ hi_score = persist_read_int(0);
+ else hi_score = 0;
+
+ // Loading ship bitmap
+ ship_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SHIP);
+
+ // Crating window
+ s_window = window_create();
+ window_set_background_color(s_window, GColorWhite);
+ window_set_fullscreen(s_window, true);
+
+ window_set_window_handlers(s_window, (WindowHandlers) {
+ .unload = handle_window_unload,
+ });
+
+ // The layer
+ Layer *window_layer = window_get_root_layer(s_window);
+ GRect bounds = layer_get_frame(window_layer);
+ gfx_layer = layer_create(bounds);
+ layer_set_update_proc(gfx_layer, game_draw);
+ layer_add_child(window_layer, gfx_layer);
+
+ window_set_click_config_provider(s_window, config_provider);
+
+ window_stack_push(s_window, true);
+
+ timer = app_timer_register(UPDATE_INTERVAL, (AppTimerCallback) update_timer, NULL);
+ accel_data_service_subscribe(0, NULL);
+ accel_service_set_sampling_rate(ACCEL_SAMPLING_100HZ);
+ app_focus_service_subscribe(app_in_focus_callback);
+}
diff --git a/src/game.h b/src/game.h
new file mode 100644
index 0000000..c925fe4
--- /dev/null
+++ b/src/game.h
@@ -0,0 +1,13 @@
+#define VERSION "v1.2"
+
+typedef struct Meteor {
+ float x;
+ float y;
+ float speed_x;
+ float speed_y;
+ int size;
+ struct Meteor* next;
+} Meteor;
+
+void show_game();
+void reset_game(); \ No newline at end of file
diff --git a/src/space-dragon.c b/src/space-dragon.c
new file mode 100644
index 0000000..fe62838
--- /dev/null
+++ b/src/space-dragon.c
@@ -0,0 +1,8 @@
+#include <pebble.h>
+#include "game.h"
+
+int main(void) {
+ show_game();
+
+ app_event_loop();
+}