commit 2173fc2774defa5fbd8a162319066158d7e79ccf Author: rgimad Date: Thu Mar 7 02:02:03 2024 +0300 first commit 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 0000000..3677301 Binary files /dev/null and b/dino differ diff --git a/distance_meter.c b/distance_meter.c new file mode 100644 index 0000000..18465cb --- /dev/null +++ b/distance_meter.c @@ -0,0 +1,134 @@ +#include "distance_meter.h" + +DistanceMeter distanceMeter; + +void distanceMeterInit(int w) { + distanceMeter.x = 0; + distanceMeter.y = 5; + distanceMeter.currentDistance = 0; + distanceMeter.maxScore = 0; + distanceMeter.achievement = false; + distanceMeter.flashTimer = 0; + distanceMeter.flashIterations = 0; + distanceMeter.invertTrigger = false; + distanceMeter.maxScoreUnits = DM_MAX_DISTANCE_UNITS; + distanceMeterCalcXPos(w); + for (int i = 0; i < distanceMeter.maxScoreUnits; i++) { + distanceMeterDraw(i, 0, false); + } + distanceMeter.maxScore = (int)pow(10, distanceMeter.maxScoreUnits) - 1; + distanceMeter.digits[0] = '\0'; + distanceMeter.highScore[0] = '\0'; +} + +void distanceMeterCalcXPos(int w) { + distanceMeter.x = w - (DM_DEST_WIDTH * (distanceMeter.maxScoreUnits + 1)); +} + +void distanceMeterDraw(int digitPos, int value, bool opt_highscore) { + + int dx, dy; + if (opt_highscore) { + dx = distanceMeter.x - (distanceMeter.maxScoreUnits * 2) * DM_WIDTH; + dy = distanceMeter.y; + } + else { + dx = distanceMeter.x; + dy = distanceMeter.y; + } + //printf("%d %d %d %d %d %d\n", digitPos, value, opt_highscore, dx, dy, DM_WIDTH * value + ATLAS_TEXT_SPRITE_X); + graphicsBlitAtlasImage( + DM_WIDTH * value + ATLAS_TEXT_SPRITE_X, + 0 + ATLAS_TEXT_SPRITE_Y, + digitPos * DM_DEST_WIDTH + dx, + distanceMeter.y + dy, + DM_WIDTH, + DM_HEIGHT, + false + ); +} + +void distanceMeterDrawHighScore() { + // TODO canvasCtx.globalAlpha = .8; + for (int i = (int)strlen(distanceMeter.highScore) - 1; i >= 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