123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- /*
- * main.c
- * Duality
- *
- * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
- *
- * Based on examples from gbdk-2020:
- * https://github.com/gbdk-2020/gbdk-2020/blob/develop/gbdk-lib/examples/gb/rand/rand.c
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * See <http://www.gnu.org/licenses/>.
- */
-
- #include <gbdk/metasprites.h>
- #include <rand.h>
-
- #include "banks.h"
- #include "config.h"
- #include "maps.h"
- #include "obj.h"
- #include "sprites.h"
- #include "sound.h"
- #include "input.h"
- #include "game.h"
- #include "score.h"
- #include "sgb_border.h"
- #include "border_sgb.h"
- #include "timer.h"
- #include "sample.h"
- #include "main.h"
-
- uint8_t debug_menu_index = 0;
- uint8_t debug_special_value = 0;
-
- BANKREF(main)
-
- const struct debug_entry debug_entries[DEBUG_ENTRY_COUNT] = {
- { .name = "marker", .flag = DBG_MARKER, .max = 1 }, // 0
- { .name = "invuln", .flag = DBG_GOD_MODE, .max = 1 }, // 1
- { .name = "music", .flag = DBG_NONE, .max = 3 }, // 2
- { .name = "sfx-test", .flag = DBG_NONE, .max = 3 }, // 3
- { .name = "cl score", .flag = DBG_CLEAR_SCORE, .max = 1 }, // 4
- { .name = "0 scores", .flag = DBG_ZERO_SCORE, .max = 1 }, // 5
- };
-
- static void highscore(uint8_t is_black) NONBANKED {
- HIDE_WIN;
-
- move_win(MINWNDPOSX, MINWNDPOSY);
- hide_sprites_range(SPR_NUM_START, MAX_HARDWARE_SPRITES);
- win_score_clear(is_black ? 1 : 0);
-
- SHOW_WIN;
-
- for (uint8_t i = 0; i < SCORE_NUM; i++) {
- struct scores score;
- is_black ? score_lowest(i, &score) : score_highest(i, &score);
- win_score_draw(score, i, is_black);
- }
-
- while (1) {
- key_read();
-
- if (key_pressed(J_A) || key_pressed(J_B)) {
- break;
- }
-
- vsync();
- }
- }
-
- static void about_screen(void) NONBANKED {
- HIDE_WIN;
-
- move_win(MINWNDPOSX, MINWNDPOSY);
- hide_sprites_range(SPR_NUM_START, MAX_HARDWARE_SPRITES);
- win_about();
-
- SHOW_WIN;
-
- while (1) {
- key_read();
-
- if (key_pressed(J_A) || key_pressed(J_B) || key_pressed(J_SELECT)) {
- break;
- }
-
- vsync();
- }
- }
-
- static void splash_win(void) NONBANKED {
- HIDE_WIN;
-
- if (conf_get()->debug_flags & DBG_MENU) {
- win_debug();
- move_win(MINWNDPOSX, MINWNDPOSY);
- } else {
- // initially show the top 1 scores
- struct scores score;
-
- score_lowest(0, &score);
- int32_t low = score.score;
-
- score_highest(0, &score);
- int32_t high = score.score;
-
- win_splash_draw(-low, high);
-
- move_win(MINWNDPOSX, MINWNDPOSY + DEVICE_SCREEN_PX_HEIGHT - (8 * 4));
- }
-
- SHOW_WIN;
- }
-
- static void splash_anim(uint8_t *hiwater) NONBANKED {
- static uint8_t frame = 0;
- static uint8_t state = 0;
-
- if (++frame >= 60) {
- frame = 0;
- if (++state >= 10) {
- state = 0;
- }
- }
-
- int16_t spd_off_x = 0;
- int16_t spd_off_y = 0;
- int32_t score = 0;
- obj_do(&spd_off_x, &spd_off_y, &score, hiwater, 1);
-
- switch (state) {
- case 0:
- case 2:
- spr_draw(SPR_SHIP, FLIP_NONE, -4, -42 - 1, 4, hiwater);
- break;
-
- case 1:
- spr_draw(SPR_SHIP, FLIP_NONE, -4, -42 - 1, 4, hiwater);
- if (frame == 0) {
- obj_add(SPR_SHOT, SHIP_OFF, -42, SHOT_SPEED, 0);
- sample_play_shoot();
- }
- break;
-
- case 3:
- if (frame == 30) {
- obj_add(SPR_LIGHT, 42, -42, 0, 0);
- }
- spr_draw(SPR_SHIP, FLIP_NONE, -1, -42 + 4, 0, hiwater);
- break;
-
- case 8:
- if (frame == 30) {
- obj_add(SPR_DARK, -42, -42, 0, 0);
- }
- spr_draw(SPR_SHIP, FLIP_NONE, -1, -42 + 4, 0, hiwater);
- break;
-
- case 4:
- case 9:
- spr_draw(SPR_SHIP, FLIP_NONE, -1, -42 + 4, 0, hiwater);
- break;
-
- case 5:
- case 7:
- spr_draw(SPR_SHIP, FLIP_X, 4, -42, 4, hiwater);
- break;
-
- case 6:
- spr_draw(SPR_SHIP, FLIP_X, 4, -42, 4, hiwater);
- if (frame == 0) {
- obj_add(SPR_SHOT, -SHIP_OFF, -42, -SHOT_SPEED, 0);
- sample_play_shoot();
- }
- break;
- }
- }
-
- static void splash(void) NONBANKED {
- snd_music_off();
- snd_note_off();
-
- disable_interrupts();
- DISPLAY_OFF;
- map_title();
- move_bkg(0, 0);
- SHOW_BKG;
- spr_init_pal();
- SHOW_SPRITES;
- SPRITES_8x8;
-
- obj_init();
- obj_add(SPR_LIGHT, 42, -42, 0, 0);
- obj_add(SPR_DARK, -42, -42, 0, 0);
-
- win_init(1);
- splash_win();
-
- DISPLAY_ON;
- enable_interrupts();
-
- if (!(conf_get()->debug_flags & DBG_MENU)) {
- snd_menu_music();
- }
-
- while (1) {
- key_read();
-
- if (key_pressed(J_LEFT) && (!(conf_get()->debug_flags & DBG_MENU))) {
- highscore(1);
- splash_win();
- } else if (key_pressed(J_RIGHT) && (!(conf_get()->debug_flags & DBG_MENU))) {
- highscore(0);
- splash_win();
- } else if (key_pressed(J_SELECT)) {
- about_screen();
- splash_win();
- } else if (key_pressed(J_START)) {
- if ((key_debug() == 0) && (!(conf_get()->debug_flags & DBG_MENU))) {
- conf_get()->debug_flags |= DBG_MENU;
- snd_music_off();
- snd_note_off();
- conf_write_crc();
- splash_win();
- } else {
- break;
- }
- } else {
- if (conf_get()->debug_flags & DBG_MENU) {
- // do it here so you quickly see the flag going to 1 and back to 0
- if (conf_get()->debug_flags & DBG_CLEAR_SCORE) {
- score_reset();
- conf_get()->debug_flags &= ~DBG_CLEAR_SCORE;
- conf_write_crc();
- splash_win();
- }
- if (conf_get()->debug_flags & DBG_ZERO_SCORE) {
- score_zero();
- conf_get()->debug_flags &= ~DBG_ZERO_SCORE;
- conf_write_crc();
- splash_win();
- }
-
- uint8_t switch_special = 0;
-
- if (key_pressed(J_UP)) {
- if (debug_menu_index > 0) {
- debug_menu_index--;
- } else {
- debug_menu_index = DEBUG_ENTRY_COUNT - 1;
- }
- debug_special_value = 0;
- snd_music_off();
- snd_note_off();
- splash_win();
- } else if (key_pressed(J_DOWN)) {
- if (debug_menu_index < (DEBUG_ENTRY_COUNT - 1)) {
- debug_menu_index++;
- } else {
- debug_menu_index = 0;
- }
- debug_special_value = 0;
- snd_music_off();
- snd_note_off();
- splash_win();
- } else if (key_pressed(J_LEFT)) {
- START_ROM_BANK(BANK(main));
- if (debug_entries[debug_menu_index].flag != DBG_NONE) {
- conf_get()->debug_flags ^= debug_entries[debug_menu_index].flag;
- conf_write_crc();
- } else {
- if (debug_special_value > 0) {
- debug_special_value--;
- } else {
- debug_special_value = debug_entries[debug_menu_index].max;
- }
- switch_special = 1;
- }
- END_ROM_BANK();
- splash_win();
- } else if (key_pressed(J_RIGHT)) {
- START_ROM_BANK(BANK(main));
- if (debug_entries[debug_menu_index].flag != DBG_NONE) {
- conf_get()->debug_flags ^= debug_entries[debug_menu_index].flag;
- conf_write_crc();
- } else {
- if (debug_special_value < debug_entries[debug_menu_index].max) {
- debug_special_value++;
- } else {
- debug_special_value = 0;
- }
- switch_special = 1;
- }
- END_ROM_BANK();
- splash_win();
- } else if (key_pressed(J_A)) {
- START_ROM_BANK(BANK(main));
- if (debug_entries[debug_menu_index].flag != DBG_NONE) {
- conf_get()->debug_flags ^= debug_entries[debug_menu_index].flag;
- conf_write_crc();
- } else {
- if (debug_special_value < debug_entries[debug_menu_index].max) {
- debug_special_value++;
- } else {
- debug_special_value = 0;
- }
- switch_special = 1;
- }
- END_ROM_BANK();
- splash_win();
- } else if (key_pressed(J_B)) {
- conf_get()->debug_flags &= ~DBG_MENU;
- debug_special_value = 0;
- conf_write_crc();
- splash_win();
- snd_menu_music();
- }
-
- if (switch_special && (debug_menu_index == 2)) {
- switch (debug_special_value) {
- case 0:
- snd_music_off();
- snd_note_off();
- break;
-
- case 1:
- snd_menu_music();
- snd_note_off();
- break;
-
- case 2:
- snd_game_music();
- snd_note_off();
- break;
-
- case 3:
- snd_gameover_music();
- snd_note_off();
- break;
-
- default:
- break;
- }
- } else if (switch_special && (debug_menu_index == 3)) {
- switch (debug_special_value) {
- case 1:
- sample_play_shoot();
- break;
-
- case 2:
- sample_play_explosion_orbs();
- break;
-
- case 3:
- sample_play_explosion_ship();
- break;
-
- default:
- break;
- }
- }
- }
- }
-
- uint8_t hiwater = SPR_NUM_START;
-
- if (!(conf_get()->debug_flags & DBG_MENU)) {
- if (conf_get()->debug_flags & DBG_MARKER) {
- spr_draw(SPR_DEBUG, FLIP_NONE, 0, -10, 0, &hiwater);
- spr_draw(SPR_SHOT_LIGHT, FLIP_NONE, 0, -10, 0, &hiwater);
-
- spr_draw(SPR_DEBUG, FLIP_NONE, 0, 0, 0, &hiwater);
- spr_draw(SPR_SHOT, FLIP_NONE, 0, 0, 0, &hiwater);
-
- spr_draw(SPR_DEBUG, FLIP_NONE, 0, 10, 0, &hiwater);
- spr_draw(SPR_SHOT_DARK, FLIP_NONE, 0, 10, 0, &hiwater);
-
- spr_draw(SPR_DEBUG, FLIP_NONE, 42, -42, 0, &hiwater);
- spr_draw(SPR_DEBUG, FLIP_NONE, 0, -42, 0, &hiwater);
- spr_draw(SPR_DEBUG, FLIP_NONE, -42, -42, 0, &hiwater);
- }
-
- splash_anim(&hiwater);
- }
-
- hide_sprites_range(hiwater, MAX_HARDWARE_SPRITES);
-
- vsync();
- }
- }
-
- static uint16_t ask_name(int32_t score) NONBANKED {
- snd_music_off();
- snd_note_off();
-
- disable_interrupts();
- DISPLAY_OFF;
- map_title();
- move_bkg(0, 0);
- SHOW_BKG;
- spr_init_pal();
- SHOW_SPRITES;
- SPRITES_8x8;
-
- hide_sprites_range(SPR_NUM_START, MAX_HARDWARE_SPRITES);
-
- win_init(1);
- win_name(score);
-
- move_win(MINWNDPOSX, MINWNDPOSY);
- SHOW_WIN;
-
- DISPLAY_ON;
- enable_interrupts();
-
- snd_gameover_music();
-
- char name[3] = { 'a', 'a', 'a' };
- uint8_t pos = 0;
- win_name_draw(convert_name(name[0], name[1], name[2]), score < 0, pos);
-
- while (1) {
- key_read();
-
- if (key_pressed(J_LEFT)) {
- if (pos > 0) {
- pos--;
- win_name_draw(convert_name(name[0], name[1], name[2]), score < 0, pos);
- }
- } else if (key_pressed(J_RIGHT)) {
- if (pos < 3) {
- pos++;
- win_name_draw(convert_name(name[0], name[1], name[2]), score < 0, pos);
- }
- } else if (key_pressed(J_UP)) {
- if (pos < 3) {
- name[pos]++;
- if (name[pos] > 'z') {
- name[pos] -= 'z' - 'a' + 1;
- }
- win_name_draw(convert_name(name[0], name[1], name[2]), score < 0, pos);
- }
- } else if (key_pressed(J_DOWN)) {
- if (pos < 3) {
- name[pos]--;
- if (name[pos] < 'a') {
- name[pos] += 'z' - 'a' + 1;
- }
- win_name_draw(convert_name(name[0], name[1], name[2]), score < 0, pos);
- }
- } else if (key_pressed(J_A)) {
- if (pos < 3) {
- pos++;
- win_name_draw(convert_name(name[0], name[1], name[2]), score < 0, pos);
- } else {
- break;
- }
- } else if (key_pressed(J_START)) {
- break;
- }
-
- vsync();
- }
-
- return convert_name(name[0], name[1], name[2]);
- }
-
- static void sgb_init(void) NONBANKED {
- // Wait 4 frames
- // For SGB on PAL SNES this delay is required on startup, otherwise borders don't show up
- for (uint8_t i = 0; i < 4; i++) {
- vsync();
- }
-
- DISPLAY_ON;
-
- START_ROM_BANK(BANK(border_sgb));
- set_sgb_border((const uint8_t *)border_sgb_tiles, sizeof(border_sgb_tiles),
- (const uint8_t *)border_sgb_map, sizeof(border_sgb_map),
- (const uint8_t *)border_sgb_palettes, sizeof(border_sgb_palettes));
- END_ROM_BANK();
-
- DISPLAY_OFF;
- }
-
- void main(void) NONBANKED {
- // load sgb border
- sgb_init();
-
- // "cheat" and enable double-speed CPU mode on GBC
- if (_cpu == CGB_TYPE) {
- cpu_fast();
- }
-
- conf_init();
- timer_init();
- spr_init();
- snd_init();
-
- splash();
-
- uint16_t seed = DIV_REG;
- waitpadup();
- seed |= ((uint16_t)DIV_REG) << 8;
- initarand(seed);
-
- while (1) {
- int32_t score = game();
-
- if ((!(conf_get()->debug_flags & DBG_GOD_MODE)) && (score != 0) && score_ranking(score)) {
- uint16_t name = ask_name(score);
- struct scores s = { .name = name, .score = score };
- score_add(s);
- }
-
- splash();
- }
- }
|