diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | game.c | 110 | ||||
-rw-r--r-- | game.h | 12 | ||||
-rw-r--r-- | intermediate/map.bmp | bin | 41098 -> 41098 bytes | |||
-rw-r--r-- | intermediate/rooms.bmp | bin | 2538 -> 20538 bytes | |||
-rw-r--r-- | map.c | 161 | ||||
-rw-r--r-- | map.h | 9 | ||||
-rw-r--r-- | random.c | 4 | ||||
-rw-r--r-- | random.h | 1 | ||||
-rw-r--r-- | str.c | 88 | ||||
-rw-r--r-- | str.h | 14 | ||||
-rw-r--r-- | world.c | 5 | ||||
-rw-r--r-- | world.h | 2 |
13 files changed, 357 insertions, 50 deletions
@@ -62,6 +62,7 @@ sources = \ random.c \ rect.c \ render.c \ + str.c \ world.c \ image_sources = \ @@ -3,6 +3,7 @@ #include "game.h" #include "plat.h" #include "render.h" +#include "str.h" const Rect logo_rect = { 47, 0, 54, 16 }; @@ -11,6 +12,8 @@ void init_game(Game* g, Game_State s) { g->st = s; g->frame = 0; g->want_next = 0; + g->kills = 0; + g->cfloor = 0; init_world(w); } @@ -19,9 +22,13 @@ void update_menu(Game* g, App* a) { for (i = 0; i < btn_count; i++) if (a->btn_states[i] & btn_state_pressed) { queue_gs(g, game_state_fade_out); - queue_gs(g, game_state_generate); + queue_gs(g, game_state_reset); + queue_gs(g, game_state_compute_ec); + queue_gs(g, game_state_reset); + queue_gs(g, game_state_restart); + queue_gs(g, game_state_generate_intro); queue_gs(g, game_state_fade_in); - queue_gs(g, game_state_play); + queue_gs(g, game_state_intro); g->want_next = 1; return; } @@ -33,9 +40,12 @@ void update_gameover(Game* g, App* a) { if (a->btn_states[i] & btn_state_pressed) { queue_gs(g, game_state_fade_out); queue_gs(g, game_state_reset); - queue_gs(g, game_state_generate); + queue_gs(g, game_state_compute_ec); + queue_gs(g, game_state_reset); + queue_gs(g, game_state_restart); + queue_gs(g, game_state_generate_intro); queue_gs(g, game_state_fade_in); - queue_gs(g, game_state_play); + queue_gs(g, game_state_intro); g->want_next = 1; return; } @@ -63,9 +73,18 @@ int detect_next(Game* g, const World* w) { const Player* p = &w->player; if (p->y < 0) /* next level */ { queue_gs(g, game_state_fade_out); - queue_gs(g, game_state_generate); - queue_gs(g, game_state_fade_in); - queue_gs(g, game_state_play); + queue_gs(g, game_state_next); + queue_gs(g, game_state_reset); + queue_gs(g, game_state_restore_player); + if (g->cfloor + 1 >= room_count()) { + queue_gs(g, game_state_generate_outro); + queue_gs(g, game_state_fade_in); + queue_gs(g, game_state_outro); + } else { + queue_gs(g, game_state_generate); + queue_gs(g, game_state_fade_in); + queue_gs(g, game_state_play); + } g->want_next = 1; return 1; } @@ -85,6 +104,30 @@ int detect_gameover(Game* g, const World* w) { return 0; } +int detect_start(Game* g, const World* w) { + if (w->player.x < (30 << fbits)) { + queue_gs(g, game_state_fade_out); + queue_gs(g, game_state_reset); + queue_gs(g, game_state_generate); + queue_gs(g, game_state_fade_in); + queue_gs(g, game_state_play); + g->want_next = 1; + return 1; + } + return 0; +} + +int compute_ec(World* w) { + int i, e = room_count(); + int c = 0; + for (i = 0; i < e; i++) { + init_world(w); + generate_floor(&w->map, w, i); + c += w->enemy_count; + } + return c; +} + void update_game(Game* g, App* a) { const Animation* fa; if (g->want_next) { @@ -112,12 +155,30 @@ void update_game(Game* g, App* a) { else g->want_next = 1; break; + case game_state_restart: + g->cfloor = 0; + g->kills = 0; + g->want_next = 1; + break; case game_state_reset: + g->hp = g->w.player.hp; + g->ch = g->w.player.charge; init_world(&g->w); g->want_next = 1; break; + case game_state_restore_player: + g->w.player.hp = g->hp; + g->w.player.charge = g->ch; + g->hp = -1; + g->want_next = 1; + break; + case game_state_next: + g->cfloor++; + g->kills += g->w.kills; + g->want_next = 1; + break; case game_state_generate: - generate_floor(&g->w.map, &g->w); + generate_floor(&g->w.map, &g->w, g->cfloor); g->want_next = 1; break; case game_state_play: @@ -125,6 +186,25 @@ void update_game(Game* g, App* a) { if (detect_next(g, &g->w)) break; detect_gameover(g, &g->w); break; + case game_state_generate_intro: + generate_intro(&g->w.map, &g->w); + g->want_next = 1; + break; + case game_state_generate_outro: + generate_outro(&g->w.map, &g->w); + g->want_next = 1; + break; + case game_state_compute_ec: + g->me = compute_ec(&g->w); + g->want_next = 1; + break; + case game_state_intro: + update_world(&g->w, a); + detect_start(g, &g->w); + break; + case game_state_outro: + update_world(&g->w, a); + break; } g->frame++; } @@ -193,6 +273,20 @@ void ren_state(const Game* g, Game_State s, Renderer* r) { break; case game_state_generate: break; + case game_state_outro: { + char buf[32]; + int x = 228; + ren_text(r, 200, 100, "You win!"); + ren_text(r, 200, 105, "Kills: "); + int_to_buf(g->kills, buf); + ren_text(r, x, 105, buf); + x += string_len(buf) * 4; + ren_text(r, x, 105, "/"); + int_to_buf(g->me, buf); + ren_text(r, x + 4, 105, buf); + } + /* fall through */ + case game_state_intro: case game_state_play: ren_world(&g->w, r); break; @@ -11,8 +11,16 @@ struct App; typedef enum { game_state_menu, game_state_play, + game_state_intro, + game_state_outro, + game_state_compute_ec, game_state_generate, + game_state_generate_intro, + game_state_generate_outro, + game_state_restart, game_state_reset, + game_state_restore_player, + game_state_next, game_state_fade_in, game_state_fade_out, game_state_over @@ -23,8 +31,8 @@ typedef struct { Game_State st, ps; Game_State sq[game_max_state_queue]; Rect fr; - int want_next; - int qt, frame, ff; + int want_next, me, kills, cfloor; + int qt, frame, ff, hp, ch; } Game; void init_game(Game* g, Game_State s); diff --git a/intermediate/map.bmp b/intermediate/map.bmp Binary files differindex 588a13d..8389d9f 100644 --- a/intermediate/map.bmp +++ b/intermediate/map.bmp diff --git a/intermediate/rooms.bmp b/intermediate/rooms.bmp Binary files differindex a0bf456..60d021c 100644 --- a/intermediate/rooms.bmp +++ b/intermediate/rooms.bmp @@ -14,18 +14,24 @@ Rect tile_rects[] = { #undef x }; -void generate_room(Map* m) { +void generate_room(Map* m, int r) { const Bitmap* b = get_bitmap(asset_id_rooms_img); const unsigned* p = (const unsigned*)&b[1]; - const int max_rooms = b->h / map_h; - const int r = get_r() % max_rooms; +#ifdef force_room + r = force_room; +#endif int o = r * map_w * map_h; int e = map_w * map_h, i; for (i = 0; i < e; i++, o++) { int bit = 1 << (o & 0x1f); - if (p[o >> 5] & bit) - m->tiles[i] = tile_brick; - else + if (p[o >> 5] & bit) { + if ((get_r() & 127) == 0) + m->tiles[i] = tile_block1; + else if ((get_r() & 127) == 0) + m->tiles[i] = tile_stone; + else + m->tiles[i] = tile_brick; + } else m->tiles[i] = null_tile; } } @@ -146,6 +152,8 @@ void generate_doors(Map* m) { #undef check #undef place +#undef place_a +#undef place_o void generate_enemies(const Map* m, World* w) { #define check \ @@ -178,46 +186,41 @@ void generate_enemies(const Map* m, World* w) { } } /* flying enemies */ - for (y = 1; y < ey; y++) + for (y = 1; y < ey; y++) { + int next_y = y; for (x = 1; x < ex; x++) { - Rect rect = { 0 }; - int i, j; - rect.x = x; - rect.y = y; - for (j = y; j < ey; j++) { - if (m->collision[i + j * map_w]) - rect.h = 0; - for (i = x, rect.w = 0; i < ex && rect.h > 3; i++) { - if (!m->collision[i + j * map_w]) - rect.w++; - if (rect.w > 4) { - Enemy* e; - e = inst_enemy( - w, - enemy_fly, - ((x + (rect.w >> 1)) * map_tile_size) << fbits, - ((y + (rect.h >> 1)) * map_tile_size) << fbits - ); - e->face = get_r() & 1; - x = i; - y = j; - rect.w = 0; - rect.h = 0; - goto found; - } + int i = 0, j = 0; + int ei = x + 4, ej = y + 4; + int ok = 1; + for (j = y; j < ej; j++) + for (i = x; i < ei; i++) { + if (m->collision[i + j * map_w]) + ok = 0; } - rect.h++; + if (ok) { + Enemy* e; + e = inst_enemy( + w, + enemy_fly, + ((x + 2) * map_tile_size) << fbits, + ((y + 2) * map_tile_size) << fbits + ); + e->face = get_r() & 1; + x = i; + next_y = j; } - found:; } + y = next_y; + } } -void generate_floor(Map* m, World* w) { +void generate_floor(Map* m, World* w, int fi) { Player* p = &w->player; w->enemy_count = 0; w->effect_count = 0; w->deathzone_count = 0; - generate_room(m); + set_ri(0); + generate_room(m, fi); generate_ramps(m); generate_doors(m); generate_floors(m); @@ -249,3 +252,89 @@ void render_map(const Map* m, Renderer* r) { } } } + +#define place(t, c, x, y) { \ + m->tiles[x + y * map_w] = t; \ + m->collision[x + y * map_w] = c; \ + } +#define ncol(x, y) \ + m->collision[x + y * map_w] = 0; + +void generate_intro(Map* m, struct World* w) { + int i, j, b = map_h - 1; + int e = map_w * map_h; + for (i = 0; i < e; i++) { + m->tiles[i] = null_tile; + m->collision[i] = 0; + } + for (i = 0; i < map_w; i++) { + int bot = b * map_w; + m->collision[i] = 1; + m->tiles[i + bot] = tile_stone; + m->collision[i + bot] = 1; + } + b = map_w - 1; + for (i = 0; i < map_h; i++) + m->collision[b + i * map_w] = 1; + for (i = 0; i < 15; i++) + place(tile_stone, 1, i, 13); + for (i = 0; i < 14; i++) + place(tile_stone, 1, i, 12); + for (i = 0; i < 12; i++) + place(tile_stone, 1, i, 11); + for (i = 0; i < 10; i++) + place(tile_stone, 1, i, 10); + place(tile_stone_ramp7, 4, 15, 13); + place(tile_stone_ramp8, 5, 16, 13); + place(tile_stone_ramp10, 7, 14, 12); + place(tile_stone_ramp7, 4, 12, 11); + place(tile_stone_ramp8, 5, 13, 11); + place(tile_stone_ramp7, 4, 10, 10); + place(tile_stone_ramp8, 5, 11, 10); + for (j = 0; j < 10; j++) + for (i = 0; i < 5; i++) + place(tile_brick, 1, i, j); + place(tile_trapdoor3, 0, 4, 9); + place(tile_brick_dark, 0, 3, 9); + place(tile_gate, 0, 2, 9); + place(tile_window_top, 1, 4, 3); + place(tile_window_mid, 1, 4, 4); + place(tile_window_bot, 1, 4, 5); + place(tile_window_top, 1, 4, 0); + place(tile_window_mid, 1, 4, 1); + place(tile_window_bot, 1, 4, 2); + w->player.x = (18 * map_tile_size) << fbits; + w->player.y = (13 * map_tile_size) << fbits; +} + +void generate_outro(Map* m, struct World* w) { + int i, j; + int e = map_w * map_h; + for (i = 0; i < e; i++) { + m->tiles[i] = null_tile; + m->collision[i] = 0; + } + for (i = 0; i < map_h; i++) { + int y = i * map_w; + m->collision[y + (map_w - 1)] = 1; + m->collision[y] = 1; + } + for (j = 10; j < map_h; j++) + for (i = 0; i < map_w; i++) + place(tile_brick, 1, i, j); + for (i = 0; i < map_w; i++) + if (i & 1) + place(tile_brick_floor, 0, i, 9); + w->player.x = (map_w >> 1) << fbits; + w->player.x *= map_tile_size; + w->player.x += (map_tile_size >> 1) << fbits; + w->player.y = (9 * map_tile_size) << fbits; +} + +#undef place +#undef ncol + +int room_count(void) { + const Bitmap* bm = get_bitmap(asset_id_rooms_img); + return bm->h / map_h; +} @@ -43,7 +43,9 @@ struct World; x(tile_trapdoor1, 64, 48) \ x(tile_trapdoor2, 80, 48) \ x(tile_trapdoor3, 96, 48) \ - x(tile_trapdoor4, 112, 48) + x(tile_trapdoor4, 112, 48) \ + x(tile_gate, 128, 48) \ + x(tile_brick_dark, 144, 48) typedef enum { #define x(n, x, y) \ @@ -63,7 +65,10 @@ typedef struct Map { */ } Map; -void generate_floor(Map* m, struct World* w); +void generate_floor(Map* m, struct World* w, int fi); +void generate_intro(Map* m, struct World* w); +void generate_outro(Map* m, struct World* w); void render_map(const Map* m, struct Renderer* r); +int room_count(void); #endif @@ -48,6 +48,10 @@ int random_table[] = { }; static int index = 0; +void set_ri(int i) { + index = i; +} + int get_r(void) { index = (index + 1) & random_table_mask; return random_table[index]; @@ -7,6 +7,7 @@ extern int random_table[random_table_count]; +void set_ri(int i); int get_r(void); int get_rrange(int mi, int ma); int get_rf(void); @@ -0,0 +1,88 @@ +#include "maths.h" +#include "memory.h" +#include "str.h" + +int string_equal(const char* a, const char* b) { + while (*a && *b) { + if (*a != *b) { return 0; } + a++; b++; + } + return 1; +} + +int string_copy(char* dst, const char* src) { + int i; + for (i = 0; *src; src++, dst++, i++) { + *dst = *src; + } + return i; +} + +int string_len(const char* s) { + int l; + for (l = 0; *s; s++, l++); + return l; +} + +char* dup_string(Arena* a, const char* s) { + int size = string_len(s) + 1; + char* d = arena_alloc_aligned(a, size, 1); + string_copy(d, s); + return d; +} + +int int_to_buf(int n, char* buf) { + int i, sign, t; + unsigned n1; + + if(n == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return 1; + } + + i = 0; + sign = n < 0; + + n1 = sign ? -n : n; + + while (n1 != 0) { + buf[i++] = n1 % 10 + '0'; + n1 =n1 / 10; + } + + if(sign) { + buf[i++] = '-'; + } + + buf[i] = '\0'; + + for (t = 0; t < i/2; t++) { + buf[t] ^= buf[i-t-1]; + buf[i-t-1] ^= buf[t]; + buf[t] ^= buf[i-t-1]; + } + + return i; +} + +int f_to_buf(int f, char* buf) { + int frac, w, i; + + frac = (((f) % (1 << fbits)) * 100) / (1 << fbits); + w = f >> fbits; + + i = 0; + + if (f < 0) { + buf[i] = '-'; + i++; + } + + i += int_to_buf(absolute(w), buf + i); + buf[i] = '.'; + i++; + i += int_to_buf(absolute(frac), buf + i); + + return i; +} @@ -0,0 +1,14 @@ +#ifndef str_h +#define str_h + +struct Arena; + +int string_equal(const char* a, const char* b); +int string_copy(char* dst, const char* src); +int string_len(const char* s); +char* dup_string(struct Arena* a, const char* s); + +int int_to_buf(int n, char* buf); +int f_to_buf(int f, char* buf); + +#endif @@ -9,6 +9,7 @@ void init_world(World* w) { w->projectile_count = 0; w->frame = 0; w->freeze = 0; + w->kills = 0; init_player(&w->player); init_laser(&w->laser); } @@ -104,8 +105,10 @@ void update_world(World* w, const App* a) { if (update_effect(&w->effects[i])) w->effects[i] = w->effects[--w->effect_count]; for (i = w->enemy_count - 1; i >= 0; i--) - if (update_enemy(&w->enemies[i], w)) + if (update_enemy(&w->enemies[i], w)) { w->enemies[i] = w->enemies[--w->enemy_count]; + w->kills++; + } for (i = w->projectile_count - 1; i >= 0; i--) if (update_projectile(&w->projectiles[i], w)) w->projectiles[i] = w->projectiles[--w->projectile_count]; @@ -23,7 +23,7 @@ typedef struct World { Player player; Laser laser; Map map; - int frame, freeze; + int frame, freeze, kills; } World; void init_world(World* w); |