dino/runner.c
2024-03-07 02:02:03 +03:00

318 lines
8.5 KiB
C

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