first commit

This commit is contained in:
rgimad 2024-03-07 02:02:03 +03:00
commit 2173fc2774
31 changed files with 2238 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.exe
*.img
*.code-workspace
*.zip

18
Makefile Normal file
View File

@ -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)

2
README.md Normal file
View File

@ -0,0 +1,2 @@
## Dino
T-Rex Runner game from Chrome browser, rewritten in C

34
cloud.c Normal file
View File

@ -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;
}

29
cloud.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef CLOUD_H
#define CLOUD_H
#include <stdbool.h>
#include <math.h>
#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

11
collisionbox.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef COLLISIONBOX_H
#define COLLISIONBOX_H
typedef struct {
int x;
int y;
int width;
int height;
} CollisionBox;
#endif

7
config.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef CONFIG_H
#define CONFIG_H
#define DEFAULT_WIDTH 600
#define FPS 60
#endif

BIN
dino Normal file

Binary file not shown.

134
distance_meter.c Normal file
View File

@ -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;
}

44
distance_meter.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef DISTANCE_METER_H
#define DISTANCE_METER_H
#include <stdbool.h>
#include <string.h>
#include <math.h>
#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

22
game_over_panel.c Normal file
View File

@ -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);
}

24
game_over_panel.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef GAME_OVER_PANEL_H
#define GAME_OVER_PANEL_H
#include <math.h>
#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

61
graphics.c Normal file
View File

@ -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);
}

43
graphics.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/ksys.h>
#include <clayer/libimg.h>
#include <stdio.h>
#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

144
horizon.c Normal file
View File

@ -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));
}

41
horizon.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef HORIZON_H
#define HORIZON_H
#include <math.h>
#include <stdbool.h>
#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

55
horizon_line.c Normal file
View File

@ -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;
}

33
horizon_line.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef HORIZON_LINE_H
#define HORIZON_LINE_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#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

125
main.c Normal file
View File

@ -0,0 +1,125 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/ksys.h>
#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;
}

27
misc.c Normal file
View File

@ -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;
}

13
misc.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef MISC_H
#define MISC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ksys.h>
int getRandomNumber(int _min, int _max);
void intToStr(int num, int ndigits, char* result);
int getTimeStamp();
#endif

153
obstacle.c Normal file
View File

@ -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;
}

67
obstacle.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef OBSTACLE_H
#define OBSTACLE_H
#include <stdbool.h>
#include <stdlib.h>
#include <math.h>
#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

1
run.bat Normal file
View File

@ -0,0 +1 @@
qemu-system-i386 -fda kolibri.img -boot a -m 512 -usbdevice tablet -drive file=fat:rw:.

317
runner.c Normal file
View File

@ -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;
}

89
runner.h Normal file
View File

@ -0,0 +1,89 @@
#ifndef RUNNER_H
#define RUNNER_H
#include <stdbool.h>
#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

169
sprites.h Normal file
View File

@ -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
};

216
trex.c Normal file
View File

@ -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;
}

83
trex.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef TREX_H
#define TREX_H
#include <stdbool.h>
#include <stdlib.h>
#include <math.h>
#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

241
ulist.c Normal file
View File

@ -0,0 +1,241 @@
#include <stdio.h>
#include <stdlib.h>
#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);
}

31
ulist.h Normal file
View File

@ -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_ */