734 lines
22 KiB
C++
734 lines
22 KiB
C++
// Required libs
|
|
#include "FastLED.h"
|
|
#include "I2Cdev.h"
|
|
#include "MPU6050.h"
|
|
#include "Wire.h"
|
|
#include "toneAC.h"
|
|
#include "iSin.h"
|
|
#include "RunningMedian.h"
|
|
|
|
// Included libs
|
|
#include "Enemy.h"
|
|
#include "Particle.h"
|
|
#include "Spawner.h"
|
|
#include "Lava.h"
|
|
#include "Boss.h"
|
|
#include "Conveyor.h"
|
|
|
|
// MPU
|
|
MPU6050 accelgyro;
|
|
int16_t ax, ay, az;
|
|
int16_t gx, gy, gz;
|
|
|
|
// LED setup
|
|
#define NUM_LEDS 475
|
|
#define DATA_PIN 3
|
|
#define CLOCK_PIN 4
|
|
#define LED_COLOR_ORDER BGR //if colours aren't working, try GRB or GBR
|
|
#define BRIGHTNESS 150 //Use a lower value for lower current power supplies(<2 amps)
|
|
#define DIRECTION 1 // 0 = right to left, 1 = left to right
|
|
#define MIN_REDRAW_INTERVAL 16 // Min redraw interval (ms) 33 = 30fps / 16 = 63fps
|
|
#define USE_GRAVITY 1 // 0/1 use gravity (LED strip going up wall)
|
|
#define BEND_POINT 550 // 0/1000 point at which the LED strip goes up the wall
|
|
#define LED_TYPE APA102//type of LED strip to use(APA102 - DotStar, WS2811 - NeoPixel) For Neopixels, uncomment line #108 and comment out line #106
|
|
|
|
// GAME
|
|
long previousMillis = 0; // Time of the last redraw
|
|
int levelNumber = 0;
|
|
long lastInputTime = 0;
|
|
#define TIMEOUT 30000
|
|
#define LEVEL_COUNT 9
|
|
#define MAX_VOLUME 10
|
|
iSin isin = iSin();
|
|
|
|
// JOYSTICK
|
|
#define JOYSTICK_ORIENTATION 1 // 0, 1 or 2 to set the angle of the joystick
|
|
#define JOYSTICK_DIRECTION 1 // 0/1 to flip joystick direction
|
|
#define ATTACK_THRESHOLD 30000 // The threshold that triggers an attack
|
|
#define JOYSTICK_DEADZONE 5 // Angle to ignore
|
|
int joystickTilt = 0; // Stores the angle of the joystick
|
|
int joystickWobble = 0; // Stores the max amount of acceleration (wobble)
|
|
|
|
// WOBBLE ATTACK
|
|
#define ATTACK_WIDTH 70 // Width of the wobble attack, world is 1000 wide
|
|
#define ATTACK_DURATION 500 // Duration of a wobble attack (ms)
|
|
long attackMillis = 0; // Time the attack started
|
|
bool attacking = 0; // Is the attack in progress?
|
|
#define BOSS_WIDTH 40
|
|
|
|
// PLAYER
|
|
#define MAX_PLAYER_SPEED 10 // Max move speed of the player
|
|
char* stage; // what stage the game is at (PLAY/DEAD/WIN/GAMEOVER)
|
|
long stageStartTime; // Stores the time the stage changed for stages that are time based
|
|
int playerPosition; // Stores the player position
|
|
int playerPositionModifier; // +/- adjustment to player position
|
|
bool playerAlive;
|
|
long killTime;
|
|
int lives = 3;
|
|
|
|
// POOLS
|
|
int lifeLEDs[3] = {52, 50, 40};
|
|
Enemy enemyPool[10] = {
|
|
Enemy(), Enemy(), Enemy(), Enemy(), Enemy(), Enemy(), Enemy(), Enemy(), Enemy(), Enemy()
|
|
};
|
|
int const enemyCount = 10;
|
|
Particle particlePool[40] = {
|
|
Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle(), Particle()
|
|
};
|
|
int const particleCount = 40;
|
|
Spawner spawnPool[2] = {
|
|
Spawner(), Spawner()
|
|
};
|
|
int const spawnCount = 2;
|
|
Lava lavaPool[4] = {
|
|
Lava(), Lava(), Lava(), Lava()
|
|
};
|
|
int const lavaCount = 4;
|
|
Conveyor conveyorPool[2] = {
|
|
Conveyor(), Conveyor()
|
|
};
|
|
int const conveyorCount = 2;
|
|
Boss boss = Boss();
|
|
|
|
CRGB leds[NUM_LEDS];
|
|
RunningMedian MPUAngleSamples = RunningMedian(5);
|
|
RunningMedian MPUWobbleSamples = RunningMedian(5);
|
|
|
|
void setup() {
|
|
Serial.begin(9600);
|
|
while (!Serial);
|
|
|
|
// MPU
|
|
Wire.begin();
|
|
accelgyro.initialize();
|
|
|
|
// Fast LED
|
|
FastLED.addLeds<LED_TYPE, DATA_PIN, CLOCK_PIN, LED_COLOR_ORDER>(leds, NUM_LEDS);
|
|
//If using Neopixels, use
|
|
//FastLED.addLeds<LED_TYPE, DATA_PIN, LED_COLOR_ORDER>(leds, NUM_LEDS);
|
|
FastLED.setBrightness(BRIGHTNESS);
|
|
FastLED.setDither(1);
|
|
|
|
// Life LEDs
|
|
for(int i = 0; i<3; i++){
|
|
pinMode(lifeLEDs[i], OUTPUT);
|
|
digitalWrite(lifeLEDs[i], HIGH);
|
|
}
|
|
|
|
loadLevel();
|
|
}
|
|
|
|
void loop() {
|
|
long mm = millis();
|
|
int brightness = 0;
|
|
|
|
if(stage == "PLAY"){
|
|
if(attacking){
|
|
SFXattacking();
|
|
}else{
|
|
SFXtilt(joystickTilt);
|
|
}
|
|
}else if(stage == "DEAD"){
|
|
SFXdead();
|
|
}
|
|
|
|
if (mm - previousMillis >= MIN_REDRAW_INTERVAL) {
|
|
getInput();
|
|
long frameTimer = mm;
|
|
previousMillis = mm;
|
|
|
|
if(abs(joystickTilt) > JOYSTICK_DEADZONE){
|
|
lastInputTime = mm;
|
|
if(stage == "SCREENSAVER"){
|
|
levelNumber = -1;
|
|
stageStartTime = mm;
|
|
stage = "WIN";
|
|
}
|
|
}else{
|
|
if(lastInputTime+TIMEOUT < mm){
|
|
stage = "SCREENSAVER";
|
|
}
|
|
}
|
|
if(stage == "SCREENSAVER"){
|
|
screenSaverTick();
|
|
}else if(stage == "PLAY"){
|
|
// PLAYING
|
|
if(attacking && attackMillis+ATTACK_DURATION < mm) attacking = 0;
|
|
|
|
// If not attacking, check if they should be
|
|
if(!attacking && joystickWobble > ATTACK_THRESHOLD){
|
|
attackMillis = mm;
|
|
attacking = 1;
|
|
}
|
|
|
|
// If still not attacking, move!
|
|
playerPosition += playerPositionModifier;
|
|
if(!attacking){
|
|
int moveAmount = (joystickTilt/6.0);
|
|
if(DIRECTION) moveAmount = -moveAmount;
|
|
moveAmount = constrain(moveAmount, -MAX_PLAYER_SPEED, MAX_PLAYER_SPEED);
|
|
playerPosition -= moveAmount;
|
|
if(playerPosition < 0) playerPosition = 0;
|
|
if(playerPosition >= 1000 && !boss.Alive()) {
|
|
// Reached exit!
|
|
levelComplete();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(inLava(playerPosition)){
|
|
die();
|
|
}
|
|
|
|
// Ticks and draw calls
|
|
FastLED.clear();
|
|
tickConveyors();
|
|
tickSpawners();
|
|
tickBoss();
|
|
tickLava();
|
|
tickEnemies();
|
|
drawPlayer();
|
|
drawAttack();
|
|
drawExit();
|
|
}else if(stage == "DEAD"){
|
|
// DEAD
|
|
FastLED.clear();
|
|
if(!tickParticles()){
|
|
loadLevel();
|
|
}
|
|
}else if(stage == "WIN"){
|
|
// LEVEL COMPLETE
|
|
FastLED.clear();
|
|
if(stageStartTime+500 > mm){
|
|
int n = max(map(((mm-stageStartTime)), 0, 500, NUM_LEDS, 0), 0);
|
|
for(int i = NUM_LEDS; i>= n; i--){
|
|
brightness = 255;
|
|
leds[i] = CRGB(0, brightness, 0);
|
|
}
|
|
SFXwin();
|
|
}else if(stageStartTime+1000 > mm){
|
|
int n = max(map(((mm-stageStartTime)), 500, 1000, NUM_LEDS, 0), 0);
|
|
for(int i = 0; i< n; i++){
|
|
brightness = 255;
|
|
leds[i] = CRGB(0, brightness, 0);
|
|
}
|
|
SFXwin();
|
|
}else if(stageStartTime+1200 > mm){
|
|
leds[0] = CRGB(0, 255, 0);
|
|
}else{
|
|
nextLevel();
|
|
}
|
|
}else if(stage == "COMPLETE"){
|
|
FastLED.clear();
|
|
SFXcomplete();
|
|
if(stageStartTime+500 > mm){
|
|
int n = max(map(((mm-stageStartTime)), 0, 500, NUM_LEDS, 0), 0);
|
|
for(int i = NUM_LEDS; i>= n; i--){
|
|
brightness = (sin(((i*10)+mm)/500.0)+1)*255;
|
|
leds[i].setHSV(brightness, 255, 50);
|
|
}
|
|
}else if(stageStartTime+5000 > mm){
|
|
for(int i = NUM_LEDS; i>= 0; i--){
|
|
brightness = (sin(((i*10)+mm)/500.0)+1)*255;
|
|
leds[i].setHSV(brightness, 255, 50);
|
|
}
|
|
}else if(stageStartTime+5500 > mm){
|
|
int n = max(map(((mm-stageStartTime)), 5000, 5500, NUM_LEDS, 0), 0);
|
|
for(int i = 0; i< n; i++){
|
|
brightness = (sin(((i*10)+mm)/500.0)+1)*255;
|
|
leds[i].setHSV(brightness, 255, 50);
|
|
}
|
|
}else{
|
|
nextLevel();
|
|
}
|
|
}else if(stage == "GAMEOVER"){
|
|
// GAME OVER!
|
|
FastLED.clear();
|
|
stageStartTime = 0;
|
|
}
|
|
|
|
Serial.print(millis()-mm);
|
|
Serial.print(" - ");
|
|
FastLED.show();
|
|
Serial.println(millis()-mm);
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// ------------ LEVELS -------------
|
|
// ---------------------------------
|
|
void loadLevel(){
|
|
updateLives();
|
|
cleanupLevel();
|
|
playerPosition = 0;
|
|
playerAlive = 1;
|
|
switch(levelNumber){
|
|
case 0:
|
|
// Left or right?
|
|
playerPosition = 200;
|
|
spawnEnemy(1, 0, 0, 0);
|
|
break;
|
|
case 1:
|
|
// Slow moving enemy
|
|
spawnEnemy(900, 0, 1, 0);
|
|
break;
|
|
case 2:
|
|
// Spawning enemies at exit every 2 seconds
|
|
spawnPool[0].Spawn(1000, 3000, 2, 0, 0);
|
|
break;
|
|
case 3:
|
|
// Lava intro
|
|
spawnLava(400, 490, 2000, 2000, 0, "OFF");
|
|
spawnPool[0].Spawn(1000, 5500, 3, 0, 0);
|
|
break;
|
|
case 4:
|
|
// Sin enemy
|
|
spawnEnemy(700, 1, 7, 275);
|
|
spawnEnemy(500, 1, 5, 250);
|
|
break;
|
|
case 5:
|
|
// Conveyor
|
|
spawnConveyor(100, 600, -1);
|
|
spawnEnemy(800, 0, 0, 0);
|
|
break;
|
|
case 6:
|
|
// Conveyor of enemies
|
|
spawnConveyor(50, 1000, 1);
|
|
spawnEnemy(300, 0, 0, 0);
|
|
spawnEnemy(400, 0, 0, 0);
|
|
spawnEnemy(500, 0, 0, 0);
|
|
spawnEnemy(600, 0, 0, 0);
|
|
spawnEnemy(700, 0, 0, 0);
|
|
spawnEnemy(800, 0, 0, 0);
|
|
spawnEnemy(900, 0, 0, 0);
|
|
break;
|
|
case 7:
|
|
// Lava run
|
|
spawnLava(195, 300, 2000, 2000, 0, "OFF");
|
|
spawnLava(350, 455, 2000, 2000, 0, "OFF");
|
|
spawnLava(510, 610, 2000, 2000, 0, "OFF");
|
|
spawnLava(660, 760, 2000, 2000, 0, "OFF");
|
|
spawnPool[0].Spawn(1000, 3800, 4, 0, 0);
|
|
break;
|
|
case 8:
|
|
// Sin enemy #2
|
|
spawnEnemy(700, 1, 7, 275);
|
|
spawnEnemy(500, 1, 5, 250);
|
|
spawnPool[0].Spawn(1000, 5500, 4, 0, 3000);
|
|
spawnPool[1].Spawn(0, 5500, 5, 1, 10000);
|
|
spawnConveyor(100, 900, -1);
|
|
break;
|
|
case 9:
|
|
// Boss
|
|
spawnBoss();
|
|
break;
|
|
}
|
|
stageStartTime = millis();
|
|
stage = "PLAY";
|
|
}
|
|
|
|
void spawnBoss(){
|
|
boss.Spawn();
|
|
moveBoss();
|
|
}
|
|
|
|
void moveBoss(){
|
|
int spawnSpeed = 2500;
|
|
if(boss._lives == 2) spawnSpeed = 2000;
|
|
if(boss._lives == 1) spawnSpeed = 1500;
|
|
spawnPool[0].Spawn(boss._pos, spawnSpeed, 3, 0, 0);
|
|
spawnPool[1].Spawn(boss._pos, spawnSpeed, 3, 1, 0);
|
|
}
|
|
|
|
void spawnEnemy(int pos, int dir, int sp, int wobble){
|
|
for(int e = 0; e<enemyCount; e++){
|
|
if(!enemyPool[e].Alive()){
|
|
enemyPool[e].Spawn(pos, dir, sp, wobble);
|
|
enemyPool[e].playerSide = pos > playerPosition?1:-1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void spawnLava(int left, int right, int ontime, int offtime, int offset, char* state){
|
|
for(int i = 0; i<lavaCount; i++){
|
|
if(!lavaPool[i].Alive()){
|
|
lavaPool[i].Spawn(left, right, ontime, offtime, offset, state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void spawnConveyor(int startPoint, int endPoint, int dir){
|
|
for(int i = 0; i<conveyorCount; i++){
|
|
if(!conveyorPool[i]._alive){
|
|
conveyorPool[i].Spawn(startPoint, endPoint, dir);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cleanupLevel(){
|
|
for(int i = 0; i<enemyCount; i++){
|
|
enemyPool[i].Kill();
|
|
}
|
|
for(int i = 0; i<particleCount; i++){
|
|
particlePool[i].Kill();
|
|
}
|
|
for(int i = 0; i<spawnCount; i++){
|
|
spawnPool[i].Kill();
|
|
}
|
|
for(int i = 0; i<lavaCount; i++){
|
|
lavaPool[i].Kill();
|
|
}
|
|
for(int i = 0; i<conveyorCount; i++){
|
|
conveyorPool[i].Kill();
|
|
}
|
|
boss.Kill();
|
|
}
|
|
|
|
void levelComplete(){
|
|
stageStartTime = millis();
|
|
stage = "WIN";
|
|
if(levelNumber == LEVEL_COUNT) stage = "COMPLETE";
|
|
lives = 3;
|
|
updateLives();
|
|
}
|
|
|
|
void nextLevel(){
|
|
levelNumber ++;
|
|
if(levelNumber > LEVEL_COUNT) levelNumber = 0;
|
|
loadLevel();
|
|
}
|
|
|
|
void gameOver(){
|
|
levelNumber = 0;
|
|
loadLevel();
|
|
}
|
|
|
|
void die(){
|
|
playerAlive = 0;
|
|
if(levelNumber > 0) lives --;
|
|
updateLives();
|
|
if(lives == 0){
|
|
levelNumber = 0;
|
|
lives = 3;
|
|
}
|
|
for(int p = 0; p < particleCount; p++){
|
|
particlePool[p].Spawn(playerPosition);
|
|
}
|
|
stageStartTime = millis();
|
|
stage = "DEAD";
|
|
killTime = millis();
|
|
}
|
|
|
|
// ----------------------------------
|
|
// -------- TICKS & RENDERS ---------
|
|
// ----------------------------------
|
|
void tickEnemies(){
|
|
for(int i = 0; i<enemyCount; i++){
|
|
if(enemyPool[i].Alive()){
|
|
enemyPool[i].Tick();
|
|
// Hit attack?
|
|
if(attacking){
|
|
if(enemyPool[i]._pos > playerPosition-(ATTACK_WIDTH/2) && enemyPool[i]._pos < playerPosition+(ATTACK_WIDTH/2)){
|
|
enemyPool[i].Kill();
|
|
SFXkill();
|
|
}
|
|
}
|
|
if(inLava(enemyPool[i]._pos)){
|
|
enemyPool[i].Kill();
|
|
SFXkill();
|
|
}
|
|
// Draw (if still alive)
|
|
if(enemyPool[i].Alive()) {
|
|
leds[getLED(enemyPool[i]._pos)] = CRGB(255, 0, 0);
|
|
}
|
|
// Hit player?
|
|
if(
|
|
(enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= playerPosition) ||
|
|
(enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= playerPosition)
|
|
){
|
|
die();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void tickBoss(){
|
|
// DRAW
|
|
if(boss.Alive()){
|
|
boss._ticks ++;
|
|
for(int i = getLED(boss._pos-BOSS_WIDTH/2); i<=getLED(boss._pos+BOSS_WIDTH/2); i++){
|
|
leds[i] = CRGB::DarkRed;
|
|
leds[i] %= 100;
|
|
}
|
|
// CHECK COLLISION
|
|
if(getLED(playerPosition) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(playerPosition) < getLED(boss._pos + BOSS_WIDTH)){
|
|
die();
|
|
return;
|
|
}
|
|
// CHECK FOR ATTACK
|
|
if(attacking){
|
|
if(
|
|
(getLED(playerPosition+(ATTACK_WIDTH/2)) >= getLED(boss._pos - BOSS_WIDTH/2) && getLED(playerPosition+(ATTACK_WIDTH/2)) <= getLED(boss._pos + BOSS_WIDTH/2)) ||
|
|
(getLED(playerPosition-(ATTACK_WIDTH/2)) <= getLED(boss._pos + BOSS_WIDTH/2) && getLED(playerPosition-(ATTACK_WIDTH/2)) >= getLED(boss._pos - BOSS_WIDTH/2))
|
|
){
|
|
boss.Hit();
|
|
if(boss.Alive()){
|
|
moveBoss();
|
|
}else{
|
|
spawnPool[0].Kill();
|
|
spawnPool[1].Kill();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawPlayer(){
|
|
leds[getLED(playerPosition)] = CRGB(0, 255, 0);
|
|
}
|
|
|
|
void drawExit(){
|
|
if(!boss.Alive()){
|
|
leds[NUM_LEDS-1] = CRGB(0, 0, 255);
|
|
}
|
|
}
|
|
|
|
void tickSpawners(){
|
|
long mm = millis();
|
|
for(int s = 0; s<spawnCount; s++){
|
|
if(spawnPool[s].Alive() && spawnPool[s]._activate < mm){
|
|
if(spawnPool[s]._lastSpawned + spawnPool[s]._rate < mm || spawnPool[s]._lastSpawned == 0){
|
|
spawnEnemy(spawnPool[s]._pos, spawnPool[s]._dir, spawnPool[s]._sp, 0);
|
|
spawnPool[s]._lastSpawned = mm;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void tickLava(){
|
|
int A, B, p, i, brightness, flicker;
|
|
long mm = millis();
|
|
Lava LP;
|
|
for(i = 0; i<lavaCount; i++){
|
|
flicker = random8(5);
|
|
LP = lavaPool[i];
|
|
if(LP.Alive()){
|
|
A = getLED(LP._left);
|
|
B = getLED(LP._right);
|
|
if(LP._state == "OFF"){
|
|
if(LP._lastOn + LP._offtime < mm){
|
|
LP._state = "ON";
|
|
LP._lastOn = mm;
|
|
}
|
|
for(p = A; p<= B; p++){
|
|
leds[p] = CRGB(3+flicker, (3+flicker)/1.5, 0);
|
|
}
|
|
}else if(LP._state == "ON"){
|
|
if(LP._lastOn + LP._ontime < mm){
|
|
LP._state = "OFF";
|
|
LP._lastOn = mm;
|
|
}
|
|
for(p = A; p<= B; p++){
|
|
leds[p] = CRGB(150+flicker, 100+flicker, 0);
|
|
}
|
|
}
|
|
}
|
|
lavaPool[i] = LP;
|
|
}
|
|
}
|
|
|
|
bool tickParticles(){
|
|
bool stillActive = false;
|
|
for(int p = 0; p < particleCount; p++){
|
|
if(particlePool[p].Alive()){
|
|
particlePool[p].Tick(USE_GRAVITY);
|
|
leds[getLED(particlePool[p]._pos)] += CRGB(particlePool[p]._power, 0, 0);
|
|
stillActive = true;
|
|
}
|
|
}
|
|
return stillActive;
|
|
}
|
|
|
|
void tickConveyors(){
|
|
int b, dir, n, i, ss, ee, led;
|
|
long m = 10000+millis();
|
|
playerPositionModifier = 0;
|
|
|
|
for(i = 0; i<conveyorCount; i++){
|
|
if(conveyorPool[i]._alive){
|
|
dir = conveyorPool[i]._dir;
|
|
ss = getLED(conveyorPool[i]._startPoint);
|
|
ee = getLED(conveyorPool[i]._endPoint);
|
|
for(led = ss; led<ee; led++){
|
|
b = 5;
|
|
n = (-led + (m/100)) % 5;
|
|
if(dir == -1) n = (led + (m/100)) % 5;
|
|
b = (5-n)/2.0;
|
|
if(b > 0) leds[led] = CRGB(0, 0, b);
|
|
}
|
|
|
|
if(playerPosition > conveyorPool[i]._startPoint && playerPosition < conveyorPool[i]._endPoint){
|
|
if(dir == -1){
|
|
playerPositionModifier = -(MAX_PLAYER_SPEED-4);
|
|
}else{
|
|
playerPositionModifier = (MAX_PLAYER_SPEED-4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawAttack(){
|
|
if(!attacking) return;
|
|
int n = map(millis() - attackMillis, 0, ATTACK_DURATION, 100, 5);
|
|
for(int i = getLED(playerPosition-(ATTACK_WIDTH/2))+1; i<=getLED(playerPosition+(ATTACK_WIDTH/2))-1; i++){
|
|
leds[i] = CRGB(0, 0, n);
|
|
}
|
|
if(n > 90) {
|
|
n = 255;
|
|
leds[getLED(playerPosition)] = CRGB(255, 255, 255);
|
|
}else{
|
|
n = 0;
|
|
leds[getLED(playerPosition)] = CRGB(0, 255, 0);
|
|
}
|
|
leds[getLED(playerPosition-(ATTACK_WIDTH/2))] = CRGB(n, n, 255);
|
|
leds[getLED(playerPosition+(ATTACK_WIDTH/2))] = CRGB(n, n, 255);
|
|
}
|
|
|
|
int getLED(int pos){
|
|
// The world is 1000 pixels wide, this converts world units into an LED number
|
|
return constrain((int)map(pos, 0, 1000, 0, NUM_LEDS-1), 0, NUM_LEDS-1);
|
|
}
|
|
|
|
bool inLava(int pos){
|
|
// Returns if the player is in active lava
|
|
int i;
|
|
Lava LP;
|
|
for(i = 0; i<lavaCount; i++){
|
|
LP = lavaPool[i];
|
|
if(LP.Alive() && LP._state == "ON"){
|
|
if(LP._left < pos && LP._right > pos) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void updateLives(){
|
|
// Updates the life LEDs to show how many lives the player has left
|
|
for(int i = 0; i<3; i++){
|
|
digitalWrite(lifeLEDs[i], lives>i?HIGH:LOW);
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// --------- SCREENSAVER -----------
|
|
// ---------------------------------
|
|
void screenSaverTick(){
|
|
int n, b, c, i;
|
|
long mm = millis();
|
|
int mode = (mm/20000)%2;
|
|
|
|
for(i = 0; i<NUM_LEDS; i++){
|
|
leds[i].nscale8(250);
|
|
}
|
|
if(mode == 0){
|
|
// Marching green <> orange
|
|
n = (mm/250)%10;
|
|
b = 10+((sin(mm/500.00)+1)*20.00);
|
|
c = 20+((sin(mm/5000.00)+1)*33);
|
|
for(i = 0; i<NUM_LEDS; i++){
|
|
if(i%10 == n){
|
|
leds[i] = CHSV( c, 255, 150);
|
|
}
|
|
}
|
|
}else if(mode == 1){
|
|
// Random flashes
|
|
randomSeed(mm);
|
|
for(i = 0; i<NUM_LEDS; i++){
|
|
if(random8(200) == 0){
|
|
leds[i] = CHSV( 25, 255, 100);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------
|
|
// ----------- JOYSTICK ------------
|
|
// ---------------------------------
|
|
void getInput(){
|
|
// This is responsible for the player movement speed and attacking.
|
|
// You can replace it with anything you want that passes a -90>+90 value to joystickTilt
|
|
// and any value to joystickWobble that is greater than ATTACK_THRESHOLD (defined at start)
|
|
// For example you could use 3 momentery buttons:
|
|
// if(digitalRead(leftButtonPinNumber) == HIGH) joystickTilt = -90;
|
|
// if(digitalRead(rightButtonPinNumber) == HIGH) joystickTilt = 90;
|
|
// if(digitalRead(attackButtonPinNumber) == HIGH) joystickWobble = ATTACK_THRESHOLD;
|
|
|
|
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
|
|
int a = (JOYSTICK_ORIENTATION == 0?ax:(JOYSTICK_ORIENTATION == 1?ay:az))/166;
|
|
int g = (JOYSTICK_ORIENTATION == 0?gx:(JOYSTICK_ORIENTATION == 1?gy:gz));
|
|
if(abs(a) < JOYSTICK_DEADZONE) a = 0;
|
|
if(a > 0) a -= JOYSTICK_DEADZONE;
|
|
if(a < 0) a += JOYSTICK_DEADZONE;
|
|
MPUAngleSamples.add(a);
|
|
MPUWobbleSamples.add(g);
|
|
|
|
joystickTilt = MPUAngleSamples.getMedian();
|
|
if(JOYSTICK_DIRECTION == 1) {
|
|
joystickTilt = 0-joystickTilt;
|
|
}
|
|
joystickWobble = abs(MPUWobbleSamples.getHighest());
|
|
}
|
|
|
|
|
|
// ---------------------------------
|
|
// -------------- SFX --------------
|
|
// ---------------------------------
|
|
void SFXtilt(int amount){
|
|
int f = map(abs(amount), 0, 90, 80, 900)+random8(100);
|
|
if(playerPositionModifier < 0) f -= 500;
|
|
if(playerPositionModifier > 0) f += 200;
|
|
toneAC(f, min(min(abs(amount)/9, 5), MAX_VOLUME));
|
|
|
|
}
|
|
void SFXattacking(){
|
|
int freq = map(sin(millis()/2.0)*1000.0, -1000, 1000, 500, 600);
|
|
if(random8(5)== 0){
|
|
freq *= 3;
|
|
}
|
|
toneAC(freq, MAX_VOLUME);
|
|
}
|
|
void SFXdead(){
|
|
int freq = max(1000 - (millis()-killTime), 10);
|
|
freq += random8(200);
|
|
int vol = max(10 - (millis()-killTime)/200, 0);
|
|
toneAC(freq, MAX_VOLUME);
|
|
}
|
|
void SFXkill(){
|
|
toneAC(2000, MAX_VOLUME, 1000, true);
|
|
}
|
|
void SFXwin(){
|
|
int freq = (millis()-stageStartTime)/3.0;
|
|
freq += map(sin(millis()/20.0)*1000.0, -1000, 1000, 0, 20);
|
|
int vol = 10;//max(10 - (millis()-stageStartTime)/200, 0);
|
|
toneAC(freq, MAX_VOLUME);
|
|
}
|
|
|
|
void SFXcomplete(){
|
|
noToneAC();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|