├── 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 | 
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 | 
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 | 
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 | }
--------------------------------------------------------------------------------