From 2173fc2774defa5fbd8a162319066158d7e79ccf Mon Sep 17 00:00:00 2001 From: rgimad Date: Thu, 7 Mar 2024 02:02:03 +0300 Subject: [PATCH] first commit --- .gitignore | 4 + Makefile | 18 +++ README.md | 2 + cloud.c | 34 +++++ cloud.h | 29 +++++ collisionbox.h | 11 ++ config.h | 7 + dino | Bin 0 -> 24092 bytes distance_meter.c | 134 ++++++++++++++++++++ distance_meter.h | 44 +++++++ game_over_panel.c | 22 ++++ game_over_panel.h | 24 ++++ graphics.c | 61 +++++++++ graphics.h | 43 +++++++ horizon.c | 144 +++++++++++++++++++++ horizon.h | 41 ++++++ horizon_line.c | 55 ++++++++ horizon_line.h | 33 +++++ main.c | 125 ++++++++++++++++++ misc.c | 27 ++++ misc.h | 13 ++ obstacle.c | 153 ++++++++++++++++++++++ obstacle.h | 67 ++++++++++ run.bat | 1 + runner.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++ runner.h | 89 +++++++++++++ sprites.h | 169 ++++++++++++++++++++++++ trex.c | 216 +++++++++++++++++++++++++++++++ trex.h | 83 ++++++++++++ ulist.c | 241 +++++++++++++++++++++++++++++++++++ ulist.h | 31 +++++ 31 files changed, 2238 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 cloud.c create mode 100644 cloud.h create mode 100644 collisionbox.h create mode 100644 config.h create mode 100644 dino create mode 100644 distance_meter.c create mode 100644 distance_meter.h create mode 100644 game_over_panel.c create mode 100644 game_over_panel.h create mode 100644 graphics.c create mode 100644 graphics.h create mode 100644 horizon.c create mode 100644 horizon.h create mode 100644 horizon_line.c create mode 100644 horizon_line.h create mode 100644 main.c create mode 100644 misc.c create mode 100644 misc.h create mode 100644 obstacle.c create mode 100644 obstacle.h create mode 100644 run.bat create mode 100644 runner.c create mode 100644 runner.h create mode 100644 sprites.h create mode 100644 trex.c create mode 100644 trex.h create mode 100644 ulist.c create mode 100644 ulist.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b69261a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.exe +*.img +*.code-workspace +*.zip \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7160d70 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +KTCC_DIR = ../../develop/ktcc/trunk +KLIBC = ../../develop/ktcc/trunk/libc.obj + +NAME = dino + +KTCC = $(KTCC_DIR)/bin/kos32-tcc +KPACK = kpack + +SRC = $(wildcard *.c) +FLAGS= -B$(KTCC_DIR)/bin -I $(KLIBC)/include +LIBS = -limg + +all: $(SRC) + $(KTCC) $(FLAGS) $(SRC) $(LIBS) -o $(NAME) +# $(KPACK) $(NAME) + +clean: + rm $(NAME) diff --git a/README.md b/README.md new file mode 100644 index 0000000..b09e2c0 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +## Dino +T-Rex Runner game from Chrome browser, rewritten in C diff --git a/cloud.c b/cloud.c new file mode 100644 index 0000000..e695ec5 --- /dev/null +++ b/cloud.c @@ -0,0 +1,34 @@ +#include "cloud.h" + +void cloudInit(Cloud* cloud, int w) { + cloud->width = w; + cloud->xPos = w; + cloud->yPos = 0; + cloud->remove = false; + cloud->cloudGap = getRandomNumber(CLOUD_MIN_GAP, CLOUD_MAX_GAP); + + cloud->yPos = getRandomNumber(CLOUD_MAX_SKY_LEVEL, CLOUD_MIN_SKY_LEVEL); // NOTE why swapped + cloudDraw(cloud); +} + +void cloudDraw(const Cloud* cloud) { + graphicsBlitAtlasImage(ATLAS_CLOUD_X, ATLAS_CLOUD_Y, cloud->xPos, cloud->yPos, CLOUD_WIDTH, CLOUD_HEIGHT, false); +} + +void cloudUpdate(Cloud* cloud, double speed) { + // printf("cloudUpdate(., %f)\n", speed); + if (!cloud->remove) { + cloud->xPos -= (int)ceil(speed); + cloudDraw(cloud); + + // Mark as removeable if no longer in the canvas. + if (!cloudIsVisible(cloud)) { + cloud->remove = true; + } + } +} + +bool cloudIsVisible(const Cloud* cloud) { + return cloud->xPos + CLOUD_WIDTH > 0; +} + diff --git a/cloud.h b/cloud.h new file mode 100644 index 0000000..604653a --- /dev/null +++ b/cloud.h @@ -0,0 +1,29 @@ +#ifndef CLOUD_H +#define CLOUD_H + +#include +#include +#include "graphics.h" +#include "misc.h" + +#define CLOUD_WIDTH 46 +#define CLOUD_HEIGHT 14 +#define CLOUD_MAX_GAP 400 +#define CLOUD_MAX_SKY_LEVEL 30 +#define CLOUD_MIN_GAP 100 +#define CLOUD_MIN_SKY_LEVEL 71 + +typedef struct { + int width; + int xPos; + int yPos; + bool remove; + int cloudGap; +} Cloud; + +void cloudInit(Cloud* cloud, int w); +void cloudDraw(const Cloud* cloud); +void cloudUpdate(Cloud* cloud, double speed); +bool cloudIsVisible(const Cloud* cloud); + +#endif diff --git a/collisionbox.h b/collisionbox.h new file mode 100644 index 0000000..e9e74ab --- /dev/null +++ b/collisionbox.h @@ -0,0 +1,11 @@ +#ifndef COLLISIONBOX_H +#define COLLISIONBOX_H + +typedef struct { + int x; + int y; + int width; + int height; +} CollisionBox; + +#endif diff --git a/config.h b/config.h new file mode 100644 index 0000000..6c23045 --- /dev/null +++ b/config.h @@ -0,0 +1,7 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define DEFAULT_WIDTH 600 +#define FPS 60 + +#endif diff --git a/dino b/dino new file mode 100644 index 0000000000000000000000000000000000000000..3677301a0cd4c41d8722b07d10450ca3dce70e8a GIT binary patch literal 24092 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 Zp6PgI= 0; i--) { + distanceMeterDraw(i, distanceMeter.highScore[i] > 12 ? distanceMeter.highScore[i] - '0' : distanceMeter.highScore[i], true); + } +} + +void distanceMeterSetHighScore(int _distance) { + int distance = distanceMeterGetActualDistance(_distance); + distanceMeter.highScore[0] = 10; + distanceMeter.highScore[1] = 11; + distanceMeter.highScore[2] = 12; + intToStr(distance, distanceMeter.maxScoreUnits, distanceMeter.highScore + 3); +} + +void distanceMeterReset() { + distanceMeterUpdate(0, 0); + distanceMeter.achievement = false; +} + +int distanceMeterGetActualDistance(int distance) { + return distance ? (int)round(distance * DM_COEFFICIENT) : 0; +} + +bool distanceMeterUpdate(int deltaTime, int _distance) { + bool paint = true; + bool playSound = false; + int distance = _distance; + if (!distanceMeter.achievement) { + distance = distanceMeterGetActualDistance(_distance); + // check if score has gone beyond the initial digit count. + if (distance > distanceMeter.maxScore && distanceMeter.maxScoreUnits == DM_MAX_DISTANCE_UNITS) { + distanceMeter.maxScoreUnits++; + distanceMeter.maxScore = distanceMeter.maxScore * 10 + 9; + } + // else { + // this.distance was in original but i didsnt see any usage of this field + // } + + if (distance > 0) { + // Acheivement unlocked + if (distance % DM_ACHIEVEMENT_DISTANCE == 0) { + // Flash score and play sound + distanceMeter.achievement = true; + distanceMeter.flashTimer = 0; + playSound = true; + } + // Create a string representation of the distance with leading 0. + intToStr(distance, distanceMeter.maxScoreUnits, distanceMeter.digits); + } + else { + intToStr(0, distanceMeter.maxScoreUnits, distanceMeter.digits); + } + } + else { + // Control flashing of the score on reaching acheivement. + if (distanceMeter.flashIterations <= DM_FLASH_ITERATIONS) { + distanceMeter.flashTimer += deltaTime; + + if (distanceMeter.flashTimer < DM_FLASH_DURATION) { + paint = false; + } + else if (distanceMeter.flashTimer > DM_FLASH_DURATION * 2) { + distanceMeter.flashTimer = 0; + distanceMeter.flashIterations++; + } + } + else { + distanceMeter.achievement = false; + distanceMeter.flashIterations = 0; + distanceMeter.flashTimer = 0; + } + } + + // Draw the digits if not flashing + if (paint) { + for (int i = distanceMeter.maxScoreUnits - 1; i >= 0; i--) { + distanceMeterDraw(i, (int)distanceMeter.digits[i] - '0', false); + } + } + + distanceMeterDrawHighScore(); + return playSound; +} diff --git a/distance_meter.h b/distance_meter.h new file mode 100644 index 0000000..2efa11b --- /dev/null +++ b/distance_meter.h @@ -0,0 +1,44 @@ +#ifndef DISTANCE_METER_H +#define DISTANCE_METER_H + +#include +#include +#include +#include "graphics.h" +#include "misc.h" + +#define DM_MAX_DISTANCE_UNITS 5 +#define DM_ACHIEVEMENT_DISTANCE 100 +#define DM_COEFFICIENT 0.025 +#define DM_FLASH_DURATION 1000/4 +#define DM_FLASH_ITERATIONS 3 +#define DM_WIDTH 10 +#define DM_HEIGHT 13 +#define DM_DEST_WIDTH 11 + +typedef struct { + int x; + int y; + int currentDistance; + int maxScore; + char digits[16]; + char highScore[16]; + bool achievement; + int flashTimer; + int flashIterations; + bool invertTrigger; + int maxScoreUnits; +} DistanceMeter; + +extern DistanceMeter distanceMeter; + +void distanceMeterInit(int w); +void distanceMeterCalcXPos(int w); +void distanceMeterDraw(int digitPos, int value, bool opt_highscore); +int distanceMeterGetActualDistance(int distance); +bool distanceMeterUpdate(int deltaTime, int _distance); +void distanceMeterDrawHighScore(); +void distanceMeterSetHighScore(int _distance); +void distanceMeterReset(); + +#endif diff --git a/game_over_panel.c b/game_over_panel.c new file mode 100644 index 0000000..ba9fefa --- /dev/null +++ b/game_over_panel.c @@ -0,0 +1,22 @@ +#include "game_over_panel.h" + +GameOverPanel gameOverPanel; + +void gameOverPanelInit(int width, int height) { + gameOverPanel.width = width; + gameOverPanel.height = height; +} + +void gameOverPanelDraw() { + double centerX = gameOverPanel.width / 2; + int textTargetX = (int)round(centerX - (GOP_TEXT_WIDTH / 2)); + int textTargetY = (int)round((gameOverPanel.height - 25) / 3); + int restartTargetX = centerX - (GOP_RESTART_WIDTH / 2); + int restartTargetY = gameOverPanel.height / 2; + // Game over text from sprite + graphicsBlitAtlasImage(GOP_TEXT_X + ATLAS_TEXT_SPRITE_X, GOP_TEXT_Y + ATLAS_TEXT_SPRITE_Y, + textTargetX, textTargetY, GOP_TEXT_WIDTH, GOP_TEXT_HEIGHT, false); + // Restart button + graphicsBlitAtlasImage(ATLAS_RESTART_X, ATLAS_RESTART_Y, + restartTargetX, restartTargetY, GOP_RESTART_WIDTH, GOP_RESTART_HEIGHT, false); +} diff --git a/game_over_panel.h b/game_over_panel.h new file mode 100644 index 0000000..8644482 --- /dev/null +++ b/game_over_panel.h @@ -0,0 +1,24 @@ +#ifndef GAME_OVER_PANEL_H +#define GAME_OVER_PANEL_H + +#include +#include "graphics.h" + +#define GOP_TEXT_X 0 +#define GOP_TEXT_Y 13 +#define GOP_TEXT_WIDTH 191 +#define GOP_TEXT_HEIGHT 11 +#define GOP_RESTART_WIDTH 36 +#define GOP_RESTART_HEIGHT 32 + +typedef struct { + int width; + int height; +} GameOverPanel; + +extern GameOverPanel gameOverPanel; + +void gameOverPanelInit(int width, int height); +void gameOverPanelDraw(); + +#endif diff --git a/graphics.c b/graphics.c new file mode 100644 index 0000000..3cc531e --- /dev/null +++ b/graphics.c @@ -0,0 +1,61 @@ +#include "graphics.h" +#include "sprites.h" + +static ksys_colors_table_t sys_color_table; +static ksys_pos_t win_pos; +static Image* screenImage; +static Image* spriteAtlas; + + +void graphicsInit() { + win_pos = _ksys_get_mouse_pos(KSYS_MOUSE_SCREEN_POS); + _ksys_get_system_colors(&sys_color_table); + spriteAtlas = img_decode((void*)sprites100, sizeof(sprites100), 0); + *((uint8_t*)spriteAtlas->Palette + 3) = 0; // set black as transparent + // for (int i = 0; i < 16; i++) { + // debug_printf("%x\n", *((uint8_t*)spriteAtlas->Palette + i)); + // } + if (spriteAtlas->Type != IMAGE_BPP32) { + spriteAtlas = img_convert(spriteAtlas, NULL, IMAGE_BPP32, 0, 0); + if (!spriteAtlas) { + debug_printf("spriteAtlas convert error\n"); + exit(-1); + } + } + debug_printf("spriteAtlas->Type = %d\n", spriteAtlas->Type); + screenImage = img_create(DEFAULT_WIDTH, 200, IMAGE_BPP32); +} + +void graphicsBlitAtlasImage(int atlasX, int atlasY, int destX, int destY, int w, int h, bool center) { + // debug_printf("start graphicsBlitAtlasImage %d %d %d %d %d %d %x %x\n", atlasX, atlasY, destX, destY, w, h, screenImage, spriteAtlas); + if (destX < 0 || destY < 0) { + return; + } + img_blend(screenImage, spriteAtlas, destX, destY, atlasX, atlasY, w, h); + // debug_printf("end graphicsBlitAtlasImage\n\n"); +} + +void graphicsFillBackground(unsigned r, unsigned g, unsigned b) { + img_fill_color(screenImage, screenImage->Width, screenImage->Height, (0xFF << 24) | (r << 16) | (g << 8) | b); +} + +void graphicsRender() { + _ksys_start_draw(); + _ksys_create_window(win_pos.x, win_pos.y, screenImage->Width + 10, screenImage->Height + 29, "DINO", sys_color_table.work_area, 0x54); // 0x54. note: C = 1 !! + img_draw(screenImage, 5, 24, screenImage->Width, screenImage->Height, 0, 0); + //ksys_draw_bitmap_palette(screenImage->Data, 5, 24, screenImage->Width, screenImage->Height, 32, 0, 0); + // ksys_blitter_params_t bp = {5, 24, screenImage->Width, screenImage->Height, 0, 0, screenImage->Width, screenImage->Height, screenImage->Data, screenImage->Width*4}; + // _ksys_blitter(0, &bp); + _ksys_end_draw(); +} + +void graphicsDelay(int ms) { + // debug_printf("ms = %d\n", ms); + _ksys_delay(ms/10 ? ms/10 : 2); +} + + +void graphicsDestroy() { + img_destroy(screenImage); + img_destroy(spriteAtlas); +} diff --git a/graphics.h b/graphics.h new file mode 100644 index 0000000..61c4d44 --- /dev/null +++ b/graphics.h @@ -0,0 +1,43 @@ +#ifndef GRAPHICS_H +#define GRAPHICS_H + +#include +#include +#include + +#include +#include + +#include + +#include "config.h" + +#define ATLAS_CACTUS_LARGE_X 332 +#define ATLAS_CACTUS_LARGE_Y 2 +#define ATLAS_CACTUS_SMALL_X 228 +#define ATLAS_CACTUS_SMALL_Y 2 +#define ATLAS_CLOUD_X 86 +#define ATLAS_CLOUD_Y 2 +#define ATLAS_HORIZON_X 2 +#define ATLAS_HORIZON_Y 54 +#define ATLAS_MOON_X 484 +#define ATLAS_MOON_Y 2 +#define ATLAS_PTERODACTYL_X 134 +#define ATLAS_PTERODACTYL_Y 2 +#define ATLAS_RESTART_X 2 +#define ATLAS_RESTART_Y 2 +#define ATLAS_TEXT_SPRITE_X 655 +#define ATLAS_TEXT_SPRITE_Y 2 +#define ATLAS_TREX_X 848 +#define ATLAS_TREX_Y 2 +#define ATLAS_STAR_X 645 +#define ATLAS_STAR_Y 2 + +void graphicsInit(); +void graphicsBlitAtlasImage(int atlasX, int atlasY, int destX, int destY, int w, int h, bool center); +void graphicsFillBackground(unsigned r, unsigned g, unsigned b); +void graphicsRender(); +void graphicsDelay(int ms); +void graphicsDestroy(); + +#endif diff --git a/horizon.c b/horizon.c new file mode 100644 index 0000000..65b2905 --- /dev/null +++ b/horizon.c @@ -0,0 +1,144 @@ +#include "horizon.h" + +Horizon horizon; + +void horizonInit(int dim_width, double gapCoefficient) { + horizon.dim_width = dim_width; + horizon.gapCoefficient = gapCoefficient; + horizon.obstacles = ulist_create(); + horizon.obstacleHistory = ulist_create(); + horizon.clouds = ulist_create(); + + horizonAddCloud(); + + horizonLineInit(); +} + +void horizonUpdate(int deltaTime, double currentSpeed, bool updateObstacles, bool showNightMode) { + // horizon.runningTime += deltaTime; + horizonLineUpdate(deltaTime, currentSpeed); + // horizon.nightMode.update(showNightMode); + horizonUpdateClouds(deltaTime, currentSpeed); + if (updateObstacles) { + horizonUpdateObstacles(deltaTime, currentSpeed); + } +} + +void horizonUpdateClouds(int deltaTime, double speed) { + //printf("horizonUpdateClouds()\n"); + double cloudSpeed = HORIZON_BG_CLOUD_SPEED / 1000 * deltaTime * speed; + int numClouds = ulist_size(horizon.clouds); + //printf("horizonUpdateClouds() %d\n", numClouds); + + if (numClouds) { + Node *cloudNode = horizon.clouds->tail; + while (cloudNode != NULL) { + cloudUpdate(cloudNode->data, cloudSpeed); + cloudNode = cloudNode->prev; + } + Cloud *lastCloud = horizon.clouds->tail->data; + // Check for adding a new cloud + if (numClouds < HORIZON_MAX_CLOUDS && (horizon.dim_width - lastCloud->xPos) > lastCloud->cloudGap && HORIZON_CLOUD_FREQUENCY > (double)rand()/RAND_MAX) { + horizonAddCloud(); + } + // Remove expired clouds + cloudNode = horizon.clouds->head; + while (cloudNode != NULL) { + Node* cloudNodeNext = cloudNode->next; + Cloud* c = cloudNode->data; + if (c->remove) { + ulist_remove(horizon.clouds, cloudNode); + } + cloudNode = cloudNodeNext; + } + } + else { + horizonAddCloud(); + } +} + +void horizonUpdateObstacles(int deltaTime, double currentSpeed) { + //printf("horizonUpdateObstacles()\n"); + // Obstacles, move to Horizon layer + Node* obNode = horizon.obstacles->head; + while (obNode != NULL) { + Node* obNodeNext = obNode->next; + Obstacle* ob = obNode->data; + obstacleUpdate(ob, deltaTime, currentSpeed); + // Clean up existing obstacles + if (ob->remove) { + //ulist_remove(horizon.obstacles, obNode); + //ulist_print(horizon.obstacles); + ulist_remove_front(horizon.obstacles); + //ulist_print(horizon.obstacles); + //puts(""); + } + obNode = obNodeNext; + } + + if (ulist_size(horizon.obstacles) > 0) { + Obstacle *lastObstacle = horizon.obstacles->tail->data; + + if (lastObstacle && !lastObstacle->followingObstacleCreated && obstacleIsVisible(lastObstacle) && (lastObstacle->xPos + lastObstacle->width + lastObstacle->gap) < horizon.dim_width) { + horizonAddNewObstacle(currentSpeed); + lastObstacle->followingObstacleCreated = true; + } + } + else { + // Create new obstacles. + horizonAddNewObstacle(currentSpeed); + } +} + +void horizonAddNewObstacle(double currentSpeed) { + int obstacleTypeIndex = getRandomNumber(0, sizeof(obstacleTypeConfigs)/sizeof(ObstacleTypeConfig) - 1); + ObstacleTypeConfig *otc = &obstacleTypeConfigs[obstacleTypeIndex]; + + // Check for multiples of the same type of obstacle. + // Also check obstacle is available at current speed. + if (horizonDuplicateObstacleCheck(otc->type) || currentSpeed < otc->minSpeed) { + horizonAddNewObstacle(currentSpeed); + } + else { + Obstacle* ob = malloc(sizeof(Obstacle)); + obstacleInit(ob, otc, horizon.dim_width, horizon.gapCoefficient, currentSpeed, otc->width); + ulist_push_back(horizon.obstacles, ob); + ulist_push_front(horizon.obstacleHistory, &(otc->type)); + if (ulist_size(horizon.obstacleHistory) > 1) { + ulist_splice(horizon.obstacleHistory, RUNNER_MAX_OBSTACLE_DUPLICATION); + } + } +} + +bool horizonDuplicateObstacleCheck(ObstacleType nextObstacleType) { + //printf("horizonDuplicateObstacleCheck(%d)\n", nextObstacleType); + int duplicateCount = 0; + Node* ohNode = horizon.obstacleHistory->head; + while (ohNode != NULL) { + //printf("%d\n", *(int*)ohNode->data); + duplicateCount = *(int*)ohNode->data == nextObstacleType ? duplicateCount + 1 : 0; + ohNode = ohNode->next; + } + //printf("duplicateCount = %d\n\n", duplicateCount); + return duplicateCount >= RUNNER_MAX_OBSTACLE_DUPLICATION; +} + +void horizonReset() { + // printf("horizonReset() !!\n"); + ulist_destroy(horizon.obstacles); + horizon.obstacles = ulist_create(); + horizonLineReset(); +} + +//void horizonResize(int width, int height) { +// +//} + +void horizonAddCloud() { + Cloud* c = malloc(sizeof(Cloud)); + cloudInit(c, horizon.dim_width); + //printf("horizonAddCloud() %d -> ", ulist_size(horizon.obstacles)); + ulist_push_back(horizon.clouds, c); + //printf("%d\n", ulist_size(horizon.obstacles)); +} + diff --git a/horizon.h b/horizon.h new file mode 100644 index 0000000..7855ab8 --- /dev/null +++ b/horizon.h @@ -0,0 +1,41 @@ +#ifndef HORIZON_H +#define HORIZON_H + +#include +#include +#include "obstacle.h" +#include "cloud.h" +#include "horizon_line.h" +#include "runner.h" +#include "graphics.h" +#include "ulist.h" + +#define HORIZON_BG_CLOUD_SPEED 0.2 +#define HORIZON_BUMPY_THRESHOLD 0.3 +#define HORIZON_CLOUD_FREQUENCY 0.5 +#define HORIZON_HORIZON_HEIGHT 16 +#define HORIZON_MAX_CLOUDS 6 + +typedef struct { + int dim_width; + double gapCoefficient; + Ulist* obstacles; + Ulist* obstacleHistory; + // nightMode + Ulist* clouds; +} Horizon; + +extern Horizon horizon; + +void horizonInit(int dim_width, double gapCoefficient); +void horizonUpdate(int deltaTime, double currentSpeed, bool updateObstacles, bool showNightMode); +void horizonUpdateClouds(int deltaTime, double speed); +void horizonUpdateObstacles(int deltaTime, double currentSpeed); +//void horizonRemoveFirstObstacle(); +void horizonAddNewObstacle(double currentSpeed); +bool horizonDuplicateObstacleCheck(ObstacleType nextObstacleType); +void horizonReset(); +//void horizonResize(int width, int height); +void horizonAddCloud(); + +#endif diff --git a/horizon_line.c b/horizon_line.c new file mode 100644 index 0000000..46fe45d --- /dev/null +++ b/horizon_line.c @@ -0,0 +1,55 @@ +#include "horizon_line.h" + +HorizonLine horizonLine; + +void horizonLineInit() { + horizonLine.width = HORIZON_LINE_WIDTH; + horizonLine.height = HORIZON_LINE_HEIGHT; + horizonLine.sourceXPos[0] = ATLAS_HORIZON_X; + horizonLine.sourceXPos[1] = ATLAS_HORIZON_X + horizonLine.width; + horizonLine.bumpThreshold = 0.5; + horizonLine.xPos[0] = 0; + horizonLine.xPos[1] = horizonLine.width; + horizonLine.yPos = HORIZON_LINE_YPOS; + horizonLineDraw(); +} + +void horizonLineDraw() { + //printf("horizonLineDraw(); xPos[0] = %d, xPos[1] = %d, yPos = %d, width = %d, height = %d\n", horizonLine.xPos[0], horizonLine.xPos[1], horizonLine.yPos, horizonLine.width, horizonLine.height); + graphicsBlitAtlasImage(horizonLine.sourceXPos[0], ATLAS_HORIZON_Y, horizonLine.xPos[0], horizonLine.yPos, horizonLine.width, horizonLine.height, false); + graphicsBlitAtlasImage(horizonLine.sourceXPos[1], ATLAS_HORIZON_Y, horizonLine.xPos[1], horizonLine.yPos, horizonLine.width, horizonLine.height, false); +} + +int horizonLineGetRandomType() { + return (double)rand() / RAND_MAX > horizonLine.bumpThreshold ? horizonLine.width : 0; +} + +void horizonLineUpdateXPos(int pos, int increment) { + int line1 = pos; + int line2 = pos == 0 ? 1 : 0; + + horizonLine.xPos[line1] -= increment; + horizonLine.xPos[line2] = horizonLine.xPos[line1] + horizonLine.width; + + if (horizonLine.xPos[line1] <= -horizonLine.width) { + horizonLine.xPos[line1] += horizonLine.width * 2; + horizonLine.xPos[line2] = horizonLine.xPos[line1] - horizonLine.width; + horizonLine.sourceXPos[line1] = horizonLineGetRandomType() + ATLAS_HORIZON_X; + } +} + +void horizonLineUpdate(int deltaTime, double speed) { + int increment = floor(speed * (FPS / 1000.0) * deltaTime); + if (horizonLine.xPos[0] <= 0) { + horizonLineUpdateXPos(0, increment); + } + else { + horizonLineUpdateXPos(1, increment); + } + horizonLineDraw(); +} + +void horizonLineReset() { + horizonLine.xPos[0] = 0; + horizonLine.xPos[1] = horizonLine.width; +} diff --git a/horizon_line.h b/horizon_line.h new file mode 100644 index 0000000..d650ff8 --- /dev/null +++ b/horizon_line.h @@ -0,0 +1,33 @@ +#ifndef HORIZON_LINE_H +#define HORIZON_LINE_H + +#include +#include +#include +#include +#include "config.h" +#include "graphics.h" + +#define HORIZON_LINE_WIDTH 600 +#define HORIZON_LINE_HEIGHT 12 +#define HORIZON_LINE_YPOS 127 + +typedef struct { + int width; + int height; + int sourceXPos[2]; + int xPos[2]; + int yPos; + double bumpThreshold; +} HorizonLine; + +extern HorizonLine horizonLine; + +void horizonLineInit(); +void horizonLineDraw(); +int horizonLineGetRandomType(); +void horizonLineUpdateXPos(int pos, int increment); +void horizonLineUpdate(int deltaTime, double speed); +void horizonLineReset(); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..4a8906a --- /dev/null +++ b/main.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "misc.h" +#include "graphics.h" +#include "distance_meter.h" +#include "cloud.h" +#include "obstacle.h" +#include "horizon_line.h" +#include "trex.h" +#include "runner.h" + +#pragma warning(disable:4996) + +uint8_t keyboard_layout[128]; + +int main(int argc, char* args[]) { + srand((unsigned int)time(NULL)); // Seed the random number generator + + graphicsInit(); + + runnerInit(); + + _ksys_debug_puts("3333333"); + _ksys_debug_puts("xABCDEFuuit56\n\n"); + + _ksys_set_event_mask(0xC0000027); // ! + _ksys_set_key_input_mode(KSYS_KEY_INPUT_MODE_SCANC); + _ksys_keyboard_layout(KSYS_KEYBOARD_LAYOUT_NORMAL, keyboard_layout); + + uint32_t kos_event; + int ext_code = 0; + uint8_t old_mode = 0; + + bool quit = false; + while (quit == false) { + int frameStartTime = getTimeStamp(); + //printf("frameStartTime = %d\n", frameStartTime); + kos_event = _ksys_check_event(); + switch (kos_event) { + case KSYS_EVENT_BUTTON: + switch (_ksys_get_button()){ + case 1: + quit = true; + break; + default: + break; + } + break; + case KSYS_EVENT_KEY: + { + ksys_oskey_t key = _ksys_get_key(); + uint8_t scancode = key.code; + if (scancode == 0xE0 || scancode == 0xE1) { + ext_code = scancode; + break; + } + if (ext_code == 0xE1 && (scancode & 0x7F) == 0x1D) { + break; + } + if (ext_code == 0xE1 && scancode == 0xC5) { + ext_code = 0; + break; + } + uint8_t code = keyboard_layout[scancode & 0x7F]; + + if (ext_code == 0xE0) { + code -= 96; + } + ext_code = 0; + + if (scancode < 128) { // KEYDOWN + //debug_printf("Keydown: key = 0x%x, scancode = 0x%x, code = 0x%x (%u) state = 0x%x\n", key.val, scancode, code, code, key.state); + runnerOnKeyDown(code); + } else { // KEYUP + //debug_printf("Keyup: key = 0x%x, scancode = 0x%x, code = 0x%x (%u) state = 0x%x\n", key.val, scancode, code, code, key.state); + runnerOnKeyUp(code); + } + } + break; + // case SDL_KEYDOWN: + // //printf("DOWN%d ", event.key.keysym.sym & 0xFF); + // runnerOnKeyDown(event.key.keysym.sym & 0xFF); + // break; + // case SDL_KEYUP: + // //printf("UP%d ", event.key.keysym.sym & 0xFF); + // runnerOnKeyUp(event.key.keysym.sym & 0xFF); + // break; + case KSYS_EVENT_REDRAW: + graphicsRender(); + break; + default: + break; + } + if (runner.nextUpdateScheduled) { + //printf("runner update! %u\n", getTimeStamp()); + runnerUpdate(); + } + else { + if (runner.skipUpdateNow) { + //printf("Skipped one update\n"); + runner.nextUpdateScheduled = true; + runner.skipUpdateNow = false; + } + } + + int frameTime = getTimeStamp() - frameStartTime; +#define FRAME_TIME 20 //16 + // debug_printf("frameTime = %d\n", frameTime); + if (frameTime < FRAME_TIME) { // 1000ms/60frames = 16.(6) + // printf("frameTime = %d\n", frameTime); + graphicsDelay(FRAME_TIME - frameTime); + } + } + + graphicsDestroy(); + + return 0; +} diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..e99ce03 --- /dev/null +++ b/misc.c @@ -0,0 +1,27 @@ +#include "misc.h" +#pragma warning(disable:4996) + +int getRandomNumber(int _min, int _max) { + return rand() % (_max - _min + 1) + _min; +} + +void intToStr(int num, int ndigits, char* result) { + char num_str[16]; // 16 more than enough for int + sprintf(num_str, "%d", num); // Convert num to a string + if (strlen(num_str) > ndigits) { + // Copy only the last ndigits to result + strcpy(result, num_str + strlen(num_str) - ndigits); + } + else { + // Pad the string with leading zeros until it reaches a length of ndigits + size_t z = ndigits - strlen(num_str); + for (size_t i = 0; i < z; i++) { + result[i] = '0'; + } + strcpy(result + z, num_str); + } +} + +int getTimeStamp() { + return _ksys_get_ns_count()/1000000; +} diff --git a/misc.h b/misc.h new file mode 100644 index 0000000..4c8810e --- /dev/null +++ b/misc.h @@ -0,0 +1,13 @@ +#ifndef MISC_H +#define MISC_H + +#include +#include +#include +#include + +int getRandomNumber(int _min, int _max); +void intToStr(int num, int ndigits, char* result); +int getTimeStamp(); + +#endif diff --git a/obstacle.c b/obstacle.c new file mode 100644 index 0000000..94a6d6f --- /dev/null +++ b/obstacle.c @@ -0,0 +1,153 @@ +#include "obstacle.h" + +ObstacleTypeConfig obstacleTypeConfigs[3] = { + { + .type = CACTUS_SMALL, + .width = 17, + .height = 35, + .yPos = 105, + .multipleSpeed = 4, + .minGap = 120, + .minSpeed = 0, + .collisionBoxesCount = 3, + .collisionBoxes = { + {.x = 0, .y = 7, .width = 5, .height = 27}, + {.x = 4, .y = 0, .width = 6, .height = 34}, + {.x = 10, .y = 4, .width = 7, .height = 14} + }, + .numFrames = 1, + .speedOffset = 0 + }, + { + .type = CACTUS_LARGE, + .width = 25, + .height = 50, + .yPos = 90, + .multipleSpeed = 7, + .minGap = 120, + .minSpeed = 0, + .collisionBoxesCount = 3, + .collisionBoxes = { + {.x = 0, .y = 12, .width = 7, .height = 38}, + {.x = 8, .y = 0, .width = 7, .height = 49}, + {.x = 13, .y = 10, .width = 10, .height = 38} + }, + .numFrames = 1, + .speedOffset = 0 + }, + { + .type = PTERODACTYL, + .width = 46, + .height = 40, + .yPos = -1, + .yPosArrSize = 3, + .yPosArr = {100, 75, 50}, + .multipleSpeed = 999, + .minGap = 150, + .minSpeed = 8.5, + .collisionBoxesCount = 5, + .collisionBoxes = { + {.x = 15, .y = 15, .width = 16, .height = 5}, + {.x = 18, .y = 21, .width = 24, .height = 6}, + {.x = 2, .y = 14, .width = 4, .height = 3}, + {.x = 6, .y = 10, .width = 4, .height = 7}, + {.x = 10, .y = 8, .width = 6, .height = 9} + }, + .numFrames = 2, + .frameRate = 1000 / 6, + .speedOffset = 0.8 + } +}; + +int obstacleSpritePosX[3] = { ATLAS_CACTUS_SMALL_X, ATLAS_CACTUS_LARGE_X, ATLAS_PTERODACTYL_X}; +int obstacleSpritePosY[3] = { ATLAS_CACTUS_SMALL_Y, ATLAS_CACTUS_LARGE_Y, ATLAS_PTERODACTYL_Y}; + + +void obstacleInit(Obstacle* ob, ObstacleTypeConfig *otc, int dim_width, double gapCoefficient, double speed, int opt_xOffset) { + ob->typeConfig = *otc; + ob->gapCoefficient = gapCoefficient; + ob->size = getRandomNumber(1, OBSTACLE_MAX_OBSTACLE_LENGTH); + ob->remove = false; + ob->xPos = dim_width + opt_xOffset; + ob->yPos = 0; + + // For animated obstacles + ob->currentFrame = 0; + ob->timer = 0; + + ob->followingObstacleCreated = false; + + if (ob->size > 1 && ob->typeConfig.multipleSpeed > speed) { // NOTE what it this? + ob->size = 1; + } + ob->width = ob->typeConfig.width * ob->size; + + if (ob->typeConfig.yPos == -1) { + ob->yPos = ob->typeConfig.yPosArr[getRandomNumber(0, ob->typeConfig.yPosArrSize)]; + } + else { + ob->yPos = ob->typeConfig.yPos; + } + + obstacleDraw(ob); + + // Make collision box adjustments, + // Central box is adjusted to the size as one box. + // ____ ______ ________ + // _| |-| _| |-| _| |-| + // | |<->| | | |<--->| | | |<----->| | + // | | 1 | | | | 2 | | | | 3 | | + // |_|___|_| |_|_____|_| |_|_______|_| + // + + if (ob->size > 1) { + ob->typeConfig.collisionBoxes[1].width = ob->width - ob->typeConfig.collisionBoxes[0].width - ob->typeConfig.collisionBoxes[2].width; + ob->typeConfig.collisionBoxes[2].x = ob->width - ob->typeConfig.collisionBoxes[2].width; + } + + // For obstacles that go at a different speed from the horizon + if (ob->typeConfig.speedOffset) { + ob->typeConfig.speedOffset = (double)rand() / RAND_MAX > 0.5 ? ob->typeConfig.speedOffset : -ob->typeConfig.speedOffset; + } + ob->gap = obstacleGetGap(ob, ob->gapCoefficient, speed); +} + +void obstacleDraw(const Obstacle *ob) { + int sourceWidth = ob->typeConfig.width; + int sourceHeight = ob->typeConfig.height; + int sourceX = (sourceWidth * ob->size) * (0.5 * ((double)ob->size - 1)) + obstacleSpritePosX[ob->typeConfig.type]; + if (ob->currentFrame > 0) { + sourceX += sourceWidth*ob->currentFrame; + } + graphicsBlitAtlasImage(sourceX, obstacleSpritePosY[ob->typeConfig.type], ob->xPos, ob->yPos, sourceWidth*ob->size, sourceHeight, false); +} + +void obstacleUpdate(Obstacle *ob, int deltaTime, double speed) { + if (!ob->remove) { + ob->xPos -= floor(((speed + ob->typeConfig.speedOffset)*FPS/1000.)*deltaTime); + } + // Update frames + if (ob->typeConfig.numFrames > 1) { + ob->timer += deltaTime; + if (ob->timer >= ob->typeConfig.frameRate) { + ob->currentFrame = ob->currentFrame == ob->typeConfig.numFrames - 1 ? 0 : ob->currentFrame + 1; + ob->timer = 0; + } + } + obstacleDraw(ob); + if (!obstacleIsVisible(ob)) { + ob->remove = true; + } +} + +int obstacleGetGap(const Obstacle *ob, double gapCoefficient, double speed) { + int minGap = round(ob->width * speed + ob->typeConfig.minGap * gapCoefficient); + int maxGap = round(minGap * OBSTACLE_MAX_GAP_COEFFICIENT); + return getRandomNumber(minGap, maxGap); +} + +bool obstacleIsVisible(const Obstacle* ob) { + return ob->xPos + ob->width > 0; +} + + diff --git a/obstacle.h b/obstacle.h new file mode 100644 index 0000000..a108109 --- /dev/null +++ b/obstacle.h @@ -0,0 +1,67 @@ +#ifndef OBSTACLE_H +#define OBSTACLE_H + +#include +#include +#include +#include "graphics.h" +#include "misc.h" +#include "config.h" +#include "collisionbox.h" + +// Coefficient for calculating the maximum gap +#define OBSTACLE_MAX_GAP_COEFFICIENT 1.5 + +// Maximum obstacle grouping count +#define OBSTACLE_MAX_OBSTACLE_LENGTH 3 + +typedef enum { + CACTUS_SMALL = 0, + CACTUS_LARGE = 1, + PTERODACTYL = 2 +} ObstacleType; + +extern int obstacleSpritePosX[3]; +extern int obstacleSpritePosY[3]; + +typedef struct { + ObstacleType type; + int width; + int height; + int yPos; + int yPosArrSize; + int yPosArr[3]; // used if yPos is -1 + int multipleSpeed; + int minGap; + int minSpeed; + int collisionBoxesCount; + CollisionBox collisionBoxes[5]; + int numFrames; + double frameRate; + double speedOffset; +} ObstacleTypeConfig; + +typedef struct { + ObstacleTypeConfig typeConfig; + double gapCoefficient; + int size; + bool remove; + int xPos; + int yPos; + int width; + int gap; + // double speedOffset; + int currentFrame; + int timer; + bool followingObstacleCreated; +} Obstacle; + +extern ObstacleTypeConfig obstacleTypeConfigs[3]; + +void obstacleInit(Obstacle *ob, ObstacleTypeConfig *otc, int dim_width, double gapCoefficient, double speed, int opt_xOffset); +void obstacleDraw(const Obstacle* ob); +void obstacleUpdate(Obstacle* ob, int deltaTime, double speed); +int obstacleGetGap(const Obstacle* ob, double gapCoefficient, double speed); +bool obstacleIsVisible(const Obstacle* ob); + +#endif diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..2e8bdee --- /dev/null +++ b/run.bat @@ -0,0 +1 @@ +qemu-system-i386 -fda kolibri.img -boot a -m 512 -usbdevice tablet -drive file=fat:rw:. \ No newline at end of file diff --git a/runner.c b/runner.c new file mode 100644 index 0000000..16ad003 --- /dev/null +++ b/runner.c @@ -0,0 +1,317 @@ +#include "runner.h" + +Runner runner; + +void runnerInit() { + runner.distanceRan = 0; + runner.highestScore = 0; + runner.time = 0; + runner.msPerFrame = 1000 / FPS; + runner.currentSpeed = RUNNER_SPEED; + runner.activated = false; + runner.playing = false; + runner.crashed = false; + runner.paused = false; + runner.inverted = false; + runner.playingIntro = false; + runner.isRunning = false; // is running or game stopped + runner.invertTimer = 0; + runner.playCount = 0; + runner.nextUpdateScheduled = false; + runner.skipUpdateNow = false; + // TODO sound + // runnerLoadImages(); + runnerAdjustDimensions(); + // setSpeed + graphicsFillBackground(0xF7, 0xF7, 0xF7); + + gameOverPanelInit(runner.width, runner.height); + + horizonInit(runner.width, RUNNER_GAP_COEFFICIENT); + distanceMeterInit(runner.width); + trexInit(); + + // this.startListening(); + runnerUpdate(); + //window.addEventListener(Runner.events.RESIZE, this.debounceResize.bind(this)); +} + +void runnerAdjustDimensions() { + runner.width = DEFAULT_WIDTH; + runner.height = RUNNER_DEFAULT_HEIGHT; + // distance meter ... +} + +void runnerOnKeyDown(int key) { + if (!runner.crashed && (key == RUNNER_KEYCODE_JUMP_1 || key == RUNNER_KEYCODE_JUMP_2)) { + if (!runner.playing) { + // this.loadSounds(); // TODO + runner.playing = true; + //printf("first jump! %u\n", getTimeStamp()); + runnerUpdate(); + runner.nextUpdateScheduled = false; + runner.skipUpdateNow = true; + } + // Play sound effect and jump on starting the game for the first time. + if (!trex.jumping && !trex.ducking) { + // this.playSound(this.soundFx.BUTTON_PRESS); // TODO + trexStartJump(runner.currentSpeed); + } + } + if (runner.playing && !runner.crashed && key == RUNNER_KEYCODE_DUCK) { + if (trex.jumping) { + // Speed drop, activated only when jump key is not pressed. + trexSetSpeedDrop(); + } + else if (!trex.jumping && !trex.ducking) { + // Duck + trexSetDuck(true); + } + } +} + +void runnerOnKeyUp(int key) { + if (runner.isRunning && (key == RUNNER_KEYCODE_JUMP_1 || key == RUNNER_KEYCODE_JUMP_2)) { + trexEndJump(); + } + else if (key == RUNNER_KEYCODE_DUCK) { + trex.speedDrop = false; + trexSetDuck(false); + } + else if (runner.crashed) { + // Check that enough time has elapsed before allowing jump key to restart. + int deltaTime = getTimeStamp() - runner.time; + //printf(".deltaTime = %d\n", deltaTime); + if (key == RUNNER_KEYCODE_RESTART || (deltaTime >= RUNNER_GAMEOVER_CLEAR_TIME && (key == RUNNER_KEYCODE_JUMP_1 || key == RUNNER_KEYCODE_JUMP_2))) { + runnerRestart(); + } + } + else if (runner.paused && (key == RUNNER_KEYCODE_JUMP_1 || key == RUNNER_KEYCODE_JUMP_2)) { + trexReset(); + runnerPlay(); + } +} + +void runnerClearCanvas() { + graphicsFillBackground(0xF7, 0xF7, 0xF7); + //graphicsRender(); +} + +void runnerUpdate() { + //printf("runnerUpdate() runner.playing = %d\n", runner.playing); + //runner.updatePending = false; + int now = getTimeStamp(); + //printf("now = %d\n", now); + int deltaTime = now - (runner.time ? runner.time : 0); + //printf("runnerUpdate() deltaTime = %d\n", deltaTime); + runner.time = now; + if (runner.playing) { + //printf("runnerUpdate() %d\n", getTimeStamp()); + runnerClearCanvas(); + + if (trex.jumping) { + trexUpdateJump(deltaTime); + } + + runner.runningTime += deltaTime; + bool hasObstacles = runner.runningTime > RUNNER_CLEAR_TIME; + + // First jump triggers the intro. + if (trex.jumpCount == 1 && !runner.playingIntro) { + //printf("trex.jumpCount = %d\n", trex.jumpCount); + runnerPlayIntro(); + } + + // The horizon doesn't move until the intro is over. + if (runner.playingIntro) { + horizonUpdate(0, runner.currentSpeed, hasObstacles, false); + } + else { + deltaTime = !runner.activated ? 0 : deltaTime; + horizonUpdate(deltaTime, runner.currentSpeed, hasObstacles, runner.inverted); + } + + // Check for collisions. + bool collision = hasObstacles && runnerCheckForCollision(horizon.obstacles->head->data); + + if (!collision) { + runner.distanceRan += runner.currentSpeed * deltaTime / runner.msPerFrame; + + if (runner.currentSpeed < RUNNER_MAX_SPEED) { + runner.currentSpeed += RUNNER_ACCELERATION; + } + } + else { + runnerGameOver(); + } + + bool playAchievementSound = distanceMeterUpdate(deltaTime, (int)ceil(runner.distanceRan)); + + if (playAchievementSound) { + //this.playSound(this.soundFx.SCORE); // TODO + } + + /*// Night mode. + if (this.invertTimer > this.config.INVERT_FADE_DURATION) { + this.invertTimer = 0; + this.invertTrigger = false; + this.invert(); + } + else if (this.invertTimer) { + this.invertTimer += deltaTime; + } + else { + var actualDistance = + this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan)); + + if (actualDistance > 0) { + this.invertTrigger = !(actualDistance % + this.config.INVERT_DISTANCE); + + if (this.invertTrigger&& this.invertTimer == = 0) { + this.invertTimer += deltaTime; + this.invert(); + } + } + }*/ + } + + runner.nextUpdateScheduled = false;// + if (runner.playing || (!runner.activated && trex.blinkCount < RUNNER_MAX_BLINK_COUNT)) { + trexUpdate(deltaTime, -1); + runner.nextUpdateScheduled = true; + } + + graphicsRender(); // blit all drawn to the screen + //printf("runner update end\n\n"); +} + +void runnerGameOver() { + // this.playSound(this.soundFx.HIT); // TODO + runnerStop(); + runner.crashed = true; + distanceMeter.achievement = false; + trexUpdate(100, TREX_STATUS_CRASHED); + + // Game over panel + gameOverPanelDraw(); + // Update the high score + if (runner.distanceRan > runner.highestScore) { + runner.highestScore = (int)ceil(runner.distanceRan); + distanceMeterSetHighScore(runner.highestScore); + } + // Reset the time clock + runner.time = getTimeStamp(); +} + +void runnerStop() { + runner.playing = false; + runner.paused = true; + runner.isRunning = false; +} + +void runnerPlay() { + if (!runner.crashed) { + runner.playing = true; + runner.paused = false; + trexUpdate(0, TREX_STATUS_RUNNING); + runner.time = getTimeStamp(); + runnerUpdate(); + } +} + +void runnerRestart() { + if (!runner.isRunning) { + runner.playCount++; + runner.runningTime = 0; + runner.playing = true; + runner.crashed = false; + runner.distanceRan = 0; + runner.currentSpeed = RUNNER_SPEED; + runner.time = getTimeStamp(); + runnerClearCanvas(); + distanceMeterReset(runner.highestScore); + horizonReset(); + trexReset(); + //this.playSound(this.soundFx.BUTTON_PRESS); + //this.invert(true); + runner.isRunning = true; + runnerUpdate(); + } +} + +void runnerPlayIntro() { + //printf("runnerPlayIntro()\n"); + if (!runner.activated && !runner.crashed) { + runner.playingIntro = true; + trex.playingIntro = true; + runner.playing = true; + runner.activated = true; + } + else if (runner.crashed) { + runnerRestart(); + } +} + +void runnerStartGame() { + runner.runningTime = 0; + runner.playingIntro = false; + trex.playingIntro = false; + runner.playCount++; + runner.isRunning = true; +} + +CollisionBox createAdjustedCollisionBox(CollisionBox box, CollisionBox adjustment) { + return (CollisionBox){ .x = box.x + adjustment.x, .y = box.y + adjustment.y, .width = box.width, .height = box.height }; +} + +// Returns whether boxes intersected +bool boxCompare(CollisionBox tRexBox, CollisionBox obstacleBox) { + // Axis-Aligned Bounding Box method. + return (tRexBox.x < obstacleBox.x + obstacleBox.width && + tRexBox.x + tRexBox.width > obstacleBox.x && + tRexBox.y < obstacleBox.y + obstacleBox.height && + tRexBox.height + tRexBox.y > obstacleBox.y); +} + +bool runnerCheckForCollision(const Obstacle* obstacle) { + // Adjustments are made to the bounding box as there is a 1 pixel white + // border around the t-rex and obstacles. + CollisionBox tRexBox = { + .x = trex.xPos + 1, + .y = trex.yPos + 1, + .width = TREX_WIDTH - 2, + .height = TREX_HEIGHT - 2 }; + + CollisionBox obstacleBox = { + .x = obstacle->xPos + 1, + .y = obstacle->yPos + 1, + .width = obstacle->typeConfig.width * obstacle->size - 2, + .height = obstacle->typeConfig.height - 2 }; + + // Simple outer bounds check. + if (boxCompare(tRexBox, obstacleBox)) { + CollisionBox* tRexCollisionBoxes = &trexDuckingCollisionBox; + int tRexCollisionBoxesCount = 1; + if (!trex.ducking) { + tRexCollisionBoxes = trexRunningCollisionBox; + tRexCollisionBoxesCount = 6; + } + + // Detailed axis aligned box check. + for (int t = 0; t < tRexCollisionBoxesCount; t++) { + for (int i = 0; i < obstacle->typeConfig.collisionBoxesCount; i++) { + // Adjust the box to actual positions. + CollisionBox adjTrexBox = createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox); + CollisionBox adjObstacleBox = createAdjustedCollisionBox(obstacle->typeConfig.collisionBoxes[i], obstacleBox); + + if (boxCompare(adjTrexBox, adjObstacleBox)) { + return true;// [adjTrexBox, adjObstacleBox] ; + } + } + } + } + return false; +} + + diff --git a/runner.h b/runner.h new file mode 100644 index 0000000..35bea13 --- /dev/null +++ b/runner.h @@ -0,0 +1,89 @@ +#ifndef RUNNER_H +#define RUNNER_H + +#include +#include "config.h" +#include "ulist.h" +#include "graphics.h" +#include "horizon.h" +#include "distance_meter.h" +#include "game_over_panel.h" +#include "trex.h" + +#define RUNNER_DEFAULT_HEIGHT 150 + +#define RUNNER_ACCELERATION 0.001 +#define RUNNER_BG_CLOUD_SPEED 0.2 +#define RUNNER_BOTTOM_PAD 10 +#define RUNNER_CLEAR_TIME 3000 +#define RUNNER_CLOUD_FREQUENCY 0.5 +#define RUNNER_GAMEOVER_CLEAR_TIME 750 +#define RUNNER_GAP_COEFFICIENT 0.6 +#define RUNNER_GRAVITY 0.6 +#define RUNNER_INITIAL_JUMP_VELOCITY 12 +#define RUNNER_INVERT_FADE_DURATION 12000 +#define RUNNER_INVERT_DISTANCE 700 +#define RUNNER_MAX_BLINK_COUNT 3 +#define RUNNER_MAX_CLOUDS 6 +#define RUNNER_MAX_OBSTACLE_LENGTH 3 +#define RUNNER_MAX_OBSTACLE_DUPLICATION 2 +#define RUNNER_MAX_SPEED 13 +#define RUNNER_MIN_JUMP_HEIGHT 35 +#define RUNNER_MOBILE_SPEED_COEFFICIENT 1.2 +#define RUNNER_SPEED 6 +#define RUNNER_SPEED_DROP_COEFFICIENT 3 + +#define RUNNER_KEYCODE_JUMP_1 82 +#define RUNNER_KEYCODE_JUMP_2 32 +#define RUNNER_KEYCODE_DUCK 81 +#define RUNNER_KEYCODE_RESTART 13 + +typedef struct { + int width; + int height; + double distanceRan; + int highestScore; + int time; + int runningTime; + double msPerFrame; + double currentSpeed; + // Ulist* obstacles; + bool activated; + bool playing; + bool crashed; + bool paused; + bool inverted; + bool invertTimer; + bool playingIntro; + bool isRunning; + // resizeTimerId_ + int playCount; + // soundFx + // audioContext + // images + // imagesLoaded + bool nextUpdateScheduled; + bool skipUpdateNow; +} Runner; + +extern Runner runner; + +void runnerInit(); +void runnerAdjustDimensions(); +//void runnerLoadImages(); + +void runnerClearCanvas(); + +void runnerPlayIntro(); +void runnerStartGame(); +void runnerUpdate(); +void runnerOnKeyDown(int key); +void runnerOnKeyUp(int key); +void runnerGameOver(); +void runnerStop(); +void runnerPlay(); +void runnerRestart(); + +bool runnerCheckForCollision(const Obstacle *obstacle); + +#endif diff --git a/sprites.h b/sprites.h new file mode 100644 index 0000000..dc3fe33 --- /dev/null +++ b/sprites.h @@ -0,0 +1,169 @@ +// Original chrome dino sprite pack +static const char sprites100[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x04, 0xd1, 0x00, 0x00, 0x00, 0x44, 0x08, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x23, 0xfc, + 0x41, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x76, 0x93, 0xcd, 0x38, 0x00, + 0x00, 0x0a, 0x0e, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x9d, 0x5b, 0x76, 0xa4, 0xb8, 0x16, + 0x44, 0x83, 0xbb, 0xb8, 0x0f, 0x4d, 0x84, 0xa1, 0xd8, 0x03, 0x80, 0x4f, 0x0d, 0x4f, 0x9f, 0xa9, + 0x01, 0x54, 0x0d, 0x85, 0x89, 0xe8, 0x93, 0xae, 0x57, 0xac, 0xab, 0xaa, 0x68, 0xf2, 0x58, 0x2d, + 0xd2, 0x98, 0xf4, 0xd9, 0xbd, 0x3a, 0x13, 0x9f, 0x07, 0x95, 0x18, 0x57, 0x38, 0x24, 0x01, 0x05, + 0xc7, 0x71, 0x1c, 0xc7, 0x71, 0x1c, 0xe7, 0xc3, 0x31, 0xe0, 0x3b, 0x05, 0xf7, 0x08, 0x38, 0x8e, + 0x15, 0xc0, 0x84, 0x83, 0x29, 0x08, 0x6f, 0x2b, 0x43, 0xb8, 0x1f, 0xd8, 0x80, 0xa1, 0xb7, 0x85, + 0x65, 0x2c, 0x7c, 0x58, 0x82, 0xb4, 0x76, 0xac, 0xf8, 0xce, 0xd4, 0x11, 0x83, 0x55, 0xdd, 0x10, + 0x23, 0x5f, 0x5f, 0xf0, 0x93, 0x19, 0x40, 0xe6, 0x36, 0x32, 0x43, 0x3f, 0xa3, 0x33, 0xb8, 0xc9, + 0xca, 0x2a, 0xc4, 0x34, 0x63, 0xe0, 0x26, 0xcb, 0x19, 0xe2, 0xae, 0xea, 0xe6, 0xc7, 0x52, 0xa0, + 0x84, 0x0b, 0xd6, 0x5e, 0x80, 0x11, 0x00, 0x4a, 0xc4, 0x3d, 0x4a, 0x40, 0x07, 0xf2, 0xb3, 0xbc, + 0xae, 0x98, 0x70, 0x02, 0x25, 0xf2, 0x50, 0xf6, 0x02, 0xdb, 0x02, 0x6c, 0x43, 0x4f, 0x4b, 0x55, + 0xc6, 0xc2, 0xa3, 0x13, 0x4a, 0x7b, 0xc7, 0x84, 0xd3, 0x98, 0x80, 0x15, 0x77, 0x98, 0xa9, 0x64, + 0x94, 0xa0, 0xea, 0x3d, 0x4b, 0x21, 0x31, 0x42, 0x33, 0xb2, 0xec, 0xe5, 0x39, 0x71, 0x46, 0x5b, + 0xd0, 0x10, 0x4b, 0x40, 0xbf, 0x96, 0xff, 0xd0, 0x31, 0x6a, 0xda, 0x84, 0xe3, 0x28, 0x88, 0xa6, + 0x4b, 0xe3, 0x51, 0xf2, 0x50, 0x24, 0x40, 0x75, 0xc2, 0xb2, 0x0d, 0x1d, 0x2d, 0x55, 0x19, 0x0b, + 0x0f, 0x4f, 0xa8, 0x57, 0xb4, 0x3b, 0xc8, 0xba, 0x1f, 0x9b, 0xac, 0x3a, 0xac, 0xa6, 0xdd, 0xae, + 0x63, 0x54, 0x2f, 0xa3, 0x4e, 0xc9, 0xaa, 0x4c, 0xb4, 0x67, 0x14, 0xba, 0x2c, 0x59, 0xd9, 0x64, + 0x87, 0x6e, 0xc9, 0x7e, 0x33, 0xde, 0xc1, 0xa1, 0x45, 0xfc, 0x4e, 0xba, 0x72, 0xad, 0xf1, 0x9b, + 0x24, 0x9f, 0xac, 0x68, 0x14, 0x34, 0x5b, 0xd2, 0x3a, 0x8e, 0x7c, 0x45, 0xfd, 0x93, 0xbe, 0xe2, + 0x7a, 0xa8, 0xeb, 0xa1, 0x90, 0x9d, 0xea, 0x37, 0xdb, 0x99, 0x1e, 0x52, 0x3b, 0x61, 0xed, 0xb1, + 0x69, 0x5f, 0x7f, 0x0c, 0x3b, 0xa9, 0x5b, 0x33, 0xe6, 0x4c, 0x69, 0xe3, 0x98, 0x11, 0x32, 0x12, + 0xcd, 0x8c, 0xcd, 0xf5, 0x68, 0xb2, 0xee, 0xc8, 0x6e, 0xca, 0x3e, 0x1d, 0x23, 0xde, 0x89, 0x49, + 0x05, 0xee, 0x38, 0xc4, 0xa5, 0x59, 0xca, 0xa4, 0x81, 0x0d, 0x83, 0x4a, 0x55, 0x5d, 0x51, 0x22, + 0x1b, 0x59, 0xb5, 0x6c, 0xda, 0xf2, 0x78, 0xd4, 0x2b, 0xda, 0x74, 0x3b, 0x34, 0x8d, 0x31, 0x3a, + 0xdd, 0x8f, 0x4d, 0x58, 0xed, 0xba, 0xaf, 0x7c, 0x7d, 0x31, 0x4d, 0xc0, 0xcc, 0x31, 0xa4, 0xd6, + 0x64, 0x16, 0x66, 0xed, 0x81, 0x86, 0xde, 0x95, 0x68, 0xfc, 0xc4, 0x5e, 0xb9, 0x76, 0xd6, 0x08, + 0xf2, 0x65, 0x15, 0xcd, 0x3e, 0x72, 0x85, 0x23, 0xcf, 0x93, 0x26, 0x74, 0x4a, 0x2c, 0x41, 0x03, + 0xdb, 0x82, 0x4d, 0xa5, 0xaa, 0xaa, 0x28, 0x11, 0xb1, 0xfc, 0x2e, 0x68, 0x58, 0xb4, 0xa5, 0x05, + 0x1d, 0x3e, 0x3e, 0x9e, 0x09, 0xe7, 0x23, 0x36, 0xed, 0x85, 0x72, 0x76, 0x9f, 0x2c, 0x5f, 0x8a, + 0x7e, 0x49, 0x41, 0x9e, 0xeb, 0xad, 0x53, 0xb9, 0x61, 0x39, 0x63, 0xbf, 0x1d, 0xb5, 0xc0, 0xd2, + 0x5f, 0x7b, 0xbe, 0xa2, 0xa5, 0x88, 0x36, 0x32, 0xe6, 0x46, 0x6f, 0xb0, 0x4e, 0xc0, 0x54, 0x25, + 0x6a, 0x4a, 0xd3, 0xaa, 0x4a, 0x69, 0x76, 0x69, 0x25, 0xd6, 0xe6, 0x86, 0x81, 0xff, 0x2d, 0xc0, + 0x72, 0xab, 0xa4, 0x6a, 0xf8, 0xb3, 0x02, 0xfb, 0xb0, 0xe5, 0xf8, 0xe1, 0x63, 0xbf, 0x38, 0xf6, + 0x39, 0xb4, 0x49, 0x63, 0xfb, 0x4e, 0x6b, 0x5a, 0xff, 0xb9, 0x4b, 0xc3, 0xcb, 0xd7, 0x7d, 0x41, + 0xa3, 0xf3, 0xe2, 0xeb, 0x4c, 0x3f, 0xa6, 0x43, 0xca, 0xcc, 0x6c, 0x3d, 0xe4, 0x9c, 0x7f, 0x7c, + 0xa5, 0xcb, 0xa6, 0xce, 0x7d, 0x16, 0xdc, 0x7a, 0x6b, 0x4f, 0x60, 0x44, 0xaf, 0xa4, 0x09, 0xf6, + 0xd0, 0x64, 0x92, 0xd1, 0xe7, 0xd5, 0x28, 0xe1, 0x9c, 0xe1, 0xa3, 0x50, 0x82, 0x88, 0xe3, 0xd5, + 0x99, 0xf3, 0xbd, 0x31, 0x27, 0x93, 0x1a, 0xb3, 0x39, 0x57, 0xc6, 0x96, 0x53, 0xf7, 0xcb, 0xda, + 0xa7, 0x67, 0x44, 0xb7, 0xa4, 0xcd, 0xa6, 0x2b, 0x98, 0xcc, 0xc1, 0x07, 0x29, 0x88, 0xc6, 0xf5, + 0x65, 0x5a, 0x9d, 0x90, 0x10, 0x0d, 0x97, 0x56, 0x82, 0x15, 0xb0, 0x5b, 0x74, 0xe4, 0x7a, 0x3e, + 0xfc, 0x20, 0xb6, 0x38, 0xae, 0xf6, 0xf9, 0x31, 0xeb, 0x48, 0xeb, 0x5c, 0xda, 0x6a, 0xd7, 0x91, + 0x3c, 0xcb, 0x08, 0x73, 0xff, 0x6b, 0x5b, 0xcc, 0x64, 0x10, 0xc1, 0x0e, 0x1b, 0xe7, 0xd6, 0x5f, + 0x7b, 0xb6, 0xa2, 0xa5, 0x5a, 0xd2, 0x12, 0x10, 0x8f, 0xf2, 0x66, 0x46, 0xd5, 0x49, 0x73, 0x67, + 0x6d, 0x2d, 0x3a, 0x0c, 0x3d, 0x1f, 0x0e, 0xa1, 0xaf, 0x30, 0x8b, 0x66, 0x2c, 0x8e, 0x6e, 0xa0, + 0xa4, 0x55, 0xab, 0x99, 0x14, 0x2d, 0x0e, 0x3c, 0xbf, 0x27, 0xa9, 0x4a, 0xe4, 0xff, 0x31, 0xcd, + 0xd6, 0x0a, 0x29, 0x39, 0xf9, 0x33, 0x9e, 0x01, 0xf7, 0x7e, 0xff, 0x12, 0x41, 0xab, 0xb6, 0xd2, + 0x3b, 0x4c, 0x43, 0x4f, 0x55, 0x51, 0x29, 0xb1, 0x41, 0x44, 0xb5, 0x3a, 0xc6, 0x52, 0x0c, 0x05, + 0x92, 0xc0, 0xa3, 0x45, 0xab, 0x94, 0xb2, 0x97, 0x30, 0x3a, 0x3e, 0x11, 0x5f, 0xf8, 0xea, 0x38, + 0xc7, 0x79, 0xb4, 0xa4, 0x4b, 0x98, 0x29, 0x76, 0xf8, 0xb3, 0x95, 0x8b, 0x00, 0x06, 0x93, 0xd6, + 0x5c, 0x1f, 0x73, 0x72, 0xab, 0xe8, 0x20, 0xd9, 0xe8, 0x90, 0xfa, 0x86, 0xb5, 0x02, 0x59, 0x58, + 0x66, 0x8c, 0x71, 0x6b, 0xcc, 0xd9, 0x35, 0xee, 0x9c, 0x56, 0x8d, 0xd1, 0xa5, 0xfd, 0x88, 0xbd, + 0x02, 0xe0, 0x6b, 0x06, 0xa0, 0x1b, 0xba, 0xc9, 0xaf, 0x18, 0xd3, 0x42, 0xc9, 0x6a, 0x5b, 0x86, + 0xf3, 0xd4, 0xf3, 0x68, 0xf5, 0x68, 0x33, 0x22, 0xf5, 0x0d, 0x38, 0xa7, 0xc6, 0x8b, 0x69, 0x0b, + 0x62, 0xc3, 0xba, 0x65, 0x5d, 0x9d, 0x00, 0xed, 0x3c, 0x03, 0x4a, 0x88, 0xce, 0xfc, 0x6b, 0x02, + 0x88, 0x09, 0xc4, 0xea, 0xa0, 0x06, 0x86, 0x0e, 0x39, 0xfd, 0xe0, 0x2e, 0xed, 0xcb, 0x2b, 0xba, + 0x71, 0x9c, 0x11, 0x4a, 0x44, 0x37, 0x93, 0x6c, 0x5d, 0x80, 0xe5, 0x51, 0x8e, 0x8c, 0x89, 0xee, + 0xbb, 0x50, 0x63, 0xd3, 0x3e, 0x28, 0x8e, 0xd7, 0x19, 0x74, 0xbe, 0x02, 0xd8, 0xbd, 0x7b, 0x7c, + 0xde, 0x09, 0x66, 0xbe, 0x4b, 0xb1, 0xee, 0x47, 0xba, 0x59, 0xd1, 0xb3, 0xcf, 0xb9, 0x8a, 0x9a, + 0x24, 0x90, 0x33, 0x6b, 0x9f, 0x98, 0xf1, 0x28, 0x41, 0x4b, 0xfd, 0x77, 0x67, 0xee, 0xde, 0x31, + 0x1a, 0xcc, 0x6a, 0x9c, 0xef, 0xd2, 0x54, 0x42, 0x34, 0x71, 0x82, 0x1a, 0x19, 0x4f, 0xd1, 0x68, + 0x88, 0x99, 0xe3, 0xce, 0xf6, 0xd8, 0xb4, 0x32, 0x46, 0x39, 0x73, 0x9c, 0xa3, 0x15, 0x2d, 0xe2, + 0x13, 0x32, 0x6c, 0x0b, 0x9c, 0x13, 0x79, 0xe5, 0xa0, 0x93, 0xce, 0x48, 0x98, 0x79, 0xbf, 0xa7, + 0x26, 0xf2, 0x0c, 0x05, 0x5a, 0xb9, 0xe7, 0xef, 0x66, 0xa3, 0xd6, 0xdc, 0x67, 0xbd, 0x1f, 0xb9, + 0x46, 0xbc, 0xe8, 0x5f, 0x2f, 0x24, 0x04, 0xe0, 0x12, 0xb5, 0x60, 0x6d, 0xc3, 0xf7, 0xe8, 0x1a, + 0xf7, 0x75, 0x36, 0x7c, 0x97, 0xfa, 0x1d, 0x9a, 0xf8, 0xb5, 0xd0, 0x50, 0x7d, 0x97, 0x28, 0x01, + 0x53, 0xd2, 0xe2, 0xe9, 0x8b, 0xd6, 0xf2, 0xcd, 0x78, 0x36, 0xbe, 0xbc, 0xf2, 0xf5, 0x9e, 0x9e, + 0x01, 0x99, 0xaa, 0x21, 0x49, 0xc7, 0xa1, 0xa2, 0x7d, 0x3e, 0x52, 0x28, 0x7f, 0x06, 0x80, 0x61, + 0x03, 0x16, 0xbb, 0xa5, 0xe2, 0xb6, 0xe0, 0x04, 0xea, 0xf5, 0x81, 0x82, 0x27, 0xe1, 0x95, 0xaf, + 0xd7, 0xa7, 0x70, 0xa5, 0xaa, 0x3a, 0x45, 0x11, 0x24, 0x5d, 0xb7, 0x96, 0x7e, 0x58, 0x22, 0x24, + 0x43, 0xaf, 0x2f, 0x1c, 0xd0, 0x40, 0x5f, 0x2b, 0x19, 0x1f, 0x70, 0xa6, 0xba, 0xe7, 0xe1, 0x22, + 0x74, 0x6e, 0xac, 0x26, 0xa0, 0xbc, 0xb9, 0x3a, 0x40, 0xd5, 0xe9, 0x6f, 0x03, 0x03, 0x36, 0xfa, + 0x2e, 0xbb, 0xe5, 0x67, 0xfd, 0xf2, 0xde, 0x56, 0x8d, 0xeb, 0x03, 0x4f, 0x38, 0x3f, 0x30, 0x18, + 0x8b, 0x00, 0xed, 0xcc, 0x1d, 0x63, 0x20, 0x1d, 0xc9, 0x9a, 0xfb, 0x9c, 0xd1, 0x8a, 0xb3, 0x41, + 0x19, 0x3a, 0x5b, 0x47, 0x7c, 0x2e, 0x12, 0x10, 0xf6, 0x03, 0xe4, 0x36, 0x14, 0xa9, 0x10, 0x68, + 0xeb, 0xd8, 0x72, 0x96, 0xe1, 0x7c, 0x8e, 0xbb, 0x39, 0xed, 0x87, 0x1f, 0xcc, 0xfb, 0xc5, 0x7f, + 0x5b, 0x9f, 0xbb, 0xfe, 0xf4, 0x59, 0xf7, 0x20, 0x11, 0x43, 0x82, 0xf9, 0x6b, 0x5f, 0x08, 0x20, + 0x17, 0xad, 0x25, 0xb3, 0x75, 0xf3, 0xfa, 0x06, 0x60, 0xd8, 0xd1, 0x1f, 0x0c, 0x87, 0xb7, 0xaa, + 0xa2, 0x25, 0x28, 0xfd, 0x47, 0x6e, 0x73, 0x67, 0x9e, 0x4c, 0xed, 0x48, 0xaa, 0xea, 0xed, 0xea, + 0x82, 0x50, 0xc5, 0xd8, 0xa9, 0x81, 0x0a, 0xaa, 0x93, 0x56, 0xa8, 0x4d, 0x3b, 0x46, 0xd0, 0x4a, + 0x9f, 0xa0, 0xc5, 0x12, 0x64, 0x57, 0xd7, 0xe2, 0xeb, 0xcb, 0x8f, 0xff, 0x3f, 0x13, 0xce, 0x06, + 0x1d, 0xde, 0xdc, 0xb0, 0xf5, 0xb4, 0x0e, 0xa8, 0x15, 0x2d, 0xa6, 0x67, 0x5d, 0xf7, 0x0c, 0x25, + 0x1a, 0x81, 0x8a, 0x1b, 0x30, 0xec, 0xb4, 0xc4, 0x7b, 0x2d, 0x1d, 0x44, 0xb4, 0x43, 0x69, 0xe7, + 0xcd, 0x9d, 0xd7, 0x3e, 0x63, 0x2f, 0x7c, 0x86, 0xad, 0xce, 0xfd, 0xcb, 0xcd, 0xe9, 0x4a, 0xc6, + 0xc1, 0x64, 0x7a, 0xae, 0x36, 0x5b, 0x97, 0x65, 0xde, 0x43, 0x7e, 0xd7, 0x8a, 0x6d, 0xb8, 0x5a, + 0x2d, 0xcf, 0x8e, 0x9e, 0x0a, 0x75, 0xb5, 0x4b, 0x25, 0x51, 0x4b, 0xc3, 0xbd, 0xed, 0x5d, 0xad, + 0x23, 0xee, 0x48, 0x5a, 0x04, 0xda, 0x8f, 0xfc, 0x00, 0x82, 0xfa, 0xae, 0xca, 0x42, 0x86, 0xb6, + 0xea, 0x66, 0x49, 0x1b, 0xd0, 0xde, 0x72, 0x84, 0xd6, 0xb6, 0x13, 0x70, 0x7d, 0xe4, 0x19, 0xb6, + 0xbc, 0xdf, 0xbc, 0x9b, 0xf9, 0x9e, 0xde, 0x51, 0x3d, 0x59, 0xa1, 0xa9, 0xdc, 0xb6, 0xcf, 0x3c, + 0x37, 0x3f, 0xbb, 0xd5, 0x59, 0xa0, 0x91, 0x5b, 0x67, 0xeb, 0xa8, 0x0a, 0xf6, 0x8c, 0x84, 0x02, + 0x44, 0x0d, 0x34, 0xb6, 0xc4, 0xe3, 0xbf, 0x4d, 0xdc, 0xe9, 0xf9, 0x7b, 0x3a, 0x0b, 0x79, 0x86, + 0xed, 0xf5, 0xe1, 0x9d, 0x79, 0x51, 0xe2, 0x09, 0xf1, 0xe2, 0xb5, 0xd4, 0xed, 0xd9, 0x9a, 0xfc, + 0x54, 0xe5, 0xd1, 0x84, 0xb6, 0xf5, 0xb7, 0x8e, 0x8f, 0x3b, 0x53, 0xc7, 0xa3, 0x4e, 0x50, 0x5d, + 0x9a, 0x56, 0xb3, 0x44, 0x7b, 0xf6, 0xd9, 0x6b, 0x89, 0xba, 0xfa, 0x79, 0xae, 0x10, 0x95, 0x00, + 0x6e, 0x71, 0x4f, 0xd7, 0x9c, 0x47, 0xfb, 0xfe, 0xdf, 0x1d, 0x4f, 0xc5, 0x60, 0x6e, 0x8b, 0x1b, + 0xe4, 0xa6, 0x54, 0xbe, 0x5b, 0x9a, 0x71, 0x59, 0xfc, 0x7a, 0x34, 0x27, 0x85, 0x8f, 0xe0, 0xd2, + 0xb8, 0x28, 0xc0, 0xad, 0x4b, 0xbb, 0xb4, 0xa7, 0xf0, 0x68, 0xf6, 0x78, 0x27, 0x5d, 0xb7, 0xd6, + 0x5e, 0x75, 0x6e, 0xfc, 0x37, 0x0d, 0x74, 0x67, 0xfd, 0xad, 0x18, 0x1f, 0x79, 0xa6, 0xfa, 0xb1, + 0x67, 0xd1, 0xd4, 0xa5, 0xd9, 0xd5, 0x9d, 0x24, 0x63, 0xbf, 0xa5, 0xde, 0xb2, 0x13, 0xa1, 0x00, + 0x88, 0x9a, 0x20, 0xf6, 0x53, 0xdc, 0xb8, 0x15, 0xea, 0x45, 0x68, 0x79, 0xb8, 0xe2, 0x3a, 0x49, + 0x8c, 0x34, 0xc6, 0x94, 0xe9, 0x9f, 0xc5, 0x56, 0xc6, 0x1c, 0xc7, 0x3d, 0xda, 0x79, 0x04, 0x5b, + 0xf3, 0xeb, 0xad, 0x14, 0x8a, 0x26, 0x2a, 0xa3, 0x17, 0x80, 0xa2, 0x1d, 0xaa, 0x81, 0x36, 0xa1, + 0x2e, 0x2c, 0xfc, 0x43, 0x2e, 0xc1, 0x8b, 0xff, 0xec, 0x5c, 0xbc, 0x56, 0xaf, 0x6c, 0xba, 0x61, + 0xe9, 0x6f, 0x3d, 0x4d, 0xd1, 0xc2, 0xc3, 0x9c, 0x19, 0x09, 0x6d, 0xb3, 0x5e, 0x08, 0x1f, 0x7b, + 0x84, 0xaa, 0x71, 0xcd, 0xa8, 0x06, 0x16, 0x18, 0x48, 0x07, 0x3e, 0x11, 0x8e, 0x33, 0x1a, 0x13, + 0x39, 0xfe, 0xd7, 0xa2, 0x77, 0x4a, 0x2c, 0x05, 0x26, 0x9a, 0x04, 0x2d, 0xc5, 0xfe, 0xf3, 0xc0, + 0x8e, 0xfa, 0x71, 0xb1, 0xd0, 0xf1, 0xe4, 0x3a, 0x75, 0x8e, 0x3b, 0x27, 0x23, 0xb6, 0x3e, 0xf7, + 0x98, 0x53, 0xe7, 0x3f, 0x9e, 0xae, 0x96, 0x64, 0xcc, 0x1f, 0xdd, 0xa5, 0x8d, 0xb0, 0x25, 0x2d, + 0x05, 0x3d, 0xf2, 0xc7, 0xd3, 0xfc, 0x2c, 0x8f, 0xf0, 0x21, 0x25, 0x2d, 0x05, 0x26, 0xe4, 0xda, + 0x58, 0xe3, 0xdb, 0x1d, 0x61, 0x5b, 0x37, 0xff, 0x55, 0x74, 0x25, 0x12, 0xc2, 0xd3, 0xd6, 0xe6, + 0x0f, 0x35, 0x8f, 0x66, 0x8c, 0x65, 0x42, 0xc3, 0x91, 0xbb, 0x4b, 0x13, 0xe1, 0x92, 0x04, 0xa3, + 0x82, 0x74, 0x68, 0x82, 0x51, 0x1b, 0x55, 0x4d, 0x71, 0x69, 0xa7, 0xb2, 0x82, 0xb8, 0x4b, 0x73, + 0x97, 0x76, 0xeb, 0x69, 0x55, 0x45, 0x43, 0xe8, 0x3f, 0xf2, 0x7e, 0x9e, 0x42, 0x29, 0x7f, 0x5f, + 0x6e, 0x0c, 0x9a, 0x30, 0x08, 0x7b, 0xea, 0x18, 0x24, 0xda, 0xb0, 0x2b, 0x63, 0x3c, 0xb9, 0x4e, + 0x66, 0x5d, 0xcf, 0x3a, 0xe7, 0x6a, 0xd7, 0x39, 0x8e, 0xaf, 0x75, 0x9e, 0x8d, 0xbd, 0xdc, 0xa8, + 0x89, 0x76, 0x75, 0x8c, 0x1a, 0x8d, 0x70, 0x2e, 0xe8, 0xd2, 0xd2, 0x95, 0x6b, 0x0d, 0x97, 0x96, + 0x5b, 0x5d, 0x1a, 0xcd, 0x15, 0x53, 0x0b, 0x18, 0x19, 0x3a, 0x5b, 0xc7, 0x03, 0xce, 0xd4, 0x95, + 0xdc, 0x53, 0xb8, 0x13, 0x18, 0xb6, 0x1f, 0xaf, 0x1d, 0x2d, 0xc7, 0xab, 0x63, 0xb1, 0x6f, 0xcf, + 0xb2, 0x31, 0xdd, 0xd7, 0x8a, 0x76, 0x97, 0xe6, 0x0e, 0xcd, 0x71, 0x8f, 0x76, 0x2a, 0xc1, 0x0c, + 0x0c, 0x1d, 0x2d, 0xef, 0x6c, 0x01, 0xfe, 0x78, 0x20, 0x48, 0x7c, 0xeb, 0x32, 0xc0, 0xda, 0xa4, + 0x23, 0xeb, 0x84, 0x86, 0x62, 0x9f, 0x44, 0x33, 0xae, 0xf8, 0x0e, 0x4f, 0x51, 0xab, 0x2e, 0x2d, + 0x63, 0x9f, 0x01, 0xd8, 0xc4, 0x6a, 0x55, 0x16, 0x4e, 0x22, 0xbd, 0xad, 0xe3, 0x81, 0x67, 0xca, + 0x67, 0xd1, 0xce, 0x5c, 0x89, 0x48, 0xe1, 0xc1, 0x2e, 0x0d, 0xab, 0x3b, 0x34, 0xa7, 0x9f, 0x9b, + 0xf1, 0x8c, 0x8d, 0xce, 0xd6, 0x11, 0xd7, 0xc7, 0x25, 0x4d, 0x6f, 0xcc, 0xea, 0xb7, 0x69, 0x8d, + 0x8b, 0xa3, 0xab, 0x1b, 0x34, 0x7d, 0x7a, 0x03, 0xdf, 0x1f, 0x54, 0x0b, 0x44, 0x44, 0x20, 0x15, + 0x20, 0xec, 0xd7, 0xb2, 0xb2, 0xff, 0x33, 0xa8, 0x4b, 0xe3, 0xbb, 0xc1, 0x52, 0x4d, 0x7c, 0xdd, + 0x5a, 0xc6, 0x38, 0x75, 0xd3, 0xc2, 0xfd, 0xdc, 0x6f, 0x1d, 0x1f, 0x7b, 0xa6, 0x7a, 0x5c, 0x7b, + 0x80, 0x63, 0x13, 0x0a, 0xe2, 0xdb, 0x6d, 0xf2, 0x44, 0x41, 0x91, 0x18, 0x85, 0xae, 0x8a, 0x19, + 0x36, 0xcd, 0xf6, 0x5d, 0xab, 0xfb, 0x33, 0x67, 0xdb, 0x19, 0x30, 0x2e, 0x7c, 0xe8, 0xac, 0xd5, + 0xaa, 0xb0, 0xb5, 0x4f, 0xd1, 0xe6, 0x86, 0x5c, 0xc6, 0x7b, 0xe2, 0x92, 0xd6, 0x0c, 0xd5, 0xcb, + 0x2f, 0x43, 0x7b, 0xf8, 0x64, 0x67, 0x02, 0x67, 0x6a, 0xc2, 0x03, 0x6a, 0x13, 0x83, 0xbb, 0xb5, + 0x09, 0x11, 0x11, 0x09, 0xa9, 0xfb, 0x33, 0x28, 0xb9, 0xd5, 0xa5, 0x01, 0x37, 0x0c, 0x68, 0x84, + 0x4d, 0xdb, 0xc1, 0x2b, 0x03, 0x99, 0xef, 0xf6, 0x91, 0x7f, 0xe8, 0xfb, 0x44, 0x9d, 0xa9, 0x3f, + 0xd6, 0xbf, 0x07, 0xe7, 0x79, 0x51, 0x47, 0xa6, 0xdc, 0x7e, 0xe6, 0x1e, 0xd0, 0x3a, 0xc2, 0x71, + 0x9c, 0x4b, 0xde, 0xf7, 0x64, 0xd7, 0xd3, 0xc7, 0x05, 0x94, 0x33, 0xef, 0x7b, 0xba, 0x01, 0x3d, + 0x0e, 0x0d, 0x95, 0x86, 0x6d, 0x30, 0x18, 0xdf, 0x75, 0x1d, 0xd2, 0x71, 0x1c, 0x67, 0x78, 0x60, + 0xeb, 0x88, 0x26, 0x66, 0x38, 0x8e, 0xf3, 0x56, 0x22, 0x0a, 0x42, 0xb5, 0x75, 0x7c, 0xad, 0x05, + 0xab, 0x8e, 0xff, 0x0c, 0x33, 0xb2, 0x6c, 0x41, 0xa9, 0xcd, 0xd5, 0x22, 0x13, 0xfa, 0x0d, 0x53, + 0x70, 0xe4, 0x58, 0x45, 0xcb, 0x00, 0xd2, 0xdb, 0x8f, 0xdc, 0x29, 0xe1, 0xd1, 0x09, 0xd2, 0xdf, + 0xa1, 0x38, 0x8e, 0xdf, 0x33, 0x30, 0xe7, 0xcf, 0xae, 0x61, 0x45, 0xff, 0x1d, 0x80, 0xf6, 0x04, + 0xb1, 0x13, 0x8a, 0x76, 0x38, 0x67, 0x90, 0x64, 0xeb, 0xe8, 0xda, 0x16, 0x8e, 0xfd, 0x0c, 0x59, + 0xb7, 0x2c, 0x6e, 0x10, 0xfa, 0x9a, 0xfa, 0xaf, 0xde, 0xc8, 0xe6, 0x91, 0xfb, 0xa5, 0x1b, 0x25, + 0x96, 0x08, 0x90, 0x50, 0x42, 0x7b, 0x42, 0xc4, 0x91, 0x09, 0xd3, 0x76, 0x69, 0x87, 0xe3, 0xb8, + 0x47, 0x33, 0x34, 0x39, 0xe8, 0x96, 0x53, 0xcb, 0x48, 0xa9, 0xbf, 0xec, 0x48, 0x50, 0xea, 0x98, + 0x10, 0xdb, 0x25, 0x0a, 0xa7, 0x1d, 0xce, 0x69, 0x04, 0xd9, 0x3a, 0xb6, 0xb6, 0x81, 0xfe, 0xcf, + 0xd0, 0xcf, 0xd0, 0xdf, 0xe4, 0x9c, 0x4c, 0x99, 0x4b, 0x5f, 0xa2, 0xa0, 0x10, 0x26, 0xf8, 0xf6, + 0x1d, 0x36, 0x94, 0x52, 0xee, 0x74, 0xfc, 0xd5, 0xce, 0x7d, 0x25, 0x35, 0x0c, 0x04, 0x51, 0x14, + 0x1d, 0xad, 0xf1, 0xae, 0x73, 0xd6, 0xa7, 0x5f, 0x7e, 0x00, 0x23, 0x39, 0x36, 0xd5, 0xcf, 0xf1, + 0x1e, 0x32, 0xd8, 0x0a, 0x8f, 0xc9, 0x53, 0x65, 0xb5, 0x90, 0xb4, 0xe6, 0xff, 0xb0, 0x5e, 0x3f, + 0xb9, 0x24, 0x49, 0x92, 0x24, 0x65, 0xb8, 0xf4, 0x26, 0x11, 0xd9, 0x80, 0x37, 0x40, 0x83, 0x95, + 0x1e, 0x00, 0x0e, 0x9f, 0xf5, 0xff, 0x00, 0xaf, 0xff, 0xde, 0x31, 0x9a, 0x94, 0xc6, 0xcf, 0x20, + 0x82, 0xce, 0xc1, 0x84, 0x01, 0x1a, 0xac, 0xf4, 0x00, 0xb0, 0xf9, 0xbe, 0x87, 0x01, 0x6e, 0xb3, + 0x84, 0xa7, 0x1b, 0xa3, 0x31, 0xde, 0x92, 0x34, 0xc7, 0x01, 0x17, 0xfe, 0xcc, 0xa8, 0x30, 0xc0, + 0xed, 0x78, 0xed, 0x31, 0xd7, 0xb4, 0x25, 0x09, 0xde, 0x66, 0xfc, 0x26, 0xd7, 0xd1, 0x24, 0xec, + 0xf6, 0x6d, 0xd1, 0x0e, 0x08, 0x17, 0x04, 0xba, 0x4a, 0x5a, 0xfe, 0x52, 0xf3, 0x77, 0x92, 0x17, + 0xbf, 0x3b, 0xcb, 0x55, 0x9d, 0xe5, 0xaa, 0x6e, 0x09, 0xc5, 0xc3, 0x1c, 0x5b, 0xb4, 0x1c, 0xbd, + 0x70, 0x3c, 0x66, 0xe1, 0xcf, 0xa9, 0x4b, 0xd9, 0x62, 0xf6, 0x55, 0x8a, 0xed, 0x13, 0xa3, 0x4f, + 0x65, 0x6e, 0xee, 0xb1, 0x70, 0x04, 0xcb, 0x55, 0x99, 0xe5, 0xea, 0x46, 0x22, 0x70, 0x8c, 0x30, + 0xb8, 0x53, 0x06, 0xc0, 0x90, 0xe5, 0xea, 0xd3, 0xca, 0x15, 0xd5, 0x94, 0x00, 0x12, 0x41, 0x13, + 0x88, 0x8c, 0xed, 0x05, 0xbc, 0x70, 0x45, 0x87, 0xe7, 0xaf, 0xce, 0x96, 0xab, 0x10, 0xcb, 0xd5, + 0xb2, 0x9f, 0x17, 0x30, 0x66, 0x79, 0x1e, 0xe1, 0xcb, 0xdd, 0xc6, 0x46, 0xce, 0x9b, 0xbc, 0xf3, + 0x89, 0x31, 0x66, 0xd3, 0x7f, 0xd4, 0x72, 0xd5, 0xca, 0x72, 0xf5, 0xba, 0x24, 0x49, 0x92, 0xf4, + 0x05, 0x52, 0x0d, 0x4e, 0x68, 0xa4, 0x9a, 0xa9, 0x29, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82 +}; \ No newline at end of file diff --git a/trex.c b/trex.c new file mode 100644 index 0000000..4fa5275 --- /dev/null +++ b/trex.c @@ -0,0 +1,216 @@ +#include "trex.h" + +Trex trex; + +CollisionBox trexDuckingCollisionBox = {.x = 1, .y = 18, .width = 55, .height = 25}; +CollisionBox trexRunningCollisionBox[6] = +{ + {.x = 22, .y = 0, .width = 17, .height = 16}, + {.x = 1, .y = 18, .width = 30, .height = 9}, + {.x = 10, .y = 35, .width = 14, .height = 8}, + {.x = 1, .y = 24, .width = 29, .height = 5}, + {.x = 5, .y = 30, .width = 21, .height = 4}, + {.x = 9, .y = 34, .width = 15, .height = 4} +}; + +TrexAnimFramesEntry trexAnimFrames[5] = { + {.frameCount = 2, .frames = {44, 0}, .msPerFrame = 1000./3}, + {.frameCount = 2, .frames = {88, 132}, .msPerFrame = 1000./12}, + {.frameCount = 1, .frames = {220}, .msPerFrame = 1000./60}, + {.frameCount = 1, .frames = {0}, .msPerFrame = 1000./60}, + {.frameCount = 2, .frames = {264, 323}, .msPerFrame = 1000./8} +}; + +// T - rex player initaliser +// Sets the t - rex to blink at random intervals +void trexInit() { + trex.xPos = 0; + trex.currentFrame = 0; + //this.currentAnimFrames = []; + trex.blinkDelay = 0; + trex.blinkCount = 0; + trex.animStartTime = 0; + trex.timer = 0; + trex.msPerFrame = 1000. / FPS; + trex.status = TREX_STATUS_WAITING; + + trex.jumping = false; + trex.ducking = false; + trex.jumpVelocity = 0; + trex.reachedMinHeight = false; + trex.speedDrop = false; + trex.jumpCount = 0; + trex.jumpspotX = 0; + + trex.groundYPos = RUNNER_DEFAULT_HEIGHT - TREX_HEIGHT - RUNNER_BOTTOM_PAD; + trex.yPos = trex.groundYPos; + trex.minJumpHeight = trex.groundYPos - TREX_MIN_JUMP_HEIGHT; + trex.playingIntro = false; + + trexDraw(0, 0); + trexUpdate(0, TREX_STATUS_WAITING); +} + +// Set the animation status +void trexUpdate(int deltaTime, int opt_status) { + //printf("trex.status = %d\n", trex.status); + trex.timer += deltaTime; + // Update the status + if (opt_status != -1) { + trex.status = opt_status; + trex.currentFrame = 0; + trex.msPerFrame = trexAnimFrames[opt_status].msPerFrame; + trex.currentAnimFrames = trexAnimFrames[opt_status]; + if (opt_status == TREX_STATUS_WAITING) { + trex.animStartTime = getTimeStamp(); + trexSetBlinkDelay(); + } + } + // Game intro animation, T-rex moves in from the left. + if (trex.playingIntro) { + if (trex.xPos < TREX_START_X_POS) { + //printf("trex.xPos = %d\n", trex.xPos); + trex.xPos += max((int)round(((double)TREX_START_X_POS / TREX_INTRO_DURATION) * deltaTime), 1); + } + else { + runnerStartGame(); + } + } + + if (trex.status == TREX_STATUS_WAITING) { + trexBlink(getTimeStamp()); + } + else { + // printf("trex.status = %d\n", trex.status); + trexDraw(trex.currentAnimFrames.frames[trex.currentFrame], 0); + } + + // Update the frame position. + if (trex.timer >= trex.msPerFrame) { + trex.currentFrame = trex.currentFrame == trex.currentAnimFrames.frameCount - 1 ? 0 : trex.currentFrame + 1; + trex.timer = 0; + } + + // Speed drop becomes duck if the down key is still being pressed. + if (trex.speedDrop && trex.yPos == trex.groundYPos) { + trex.speedDrop = false; + trexSetDuck(true); + } +} + +void trexDraw(int x, int y) { + //printf("trexDraw();\n"); + int sourceWidth = trex.ducking && trex.status != TREX_STATUS_CRASHED ? TREX_WIDTH_DUCK : TREX_WIDTH; + int sourceHeight = TREX_HEIGHT; + // Adjustments for sprite sheet position. + int sourceX = x + ATLAS_TREX_X; + int sourceY = y + ATLAS_TREX_Y; + + // Ducking. + if (trex.ducking && trex.status != TREX_STATUS_CRASHED) { + graphicsBlitAtlasImage(sourceX, sourceY, trex.xPos, trex.yPos, sourceWidth, sourceHeight, false); + } + else { + // Crashed whilst ducking. Trex is standing up so needs adjustment. + if (trex.ducking && trex.status == TREX_STATUS_CRASHED) { + trex.xPos++; + } + // Standing / running + graphicsBlitAtlasImage(sourceX, sourceY, trex.xPos, trex.yPos, sourceWidth, sourceHeight, false); + } +} + +void trexSetBlinkDelay() { + trex.blinkDelay = (int)ceil(((double)rand()/RAND_MAX)*TREX_BLINK_TIMING); +} + +void trexBlink(int time) { + //printf("trexBlink(%d)\n", time); + int deltaTime = time - trex.animStartTime; + if (deltaTime >= trex.blinkDelay) { + trexDraw(trex.currentAnimFrames.frames[trex.currentFrame], 0); + if (trex.currentFrame == 1) { + // Set new random delay to blink. + trexSetBlinkDelay(); + trex.animStartTime = time; + trex.blinkCount++; + } + } +} + +// Initialise a jump +void trexStartJump(double speed) { + if (!trex.jumping) { + trexUpdate(0, TREX_STATUS_JUMPING); + // Tweak the jump velocity based on the speed + trex.jumpVelocity = TREX_INITIAL_JUMP_VELOCITY - (speed / 10); + trex.jumping = true; + trex.reachedMinHeight = false; + trex.speedDrop = false; + } +} + +// Jump is complete, falling down +void trexEndJump() { + if (trex.reachedMinHeight && trex.jumpVelocity < TREX_DROP_VELOCITY) { + trex.jumpVelocity = TREX_DROP_VELOCITY; + } +} + +// Update frame for a jump +void trexUpdateJump(int deltaTime) { + double msPerFrame = trexAnimFrames[trex.status].msPerFrame; + double framesElapsed = deltaTime / msPerFrame; + + // Speed drop makes Trex fall faster. + if (trex.speedDrop) { + trex.yPos += (int)round(trex.jumpVelocity * TREX_SPEED_DROP_COEFFICIENT * framesElapsed); + } + else { + trex.yPos += (int)round(trex.jumpVelocity * framesElapsed); + } + trex.jumpVelocity += TREX_GRAVITY * framesElapsed; + // Minimum height has been reached. + if (trex.yPos < trex.minJumpHeight || trex.speedDrop) { + trex.reachedMinHeight = true; + } + // Reached max height + if (trex.yPos < TREX_MAX_JUMP_HEIGHT || trex.speedDrop) { + trexEndJump(); + } + // Back down at ground level. Jump completed. + if (trex.yPos > trex.groundYPos) { + trexReset(); + trex.jumpCount++; + } + trexUpdate(deltaTime, -1); +} + +// Set the speed drop.Immediately cancels the current jump +void trexSetSpeedDrop() { + trex.speedDrop = true; + trex.jumpVelocity = 1; +} + +void trexSetDuck(bool isDucking) { + if (isDucking && trex.status != TREX_STATUS_DUCKING) { + trexUpdate(0, TREX_STATUS_DUCKING); + trex.ducking = true; + } + else if (trex.status == TREX_STATUS_DUCKING) { + trexUpdate(0, TREX_STATUS_RUNNING); + trex.ducking = false; + } +} + +// Reset the t-rex to running at start of game +void trexReset() { + trex.yPos = trex.groundYPos; + trex.jumpVelocity = 0; + trex.jumping = false; + trex.ducking = false; + trexUpdate(0, TREX_STATUS_RUNNING); + //trex.midair = false; TODO: WTF is midair + trex.speedDrop = false; + trex.jumpCount = 0; +} diff --git a/trex.h b/trex.h new file mode 100644 index 0000000..0b88612 --- /dev/null +++ b/trex.h @@ -0,0 +1,83 @@ +#ifndef TREX_H +#define TREX_H + +#include +#include +#include +#include "collisionbox.h" +#include "runner.h" +#include "graphics.h" +#include "misc.h" + +// Blinking coefficient +#define TREX_BLINK_TIMING 7000 + +#define TREX_DROP_VELOCITY -5 +#define TREX_GRAVITY 0.6 +#define TREX_HEIGHT 47 +#define TREX_HEIGHT_DUCK 25 +#define TREX_INITIAL_JUMP_VELOCITY -10 +#define TREX_INTRO_DURATION 750 +#define TREX_MAX_JUMP_HEIGHT 30 +#define TREX_MIN_JUMP_HEIGHT 30 +#define TREX_SPEED_DROP_COEFFICIENT 3 +#define TREX_SPRITE_WIDTH 262 +#define TREX_START_X_POS 25 +#define TREX_WIDTH 44 +#define TREX_WIDTH_DUCK 59 + +// Animation states +typedef enum { + TREX_STATUS_WAITING = 0, + TREX_STATUS_RUNNING = 1, + TREX_STATUS_CRASHED = 2, + TREX_STATUS_JUMPING = 3, + TREX_STATUS_DUCKING = 4, +} TrexStatus; + +typedef struct { + int frameCount; + int frames[2]; + double msPerFrame; +} TrexAnimFramesEntry; + +typedef struct { + int xPos; + int yPos; + int groundYPos; + int currentFrame; + TrexAnimFramesEntry currentAnimFrames; + int blinkDelay; + int blinkCount; + int animStartTime; + int timer; + double msPerFrame; + TrexStatus status; + bool jumping; + bool ducking; + double jumpVelocity; + bool reachedMinHeight; + bool speedDrop; + int jumpCount; + int jumpspotX; + int minJumpHeight; + bool playingIntro; +} Trex; + +extern CollisionBox trexDuckingCollisionBox; +extern CollisionBox trexRunningCollisionBox[6]; +extern Trex trex; + +void trexInit(); +void trexUpdate(int deltaTime, int opt_status); +void trexDraw(int x, int y); +void trexSetBlinkDelay(); +void trexBlink(int time); +void trexStartJump(double speed); +void trexEndJump(); +void trexUpdateJump(int deltaTime); +void trexSetSpeedDrop(); +void trexSetDuck(bool isDucking); +void trexReset(); + +#endif diff --git a/ulist.c b/ulist.c new file mode 100644 index 0000000..6a57863 --- /dev/null +++ b/ulist.c @@ -0,0 +1,241 @@ +#include +#include +#include "ulist.h" + +Ulist* ulist_create() { + Ulist* list = (Ulist*)malloc(sizeof(Ulist)); + if (list == NULL) { + // abort(); + exit(-1); + } + list->head = NULL; + list->tail = NULL; + list->size = 0; + return list; +} + +void ulist_destroy(Ulist* list) { + Node* current = list->head; + Node* next; + + while (current != NULL) { + next = current->next; + free(current); + current = next; + } + + free(list); +} + +void ulist_push_front(Ulist* list, void* data) { + Node* new_node = (Node*)malloc(sizeof(Node)); + if (new_node == NULL) { + // abort(); + exit(-1); + } + new_node->data = data; + new_node->prev = NULL; + new_node->next = list->head; + + if (list->head != NULL) { + list->head->prev = new_node; + } + + list->head = new_node; + + if (list->tail == NULL) { + list->tail = new_node; + } + + list->size++; +} + +void ulist_push_back(Ulist* list, void* data) { + Node* new_node = (Node*)malloc(sizeof(Node)); + if (new_node == NULL) { + // abort(); + exit(-1); + } + new_node->data = data; + new_node->next = NULL; + new_node->prev = list->tail; + + if (list->tail != NULL) { + list->tail->next = new_node; + } + + list->tail = new_node; + + if (list->head == NULL) { + list->head = new_node; + } + + list->size++; +} + +void ulist_remove(Ulist* list, Node* node) { + if (list == NULL || node == NULL) { + return; + } + // Update previous node's next pointer + if (node->prev != NULL) { + node->prev->next = node->next; + } + else { + // If the node is the head, update the head pointer + list->head = node->next; + } + // Update next node's previous pointer + if (node->next != NULL) { + node->next->prev = node->prev; + } + else { + // If the node is the tail, update the tail pointer + list->tail = node->prev; + } + // Free the memory occupied by the node + free(node); + list->size--; +} + +void ulist_remove_front(Ulist* list) { + if (list->head == NULL) { + return; + } + + Node* node_to_remove = list->head; + list->head = list->head->next; + + if (list->head != NULL) { + list->head->prev = NULL; + } + else { + list->tail = NULL; + } + + free(node_to_remove); + list->size--; +} + +void ulist_splice(Ulist* list, int n) { + if (list->size <= n) { + return; // No need to splice if the list size is less than or equal to n + } + int count = list->size - n; + while (count > 0) { + ulist_remove_back(list); + count--; + } +} + +void ulist_remove_back(Ulist* list) { + if (list->tail == NULL) { + return; + } + + Node* node_to_remove = list->tail; + list->tail = list->tail->prev; + + if (list->tail != NULL) { + list->tail->next = NULL; + } + else { + list->head = NULL; + } + + free(node_to_remove); + list->size--; +} + +int ulist_search(Ulist* list, void* data) { + Node* current = list->head; + int index = 0; + + while (current != NULL) { + if (current->data == data) { + return index; + } + + current = current->next; + index++; + } + + return -1; +} + +void* ulist_get_front(Ulist* list) { + if (list->head == NULL) { + return NULL; + } + + return list->head->data; +} + +void* ulist_get_back(Ulist* list) { + if (list->tail == NULL) { + return NULL; + } + + return list->tail->data; +} + +int ulist_size(Ulist* list) { + return list->size; +} + +void ulist_print(Ulist* list) { + Node* current = list->head; + + while (current != NULL) { + printf("%p ", current->data); + current = current->next; + } + + printf("\n"); +} + +void ulist_test() { + // Create a new Ulist + Ulist* list = ulist_create(); + + // Test insertFront + int data1 = 10; + ulist_push_front(list, &data1); + printf("List after inserting 10 at the front: "); + ulist_print(list); // Expected output: 10 + + // Test insertBack + int data2 = 20; + ulist_push_back(list, &data2); + printf("List after inserting 20 at the back: "); + ulist_print(list); // Expected output: 10 20 + + // Test removeFront + ulist_remove_front(list); + printf("List after removing front element: "); + ulist_print(list); // Expected output: 20 + + // Test removeBack + ulist_remove_back(list); + printf("List after removing back element: "); + ulist_print(list); // Expected output: + + // Test search + int data3 = 30; + ulist_push_front(list, &data3); + printf("Index of 30 in the list: %d\n", ulist_search(list, &data3)); // Expected output: 0 + + // Test getFront + int* front = (int*)ulist_get_front(list); + printf("Front element of the list: %d\n", *front); // Expected output: 30 + + // Test getBack + int* back = (int*)ulist_get_back(list); + printf("Back element of the list: %d\n", *back); // Expected output: 30 + + // Test getSize + printf("Size of the list: %d\n", ulist_size(list)); // Expected output: 1 + + // Destroy the list + ulist_destroy(list); +} diff --git a/ulist.h b/ulist.h new file mode 100644 index 0000000..cdb056b --- /dev/null +++ b/ulist.h @@ -0,0 +1,31 @@ +#ifndef ULIST_H_ +#define ULIST_H_ + +typedef struct Node { + void* data; + struct Node* prev; + struct Node* next; +} Node; + +typedef struct Ulist { + Node* head; + Node* tail; + int size; +} Ulist; + +Ulist* ulist_create(); +void ulist_destroy(Ulist* list); +void ulist_push_front(Ulist* list, void* data); +void ulist_push_back(Ulist* list, void* data); +void ulist_remove(Ulist *list, Node *node); +void ulist_remove_front(Ulist* list); +void ulist_splice(Ulist* list, int n); +void ulist_remove_back(Ulist* list); +int ulist_search(Ulist* list, void* data); +void* ulist_get_front(Ulist* list); +void* ulist_get_back(Ulist* list); +int ulist_size(Ulist* list); +void ulist_print(Ulist* list); +void ulist_test(); + +#endif /* ULIST_H_ */ \ No newline at end of file