├── audio ├── hit.wav ├── button-pressed.wav └── score-reached.wav ├── readme ├── boy.bmp ├── cover.png ├── dino1.png ├── dino2.png ├── dino3.png ├── scaled.png ├── dino-gba-1.png └── dino-gba-2.png ├── graphics ├── default.bmp ├── dinoSheet.bmp └── gbc │ ├── GBC-AUP.bmp │ ├── GBC-BUP.bmp │ ├── GBC-UP.bmp │ ├── GBC-ADOWN.bmp │ ├── GBC-ALEFT.bmp │ ├── GBC-ARIGHT.bmp │ ├── GBC-BDOWN.bmp │ ├── GBC-BLEFT.bmp │ ├── GBC-BRIGHT.bmp │ ├── GBC-DOWN.bmp │ ├── GBC-LEFT.bmp │ └── GBC-RIGHT.bmp ├── .gitmodules ├── .gitignore ├── include ├── save.h ├── graphics │ ├── graphics.h │ ├── graphicsContext.h │ └── dinoSheetHelper.h ├── meter.h ├── hitbox.h ├── input.h ├── horizon.h ├── game.h ├── obstacle.h ├── dino.h └── state.h ├── template.pnproj ├── source ├── save.c ├── hitbox.c ├── meter.c ├── input.c ├── graphics │ ├── dinoSheetHelper.c │ └── graphics.c ├── obstacle.c ├── game.c ├── dino.c └── horizon.c ├── LICENSE ├── CMakePresets.json ├── licenses ├── chrome.txt └── chromium_os.txt ├── README.md └── CMakeLists.txt /audio/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/audio/hit.wav -------------------------------------------------------------------------------- /readme/boy.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/boy.bmp -------------------------------------------------------------------------------- /readme/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/cover.png -------------------------------------------------------------------------------- /readme/dino1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/dino1.png -------------------------------------------------------------------------------- /readme/dino2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/dino2.png -------------------------------------------------------------------------------- /readme/dino3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/dino3.png -------------------------------------------------------------------------------- /readme/scaled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/scaled.png -------------------------------------------------------------------------------- /graphics/default.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/default.bmp -------------------------------------------------------------------------------- /graphics/dinoSheet.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/dinoSheet.bmp -------------------------------------------------------------------------------- /readme/dino-gba-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/dino-gba-1.png -------------------------------------------------------------------------------- /readme/dino-gba-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/readme/dino-gba-2.png -------------------------------------------------------------------------------- /audio/button-pressed.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/audio/button-pressed.wav -------------------------------------------------------------------------------- /audio/score-reached.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/audio/score-reached.wav -------------------------------------------------------------------------------- /graphics/gbc/GBC-AUP.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-AUP.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-BUP.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-BUP.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-UP.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-UP.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-ADOWN.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-ADOWN.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-ALEFT.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-ALEFT.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-ARIGHT.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-ARIGHT.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-BDOWN.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-BDOWN.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-BLEFT.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-BLEFT.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-BRIGHT.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-BRIGHT.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-DOWN.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-DOWN.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-LEFT.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-LEFT.bmp -------------------------------------------------------------------------------- /graphics/gbc/GBC-RIGHT.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fritzem/Google-Dino-GBA/HEAD/graphics/gbc/GBC-RIGHT.bmp -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gba-toolchain"] 2 | path = gba-toolchain 3 | url = https://github.com/felixjones/gba-toolchain 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vbalink.ini 3 | cmake-build 4 | bin 5 | *.gba 6 | *.elf 7 | *.nlz 8 | *.sa1 9 | *.sav 10 | .DS_Store -------------------------------------------------------------------------------- /include/save.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_SAVE_H 2 | #define DINO_GBA_SAVE_H 3 | 4 | #include "tonc.h" 5 | 6 | s32 readHiscore(); 7 | void setHiscore(int score); 8 | 9 | #endif //DINO_GBA_SAVE_H 10 | -------------------------------------------------------------------------------- /template.pnproj: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /include/graphics/graphics.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_GRAPHICS_H 2 | #define DINO_GBA_GRAPHICS_H 3 | 4 | #include "tonc.h" 5 | #include "dinoSheetHelper.h" 6 | #include "graphicsContext.h" 7 | 8 | GRAPHICS_CTX *initGraphics(); 9 | 10 | void drawGame(GRAPHICS_CTX *ctx, STATE *state); 11 | 12 | // No clue why ATTR0_Y/ATTR0_X doesn't include these masks 13 | #define Y(y) ((y) & ATTR0_Y_MASK) 14 | #define X(x) ((x) & ATTR1_X_MASK) 15 | 16 | #endif //DINO_GBA_GRAPHICS_H -------------------------------------------------------------------------------- /include/meter.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_METER_H 2 | #define DINO_GBA_METER_H 3 | 4 | #include "tonc.h" 5 | #include "state.h" 6 | 7 | INLINE void initMeter(METER_STATE * meter) { 8 | meter->distance = 0; 9 | meter->invertCounter = 0; 10 | meter->achievementCounter = 0; 11 | meter->achieving = false; 12 | meter->flashFrame = 0; 13 | meter->flashIteration = 0; 14 | } 15 | 16 | bool updateDistanceMeter(METER_STATE * meterState, int distance); 17 | 18 | #endif //DINO_GBA_METER_H -------------------------------------------------------------------------------- /source/save.c: -------------------------------------------------------------------------------- 1 | #include "save.h" 2 | 3 | #define HISCORE (u8 *) MEM_SRAM 4 | 5 | s32 readHiscore() { 6 | int score = 0; 7 | score += *HISCORE; 8 | score += *(HISCORE + 1) << 8; 9 | score += *(HISCORE + 2) << 16; 10 | score += *(HISCORE + 3) << 24; 11 | return score; 12 | } 13 | 14 | void setHiscore(int score) { 15 | *HISCORE = score & 0xFF; 16 | *(HISCORE + 1) = (score >> 8) & 0xFF; 17 | *(HISCORE + 2) = (score >> 16) & 0xFF; 18 | *(HISCORE + 3) = (score >> 24) & 0xFF; 19 | } -------------------------------------------------------------------------------- /include/hitbox.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_HITBOX_H 2 | #define DINO_GBA_HITBOX_H 3 | 4 | #include "tonc.h" 5 | 6 | typedef struct COLLISION_BOX { 7 | s32 x; 8 | s32 y; 9 | s32 w; 10 | s32 h; 11 | } COLLISION_BOX, COLLISION_BOX; 12 | 13 | void adjustBox(COLLISION_BOX * box, int size, int width); 14 | void cloneBox(COLLISION_BOX * clone, const COLLISION_BOX * base, int boxes); 15 | bool boxCheck(COLLISION_BOX * a, COLLISION_BOX * b); 16 | bool boxCheckOffset(const COLLISION_BOX * a, COLLISION_BOX * b, 17 | int x1, int y1, int x2, int y2); 18 | 19 | #endif //DINO_GBA_HITBOX_H -------------------------------------------------------------------------------- /include/input.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_INPUT_H 2 | #define DINO_GBA_INPUT_H 3 | 4 | #include "tonc.h" 5 | #include "state.h" 6 | 7 | #define BIT_HIT_UP 0x1 8 | #define BIT_HIT_DOWN 0x2 9 | #define BIT_RELEASE_UP 0x4 10 | #define BIT_RELEASE_DOWN 0x8 11 | extern u32 dino_input; 12 | 13 | #define HIT_UP (dino_input & BIT_HIT_UP) 14 | #define HIT_DOWN (dino_input & BIT_HIT_DOWN) 15 | #define RELEASE_UP (dino_input & BIT_RELEASE_UP) 16 | #define RELEASE_DOWN (dino_input & BIT_RELEASE_DOWN) 17 | 18 | void checkModeOverride(STATE * state); 19 | void inputP1(STATE * state); 20 | void inputP2(STATE * state); 21 | 22 | #endif //DINO_GBA_INPUT_H -------------------------------------------------------------------------------- /include/horizon.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_HORIZON_H 2 | #define DINO_GBA_HORIZON_H 3 | 4 | #include 5 | #include "tonc.h" 6 | #include "obstacle.h" 7 | #include "state.h" 8 | #include "meter.h" 9 | 10 | #define MAX_OBSTACLES 2 11 | #define MAX_CLOUDS 6 12 | 13 | #define INVERT_FRAMES 90 14 | 15 | #define MOON_Y 30 16 | 17 | typedef struct CLOUD { 18 | int xPos; 19 | int yPos; 20 | int cloudGap; 21 | int cloudNum; 22 | } CLOUD, CLOUD; 23 | 24 | void updateNight(HORIZON_STATE * horizonState, METER_STATE * meterState); 25 | void updateHorizon(STATE * state, HORIZON_STATE * horizonState); 26 | 27 | void addCloud(HORIZON_STATE * horizonState); 28 | 29 | void initHorizon(HORIZON_STATE * horizon); 30 | void resetHorizon(HORIZON_STATE * horizon); 31 | 32 | #endif //DINO_GBA_HORIZON_H -------------------------------------------------------------------------------- /include/graphics/graphicsContext.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_GFX_CONTEXT_H 2 | #define DINO_GBA_GFX_CONTEXT_H 3 | 4 | #include "tonc.h" 5 | 6 | typedef struct { 7 | struct { 8 | u32 x; 9 | u32 y; 10 | u32 width; 11 | u32 height; 12 | } viewport; 13 | OBJ_ATTR * baseOAM; 14 | OBJ_ATTR * cursorOAM; 15 | } GRAPHICS_CTX; 16 | #define CTX_X ctx->viewport.x 17 | #define CTX_Y ctx->viewport.y 18 | #define CTX_W ctx->viewport.width 19 | #define CTX_H ctx->viewport.height 20 | 21 | #define CTX_OAM ctx->cursorOAM 22 | 23 | INLINE void INIT_GRAPHICS_CTX_NORMAL(GRAPHICS_CTX * ctx) { 24 | CTX_X = 0; 25 | CTX_Y = 0; 26 | CTX_W = SCREEN_WIDTH; 27 | CTX_H = SCREEN_HEIGHT; 28 | } 29 | 30 | INLINE void INIT_GRAPHICS_CTX_HALF(GRAPHICS_CTX * ctx) { 31 | CTX_X = 0; 32 | CTX_Y = 0; 33 | CTX_W = SCREEN_WIDTH; 34 | CTX_H = SCREEN_HEIGHT / 2; 35 | } 36 | 37 | #endif //DINO_GBA_GFX_CONTEXT_H -------------------------------------------------------------------------------- /include/game.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_GAME_H 2 | #define DINO_GBA_GAME_H 3 | 4 | #include "tonc.h" 5 | #include "dino.h" 6 | #include "horizon.h" 7 | #include "meter.h" 8 | #include "state.h" 9 | 10 | // Delay until game over screen accepts input 11 | #define RESET_FRAMES 45 12 | 13 | INLINE void initState(GAME_STATE * state) { 14 | state->speed = SPEED; 15 | state->curtainScroll = STARTING_CURTAIN_SCROLL; 16 | state->distanceRan = 0; 17 | state->distanceRanPoint = 0; 18 | 19 | state->runningFrames = 0; 20 | state->randoFrames = 0; 21 | state->spawnObstacles = false; 22 | 23 | state->startedPlaying = false; 24 | state->playingIntro = false; 25 | 26 | state->gameOver = false; 27 | state->gameOverFrames = 0; 28 | } 29 | 30 | INLINE void resetState(GAME_STATE * state) { 31 | state->speed = SPEED; 32 | state->distanceRan = 0; 33 | state->distanceRanPoint = 0; 34 | 35 | state->runningFrames = 0; 36 | state->spawnObstacles = false; 37 | 38 | state->gameOver = false; 39 | } 40 | 41 | void startGame(STATE * state); 42 | 43 | #endif //DINO_GBA_GAME_H -------------------------------------------------------------------------------- /source/hitbox.c: -------------------------------------------------------------------------------- 1 | #include "hitbox.h" 2 | 3 | void adjustBox(COLLISION_BOX * box, int size, int width) { 4 | if (size > 1) { 5 | (box + 1)->w = width - box->w - (box + 2)->w; 6 | (box + 2)->x = width - (box + 2)->w; 7 | } 8 | } 9 | 10 | void cloneBox(COLLISION_BOX * clone, const COLLISION_BOX * base, int boxes) { 11 | for (int i = 0; i < boxes; i++) { 12 | (clone + i)->x = (base + i)->x; 13 | (clone + i)->y = (base + i)->y; 14 | (clone + i)->w = (base + i)->w; 15 | (clone + i)->h = (base + i)->h; 16 | } 17 | } 18 | 19 | bool boxCheck(COLLISION_BOX * a, COLLISION_BOX * b) { 20 | return (a->x < b->x + b->w && 21 | a->x + a->w > b->x && 22 | a->y < b->y + b->h && 23 | a->y + a->h > b->y); 24 | } 25 | 26 | bool boxCheckOffset(const COLLISION_BOX * a, COLLISION_BOX * b, 27 | int x1, int y1, int x2, int y2) { 28 | return (a->x + x1 < b->x + x2 + b->w && 29 | a->x + x1 + a->w > b->x + x2 && 30 | a->y + y1 < b->y + y2 + b->h && 31 | a->y + y1 + a->h > b->y + y2); 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ethan Fritz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "agb-base", 11 | "hidden": true, 12 | "generator": "Unix Makefiles", 13 | "toolchainFile": "gba-toolchain/cmake/gba.toolchain.cmake", 14 | "binaryDir": "cmake-build/${presetName}", 15 | "installDir": "bin/${presetName}" 16 | }, 17 | { 18 | "name": "verbose-base", 19 | "hidden": true, 20 | "cacheVariables": { 21 | "MGBA_LOGS": "true" 22 | } 23 | }, 24 | { 25 | "name": "agb-debug", 26 | "displayName": "AGB Debug", 27 | "description": "Build for GBA with no optimizations", 28 | "inherits": ["agb-base", "verbose-base"], 29 | "cacheVariables": { 30 | "CMAKE_BUILD_TYPE": "debug" 31 | } 32 | }, 33 | { 34 | "name": "agb-release", 35 | "displayName": "AGB Release", 36 | "description": "Build for GBA with -O3 optimizations", 37 | "inherits": "agb-base", 38 | "cacheVariables": { 39 | "CMAKE_BUILD_TYPE": "release" 40 | } 41 | }, 42 | { 43 | "name": "agb-release-verbose", 44 | "displayName": "AGB Release (Verbose)", 45 | "description": "Optimized build with mGBA logging enabled", 46 | "inherits": ["agb-release", "verbose-base"] 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /licenses/chrome.txt: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Chromium Authors 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google LLC nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /licenses/chromium_os.txt: -------------------------------------------------------------------------------- 1 | // Copyright 2006-2009 The ChromiumOS Authors 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google LLC nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Title Screen](readme/dino1.png) 2 | 3 | # Google-Dino-GBA 4 | Google Dino Advance is a recreation of the Chromium Dino game for Nintendo's 2001 handheld: the Game Boy Advance. 5 | 6 | ![Dino can jump](readme/dino2.png) 7 | 8 | # Playing 9 | The ROM can be downloaded from the releases section. It can be opened in any Game Boy Advance emulator of your choosing, or a flash cart. 10 | 11 | - Up on the D-pad and the A button make Dino jump, down on the D-pad crouches or speeds up Dino's fall. 12 | - Local Co-Op 13 | - Hold L+R on the `No Internet` screen for five seconds to launch a Co-Op session. 14 | - Player One controls Dino using the D-Pad 15 | - Player Two controls Mr. Dino using the face buttons. 16 | - Revive system: Grab the meat to revive your partner! 17 | - Alternate Palettes 18 | - Hold down a key combination while the game is booting for alternate colors from the GBC backwards compatibility. 19 | - List available here: 20 | - https://gbstudiocentral.com/tips/game-boy-color-modes/ 21 | 22 | ![Dino at night](readme/dino3.png) 23 | 24 | # Building 25 | You can build a rom of your own with the CMake presets. Requires [devkitPro](https://devkitpro.org/wiki/Getting_Started) 26 | 27 | # Notes 28 | Referenced the original [chromium source](https://source.chromium.org/chromium/chromium/src/+/master:components/neterror/resources/offline.js;l=7?q=t-rex%20package:%5Echromium$&ss=chromium) heavily. 29 | Game has been balanced to run as the actual game plays in a 240p sized window. The max speed was scaled down the same way the starting speed is scaled down in the original game. 30 | All game art is property of Google :) 31 | -------------------------------------------------------------------------------- /source/meter.c: -------------------------------------------------------------------------------- 1 | #include "meter.h" 2 | #include "dinoSheetHelper.h" 3 | 4 | #define ACHIEVEMENT_DISTANCE 100 5 | #define FLASH_FRAMES 15 6 | #define FLASH_ITERATIONS 3 7 | 8 | int distanceConvert(int distance); 9 | 10 | bool updateDistanceMeter(METER_STATE * meterState, int distance) { 11 | int trueDistance = distanceConvert(distance); 12 | int deltaDistance = (trueDistance - meterState->distance); 13 | meterState->invertCounter += deltaDistance; 14 | meterState->achievementCounter += deltaDistance; 15 | meterState->distance = trueDistance; 16 | if (meterState->achieving) { 17 | if (meterState->flashIteration <= FLASH_ITERATIONS) { 18 | meterState->flashFrame += 1; 19 | if (meterState->flashFrame == 1) { 20 | meterState->flashing = true; 21 | } else if (meterState->flashFrame == FLASH_FRAMES * 2) { 22 | meterState->flashFrame = 0; 23 | meterState->flashIteration += 1; 24 | meterState->flashing = false; 25 | } else if (meterState->flashFrame == FLASH_FRAMES) { 26 | meterState->flashing = false; 27 | } 28 | } else { 29 | meterState->flashIteration = 0; 30 | meterState->flashFrame = 0; 31 | meterState->achieving = false; 32 | } 33 | } else { 34 | if (meterState->achievementCounter >= ACHIEVEMENT_DISTANCE) { 35 | meterState->achievementCounter -= ACHIEVEMENT_DISTANCE; 36 | meterState->achieving = true; 37 | meterState->displayNumber = trueDistance; 38 | return true; 39 | } else { 40 | meterState->displayNumber = trueDistance; 41 | } 42 | } 43 | return false; 44 | } 45 | 46 | int distanceConvert(int distance) { 47 | return distance / 40; // (* 0.025) 48 | } 49 | 50 | -------------------------------------------------------------------------------- /source/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "input.h" 4 | #include "dino.h" 5 | #include "game.h" 6 | 7 | #define MODE_OVERRIDE_FRAMES (60 * 5) 8 | 9 | u32 dino_input = 0; 10 | u32 coopFrames = 0; 11 | 12 | void initCoop(STATE * state) { 13 | mmEffect(SFX_SCORE_REACHED); 14 | state->mode = COOP; 15 | 16 | DINO_STATE * mino = &state->minoState; 17 | mino->yPos = DINO_GROUND_Y - 70; 18 | mino->status = ENTERING; 19 | mino->jumpVelocity = INITIAL_JUMP_VELOCITY + 20; 20 | 21 | mino->xPos = 28; 22 | mino->hat = true; 23 | 24 | state->dinoState.status = RUNNING; 25 | state->dinoState.frameTime = RUN_FRAME; 26 | startGame(state); 27 | } 28 | 29 | void checkModeOverride(STATE * state) { 30 | if (key_is_down(KEY_L) && key_is_down(KEY_R)) { 31 | if (coopFrames++ >= MODE_OVERRIDE_FRAMES) { 32 | initCoop(state); 33 | } 34 | } else { 35 | coopFrames = 0; 36 | } 37 | } 38 | 39 | #define KEY_MAP(key, bit) \ 40 | dino_input |= key ? bit : 0 41 | 42 | void inputP1(STATE * state) { 43 | dino_input = 0; 44 | if (state->mode == NORMAL) { 45 | KEY_MAP(key_hit(KEY_A | KEY_UP), BIT_HIT_UP); 46 | KEY_MAP(key_hit(KEY_DOWN), BIT_HIT_DOWN); 47 | KEY_MAP(key_released(KEY_A | KEY_UP), BIT_RELEASE_UP); 48 | KEY_MAP(key_released(KEY_DOWN), BIT_RELEASE_DOWN); 49 | } else if (state->mode == COOP) { 50 | KEY_MAP(key_hit(KEY_UP), BIT_HIT_UP); 51 | KEY_MAP(key_hit(KEY_DOWN), BIT_HIT_DOWN); 52 | KEY_MAP(key_released(KEY_UP), BIT_RELEASE_UP); 53 | KEY_MAP(key_released(KEY_DOWN), BIT_RELEASE_DOWN); 54 | } 55 | } 56 | 57 | void inputP2(STATE * state) { 58 | dino_input = 0; 59 | if (state->mode == COOP) { 60 | KEY_MAP(key_hit(KEY_A), BIT_HIT_UP); 61 | KEY_MAP(key_hit(KEY_B), BIT_HIT_DOWN); 62 | KEY_MAP(key_released(KEY_A), BIT_RELEASE_UP); 63 | KEY_MAP(key_released(KEY_B), BIT_RELEASE_DOWN); 64 | } 65 | } -------------------------------------------------------------------------------- /include/obstacle.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_OBSTACLE_H 2 | #define DINO_GBA_OBSTACLE_H 3 | 4 | #include "hitbox.h" 5 | 6 | #define MAX_CACTUS_SIZE 3 7 | 8 | #define CACTUS_SMALL_Y 113 9 | #define CACTUS_SMALL_Y_SPRITE_OFFSET 5 10 | #define CACTUS_SMALL_WIDTH 17 11 | #define CACTUS_SMALL_HEIGHT 35 12 | #define CACTUS_SMALL_MULTI_SPEED 4 13 | 14 | #define CACTUS_LARGE_Y 103 15 | #define CACTUS_LARGE_WIDTH 25 16 | #define CACTUS_LARGE_HEIGHT 50 17 | #define CACTUS_LARGE_MULTI_SPEED 6 18 | #define CACTUS_GAP 120 * SPEED_POINT 19 | 20 | #define DACTYL_WIDTH 46 21 | #define DACTYL_HEIGHT 40 22 | #define DACTYL_GAP 150 * SPEED_POINT 23 | #define DACTYL_FRAMES 10 24 | #define DACTYL_MIN_SPEED 6000 25 | #define DACTYL_SPEED_OFFSET 800 26 | 27 | #define REVIVE_WIDTH 32 28 | #define REVIVE_HEIGHT 32 29 | #define REVIVE_GAP 150 * SPEED_POINT 30 | #define REVIVE_SPEED_OFFSET 800 31 | 32 | typedef enum { 33 | REVIVE, 34 | CACTUS_SMALL, 35 | CACTUS_LARGE, 36 | PTERODACTYL, 37 | OBSTACLE_TYPE_COUNT 38 | } OBSTACLE_TYPE; 39 | 40 | typedef struct OBSTACLE { 41 | OBSTACLE_TYPE type; 42 | int x; 43 | int y; 44 | int width; 45 | int height; 46 | int gap; 47 | int speedOffset; 48 | bool visible; 49 | 50 | int frames; 51 | int extraSpeed; 52 | 53 | int numBoxes; 54 | COLLISION_BOX * colBox; 55 | int spriteY; 56 | 57 | union { 58 | int cactusSize; 59 | bool flap; 60 | int typeCategory; 61 | }; 62 | } OBSTACLE, OBSTACLE; 63 | 64 | INLINE void resetObstacles(OBSTACLE * obs) { 65 | obs->visible = 0; 66 | (obs + 1)->visible = 0; 67 | } 68 | 69 | INLINE void despawnObstacle(OBSTACLE * obs) { 70 | obs->x = -obs->width; 71 | } 72 | 73 | void createCactusSmall(OBSTACLE * obs, int speed); 74 | void createCactusLarge(OBSTACLE * obs, int speed); 75 | void createPterodactyl(OBSTACLE * obs, int speed); 76 | void createRevive(OBSTACLE * obs, int speed); 77 | bool updateObstacle(OBSTACLE * obs, int scrollSpeed, int index); 78 | 79 | #endif //DINO_GBA_OBSTACLE_H -------------------------------------------------------------------------------- /include/dino.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_H 2 | #define DINO_H 3 | 4 | #include 5 | #include "tonc.h" 6 | #include "hitbox.h" 7 | #include "obstacle.h" 8 | #include "state.h" 9 | #include "horizon.h" 10 | 11 | #define SPEED 2946 12 | //6000 13 | #define SPEED_MAX 6383 14 | //13000 15 | #define ACCELERATION 1 16 | //0.001 17 | #define SPEED_POINT 0x400 18 | #define SPEED_POINT_DIV 10 19 | #define SPEED_REM 0x3FF 20 | 21 | #define DINO_GROUND_Y 7 22 | #define INITIAL_JUMP_VELOCITY 100 23 | #define DROP_VELOCITY -50 24 | #define SPEED_DROP_COEFFICIENT 3 25 | #define MAX_JUMP_HEIGHT 37 26 | #define MIN_JUMP_HEIGHT 30 - DINO_GROUND_Y 27 | #define GRAVITY -6 28 | #define DINO_WIDTH 44 29 | #define DINO_HEIGHT 47 30 | #define DINO_WIDTH_DUCK 59 31 | #define DINO_HEIGHT_DUCK 25 32 | #define MAX_BLINKS 3 33 | #define MAX_BLINK_DELAY 420 34 | #define BLINK_TIME 30 35 | 36 | #define CLEAR_FRAMES 180 37 | #define STARTING_CURTAIN_SCROLL 468 38 | 39 | #define RUN_FRAME 5 40 | #define DUCK_FRAME 8 41 | 42 | #define DINO_COLLISION_BOXES 6 43 | #define DUCK_COLLISION_BOXES 1 44 | #define CACT_COLLISION_BOXES 3 45 | #define DACTYL_COLLISION_BOXES 5 46 | #define REVIVE_COLLISION_BOXES 1 47 | #define MAX_HITBOXES 6 48 | 49 | #define DINO_ANIMATING ((dinoState->status == RUNNING) || dinoState->status == DUCKING || dinoState->status == CRASHED) 50 | 51 | void updateDino(DINO_STATE * dinoState); 52 | void inputDino(DINO_STATE * dinoState, GAME_STATE * gameState); 53 | 54 | OBSTACLE * collisionCheck(DINO_STATE * dinoState, HORIZON_STATE * horizonState); 55 | 56 | void addPoint(int add, int *base, int *point); 57 | 58 | INLINE void initDino(DINO_STATE * dino) { 59 | dino->yPos = DINO_GROUND_Y; 60 | dino->status = WAITING; 61 | } 62 | 63 | INLINE void dinoCrash(DINO_STATE * state) { 64 | state->status = CRASHED; 65 | state->frameCounter = 0; 66 | state->frame = 0; 67 | state->frameTime = 48; 68 | } 69 | 70 | INLINE void dinoRevive(DINO_STATE * state) { 71 | state->frame = 0; 72 | state->frameCounter = 0; 73 | state->frameTime = RUN_FRAME; 74 | state->status = RUNNING; 75 | state->speedDrop = false; 76 | state->jumpVelocity = 0; 77 | state->yPos = DINO_GROUND_Y; 78 | } 79 | 80 | void resetDino(DINO_STATE * dino); 81 | 82 | #endif -------------------------------------------------------------------------------- /include/state.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_GBA_STATE_H 2 | #define DINO_GBA_STATE_H 3 | 4 | typedef void (*FP)(); 5 | 6 | typedef struct GAME_STATE { 7 | int speed; 8 | int curtainScroll; 9 | 10 | int hiScore; 11 | 12 | int distanceRan; 13 | int distanceRanPoint; 14 | 15 | int runningFrames; 16 | int randoFrames; 17 | bool spawnObstacles; 18 | 19 | bool startedPlaying; 20 | bool playingIntro; 21 | 22 | bool gameOver; 23 | int gameOverFrames; 24 | } GAME_STATE, GAME_STATE; 25 | 26 | typedef enum {CRASHED, DUCKING, JUMPING, RUNNING, WAITING, ENTERING} DINO_STATUS; 27 | 28 | typedef struct DINO_STATE { 29 | int xPos; 30 | int yPos; 31 | int jumpVelocity; 32 | 33 | DINO_STATUS status; 34 | bool speedDrop; 35 | bool jumped; 36 | bool reachedMin; 37 | 38 | int frame; 39 | int frameCounter; 40 | int frameTime; 41 | 42 | int blinkTime; 43 | int blinks; 44 | int blinkFrame; 45 | bool blinking; 46 | bool hat; 47 | } DINO_STATE, DINO_STATE; 48 | 49 | typedef struct HORIZON_STATE { 50 | int scroll; 51 | int nextScrollTile; 52 | int scrolled; 53 | int terrainScroll; 54 | bool bumpy; 55 | int extraScroll; 56 | 57 | struct CLOUD * clouds; 58 | int cloudCursor; 59 | int cloudCount; 60 | 61 | int invertTimer; 62 | int invertFrame; 63 | int fadeFrame; 64 | int opacity; 65 | bool night; 66 | bool inverting; 67 | bool fading; 68 | 69 | int star0X; 70 | int star0Y; 71 | int star1X; 72 | int star1Y; 73 | int starMov; 74 | 75 | int moonPhase; 76 | int moonX; 77 | int moonMov; 78 | 79 | struct OBSTACLE * obstacles; 80 | int obstacleCount; 81 | int obstacleCursor; 82 | int lastObstacle; 83 | } HORIZON_STATE, HORIZON_STATE; 84 | 85 | typedef struct METER_STATE { 86 | int distance; 87 | int invertCounter; 88 | int achievementCounter; 89 | 90 | bool achieving; 91 | int flashFrame; 92 | int flashIteration; 93 | 94 | u32 displayNumber; 95 | bool flashing; 96 | } METER_STATE, METER_STATE; 97 | 98 | typedef enum { NORMAL, COOP } MODE; 99 | typedef struct { 100 | GAME_STATE gameState; 101 | DINO_STATE dinoState; 102 | DINO_STATE minoState; 103 | HORIZON_STATE horizonState; 104 | METER_STATE meterState; 105 | 106 | MODE mode; 107 | } STATE; 108 | 109 | #endif //DINO_GBA_STATE_H -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(DINO_GBA LANGUAGES ASM C) 3 | 4 | set(GAME_SOURCES 5 | source/dino.c 6 | source/graphics/dinoSheetHelper.c 7 | source/save.c 8 | source/hitbox.c 9 | source/obstacle.c 10 | source/game.c 11 | source/horizon.c 12 | source/meter.c 13 | source/graphics/graphics.c 14 | source/input.c 15 | ) 16 | add_executable(dino-gba ${GAME_SOURCES}) 17 | 18 | set(GAME_INCLUDES 19 | include 20 | include/graphics 21 | ) 22 | target_include_directories(dino-gba PRIVATE ${GAME_INCLUDES}) 23 | 24 | # gba-toolchain sets `CMAKE_SYSTEM_NAME` to `AdvancedGameBoy` 25 | if(CMAKE_SYSTEM_NAME STREQUAL AdvancedGameBoy) 26 | find_package(librom REQUIRED) # GBA ROM runtime library 27 | find_package(tonclib REQUIRED) # Tonc C development library 28 | find_package(maxmod REQUIRED) # GBA music and sound solution 29 | find_package(grit REQUIRED) # GBA bitmap converter 30 | 31 | add_maxmod_library(dino_soundbank 32 | audio/button-pressed.wav 33 | audio/hit.wav 34 | audio/score-reached.wav 35 | ) 36 | 37 | add_grit_library(palette_bank PALETTE 38 | NO_GRAPHICS NO_MAP PALETTE_COUNT 32 39 | graphics/default.bmp 40 | graphics/gbc/GBC-ADOWN.bmp 41 | graphics/gbc/GBC-ALEFT.bmp 42 | graphics/gbc/GBC-ARIGHT.bmp 43 | graphics/gbc/GBC-AUP.bmp 44 | graphics/gbc/GBC-BDOWN.bmp 45 | graphics/gbc/GBC-BLEFT.bmp 46 | graphics/gbc/GBC-BRIGHT.bmp 47 | graphics/gbc/GBC-BUP.bmp 48 | graphics/gbc/GBC-DOWN.bmp 49 | graphics/gbc/GBC-LEFT.bmp 50 | graphics/gbc/GBC-RIGHT.bmp 51 | graphics/gbc/GBC-UP.bmp 52 | ) 53 | 54 | add_grit_library(dino_sheet NO_PALETTE NO_MAP GRAPHICS_BIT_DEPTH 4 55 | graphics/dinoSheet.bmp 56 | ) 57 | 58 | target_compile_options(dino-gba PRIVATE 59 | -mthumb 60 | -mthumb-interwork 61 | -g 62 | -Wall 63 | -Wno-unknown-pragmas 64 | -mcpu=arm7tdmi 65 | -mtune=arm7tdmi 66 | -gdwarf-4 67 | ) 68 | 69 | if(CMAKE_BUILD_TYPE STREQUAL "debug") 70 | message("Configuring for Debug build") 71 | target_compile_options(dino-gba PRIVATE -Og) 72 | elseif(CMAKE_BUILD_TYPE STREQUAL "release") 73 | message("Configuring for Release build") 74 | target_compile_options(dino-gba PRIVATE -O3) 75 | endif() 76 | 77 | if (MGBA_LOGS STREQUAL "true") 78 | message("Enabling mGBA logging") 79 | target_compile_definitions(dino-gba PRIVATE VERBOSE=1) 80 | endif() 81 | 82 | target_link_libraries(dino-gba PRIVATE librom tonclib maxmod 83 | dino_soundbank dino_sheet palette_bank) 84 | 85 | # ROM header info 86 | set_target_properties(dino-gba PROPERTIES 87 | ROM_TITLE "Dino Advance" 88 | ROM_MAKER EF 89 | ROM_VERSION 2 90 | C_STANDARD 17 91 | ) 92 | 93 | # install to .gba 94 | install_rom(dino-gba) 95 | 96 | endif() 97 | -------------------------------------------------------------------------------- /include/graphics/dinoSheetHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef DINO_SHEET_HELPER_H 2 | #define DINO_SHEET_HELPER_H 3 | 4 | #include 5 | #include "tonc.h" 6 | #include "dino.h" 7 | 8 | #define attr0Visibility 0x200 9 | 10 | //Sprite Indices 11 | #define replaySI 0x0 12 | #define replayTailSI 0x0 13 | 14 | #define cloudLeftSI 0x4 15 | #define cloudRightSI 0x8 16 | 17 | #define smallCactusSingleSI 0xA 18 | #define smallCactusDoubleSI 0xA 19 | #define smallCactusTripleSI 0x11 20 | #define smallCactusStumpSI 0xC8 21 | 22 | #define bigCactusSingleSI 0x180 23 | #define bigCactusDoubleSI 0x184 24 | #define bigCactusTripleSI 0x1CC 25 | #define bigCactusTripleRunoffSI 0x1D4 26 | 27 | #define dinoHeadSI 0x1A 28 | #define dinoWinkSI 0x1E 29 | #define dinoDeadeyeSI 0x1F 30 | #define dinoTailSI 0x5E 31 | #define dinoFeet0_SI 0x110 32 | #define dinoFeet1_SI 0x114 33 | #define dinoFeet2_SI 0x118 34 | #define dinoCrouchSI 0x150 35 | #define dinoCrouchFeet0_SI 0x11C 36 | #define dinoCrouchFeet1_SI 0x15C 37 | #define deadDinoSI 0x198 38 | #define dinoHatSI 881 39 | #define dinoFlapSI 923 40 | #define dinoAngelSI 925 41 | #define dinoAngelHeadSI 927 42 | #define anoNikuSI 604 43 | 44 | #define birdBeakSI 0x44 45 | #define birdFlapDownSI 0x80 46 | #define birdFlapUpSI 0x84 47 | #define birdFlapTrans (birdFlapDownSI ^ birdFlapUpSI) 48 | 49 | #define star0_SI 0x46 50 | #define star1_SI 0x48 51 | #define star2_SI 0x88 52 | INLINE int starSI(int x) {return (x == 0) ? star0_SI : (x == 1) ? star1_SI : star2_SI;} 53 | 54 | #define fullMoonTopSI 0xE8 55 | #define moon0_SI 0x100 56 | #define moon1_SI 0x104 57 | #define moon2_SI 0x108 58 | #define fullMoonSI 0x10C 59 | 60 | #define num0_SI 0x8A 61 | #define num1_SI 0x8C 62 | #define num2_SI 0x8E 63 | #define num3_SI 0x90 64 | #define num4_SI 0x92 65 | #define num5_SI 0x94 66 | #define num6_SI 0x96 67 | #define num7_SI 0x98 68 | #define num8_SI 0x9A 69 | #define num9_SI 0x9C 70 | INLINE int numToSI(int x) {return num0_SI + (x * 0x2);} 71 | 72 | #define charG_SI 0xCC 73 | #define charA_SI 0xCE 74 | #define charM_SI 0xD0 75 | #define charE_SI 0xD2 76 | #define charO_SI 0xD4 77 | #define charV_SI 0xD6 78 | #define charR_SI 0xD8 79 | 80 | #define charY 40 81 | #define charXg 20 82 | #define charXa charXg + 24 83 | #define charXm charXa + 24 84 | #define charXe charXm + 24 85 | #define charXo charXe + 44 86 | #define charXv charXo + 24 87 | #define charXe2 charXv + 24 88 | #define charXr charXe2 + 24 89 | 90 | #define hiSI 0xDA 91 | 92 | #define titleLeftSI 0x380 93 | #define titleRightSI 0x388 94 | 95 | //Terrain 96 | 97 | #define blankTile_SI 0x359 98 | #define transparent_SI 0xA 99 | #define firstTerrain_SI 0x2C0 100 | 101 | #define GROUND_Y 18 102 | #define TERRAIN_STRIP_LENGTH 75 103 | #define BUMPY_OFFSET 96 104 | #define BUMPY_TOP_OFFSET 64 105 | 106 | #define TILE_SIZE 8 107 | #define BG_TILE_LENGTH 32 108 | 109 | #define CLOUD_WIDTH 48 110 | 111 | #define PALETTE_POINT 1000 112 | 113 | typedef struct PALETTE_TRACKER { 114 | u8 minRed; 115 | u8 maxRed; 116 | int curRed; 117 | int incRed; 118 | 119 | u8 minGreen; 120 | u8 maxGreen; 121 | int curGreen; 122 | int incGreen; 123 | 124 | u8 minBlue; 125 | u8 maxBlue; 126 | int curBlue; 127 | int incBlue; 128 | } PALETTE_TRACKER, PALETTE_TRACKER; 129 | 130 | extern PALETTE_TRACKER *paletteTrackers; 131 | 132 | void initSets(); 133 | void assembleSets(); 134 | 135 | //Background helpers 136 | void whiteOutBG(); 137 | void backgroundInit(); 138 | void updateHorizonTile(int bg_index, int terrainIndex, bool bumpy); 139 | void inversionUpdate(bool night, bool invertOver); 140 | PALETTE_TRACKER *createTrackers(); 141 | 142 | #endif -------------------------------------------------------------------------------- /source/graphics/dinoSheetHelper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dinoSheetHelper.h" 3 | #include "dino.h" 4 | #include "tonc.h" 5 | #include "horizon.h" 6 | 7 | PALETTE_TRACKER *trackers; 8 | 9 | #define PAL_BG 0 10 | #define PAL_OBJ0 0 11 | #define PAL_OBJ1 0 12 | #define PAL_TITLE 1 13 | 14 | #define INVERT_COUNT 10 15 | 16 | void initSets() { 17 | trackers = createTrackers(); 18 | } 19 | 20 | //Terrain helpers 21 | 22 | void whiteOutBG() { 23 | se_fill(&se_mem[29][0], blankTile_SI | SE_PALBANK(PAL_BG)); 24 | se_fill(&se_mem[30][0], transparent_SI | SE_PALBANK(PAL_BG)); 25 | se_fill(&se_mem[31][0], blankTile_SI | SE_PALBANK(PAL_BG)); 26 | } 27 | 28 | //x1819 29 | void backgroundInit() { 30 | REG_BG1HOFS = STARTING_CURTAIN_SCROLL; 31 | 32 | for (int i = 0; i < 31; i++) { 33 | se_plot(&se_mem[31][0], i, GROUND_Y, (firstTerrain_SI + i) | SE_PALBANK(PAL_BG)); 34 | } 35 | } 36 | 37 | void updateHorizonTile(int bg_index, int terrainIndex, bool bumpy) { 38 | int top_SI = blankTile_SI; 39 | if (bumpy && (terrainIndex == 16 || terrainIndex == 17 40 | || terrainIndex == 20 || terrainIndex == 21 41 | || (terrainIndex >= 56 && terrainIndex <= 61))) { 42 | top_SI = firstTerrain_SI + terrainIndex + BUMPY_TOP_OFFSET; 43 | terrainIndex += BUMPY_OFFSET; 44 | } 45 | se_plot(&se_mem[31][0], bg_index, GROUND_Y - 1, top_SI); 46 | se_plot(&se_mem[31][0], bg_index, GROUND_Y, firstTerrain_SI + terrainIndex); 47 | } 48 | 49 | PALETTE_TRACKER *createTrackers() { 50 | PALETTE_TRACKER *trackers = malloc(INVERT_COUNT * sizeof(PALETTE_TRACKER)); 51 | for (u16 palIndex = 1; palIndex < INVERT_COUNT; palIndex++) { 52 | (trackers + palIndex)->maxRed = ((*(pal_obj_mem + palIndex) & RED_MASK) >> RED_SHIFT); 53 | (trackers + palIndex)->minRed = 31 - ((*(pal_obj_mem + palIndex) & RED_MASK) >> RED_SHIFT); 54 | (trackers + palIndex)->curRed = ((*(pal_obj_mem + palIndex) & RED_MASK) >> RED_SHIFT) * PALETTE_POINT; 55 | (trackers + palIndex)->incRed = ((trackers + palIndex)->minRed - (trackers + palIndex)->maxRed) * PALETTE_POINT / INVERT_FRAMES; 56 | 57 | (trackers + palIndex)->maxGreen = ((*(pal_obj_mem + palIndex) & GREEN_MASK) >> GREEN_SHIFT); 58 | (trackers + palIndex)->minGreen = 31 - ((*(pal_obj_mem + palIndex) & GREEN_MASK) >> GREEN_SHIFT); 59 | (trackers + palIndex)->curGreen = ((*(pal_obj_mem + palIndex) & GREEN_MASK) >> GREEN_SHIFT) * PALETTE_POINT; 60 | (trackers + palIndex)->incGreen = ((trackers + palIndex)->minGreen - (trackers + palIndex)->maxGreen) * PALETTE_POINT / INVERT_FRAMES; 61 | 62 | (trackers + palIndex)->maxBlue = ((*(pal_obj_mem + palIndex) & BLUE_MASK) >> BLUE_SHIFT); 63 | (trackers + palIndex)->minBlue = 31 - ((*(pal_obj_mem + palIndex) & BLUE_MASK) >> BLUE_SHIFT); 64 | (trackers + palIndex)->curBlue = ((*(pal_obj_mem + palIndex) & BLUE_MASK) >> BLUE_SHIFT) * PALETTE_POINT; 65 | (trackers + palIndex)->incBlue = ((trackers + palIndex)->minBlue - (trackers + palIndex)->maxBlue) * PALETTE_POINT / INVERT_FRAMES; 66 | } 67 | return trackers; 68 | } 69 | 70 | void inversionUpdate(bool night, bool invertOver) { 71 | if (invertOver) { 72 | int red = 0; 73 | int green = 0; 74 | int blue = 0; 75 | for (u16 palIndex = 1; palIndex < INVERT_COUNT; palIndex++) { 76 | if (night) { 77 | red = (trackers + palIndex)->minRed; 78 | green = (trackers + palIndex)->minGreen; 79 | blue = (trackers + palIndex)->minBlue; 80 | } else { 81 | red = (trackers + palIndex)->maxRed; 82 | green = (trackers + palIndex)->maxGreen; 83 | blue = (trackers + palIndex)->maxBlue; 84 | } 85 | *(pal_obj_mem + palIndex) = RGB15(red, green, blue); 86 | *(pal_bg_mem + palIndex) = RGB15(red, green, blue); 87 | } 88 | } else if (night) { 89 | for (u16 palIndex = 1; palIndex < INVERT_COUNT; palIndex++) { 90 | (trackers + palIndex)->curRed += (trackers + palIndex)->incRed; 91 | (trackers + palIndex)->curGreen += (trackers + palIndex)->incGreen; 92 | (trackers + palIndex)->curBlue += (trackers + palIndex)->incBlue; 93 | int red = (trackers + palIndex)->curRed / PALETTE_POINT; 94 | int green = (trackers + palIndex)->curGreen / PALETTE_POINT; 95 | int blue = (trackers + palIndex)->curBlue / PALETTE_POINT; 96 | *(pal_obj_mem + palIndex) = RGB15(red, green, blue); 97 | *(pal_bg_mem + palIndex) = RGB15(red, green, blue); 98 | } 99 | } else { 100 | for (u16 palIndex = 1; palIndex < INVERT_COUNT; palIndex++) { 101 | (trackers + palIndex)->curRed -= (trackers + palIndex)->incRed; 102 | (trackers + palIndex)->curGreen -= (trackers + palIndex)->incGreen; 103 | (trackers + palIndex)->curBlue -= (trackers + palIndex)->incBlue; 104 | int red = (trackers + palIndex)->curRed / PALETTE_POINT; 105 | int green = (trackers + palIndex)->curGreen / PALETTE_POINT; 106 | int blue = (trackers + palIndex)->curBlue / PALETTE_POINT; 107 | *(pal_obj_mem + palIndex) = RGB15(red, green, blue); 108 | *(pal_bg_mem + palIndex) = RGB15(red, green, blue); 109 | } 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /source/obstacle.c: -------------------------------------------------------------------------------- 1 | #include "obstacle.h" 2 | #include "dino.h" 3 | #include "dinoSheetHelper.h" 4 | 5 | /** 6 | * 7 | * @param obs Obstacle to update 8 | * @param scrollSpeed 9 | * @param index 10 | * @return True if obstacle is out of play. 11 | */ 12 | bool updateObstacle(OBSTACLE * obs, int scrollSpeed, int index) { 13 | int speedOffset = (obs->speedOffset + obs->extraSpeed) >> SPEED_POINT_DIV; 14 | obs->extraSpeed = (obs->speedOffset + obs->extraSpeed) & SPEED_REM; 15 | 16 | obs->x -= (scrollSpeed >> SPEED_POINT_DIV) + speedOffset; 17 | obs->visible = (obs->x > -(obs->width)); 18 | if (!(obs->visible)) { 19 | return true; 20 | } 21 | 22 | if (obs->type == PTERODACTYL) { 23 | obs->frames += 1; 24 | if (obs->frames == DACTYL_FRAMES) { 25 | obs->frames = 0; 26 | obs->flap ^= 1; 27 | } 28 | } 29 | 30 | return false; 31 | } 32 | 33 | const COLLISION_BOX cactSmallBoxes[] = { 34 | {0, 7, 5, 27}, 35 | {4, 0, 6, 34}, 36 | {10, 4, 7, 14} 37 | }; 38 | 39 | const COLLISION_BOX cactLargeBoxes[] = { 40 | {0, 12, 7, 38}, 41 | {8, 0, 7, 49}, 42 | {13, 10, 10, 38} 43 | }; 44 | 45 | const COLLISION_BOX pterodactylBoxes[] = { 46 | {15, 15, 16, 5}, 47 | {18, 21, 24, 6}, 48 | {2, 14, 4, 3}, 49 | {6, 10, 4, 7}, 50 | {10, 8, 6, 9} 51 | }; 52 | 53 | const COLLISION_BOX reviveBoxes[] = { 54 | {0, 0, 32, 32} 55 | }; 56 | 57 | void createCactusSmall(OBSTACLE * obs, int speed) { 58 | obs->type = CACTUS_SMALL; 59 | obs->x = SCREEN_WIDTH; 60 | obs->y = CACTUS_SMALL_Y; 61 | obs->cactusSize = (speed >> SPEED_POINT_DIV >= CACTUS_SMALL_MULTI_SPEED) ? 62 | qran_range(1, MAX_CACTUS_SIZE + 1) : 1; 63 | obs->width = CACTUS_SMALL_WIDTH * obs->cactusSize; 64 | obs->height = CACTUS_SMALL_HEIGHT; 65 | obs->gap = qran_range(((speed) * (obs->width) + (CACTUS_GAP / 10 * 6)) >> SPEED_POINT_DIV, 66 | ((CACTUS_GAP + CACTUS_GAP / 2) >> SPEED_POINT_DIV) + 1); 67 | obs->speedOffset = 0; 68 | obs->visible = true; 69 | 70 | obs->extraSpeed = 0; 71 | 72 | obs->numBoxes = CACT_COLLISION_BOXES; 73 | obs->spriteY = obs->y + CACTUS_SMALL_Y_SPRITE_OFFSET; 74 | cloneBox(obs->colBox, cactSmallBoxes, obs->numBoxes); 75 | adjustBox(obs->colBox, obs->cactusSize, obs->width); 76 | } 77 | 78 | void createCactusLarge(OBSTACLE * obs, int speed) { 79 | obs->type = CACTUS_LARGE; 80 | obs->x = SCREEN_WIDTH; 81 | obs->y = CACTUS_LARGE_Y; 82 | obs->cactusSize = (speed >> SPEED_POINT_DIV >= CACTUS_LARGE_MULTI_SPEED) ? 83 | qran_range(1, MAX_CACTUS_SIZE + 1) : 1; 84 | obs->width = CACTUS_LARGE_WIDTH * obs->cactusSize; 85 | obs->height = CACTUS_LARGE_HEIGHT; 86 | obs->gap = qran_range((obs->width * speed + (CACTUS_GAP / 10 * 6)) >> SPEED_POINT_DIV, 87 | ((CACTUS_GAP + CACTUS_GAP / 2) >> SPEED_POINT_DIV) + 1); 88 | obs->speedOffset = 0; 89 | obs->visible = true; 90 | 91 | obs->extraSpeed = 0; 92 | 93 | obs->numBoxes = CACT_COLLISION_BOXES; 94 | obs->spriteY = CACTUS_LARGE_Y; 95 | cloneBox(obs->colBox, cactLargeBoxes, obs->numBoxes); 96 | adjustBox(obs->colBox, obs->cactusSize, obs->width); 97 | } 98 | 99 | const int dactylHeights[3] = {100,75,50}; 100 | 101 | void createPterodactyl(OBSTACLE * obs, int speed) { 102 | obs->type = PTERODACTYL; 103 | obs->x = SCREEN_WIDTH; 104 | obs->y = dactylHeights[qran_range(0,3)]; 105 | obs->flap = false; 106 | obs->width = DACTYL_WIDTH; 107 | obs->height = DACTYL_HEIGHT; 108 | obs->gap = qran_range((obs->width * speed + (DACTYL_GAP / 10 * 6)) >> SPEED_POINT_DIV, 109 | ((DACTYL_GAP + DACTYL_GAP / 2) >> SPEED_POINT_DIV) + 1); 110 | obs->speedOffset = (qran_range(0,2)) ? DACTYL_SPEED_OFFSET : -DACTYL_SPEED_OFFSET; 111 | obs->visible = true; 112 | 113 | obs->frames = 0; 114 | obs->extraSpeed = 0; 115 | 116 | obs->spriteY = obs->y; 117 | 118 | obs->numBoxes = DACTYL_COLLISION_BOXES; 119 | cloneBox(obs->colBox, pterodactylBoxes, obs->numBoxes); 120 | } 121 | 122 | void createRevive(OBSTACLE * obs, int speed) { 123 | obs->type = REVIVE; 124 | obs->x = SCREEN_WIDTH; 125 | obs->y = dactylHeights[qran_range(0,3)]; 126 | obs->width = REVIVE_WIDTH; 127 | obs->height = REVIVE_HEIGHT; 128 | obs->gap = qran_range((obs->width * speed + (REVIVE_GAP / 10 * 6)) >> SPEED_POINT_DIV, 129 | ((REVIVE_GAP + REVIVE_GAP / 2) >> SPEED_POINT_DIV) + 1); 130 | obs->speedOffset = (qran_range(0,2)) ? REVIVE_SPEED_OFFSET : -REVIVE_SPEED_OFFSET; 131 | obs->visible = true; 132 | 133 | obs->typeCategory = 0; 134 | obs->frames = 0; 135 | obs->extraSpeed = 0; 136 | 137 | obs->spriteY = obs->y; 138 | 139 | obs->numBoxes = REVIVE_COLLISION_BOXES; 140 | cloneBox(obs->colBox, reviveBoxes, obs->numBoxes); 141 | } -------------------------------------------------------------------------------- /source/game.c: -------------------------------------------------------------------------------- 1 | #include "game.h" 2 | #include "tonc.h" 3 | #include "dino.h" 4 | #include 5 | #include 6 | #include "obstacle.h" 7 | #include "save.h" 8 | #include "graphics.h" 9 | #include "input.h" 10 | 11 | //Ethan Fritz 2021, 2025 12 | 13 | extern const byte dino_soundbank_bin; 14 | const char saveType[] __attribute__((aligned(4))) = "SRAM_Vnnn"; 15 | 16 | void input(STATE * state); 17 | void updateGame(STATE * state); 18 | void gameOver(GAME_STATE * gameState, METER_STATE * meterState); 19 | 20 | STATE * initGame(); 21 | void resetGame(STATE * state); 22 | 23 | void initSound(); 24 | 25 | int main() { 26 | key_poll(); 27 | 28 | initSound(); 29 | GRAPHICS_CTX *graphicsCtx = initGraphics(); 30 | 31 | STATE * state = initGame(); 32 | 33 | // Ensure it's not optimized away 34 | char y = saveType[0]; y = y; 35 | 36 | while(1) { 37 | mmFrame(); 38 | VBlankIntrWait(); 39 | 40 | input(state); 41 | updateGame(state); 42 | drawGame(graphicsCtx, state); 43 | } 44 | } 45 | 46 | void input(STATE * state) { 47 | key_poll(); 48 | inputP1(state); 49 | 50 | if (!state->gameState.startedPlaying) { 51 | // Start game, pass input to Dino 52 | if (HIT_UP) { 53 | inputDino(&state->dinoState, &state->gameState); 54 | return; 55 | } 56 | checkModeOverride(state); 57 | } else if (state->gameState.gameOver) { 58 | if (RELEASE_UP) { 59 | if (state->dinoState.status == CRASHED && state->gameState.gameOverFrames >= RESET_FRAMES) { 60 | resetGame(state); 61 | } 62 | } 63 | } else { 64 | inputDino(&state->dinoState, &state->gameState); 65 | if (state->mode == COOP) { 66 | inputP2(state); 67 | inputDino(&state->minoState, &state->gameState); 68 | } 69 | } 70 | } 71 | 72 | void updateGame(STATE * state) { 73 | // #using state 74 | GAME_STATE * gameState = &state->gameState; 75 | DINO_STATE * dinoState = &state->dinoState; 76 | DINO_STATE * minoState = &state->minoState; 77 | HORIZON_STATE * horizonState = &state->horizonState; 78 | METER_STATE * meterState = &state->meterState; 79 | 80 | updateDino(dinoState); 81 | if (state->mode == COOP) { 82 | updateDino(minoState); 83 | } 84 | if (!gameState->startedPlaying) { 85 | if (dinoState->status == RUNNING || dinoState->status == DUCKING) { 86 | // First landing, start intro 87 | startGame(state); 88 | } else { 89 | // Waiting at title 90 | gameState->randoFrames += 1; 91 | return; 92 | } 93 | } else if (gameState->gameOver) { 94 | gameState->gameOverFrames += 1; 95 | return; 96 | } 97 | 98 | //If 3 second grace period expires, spawn obstacles 99 | //Accounts for overflow if you somehow make it 400 or so days 100 | gameState->runningFrames += 1; 101 | gameState->spawnObstacles = (gameState->runningFrames > CLEAR_FRAMES) 102 | || gameState->spawnObstacles; 103 | 104 | if (gameState->playingIntro) { 105 | gameState->curtainScroll -= 8; 106 | if (gameState->curtainScroll <= 256) { 107 | gameState->curtainScroll = 256; 108 | gameState->playingIntro = false; 109 | } 110 | REG_BG1HOFS = gameState->curtainScroll; 111 | } else { 112 | updateHorizon(state, horizonState); 113 | } 114 | 115 | OBSTACLE * collided = collisionCheck(dinoState, horizonState); 116 | bool collision = gameState->spawnObstacles && collided && dinoState->status != CRASHED; 117 | 118 | if (!collision) { 119 | addPoint(gameState->speed, &gameState->distanceRan, &gameState->distanceRanPoint); 120 | 121 | if (gameState->speed < SPEED_MAX) 122 | gameState->speed += ACCELERATION; 123 | } else { 124 | if (state->mode == COOP && collided->type == REVIVE) { 125 | despawnObstacle(collided); 126 | mmEffect(SFX_SCORE_REACHED); 127 | if (minoState->status == CRASHED) 128 | dinoRevive(minoState); 129 | } else { 130 | mmEffect(SFX_HIT); 131 | dinoCrash(dinoState); 132 | } 133 | } 134 | 135 | if (state->mode == COOP) { 136 | // Check for mino collision, decide Co-Op game over 137 | collided = collisionCheck(minoState, horizonState); 138 | collision = gameState->spawnObstacles && collided && minoState->status != CRASHED; 139 | if (collision) { 140 | if (collided->type == REVIVE) { 141 | despawnObstacle(collided); 142 | mmEffect(SFX_SCORE_REACHED); 143 | if (dinoState->status == CRASHED) 144 | dinoRevive(dinoState); 145 | } else { 146 | mmEffect(SFX_HIT); 147 | dinoCrash(minoState); 148 | } 149 | } 150 | if (state->dinoState.status == CRASHED && state->minoState.status == CRASHED) { 151 | gameOver(gameState, meterState); 152 | } 153 | } else { 154 | // Regular game over check 155 | if (state->dinoState.status == CRASHED) { 156 | gameOver(gameState, meterState); 157 | } 158 | } 159 | 160 | if (updateDistanceMeter(meterState, (gameState->distanceRan) + ((gameState->distanceRanPoint) ? 1 : 0))) 161 | mmEffect(SFX_SCORE_REACHED); 162 | 163 | updateNight(horizonState, meterState); 164 | } 165 | 166 | void gameOver(GAME_STATE * gameState, METER_STATE * meterState) { 167 | gameState->gameOver = true; 168 | gameState->gameOverFrames = 0; 169 | meterState->flashing = false; 170 | if (meterState->distance > readHiscore()) { 171 | setHiscore(meterState->distance); 172 | gameState->hiScore = meterState->distance; 173 | } 174 | } 175 | 176 | STATE * initGame() { 177 | STATE * state = calloc(sizeof(STATE), 1); 178 | initState(&state->gameState); 179 | initDino(&state->dinoState); 180 | initHorizon(&state->horizonState); 181 | initMeter(&state->meterState); 182 | 183 | if (readHiscore() == -1) 184 | setHiscore(0); 185 | state->gameState.hiScore = readHiscore(); 186 | sqran(state->gameState.hiScore); 187 | 188 | state->mode = NORMAL; 189 | return state; 190 | } 191 | 192 | void startGame(STATE * state) { 193 | state->gameState.playingIntro = true; 194 | state->gameState.startedPlaying = true; 195 | sqran(state->gameState.randoFrames); 196 | addCloud(&state->horizonState); 197 | } 198 | 199 | void resetGame(STATE * state) { 200 | resetState(&state->gameState); 201 | resetDino(&state->dinoState); 202 | if (state->mode == COOP) { 203 | resetDino(&state->minoState); 204 | } 205 | resetHorizon(&state->horizonState); 206 | resetObstacles(state->horizonState.obstacles); 207 | initMeter(&state->meterState); 208 | } 209 | 210 | void initSound() { 211 | IRQ_INIT(); 212 | 213 | irq_add(II_VBLANK, mmVBlank); 214 | irq_enable(II_VBLANK); 215 | mmInitDefault((mm_addr)&dino_soundbank_bin, 8); 216 | } -------------------------------------------------------------------------------- /source/dino.c: -------------------------------------------------------------------------------- 1 | #include "dino.h" 2 | #include 3 | #include 4 | #include "tonc.h" 5 | #include "hitbox.h" 6 | #include "obstacle.h" 7 | #include "input.h" 8 | 9 | const COLLISION_BOX dinoBoxes[] = { 10 | {22, 0, 17, 16}, 11 | {1, 18, 30, 9}, 12 | {10, 35, 14, 8}, 13 | {1, 24, 29, 5}, 14 | {5, 30, 21, 4}, 15 | {9, 34, 15, 4} 16 | }; 17 | 18 | const COLLISION_BOX duckBoxes[] = { 19 | {1, 18, 55, 25} 20 | }; 21 | 22 | void dinoJump(DINO_STATE * dinoState, GAME_STATE * gameState); 23 | void updateJump(DINO_STATE * dinoState); 24 | void updateEntry(DINO_STATE * dinoState); 25 | void endJump(DINO_STATE * dinoState); 26 | void updateBlink(DINO_STATE * dinoState); 27 | void dinoRun(DINO_STATE * dinoState); 28 | void dinoDuck(DINO_STATE * dinoState); 29 | 30 | int getBlinkTime(); 31 | 32 | void updateDino(DINO_STATE * dinoState) { 33 | if (dinoState->status == JUMPING) 34 | updateJump(dinoState); 35 | if (dinoState->status == WAITING) 36 | updateBlink(dinoState); 37 | if (dinoState->status == ENTERING) 38 | updateEntry(dinoState); 39 | 40 | dinoState->frameCounter += 1; 41 | if ((DINO_ANIMATING) && (dinoState->frameCounter >= dinoState->frameTime)) { 42 | dinoState->frame = dinoState->frame == 1 ? 0 : 1; 43 | dinoState->frameCounter = 0; 44 | } 45 | } 46 | 47 | void inputDino(DINO_STATE * dinoState, GAME_STATE * gameState) { 48 | if (dinoState->status == CRASHED || dinoState->status == ENTERING) { 49 | return; 50 | } 51 | 52 | if (HIT_UP && (dinoState->status == WAITING || dinoState->status == RUNNING)) { 53 | dinoJump(dinoState, gameState); 54 | return; 55 | } else if (RELEASE_UP) { 56 | if (dinoState->status == JUMPING) 57 | endJump(dinoState); 58 | } 59 | 60 | if (HIT_DOWN) { 61 | if (dinoState->status == JUMPING) { 62 | dinoState->speedDrop = true; 63 | dinoState->jumpVelocity = -1; 64 | } else if (dinoState->status == RUNNING) { 65 | dinoDuck(dinoState); 66 | } 67 | } else if (RELEASE_DOWN) { 68 | if (dinoState->status == JUMPING) { 69 | dinoState->speedDrop = false; 70 | } else if (dinoState->status == DUCKING) { 71 | dinoRun(dinoState); 72 | } 73 | } 74 | } 75 | 76 | void dinoJump(DINO_STATE * dinoState, GAME_STATE * gameState) { 77 | dinoState->status = JUMPING; 78 | dinoState->speedDrop = false; 79 | dinoState->jumpVelocity = INITIAL_JUMP_VELOCITY + gameState->speed / SPEED_POINT / 10; 80 | dinoState->reachedMin = false; 81 | dinoState->blinking = false; 82 | mmEffect(SFX_BUTTON_PRESSED); 83 | } 84 | 85 | void updateJump(DINO_STATE * dinoState) { 86 | if (dinoState->speedDrop) 87 | dinoState->yPos += (dinoState->jumpVelocity * SPEED_DROP_COEFFICIENT) / 10; 88 | else 89 | dinoState->yPos += (dinoState->jumpVelocity) / 10; 90 | 91 | dinoState->jumpVelocity += GRAVITY; 92 | 93 | if (dinoState->yPos > MIN_JUMP_HEIGHT || dinoState->speedDrop) 94 | dinoState->reachedMin = true; 95 | if (dinoState->yPos > MAX_JUMP_HEIGHT || dinoState->speedDrop) 96 | endJump(dinoState); 97 | 98 | if (dinoState->yPos < DINO_GROUND_Y) { 99 | bool drop = dinoState->speedDrop; 100 | dinoRun(dinoState); 101 | if (drop) 102 | dinoDuck(dinoState); 103 | 104 | //if first landing, pull back the curtain 105 | dinoState->jumped |= !dinoState->jumped; 106 | } 107 | } 108 | 109 | void updateEntry(DINO_STATE * dinoState) { 110 | dinoState->yPos += (dinoState->jumpVelocity) / 10; 111 | dinoState->jumpVelocity += GRAVITY; 112 | 113 | if (dinoState->yPos > MIN_JUMP_HEIGHT || dinoState->speedDrop) 114 | dinoState->reachedMin = true; 115 | if (dinoState->yPos > MAX_JUMP_HEIGHT || dinoState->speedDrop) 116 | endJump(dinoState); 117 | 118 | if (dinoState->yPos < DINO_GROUND_Y && dinoState->reachedMin) { 119 | bool drop = dinoState->speedDrop; 120 | dinoRun(dinoState); 121 | if (drop) 122 | dinoDuck(dinoState); 123 | 124 | //if first landing, pull back the curtain 125 | dinoState->jumped |= !dinoState->jumped; 126 | } 127 | } 128 | 129 | void endJump(DINO_STATE * dinoState) { 130 | if (dinoState->reachedMin && dinoState->jumpVelocity < DROP_VELOCITY) 131 | dinoState->jumpVelocity = DROP_VELOCITY; 132 | } 133 | 134 | void updateBlink(DINO_STATE * dinoState) { 135 | if (!(dinoState->blinking)) { 136 | if (dinoState->blinkFrame == 0) { 137 | if (dinoState->blinks >= MAX_BLINKS) 138 | return; 139 | dinoState->blinkTime = getBlinkTime(); 140 | } 141 | dinoState->blinkFrame += 1; 142 | if (dinoState->blinkFrame >= dinoState->blinkTime) { 143 | dinoState->blinking = true; 144 | dinoState->blinkFrame = 0; 145 | } 146 | } else { 147 | dinoState->blinkFrame += 1; 148 | if (dinoState->blinkFrame >= BLINK_TIME) { 149 | dinoState->blinking = false; 150 | dinoState->blinkFrame = 0; 151 | dinoState->blinks += 1; 152 | } 153 | } 154 | } 155 | 156 | int getBlinkTime() { 157 | return qran_range(0, MAX_BLINK_DELAY); 158 | } 159 | 160 | void dinoRun(DINO_STATE * dinoState) { 161 | dinoState->frame = 0; 162 | dinoState->frameCounter = 0; 163 | dinoState->frameTime = RUN_FRAME; 164 | 165 | dinoState->status = RUNNING; 166 | dinoState->speedDrop = false; 167 | 168 | dinoState->jumpVelocity = 0; 169 | dinoState->yPos = DINO_GROUND_Y; 170 | } 171 | 172 | void dinoDuck(DINO_STATE * dinoState) { 173 | dinoState->frame = 0; 174 | dinoState->frameCounter = 0; 175 | dinoState->frameTime = DUCK_FRAME; 176 | 177 | dinoState->status = DUCKING; 178 | } 179 | 180 | void resetDino(DINO_STATE * dino) { 181 | dino->yPos = DINO_GROUND_Y; 182 | dino->jumpVelocity = 0; 183 | 184 | dino->status = RUNNING; 185 | dino->speedDrop = false; 186 | dino->jumped = true; 187 | dino->reachedMin = false; 188 | 189 | dino->frame = 0; 190 | dino->frameCounter = 0; 191 | dino->frameTime = RUN_FRAME; 192 | } 193 | 194 | void addPoint(int add, int *base, int *point) { 195 | *base += (add >> SPEED_POINT_DIV); 196 | *point += (add & SPEED_REM); 197 | if (*point >> SPEED_POINT_DIV) { 198 | *base += (*point >> SPEED_POINT_DIV); 199 | *point = *point & SPEED_REM; 200 | } 201 | } 202 | 203 | OBSTACLE * collisionCheck(DINO_STATE * dinoState, HORIZON_STATE * horizonState) { 204 | int tW = ((dinoState->status == DUCKING) ? DINO_WIDTH_DUCK : DINO_WIDTH) - 2; 205 | int tH = ((dinoState->status == DUCKING) ? DINO_HEIGHT_DUCK : DINO_HEIGHT) - 2; 206 | int tX = dinoState->xPos + 1; 207 | int tY = SCREEN_HEIGHT - dinoState->yPos - tH - 1; 208 | 209 | COLLISION_BOX dBox = {tX, tY, tW, tH}; 210 | COLLISION_BOX oBox = {0,0,0,0}; 211 | 212 | for (int i = 0; i < MAX_OBSTACLES; i++) { 213 | OBSTACLE *obs = (horizonState->obstacles + i); 214 | if (obs->visible) { 215 | oBox.w = obs->width - 2; 216 | oBox.h = obs->height - 2; 217 | oBox.x = obs->x + 1; 218 | oBox.y = obs->spriteY + 1; 219 | 220 | if (boxCheck(&dBox, &oBox)) 221 | { 222 | if (dinoState->status == DUCKING) { 223 | for (int k = 0; k < obs->numBoxes; k++) { 224 | if (boxCheckOffset(duckBoxes, (obs->colBox + k), 225 | tX, tY, oBox.x, oBox.y)) { 226 | return obs; 227 | } 228 | } 229 | } else { 230 | for (int i = 0; i < DINO_COLLISION_BOXES; i++) { 231 | for (int k = 0; k < obs->numBoxes; k++) { 232 | if (boxCheckOffset(dinoBoxes + i, (obs->colBox + k), 233 | tX, tY, oBox.x, oBox.y)) { 234 | return obs; 235 | } 236 | } 237 | } 238 | } 239 | } 240 | } 241 | } 242 | 243 | return 0; 244 | } 245 | 246 | 247 | -------------------------------------------------------------------------------- /source/horizon.c: -------------------------------------------------------------------------------- 1 | #include "horizon.h" 2 | #include "dinoSheetHelper.h" 3 | #include "dino.h" 4 | 5 | #define MIN_CLOUD_GAP 100 6 | #define MAX_CLOUD_GAP 400 7 | #define MIN_SKY_LEVEL 71 8 | #define MAX_SKY_LEVEL 30 9 | 10 | #define STAR_MAX_Y 70 11 | #define STAR_SPEED 3 12 | #define STAR_MOVE_THRESHOLD 10 13 | 14 | #define FADE_SKY_FRAMES 29 15 | 16 | #define MOON_SPEED 25 17 | #define MOON_MOVE_THRESHOLD 100 18 | #define MOON_WIDTH 40 19 | 20 | #define INVERT_DISTANCE 700 21 | #define INVERT_FADE_DURATION 720 22 | 23 | bool randomBool(); 24 | 25 | void initCloud(CLOUD * cloud, int cloudNum); 26 | bool updateCloud(CLOUD * cloud); 27 | 28 | void placeStars(HORIZON_STATE * horizonState); 29 | 30 | void updateObstacles(STATE * state, HORIZON_STATE * horizonState, int scrollSpeed); 31 | void addObstacle(HORIZON_STATE * horizonState, int speed, bool canSpawnRevive); 32 | 33 | void updateHorizon(STATE * state, HORIZON_STATE * horizonState) { 34 | GAME_STATE * gameState = &state->gameState; 35 | int scrollSpeed = (gameState->speed + horizonState->extraScroll); 36 | int scrolled = scrollSpeed >> SPEED_POINT_DIV; 37 | horizonState->extraScroll = (gameState->speed + horizonState->extraScroll) & SPEED_REM; 38 | horizonState->scroll += scrolled; 39 | horizonState->scrolled += scrolled; 40 | 41 | //horizon terrain 42 | if (horizonState->scrolled >= TILE_SIZE) { 43 | 44 | horizonState->terrainScroll += (horizonState->scrolled >> 3); 45 | if (horizonState->terrainScroll >= TERRAIN_STRIP_LENGTH) { 46 | horizonState->bumpy = randomBool(); 47 | horizonState->terrainScroll %= TERRAIN_STRIP_LENGTH; 48 | } 49 | 50 | for (int i = 0; i < (horizonState->scrolled >> 3); i++) { 51 | updateHorizonTile(horizonState->nextScrollTile, horizonState->terrainScroll, horizonState->bumpy); 52 | horizonState->nextScrollTile++; 53 | horizonState->nextScrollTile &= 0x1F; 54 | } 55 | 56 | horizonState->scrolled &= 0x7; 57 | } 58 | 59 | //clouds 60 | if (horizonState->cloudCount) { 61 | int tempCursor = horizonState->cloudCursor; 62 | int lastCursor = 0; 63 | int preCount = horizonState->cloudCount; 64 | for (int i = 0; i < preCount; i++) { 65 | lastCursor = tempCursor; 66 | if (updateCloud((horizonState->clouds) + tempCursor)) { 67 | horizonState->cloudCursor = (horizonState->cloudCursor + 1) % MAX_CLOUDS; 68 | horizonState->cloudCount -= 1; 69 | } 70 | tempCursor = (tempCursor + 1) % MAX_CLOUDS; 71 | } 72 | 73 | if ((horizonState->cloudCount < MAX_CLOUDS && 74 | ((SCREEN_WIDTH - ((horizonState->clouds + lastCursor)->xPos)) > 75 | (horizonState->clouds + lastCursor)->cloudGap)) && randomBool()) { 76 | addCloud(horizonState); 77 | } 78 | } else { 79 | addCloud(horizonState); 80 | } 81 | 82 | //night 83 | if (horizonState->inverting) { 84 | horizonState->invertFrame += 1; 85 | bool invertOver = horizonState->invertFrame >= INVERT_FRAMES; 86 | inversionUpdate(horizonState->night, invertOver); 87 | 88 | if (invertOver) { 89 | horizonState->inverting = false; 90 | horizonState->invertFrame = 0; 91 | } 92 | } 93 | 94 | if (horizonState->fading) { 95 | int truOpac = horizonState->opacity / 2; 96 | REG_BLDALPHA = BLDA_BUILD(truOpac, 16 - truOpac); 97 | REG_BLDY= BLDY_BUILD(16 - truOpac); 98 | 99 | if (horizonState->night) 100 | horizonState->opacity += 1; 101 | else 102 | horizonState->opacity -= 1; 103 | 104 | if (horizonState->opacity == 32 || horizonState->opacity == 0) 105 | horizonState->fading = false; 106 | } 107 | 108 | if (horizonState->night || horizonState->fading) { 109 | horizonState->starMov += STAR_SPEED; 110 | horizonState->moonMov += MOON_SPEED; 111 | 112 | if (horizonState->starMov >= STAR_MOVE_THRESHOLD) { 113 | horizonState->starMov -= STAR_MOVE_THRESHOLD; 114 | horizonState->star0X -= 1; 115 | horizonState->star1X -= 1; 116 | } else { 117 | if (horizonState->star0X < -SCREEN_WIDTH) 118 | horizonState->star0X = SCREEN_WIDTH; 119 | if (horizonState->star1X < -SCREEN_WIDTH) 120 | horizonState->star1X = SCREEN_WIDTH; 121 | } 122 | 123 | if (horizonState->moonMov >= MOON_MOVE_THRESHOLD) { 124 | horizonState->moonX -= 1; 125 | horizonState->moonMov = 0; 126 | } else if (horizonState->moonX < -MOON_WIDTH) { 127 | horizonState->moonX = SCREEN_WIDTH; 128 | } 129 | } 130 | 131 | //obstacles 132 | if (gameState->spawnObstacles) 133 | updateObstacles(state, horizonState, scrollSpeed); 134 | 135 | 136 | REG_BG0HOFS = horizonState->scroll; 137 | } 138 | 139 | void updateObstacles(STATE * state, HORIZON_STATE * horizonState, int scrollSpeed) { 140 | int gameSpeed = state->gameState.speed; 141 | 142 | for (int i = 0; i < MAX_OBSTACLES; i++) { 143 | OBSTACLE * obs = (horizonState->obstacles + i); 144 | if (obs->visible) { 145 | horizonState->obstacleCount -= updateObstacle(obs, scrollSpeed, i);; 146 | } 147 | } 148 | 149 | bool canRevive = state->mode == COOP 150 | && (state->dinoState.status == CRASHED || state->minoState.status == CRASHED); 151 | 152 | if (horizonState->obstacleCount > 0) { 153 | OBSTACLE *lastObs = (horizonState->obstacles + (horizonState->lastObstacle)); 154 | if ((horizonState->obstacleCount < MAX_OBSTACLES) && 155 | (((lastObs->gap) + (lastObs->x) + (lastObs->width)) < SCREEN_WIDTH)) { 156 | addObstacle(horizonState, gameSpeed, canRevive); 157 | } 158 | } else { 159 | addObstacle(horizonState, gameSpeed, canRevive); 160 | } 161 | } 162 | 163 | void addObstacle(HORIZON_STATE * horizonState, int speed, bool canSpawnRevive) { 164 | OBSTACLE *obs = (OBSTACLE*)(horizonState->obstacles + (horizonState->obstacleCursor)); 165 | 166 | bool reviveInPool = canSpawnRevive && (qran() > 0x6800); 167 | 168 | switch (qran_range(true ^ reviveInPool,(OBSTACLE_TYPE_COUNT - (speed < DACTYL_MIN_SPEED)))) { 169 | case REVIVE: 170 | createRevive(obs, speed); 171 | break; 172 | case CACTUS_SMALL: 173 | createCactusSmall(obs, speed); 174 | break; 175 | case CACTUS_LARGE: 176 | createCactusLarge(obs, speed); 177 | break; 178 | case PTERODACTYL: 179 | createPterodactyl(obs, speed); 180 | break; 181 | } 182 | horizonState->obstacleCount += 1; 183 | horizonState->lastObstacle = horizonState->obstacleCursor; 184 | horizonState->obstacleCursor += 1; 185 | if (horizonState->obstacleCursor >= MAX_OBSTACLES) 186 | horizonState->obstacleCursor = 0; 187 | } 188 | 189 | void updateNight(HORIZON_STATE * horizonState, METER_STATE * meterState) { 190 | if (!(horizonState->night) && meterState->invertCounter >= INVERT_DISTANCE) { 191 | meterState->invertCounter -= INVERT_DISTANCE; 192 | horizonState->night = true; 193 | horizonState->inverting = true; 194 | horizonState->fading = true; 195 | placeStars(horizonState); 196 | horizonState->moonPhase++; 197 | horizonState->moonPhase -= (horizonState->moonPhase == 7) * 7; 198 | } else if ((horizonState->night) && horizonState->invertTimer >= INVERT_FADE_DURATION) { 199 | horizonState->invertTimer = 0; 200 | horizonState->night = false; 201 | horizonState->inverting = true; 202 | horizonState->fading = true; 203 | } else if (horizonState->night) { 204 | horizonState->invertTimer += 1; 205 | } 206 | } 207 | 208 | void placeStars(HORIZON_STATE * horizonState) { 209 | horizonState->star0X = qran_range(0, SCREEN_WIDTH / 2); 210 | horizonState->star0Y = qran_range(0, STAR_MAX_Y); 211 | horizonState->star1X = qran_range(SCREEN_WIDTH / 2, SCREEN_WIDTH); 212 | horizonState->star1Y = qran_range(0, STAR_MAX_Y); 213 | } 214 | 215 | bool randomBool() { 216 | return ((qran_range(0, 1000) > 500) ? true : false); 217 | } 218 | 219 | void initHorizon(HORIZON_STATE * horizon) { 220 | horizon->nextScrollTile = 31; 221 | horizon->terrainScroll = 31; 222 | horizon->bumpy = false; 223 | 224 | horizon->clouds = malloc(MAX_CLOUDS * sizeof(CLOUD)); 225 | 226 | horizon->night = false; 227 | horizon->inverting = false; 228 | horizon->fading = false; 229 | 230 | horizon->moonPhase = -1; 231 | horizon->moonX = SCREEN_WIDTH - 50; 232 | 233 | horizon->obstacles = malloc(MAX_OBSTACLES * sizeof(OBSTACLE)); 234 | for (int i = 0; i < MAX_OBSTACLES; i++) { 235 | (horizon->obstacles + i)->colBox = malloc(MAX_HITBOXES * sizeof(COLLISION_BOX)); 236 | (horizon->obstacles + i)->visible = false; 237 | } 238 | } 239 | 240 | void resetHorizon(HORIZON_STATE * horizon) { 241 | horizon->scroll = 0; 242 | horizon->nextScrollTile = 31; 243 | horizon->scrolled = 0; 244 | horizon->terrainScroll = 31; 245 | horizon->bumpy = false; 246 | horizon->extraScroll = 0; 247 | 248 | if (horizon->night) { 249 | horizon->night = false; 250 | horizon->inverting = true; 251 | horizon->fading = true; 252 | } 253 | 254 | horizon->obstacleCount = 0; 255 | horizon->obstacleCursor = 0; 256 | horizon->lastObstacle = 0; 257 | } 258 | 259 | void initCloud(CLOUD * cloud, int cloudNum) { 260 | cloud->xPos = SCREEN_WIDTH; 261 | cloud->yPos = qran_range(MIN_SKY_LEVEL, MAX_SKY_LEVEL); 262 | cloud->cloudGap = qran_range(MIN_CLOUD_GAP, MAX_CLOUD_GAP); 263 | cloud->cloudNum = cloudNum; 264 | } 265 | 266 | void addCloud(HORIZON_STATE * horizonState) { 267 | int newCursor = (horizonState->cloudCursor + horizonState->cloudCount) % MAX_CLOUDS; 268 | horizonState->cloudCount += 1; 269 | initCloud(((horizonState->clouds) + newCursor), newCursor); 270 | } 271 | 272 | // Return true if cloud is no longer visible 273 | bool updateCloud(CLOUD * cloud) { 274 | cloud->xPos -= 1; 275 | return ((cloud->xPos) <= -CLOUD_WIDTH); 276 | } -------------------------------------------------------------------------------- /source/graphics/graphics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "graphics.h" 5 | #include "tonc.h" 6 | #include "dinoSheetHelper.h" 7 | 8 | const int animRun[] = {dinoFeet1_SI, dinoFeet2_SI}; 9 | const int animDuc[] = {dinoCrouchFeet0_SI, dinoCrouchFeet1_SI}; 10 | const int animAng[] = { dinoAngelSI, dinoFlapSI}; 11 | 12 | #define PAL_BG 0 13 | #define PAL_OBJ0 0 14 | #define PAL_OBJ1 0 15 | #define PAL_TITLE 1 16 | 17 | #define SCORE_Y 10 18 | #define HI_X 74 19 | #define HISCORE_X 107 20 | #define SCORE_X 174 21 | 22 | OBJ_ATTR obj_buffer[128]; 23 | 24 | void initGraphicsMem(); 25 | 26 | void drawTitle(GRAPHICS_CTX * ctx) { 27 | obj_set_attr(CTX_OAM++, Y(40) | ATTR0_WIDE, 28 | ATTR1_SIZE_64, titleLeftSI | ATTR2_PALBANK(PAL_TITLE)); 29 | obj_set_attr(CTX_OAM++, Y(40) | ATTR0_WIDE, 30 | 64 | ATTR1_SIZE_64, titleRightSI | ATTR2_PALBANK(PAL_TITLE)); 31 | } 32 | 33 | void drawReplay(GRAPHICS_CTX * ctx) { 34 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(70), 35 | ATTR1_SIZE_32 | X(102), 36 | replaySI | ATTR2_PALBANK(PAL_OBJ0)); 37 | obj_set_attr(CTX_OAM++, ATTR0_TALL | Y(70), 38 | ATTR1_SIZE_16 | ATTR1_HFLIP | X(102 + 32), 39 | replayTailSI | ATTR2_PALBANK(PAL_OBJ0)); 40 | } 41 | 42 | #define GOVR_CHAR(si, x) \ 43 | obj_set_attr(CTX_OAM++, Y(charY) | ATTR0_SQUARE, \ 44 | X(x) | ATTR1_SIZE_16, \ 45 | si | ATTR2_PALBANK(PAL_OBJ0)) 46 | void drawGameover(GRAPHICS_CTX * ctx) { 47 | GOVR_CHAR(charG_SI, charXg); 48 | GOVR_CHAR(charA_SI, charXa); 49 | GOVR_CHAR(charM_SI, charXm); 50 | GOVR_CHAR(charE_SI, charXe); 51 | GOVR_CHAR(charO_SI, charXo); 52 | GOVR_CHAR(charV_SI, charXv); 53 | GOVR_CHAR(charE_SI, charXe2); 54 | GOVR_CHAR(charR_SI, charXr); 55 | } 56 | 57 | void drawHi(GRAPHICS_CTX * ctx) { 58 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(SCORE_Y), 59 | ATTR1_SIZE_16 | X(HI_X), 60 | hiSI | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 61 | obj_set_attr(CTX_OAM++, ATTR0_TALL | Y(SCORE_Y), 62 | ATTR1_SIZE_8 | X(HI_X + 16), 63 | (hiSI + 2) | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 64 | } 65 | 66 | void drawNumber(GRAPHICS_CTX * ctx, u32 posX, u32 number) { 67 | u32 digit = numToSI(number % 10); 68 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(SCORE_Y), 69 | ATTR1_SIZE_16 | X(posX + 44), 70 | digit | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 71 | number /= 10; 72 | digit = numToSI(number % 10); 73 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(SCORE_Y), 74 | ATTR1_SIZE_16 | X(posX + 33), 75 | digit | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 76 | number /= 10; 77 | digit = numToSI(number % 10); 78 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(SCORE_Y), 79 | ATTR1_SIZE_16 | X(posX + 22), 80 | digit | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 81 | number /= 10; 82 | digit = numToSI(number % 10); 83 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(SCORE_Y), 84 | ATTR1_SIZE_16 | X(posX + 11), 85 | digit | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 86 | number /= 10; 87 | digit = numToSI(number % 10); 88 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(SCORE_Y), 89 | ATTR1_SIZE_16 | X(posX), 90 | digit | ATTR2_PALBANK(PAL_OBJ0) | ATTR2_PRIO(1)); 91 | } 92 | 93 | /** 94 | * 0-31 95 | */ 96 | int floating(int x) { 97 | int t; 98 | if (x < 24) { 99 | t = x; 100 | return (t * (24 - t)) / 12; 101 | } else { 102 | t = x - 24; 103 | return -(t * (24 - t)) / 12; 104 | } 105 | } 106 | 107 | void drawDino(GRAPHICS_CTX * ctx, DINO_STATE * dinoState, MODE mode) { 108 | int x = dinoState->xPos; 109 | int y = CTX_H - 48 - dinoState->yPos; 110 | int crouchY = CTX_H - 32 - dinoState->yPos; 111 | switch (dinoState->status) { 112 | case CRASHED: 113 | if (mode == COOP) { 114 | u32 frameCounter = dinoState->frameCounter; 115 | x += 16; 116 | y += floating(frameCounter); 117 | // Angel view 118 | obj_set_attr(CTX_OAM++, ATTR0_TALL | Y(y), 119 | ATTR1_SIZE_32 | X(x), 120 | (dinoAngelSI - ((frameCounter & 0xF) > 12) * 2) | ATTR2_PALBANK(PAL_OBJ0)); 121 | obj_set_attr(CTX_OAM++, ATTR0_TALL | Y(y), 122 | ATTR1_SIZE_16 | X(x + 16), 123 | dinoAngelHeadSI | ATTR2_PALBANK(PAL_OBJ0)); 124 | } else { 125 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y), 126 | ATTR1_SIZE_64 | X(x - 22), 127 | deadDinoSI | ATTR2_PALBANK(PAL_OBJ0)); 128 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y + 16), 129 | ATTR1_SIZE_64 | X(x - 22), 130 | (deadDinoSI + (32 * 2)) | ATTR2_PALBANK(PAL_OBJ0)); 131 | } 132 | break; 133 | case DUCKING: 134 | if (dinoState->hat) { 135 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y + 10), 136 | ATTR1_SIZE_64 | X(x + 29), 137 | dinoHatSI | ATTR2_PALBANK(PAL_OBJ0)); 138 | } 139 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(crouchY), 140 | ATTR1_SIZE_64 | X(x), 141 | dinoCrouchSI | ATTR2_PALBANK(PAL_OBJ0)); 142 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(crouchY + 16), 143 | ATTR1_SIZE_32 | X(x + 8), 144 | animDuc[dinoState->frame] | ATTR2_PALBANK(PAL_OBJ0)); 145 | break; 146 | case RUNNING: 147 | if (dinoState->hat) { 148 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y - 9), 149 | ATTR1_SIZE_64 | X(x + 14), 150 | dinoHatSI | ATTR2_PALBANK(PAL_OBJ0)); 151 | } 152 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y), 153 | ATTR1_SIZE_32 | X(x + 16), 154 | dinoHeadSI | ATTR2_PALBANK(PAL_OBJ0)); 155 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y + 16), 156 | ATTR1_SIZE_16 | X(x), 157 | dinoTailSI | ATTR2_PALBANK(PAL_OBJ0)); 158 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y + 32), 159 | ATTR1_SIZE_32 | X(x), 160 | animRun[dinoState->frame] | ATTR2_PALBANK(PAL_OBJ0)); 161 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_HIDE | Y(y), 162 | ATTR1_SIZE_8 | X(x + 24), 163 | dinoWinkSI | ATTR2_PALBANK(PAL_OBJ0)); 164 | break; 165 | case WAITING: 166 | case JUMPING: 167 | case ENTERING: 168 | if (dinoState->hat) { 169 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y - 9), 170 | ATTR1_SIZE_64 | X(x + 14), 171 | dinoHatSI | ATTR2_PALBANK(PAL_OBJ0)); 172 | } 173 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | (ATTR0_HIDE * (!dinoState->blinking)) | Y(y), 174 | ATTR1_SIZE_8 | X(x + 24), 175 | dinoWinkSI | ATTR2_PALBANK(PAL_OBJ0)); 176 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y), 177 | ATTR1_SIZE_32 | X(x + 16), 178 | dinoHeadSI | ATTR2_PALBANK(PAL_OBJ0)); 179 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y + 16), 180 | ATTR1_SIZE_16 | X(x), 181 | dinoTailSI | ATTR2_PALBANK(PAL_OBJ0)); 182 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(y + 32), 183 | ATTR1_SIZE_32 | X(x), 184 | dinoFeet0_SI | ATTR2_PALBANK(PAL_OBJ0)); 185 | break; 186 | } 187 | } 188 | 189 | #define TYPEM 10 190 | #define CACTUS_SMALL_SIZE(s) (CACTUS_SMALL * TYPEM + s) 191 | #define CACTUS_LARGE_SIZE(s) (CACTUS_LARGE * TYPEM + s) 192 | #define PTERODACTYL_FLAP(flap) (PTERODACTYL * TYPEM + flap) 193 | #define REVIVE_ (REVIVE * TYPEM) 194 | 195 | void drawObstacle(GRAPHICS_CTX * ctx, OBSTACLE * obstacle) { 196 | s32 x = obstacle->x; 197 | s32 y = obstacle->y; 198 | 199 | switch (obstacle->type * TYPEM + obstacle->typeCategory) { 200 | case CACTUS_SMALL_SIZE(1): 201 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y), 202 | ATTR1_SIZE_32 | X(x - 15), 203 | smallCactusSingleSI | ATTR2_PALBANK(PAL_OBJ0)); 204 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y + 32), 205 | ATTR1_SIZE_8 | X(x + 5), 206 | smallCactusStumpSI | ATTR2_PALBANK(PAL_OBJ0)); 207 | break; 208 | case CACTUS_SMALL_SIZE(2): 209 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(CACTUS_SMALL_Y), 210 | ATTR1_SIZE_64 | X(x - 15), 211 | smallCactusDoubleSI | ATTR2_PALBANK(PAL_OBJ0)); 212 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y + 32), 213 | ATTR1_SIZE_8 | X(x + 5), 214 | smallCactusStumpSI | ATTR2_PALBANK(PAL_OBJ0)); 215 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y + 32), 216 | ATTR1_SIZE_8 | X(x + 22), 217 | smallCactusStumpSI | ATTR2_PALBANK(PAL_OBJ0)); 218 | break; 219 | case CACTUS_SMALL_SIZE(3): 220 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(CACTUS_SMALL_Y), 221 | ATTR1_SIZE_64 | X(x - 8), 222 | smallCactusTripleSI | ATTR2_PALBANK(PAL_OBJ0)); 223 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y + 32), 224 | ATTR1_SIZE_8 | X(x + 5), 225 | smallCactusStumpSI | ATTR2_PALBANK(PAL_OBJ0)); 226 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y + 32), 227 | ATTR1_SIZE_8 | X(x + 22), 228 | smallCactusStumpSI | ATTR2_PALBANK(PAL_OBJ0)); 229 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_SMALL_Y + 32), 230 | ATTR1_SIZE_8 | X(x + 39), 231 | smallCactusStumpSI | ATTR2_PALBANK(PAL_OBJ0)); 232 | break; 233 | case CACTUS_LARGE_SIZE(1): 234 | obj_set_attr(CTX_OAM++, ATTR0_TALL | Y(CACTUS_LARGE_Y), 235 | ATTR1_SIZE_64 | X(x), 236 | bigCactusSingleSI | ATTR2_PALBANK(PAL_OBJ0)); 237 | break; 238 | case CACTUS_LARGE_SIZE(2): 239 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_LARGE_Y), 240 | ATTR1_SIZE_64 | X(x), 241 | bigCactusDoubleSI | ATTR2_PALBANK(PAL_OBJ0)); 242 | break; 243 | case CACTUS_LARGE_SIZE(3): 244 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(CACTUS_LARGE_Y), 245 | ATTR1_SIZE_64 | X(x), 246 | bigCactusTripleSI | ATTR2_PALBANK(PAL_OBJ0)); 247 | obj_set_attr(CTX_OAM++, ATTR0_TALL | Y(CACTUS_LARGE_Y), 248 | ATTR1_SIZE_64 | X(x + 64), 249 | bigCactusTripleRunoffSI | ATTR2_PALBANK(PAL_OBJ0)); 250 | break; 251 | case PTERODACTYL_FLAP(false): 252 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y), 253 | ATTR1_SIZE_16 | X(x), 254 | birdBeakSI | ATTR2_PALBANK(PAL_OBJ0)); 255 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y - 4), 256 | ATTR1_SIZE_32 | X(x + 16), 257 | birdFlapUpSI | ATTR2_PALBANK(PAL_OBJ0)); 258 | break; 259 | case PTERODACTYL_FLAP(true): 260 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y), 261 | ATTR1_SIZE_16 | X(x), 262 | birdBeakSI | ATTR2_PALBANK(PAL_OBJ0)); 263 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y + 4), 264 | ATTR1_SIZE_32 | X(x + 16), 265 | birdFlapDownSI | ATTR2_PALBANK(PAL_OBJ0)); 266 | break; 267 | case REVIVE_: 268 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(y), 269 | ATTR1_SIZE_32 | X(x), 270 | anoNikuSI | ATTR2_PALBANK(PAL_OBJ1)); 271 | } 272 | } 273 | 274 | void drawCloud(GRAPHICS_CTX * ctx, CLOUD * cloud) { 275 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | Y(cloud->yPos), 276 | ATTR1_SIZE_32 | X(cloud->xPos), 277 | cloudLeftSI | ATTR2_PALBANK(PAL_OBJ1)); 278 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | Y(cloud->yPos), 279 | ATTR1_SIZE_16 | X(cloud->xPos + 32), 280 | cloudRightSI | ATTR2_PALBANK(PAL_OBJ1)); 281 | } 282 | 283 | void drawStars(GRAPHICS_CTX * ctx, HORIZON_STATE * horizonState) { 284 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(horizonState->star0Y), 285 | ATTR1_SIZE_16 | X(horizonState->star0X), 286 | star0_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 287 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(horizonState->star1Y), 288 | ATTR1_SIZE_16 | X(horizonState->star1X), 289 | star1_SI | ATTR2_PRIO(2) |ATTR2_PALBANK(PAL_OBJ1)); 290 | } 291 | 292 | void drawMoon(GRAPHICS_CTX * ctx, HORIZON_STATE * horizonState) { 293 | switch(horizonState->moonPhase) { 294 | case 0: 295 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y), 296 | ATTR1_SIZE_32 | ATTR1_HFLIP | X(horizonState->moonX), 297 | moon1_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 298 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 299 | ATTR1_SIZE_32 | ATTR1_HFLIP | ATTR1_VFLIP | X(horizonState->moonX), 300 | moon1_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 301 | break; 302 | case 1: 303 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y), 304 | ATTR1_SIZE_32 | ATTR1_HFLIP | X(horizonState->moonX), 305 | moon2_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 306 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 307 | ATTR1_SIZE_32 | ATTR1_HFLIP | ATTR1_VFLIP | X(horizonState->moonX), 308 | moon2_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 309 | break; 310 | case 2: 311 | obj_set_attr(CTX_OAM++, ATTR0_TALL | ATTR0_BLEND | Y(MOON_Y), 312 | ATTR1_SIZE_32 | X(horizonState->moonX), 313 | moon2_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 314 | obj_set_attr(CTX_OAM++, ATTR0_TALL | ATTR0_BLEND | Y(MOON_Y + 8), 315 | ATTR1_SIZE_32 | ATTR1_VFLIP | X(horizonState->moonX), 316 | moon2_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 317 | obj_set_attr(CTX_OAM++, ATTR0_WIDE | ATTR0_BLEND | Y(MOON_Y), 318 | ATTR1_SIZE_32 | X(horizonState->moonX + 8), 319 | fullMoonTopSI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 320 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 321 | ATTR1_SIZE_32 | X(horizonState->moonX + 8), 322 | fullMoonSI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 323 | break; 324 | case 3: 325 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y), 326 | ATTR1_SIZE_32 | X(horizonState->moonX), 327 | moon2_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 328 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 329 | ATTR1_SIZE_32 | ATTR1_VFLIP | X(horizonState->moonX), 330 | moon2_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 331 | break; 332 | case 4: 333 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y), 334 | ATTR1_SIZE_32 | X(horizonState->moonX), 335 | moon1_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 336 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 337 | ATTR1_SIZE_32 | ATTR1_VFLIP | X(horizonState->moonX), 338 | moon1_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 339 | break; 340 | case 5: 341 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y), 342 | ATTR1_SIZE_32 | X(horizonState->moonX), 343 | moon0_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 344 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 345 | ATTR1_SIZE_32 | ATTR1_VFLIP | X(horizonState->moonX), 346 | moon0_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 347 | break; 348 | case 6: 349 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y), 350 | ATTR1_SIZE_32 | ATTR1_HFLIP | X(horizonState->moonX), 351 | moon0_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 352 | obj_set_attr(CTX_OAM++, ATTR0_SQUARE | ATTR0_BLEND | Y(MOON_Y + 8), 353 | ATTR1_SIZE_32 | ATTR1_HFLIP | ATTR1_VFLIP | X(horizonState->moonX), 354 | moon0_SI | ATTR2_PRIO(2) | ATTR2_PALBANK(PAL_OBJ1)); 355 | break; 356 | } 357 | } 358 | 359 | void drawGame(GRAPHICS_CTX * ctx, STATE * state) { 360 | CTX_OAM = ctx->baseOAM; 361 | if (state->dinoState.status == WAITING) 362 | drawTitle(ctx); 363 | if (state->gameState.gameOver) { 364 | drawReplay(ctx); 365 | drawGameover(ctx); 366 | } 367 | drawHi(ctx); 368 | drawNumber(ctx, HISCORE_X, state->gameState.hiScore); 369 | if (!state->meterState.flashing) 370 | drawNumber(ctx, SCORE_X, state->meterState.displayNumber); 371 | drawDino(ctx, &state->dinoState, state->mode); 372 | if (state->mode == COOP) { 373 | drawDino(ctx, &state->minoState, state->mode); 374 | } 375 | for (int i = 0; i < state->horizonState.obstacleCount; i++) { 376 | int obstacle = (1 + i + state->horizonState.obstacleCursor) % MAX_OBSTACLES; 377 | drawObstacle(ctx, state->horizonState.obstacles + obstacle); 378 | } 379 | for (int i = 0; i < state->horizonState.cloudCount; i++) { 380 | int cloudIndex = (state->horizonState.cloudCursor + i) % MAX_CLOUDS; 381 | drawCloud(ctx, state->horizonState.clouds + cloudIndex); 382 | } 383 | drawStars(ctx, &state->horizonState); 384 | drawMoon(ctx, &state->horizonState); 385 | 386 | u32 emptyOAM = 64 - (CTX_OAM - ctx->baseOAM); 387 | oam_init(CTX_OAM, emptyOAM); 388 | oam_copy(oam_mem, obj_buffer, 64); 389 | } 390 | 391 | GRAPHICS_CTX * initGraphics() { 392 | initGraphicsMem(); 393 | 394 | whiteOutBG(); 395 | backgroundInit(); 396 | 397 | //horizon layer 398 | REG_BG0CNT = BG_PRIO(3) | BG_CBB(0) | BG_SBB(31) | BG_4BPP | BG_REG_32x32; 399 | //curtain layer 400 | REG_BG1CNT = BG_PRIO(0) | BG_CBB(0) | BG_SBB(29) | BG_4BPP | BG_REG_64x32; 401 | 402 | oam_init(obj_buffer, 128); 403 | initSets(); 404 | 405 | REG_DISPCNT= DCNT_OBJ | DCNT_OBJ_2D | DCNT_BG0 | DCNT_BG1; 406 | REG_BLDCNT = BLD_BUILD(BLD_OBJ, BLD_BG0, BLD_STD); 407 | REG_BLDALPHA = BLDA_BUILD(0, 16); 408 | REG_BLDY= BLDY_BUILD(16); 409 | 410 | GRAPHICS_CTX *ctx = calloc(sizeof(GRAPHICS_CTX), 1); 411 | INIT_GRAPHICS_CTX_NORMAL(ctx); 412 | ctx->baseOAM = obj_buffer; 413 | return ctx; 414 | } 415 | 416 | enum DIR { DOWN, LEFT, RIGHT, UP }; 417 | #define KEYMAP(key, mapping) (!!key_is_down(key)) * mapping 418 | const uint8_t *PALETTE_LUT[4][3] = { 419 | [DOWN] = {GBC_DOWNPal, GBC_ADOWNPal, GBC_BDOWNPal}, 420 | [LEFT] = {GBC_LEFTPal, GBC_ALEFTPal, GBC_BLEFTPal}, 421 | [RIGHT] = {GBC_RIGHTPal, GBC_ARIGHTPal, GBC_BRIGHTPal}, 422 | [UP] = {GBC_UPPal, GBC_AUPPal, GBC_BUPPal} 423 | 424 | }; 425 | 426 | void initGraphicsMem() { 427 | memcpy(&tile_mem[4][0], dinoSheetTiles, dinoSheetTilesLen); 428 | memcpy(&tile_mem[0][0], dinoSheetTiles, dinoSheetTilesLen); 429 | 430 | if (key_is_down(KEY_FIRE | KEY_DIR)) { 431 | int dirIndex = max( 432 | max(KEYMAP(KEY_DOWN, DOWN), KEYMAP(KEY_UP, UP)), 433 | max(KEYMAP(KEY_LEFT, LEFT), KEYMAP(KEY_RIGHT, RIGHT)) 434 | ); 435 | int faceIndex = max(key_is_down(KEY_A), key_is_down(KEY_B)); 436 | memcpy(pal_obj_mem, PALETTE_LUT[dirIndex][faceIndex], defaultPalLen); 437 | memcpy(pal_bg_mem, PALETTE_LUT[dirIndex][faceIndex], defaultPalLen); 438 | } else { 439 | memcpy(pal_obj_mem, defaultPal, defaultPalLen); 440 | memcpy(pal_bg_mem, defaultPal, defaultPalLen); 441 | } 442 | 443 | } --------------------------------------------------------------------------------