#include <SPI.h>
#include "Item.h"
#include <Wire.h>
#include "Sound.h"
#include "Enemy.h"
#include "Player.h"
#include "Graphics.h"
#include "Projectile.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
constexpr uint8_t PROJECTILE_MAX = 16;
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr uint8_t OLED_RESET = -1;
constexpr uint8_t SCREEN_ADDRESS = 0x3C;
constexpr uint8_t LEFT_BUTTON = 2;
constexpr uint8_t RIGHT_BUTTON = 3;
constexpr uint8_t JUMP_BUTTON = 4;
constexpr uint8_t SHOOT_BUTTON = 5;
Item item;
Enemy enemy;
Player player;
Projectile projectiles [PROJECTILE_MAX];
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int score;
int8_t levelX;
uint8_t platformX = 30;
uint8_t projIndex;
unsigned long shootTimer;
unsigned long enemyShootTimer;
void setup()
{
randomSeed(analogRead(A0));
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LEFT_BUTTON, INPUT_PULLUP);
pinMode(RIGHT_BUTTON, INPUT_PULLUP);
pinMode(JUMP_BUTTON, INPUT_PULLUP);
pinMode(SHOOT_BUTTON, INPUT_PULLUP);
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.clearDisplay();
display.drawBitmap(0, 0, titleScreen, 128, 64, 1);
display.display();
delay(500);
playIntroMusic();
display.clearDisplay();
}
void drawGame()
{
display.clearDisplay();
if (player.takingDamage == false || player.damageCounter % 2 == 0)
{
if (player.dir == 1)
{
display.drawBitmap(player.coords[0], player.coords[1],
playerRightBmpArr[player.animFrame], 16, 16, 1);
}
else
{
display.drawBitmap(player.coords[0], player.coords[1],
playerLeftBmpArr[player.animFrame], 16, 16, 1);
}
}
if (enemy.alive)
{
if (enemy.boss)
{
display.drawBitmap(enemy.coords[0], enemy.coords[1],
bossBmpArr[enemy.animFrame], 32, 32, 1);
}
else
{
if (enemy.dir == 1)
{
display.drawBitmap(enemy.coords[0], enemy.coords[1],
enemyRightBmpArr[enemy.animFrame], 16, 16, 1);
}
else
{
display.drawBitmap(enemy.coords[0], enemy.coords[1],
enemyRightBmpArr[enemy.animFrame], 16, 16, 1);
}
}
}
for (int i = 0; i < PROJECTILE_MAX; ++i)
{
if (projectiles[i].active)
{
display.drawBitmap(projectiles[i].coords[0], projectiles[i].coords[1],
projectiles[i].hostile ? enProjBmp : projBmp, 4, 4, 1
);
}
}
for (int i = 0; i < levelX + 43; i++)
{
display.drawPixel(i, 51, SSD1306_WHITE);
}
for (int i = levelX + 80; i < 128; i++)
{
display.drawPixel(i, 51, SSD1306_WHITE);
}
for (int i = platformX; i < platformX + 22; i++)
{
display.drawPixel(i, 31, SSD1306_WHITE);
}
if (item.exists)
{
display.drawBitmap(item.coords[0], item.coords[1],
item.itemType == healthItem ? healthBmp :
item.itemType == speedItem ? speedBmp :
rofBmp, 8, 8, 1
);
}
display.setCursor(0, 0);
display.print(F("HP: "));
display.print(player.hp);
display.print(F(" SCORE: "));
display.print(score);
if (player.rof < 500)
{
display.drawBitmap(0, 56, rofBmp, 8, 8, 1);
}
if (player.speed > 1)
{
display.drawBitmap(12, 56, speedBmp, 8, 8, 1);
}
display.display();
}
void spawnProjectile(int8_t coords[2], int8_t dir, bool hostile)
{
uint8_t offset = hostile && enemy.boss ? random(5, 25) : 5;
projectiles[projIndex].dir = dir;
projectiles[projIndex].coords[0] = coords[0];
projectiles[projIndex].coords[1] = coords[1] + offset;
projectiles[projIndex].hostile = hostile;
projectiles[projIndex].active = true;
projIndex = projIndex < PROJECTILE_MAX - 1 ? projIndex + 1 : 0;
shootSound();
}
void nextRoom()
{
score += 10;
item.exists = false;
enemy.alive = false;
player.coords[0] = 0;
levelX = random(-24, 32);
platformX = random(levelX + 40, levelX + 80);
for (int i = 0; i < 10; ++i)
{
projectiles[i].active = false;
}
if (random(1, 100) > 50)
{
if (score % 100 == 0)
{
enemy.spawn(levelX, true);
playBossMusic();
}
else
{
enemy.spawn(levelX, false);
}
}
if (random(1, 100) > 75)
{
item.spawn(platformX);
}
}
void gameOver()
{
display.clearDisplay();
display.setCursor(40, 18);
display.print(F("GAME OVER"));
display.setCursor(40, 28);
display.print(F("SCORE: "));
display.print(score);
display.display();
gameOverSound();
delay(3000);
score = 0;
levelX = 0;
platformX = 30;
player.reset();
item.exists = false;
enemy.alive = false;
for (int i = 0; i < 10; ++i)
{
projectiles[i].active = false;
}
if (random(1, 100) > 50)
{
enemy.spawn(levelX, false);
}
if (random(1, 100) > 75)
{
item.spawn(platformX);
}
}
int8_t projectileDirection()
{
if (player.coords[0] < enemy.coords[0] && enemy.dir == -1)
{
return -1;
}
if(player.coords[0] > enemy.coords[0] && enemy.dir == 1)
{
return 1;
}
return 0;
}
void getInput()
{
player.moving = false;
if (player.coords[0] > 0)
{
if (!digitalRead(LEFT_BUTTON))
{
player.moveLeft(levelX);
}
}
if (player.coords[0] < 120)
{
if (!digitalRead(RIGHT_BUTTON))
{
player.moveRight(levelX);
}
}
else
{
nextRoom();
}
if (player.canJump(levelX, platformX))
{
if (!digitalRead(JUMP_BUTTON))
{
player.jump(levelX, platformX);
}
}
unsigned long now = millis();
if (now - shootTimer > player.rof)
{
if (!digitalRead(SHOOT_BUTTON))
{
spawnProjectile(player.coords, player.dir, false);
shootTimer = now;
}
}
}
void applyGravity()
{
if (player.jumping)
{
int8_t jumpEnd = player.jumpStart - 24;
if (player.coords[1] > jumpEnd && player.coords[1] > 0)
{
player.coords[1] -= 2;
}
else
{
player.jumping = false;
}
}
else if (player.coords[1] < player.lowLimit(levelX, platformX) - 6)
{
player.coords[1] += 1;
}
else if (player.coords[1] < player.lowLimit(levelX, platformX))
{
player.coords[1] = player.lowLimit(levelX, platformX);
}
}
void combatUpdate()
{
unsigned long now = millis();
if (now - enemyShootTimer > enemy.rof)
{
int8_t projDir = projectileDirection();
if (random(0, 100) > 50 && enemy.alive && projDir != 0)
{
spawnProjectile(enemy.coords, projDir, true);
}
enemyShootTimer = now;
}
}
void loop()
{
player.update();
enemy.update(player);
combatUpdate();
if (item.exists)
{
item.update(player);
}
for (int i = 0; i < PROJECTILE_MAX; ++i)
{
projectiles[i].update(player, enemy, score);
}
drawGame();
getInput();
applyGravity();
if (!player.moving)
{
player.animFrame = 8;
}
if (player.hp <= 0 || player.coords[1] >= 48)
{
gameOver();
}
}