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