A complex 2D-dungeon game on LPC1768 in SWJTU-Leeds Joint School XJEL2645 project. Referenced from the framework contributed by https://os.mbed.com/users/Siriagus/code/SimplePlatformGame/

Dependencies:   mbed N5110 ShiftReg PinDetect

Committer:
Siriagus
Date:
Mon May 11 03:52:18 2015 +0000
Revision:
17:d6a3b29cab31
Parent:
16:caf613d5b85e
Child:
19:89c3eeb3761b
Added sound effects ++

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Siriagus 7:678873947b29 1 #include "Game.h"
Siriagus 7:678873947b29 2
Siriagus 17:d6a3b29cab31 3 /// @file Game.cpp
Siriagus 17:d6a3b29cab31 4
Siriagus 17:d6a3b29cab31 5 Game::~Game()
Siriagus 17:d6a3b29cab31 6 {
Siriagus 17:d6a3b29cab31 7 // Free allocated memory from bullets
Siriagus 17:d6a3b29cab31 8 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
Siriagus 17:d6a3b29cab31 9 delete *it;
Siriagus 17:d6a3b29cab31 10
Siriagus 17:d6a3b29cab31 11 bullets.clear();
Siriagus 17:d6a3b29cab31 12
Siriagus 17:d6a3b29cab31 13 // Free allocated memory from enemies
Siriagus 17:d6a3b29cab31 14 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 17:d6a3b29cab31 15 delete *it;
Siriagus 17:d6a3b29cab31 16
Siriagus 17:d6a3b29cab31 17 enemies.clear();
Siriagus 17:d6a3b29cab31 18 }
Siriagus 8:9ac6a428fa26 19
Siriagus 8:9ac6a428fa26 20 void Game::init()
Siriagus 16:caf613d5b85e 21 {
Siriagus 17:d6a3b29cab31 22 Global::score = 0;
Siriagus 15:d5eb13c4c1c6 23 paused = false;
Siriagus 17:d6a3b29cab31 24 livesLeft = 2;
Siriagus 15:d5eb13c4c1c6 25
Siriagus 12:8178fad5e660 26 // Set initial values for the player
Siriagus 13:7ab71c7c311b 27 player.width = player.height = 5; // important that this is correct, see size of Image::Player in Resources.h
Siriagus 12:8178fad5e660 28 player.onGround = false;
Siriagus 17:d6a3b29cab31 29 respawnPlayer();
Siriagus 17:d6a3b29cab31 30
Siriagus 17:d6a3b29cab31 31 spawnRate = 2; // Probability for spawning a new enemy in percent
Siriagus 17:d6a3b29cab31 32 spawnEnemy();
Siriagus 16:caf613d5b85e 33 }
Siriagus 16:caf613d5b85e 34
Siriagus 17:d6a3b29cab31 35 // temporary move to game
Siriagus 17:d6a3b29cab31 36 const int Game::spawnPoints[3][2] = {{27,20}, {2,29}, {24,45}};
Siriagus 17:d6a3b29cab31 37
Siriagus 16:caf613d5b85e 38 void Game::spawnEnemy()
Siriagus 16:caf613d5b85e 39 {
Siriagus 17:d6a3b29cab31 40 // Get random spawn point
Siriagus 17:d6a3b29cab31 41 int r = rand() % 3;
Siriagus 17:d6a3b29cab31 42 int x = Game::spawnPoints[r][0];
Siriagus 17:d6a3b29cab31 43 int y = Game::spawnPoints[r][1];
Siriagus 17:d6a3b29cab31 44
Siriagus 17:d6a3b29cab31 45 // Spawn random enemy
Siriagus 17:d6a3b29cab31 46 int randPercent = (rand() % 100);
Siriagus 17:d6a3b29cab31 47 Enemy::Type type;
Siriagus 17:d6a3b29cab31 48
Siriagus 17:d6a3b29cab31 49 if (randPercent >= 40) // 60% probability
Siriagus 17:d6a3b29cab31 50 type = Enemy::JUMPER;
Siriagus 17:d6a3b29cab31 51 else if (randPercent >= 15) // 25% probablitiy
Siriagus 17:d6a3b29cab31 52 type = Enemy::SIMPLE;
Siriagus 17:d6a3b29cab31 53 else // 15 % probability
Siriagus 17:d6a3b29cab31 54 type = Enemy::RUNNER;
Siriagus 17:d6a3b29cab31 55
Siriagus 17:d6a3b29cab31 56 // Create enemy
Siriagus 17:d6a3b29cab31 57 Enemy *enemy = new Enemy(x, y, true, type);
Siriagus 16:caf613d5b85e 58 enemies.push_back(enemy);
Siriagus 8:9ac6a428fa26 59 }
Siriagus 8:9ac6a428fa26 60
Siriagus 8:9ac6a428fa26 61 // Functions
Siriagus 7:678873947b29 62 void Game::update(float dt)
Siriagus 16:caf613d5b85e 63 {
Siriagus 15:d5eb13c4c1c6 64 // Pause button input
Siriagus 15:d5eb13c4c1c6 65 if (input->read(Input::ButtonC))
Siriagus 15:d5eb13c4c1c6 66 {
Siriagus 15:d5eb13c4c1c6 67 if (releasedBtnC)
Siriagus 15:d5eb13c4c1c6 68 {
Siriagus 15:d5eb13c4c1c6 69 paused = !paused;
Siriagus 15:d5eb13c4c1c6 70 releasedBtnC = false;
Siriagus 15:d5eb13c4c1c6 71 }
Siriagus 15:d5eb13c4c1c6 72 }
Siriagus 15:d5eb13c4c1c6 73 else
Siriagus 15:d5eb13c4c1c6 74 releasedBtnC = true;
Siriagus 15:d5eb13c4c1c6 75
Siriagus 15:d5eb13c4c1c6 76 // Skip the rest if paused
Siriagus 17:d6a3b29cab31 77 if (paused) return;
Siriagus 15:d5eb13c4c1c6 78
Siriagus 17:d6a3b29cab31 79 if ((rand() % 100) < spawnRate )
Siriagus 16:caf613d5b85e 80 spawnEnemy();
Siriagus 16:caf613d5b85e 81
Siriagus 8:9ac6a428fa26 82 // Handle input, should be its own function
Siriagus 8:9ac6a428fa26 83 switch(input->joystick->getDirection())
Siriagus 8:9ac6a428fa26 84 {
Siriagus 8:9ac6a428fa26 85 case LEFT:
Siriagus 8:9ac6a428fa26 86 case UP_LEFT:
Siriagus 8:9ac6a428fa26 87 case DOWN_LEFT:
Siriagus 8:9ac6a428fa26 88 player.vx = -2;
Siriagus 11:adb68da98262 89 player.facingLeft = true;
Siriagus 8:9ac6a428fa26 90 break;
Siriagus 8:9ac6a428fa26 91
Siriagus 8:9ac6a428fa26 92 case RIGHT:
Siriagus 8:9ac6a428fa26 93 case UP_RIGHT:
Siriagus 8:9ac6a428fa26 94 case DOWN_RIGHT:
Siriagus 8:9ac6a428fa26 95 player.vx = 2;
Siriagus 11:adb68da98262 96 player.facingLeft = false;
Siriagus 8:9ac6a428fa26 97 break;
Siriagus 8:9ac6a428fa26 98
Siriagus 8:9ac6a428fa26 99 case CENTER:
Siriagus 8:9ac6a428fa26 100 player.vx = 0;
Siriagus 8:9ac6a428fa26 101 break;
Siriagus 8:9ac6a428fa26 102 }
Siriagus 7:678873947b29 103
Siriagus 16:caf613d5b85e 104
Siriagus 16:caf613d5b85e 105 // MOVE: Random enemies
Siriagus 13:7ab71c7c311b 106
Siriagus 13:7ab71c7c311b 107 // Gravity
Siriagus 11:adb68da98262 108 player.vy += 1;
Siriagus 8:9ac6a428fa26 109
Siriagus 11:adb68da98262 110 // Check if player is trying to jump. Player can only jump if it's on the ground
Siriagus 12:8178fad5e660 111 if (input->read(Input::ButtonA) && player.onGround)
Siriagus 8:9ac6a428fa26 112 {
Siriagus 8:9ac6a428fa26 113 player.vy = -4;
Siriagus 12:8178fad5e660 114 player.onGround = false;
Siriagus 17:d6a3b29cab31 115 sound->playNote(SFX::PLAYER_JUMP);
Siriagus 8:9ac6a428fa26 116 }
Siriagus 8:9ac6a428fa26 117
Siriagus 11:adb68da98262 118 // Terminal velocity 3 px/update
Siriagus 14:b4fed570abaf 119 if (player.vy > TERMINAL_VELOCITY) player.vy = TERMINAL_VELOCITY;
Siriagus 8:9ac6a428fa26 120
Siriagus 17:d6a3b29cab31 121 if (!player.dead)
Siriagus 17:d6a3b29cab31 122 moveWithCollisionTest(&player, map);
Siriagus 17:d6a3b29cab31 123 else // move without testing collision agains the map
Siriagus 17:d6a3b29cab31 124 {
Siriagus 17:d6a3b29cab31 125 player.x += player.vx;
Siriagus 17:d6a3b29cab31 126 player.y += player.vy;
Siriagus 17:d6a3b29cab31 127 }
Siriagus 11:adb68da98262 128
Siriagus 17:d6a3b29cab31 129
Siriagus 17:d6a3b29cab31 130 moveEnemies();
Siriagus 8:9ac6a428fa26 131
Siriagus 11:adb68da98262 132 // Check if bullet should be fired
Siriagus 9:da608ae65df9 133 if (input->read(Input::ButtonB) && releasedBtnB)
Siriagus 8:9ac6a428fa26 134 {
Siriagus 11:adb68da98262 135 // Create a new bullet and give it initial values
Siriagus 8:9ac6a428fa26 136 Point* bullet = new Point;
Siriagus 15:d5eb13c4c1c6 137 bullet->x = (int)(player.x + (player.width / 2)); //(player.facingLeft) ? (player.x-1) : (player.x + player.width);
Siriagus 8:9ac6a428fa26 138 bullet->y = player.y + 2;
Siriagus 11:adb68da98262 139 bullet->vx = (player.facingLeft) ? -4 : 4;
Siriagus 9:da608ae65df9 140 bullet->vy = 0;
Siriagus 8:9ac6a428fa26 141
Siriagus 8:9ac6a428fa26 142 bullets.push_back(bullet);
Siriagus 9:da608ae65df9 143 releasedBtnB = false;
Siriagus 17:d6a3b29cab31 144
Siriagus 17:d6a3b29cab31 145 // Play sound
Siriagus 17:d6a3b29cab31 146 sound->playNote(SFX::BULLET_FIRED);
Siriagus 8:9ac6a428fa26 147 }
Siriagus 9:da608ae65df9 148 else if (!input->read(Input::ButtonB))
Siriagus 9:da608ae65df9 149 releasedBtnB = true;
Siriagus 8:9ac6a428fa26 150
Siriagus 15:d5eb13c4c1c6 151 // Loop through bullets and move them + collision test
Siriagus 9:da608ae65df9 152 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end();)
Siriagus 8:9ac6a428fa26 153 {
Siriagus 15:d5eb13c4c1c6 154 Point* bullet = *it;
Siriagus 15:d5eb13c4c1c6 155
Siriagus 15:d5eb13c4c1c6 156 int x0; // left border of collision rect
Siriagus 15:d5eb13c4c1c6 157 int x1; // right border of collision rect
Siriagus 15:d5eb13c4c1c6 158
Siriagus 15:d5eb13c4c1c6 159 int oldX = bullet->x;
Siriagus 15:d5eb13c4c1c6 160 int newX = bullet->x + bullet->vx;
Siriagus 15:d5eb13c4c1c6 161
Siriagus 15:d5eb13c4c1c6 162 x0 = min(oldX, newX);
Siriagus 15:d5eb13c4c1c6 163 x1 = max(oldX, newX);
Siriagus 15:d5eb13c4c1c6 164
Siriagus 15:d5eb13c4c1c6 165 // Collision rect for bullet in this time step
Siriagus 15:d5eb13c4c1c6 166 Rectangle bulletColRect(x0, bullet->y, (x1-x0)+1, 1);
Siriagus 9:da608ae65df9 167
Siriagus 15:d5eb13c4c1c6 168 bool col = false;
Siriagus 15:d5eb13c4c1c6 169 // Delete if outside screen
Siriagus 15:d5eb13c4c1c6 170 if (newX < 0 || newX > WIDTH || bulletHitMap(bulletColRect, map)) // if outside screen
Siriagus 15:d5eb13c4c1c6 171 {
Siriagus 15:d5eb13c4c1c6 172 col = true;
Siriagus 15:d5eb13c4c1c6 173 }
Siriagus 15:d5eb13c4c1c6 174 else
Siriagus 9:da608ae65df9 175 {
Siriagus 15:d5eb13c4c1c6 176 // loop through all enemies
Siriagus 17:d6a3b29cab31 177 for (std::vector<Enemy*>::iterator ite = enemies.begin(); ite != enemies.end(); ++ite)
Siriagus 15:d5eb13c4c1c6 178 {
Siriagus 17:d6a3b29cab31 179 Enemy *enemy = *ite;
Siriagus 15:d5eb13c4c1c6 180
Siriagus 17:d6a3b29cab31 181 // If bullet hits enemy
Siriagus 17:d6a3b29cab31 182 //if (!enemy->dead && bullet->x >= enemy->x && bullet->x <= enemy->getRight() && bullet->y >= enemy->y && bullet->y <= enemy->getBottom())
Siriagus 17:d6a3b29cab31 183
Siriagus 17:d6a3b29cab31 184 Rectangle enemyColRect(enemy->x, enemy->y, enemy->width, enemy->height); // collision rectangle for enemy
Siriagus 17:d6a3b29cab31 185
Siriagus 17:d6a3b29cab31 186 if (!enemy->dead && hitTestRect(bulletColRect, enemyColRect))
Siriagus 16:caf613d5b85e 187 {
Siriagus 16:caf613d5b85e 188 col = true;
Siriagus 17:d6a3b29cab31 189
Siriagus 17:d6a3b29cab31 190 enemy->dead = true;
Siriagus 16:caf613d5b85e 191 enemy->vx = bullet->vx / 2; // sends the dead enemy in the same direction as the incoming bullet
Siriagus 16:caf613d5b85e 192 enemy->vy = -3; // sends the dead enemy upwards in the air, because of impact
Siriagus 16:caf613d5b85e 193
Siriagus 17:d6a3b29cab31 194 Global::score += 5 * enemy->difficulty; // increase the score
Siriagus 17:d6a3b29cab31 195
Siriagus 17:d6a3b29cab31 196 sound->playNote(SFX::ENEMY_DEAD);
Siriagus 16:caf613d5b85e 197 }
Siriagus 15:d5eb13c4c1c6 198 }
Siriagus 15:d5eb13c4c1c6 199 }
Siriagus 15:d5eb13c4c1c6 200
Siriagus 15:d5eb13c4c1c6 201 if (!col)
Siriagus 15:d5eb13c4c1c6 202 {
Siriagus 15:d5eb13c4c1c6 203 ++it; // go to next element
Siriagus 15:d5eb13c4c1c6 204 bullet->x += bullet->vx; // update position
Siriagus 15:d5eb13c4c1c6 205 }
Siriagus 15:d5eb13c4c1c6 206 else
Siriagus 15:d5eb13c4c1c6 207 {
Siriagus 15:d5eb13c4c1c6 208 delete bullet;
Siriagus 9:da608ae65df9 209 it = bullets.erase(it); // go to next element
Siriagus 9:da608ae65df9 210 }
Siriagus 17:d6a3b29cab31 211 }
Siriagus 17:d6a3b29cab31 212
Siriagus 17:d6a3b29cab31 213 // Check if player hits enemy
Siriagus 17:d6a3b29cab31 214 Rectangle playerRect(player.x, player.y, player.width, player.height);
Siriagus 17:d6a3b29cab31 215 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 17:d6a3b29cab31 216 {
Siriagus 17:d6a3b29cab31 217 Enemy *enemy = *it;
Siriagus 8:9ac6a428fa26 218
Siriagus 17:d6a3b29cab31 219 if (enemy->dead) continue; // only test against living enemies
Siriagus 17:d6a3b29cab31 220
Siriagus 17:d6a3b29cab31 221 Rectangle enemyRect(enemy->x, enemy->y, enemy->width, enemy->height);
Siriagus 17:d6a3b29cab31 222
Siriagus 17:d6a3b29cab31 223 if (hitTestRect(playerRect, enemyRect))
Siriagus 17:d6a3b29cab31 224 {
Siriagus 17:d6a3b29cab31 225 player.dead = true;
Siriagus 17:d6a3b29cab31 226 player.vx = 0;
Siriagus 17:d6a3b29cab31 227 player.vy = -4;
Siriagus 17:d6a3b29cab31 228 player.onGround = false;
Siriagus 17:d6a3b29cab31 229 --livesLeft;
Siriagus 17:d6a3b29cab31 230
Siriagus 17:d6a3b29cab31 231 sound->playNote(SFX::PLAYER_DEAD);
Siriagus 17:d6a3b29cab31 232 break;
Siriagus 17:d6a3b29cab31 233 }
Siriagus 17:d6a3b29cab31 234 }
Siriagus 17:d6a3b29cab31 235
Siriagus 17:d6a3b29cab31 236 if (player.dead)
Siriagus 17:d6a3b29cab31 237 {
Siriagus 17:d6a3b29cab31 238 // remove all enemies (let them fall off)
Siriagus 17:d6a3b29cab31 239 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 17:d6a3b29cab31 240 {
Siriagus 17:d6a3b29cab31 241 Enemy *enemy = *it;
Siriagus 17:d6a3b29cab31 242 enemy->dead = true;
Siriagus 17:d6a3b29cab31 243 }
Siriagus 17:d6a3b29cab31 244
Siriagus 17:d6a3b29cab31 245 if (player.y >= HEIGHT && !enemies.size()) // when all enemies are removed
Siriagus 17:d6a3b29cab31 246 {
Siriagus 17:d6a3b29cab31 247 if (livesLeft)
Siriagus 17:d6a3b29cab31 248 {
Siriagus 17:d6a3b29cab31 249 respawnPlayer(); // Respawn player if it still have lives left
Siriagus 17:d6a3b29cab31 250 spawnEnemy(); // Spawn an enemy right away
Siriagus 17:d6a3b29cab31 251 }
Siriagus 17:d6a3b29cab31 252 else
Siriagus 17:d6a3b29cab31 253 {
Siriagus 17:d6a3b29cab31 254 if (Global::score > Global::highscores[2].score) // If new high score
Siriagus 17:d6a3b29cab31 255 requestStateChange(SUBMIT_HIGHSCORE);
Siriagus 17:d6a3b29cab31 256 else
Siriagus 17:d6a3b29cab31 257 requestStateChange(GAME_OVER);
Siriagus 17:d6a3b29cab31 258 }
Siriagus 17:d6a3b29cab31 259 }
Siriagus 8:9ac6a428fa26 260 }
Siriagus 7:678873947b29 261 }
Siriagus 7:678873947b29 262
Siriagus 7:678873947b29 263 void Game::render()
Siriagus 7:678873947b29 264 {
Siriagus 17:d6a3b29cab31 265
Siriagus 17:d6a3b29cab31 266 if (!player.dead)
Siriagus 17:d6a3b29cab31 267 {
Siriagus 17:d6a3b29cab31 268 // Draw map
Siriagus 17:d6a3b29cab31 269 drawImage(map);
Siriagus 17:d6a3b29cab31 270 }
Siriagus 17:d6a3b29cab31 271 else
Siriagus 17:d6a3b29cab31 272 {
Siriagus 17:d6a3b29cab31 273 // Print lives left
Siriagus 17:d6a3b29cab31 274 std::stringstream ss;
Siriagus 17:d6a3b29cab31 275 ss << "Lives left: " << livesLeft;
Siriagus 17:d6a3b29cab31 276 lcd->printString(ss.str().c_str(), 4, 2);
Siriagus 17:d6a3b29cab31 277 }
Siriagus 11:adb68da98262 278
Siriagus 12:8178fad5e660 279 // Draw player
Siriagus 17:d6a3b29cab31 280 drawImage(Image::Player, player.x, player.y, false, !player.facingLeft, player.dead);
Siriagus 17:d6a3b29cab31 281
Siriagus 13:7ab71c7c311b 282
Siriagus 13:7ab71c7c311b 283 // Draw enemies
Siriagus 16:caf613d5b85e 284 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 16:caf613d5b85e 285 {
Siriagus 16:caf613d5b85e 286 Enemy *enemy = *it;
Siriagus 17:d6a3b29cab31 287
Siriagus 17:d6a3b29cab31 288 switch (enemy->type)
Siriagus 17:d6a3b29cab31 289 {
Siriagus 17:d6a3b29cab31 290 case Enemy::SIMPLE:
Siriagus 17:d6a3b29cab31 291 drawImage(Image::EnemySimple, enemy->x, enemy->y, false, !enemy->facingLeft, enemy->dead);
Siriagus 17:d6a3b29cab31 292 break;
Siriagus 17:d6a3b29cab31 293
Siriagus 17:d6a3b29cab31 294 case Enemy::JUMPER:
Siriagus 17:d6a3b29cab31 295 drawImage(Image::EnemyJumper, enemy->x, enemy->y, false, !enemy->facingLeft, enemy->dead);
Siriagus 17:d6a3b29cab31 296 break;
Siriagus 17:d6a3b29cab31 297
Siriagus 17:d6a3b29cab31 298 case Enemy::RUNNER:
Siriagus 17:d6a3b29cab31 299 drawImage(Image::EnemyRunner, enemy->x, enemy->y, false, !enemy->facingLeft, enemy->dead);
Siriagus 17:d6a3b29cab31 300 break;
Siriagus 17:d6a3b29cab31 301
Siriagus 17:d6a3b29cab31 302 default:
Siriagus 17:d6a3b29cab31 303 ; // should not happen, don't render
Siriagus 17:d6a3b29cab31 304 }
Siriagus 17:d6a3b29cab31 305
Siriagus 17:d6a3b29cab31 306
Siriagus 16:caf613d5b85e 307 }
Siriagus 16:caf613d5b85e 308
Siriagus 9:da608ae65df9 309 // Render bullets
Siriagus 8:9ac6a428fa26 310 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
Siriagus 8:9ac6a428fa26 311 {
Siriagus 8:9ac6a428fa26 312 int x, y;
Siriagus 8:9ac6a428fa26 313 x = (*it)->x;
Siriagus 8:9ac6a428fa26 314 y = (*it)->y;
Siriagus 8:9ac6a428fa26 315
Siriagus 8:9ac6a428fa26 316 if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) // Boundary check
Siriagus 8:9ac6a428fa26 317 lcd->setPixel(x,y);
Siriagus 8:9ac6a428fa26 318 }
Siriagus 15:d5eb13c4c1c6 319
Siriagus 15:d5eb13c4c1c6 320 // Draw pause
Siriagus 15:d5eb13c4c1c6 321 if (paused)
Siriagus 15:d5eb13c4c1c6 322 {
Siriagus 15:d5eb13c4c1c6 323 lcd->drawRect(24, 13, 40, 13, 0); // outline
Siriagus 15:d5eb13c4c1c6 324 lcd->drawRect(25, 14, 38, 11, 2); // white fill
Siriagus 15:d5eb13c4c1c6 325 lcd->printString("Paused", 27, 2); // text
Siriagus 15:d5eb13c4c1c6 326 }
Siriagus 17:d6a3b29cab31 327
Siriagus 17:d6a3b29cab31 328 // GUI
Siriagus 17:d6a3b29cab31 329 renderScore();
Siriagus 13:7ab71c7c311b 330 }
Siriagus 13:7ab71c7c311b 331
Siriagus 13:7ab71c7c311b 332 // Collision test between entites and map
Siriagus 13:7ab71c7c311b 333 void Game::moveWithCollisionTest(Entity* entity, const int map[HEIGHT][WIDTH])
Siriagus 13:7ab71c7c311b 334 {
Siriagus 13:7ab71c7c311b 335 int x = entity->x;
Siriagus 13:7ab71c7c311b 336 int y = entity->y;
Siriagus 13:7ab71c7c311b 337 int steps = abs(entity->vx); // how many units (pixels) the entity should move in said direction
Siriagus 13:7ab71c7c311b 338 bool collision; // true if colliding
Siriagus 13:7ab71c7c311b 339
Siriagus 13:7ab71c7c311b 340 // Check x-axis
Siriagus 13:7ab71c7c311b 341 if (entity->vx > 0) // moving right
Siriagus 13:7ab71c7c311b 342 {
Siriagus 13:7ab71c7c311b 343 int entityRight = x + entity->width - 1; // Need to check right border of entity, since it is moving right
Siriagus 13:7ab71c7c311b 344
Siriagus 13:7ab71c7c311b 345 while(steps--) // While it still have more movement left
Siriagus 13:7ab71c7c311b 346 {
Siriagus 13:7ab71c7c311b 347 collision = false;
Siriagus 16:caf613d5b85e 348
Siriagus 16:caf613d5b85e 349 // Wrapping
Siriagus 16:caf613d5b85e 350 if (entityRight+1 >= WIDTH)
Siriagus 16:caf613d5b85e 351 entityRight = -1;// wants entityRight = -1, so next check is entityRight 0*/
Siriagus 16:caf613d5b85e 352
Siriagus 13:7ab71c7c311b 353 for (int i = 0; i < entity->height; ++i) // Loop through all vertical points on the right hand side of the entity (y+i)
Siriagus 13:7ab71c7c311b 354 {
Siriagus 13:7ab71c7c311b 355 if (map[y+i][entityRight+1]) // If moving to the right leads to collision for given y+i
Siriagus 13:7ab71c7c311b 356 {
Siriagus 15:d5eb13c4c1c6 357 // Slope + allows player to climb to top of platform by going right if it hits close to top of wall.
Siriagus 15:d5eb13c4c1c6 358 if (!map[y+i-1][entityRight+1] && entity->onGround)
Siriagus 15:d5eb13c4c1c6 359 {
Siriagus 15:d5eb13c4c1c6 360 entity->vy = -1;
Siriagus 15:d5eb13c4c1c6 361 }
Siriagus 15:d5eb13c4c1c6 362 else
Siriagus 15:d5eb13c4c1c6 363 {
Siriagus 15:d5eb13c4c1c6 364 collision = true; // Then collision is true
Siriagus 15:d5eb13c4c1c6 365 break; // Skip the for loop, no need for further testing
Siriagus 15:d5eb13c4c1c6 366 }
Siriagus 15:d5eb13c4c1c6 367
Siriagus 13:7ab71c7c311b 368 }
Siriagus 13:7ab71c7c311b 369 }
Siriagus 13:7ab71c7c311b 370
Siriagus 13:7ab71c7c311b 371 if (collision) // If collision
Siriagus 13:7ab71c7c311b 372 break; // skip the while loop, entity can not move further, even though its velocity is higher
Siriagus 13:7ab71c7c311b 373 else
Siriagus 13:7ab71c7c311b 374 ++entityRight; // Move entity one px to the right
Siriagus 13:7ab71c7c311b 375 }
Siriagus 13:7ab71c7c311b 376
Siriagus 16:caf613d5b85e 377 // If wrap didn't work, make sure entity is on the correct side of the map
Siriagus 16:caf613d5b85e 378 if (entityRight < 0)
Siriagus 16:caf613d5b85e 379 entityRight = WIDTH-1;
Siriagus 16:caf613d5b85e 380
Siriagus 13:7ab71c7c311b 381 entity->x = entityRight - (entity->width - 1); // Update entity's position. Need to set upper-left pixel.
Siriagus 13:7ab71c7c311b 382 }
Siriagus 13:7ab71c7c311b 383 else // moving left
Siriagus 13:7ab71c7c311b 384 {
Siriagus 13:7ab71c7c311b 385 while(steps--) // While still movement left
Siriagus 13:7ab71c7c311b 386 {
Siriagus 13:7ab71c7c311b 387 collision = false;
Siriagus 13:7ab71c7c311b 388
Siriagus 16:caf613d5b85e 389 // Wrap around map
Siriagus 16:caf613d5b85e 390 if (x-1 < 0)
Siriagus 16:caf613d5b85e 391 x = WIDTH; // causes x-1 in the next check to be WIDTH - 1
Siriagus 13:7ab71c7c311b 392
Siriagus 16:caf613d5b85e 393 // Check for all y-positions
Siriagus 16:caf613d5b85e 394 for (int i = 0; i < entity->height; ++i)
Siriagus 13:7ab71c7c311b 395 {
Siriagus 16:caf613d5b85e 396
Siriagus 13:7ab71c7c311b 397 if (map[y+i][x-1]) // If solid block
Siriagus 13:7ab71c7c311b 398 {
Siriagus 15:d5eb13c4c1c6 399 if (!map[y+i-1][x-1] && entity->onGround) // If slope or close to top of wall (=> can climb by going left).
Siriagus 15:d5eb13c4c1c6 400 {
Siriagus 15:d5eb13c4c1c6 401 entity->vy = -1;
Siriagus 15:d5eb13c4c1c6 402 }
Siriagus 15:d5eb13c4c1c6 403 else
Siriagus 15:d5eb13c4c1c6 404 {
Siriagus 15:d5eb13c4c1c6 405 collision = true;
Siriagus 15:d5eb13c4c1c6 406 break; // Collision detected, no further testing required
Siriagus 15:d5eb13c4c1c6 407 }
Siriagus 13:7ab71c7c311b 408 }
Siriagus 13:7ab71c7c311b 409 }
Siriagus 13:7ab71c7c311b 410
Siriagus 13:7ab71c7c311b 411 if (collision)
Siriagus 13:7ab71c7c311b 412 break;
Siriagus 13:7ab71c7c311b 413 else
Siriagus 13:7ab71c7c311b 414 --x; // Move to the left if no collision is detected
Siriagus 13:7ab71c7c311b 415 }
Siriagus 13:7ab71c7c311b 416
Siriagus 16:caf613d5b85e 417 x %= WIDTH; // In case wrapping caused entity to crash with wall on other side, x should be 0 instead of WIDTH (invalid).
Siriagus 16:caf613d5b85e 418
Siriagus 16:caf613d5b85e 419 entity->x = x; // update position
Siriagus 13:7ab71c7c311b 420 }
Siriagus 13:7ab71c7c311b 421
Siriagus 13:7ab71c7c311b 422 // Check collision with map in y-direction - works the same way as the x-axis, except for other axis
Siriagus 13:7ab71c7c311b 423 x = entity->x;
Siriagus 13:7ab71c7c311b 424 y = entity->y;
Siriagus 13:7ab71c7c311b 425 steps = abs(entity->vy);
Siriagus 13:7ab71c7c311b 426
Siriagus 13:7ab71c7c311b 427 if (entity->vy > 0) // downwards
Siriagus 13:7ab71c7c311b 428 {
Siriagus 13:7ab71c7c311b 429 int entityBottom = y + entity->height - 1; // Need to check if bottom part collides
Siriagus 13:7ab71c7c311b 430 while(steps--) // Still movement left
Siriagus 13:7ab71c7c311b 431 {
Siriagus 13:7ab71c7c311b 432 collision = false;
Siriagus 16:caf613d5b85e 433
Siriagus 13:7ab71c7c311b 434 for (int i = 0; i < entity->width; ++i) // Loop through all x-position on lower part of entity
Siriagus 16:caf613d5b85e 435 {
Siriagus 16:caf613d5b85e 436 if (map[(entityBottom+1) % HEIGHT][x+i]) // If moving the entity one step down for a given (x+i)-position gives a collision
Siriagus 13:7ab71c7c311b 437 {
Siriagus 13:7ab71c7c311b 438 collision = true;
Siriagus 13:7ab71c7c311b 439 break; // No further testing required
Siriagus 13:7ab71c7c311b 440 }
Siriagus 13:7ab71c7c311b 441 }
Siriagus 13:7ab71c7c311b 442
Siriagus 13:7ab71c7c311b 443 if (collision) // If collision
Siriagus 13:7ab71c7c311b 444 {
Siriagus 13:7ab71c7c311b 445 entity->vy = 0; // Set vertical velocity to 0 (playe
Siriagus 13:7ab71c7c311b 446 entity->onGround = true; // entity has hit ground
Siriagus 13:7ab71c7c311b 447 break; // Skip the while loop as the entity can not move further downwards
Siriagus 13:7ab71c7c311b 448 }
Siriagus 13:7ab71c7c311b 449 else // Can safely move entity without collision
Siriagus 15:d5eb13c4c1c6 450 {
Siriagus 13:7ab71c7c311b 451 ++entityBottom; // Move entity one step down
Siriagus 15:d5eb13c4c1c6 452 entity->onGround = false;
Siriagus 15:d5eb13c4c1c6 453 }
Siriagus 13:7ab71c7c311b 454 }
Siriagus 13:7ab71c7c311b 455
Siriagus 16:caf613d5b85e 456 // Wrapping
Siriagus 16:caf613d5b85e 457 y = (entityBottom - (entity->height - 1));
Siriagus 16:caf613d5b85e 458 if (y >= HEIGHT) // if completely outside map
Siriagus 16:caf613d5b85e 459 y = -entity->height; // wrap to top of map
Siriagus 16:caf613d5b85e 460
Siriagus 16:caf613d5b85e 461 entity->y = y; // (entityBottom - (entity->height - 1)); // Update position when done moving, remember that entity.y refers to upper part of the entity
Siriagus 13:7ab71c7c311b 462 }
Siriagus 13:7ab71c7c311b 463 else // moving up, check collision from top
Siriagus 13:7ab71c7c311b 464 {
Siriagus 13:7ab71c7c311b 465 while(steps--) // Still movement left
Siriagus 13:7ab71c7c311b 466 {
Siriagus 13:7ab71c7c311b 467 collision = false;
Siriagus 16:caf613d5b85e 468
Siriagus 13:7ab71c7c311b 469 for (int i = 0; i < entity->width; ++i) // Check for all x-positions
Siriagus 13:7ab71c7c311b 470 {
Siriagus 16:caf613d5b85e 471 int y1 = ((y-1) + HEIGHT) % HEIGHT; // In case negative, because of wrapping
Siriagus 16:caf613d5b85e 472
Siriagus 16:caf613d5b85e 473 if (map[y1][x+i]) // If moving upwards gives collision for a given x+i
Siriagus 13:7ab71c7c311b 474 {
Siriagus 13:7ab71c7c311b 475 collision = true; // Then we have a collision
Siriagus 13:7ab71c7c311b 476 break; // No further testing needed, skip for loop
Siriagus 13:7ab71c7c311b 477 }
Siriagus 13:7ab71c7c311b 478 }
Siriagus 13:7ab71c7c311b 479
Siriagus 13:7ab71c7c311b 480 if (collision) // If collision was detected
Siriagus 13:7ab71c7c311b 481 {
Siriagus 13:7ab71c7c311b 482 entity->vy = 0; // Set vertical velocity to zero
Siriagus 13:7ab71c7c311b 483 break; // Skip while loop as entity can not move further up
Siriagus 13:7ab71c7c311b 484 }
Siriagus 13:7ab71c7c311b 485 else // If safe to move for all x-values
Siriagus 13:7ab71c7c311b 486 --y; // Move entity one step up
Siriagus 13:7ab71c7c311b 487 }
Siriagus 13:7ab71c7c311b 488
Siriagus 16:caf613d5b85e 489 // Wrapping
Siriagus 16:caf613d5b85e 490 if (y + (entity->height - 1) < 0) // completely outside map (bottom of entity over top of map)
Siriagus 16:caf613d5b85e 491 y = HEIGHT-1 - entity->height - 1; // Sets the altitude.
Siriagus 16:caf613d5b85e 492
Siriagus 13:7ab71c7c311b 493 entity->y = y; // Update vertical position of entity
Siriagus 13:7ab71c7c311b 494 }
Siriagus 15:d5eb13c4c1c6 495 }
Siriagus 15:d5eb13c4c1c6 496
Siriagus 15:d5eb13c4c1c6 497 bool Game::hitTestRect(Rectangle r1, Rectangle r2)
Siriagus 15:d5eb13c4c1c6 498 {
Siriagus 15:d5eb13c4c1c6 499 return ((r1.x + r1.width > r2.x) // r1's right edge to the right of r2's left edge
Siriagus 15:d5eb13c4c1c6 500 && (r1.x < r2.x + r2.width) // r1's left edge to the left of r2's right edge
Siriagus 15:d5eb13c4c1c6 501 && (r1.y + r2.height > r2.y) // r1's bottom lower than r2's top
Siriagus 15:d5eb13c4c1c6 502 && (r1.y < r2.y + r2.height)); // r1's top higher than r2's bottom
Siriagus 15:d5eb13c4c1c6 503
Siriagus 15:d5eb13c4c1c6 504 // Note: Right border: r1.x + r1.width - 1, but we don't need to subtract 1 as we use > instead of >=
Siriagus 15:d5eb13c4c1c6 505 }
Siriagus 15:d5eb13c4c1c6 506
Siriagus 15:d5eb13c4c1c6 507 bool Game::bulletHitMap(Rectangle &bulletColRect, const int map[HEIGHT][WIDTH])
Siriagus 15:d5eb13c4c1c6 508 {
Siriagus 15:d5eb13c4c1c6 509 for (int j = 0; j < bulletColRect.width; ++j)
Siriagus 15:d5eb13c4c1c6 510 {
Siriagus 15:d5eb13c4c1c6 511 if (map[bulletColRect.y][bulletColRect.x + j])
Siriagus 15:d5eb13c4c1c6 512 return true;
Siriagus 15:d5eb13c4c1c6 513 }
Siriagus 15:d5eb13c4c1c6 514
Siriagus 15:d5eb13c4c1c6 515 return false;
Siriagus 16:caf613d5b85e 516 }
Siriagus 16:caf613d5b85e 517
Siriagus 16:caf613d5b85e 518 void Game::moveEnemies()
Siriagus 16:caf613d5b85e 519 {
Siriagus 17:d6a3b29cab31 520 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); )
Siriagus 16:caf613d5b85e 521 {
Siriagus 16:caf613d5b85e 522 Enemy *enemy = *it;
Siriagus 16:caf613d5b85e 523
Siriagus 16:caf613d5b85e 524 // Gravity
Siriagus 16:caf613d5b85e 525 enemy->vy += 1;
Siriagus 16:caf613d5b85e 526
Siriagus 16:caf613d5b85e 527 if (!enemy->dead)
Siriagus 16:caf613d5b85e 528 {
Siriagus 17:d6a3b29cab31 529 // Random movement for enemies
Siriagus 17:d6a3b29cab31 530 if (enemy->onGround && (rand() % 100) < enemy->jumpRate)
Siriagus 17:d6a3b29cab31 531 {
Siriagus 17:d6a3b29cab31 532 // jump
Siriagus 17:d6a3b29cab31 533 enemy->vy = -3;
Siriagus 17:d6a3b29cab31 534 enemy->onGround = false;
Siriagus 17:d6a3b29cab31 535 }
Siriagus 17:d6a3b29cab31 536 else if ((rand() % 100) > 98) // 2% chance
Siriagus 17:d6a3b29cab31 537 {
Siriagus 17:d6a3b29cab31 538 // switch direction
Siriagus 17:d6a3b29cab31 539 enemy->vx *= -1;
Siriagus 17:d6a3b29cab31 540 enemy->facingLeft = (enemy->vx < 0);
Siriagus 17:d6a3b29cab31 541 }
Siriagus 17:d6a3b29cab31 542
Siriagus 16:caf613d5b85e 543 moveWithCollisionTest(enemy, map);
Siriagus 16:caf613d5b85e 544
Siriagus 16:caf613d5b85e 545 // Enemy AI
Siriagus 17:d6a3b29cab31 546 if (enemy->y >= 0)
Siriagus 16:caf613d5b85e 547 {
Siriagus 17:d6a3b29cab31 548 int nextRight = enemy->getRight() + 1; // Next position of right edge if enemy moves to the right
Siriagus 17:d6a3b29cab31 549 nextRight %= WIDTH; // wrapping
Siriagus 17:d6a3b29cab31 550 for (int i = 0; i < enemy->height; ++i) // Check for all heighs
Siriagus 16:caf613d5b85e 551 {
Siriagus 17:d6a3b29cab31 552 // Check if crashing if moving right or left. Bounds should already be limited by moveWithCollisionTest!
Siriagus 17:d6a3b29cab31 553 if (map[enemy->y + i][nextRight] || map[enemy->y + i][enemy->x - 1])
Siriagus 17:d6a3b29cab31 554 {
Siriagus 17:d6a3b29cab31 555 enemy->vx *= -1; // move in opposite direction
Siriagus 17:d6a3b29cab31 556 enemy->facingLeft = !enemy->facingLeft; // toggle direction
Siriagus 17:d6a3b29cab31 557 break; // no further testing required
Siriagus 17:d6a3b29cab31 558 }
Siriagus 16:caf613d5b85e 559 }
Siriagus 16:caf613d5b85e 560 }
Siriagus 17:d6a3b29cab31 561
Siriagus 17:d6a3b29cab31 562 ++it; // go to next enemy
Siriagus 16:caf613d5b85e 563 }
Siriagus 17:d6a3b29cab31 564 else // if enemy is dead
Siriagus 16:caf613d5b85e 565 {
Siriagus 16:caf613d5b85e 566 enemy->y += enemy->vy;
Siriagus 16:caf613d5b85e 567 enemy->x += enemy->vx;
Siriagus 17:d6a3b29cab31 568
Siriagus 17:d6a3b29cab31 569 if (enemy->y >= HEIGHT) // if outside map (and dead)
Siriagus 17:d6a3b29cab31 570 {
Siriagus 17:d6a3b29cab31 571 delete enemy;
Siriagus 17:d6a3b29cab31 572 it = enemies.erase(it); // remove and go to next enemy
Siriagus 17:d6a3b29cab31 573 }
Siriagus 17:d6a3b29cab31 574 else
Siriagus 17:d6a3b29cab31 575 ++it; // go to next enemy
Siriagus 16:caf613d5b85e 576 }
Siriagus 16:caf613d5b85e 577 }
Siriagus 17:d6a3b29cab31 578 }
Siriagus 17:d6a3b29cab31 579
Siriagus 17:d6a3b29cab31 580 void Game::renderScore()
Siriagus 17:d6a3b29cab31 581 {
Siriagus 17:d6a3b29cab31 582 int s = (Global::score < 100000) ? Global::score : 99999; // Max possible score is 99999.
Siriagus 17:d6a3b29cab31 583
Siriagus 17:d6a3b29cab31 584 // Read digits
Siriagus 17:d6a3b29cab31 585 int digits[5]; // max five
Siriagus 17:d6a3b29cab31 586 // Count the number of digits in the score
Siriagus 17:d6a3b29cab31 587 int numDigits = 0;
Siriagus 17:d6a3b29cab31 588 do
Siriagus 17:d6a3b29cab31 589 {
Siriagus 17:d6a3b29cab31 590 digits[numDigits] = s % 10;
Siriagus 17:d6a3b29cab31 591 s /= 10;
Siriagus 17:d6a3b29cab31 592 ++numDigits;
Siriagus 17:d6a3b29cab31 593 } while (s != 0 && numDigits < 5);
Siriagus 17:d6a3b29cab31 594
Siriagus 17:d6a3b29cab31 595
Siriagus 17:d6a3b29cab31 596 // Draw score
Siriagus 17:d6a3b29cab31 597 int xStart = 79;
Siriagus 17:d6a3b29cab31 598 int xStep = 4; // width + 1
Siriagus 17:d6a3b29cab31 599 int y = 2;
Siriagus 17:d6a3b29cab31 600 int x;
Siriagus 17:d6a3b29cab31 601
Siriagus 17:d6a3b29cab31 602 for (int i = 0; i < numDigits; ++i)
Siriagus 17:d6a3b29cab31 603 {
Siriagus 17:d6a3b29cab31 604 x = xStart - i * xStep;
Siriagus 17:d6a3b29cab31 605
Siriagus 17:d6a3b29cab31 606 switch (digits[i])
Siriagus 17:d6a3b29cab31 607 {
Siriagus 17:d6a3b29cab31 608 case 1:
Siriagus 17:d6a3b29cab31 609 drawImage(Number::One, x, y);
Siriagus 17:d6a3b29cab31 610 break;
Siriagus 17:d6a3b29cab31 611
Siriagus 17:d6a3b29cab31 612 case 2:
Siriagus 17:d6a3b29cab31 613 drawImage(Number::Two, x, y);
Siriagus 17:d6a3b29cab31 614 break;
Siriagus 17:d6a3b29cab31 615
Siriagus 17:d6a3b29cab31 616 case 3:
Siriagus 17:d6a3b29cab31 617 drawImage(Number::Three, x, y);
Siriagus 17:d6a3b29cab31 618 break;
Siriagus 17:d6a3b29cab31 619
Siriagus 17:d6a3b29cab31 620 case 4:
Siriagus 17:d6a3b29cab31 621 drawImage(Number::Four, x, y);
Siriagus 17:d6a3b29cab31 622 break;
Siriagus 17:d6a3b29cab31 623
Siriagus 17:d6a3b29cab31 624 case 5:
Siriagus 17:d6a3b29cab31 625 drawImage(Number::Five, x, y);
Siriagus 17:d6a3b29cab31 626 break;
Siriagus 17:d6a3b29cab31 627
Siriagus 17:d6a3b29cab31 628 case 6:
Siriagus 17:d6a3b29cab31 629 drawImage(Number::Six, x, y);
Siriagus 17:d6a3b29cab31 630 break;
Siriagus 17:d6a3b29cab31 631
Siriagus 17:d6a3b29cab31 632 case 7:
Siriagus 17:d6a3b29cab31 633 drawImage(Number::Seven, x, y);
Siriagus 17:d6a3b29cab31 634 break;
Siriagus 17:d6a3b29cab31 635
Siriagus 17:d6a3b29cab31 636 case 8:
Siriagus 17:d6a3b29cab31 637 drawImage(Number::Eight, x, y);
Siriagus 17:d6a3b29cab31 638 break;
Siriagus 17:d6a3b29cab31 639
Siriagus 17:d6a3b29cab31 640 case 9:
Siriagus 17:d6a3b29cab31 641 drawImage(Number::Nine, x, y);
Siriagus 17:d6a3b29cab31 642 break;
Siriagus 17:d6a3b29cab31 643
Siriagus 17:d6a3b29cab31 644 case 0:
Siriagus 17:d6a3b29cab31 645 default:
Siriagus 17:d6a3b29cab31 646 drawImage(Number::Zero, x, y);
Siriagus 17:d6a3b29cab31 647 break;
Siriagus 17:d6a3b29cab31 648 }
Siriagus 17:d6a3b29cab31 649
Siriagus 17:d6a3b29cab31 650 }
Siriagus 17:d6a3b29cab31 651 }
Siriagus 17:d6a3b29cab31 652
Siriagus 17:d6a3b29cab31 653 void Game::respawnPlayer()
Siriagus 17:d6a3b29cab31 654 {
Siriagus 17:d6a3b29cab31 655 player.x = 74;
Siriagus 17:d6a3b29cab31 656 player.y = 31;
Siriagus 17:d6a3b29cab31 657 player.vx = player.vy = 0;
Siriagus 17:d6a3b29cab31 658 player.facingLeft = true;
Siriagus 17:d6a3b29cab31 659 player.dead = false;
Siriagus 7:678873947b29 660 }