diff --git a/.gitignore b/.gitignore index caa07f0c..9f840f67 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ build/ filesystem/ *.z64 -.vscode/ +# .vscode/ # Blender backup files *.blend[0-9] diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..92276a62 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,8 @@ +[submodule "libdragon"] + path = libdragon + url = https://github.com/DragonMinded/libdragon + branch = preview +[submodule "tiny3d"] + path = tiny3d + url = https://github.com/HailToDodongo/tiny3d.git + branch = main \ No newline at end of file diff --git a/.libdragon/config.json b/.libdragon/config.json new file mode 100644 index 00000000..8574aa62 --- /dev/null +++ b/.libdragon/config.json @@ -0,0 +1,5 @@ +{ + "imageName": "ghcr.io/dragonminded/libdragon:preview", + "vendorDirectory": "libdragon", + "vendorStrategy": "submodule" +} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..82da7870 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "${env:N64_INST}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-clang-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..b6addb60 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,57 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++: gcc build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "C/C++: gcc build active file", + "miDebuggerPath": "/usr/bin/gdb" + }, + { + "name": "C/C++: gcc-11 build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "C/C++: gcc-11 build active file", + "miDebuggerPath": "/usr/bin/gdb" + }, + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6ede170e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,54 @@ +{ + "files.associations": { + ".fantomasignore": "ignore", + "libdragon.h": "c", + "core.h": "c", + "minigame.h": "c", + "stdio.h": "c", + "t3dmath.h": "c", + "t3dmodel.h": "c", + "mallard.h": "c", + "profile.h": "c", + "debug.h": "c", + "memory.h": "c", + "*.tcc": "c", + "ctime": "c", + "limits": "c", + "numeric": "c", + "streambuf": "c", + "tuple": "c", + "type_traits": "c", + "utility": "c", + "algorithm": "c", + "charconv": "c", + "format": "c", + "unistd.h": "c", + "cstdint": "c", + "strings.h": "c", + "sequence_1.h": "c", + "input.h": "c", + "ascii.h": "c", + "sequence_3.h": "c", + "sequence_3_input.h": "c", + "sequence_1_input.h": "c", + "sequence_2_input.h": "c", + "sequence_1_graphics.h": "c", + "sequence_3_graphics.h": "c", + "sequence_introduction_graphics.h": "c", + "rdpq_paragraph.h": "c", + "sequence_4.h": "c", + "random": "cpp", + "sequence_introduction_input.h": "c", + "audio.h": "c", + "xm64.h": "c", + "sequence_introduction.h": "c", + "sequence_game.h": "c", + "sequence_game_graphics.h": "c", + "sequence_game_input.h": "c", + "sequence_game_initialize.h": "c", + "controller.h": "c", + "sequence_game_snowman.h": "c", + "sequence_game_duck.h": "c" + }, + "C_Cpp.errorSquiggles": "disabled" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..82adeaf5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,48 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc build active file", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: gcc-11 build active file", + "command": "/usr/bin/gcc-11", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "Task generated by Debugger." + } + ] +} \ No newline at end of file diff --git a/assets/mallard/CelticGaramondTheSecond.ttf b/assets/mallard/CelticGaramondTheSecond.ttf new file mode 100644 index 00000000..71ef5a11 Binary files /dev/null and b/assets/mallard/CelticGaramondTheSecond.ttf differ diff --git a/assets/mallard/HaloDek.ttf b/assets/mallard/HaloDek.ttf new file mode 100644 index 00000000..563eedc1 Binary files /dev/null and b/assets/mallard/HaloDek.ttf differ diff --git a/assets/mallard/HaloDekBig.ttf b/assets/mallard/HaloDekBig.ttf new file mode 100644 index 00000000..563eedc1 Binary files /dev/null and b/assets/mallard/HaloDekBig.ttf differ diff --git a/assets/mallard/HaloDekMedium.ttf b/assets/mallard/HaloDekMedium.ttf new file mode 100644 index 00000000..563eedc1 Binary files /dev/null and b/assets/mallard/HaloDekMedium.ttf differ diff --git a/assets/mallard/four/duck_damage.rgba32.png b/assets/mallard/four/duck_damage.rgba32.png new file mode 100644 index 00000000..d2cb7840 Binary files /dev/null and b/assets/mallard/four/duck_damage.rgba32.png differ diff --git a/assets/mallard/four/duck_idle.rgba32.png b/assets/mallard/four/duck_idle.rgba32.png new file mode 100644 index 00000000..97f4eabb Binary files /dev/null and b/assets/mallard/four/duck_idle.rgba32.png differ diff --git a/assets/mallard/four/duck_run.rgba32.png b/assets/mallard/four/duck_run.rgba32.png new file mode 100644 index 00000000..36f2c6db Binary files /dev/null and b/assets/mallard/four/duck_run.rgba32.png differ diff --git a/assets/mallard/four/duck_slap.rgba32.png b/assets/mallard/four/duck_slap.rgba32.png new file mode 100644 index 00000000..968aaa0e Binary files /dev/null and b/assets/mallard/four/duck_slap.rgba32.png differ diff --git a/assets/mallard/four/duck_walk_1.rgba32.png b/assets/mallard/four/duck_walk_1.rgba32.png new file mode 100644 index 00000000..7d7009ba Binary files /dev/null and b/assets/mallard/four/duck_walk_1.rgba32.png differ diff --git a/assets/mallard/libdragon.rgba32.png b/assets/mallard/libdragon.rgba32.png new file mode 100644 index 00000000..8228c930 Binary files /dev/null and b/assets/mallard/libdragon.rgba32.png differ diff --git a/assets/mallard/mallard_background_park.rgba32.png b/assets/mallard/mallard_background_park.rgba32.png new file mode 100644 index 00000000..67345f79 Binary files /dev/null and b/assets/mallard/mallard_background_park.rgba32.png differ diff --git a/assets/mallard/mallard_game_music.xm b/assets/mallard/mallard_game_music.xm new file mode 100644 index 00000000..9751b795 Binary files /dev/null and b/assets/mallard/mallard_game_music.xm differ diff --git a/assets/mallard/mallard_game_paused_text.rgba32.png b/assets/mallard/mallard_game_paused_text.rgba32.png new file mode 100644 index 00000000..1fe3936c Binary files /dev/null and b/assets/mallard/mallard_game_paused_text.rgba32.png differ diff --git a/assets/mallard/mallard_intro_music.xm b/assets/mallard/mallard_intro_music.xm new file mode 100644 index 00000000..e8f9e066 Binary files /dev/null and b/assets/mallard/mallard_intro_music.xm differ diff --git a/assets/mallard/mallard_logo_black.rgba32.png b/assets/mallard/mallard_logo_black.rgba32.png new file mode 100644 index 00000000..18f4479b Binary files /dev/null and b/assets/mallard/mallard_logo_black.rgba32.png differ diff --git a/assets/mallard/mallard_logo_white.rgba32.png b/assets/mallard/mallard_logo_white.rgba32.png new file mode 100644 index 00000000..0b5d196d Binary files /dev/null and b/assets/mallard/mallard_logo_white.rgba32.png differ diff --git a/assets/mallard/mallard_snowman_idle.rgba32.png b/assets/mallard/mallard_snowman_idle.rgba32.png new file mode 100644 index 00000000..28d5d9a8 Binary files /dev/null and b/assets/mallard/mallard_snowman_idle.rgba32.png differ diff --git a/assets/mallard/one/duck_damage.rgba32.png b/assets/mallard/one/duck_damage.rgba32.png new file mode 100644 index 00000000..3d44975a Binary files /dev/null and b/assets/mallard/one/duck_damage.rgba32.png differ diff --git a/assets/mallard/one/duck_idle.rgba32.png b/assets/mallard/one/duck_idle.rgba32.png new file mode 100644 index 00000000..f0d6e922 Binary files /dev/null and b/assets/mallard/one/duck_idle.rgba32.png differ diff --git a/assets/mallard/one/duck_run.rgba32.png b/assets/mallard/one/duck_run.rgba32.png new file mode 100644 index 00000000..f0de4de5 Binary files /dev/null and b/assets/mallard/one/duck_run.rgba32.png differ diff --git a/assets/mallard/one/duck_slap.rgba32.png b/assets/mallard/one/duck_slap.rgba32.png new file mode 100644 index 00000000..b492516b Binary files /dev/null and b/assets/mallard/one/duck_slap.rgba32.png differ diff --git a/assets/mallard/one/duck_walk_1.rgba32.png b/assets/mallard/one/duck_walk_1.rgba32.png new file mode 100644 index 00000000..49c778ea Binary files /dev/null and b/assets/mallard/one/duck_walk_1.rgba32.png differ diff --git a/assets/mallard/snowman/snowman_damage_evil.rgba32.png b/assets/mallard/snowman/snowman_damage_evil.rgba32.png new file mode 100644 index 00000000..4f3265d3 Binary files /dev/null and b/assets/mallard/snowman/snowman_damage_evil.rgba32.png differ diff --git a/assets/mallard/snowman/snowman_idle_evil.rgba32.png b/assets/mallard/snowman/snowman_idle_evil.rgba32.png new file mode 100644 index 00000000..a6f3e116 Binary files /dev/null and b/assets/mallard/snowman/snowman_idle_evil.rgba32.png differ diff --git a/assets/mallard/snowman/snowman_jump_evil.rgba32.png b/assets/mallard/snowman/snowman_jump_evil.rgba32.png new file mode 100644 index 00000000..c83ed8a7 Binary files /dev/null and b/assets/mallard/snowman/snowman_jump_evil.rgba32.png differ diff --git a/assets/mallard/three/duck_damage.rgba32.png b/assets/mallard/three/duck_damage.rgba32.png new file mode 100644 index 00000000..f133af87 Binary files /dev/null and b/assets/mallard/three/duck_damage.rgba32.png differ diff --git a/assets/mallard/three/duck_idle.rgba32.png b/assets/mallard/three/duck_idle.rgba32.png new file mode 100644 index 00000000..63ba57dd Binary files /dev/null and b/assets/mallard/three/duck_idle.rgba32.png differ diff --git a/assets/mallard/three/duck_run.rgba32.png b/assets/mallard/three/duck_run.rgba32.png new file mode 100644 index 00000000..d72add25 Binary files /dev/null and b/assets/mallard/three/duck_run.rgba32.png differ diff --git a/assets/mallard/three/duck_slap.rgba32.png b/assets/mallard/three/duck_slap.rgba32.png new file mode 100644 index 00000000..435094bb Binary files /dev/null and b/assets/mallard/three/duck_slap.rgba32.png differ diff --git a/assets/mallard/three/duck_walk_1.rgba32.png b/assets/mallard/three/duck_walk_1.rgba32.png new file mode 100644 index 00000000..49e8e428 Binary files /dev/null and b/assets/mallard/three/duck_walk_1.rgba32.png differ diff --git a/assets/mallard/two/duck_damage.rgba32.png b/assets/mallard/two/duck_damage.rgba32.png new file mode 100644 index 00000000..eb3f25df Binary files /dev/null and b/assets/mallard/two/duck_damage.rgba32.png differ diff --git a/assets/mallard/two/duck_idle.rgba32.png b/assets/mallard/two/duck_idle.rgba32.png new file mode 100644 index 00000000..0774372c Binary files /dev/null and b/assets/mallard/two/duck_idle.rgba32.png differ diff --git a/assets/mallard/two/duck_run.rgba32.png b/assets/mallard/two/duck_run.rgba32.png new file mode 100644 index 00000000..2ca77b9b Binary files /dev/null and b/assets/mallard/two/duck_run.rgba32.png differ diff --git a/assets/mallard/two/duck_slap.rgba32.png b/assets/mallard/two/duck_slap.rgba32.png new file mode 100644 index 00000000..e647def7 Binary files /dev/null and b/assets/mallard/two/duck_slap.rgba32.png differ diff --git a/assets/mallard/two/duck_walk_1.rgba32.png b/assets/mallard/two/duck_walk_1.rgba32.png new file mode 100644 index 00000000..b3f07946 Binary files /dev/null and b/assets/mallard/two/duck_walk_1.rgba32.png differ diff --git a/code/mallard/game/sequence_game.c b/code/mallard/game/sequence_game.c new file mode 100644 index 00000000..4e28630e --- /dev/null +++ b/code/mallard/game/sequence_game.c @@ -0,0 +1,208 @@ +#include +#include "../../../core.h" +#include "../../../minigame.h" +#include "../mallard.h" +#include "sequence_game.h" +#include "sequence_game_input.h" +#include "sequence_game_graphics.h" +#include "sequence_game_initialize.h" +#include "sequence_game_snowman.h" +#include "sequence_game_duck.h" + +/////////////////////////////////////////////////////////// +// Globals // +/////////////////////////////////////////////////////////// + +sprite_t *sequence_game_mallard_one_walk_sprite; +sprite_t *sequence_game_mallard_two_walk_sprite; +sprite_t *sequence_game_mallard_three_walk_sprite; +sprite_t *sequence_game_mallard_four_walk_sprite; + +sprite_t *sequence_game_mallard_one_slap_sprite; +sprite_t *sequence_game_mallard_two_slap_sprite; +sprite_t *sequence_game_mallard_three_slap_sprite; +sprite_t *sequence_game_mallard_four_slap_sprite; + +sprite_t *sequence_game_mallard_one_run_sprite; +sprite_t *sequence_game_mallard_two_run_sprite; +sprite_t *sequence_game_mallard_three_run_sprite; +sprite_t *sequence_game_mallard_four_run_sprite; + +sprite_t *sequence_game_mallard_one_idle_sprite; +sprite_t *sequence_game_mallard_two_idle_sprite; +sprite_t *sequence_game_mallard_three_idle_sprite; +sprite_t *sequence_game_mallard_four_idle_sprite; + +sprite_t *sequence_game_mallard_one_damage_sprite; +sprite_t *sequence_game_mallard_two_damage_sprite; +sprite_t *sequence_game_mallard_three_damage_sprite; +sprite_t *sequence_game_mallard_four_damage_sprite; + +sprite_t *sequence_game_snowman_idle_sprite; +sprite_t *sequence_game_snowman_damage_sprite; +sprite_t *sequence_game_snowman_jump_sprite; + +sprite_t *sequence_game_map; + +sprite_t *sequence_game_start_button_sprite; +sprite_t *sequence_game_paused_text_sprite; + +bool sequence_game_should_initialize = true; +bool sequence_game_did_initialize = false; +bool sequence_game_should_cleanup = false; +bool sequence_game_did_cleanup = false; + +bool sequence_game_paused = false; + +xm64player_t sequence_game_xm; + +AiDiff difficulty; + +wav64_t sfx_start; +wav64_t sfx_countdown; +wav64_t sfx_stop; +wav64_t sfx_winner; + +Duck *ducks; +Snowman *snowmen; +Controller *controllers; + +void sequence_game_init() +{ + /////////////////////////////////////////////////////////// + // Set up Display // + /////////////////////////////////////////////////////////// + + display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, FILTERS_RESAMPLE); + + /////////////////////////////////////////////////////////// + // Set up Graphics // + /////////////////////////////////////////////////////////// + + sequence_game_snowman_idle_sprite = sprite_load("rom:/mallard/snowman/snowman_idle_evil.rgba32.sprite"); + sequence_game_snowman_damage_sprite = sprite_load("rom:/mallard/snowman/snowman_damage_evil.rgba32.sprite"); + sequence_game_snowman_jump_sprite = sprite_load("rom:/mallard/snowman/snowman_jump_evil.rgba32.sprite"); + + sequence_game_mallard_one_walk_sprite = sprite_load("rom:/mallard/one/duck_walk_1.rgba32.sprite"); + sequence_game_mallard_two_walk_sprite = sprite_load("rom:/mallard/two/duck_walk_1.rgba32.sprite"); + sequence_game_mallard_three_walk_sprite = sprite_load("rom:/mallard/three/duck_walk_1.rgba32.sprite"); + sequence_game_mallard_four_walk_sprite = sprite_load("rom:/mallard/four/duck_walk_1.rgba32.sprite"); + + sequence_game_mallard_one_slap_sprite = sprite_load("rom:/mallard/one/duck_slap.rgba32.sprite"); + sequence_game_mallard_two_slap_sprite = sprite_load("rom:/mallard/two/duck_slap.rgba32.sprite"); + sequence_game_mallard_three_slap_sprite = sprite_load("rom:/mallard/three/duck_slap.rgba32.sprite"); + sequence_game_mallard_four_slap_sprite = sprite_load("rom:/mallard/four/duck_slap.rgba32.sprite"); + + sequence_game_mallard_one_run_sprite = sprite_load("rom:/mallard/one/duck_run.rgba32.sprite"); + sequence_game_mallard_two_run_sprite = sprite_load("rom:/mallard/two/duck_run.rgba32.sprite"); + sequence_game_mallard_three_run_sprite = sprite_load("rom:/mallard/three/duck_run.rgba32.sprite"); + sequence_game_mallard_four_run_sprite = sprite_load("rom:/mallard/four/duck_run.rgba32.sprite"); + + sequence_game_mallard_one_idle_sprite = sprite_load("rom:/mallard/one/duck_idle.rgba32.sprite"); + sequence_game_mallard_two_idle_sprite = sprite_load("rom:/mallard/two/duck_idle.rgba32.sprite"); + sequence_game_mallard_three_idle_sprite = sprite_load("rom:/mallard/three/duck_idle.rgba32.sprite"); + sequence_game_mallard_four_idle_sprite = sprite_load("rom:/mallard/four/duck_idle.rgba32.sprite"); + + sequence_game_mallard_one_damage_sprite = sprite_load("rom:/mallard/one/duck_damage.rgba32.sprite"); + sequence_game_mallard_two_damage_sprite = sprite_load("rom:/mallard/two/duck_damage.rgba32.sprite"); + sequence_game_mallard_three_damage_sprite = sprite_load("rom:/mallard/three/duck_damage.rgba32.sprite"); + sequence_game_mallard_four_damage_sprite = sprite_load("rom:/mallard/four/duck_damage.rgba32.sprite"); + + // Gmae - Background + sequence_game_map = sprite_load("rom:/mallard/mallard_background_park.rgba32.sprite"); + + sequence_game_start_button_sprite = sprite_load("rom:/core/StartButton.sprite"); + + sequence_game_paused_text_sprite = sprite_load("rom:/mallard/mallard_game_paused_text.rgba32.sprite"); + + sequence_game_should_initialize = false; + sequence_game_did_initialize = true; + difficulty = core_get_aidifficulty(); + + initialize_ducks(); + initialize_controllers(); + + /////////////////////////////////////////////////////////// + // Set up Audio // + /////////////////////////////////////////////////////////// + + xm64player_open(&sequence_game_xm, "rom:/mallard/mallard_game_music.xm64"); + + wav64_open(&sfx_start, "rom:/core/Start.wav64"); + wav64_open(&sfx_countdown, "rom:/core/Countdown.wav64"); + wav64_open(&sfx_stop, "rom:/core/Stop.wav64"); + wav64_open(&sfx_winner, "rom:/core/Winner.wav64"); + + xm64player_play(&sequence_game_xm, 0); +} + +void sequence_game_cleanup() +{ + // Free the sprites. + sprite_free(sequence_game_mallard_one_slap_sprite); + sprite_free(sequence_game_mallard_two_slap_sprite); + sprite_free(sequence_game_mallard_three_slap_sprite); + sprite_free(sequence_game_mallard_four_slap_sprite); + + sprite_free(sequence_game_mallard_one_walk_sprite); + sprite_free(sequence_game_mallard_two_walk_sprite); + sprite_free(sequence_game_mallard_three_walk_sprite); + sprite_free(sequence_game_mallard_four_walk_sprite); + + sprite_free(sequence_game_mallard_one_run_sprite); + sprite_free(sequence_game_mallard_two_run_sprite); + sprite_free(sequence_game_mallard_three_run_sprite); + sprite_free(sequence_game_mallard_four_run_sprite); + + sprite_free(sequence_game_mallard_one_idle_sprite); + sprite_free(sequence_game_mallard_two_idle_sprite); + sprite_free(sequence_game_mallard_three_idle_sprite); + sprite_free(sequence_game_mallard_four_idle_sprite); + + sprite_free(sequence_game_snowman_idle_sprite); + sprite_free(sequence_game_snowman_damage_sprite); + sprite_free(sequence_game_snowman_jump_sprite); + + sprite_free(sequence_game_map); + + sprite_free(sequence_game_start_button_sprite); + sprite_free(sequence_game_paused_text_sprite); + + free_ducks(); + free_snowmen(); + free_controllers(); + + // Stop the music and free the allocated memory. + xm64player_stop(&sequence_game_xm); + xm64player_close(&sequence_game_xm); + + wav64_close(&sfx_start); + wav64_close(&sfx_countdown); + wav64_close(&sfx_stop); + wav64_close(&sfx_winner); + + // Close the display and free the allocated memory. + rspq_wait(); + display_close(); + + // End the sequence. + sequence_game_did_cleanup = true; + sequence_game_finished = true; +} + +void sequence_game(float deltatime) +{ + if (sequence_game_should_initialize && !sequence_game_did_initialize) + { + sequence_game_init(); + } + + sequence_game_update(deltatime); + + sequence_game_render(deltatime); + + if (sequence_game_should_cleanup && !sequence_game_did_cleanup) + { + sequence_game_cleanup(); + } +} \ No newline at end of file diff --git a/code/mallard/game/sequence_game.h b/code/mallard/game/sequence_game.h new file mode 100644 index 00000000..afb144a2 --- /dev/null +++ b/code/mallard/game/sequence_game.h @@ -0,0 +1,101 @@ +#ifndef SEQUENCE_GAME_H +#define SEQUENCE_GAME_H + +#define GAME_DURATION 60.0f +#define GAME_FADE_IN_DURATION 1.0f +#define GAME_COUNTDOWN_DURATION 3.0f +#define GAME_ENDING_DURATION 3.0f +#define GAME_EXIT_DURATION 2.0f +#define GAME_EXIT_THRESHOLD_DURATION 0.1f + +extern bool sequence_game_finished; + +typedef enum +{ + LEFT = 0, + RIGHT = 1, +} Directions; + +typedef enum DuckActions +{ + DUCK_IDLE = 1, + DUCK_WALK = 2, + DUCK_SLAP = 3, + DUCK_RUN = 4, + DUCK_DAMAGE = 5, +} DuckActions; + +typedef struct Duck +{ + int id; + float x; + float y; + float score; + float time_since_last_hit; + float time_seeking_target; + DuckActions action; + Directions direction; + sprite_t *walk_sprite; + sprite_t *slap_sprite; + sprite_t *idle_sprite; + sprite_t *run_sprite; + sprite_t *damage_sprite; + int frames; + int frames_locked_for_slap; + int frames_locked_for_damage; + float collision_box_x1; + float collision_box_y1; + float collision_box_x2; + float collision_box_y2; + float slap_box_x1; + float slap_box_y1; + float slap_box_x2; + float slap_box_y2; + float hit_box_x1; + float hit_box_y1; + float hit_box_x2; + float hit_box_y2; + struct Duck *next; +} Duck; + +typedef enum SnowmanActions +{ + SNOWMAN_IDLE = 0, + SNOWMAN_JUMP = 1, + SNOWMAN_DAMAGE = 2, +} SnowmanActions; + +typedef struct Snowman +{ + int id; + float x; + float y; + float time_since_last_hit; + int health; + SnowmanActions action; + sprite_t *idle_sprite; + sprite_t *damage_sprite; + sprite_t *jump_sprite; + int frames; + int frames_locked_for_damage; + float collision_box_x1; + float collision_box_y1; + float collision_box_x2; + float collision_box_y2; + float hit_box_x1; + float hit_box_y1; + float hit_box_x2; + float hit_box_y2; + struct Snowman *next; +} Snowman; + +typedef struct Controller +{ + unsigned int start_down; + unsigned int start_up; + float start_held_elapsed; +} Controller; + +void sequence_game(float deltatime); + +#endif // SEQUENCE_GAME_H diff --git a/code/mallard/game/sequence_game_duck.c b/code/mallard/game/sequence_game_duck.c new file mode 100644 index 00000000..6417c839 --- /dev/null +++ b/code/mallard/game/sequence_game_duck.c @@ -0,0 +1,268 @@ +#include +#include "sequence_game_initialize.h" +#include "sequence_game_input.h" +#include "sequence_game.h" +#include "sequence_game_duck.h" + +void display_ducks() +{ + Duck *current = ducks; + while (current != NULL) + { + fprintf(stderr, "[Duck #%i - %f], ", current->id, current->collision_box_y2); + current = current->next; + } + fprintf(stderr, "\n"); +} + +Vector2 get_duck_spawn(int x1, int y1, int x2, int y2) +{ + int _x, _y; + float _x1, _y1, _x2, _y2; + bool _validSpawn; + Duck *currentDuck; + + while (true) + { + _validSpawn = true; + _x = random_between(x1, x2); + _y = random_between(y1, y2); + _x1 = _x + DUCK_COLLISION_BOX_X1_OFFSET; + _y1 = _y + DUCK_COLLISION_BOX_Y1_OFFSET; + _x2 = _x + DUCK_COLLISION_BOX_X2_OFFSET; + _y2 = _y + DUCK_COLLISION_BOX_Y2_OFFSET; + + currentDuck = ducks; + while (currentDuck != NULL) + { + Rect currentDuckCollisionBox = (Rect){.x1 = currentDuck->collision_box_x1, .y1 = currentDuck->collision_box_y1, .x2 = currentDuck->collision_box_x2, .y2 = currentDuck->collision_box_y2}; + + if (detect_collision( + currentDuckCollisionBox, + (Rect){.x1 = _x1, .y1 = _y1, .x2 = _x2, .y2 = _y2})) + { + _validSpawn = false; + break; + } + + currentDuck = currentDuck->next; + } + + if (_validSpawn) + { + return (Vector2){.x = _x, .y = _y}; + } + } +} + +Duck *create_duck(int i) +{ + Vector2 spawn; + Duck *duck = (Duck *)malloc(sizeof(Duck)); + + switch (i) + { + case 0: + spawn = get_duck_spawn(PLAYER_1_SPAWN_X1, PLAYER_1_SPAWN_Y1, PLAYER_1_SPAWN_X2, PLAYER_1_SPAWN_Y2); + break; + case 1: + spawn = get_duck_spawn(PLAYER_2_SPAWN_X1, PLAYER_2_SPAWN_Y1, PLAYER_2_SPAWN_X2, PLAYER_2_SPAWN_Y2); + break; + case 2: + spawn = get_duck_spawn(PLAYER_3_SPAWN_X1, PLAYER_3_SPAWN_Y1, PLAYER_3_SPAWN_X2, PLAYER_3_SPAWN_Y2); + break; + case 3: + spawn = get_duck_spawn(PLAYER_4_SPAWN_X1, PLAYER_4_SPAWN_Y1, PLAYER_4_SPAWN_X2, PLAYER_4_SPAWN_Y2); + break; + default: + spawn = get_duck_spawn(PLAYER_4_SPAWN_X1, PLAYER_4_SPAWN_Y1, PLAYER_4_SPAWN_X2, PLAYER_4_SPAWN_Y2); + break; + } + + duck->id = i; + duck->x = spawn.x; + duck->y = spawn.y; + duck->score = 0.0f; + duck->action = DUCK_IDLE; + duck->time_since_last_hit = 0.0f; + duck->time_seeking_target = 0.0f; + duck->direction = (i == 0 || i == 2) ? RIGHT : LEFT; + duck->collision_box_x1 = spawn.x + DUCK_COLLISION_BOX_X1_OFFSET; + duck->collision_box_y1 = spawn.y + DUCK_COLLISION_BOX_Y1_OFFSET; + duck->collision_box_x2 = spawn.x + DUCK_COLLISION_BOX_X2_OFFSET; + duck->collision_box_y2 = spawn.y + DUCK_COLLISION_BOX_Y2_OFFSET; + duck->slap_box_x1 = spawn.x + (duck->direction == RIGHT ? DUCK_SLAP_BOX_X1_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_X1_OFFSET_FACING_LEFT); + duck->slap_box_y1 = spawn.y + (duck->direction == RIGHT ? DUCK_SLAP_BOX_Y1_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_Y1_OFFSET_FACING_LEFT); + duck->slap_box_x2 = spawn.x + (duck->direction == RIGHT ? DUCK_SLAP_BOX_X2_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_X2_OFFSET_FACING_LEFT); + duck->slap_box_y2 = spawn.y + (duck->direction == RIGHT ? DUCK_SLAP_BOX_Y2_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_Y2_OFFSET_FACING_LEFT); + duck->hit_box_x1 = spawn.x + (duck->direction == RIGHT ? DUCK_HIT_BOX_X1_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_X1_OFFSET_FACING_LEFT); + duck->hit_box_y1 = spawn.y + (duck->direction == RIGHT ? DUCK_HIT_BOX_Y1_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_Y1_OFFSET_FACING_LEFT); + duck->hit_box_x2 = spawn.x + (duck->direction == RIGHT ? DUCK_HIT_BOX_X2_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_X2_OFFSET_FACING_LEFT); + duck->hit_box_y2 = spawn.y + (duck->direction == RIGHT ? DUCK_HIT_BOX_Y2_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_Y2_OFFSET_FACING_LEFT); + duck->frames = 0; + duck->frames_locked_for_slap = 0; + duck->frames_locked_for_damage = 0; + + switch (i) + { + case 0: + duck->walk_sprite = sequence_game_mallard_one_walk_sprite; + duck->slap_sprite = sequence_game_mallard_one_slap_sprite; + duck->run_sprite = sequence_game_mallard_one_run_sprite; + duck->idle_sprite = sequence_game_mallard_one_idle_sprite; + duck->damage_sprite = sequence_game_mallard_one_damage_sprite; + break; + case 1: + duck->walk_sprite = sequence_game_mallard_two_walk_sprite; + duck->slap_sprite = sequence_game_mallard_two_slap_sprite; + duck->run_sprite = sequence_game_mallard_two_run_sprite; + duck->idle_sprite = sequence_game_mallard_two_idle_sprite; + duck->damage_sprite = sequence_game_mallard_two_damage_sprite; + break; + case 2: + duck->walk_sprite = sequence_game_mallard_three_walk_sprite; + duck->slap_sprite = sequence_game_mallard_three_slap_sprite; + duck->run_sprite = sequence_game_mallard_three_run_sprite; + duck->idle_sprite = sequence_game_mallard_three_idle_sprite; + duck->damage_sprite = sequence_game_mallard_three_damage_sprite; + break; + case 3: + duck->walk_sprite = sequence_game_mallard_four_walk_sprite; + duck->slap_sprite = sequence_game_mallard_four_slap_sprite; + duck->run_sprite = sequence_game_mallard_four_run_sprite; + duck->idle_sprite = sequence_game_mallard_four_idle_sprite; + duck->damage_sprite = sequence_game_mallard_four_damage_sprite; + break; + default: + break; + } + + return duck; +} + +Duck *get_duck_by_id(int i) +{ + Duck *current = ducks; + while (current != NULL) + { + if (current->id == i) + { + return current; + } + current = current->next; + } + return NULL; +} + +void add_duck(int i) +{ + Duck *duck = create_duck(i); + + // Insert at the head if the list is empty. + // Insert at the head if the new value is smaller. + if (ducks == NULL || ducks->y >= duck->y) + { + duck->next = ducks; + ducks = duck; + return; + } + + // Otherwise, Traverse the ducks to find the correct insertion point. + Duck *currentDuck = ducks; + while (currentDuck->next != NULL && currentDuck->next->y < duck->y) + { + currentDuck = currentDuck->next; + } + + // Insert the new duck + duck->next = currentDuck->next; + currentDuck->next = duck; +} + +void initialize_ducks() +{ + if (ducks == NULL) + { + for (size_t i = 0; i < 4; i++) + { + add_duck(i); + } + } +} + +void free_ducks() +{ + Duck *temporary; + while (ducks != NULL) + { + temporary = ducks; + ducks = ducks->next; + free(temporary); + } +} + +void ducks_bubble_sort() +{ + bool swapped = true; + + while (swapped) + { + Duck **prev = &ducks; + Duck *curr; + Duck *next; + + swapped = false; + for (curr = ducks; curr; prev = &curr->next, curr = curr->next) + { + next = curr->next; + + if (next && curr->collision_box_y2 > next->collision_box_y2) + { + curr->next = next->next; + next->next = curr; + *prev = next; + + swapped = true; + } + } + } +} + +// Update each duck's frame, collision box, and slap box. +void update_ducks(float deltatime) +{ + if( time_elapsed >= GAME_FADE_IN_DURATION + 3 + GAME_DURATION) + { + return; + } + + if (!sequence_game_paused) + { + Duck *currentDuck = ducks; + while (currentDuck != NULL) + { + currentDuck->frames++; + currentDuck->time_since_last_hit += deltatime; + currentDuck->time_seeking_target += deltatime; + + currentDuck->collision_box_x1 = currentDuck->x + DUCK_COLLISION_BOX_X1_OFFSET; + currentDuck->collision_box_y1 = currentDuck->y + DUCK_COLLISION_BOX_Y1_OFFSET; + currentDuck->collision_box_x2 = currentDuck->x + DUCK_COLLISION_BOX_X2_OFFSET; + currentDuck->collision_box_y2 = currentDuck->y + DUCK_COLLISION_BOX_Y2_OFFSET; + + currentDuck->hit_box_x1 = currentDuck->x + (currentDuck->direction == RIGHT ? DUCK_HIT_BOX_X1_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_X1_OFFSET_FACING_LEFT); + currentDuck->hit_box_y1 = currentDuck->y + (currentDuck->direction == RIGHT ? DUCK_HIT_BOX_Y1_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_Y1_OFFSET_FACING_LEFT); + currentDuck->hit_box_x2 = currentDuck->x + (currentDuck->direction == RIGHT ? DUCK_HIT_BOX_X2_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_X2_OFFSET_FACING_LEFT); + currentDuck->hit_box_y2 = currentDuck->y + (currentDuck->direction == RIGHT ? DUCK_HIT_BOX_Y2_OFFSET_FACING_RIGHT : DUCK_HIT_BOX_Y2_OFFSET_FACING_LEFT); + + currentDuck->slap_box_x1 = currentDuck->x + (currentDuck->direction == RIGHT ? DUCK_SLAP_BOX_X1_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_X1_OFFSET_FACING_LEFT); + currentDuck->slap_box_y1 = currentDuck->y + (currentDuck->direction == RIGHT ? DUCK_SLAP_BOX_Y1_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_Y1_OFFSET_FACING_LEFT); + currentDuck->slap_box_x2 = currentDuck->x + (currentDuck->direction == RIGHT ? DUCK_SLAP_BOX_X2_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_X2_OFFSET_FACING_LEFT); + currentDuck->slap_box_y2 = currentDuck->y + (currentDuck->direction == RIGHT ? DUCK_SLAP_BOX_Y2_OFFSET_FACING_RIGHT : DUCK_SLAP_BOX_Y2_OFFSET_FACING_LEFT); + + currentDuck = currentDuck->next; + } + + ducks_bubble_sort(); + } +} \ No newline at end of file diff --git a/code/mallard/game/sequence_game_duck.h b/code/mallard/game/sequence_game_duck.h new file mode 100644 index 00000000..83de996c --- /dev/null +++ b/code/mallard/game/sequence_game_duck.h @@ -0,0 +1,43 @@ +#ifndef SEQUENCE_GAME_DUCK_H +#define SEQUENCE_GAME_DUCK_H + +#define DUCK_TIME_BETWEEN_DAMAGE 1.0f + +#define DUCK_SPRITE_WIDTH 32 +#define DUCK_SPRITE_HEIGHT 32 + +#define DUCK_COLLISION_BOX_X1_OFFSET 8 +#define DUCK_COLLISION_BOX_Y1_OFFSET 16 +#define DUCK_COLLISION_BOX_X2_OFFSET 24 +#define DUCK_COLLISION_BOX_Y2_OFFSET 24 + +#define DUCK_HIT_BOX_X1_OFFSET_FACING_LEFT 8 +#define DUCK_HIT_BOX_Y1_OFFSET_FACING_LEFT 10 +#define DUCK_HIT_BOX_X2_OFFSET_FACING_LEFT 26 +#define DUCK_HIT_BOX_Y2_OFFSET_FACING_LEFT 24 + +#define DUCK_HIT_BOX_X1_OFFSET_FACING_RIGHT 6 +#define DUCK_HIT_BOX_Y1_OFFSET_FACING_RIGHT 10 +#define DUCK_HIT_BOX_X2_OFFSET_FACING_RIGHT 24 +#define DUCK_HIT_BOX_Y2_OFFSET_FACING_RIGHT 24 + +#define DUCK_SLAP_BOX_X1_OFFSET_FACING_LEFT 3 +#define DUCK_SLAP_BOX_Y1_OFFSET_FACING_LEFT 8 +#define DUCK_SLAP_BOX_X2_OFFSET_FACING_LEFT 16 +#define DUCK_SLAP_BOX_Y2_OFFSET_FACING_LEFT 20 + +#define DUCK_SLAP_BOX_X1_OFFSET_FACING_RIGHT 16 +#define DUCK_SLAP_BOX_Y1_OFFSET_FACING_RIGHT 8 +#define DUCK_SLAP_BOX_X2_OFFSET_FACING_RIGHT 29 +#define DUCK_SLAP_BOX_Y2_OFFSET_FACING_RIGHT 20 + +extern Duck *ducks; +extern float time_elapsed; + +void display_ducks(); +Duck *get_duck_by_id(int i); +void initialize_ducks(); +void free_ducks(); +void update_ducks(float deltatime); + +#endif // SEQUENCE_GAME_DUCK_H \ No newline at end of file diff --git a/code/mallard/game/sequence_game_graphics.c b/code/mallard/game/sequence_game_graphics.c new file mode 100644 index 00000000..4b3c218f --- /dev/null +++ b/code/mallard/game/sequence_game_graphics.c @@ -0,0 +1,421 @@ +#include +#include "../../../core.h" +#include "../../../minigame.h" +#include "../mallard.h" +#include "sequence_game.h" +#include "sequence_game_initialize.h" +#include "sequence_game_graphics.h" +#include "sequence_game_duck.h" +#include "sequence_game_snowman.h" + +// These player boxes are just used for visualizing the boxes and spawn points while developing. +#define PLAYER_1_BOX_X1 16 +#define PLAYER_1_BOX_Y1 135 +#define PLAYER_1_BOX_X2 168 +#define PLAYER_1_BOX_Y2 220 + +#define PLAYER_2_BOX_X1 168 +#define PLAYER_2_BOX_Y1 320 +#define PLAYER_2_BOX_X2 288 +#define PLAYER_2_BOX_Y2 220 + +#define PLAYER_3_BOX_X1 16 +#define PLAYER_3_BOX_Y1 50 +#define PLAYER_3_BOX_X2 168 +#define PLAYER_3_BOX_Y2 135 + +#define PLAYER_4_BOX_X1 168 +#define PLAYER_4_BOX_Y1 50 +#define PLAYER_4_BOX_X2 320 +#define PLAYER_4_BOX_Y2 135 + +float sequence_game_start_held_elapsed = 0.0f; +int sequence_game_player_holding_start = -1; + +sprite_t *get_sprite_from_duck(Duck *duck) +{ + switch (duck->action) + { + case DUCK_IDLE: + return duck->idle_sprite; + case DUCK_SLAP: + return duck->slap_sprite; + case DUCK_WALK: + return duck->walk_sprite; + case DUCK_RUN: + return duck->run_sprite; + case DUCK_DAMAGE: + return duck->damage_sprite; + default: + return duck->idle_sprite; + } +} + +int get_frame_from_duck(Duck *duck) +{ + switch (duck->action) + { + case DUCK_IDLE: + return (duck->frames >> 3) % SEQUENCE_GAME_MALLARD_IDLE_FRAMES; // Update every 8 frames + case DUCK_WALK: + return (duck->frames >> 2) % SEQUENCE_GAME_MALLARD_WALK_FRAMES; // Update every 4 frames + case DUCK_SLAP: + return (duck->frames >> 2) % SEQUENCE_GAME_MALLARD_SLAP_FRAMES; // Update every 4 frames + case DUCK_RUN: + return (duck->frames >> 2) % SEQUENCE_GAME_MALLARD_RUN_FRAMES; // Update every 4 frames + case DUCK_DAMAGE: + return (duck->frames >> 2) % SEQUENCE_GAME_MALLARD_DAMAGE_FRAMES; // Update every 4 frames + default: + return 0; + } +} + +void sequence_game_render_map() +{ + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(sequence_game_map, + 0, + 0, + NULL); + rdpq_mode_pop(); +} + +void sequence_game_render_press_start_to_pause() +{ + if (sequence_game_paused == false) + { + // "Press" Text + rdpq_text_print(NULL, FONT_HALODEK, 115, 230, "$01^00Press"); + + // Start Button + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_alphacompare(1); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY_CONST); + rdpq_set_fog_color(RGBA32(0, 0, 0, 255)); + rdpq_sprite_blit(sequence_game_start_button_sprite, 146, 218, NULL); + rdpq_mode_pop(); + + // "to Pause" Text + rdpq_text_print(NULL, FONT_HALODEK, 165, 230, "$01^00to Pause"); + } +} + +void sequence_game_render_paused() +{ + if (sequence_game_paused == true) + { + float x = powf(sequence_game_start_held_elapsed, 3) * ((((float)rand() / (float)RAND_MAX) * 2.0f) - 1.0f); + float y = powf(sequence_game_start_held_elapsed, 3) * ((((float)rand() / (float)RAND_MAX) * 2.0f) - 1.0f); + float percentage = sequence_game_start_held_elapsed / GAME_EXIT_DURATION > 1.0 ? 1.0 : sequence_game_start_held_elapsed / GAME_EXIT_DURATION; + + // COLOR + char *utf8_text = "$03^01PAUSED"; + if (sequence_game_player_holding_start == 0) + utf8_text = "$03^01PAUSED"; + else if (sequence_game_player_holding_start == 1) + utf8_text = "$03^02PAUSED"; + else if (sequence_game_player_holding_start == 2) + utf8_text = "$03^03PAUSED"; + else if (sequence_game_player_holding_start == 3) + utf8_text = "$03^04PAUSED"; + + // COLORED "Paused" Text + rdpq_text_print(NULL, FONT_HALODEK_BIG, 70 + x, 140 + y, utf8_text); + + // "Hold" Text + rdpq_text_print(NULL, FONT_HALODEK, 115, 160, "$01^00Hold"); + + // Start Button + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(sequence_game_start_button_sprite, 145, 148, NULL); + + // "to Quit" Text + rdpq_text_print(NULL, FONT_HALODEK, 165, 160, "$01^00to Quit"); + + rdpq_set_scissor(70 + x + (180.0f * percentage), 0, 250 + x, 240); + + // WHITE "Paused" Text + rdpq_text_print(NULL, FONT_HALODEK_BIG, 70 + x, 140 + y, "$03^00PAUSED"); + } +} + +int get_frame_from_snowman(Snowman *snowman) +{ + switch (snowman->action) + { + case SNOWMAN_IDLE: + return (snowman->frames >> 3) % SEQUENCE_GAME_SNOWMAN_IDLE_FRAMES; // Update every 8 frames + case SNOWMAN_DAMAGE: + return (snowman->frames >> 3) % SEQUENCE_GAME_SNOWMAN_DAMAGE_FRAMES; // Update every 8 frames + case SNOWMAN_JUMP: + return (snowman->frames >> 3) % SEQUENCE_GAME_SNOWMAN_JUMP_FRAMES; // Update every 8 frames + default: + return 0; + } +} + +sprite_t *get_sprite_from_snowman(Snowman *snowman) +{ + switch (snowman->action) + { + case SNOWMAN_IDLE: + return snowman->idle_sprite; + case SNOWMAN_DAMAGE: + return snowman->damage_sprite; + case SNOWMAN_JUMP: + return snowman->jump_sprite; + default: + return snowman->idle_sprite; + } +} + +void render_debug_snowman_hit_box(Snowman *snowman) +{ + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(255, 0, 0, 64)); + rdpq_fill_rectangle(snowman->hit_box_x1, snowman->hit_box_y1, snowman->hit_box_x2, snowman->hit_box_y2); + rdpq_mode_pop(); +} + +void render_debug_snowman_collision_box(Snowman *snowman) +{ + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(0, 0, 255, 128)); + rdpq_fill_rectangle(snowman->collision_box_x1, snowman->collision_box_y1, snowman->collision_box_x2, snowman->collision_box_y2); + rdpq_mode_pop(); +} + +void render_debug_duck_slap_box(Duck *duck) +{ + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(255, 0, 0, 64)); + rdpq_fill_rectangle(duck->slap_box_x1, duck->slap_box_y1, duck->slap_box_x2, duck->slap_box_y2); + rdpq_mode_pop(); +} + +void render_debug_duck_collision_box(Duck *duck) +{ + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(0, 0, 255, 128)); + rdpq_fill_rectangle(duck->collision_box_x1, duck->collision_box_y1, duck->collision_box_x2, duck->collision_box_y2); + rdpq_mode_pop(); +} + +void render_debug_duck_hit_box(Duck *duck) +{ + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(0, 255, 0, 128)); + rdpq_fill_rectangle(duck->hit_box_x1, duck->hit_box_y1, duck->hit_box_x2, duck->hit_box_y2); + rdpq_mode_pop(); +} + +void sequence_game_render_duck(Duck *currentDuck) +{ + rdpq_blitparms_t blitparms = { + .s0 = get_frame_from_duck(currentDuck) * DUCK_SPRITE_WIDTH, + .t0 = 0, + .width = DUCK_SPRITE_WIDTH, + .height = DUCK_SPRITE_HEIGHT, + .flip_x = currentDuck->direction == RIGHT ? true : false, + }; + + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(get_sprite_from_duck(currentDuck), currentDuck->x, currentDuck->y, &blitparms); + + // render_debug_duck_slap_box(currentDuck); + // render_debug_duck_collision_box(currentDuck); + // render_debug_duck_hit_box(currentDuck); +} + +void sequence_game_render_snowman(Snowman *currentSnowman) +{ + rdpq_blitparms_t blitparms = { + .s0 = get_frame_from_snowman(currentSnowman) * SNOWMAN_SPRITE_WIDTH, + .t0 = 0, + .width = SNOWMAN_SPRITE_WIDTH, + .height = SNOWMAN_SPRITE_HEIGHT, + }; + + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(get_sprite_from_snowman(currentSnowman), currentSnowman->x, currentSnowman->y, &blitparms); + + // render_debug_snowman_hit_box(currentSnowman); + // render_debug_snowman_collision_box(currentSnowman); +} + +void sequence_game_render_snowmen_and_ducks() +{ + Snowman *currentSnowman = snowmen; + Duck *currentDuck = ducks; + + while (currentSnowman != NULL) + { + while (currentDuck != NULL && currentDuck->collision_box_y2 < currentSnowman->collision_box_y2) + { + sequence_game_render_duck(currentDuck); + currentDuck = currentDuck->next; + } + + sequence_game_render_snowman(currentSnowman); + currentSnowman = currentSnowman->next; + } + + while (currentDuck != NULL) + { + sequence_game_render_duck(currentDuck); + currentDuck = currentDuck->next; + } +} + +void sequence_game_render_scores() +{ + Duck *currentDuck = ducks; + while (currentDuck != NULL) + { + char utf8_text[10]; + float x, y; + + // TODO: Look at how many characters exist in utf8_text and determine the width accordingly for positioning x-value for player #2 & #4. + + switch (currentDuck->id) + { + case 0: + snprintf(utf8_text, 10, "$02^01%i", (int)roundf(currentDuck->score)); + x = 7; + y = 240; + break; + case 1: + snprintf(utf8_text, 10, "$02^02%i", (int)roundf(currentDuck->score)); + x = 277; + y = 240; + break; + case 2: + snprintf(utf8_text, 10, "$02^03%i", (int)roundf(currentDuck->score)); + x = 7; + y = 30; + break; + case 3: + snprintf(utf8_text, 10, "$02^04%i", (int)roundf(currentDuck->score)); + x = 277; + y = 30; + break; + default: + snprintf(utf8_text, 10, "$02^00%i", (int)roundf(currentDuck->score)); + x = 7; + y = 240; + break; + } + + rdpq_text_print(NULL, FONT_HALODEK_MEDIUM, x, y, utf8_text); + + // WHITE + // rdpq_set_scissor(70 + x + (180.0f * percentage), 0, 320, 240); + // rdpq_text_print(NULL, FONT_HALODEK_BIG, 70 + x, 140 + y, "$03^00PAUSED"); + + currentDuck = currentDuck->next; + } +} +void sequence_game_render_fade_in() +{ + if (time_elapsed < GAME_FADE_IN_DURATION) + { + float percentage = time_elapsed > GAME_FADE_IN_DURATION ? 1.0f : time_elapsed / GAME_FADE_IN_DURATION; + uint8_t alpha = (int)(255.0f * (1.0f - percentage)); + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(0, 0, 0, alpha)); + rdpq_fill_rectangle(0, 0, RESOLUTION_320x240.width, RESOLUTION_320x240.height); + rdpq_mode_pop(); + } +} + +void sequence_game_render_timer() +{ + float time; + if (time_elapsed <= GAME_FADE_IN_DURATION) + { + return; + } + else if (GAME_FADE_IN_DURATION < time_elapsed && time_elapsed <= GAME_FADE_IN_DURATION + 1) + { + time = 3; + } + else if (GAME_FADE_IN_DURATION + 1 < time_elapsed && time_elapsed <= GAME_FADE_IN_DURATION + 2) + { + time = 2; + } + else if (GAME_FADE_IN_DURATION + 2 < time_elapsed && time_elapsed <= GAME_FADE_IN_DURATION + 3) + { + time = 1; + } + else if (GAME_FADE_IN_DURATION + 3 < time_elapsed && time_elapsed <= GAME_FADE_IN_DURATION + 3 + GAME_DURATION) + { + time = GAME_FADE_IN_DURATION + 3 + GAME_DURATION - time_elapsed; + } + else + { + time = 0; + } + char utf8_text[9]; + snprintf(utf8_text, 10, "$02^00%i", (int)roundf(time)); + rdpq_text_print(NULL, FONT_HALODEK_MEDIUM, 140, 30, utf8_text); +} + +void sequence_game_render_results() +{ + if (time_elapsed > GAME_FADE_IN_DURATION + 3 + GAME_DURATION + 2) + { + char *utf8_text = "$02^00DRAW"; + if (winner == 0) + utf8_text = "$02^01P1 WINS"; + else if (winner == 1) + utf8_text = "$02^02P2 WINS"; + else if (winner == 2) + utf8_text = "$02^03P3 WINS"; + else if (winner == 3) + utf8_text = "$02^04P4 WINS"; + + // WINNING TEXT + rdpq_text_print(NULL, FONT_HALODEK_MEDIUM, 100, 140, utf8_text); + } +} + +void sequence_game_render(float deltatime) +{ + rdpq_attach(display_get(), NULL); + rdpq_clear(BLACK); + + sequence_game_render_map(); + sequence_game_render_snowmen_and_ducks(); + sequence_game_render_scores(); + sequence_game_render_press_start_to_pause(); + sequence_game_render_timer(); + sequence_game_render_fade_in(); + sequence_game_render_paused(); + sequence_game_render_results(); + + rdpq_detach_show(); +} \ No newline at end of file diff --git a/code/mallard/game/sequence_game_graphics.h b/code/mallard/game/sequence_game_graphics.h new file mode 100644 index 00000000..ddd8c67c --- /dev/null +++ b/code/mallard/game/sequence_game_graphics.h @@ -0,0 +1,41 @@ +#ifndef SEQUENCE_GAME_GRAPHICS_H +#define SEQUENCE_GAME_GRAPHICS_H + +#include + +#define SEQUENCE_GAME_MALLARD_WALK_FRAMES 8 +#define SEQUENCE_GAME_MALLARD_IDLE_FRAMES 4 +#define SEQUENCE_GAME_MALLARD_SLAP_FRAMES 3 +#define SEQUENCE_GAME_MALLARD_RUN_FRAMES 8 +#define SEQUENCE_GAME_MALLARD_DAMAGE_FRAMES 3 + +#define SEQUENCE_GAME_SNOWMAN_IDLE_FRAMES 4 +#define SEQUENCE_GAME_SNOWMAN_DAMAGE_FRAMES 3 +#define SEQUENCE_GAME_SNOWMAN_JUMP_FRAMES 7 + +extern bool sequence_game_started; +extern bool sequence_game_paused; + +extern sprite_t *sequence_game_mallard_one_walk_sprite; +extern sprite_t *sequence_game_mallard_two_walk_sprite; +extern sprite_t *sequence_game_mallard_three_walk_sprite; +extern sprite_t *sequence_game_mallard_four_walk_sprite; + +extern sprite_t *sequence_game_map; +extern sprite_t *sequence_game_start_button_sprite; +extern sprite_t *sequence_game_paused_text_sprite; + +extern Duck *ducks; +extern Snowman *snowmen; + +extern float time_elapsed; +extern int winner; + +void sequence_game_render_map(); +void sequence_game_render_paused(); +void sequence_game_render(float deltatime); + +int get_frame_from_snowman(Snowman *snowman); +int get_frame_from_duck(Duck *duck); + +#endif // SEQUENCE_GAME_GRAPHICS_H diff --git a/code/mallard/game/sequence_game_initialize.c b/code/mallard/game/sequence_game_initialize.c new file mode 100644 index 00000000..c91c772c --- /dev/null +++ b/code/mallard/game/sequence_game_initialize.c @@ -0,0 +1,34 @@ +#include +#include "../../../core.h" +#include "sequence_game_initialize.h" +#include "sequence_game.h" +#include "sequence_game_input.h" + +int random_between(int min, int max) +{ + return rand() % (max - min + 1) + min; +} + +void initialize_controllers() +{ + if (controllers == NULL) + { + int playercount = core_get_playercount(); + controllers = malloc(playercount * sizeof(struct Controller)); + for (size_t i = 0; i < playercount; i++) + { + controllers[i].start_down = 0; + controllers[i].start_up = 0; + controllers[i].start_held_elapsed = 0.0f; + } + } +} + +void free_controllers() +{ + if (controllers != NULL) + { + free(controllers); + controllers = NULL; + } +} \ No newline at end of file diff --git a/code/mallard/game/sequence_game_initialize.h b/code/mallard/game/sequence_game_initialize.h new file mode 100644 index 00000000..495b022b --- /dev/null +++ b/code/mallard/game/sequence_game_initialize.h @@ -0,0 +1,70 @@ +#ifndef SEQUENCE_GAME_INITIALIZE_H +#define SEQUENCE_GAME_INITIALIZE_H + +#include "sequence_game.h" + +#define DUCK_TIME_SEEKING_TARGET_EASY 0.75f +#define DUCK_TIME_SEEKING_TARGET_MEDIUM 0.50f +#define DUCK_TIME_SEEKING_TARGET_HARD 0.0f + +#define DUCK_MIN_X 5 +#define DUCK_MAX_X 294 +#define DUCK_MIN_Y 26 +#define DUCK_MAX_Y 194 + +#define SNOWMAN_MIN_X 17 +#define SNOWMAN_MAX_X 304 +#define SNOWMAN_MIN_Y 32 +#define SNOWMAN_MAX_Y 205 + +#define PLAYER_1_SPAWN_X1 5 +#define PLAYER_1_SPAWN_Y1 111 +#define PLAYER_1_SPAWN_X2 152 +#define PLAYER_1_SPAWN_Y2 194 +#define PLAYER_2_SPAWN_X1 5 + 152 +#define PLAYER_2_SPAWN_Y1 111 +#define PLAYER_2_SPAWN_X2 152 + 152 - 15 +#define PLAYER_2_SPAWN_Y2 194 +#define PLAYER_3_SPAWN_X1 5 +#define PLAYER_3_SPAWN_Y1 111 - 85 +#define PLAYER_3_SPAWN_X2 152 +#define PLAYER_3_SPAWN_Y2 194 - 85 - 1 +#define PLAYER_4_SPAWN_X1 5 + 152 +#define PLAYER_4_SPAWN_Y1 111 - 85 +#define PLAYER_4_SPAWN_X2 152 + 152 - 15 +#define PLAYER_4_SPAWN_Y2 194 - 85 - 1 + +extern Duck *ducks; +extern struct Snowman *snowmen; +extern struct Controller *controllers; + +extern sprite_t *sequence_game_mallard_one_walk_sprite; +extern sprite_t *sequence_game_mallard_two_walk_sprite; +extern sprite_t *sequence_game_mallard_three_walk_sprite; +extern sprite_t *sequence_game_mallard_four_walk_sprite; + +extern sprite_t *sequence_game_mallard_one_slap_sprite; +extern sprite_t *sequence_game_mallard_two_slap_sprite; +extern sprite_t *sequence_game_mallard_three_slap_sprite; +extern sprite_t *sequence_game_mallard_four_slap_sprite; + +extern sprite_t *sequence_game_mallard_one_run_sprite; +extern sprite_t *sequence_game_mallard_two_run_sprite; +extern sprite_t *sequence_game_mallard_three_run_sprite; +extern sprite_t *sequence_game_mallard_four_run_sprite; + +extern sprite_t *sequence_game_mallard_one_idle_sprite; +extern sprite_t *sequence_game_mallard_two_idle_sprite; +extern sprite_t *sequence_game_mallard_three_idle_sprite; +extern sprite_t *sequence_game_mallard_four_idle_sprite; + +extern sprite_t *sequence_game_mallard_one_damage_sprite; +extern sprite_t *sequence_game_mallard_two_damage_sprite; +extern sprite_t *sequence_game_mallard_three_damage_sprite; +extern sprite_t *sequence_game_mallard_four_damage_sprite; + +void initialize_controllers(); +void free_controllers(); +int random_between(int min, int max); + +#endif // SEQUENCE_GAME_INITIALIZE_H \ No newline at end of file diff --git a/code/mallard/game/sequence_game_input.c b/code/mallard/game/sequence_game_input.c new file mode 100644 index 00000000..3ae1d536 --- /dev/null +++ b/code/mallard/game/sequence_game_input.c @@ -0,0 +1,1163 @@ +#include +#include "../../../core.h" +#include "../../../minigame.h" +#include "sequence_game.h" +#include "sequence_game_input.h" +#include "sequence_game_initialize.h" +#include "sequence_game_graphics.h" +#include "sequence_game_snowman.h" +#include "sequence_game_duck.h" + +#define BOOST 2.0 + +float time_elapsed_since_last_snowman_spawn = 0.0f; +float time_elapsed = 0.0f; +int winner = -1; + +bool countdown_one_played = false; +bool countdown_two_played = false; +bool countdown_three_played = false; +bool start_played = false; +bool stop_played = false; +bool winner_played = false; + +void duck_slap(Duck *duck) +{ + duck->frames_locked_for_slap = 4 * SEQUENCE_GAME_MALLARD_SLAP_FRAMES; // Lock for 12 frames. + duck->action = DUCK_SLAP; + duck->frames = 0; +} + +void snowmen_bubble_sort() +{ + bool swapped = true; + + while (swapped) + { + Snowman **prev = &snowmen; + Snowman *curr; + Snowman *next; + + swapped = false; + for (curr = snowmen; curr; prev = &curr->next, curr = curr->next) + { + next = curr->next; + + if (next && curr->collision_box_y2 > next->collision_box_y2) + { + curr->next = next->next; + next->next = curr; + *prev = next; + + swapped = true; + } + } + } +} + +void remove_snowman(int id) +{ + // Remove snowman from the snowmen linked list + Snowman *currentSnowman = snowmen; + Snowman *previousSnowman = NULL; + while (currentSnowman != NULL) + { + if (currentSnowman->id == id) + { + if (previousSnowman == NULL) + { + snowmen = currentSnowman->next; + } + else + { + previousSnowman->next = currentSnowman->next; + } + + free(currentSnowman); + break; + } + + previousSnowman = currentSnowman; + currentSnowman = currentSnowman->next; + } +} + +void update_snowmen(float deltatime) +{ + if (time_elapsed >= GAME_FADE_IN_DURATION + 3 + GAME_DURATION) + { + return; + } + + if (!sequence_game_paused) + { + float SNOWMAN_SPAWN_FREQUENCY; + if (time_elapsed >= 0.0f && time_elapsed < 15.0f) + SNOWMAN_SPAWN_FREQUENCY = 1.5f; + else if (time_elapsed >= 15.0f && time_elapsed < 30.0f) + SNOWMAN_SPAWN_FREQUENCY = 1.0f; + else if (time_elapsed >= 30.0f && time_elapsed < 45.0f) + SNOWMAN_SPAWN_FREQUENCY = 0.75f; + else + SNOWMAN_SPAWN_FREQUENCY = 0.5f; + + // Update snowmen. + Snowman *currentSnowman = snowmen; + while (currentSnowman != NULL) + { + currentSnowman->frames++; + currentSnowman->time_since_last_hit += deltatime; + + currentSnowman->collision_box_x1 = currentSnowman->x + SNOWMAN_COLLISION_BOX_X1_OFFSET; + currentSnowman->collision_box_y1 = currentSnowman->y + SNOWMAN_COLLISION_BOX_Y1_OFFSET; + currentSnowman->collision_box_x2 = currentSnowman->x + SNOWMAN_COLLISION_BOX_X2_OFFSET; + currentSnowman->collision_box_y2 = currentSnowman->y + SNOWMAN_COLLISION_BOX_Y2_OFFSET; + + currentSnowman->hit_box_x1 = currentSnowman->x + SNOWMAN_HIT_BOX_X1_OFFSET; + currentSnowman->hit_box_y1 = currentSnowman->y + SNOWMAN_HIT_BOX_Y1_OFFSET; + currentSnowman->hit_box_x2 = currentSnowman->x + SNOWMAN_HIT_BOX_X2_OFFSET; + currentSnowman->hit_box_y2 = currentSnowman->y + SNOWMAN_HIT_BOX_Y2_OFFSET; + + if (currentSnowman->frames_locked_for_damage > 0) + { + currentSnowman->frames_locked_for_damage--; + } + + if (currentSnowman->frames_locked_for_damage == 0) + { + currentSnowman->action = SNOWMAN_IDLE; + + if (currentSnowman->health <= 0) + { + Snowman *temporary = currentSnowman; + currentSnowman = currentSnowman->next; + remove_snowman(temporary->id); + continue; + } + } + + currentSnowman = currentSnowman->next; + } + + // Add snowman. + if (time_elapsed_since_last_snowman_spawn >= SNOWMAN_SPAWN_FREQUENCY) + { + add_snowman(); + time_elapsed_since_last_snowman_spawn = 0.0f; + } + + snowmen_bubble_sort(); + + if (time_elapsed > GAME_FADE_IN_DURATION + 3) + { + time_elapsed_since_last_snowman_spawn += deltatime; + } + } +} + +typedef struct ValidMovement +{ + bool x; + bool y; +} ValidMovement; + +bool detect_collision(Rect a, Rect b) +{ + return a.x1 < b.x2 && a.x2 > b.x1 && a.y1 < b.y2 && a.y2 > b.y1; +} + +ValidMovement validate_movement(Duck *duck, Vector2 movement) +{ + ValidMovement validMovement = (ValidMovement){.x = false, .y = false}; + + Rect duckPotentialCollisionBox = (Rect){ + .x1 = duck->collision_box_x1 + movement.x, + .y1 = duck->collision_box_y1 + movement.y, + .x2 = duck->collision_box_x2 + movement.x, + .y2 = duck->collision_box_y2 + movement.y, + }; + Rect duckPotentialCollisionBoxX = (Rect){ + .x1 = duck->collision_box_x1 + movement.x, + .y1 = duck->collision_box_y1, + .x2 = duck->collision_box_x2 + movement.x, + .y2 = duck->collision_box_y2, + }; + Rect duckPotentialCollisionBoxY = (Rect){ + .x1 = duck->collision_box_x1, + .y1 = duck->collision_box_y1 + movement.y, + .x2 = duck->collision_box_x2, + .y2 = duck->collision_box_y2 + movement.y, + }; + + // Check each snowman for collision. + Snowman *currentSnowman = snowmen; + while (currentSnowman != NULL) + { + Rect currentSnowmanCollisionBox = (Rect){.x1 = currentSnowman->collision_box_x1, .y1 = currentSnowman->collision_box_y1, .x2 = currentSnowman->collision_box_x2, .y2 = currentSnowman->collision_box_y2}; + + if (detect_collision(duckPotentialCollisionBox, currentSnowmanCollisionBox)) + { + if (detect_collision(duckPotentialCollisionBoxX, currentSnowmanCollisionBox)) + { + validMovement.x = true; + } + + if (detect_collision(duckPotentialCollisionBoxY, currentSnowmanCollisionBox)) + { + validMovement.y = true; + } + } + + // Stop checking if we've already collided in both directions. + if (validMovement.x && validMovement.y) + { + return validMovement; + } + + currentSnowman = currentSnowman->next; + } + + // Check each duck for collision. + Duck *currentDuck = ducks; + while (currentDuck != NULL) + { + // Skip the duck we're testing. It will always be colliding with itself. + if (duck->id == currentDuck->id) + { + // Next duck. + currentDuck = currentDuck->next; + + continue; + } + + Rect currentDuckCollisionBox = (Rect){.x1 = currentDuck->collision_box_x1, .y1 = currentDuck->collision_box_y1, .x2 = currentDuck->collision_box_x2, .y2 = currentDuck->collision_box_y2}; + + if (detect_collision(duckPotentialCollisionBox, currentDuckCollisionBox)) + { + if (detect_collision(duckPotentialCollisionBoxX, currentDuckCollisionBox)) + { + validMovement.x = true; + } + + if (detect_collision(duckPotentialCollisionBoxY, currentDuckCollisionBox)) + { + validMovement.y = true; + } + } + + // Stop checking if we've already collided in both directions. + if (validMovement.x && validMovement.y) + { + return validMovement; + } + + // Next duck. + currentDuck = currentDuck->next; + } + + return validMovement; +} + +void process_input(Duck *duck, Controller *controller, joypad_buttons_t pressed, joypad_buttons_t held, joypad_buttons_t released, joypad_8way_t direction, size_t i, float deltatime) +{ + if (!sequence_game_paused && pressed.start) + { + controller->start_down = 1; + } + if (!sequence_game_paused && controller->start_down && released.start) + { + controller->start_up = 1; + } + if (!sequence_game_paused && controller->start_down && controller->start_up) + { + sequence_game_paused = true; + + for (size_t j = 0; j < core_get_playercount(); j++) + { + Controller *curr = &controllers[j]; + curr->start_down = 0; + curr->start_up = 0; + curr->start_held_elapsed = 0.0f; + } + } + + if (sequence_game_paused == true && pressed.start) + { + controller->start_down = 1; + } + if (sequence_game_paused && controller->start_down && released.start) + { + controller->start_up = 1; + } + if (sequence_game_paused && controller->start_down && controller->start_up) + { + // Unpauses the game because it wasn't held long enough. + if (sequence_game_start_held_elapsed == 0.0f) + { + sequence_game_paused = false; + } + + // Reset the controller state for all controllers. + for (size_t j = 0; j < core_get_playercount(); j++) + { + Controller *curr = &controllers[j]; + curr->start_down = 0; + curr->start_up = 0; + curr->start_held_elapsed = 0.0f; + } + } + + if (sequence_game_paused) + { + if (held.start) + { + if (sequence_game_player_holding_start == -1) + { + sequence_game_player_holding_start = i; + } + + if (sequence_game_player_holding_start == i) + { + if (sequence_game_start_held_elapsed >= GAME_EXIT_DURATION) + { + sequence_game_should_cleanup = true; + } + if (controller->start_held_elapsed >= GAME_EXIT_THRESHOLD_DURATION) + { + sequence_game_start_held_elapsed += deltatime; + } + controller->start_held_elapsed += deltatime; + } + } + else + { + if (sequence_game_player_holding_start == i) + { + sequence_game_player_holding_start = -1; + sequence_game_start_held_elapsed = 0.0f; + } + } + } + + // If the game is paused, don't process input. + if (!sequence_game_paused) + { + Vector2 movement; + ValidMovement validMovement; + + // Stun Lock. + if (duck->frames_locked_for_damage > 0) + { + duck->frames_locked_for_damage--; + return; + } + + // Movement + switch (direction) + { + case JOYPAD_8WAY_UP: + + // Position + movement = (Vector2){.x = 0, .y = -1}; + if (held.b) + movement = (Vector2){.x = 0, .y = -1 * BOOST}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_UP_RIGHT: + + // Direction + duck->direction = RIGHT; + + // Position + movement = (Vector2){.x = 1, .y = -1}; + if (held.b) + movement = (Vector2){.x = 1 * BOOST, .y = -1 * BOOST}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_RIGHT: + + // Direction + duck->direction = RIGHT; + + // Position + movement = (Vector2){.x = 1, .y = 0}; + if (held.b) + movement = (Vector2){.x = 1 * BOOST, .y = 0}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_DOWN_RIGHT: + + // Direction + duck->direction = RIGHT; + + // Position + movement = (Vector2){.x = 1, .y = 1}; + if (held.b) + movement = (Vector2){.x = 1 * BOOST, .y = 1 * BOOST}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_DOWN: + + // Position + movement = (Vector2){.x = 0, .y = 1}; + if (held.b) + movement = (Vector2){.x = 0, .y = 1 * BOOST}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_DOWN_LEFT: + + // Direction + duck->direction = LEFT; + + // Position + movement = (Vector2){.x = -1, .y = 1}; + if (held.b) + movement = (Vector2){.x = -1 * BOOST, .y = 1 * BOOST}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_LEFT: + + // Direction + duck->direction = LEFT; + + // Position + movement = (Vector2){.x = -1, .y = 0}; + if (held.b) + movement = (Vector2){.x = -1 * BOOST, .y = 0}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + break; + + case JOYPAD_8WAY_UP_LEFT: + + // Direction + duck->direction = LEFT; + + // Position + movement = (Vector2){.x = -1, .y = -1}; + if (held.b) + movement = (Vector2){.x = -1 * BOOST, .y = -1 * BOOST}; + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } + + // Action + if (duck->frames_locked_for_slap == 0) + { + if (held.b) + { + duck->action = DUCK_RUN; + } + else + { + duck->action = DUCK_WALK; + } + } + + break; + + default: + if (duck->frames_locked_for_slap == 0) + duck->action = DUCK_IDLE; + break; + } + + if (duck->x > DUCK_MAX_X) + { + duck->x = DUCK_MAX_X; + } + + if (duck->x < DUCK_MIN_X) + { + duck->x = DUCK_MIN_X; + } + + if (duck->y > DUCK_MAX_Y) + { + duck->y = DUCK_MAX_Y; + } + + if (duck->y < DUCK_MIN_Y) + { + duck->y = DUCK_MIN_Y; + } + + if (pressed.a) + { + duck_slap(duck); + } + + if (duck->frames_locked_for_slap > 0) + { + duck->frames_locked_for_slap--; + } + } +} + +Snowman *find_nearest_snowman(Duck *duck) +{ + Snowman *nearestSnowman = NULL; + float nearestDistance = 999999.0f; + + float duck_x = (duck->slap_box_x1 + duck->slap_box_x2) / 2; + float duck_y = (duck->slap_box_y1 + duck->slap_box_y2) / 2; + + Snowman *currentSnowman = snowmen; + while (currentSnowman != NULL) + { + float snowman_x = (currentSnowman->hit_box_x1 + currentSnowman->hit_box_x2) / 2; + float snowman_y = (currentSnowman->hit_box_y1 + currentSnowman->hit_box_y2) / 2; + float distance = fmax(abs(duck_x - snowman_x), abs(duck_y - snowman_y)); + if (distance < nearestDistance) + { + nearestDistance = distance; + nearestSnowman = currentSnowman; + } + + currentSnowman = currentSnowman->next; + } + + return nearestSnowman; +} + +void set_duck_direction(Duck *duck, Snowman *snowman) +{ + if (snowman == NULL) + { + return; + } + + float snowman_x = (snowman->hit_box_x1 + snowman->hit_box_x2) / 2; + float duck_x = (duck->slap_box_x1 + duck->slap_box_x2) / 2; + + if (duck_x <= snowman_x) + { + duck->direction = RIGHT; + } + else + { + duck->direction = LEFT; + } +} +void set_duck_movement(Duck *duck, Snowman *snowman) +{ + if (snowman == NULL) + { + return; + } + + Vector2 movement; + ValidMovement validMovement; + float snowman_x = (snowman->hit_box_x1 + snowman->hit_box_x2) / 2; + float snowman_y = (snowman->hit_box_y1 + snowman->hit_box_y2) / 2; + float duck_x = (duck->slap_box_x1 + duck->slap_box_x2) / 2; + float duck_y = (duck->slap_box_y1 + duck->slap_box_y2) / 2; + float dx = snowman_x - duck_x; + float dy = snowman_y - duck_y; + + float speed; + switch (difficulty) + { + case DIFF_EASY: + speed = 0.75; + break; + case DIFF_MEDIUM: + speed = 1.25; + break; + case DIFF_HARD: + speed = 1.5; + break; + default: + speed = 1.0; + break; + } + + if (dx > 1 && dy > 1) + { + movement = (Vector2){.x = 1 * speed, .y = 1 * speed}; + } + else if (dx > 1 && dy < -1) + { + movement = (Vector2){.x = 1 * speed, .y = -1 * speed}; + } + else if (dx < -1 && dy > 1) + { + movement = (Vector2){.x = -1 * speed, .y = 1 * speed}; + } + else if (dx < -1 && dy < -1) + { + movement = (Vector2){.x = -1 * speed, .y = -1 * speed}; + } + else if (dx > 1) + { + movement = (Vector2){.x = 1 * speed, .y = 0}; + } + else if (dx < -1) + { + movement = (Vector2){.x = -1 * speed, .y = 0}; + } + else if (dy > 1) + { + movement = (Vector2){.x = 0, .y = 1 * speed}; + } + else if (dy < -1) + { + movement = (Vector2){.x = 0, .y = -1 * speed}; + } + + validMovement = validate_movement(duck, movement); + + if (!validMovement.x) + { + duck->x += movement.x; + } + + if (!validMovement.y) + { + duck->y += movement.y; + } +} + +void set_duck_action(Duck *duck, Snowman *snowman) +{ + if (duck->frames_locked_for_slap != 0) + { + duck->action = DUCK_SLAP; + return; + } + + if (snowman == NULL) + { + duck->action = DUCK_IDLE; + return; + } + + Rect duckSlapBox = (Rect){.x1 = duck->slap_box_x1, .y1 = duck->slap_box_y1, .x2 = duck->slap_box_x2, .y2 = duck->slap_box_y2}; + Rect snowmanHitBox = (Rect){.x1 = snowman->hit_box_x1, .y1 = snowman->hit_box_y1, .x2 = snowman->hit_box_x2, .y2 = snowman->hit_box_y2}; + + if (detect_collision(duckSlapBox, snowmanHitBox)) + { + duck_slap(duck); + } + else + { + duck->action = DUCK_WALK; + } +} + +void simulate_input(Duck *duck, float deltatime) +{ + if (!sequence_game_paused) + { + if (duck->frames_locked_for_damage > 0) + { + duck->frames_locked_for_damage--; + return; + } + + float seeking_delay; + switch (difficulty) + { + case DIFF_EASY: + seeking_delay = DUCK_TIME_SEEKING_TARGET_EASY; + break; + case DIFF_MEDIUM: + seeking_delay = DUCK_TIME_SEEKING_TARGET_MEDIUM; + break; + case DIFF_HARD: + seeking_delay = DUCK_TIME_SEEKING_TARGET_HARD; + break; + default: + seeking_delay = DUCK_TIME_SEEKING_TARGET_MEDIUM; + break; + } + + if (duck->time_seeking_target < seeking_delay) + { + return; + } + + Snowman *nearestSnowman = find_nearest_snowman(duck); + + set_duck_direction(duck, nearestSnowman); + + set_duck_movement(duck, nearestSnowman); + + set_duck_action(duck, nearestSnowman); + + if (duck->x > DUCK_MAX_X) + { + duck->x = DUCK_MAX_X; + } + + if (duck->x < DUCK_MIN_X) + { + duck->x = DUCK_MIN_X; + } + + if (duck->y > DUCK_MAX_Y) + { + duck->y = DUCK_MAX_Y; + } + + if (duck->y < DUCK_MIN_Y) + { + duck->y = DUCK_MIN_Y; + } + + if (duck->frames_locked_for_slap > 0) + { + duck->frames_locked_for_slap--; + } + } +} + +void evaluate_attack() +{ + if (time_elapsed >= GAME_FADE_IN_DURATION + 3 + GAME_DURATION) + { + return; + } + + if (!sequence_game_paused) + { + Duck *currentDuck = ducks; + + while (currentDuck != NULL) + { + if (currentDuck->action == DUCK_SLAP) + { + Rect currentDuckSlapBox = (Rect){.x1 = currentDuck->slap_box_x1, .y1 = currentDuck->slap_box_y1, .x2 = currentDuck->slap_box_x2, .y2 = currentDuck->slap_box_y2}; + + Snowman *currentSnowman = snowmen; + while (currentSnowman != NULL) + { + Rect currentSnowmanHitBox = (Rect){.x1 = currentSnowman->hit_box_x1, .y1 = currentSnowman->hit_box_y1, .x2 = currentSnowman->hit_box_x2, .y2 = currentSnowman->hit_box_y2}; + + if (detect_collision(currentDuckSlapBox, currentSnowmanHitBox)) + { + if (currentSnowman->time_since_last_hit > SNOWMAN_TIME_BETWEEN_DAMAGE) + { + // Set action to damage. + currentSnowman->action = SNOWMAN_DAMAGE; + currentSnowman->frames = 0; + currentSnowman->frames_locked_for_damage = 4 * SEQUENCE_GAME_SNOWMAN_DAMAGE_FRAMES; + + // Reset time since last hit. + currentSnowman->time_since_last_hit = 0.0f; + + // TODO: Sound Effect. + + // Damage Snowman. If health is 0, remove snowman and reward duck. + currentSnowman->health -= 1; + + // Evaluate if snowman is dead. + if (currentSnowman->health <= 0) + { + // Reward Duck + currentDuck->score += 1; + currentDuck->time_seeking_target = 0.0f; + } + + // Next Snowman. + currentSnowman = currentSnowman->next; + } + else + { + // Next Snowman. + currentSnowman = currentSnowman->next; + } + } + else + { + // Next Snowman. + currentSnowman = currentSnowman->next; + } + } + + Duck *temporaryDuck = ducks; + while (temporaryDuck != NULL) + { + if (currentDuck->id == temporaryDuck->id) + { + temporaryDuck = temporaryDuck->next; + continue; + } + + Rect temporaryDuckHitBox = (Rect){.x1 = temporaryDuck->hit_box_x1, .y1 = temporaryDuck->hit_box_y1, .x2 = temporaryDuck->hit_box_x2, .y2 = temporaryDuck->hit_box_y2}; + + if (detect_collision(currentDuckSlapBox, temporaryDuckHitBox)) + { + // TODO: Sound Effect. + + if (temporaryDuck->time_since_last_hit > DUCK_TIME_BETWEEN_DAMAGE) + { + // Set action to damage. + temporaryDuck->action = DUCK_DAMAGE; + temporaryDuck->frames = 0; + temporaryDuck->frames_locked_for_damage = 4 * SEQUENCE_GAME_MALLARD_DAMAGE_FRAMES; + + // Reset time since last hit. + temporaryDuck->time_since_last_hit = 0.0f; + + // Reward Duck + currentDuck->score += 0.1f; + + // Damage Duck + temporaryDuck->score -= 0.1f; + if (temporaryDuck->score < 0.0f) + { + temporaryDuck->score = 0.0f; + } + } + } + temporaryDuck = temporaryDuck->next; + } + } + currentDuck = currentDuck->next; + } + } +} + +void play_sounds() +{ + if (!sequence_game_paused) + { + if (GAME_FADE_IN_DURATION < time_elapsed && countdown_one_played == false) + { + if (countdown_one_played == false) + { + wav64_play(&sfx_countdown, 31); + countdown_one_played = true; + } + return; + } + + if (GAME_FADE_IN_DURATION + 1 < time_elapsed && countdown_two_played == false) + { + if (countdown_two_played == false) + { + wav64_play(&sfx_countdown, 31); + countdown_two_played = true; + } + return; + } + + if (GAME_FADE_IN_DURATION + 2 < time_elapsed && countdown_three_played == false) + { + if (countdown_three_played == false) + { + wav64_play(&sfx_countdown, 31); + countdown_three_played = true; + } + return; + } + + if (GAME_FADE_IN_DURATION + 3 < time_elapsed && start_played == false) + { + if (start_played == false) + { + wav64_play(&sfx_start, 31); + start_played = true; + } + return; + } + } +} + +void update_time(float deltatime) +{ + if (!sequence_game_paused) + { + time_elapsed += deltatime; + } +} + +void process_human_players(float deltatime) +{ + if (time_elapsed >= GAME_FADE_IN_DURATION + 3 + GAME_DURATION) + { + return; + } + + for (size_t i = 0; i < core_get_playercount(); i++) + { + Duck *duck = get_duck_by_id(i); + Controller *controller = &controllers[i]; + + joypad_port_t controllerPort = core_get_playercontroller(i); + if (!joypad_is_connected(controllerPort)) + continue; + + joypad_buttons_t pressed = joypad_get_buttons_pressed(controllerPort); + joypad_buttons_t held = joypad_get_buttons_held(controllerPort); + joypad_buttons_t released = joypad_get_buttons_released(controllerPort); + joypad_8way_t direction = joypad_get_direction(controllerPort, JOYPAD_2D_ANY); + + process_input(duck, controller, pressed, held, released, direction, i, deltatime); + } +} + +void process_computer_players(float deltatime) +{ + if (time_elapsed >= GAME_FADE_IN_DURATION + 3 + GAME_DURATION) + { + return; + } + + for (size_t i = core_get_playercount(); i < 4; i++) + { + Duck *duck = get_duck_by_id(i); + simulate_input(duck, deltatime); + } +} + +void update_winner() +{ + if (time_elapsed > GAME_FADE_IN_DURATION + 3 + GAME_DURATION && winner == -1 && stop_played == false) + { + Duck *currentDuck = ducks; + Duck *highestDuck = currentDuck; + bool draw = false; + fprintf(stderr, "Evaluating winner\n"); + while (currentDuck != NULL) + { + if (currentDuck->id == highestDuck->id) + { + currentDuck = currentDuck->next; + continue; + } + + if ((int)roundf(currentDuck->score) == (int)roundf(highestDuck->score)) + { + draw = true; + } + + if ((int)roundf(currentDuck->score) > (int)roundf(highestDuck->score)) + { + highestDuck = currentDuck; + draw = false; + } + + currentDuck = currentDuck->next; + } + + if (!draw) + { + winner = highestDuck->id; + } + + wav64_play(&sfx_stop, 31); + stop_played = true; + } + + if (time_elapsed > GAME_FADE_IN_DURATION + 3 + GAME_DURATION + 1) + { + xm64player_stop(&sequence_game_xm); + } + + if (time_elapsed > GAME_FADE_IN_DURATION + 3 + GAME_DURATION + 2 && winner_played == false) + { + wav64_play(&sfx_winner, 31); + winner_played = true; + } + + if (time_elapsed > GAME_FADE_IN_DURATION + 3 + GAME_DURATION + 5) + { + sequence_game_should_cleanup = true; + } +} + +void sequence_game_update(float deltatime) +{ + process_human_players(deltatime); + process_computer_players(deltatime); + update_time(deltatime); + update_winner(); + update_ducks(deltatime); + update_snowmen(deltatime); + play_sounds(); + evaluate_attack(); +} \ No newline at end of file diff --git a/code/mallard/game/sequence_game_input.h b/code/mallard/game/sequence_game_input.h new file mode 100644 index 00000000..4a60b9bf --- /dev/null +++ b/code/mallard/game/sequence_game_input.h @@ -0,0 +1,38 @@ +#ifndef SEQUENCE_GAME_INPUT_H +#define SEQUENCE_GAME_INPUT_H +#include "../../../core.h" + +extern Snowman *snowmen; +extern Duck *ducks; + +extern bool sequence_game_should_cleanup; +extern bool sequence_game_paused; +extern int sequence_game_player_holding_start; +extern float sequence_game_start_held_elapsed; + +extern AiDiff difficulty; +extern xm64player_t sequence_game_xm; +extern wav64_t sfx_start; +extern wav64_t sfx_countdown; +extern wav64_t sfx_stop; +extern wav64_t sfx_winner; + +typedef struct Vector2 +{ + float x; + float y; +} Vector2; + +typedef struct Rect +{ + float x1; + float y1; + float x2; + float y2; +} Rect; + +bool detect_collision(Rect a, Rect b); + +void sequence_game_update(float deltatime); + +#endif // SEQUENCE_GAME_INPUT_H diff --git a/code/mallard/game/sequence_game_snowman.c b/code/mallard/game/sequence_game_snowman.c new file mode 100644 index 00000000..9e3acc90 --- /dev/null +++ b/code/mallard/game/sequence_game_snowman.c @@ -0,0 +1,135 @@ +#include +#include "sequence_game_initialize.h" +#include "sequence_game_snowman.h" +#include "sequence_game_input.h" + +#define MAX_SNOWMEN 100 +int snowman_uuid = 0; + +int count_snowmen() +{ + int count = 0; + Snowman *current = snowmen; + while (current != NULL) + { + count++; + current = current->next; + } + return count; +} + +void display_snowmen() +{ + Snowman *current = snowmen; + while (current != NULL) + { + fprintf(stderr, "[Snowman #%i - %f], ", current->id, current->collision_box_y2); + current = current->next; + } + fprintf(stderr, "\n"); +} + +Vector2 get_snowman_spawn() +{ + int _x, _y; + float _x1, _y1, _x2, _y2; + bool _validSpawn; + Duck *currentDuck; + while (true) + { + _validSpawn = true; + _x = random_between(SNOWMAN_MIN_X, SNOWMAN_MAX_X); + _y = random_between(SNOWMAN_MIN_Y, SNOWMAN_MAX_Y); + _x1 = _x; + _y1 = _y + 8; + _x2 = _x + 12; + _y2 = _y + 16; + + currentDuck = ducks; + while (currentDuck != NULL) + { + if (detect_collision( + (Rect){.x1 = currentDuck->collision_box_x1, .y1 = currentDuck->collision_box_y1, .x2 = currentDuck->collision_box_x2, .y2 = currentDuck->collision_box_y2}, + (Rect){.x1 = _x1, .y1 = _y1, .x2 = _x2, .y2 = _y2})) + { + _validSpawn = false; + break; + } + + currentDuck = currentDuck->next; + } + + if (_validSpawn) + { + return (Vector2){.x = _x, .y = _y}; + } + } +} + +Snowman *create_snowman() +{ + Snowman *snowman = (Snowman *)malloc(sizeof(Snowman)); + Vector2 spawn = get_snowman_spawn(); + snowman->id = snowman_uuid; + snowman->x = spawn.x; + snowman->y = spawn.y; + snowman->time_since_last_hit = 0.0f; + snowman->health = 2; + snowman->action = SNOWMAN_IDLE; + snowman->idle_sprite = sequence_game_snowman_idle_sprite; + snowman->damage_sprite = sequence_game_snowman_damage_sprite; + snowman->jump_sprite = sequence_game_snowman_jump_sprite; + snowman->frames = 0; + snowman->frames_locked_for_damage = 0; + snowman->collision_box_x1 = spawn.x + SNOWMAN_COLLISION_BOX_X1_OFFSET; + snowman->collision_box_y1 = spawn.y + SNOWMAN_COLLISION_BOX_Y1_OFFSET; + snowman->collision_box_x2 = spawn.x + SNOWMAN_COLLISION_BOX_X2_OFFSET; + snowman->collision_box_y2 = spawn.y + SNOWMAN_COLLISION_BOX_Y2_OFFSET; + snowman->hit_box_x1 = spawn.x + SNOWMAN_HIT_BOX_X1_OFFSET; + snowman->hit_box_y1 = spawn.y + SNOWMAN_HIT_BOX_Y1_OFFSET; + snowman->hit_box_x2 = spawn.x + SNOWMAN_HIT_BOX_X2_OFFSET; + snowman->hit_box_y2 = spawn.y + SNOWMAN_HIT_BOX_Y2_OFFSET; + snowman_uuid++; + return snowman; +} + +void add_snowman() +{ + if (count_snowmen() >= MAX_SNOWMEN) + { + return; + } + + Snowman *snowman = create_snowman(); + + // Insert at the head if the list is empty. + // Insert at the head if the new value is smaller. + if (snowmen == NULL || snowmen->y >= snowman->y) + { + snowman->next = snowmen; + snowmen = snowman; + return; + } + + // Otherwise, Traverse the snowmen to find the correct insertion point. + Snowman *current = snowmen; + while (current->next != NULL && current->next->y < snowman->y) + { + current = current->next; + } + + // Insert the new snowman + snowman->next = current->next; + current->next = snowman; +} + +void free_snowmen() +{ + Snowman *temporary; + while (snowmen != NULL) + { + temporary = snowmen; + snowmen = snowmen->next; + free(temporary); + } +} \ No newline at end of file diff --git a/code/mallard/game/sequence_game_snowman.h b/code/mallard/game/sequence_game_snowman.h new file mode 100644 index 00000000..061246fb --- /dev/null +++ b/code/mallard/game/sequence_game_snowman.h @@ -0,0 +1,30 @@ +#ifndef SEQUENCE_GAME_SNOWMAN_H +#define SEQUENCE_GAME_SNOWMAN_H + +#define SNOWMAN_TIME_BETWEEN_DAMAGE 0.5f + +#define SNOWMAN_SPRITE_WIDTH 15 +#define SNOWMAN_SPRITE_HEIGHT 17 + +#define SNOWMAN_COLLISION_BOX_X1_OFFSET 0 +#define SNOWMAN_COLLISION_BOX_Y1_OFFSET 10 +#define SNOWMAN_COLLISION_BOX_X2_OFFSET 12 +#define SNOWMAN_COLLISION_BOX_Y2_OFFSET 16 + +#define SNOWMAN_HIT_BOX_X1_OFFSET 2 +#define SNOWMAN_HIT_BOX_Y1_OFFSET 3 +#define SNOWMAN_HIT_BOX_X2_OFFSET 10 +#define SNOWMAN_HIT_BOX_Y2_OFFSET 12 + +extern sprite_t *sequence_game_snowman_idle_sprite; +extern sprite_t *sequence_game_snowman_damage_sprite; +extern sprite_t *sequence_game_snowman_jump_sprite; + +extern Snowman *snowmen; +extern Duck *ducks; + +void add_snowman(); +void free_snowmen(); +void display_snowmen(); + +#endif // SEQUENCE_GAME_SNOWMAN_H \ No newline at end of file diff --git a/code/mallard/introduction/sequence_introduction.c b/code/mallard/introduction/sequence_introduction.c new file mode 100644 index 00000000..09738e34 --- /dev/null +++ b/code/mallard/introduction/sequence_introduction.c @@ -0,0 +1,159 @@ +#include +#include "../mallard.h" +#include "sequence_introduction.h" +#include "sequence_introduction_input.h" +#include "sequence_introduction_graphics.h" +#include "../../../minigame.h" + +/////////////////////////////////////////////////////////// +// Globals // +/////////////////////////////////////////////////////////// +sprite_t *sequence_introduction_mallard_libdragon_sprite; +sprite_t *sequence_introduction_mallard_logo_black_sprite; +sprite_t *sequence_introduction_mallard_logo_white_sprite; + +sprite_t *sequence_introduction_a_button_sprite; +sprite_t *sequence_introduction_start_button_sprite; + +xm64player_t sequence_introduction_xm; +int sequence_introduction_currentXMPattern = 0; + +int sequence_introduction_frame = 0; +bool sequence_introduction_should_initialize = true; +bool sequence_introduction_did_initialize = false; +bool sequence_introduction_should_cleanup = false; +bool sequence_introduction_did_cleanup = false; + +// Libdragon Logo +bool sequence_introduction_libdragon_logo_started = false; +bool sequence_introduction_libdragon_logo_finished = false; +float sequence_introduction_libdragon_logo_elapsed = 0.0f; + +// Mallard Logo +bool sequence_introduction_mallard_logo_started = false; +bool sequence_introduction_mallard_logo_finished = false; +float sequence_introduction_mallard_logo_elapsed = 0.0f; + +// Paragraphs +bool sequence_introduction_paragraphs_started = false; +bool sequence_introduction_paragraphs_finished = false; +int sequence_introduction_current_paragraph = 0; +int sequence_introduction_current_paragraph_speed = 4; +bool sequence_introduction_current_paragraph_finished = false; +char *sequence_introduction_current_paragraph_string; +int sequence_introduction_current_paragraph_drawn_characters = 0; + +bool sequence_introduction_paragraph_fade_out_started = false; +float sequence_introduction_paragraph_fade_out_elapsed = 0.0f; +bool sequence_introduction_paragraph_fade_out_finished = false; + +void sequence_introduction_init() +{ + /////////////////////////////////////////////////////////// + // Set up Display // + /////////////////////////////////////////////////////////// + + display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, FILTERS_RESAMPLE); + + /////////////////////////////////////////////////////////// + // Set up Sprites // + /////////////////////////////////////////////////////////// + + // Libdragon + sequence_introduction_mallard_libdragon_sprite = sprite_load("rom:/mallard/libdragon.rgba32.sprite"); + + // Mallard Logo + sequence_introduction_mallard_logo_black_sprite = sprite_load("rom:/mallard/mallard_logo_black.rgba32.sprite"); + sequence_introduction_mallard_logo_white_sprite = sprite_load("rom:/mallard/mallard_logo_white.rgba32.sprite"); + + // Intro UI + sequence_introduction_start_button_sprite = sprite_load("rom:/core/StartButton.sprite"); + sequence_introduction_a_button_sprite = sprite_load("rom:/core/AButton.sprite"); + + /////////////////////////////////////////////////////////// + // Set up Audio // + /////////////////////////////////////////////////////////// + + xm64player_open(&sequence_introduction_xm, "rom:/mallard/mallard_intro_music.xm64"); + xm64player_play(&sequence_introduction_xm, 0); + xm64player_seek(&sequence_introduction_xm, sequence_introduction_currentXMPattern, 0, 0); + + sequence_introduction_should_initialize = false; + sequence_introduction_did_initialize = true; + + sequence_introduction_libdragon_logo_started = true; +} + +void sequence_introduction_cleanup() +{ + // Free the sprites. + + // Libdragon + sprite_free(sequence_introduction_mallard_libdragon_sprite); + + // Mallard Logo + sprite_free(sequence_introduction_mallard_logo_black_sprite); + sprite_free(sequence_introduction_mallard_logo_white_sprite); + + // Intro UI + sprite_free(sequence_introduction_start_button_sprite); + sprite_free(sequence_introduction_a_button_sprite); + + // Stop the music and free the allocated memory. + xm64player_stop(&sequence_introduction_xm); + xm64player_close(&sequence_introduction_xm); + + // Close the display and free the allocated memory. + rspq_wait(); + display_close(); + + // End the sequence. + sequence_introduction_finished = true; + + // Start the next sequence. + sequence_game_started = true; +} + +void sequence_introduction(float deltatime) +{ + sequence_introduction_process_controller(deltatime); + + if (sequence_introduction_should_initialize && !sequence_introduction_did_initialize) + { + sequence_introduction_init(); + } + + if (sequence_introduction_should_cleanup && !sequence_introduction_did_cleanup) + { + sequence_introduction_cleanup(); + return; + } + + rdpq_attach(display_get(), NULL); + rdpq_clear(BLACK); + + /////////////////////////////////////////////////////////// + // Intro Sequence // + /////////////////////////////////////////////////////////// + sequence_introduction_draw_libdragon_logo(deltatime); + sequence_introduction_draw_mallard_logo(deltatime); + sequence_introduction_draw_press_a_for_next(); + sequence_introduction_draw_press_start_to_skip(); + sequence_introduction_draw_paragraph(deltatime); + + rdpq_detach_show(); + + /////////////////////////////////////////////////////////// + // Handle Audio // + /////////////////////////////////////////////////////////// + + int patidx, row; + + xm64player_tell(&sequence_introduction_xm, &patidx, &row, NULL); + + // If the pattern index is greater than the currently allowed pattern, loop back to the start of the currently allowed pattern. + if (patidx > sequence_introduction_currentXMPattern) + xm64player_seek(&sequence_introduction_xm, sequence_introduction_currentXMPattern, 0, 0); + + sequence_introduction_frame++; +} \ No newline at end of file diff --git a/code/mallard/introduction/sequence_introduction.h b/code/mallard/introduction/sequence_introduction.h new file mode 100644 index 00000000..11e761a2 --- /dev/null +++ b/code/mallard/introduction/sequence_introduction.h @@ -0,0 +1,23 @@ +#ifndef SEQUENCE_INTRODUCTION_H +#define SEQUENCE_INTRODUCTION_H + +#include + +#define DRAW_LIBDRAGON_LOGO_DURATION 2.0f +#define DRAW_MALLARD_LOGO_FADE_IN_DURATION 1.0f +#define DRAW_MALLARD_LOGO_DURATION 1.0f +#define DRAW_MALLARD_LOGO_FADE_OUT_DURATION 1.0f +#define PARAGRAPH_FADE_OUT_DURATION 1.0f + +#define DEFAULT_PARAGRAPH_SPEED 4 + +#define SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_01 "$04^00Hark, ye gamer folk, to the tale of Mallard. That noble band who didst rise like the morning sun to lay waste upon the Winter..." +#define SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_02 "$04^00With spirit ablaze, they took siege, keen as the falcon and fierce as the storm..." +#define SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_03 "$04^00Leaving banners fallen and pride humbled, this is the tale of that very day..." + +extern bool sequence_introduction_finished; +extern bool sequence_game_started; + +void sequence_introduction(float deltatime); + +#endif // SEQUENCE_INTRODUCTION_H diff --git a/code/mallard/introduction/sequence_introduction_graphics.c b/code/mallard/introduction/sequence_introduction_graphics.c new file mode 100644 index 00000000..0e513c32 --- /dev/null +++ b/code/mallard/introduction/sequence_introduction_graphics.c @@ -0,0 +1,199 @@ +#include +#include "../mallard.h" +#include "sequence_introduction.h" +#include "sequence_introduction_graphics.h" +#include "../../../core.h" +#include "../../../minigame.h" + +void sequence_introduction_draw_press_start_to_skip() +{ + if ((sequence_introduction_libdragon_logo_started == true && sequence_introduction_libdragon_logo_finished == false) || + (sequence_introduction_mallard_logo_started == true && sequence_introduction_mallard_logo_finished == false) || + (sequence_introduction_paragraphs_started == true && sequence_introduction_paragraphs_finished == false)) + { + // Draw "Start" button + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(sequence_introduction_start_button_sprite, + RESOLUTION_320x240.width - sequence_introduction_start_button_sprite->width - 32, + RESOLUTION_320x240.height - sequence_introduction_start_button_sprite->height - 1, + NULL); + + // Draw "Skip" text + float x = RESOLUTION_320x240.width - 30; + float y = RESOLUTION_320x240.height - 5; + rdpq_text_print(NULL, FONT_HALODEK, x, y, "$01^00Skip"); + rdpq_mode_pop(); + } +} + +void sequence_introduction_draw_press_a_for_next() +{ + if ((sequence_introduction_libdragon_logo_started == true && sequence_introduction_libdragon_logo_finished == false) || + (sequence_introduction_mallard_logo_started == true && sequence_introduction_mallard_logo_finished == false) || + (sequence_introduction_paragraphs_started == true && sequence_introduction_paragraphs_finished == false)) + { + // Draw "A" button + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(sequence_introduction_a_button_sprite, + RESOLUTION_320x240.width - sequence_introduction_a_button_sprite->width - 32, + RESOLUTION_320x240.height - sequence_introduction_a_button_sprite->height - sequence_introduction_start_button_sprite->height - 2, + NULL); + + // Draw "Next" text + float x = RESOLUTION_320x240.width - 30; + float y = RESOLUTION_320x240.height - sequence_introduction_start_button_sprite->height - 6; + rdpq_text_print(NULL, FONT_HALODEK, x, y, "$01^00Next"); + rdpq_mode_pop(); + } +} + +void sequence_introduction_draw_mallard_logo(float deltatime) +{ + if (sequence_introduction_mallard_logo_started == true && sequence_introduction_mallard_logo_finished == false) + { + float scale = 0.75f + 0.10 * sequence_introduction_mallard_logo_elapsed / (DRAW_MALLARD_LOGO_FADE_IN_DURATION + DRAW_MALLARD_LOGO_DURATION + DRAW_MALLARD_LOGO_FADE_OUT_DURATION); + rdpq_blitparms_t blitparms = { + .scale_x = scale, + .scale_y = scale, + }; + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_alphacompare(1); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY_CONST); + if (sequence_introduction_mallard_logo_elapsed < DRAW_MALLARD_LOGO_FADE_IN_DURATION) + rdpq_set_fog_color(RGBA32(0, 0, 0, (int)((sequence_introduction_mallard_logo_elapsed / DRAW_MALLARD_LOGO_FADE_IN_DURATION) * 255.0f))); + if (sequence_introduction_mallard_logo_elapsed >= DRAW_MALLARD_LOGO_FADE_IN_DURATION && sequence_introduction_mallard_logo_elapsed < DRAW_MALLARD_LOGO_FADE_IN_DURATION + DRAW_MALLARD_LOGO_DURATION) + rdpq_set_fog_color(RGBA32(0, 0, 0, 255)); + if (sequence_introduction_mallard_logo_elapsed >= (DRAW_MALLARD_LOGO_FADE_IN_DURATION + DRAW_MALLARD_LOGO_DURATION)) + rdpq_set_fog_color(RGBA32(0, 0, 0, (int)((1.0f - ((sequence_introduction_mallard_logo_elapsed - DRAW_MALLARD_LOGO_FADE_IN_DURATION - DRAW_MALLARD_LOGO_DURATION) / DRAW_MALLARD_LOGO_FADE_OUT_DURATION)) * 255.0f))); + rdpq_sprite_blit(sequence_introduction_mallard_logo_white_sprite, + RESOLUTION_320x240.width / 2 - sequence_introduction_mallard_logo_white_sprite->width * scale / 2, + RESOLUTION_320x240.height / 2 - sequence_introduction_mallard_logo_white_sprite->height * scale / 2, + &blitparms); + rdpq_mode_pop(); + + sequence_introduction_mallard_logo_elapsed += deltatime; + + if (sequence_introduction_mallard_logo_elapsed > DRAW_MALLARD_LOGO_FADE_IN_DURATION + DRAW_MALLARD_LOGO_DURATION + DRAW_MALLARD_LOGO_FADE_OUT_DURATION) + { + sequence_introduction_mallard_logo_finished = true; + sequence_introduction_paragraphs_started = true; + } + } +} + +void sequence_introduction_draw_libdragon_logo(float deltatime) +{ + if (sequence_introduction_libdragon_logo_started == true && sequence_introduction_libdragon_logo_finished == false) + { + // Draw "Made with Libdragon" text + float x = RESOLUTION_320x240.width / 2 - 37; + float y = RESOLUTION_320x240.height / 2 - 72; + rdpq_set_mode_standard(); + rdpq_text_print(&(rdpq_textparms_t){.char_spacing = 2}, FONT_HALODEK, x, y, "$01^00Made with"); + + // Draw the Libdragon logo + rdpq_mode_push(); + rdpq_set_mode_standard(); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_sprite_blit(sequence_introduction_mallard_libdragon_sprite, RESOLUTION_320x240.width / 2 - sequence_introduction_mallard_libdragon_sprite->width / 2, RESOLUTION_320x240.height / 2 - sequence_introduction_mallard_libdragon_sprite->height / 2, NULL); + rdpq_mode_pop(); + + sequence_introduction_libdragon_logo_elapsed += deltatime; + + if (sequence_introduction_libdragon_logo_elapsed > DRAW_LIBDRAGON_LOGO_DURATION) + { + sequence_introduction_libdragon_logo_finished = true; + sequence_introduction_mallard_logo_started = true; + } + } +} + +void sequence_introduction_draw_paragraph(float deltatime) +{ + if (sequence_introduction_paragraphs_started == true && sequence_introduction_paragraphs_finished == false) + { + // BYPASS THE PARAGRAPHS + sequence_introduction_paragraphs_finished = true; + sequence_introduction_should_cleanup = true; + return; + + int total_chars = 0; + switch (sequence_introduction_current_paragraph) + { + case 0: + sequence_introduction_current_paragraph_string = SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_01; + total_chars = strlen(SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_01); + break; + case 1: + sequence_introduction_current_paragraph_string = SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_02; + total_chars = strlen(SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_02); + break; + case 2: + sequence_introduction_current_paragraph_string = SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_03; + total_chars = strlen(SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_03); + break; + default: + sequence_introduction_current_paragraph_string = SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_03; + total_chars = strlen(SEQEUENCE_INTRODUCTION_GAMEJAM_PARARGAPH_03); + sequence_introduction_paragraph_fade_out_started = true; + break; + } + + if (sequence_introduction_paragraph_fade_out_started == false) + { + if ((sequence_introduction_frame % sequence_introduction_current_paragraph_speed) == 0) + { + sequence_introduction_current_paragraph_drawn_characters++; + if (sequence_introduction_current_paragraph_drawn_characters > total_chars) + { + sequence_introduction_current_paragraph_drawn_characters = total_chars; + sequence_introduction_current_paragraph_finished = true; + } + } + } + else + { + sequence_introduction_current_paragraph_drawn_characters = total_chars; + } + + int x_margin = 5; + int y_margin = 0; + rdpq_textparms_t params = { + .align = ALIGN_CENTER, + .valign = VALIGN_CENTER, + .width = RESOLUTION_320x240.width - (2 * x_margin), + .height = RESOLUTION_320x240.height - (2 * y_margin), + .wrap = WRAP_WORD, + }; + + rdpq_paragraph_t *par = rdpq_paragraph_build(¶ms, FONT_CELTICGARMONDTHESECOND, sequence_introduction_current_paragraph_string, &sequence_introduction_current_paragraph_drawn_characters); + rdpq_paragraph_render(par, x_margin, y_margin); + rdpq_paragraph_free(par); + + if (sequence_introduction_paragraph_fade_out_started == true && sequence_introduction_paragraph_fade_out_finished == false) + { + xm64player_set_vol(&sequence_introduction_xm, 1.0f - (sequence_introduction_paragraph_fade_out_elapsed / PARAGRAPH_FADE_OUT_DURATION)); + if (sequence_introduction_paragraph_fade_out_elapsed > PARAGRAPH_FADE_OUT_DURATION) + { + sequence_introduction_paragraph_fade_out_finished = true; + sequence_introduction_paragraphs_finished = true; + sequence_introduction_should_cleanup = true; + } + + rdpq_mode_push(); + uint8_t alpha = (int)(255.0f * (sequence_introduction_paragraph_fade_out_elapsed / PARAGRAPH_FADE_OUT_DURATION)); + rdpq_set_mode_standard(); + rdpq_mode_combiner(RDPQ_COMBINER_FLAT); + rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); + rdpq_set_prim_color(RGBA32(0, 0, 0, alpha)); + rdpq_fill_rectangle(0, 0, RESOLUTION_320x240.width, RESOLUTION_320x240.height); + rdpq_mode_pop(); + sequence_introduction_paragraph_fade_out_elapsed += deltatime; + } + } +} \ No newline at end of file diff --git a/code/mallard/introduction/sequence_introduction_graphics.h b/code/mallard/introduction/sequence_introduction_graphics.h new file mode 100644 index 00000000..8852ec81 --- /dev/null +++ b/code/mallard/introduction/sequence_introduction_graphics.h @@ -0,0 +1,42 @@ +#ifndef SEQUENCE_INTRODUCTION_GRAPHICS_H +#define SEQUENCE_INTRODUCTION_GRAPHICS_H + +#include + +extern bool sequence_introduction_should_cleanup; + +extern bool sequence_introduction_libdragon_logo_started; +extern bool sequence_introduction_libdragon_logo_finished; +extern float sequence_introduction_libdragon_logo_elapsed; + +extern int sequence_introduction_frame; +extern int sequence_introduction_current_paragraph_drawn_characters; +extern int sequence_introduction_current_paragraph; +extern int sequence_introduction_current_paragraph_speed; +extern bool sequence_introduction_paragraphs_finished; +extern bool sequence_introduction_current_paragraph_finished; +extern bool sequence_introduction_paragraphs_started; +extern bool sequence_introduction_paragraph_fade_out_started; +extern float sequence_introduction_paragraph_fade_out_elapsed; +extern bool sequence_introduction_paragraph_fade_out_finished; +extern char *sequence_introduction_current_paragraph_string; + +extern bool sequence_introduction_mallard_logo_started; +extern bool sequence_introduction_mallard_logo_finished; +extern float sequence_introduction_mallard_logo_elapsed; + +extern sprite_t *sequence_introduction_a_button_sprite; +extern sprite_t *sequence_introduction_start_button_sprite; +extern sprite_t *sequence_introduction_mallard_libdragon_sprite; +extern sprite_t *sequence_introduction_mallard_logo_black_sprite; +extern sprite_t *sequence_introduction_mallard_logo_white_sprite; + +extern xm64player_t sequence_introduction_xm; + +void sequence_introduction_draw_libdragon_logo(float deltatime); +void sequence_introduction_draw_mallard_logo(float deltatime); +void sequence_introduction_draw_press_start_to_skip(); +void sequence_introduction_draw_press_a_for_next(); +void sequence_introduction_draw_paragraph(float deltatime); + +#endif // SEQUENCE_INTRODUCTION_GRAPHICS_H diff --git a/code/mallard/introduction/sequence_introduction_input.c b/code/mallard/introduction/sequence_introduction_input.c new file mode 100644 index 00000000..3dcbad2c --- /dev/null +++ b/code/mallard/introduction/sequence_introduction_input.c @@ -0,0 +1,94 @@ +#include +#include "sequence_introduction.h" +#include "sequence_introduction_input.h" +#include "../../../core.h" +#include "../../../minigame.h" + +void sequence_introduction_process_controller(float deltatime) +{ + for (size_t i = 0; i < core_get_playercount(); i++) + { + joypad_port_t controllerPort = core_get_playercontroller(i); + joypad_buttons_t pressed = joypad_get_buttons_pressed(controllerPort); + + if (!joypad_is_connected(controllerPort)) + { + continue; + } + + // Skip. + if (pressed.start) + { + sequence_introduction_should_cleanup = true; + } + + // Intro - Libdragon Logo. + if (sequence_introduction_libdragon_logo_started == true && sequence_introduction_libdragon_logo_finished == false) + { + if (pressed.a) + { + sequence_introduction_libdragon_logo_elapsed = DRAW_LIBDRAGON_LOGO_DURATION; + } + } + + // Intro - Mallard Logo. + if (sequence_introduction_mallard_logo_started == true && sequence_introduction_mallard_logo_finished == false) + { + if (pressed.a) + { + sequence_introduction_mallard_logo_elapsed = DRAW_MALLARD_LOGO_FADE_IN_DURATION + DRAW_MALLARD_LOGO_DURATION + DRAW_MALLARD_LOGO_FADE_OUT_DURATION; + } + } + + // Intro - Paragraphs. + if (sequence_introduction_paragraphs_started == true && sequence_introduction_paragraphs_finished == false) + { + if (pressed.a) + { + // Speed Up. + if (sequence_introduction_current_paragraph_finished == false) + { + if (sequence_introduction_current_paragraph_speed == 1) + { + sequence_introduction_current_paragraph_drawn_characters = strlen(sequence_introduction_current_paragraph_string); + } + else + { + sequence_introduction_current_paragraph_speed = 1; + } + } + + if (sequence_introduction_paragraph_fade_out_started == true) + { + sequence_introduction_paragraph_fade_out_elapsed = PARAGRAPH_FADE_OUT_DURATION; + } + + // Next. + if (sequence_introduction_current_paragraph_finished == true) + { + sequence_introduction_current_paragraph_speed = DEFAULT_PARAGRAPH_SPEED; + sequence_introduction_current_paragraph_finished = false; + sequence_introduction_current_paragraph_drawn_characters = 0; + sequence_introduction_current_paragraph++; + + // Allowing music to progress based on progression of paragraphs. + switch (sequence_introduction_current_paragraph) + { + case 0: + sequence_introduction_currentXMPattern = 1; + break; + case 1: + sequence_introduction_currentXMPattern = 2; + break; + case 2: + sequence_introduction_currentXMPattern = 3; + break; + default: + sequence_introduction_currentXMPattern = 4; + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/code/mallard/introduction/sequence_introduction_input.h b/code/mallard/introduction/sequence_introduction_input.h new file mode 100644 index 00000000..b46d7b26 --- /dev/null +++ b/code/mallard/introduction/sequence_introduction_input.h @@ -0,0 +1,35 @@ +#ifndef sequence_introduction_INPUT_H +#define sequence_introduction_INPUT_H + +#include + +// Libdragon Logo +extern bool sequence_introduction_libdragon_logo_started; +extern bool sequence_introduction_libdragon_logo_finished; +extern float sequence_introduction_libdragon_logo_elapsed; + +// Mallard Logo +extern bool sequence_introduction_mallard_logo_started; +extern bool sequence_introduction_mallard_logo_finished; +extern float sequence_introduction_mallard_logo_elapsed; + +// Paragraphs +extern bool sequence_introduction_paragraphs_started; +extern bool sequence_introduction_paragraphs_finished; +extern bool sequence_introduction_paragraph_fade_out_started; +extern bool sequence_introduction_paragraph_fade_out_finished; +extern float sequence_introduction_paragraph_fade_out_elapsed; +extern bool sequence_introduction_current_paragraph_finished; +extern int sequence_introduction_current_paragraph; +extern int sequence_introduction_current_paragraph_drawn_characters; +extern int sequence_introduction_current_paragraph_speed; +extern char *sequence_introduction_current_paragraph_string; + +extern bool sequence_introduction_should_cleanup; + +// Music +extern int sequence_introduction_currentXMPattern; + +void sequence_introduction_process_controller(float deltatime); + +#endif // sequence_introduction_INPUT_H diff --git a/code/mallard/mallard.c b/code/mallard/mallard.c new file mode 100644 index 00000000..a2a21929 --- /dev/null +++ b/code/mallard/mallard.c @@ -0,0 +1,141 @@ +#include +#include "../../core.h" +#include "../../minigame.h" +#include "mallard.h" +#include "introduction/sequence_introduction.h" +#include "game/sequence_game.h" + +const MinigameDef minigame_def = { + .gamename = "Mallard", + .developername = "Josh Kautz", + .description = "Don't migrate - fight off winter!", + .instructions = "A to slap, B to run. Highest score wins!", +}; + +rdpq_font_t *font_halo_dek; +rdpq_font_t *font_halo_dek_medium; +rdpq_font_t *font_halo_dek_big; +rdpq_font_t *font_celtic_garamond_the_second; + +bool sequence_introduction_started = true; +bool sequence_introduction_finished = false; +bool sequence_menu_started = false; +bool sequence_menu_finished = false; +bool sequence_game_started = false; +bool sequence_game_finished = false; + +/*============================== + minigame_init + The minigame initialization function +==============================*/ +void minigame_init() +{ + /////////////////////////////////////////////////////////// + // Set Fonts // + /////////////////////////////////////////////////////////// + + rdpq_fontstyle_t fontstyle_white = { + .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF), + }; + rdpq_fontstyle_t fontstyle_white_transparent = { + .color = RGBA32(0xFF, 0xFF, 0xFF, (int)(255.0f * 0.5f)), + }; + rdpq_fontstyle_t fontstyle_white_outlined = { + .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF), + .outline_color = RGBA32(0x00, 0x00, 0x00, 0xFF), + }; + rdpq_fontstyle_t fontstyle_player1_outlined = { + .color = PLAYERCOLOR_1, + .outline_color = RGBA32(0x00, 0x00, 0x00, 0xFF), + }; + rdpq_fontstyle_t fontstyle_player2_outlined = { + .color = PLAYERCOLOR_2, + .outline_color = RGBA32(0x00, 0x00, 0x00, 0xFF), + }; + rdpq_fontstyle_t fontstyle_player3_outlined = { + .color = PLAYERCOLOR_3, + .outline_color = RGBA32(0x00, 0x00, 0x00, 0xFF), + }; + rdpq_fontstyle_t fontstyle_player4_outlined = { + .color = PLAYERCOLOR_4, + .outline_color = RGBA32(0x00, 0x00, 0x00, 0xFF), + }; + + font_halo_dek = rdpq_font_load("rom:/mallard/HaloDek.font64"); + font_halo_dek_medium = rdpq_font_load("rom:/mallard/HaloDekMedium.font64"); + font_halo_dek_big = rdpq_font_load("rom:/mallard/HaloDekBig.font64"); + font_celtic_garamond_the_second = rdpq_font_load("rom:/mallard/CelticGaramondTheSecond.font64"); + + rdpq_font_style(font_halo_dek, 0, &fontstyle_white); + rdpq_font_style(font_halo_dek, 1, &fontstyle_white_transparent); + + rdpq_font_style(font_halo_dek_medium, 0, &fontstyle_white_outlined); + rdpq_font_style(font_halo_dek_medium, 1, &fontstyle_player1_outlined); + rdpq_font_style(font_halo_dek_medium, 2, &fontstyle_player2_outlined); + rdpq_font_style(font_halo_dek_medium, 3, &fontstyle_player3_outlined); + rdpq_font_style(font_halo_dek_medium, 4, &fontstyle_player4_outlined); + + rdpq_font_style(font_halo_dek_big, 0, &fontstyle_white_outlined); + rdpq_font_style(font_halo_dek_big, 1, &fontstyle_player1_outlined); + rdpq_font_style(font_halo_dek_big, 2, &fontstyle_player2_outlined); + rdpq_font_style(font_halo_dek_big, 3, &fontstyle_player3_outlined); + rdpq_font_style(font_halo_dek_big, 4, &fontstyle_player4_outlined); + + rdpq_font_style(font_celtic_garamond_the_second, 0, &fontstyle_white); + + rdpq_text_register_font(FONT_HALODEK, font_halo_dek); + rdpq_text_register_font(FONT_HALODEK_MEDIUM, font_halo_dek_medium); + rdpq_text_register_font(FONT_HALODEK_BIG, font_halo_dek_big); + rdpq_text_register_font(FONT_CELTICGARMONDTHESECOND, font_celtic_garamond_the_second); +} + +/*============================== + minigame_fixedloop + Code that is called every loop, at a fixed delta time. + Use this function for stuff where a fixed delta time is + important, like physics. + @param The fixed delta time for this tick +==============================*/ +void minigame_fixedloop(float deltatime) +{ +} + +/*============================== + minigame_loop + Code that is called every loop. + @param The delta time for this tick +==============================*/ +void minigame_loop(float deltatime) +{ + if (sequence_introduction_started && !sequence_introduction_finished) + { + sequence_introduction(deltatime); + return; + } + + if (sequence_game_started && !sequence_game_finished) + { + sequence_game(deltatime); + return; + } + + minigame_end(); + return; +} + +/*============================== + minigame_cleanup + Clean up any memory used by your game just before it ends. +==============================*/ +void minigame_cleanup() +{ + // Unregister the font and free the allocated memory. + rdpq_text_unregister_font(FONT_HALODEK); + rdpq_font_free(font_halo_dek); + rdpq_text_unregister_font(FONT_HALODEK_MEDIUM); + rdpq_font_free(font_halo_dek_medium); + rdpq_text_unregister_font(FONT_HALODEK_BIG); + rdpq_font_free(font_halo_dek_big); + rdpq_text_unregister_font(FONT_CELTICGARMONDTHESECOND); + rdpq_font_free(font_celtic_garamond_the_second); +} diff --git a/code/mallard/mallard.h b/code/mallard/mallard.h new file mode 100644 index 00000000..7bec21fa --- /dev/null +++ b/code/mallard/mallard.h @@ -0,0 +1,13 @@ +#define FONT_HALODEK 1 +#define FONT_HALODEK_MEDIUM 2 +#define FONT_HALODEK_BIG 3 +#define FONT_CELTICGARMONDTHESECOND 4 + +#define BLACK RGBA32(0, 0, 0, 255) +#define RED RGBA32(255, 0, 0, 255) +#define WHITE RGBA32(0xFF, 0xFF, 0xFF, 0xFF) +#define ASH_GRAY RGBA32(0xAD, 0xBA, 0xBD, 0xFF) +#define MAYA_BLUE RGBA32(0x6C, 0xBE, 0xED, 0xFF) +#define GUN_METAL RGBA32(0x31, 0x39, 0x3C, 0xFF) +#define REDWOOD RGBA32(0xB2, 0x3A, 0x7A, 0xFF) +#define BREWFONT RGBA32(242, 209, 155, 0xFF) \ No newline at end of file diff --git a/code/mallard/mallard.mk b/code/mallard/mallard.mk new file mode 100644 index 00000000..d57d4267 --- /dev/null +++ b/code/mallard/mallard.mk @@ -0,0 +1,45 @@ +ASSETS_LIST += \ + filesystem/mallard/CelticGaramondTheSecond.font64 \ + filesystem/mallard/HaloDek.font64 \ + filesystem/mallard/HaloDekMedium.font64 \ + filesystem/mallard/HaloDekBig.font64 \ + filesystem/mallard/mallard_intro_music.xm64 \ + filesystem/mallard/mallard_game_music.xm64 \ + filesystem/mallard/libdragon.rgba32.sprite \ + filesystem/mallard/mallard_logo_white.rgba32.sprite \ + filesystem/mallard/mallard_logo_black.rgba32.sprite \ + filesystem/mallard/one/duck_walk_1.rgba32.sprite \ + filesystem/mallard/two/duck_walk_1.rgba32.sprite \ + filesystem/mallard/three/duck_walk_1.rgba32.sprite \ + filesystem/mallard/four/duck_walk_1.rgba32.sprite \ + filesystem/mallard/one/duck_slap.rgba32.sprite \ + filesystem/mallard/two/duck_slap.rgba32.sprite \ + filesystem/mallard/three/duck_slap.rgba32.sprite \ + filesystem/mallard/four/duck_slap.rgba32.sprite \ + filesystem/mallard/one/duck_run.rgba32.sprite \ + filesystem/mallard/two/duck_run.rgba32.sprite \ + filesystem/mallard/three/duck_run.rgba32.sprite \ + filesystem/mallard/four/duck_run.rgba32.sprite \ + filesystem/mallard/one/duck_idle.rgba32.sprite \ + filesystem/mallard/two/duck_idle.rgba32.sprite \ + filesystem/mallard/three/duck_idle.rgba32.sprite \ + filesystem/mallard/four/duck_idle.rgba32.sprite \ + filesystem/mallard/one/duck_damage.rgba32.sprite \ + filesystem/mallard/two/duck_damage.rgba32.sprite \ + filesystem/mallard/three/duck_damage.rgba32.sprite \ + filesystem/mallard/four/duck_damage.rgba32.sprite \ + filesystem/mallard/snowman/snowman_idle_evil.rgba32.sprite \ + filesystem/mallard/snowman/snowman_damage_evil.rgba32.sprite \ + filesystem/mallard/snowman/snowman_jump_evil.rgba32.sprite \ + filesystem/mallard/mallard_game_paused_text.rgba32.sprite \ + filesystem/mallard/mallard_background_park.rgba32.sprite + +filesystem/mallard/HaloDekBig.font64: $(ASSETS_DIR)/mallard/HaloDekBig.ttf + @mkdir -p $(dir $@) + @echo " [FONT] $@" + $(N64_MKFONT) $(MKFONT_FLAGS) --verbose --range 50-50 --range 41-41 --range 55-55 --range 53-53 --range 44-45 --range 2e-2e --size 60 --outline 1 -o $(dir $@) "$<" + +filesystem/mallard/HaloDekMedium.font64: $(ASSETS_DIR)/mallard/HaloDekMedium.ttf + @mkdir -p $(dir $@) + @echo " [FONT] $@" + $(N64_MKFONT) $(MKFONT_FLAGS) --verbose --range 30-39 --range 2e-2e --range 50-50 --range 20-20 --range 57-57 --range 49-49 --range 4E-4E --range 53-53 --range 44-44 --range 52-52 --range 41-41 --size 36 --outline 1 -o $(dir $@) "$<" \ No newline at end of file diff --git a/libdragon b/libdragon new file mode 160000 index 00000000..1403e236 --- /dev/null +++ b/libdragon @@ -0,0 +1 @@ +Subproject commit 1403e236130f838254bfe2fdf722caa0215ed441 diff --git a/tiny3d b/tiny3d new file mode 160000 index 00000000..bfacbb32 --- /dev/null +++ b/tiny3d @@ -0,0 +1 @@ +Subproject commit bfacbb32f6f704b2c7d945626aa86eb0960c46b1