First working multiplayer version

This commit is contained in:
Phew
2026-02-11 23:20:46 +01:00
parent 08686e0fd6
commit a11da1b3d8
7 changed files with 284 additions and 163 deletions

View File

@@ -9,7 +9,7 @@ class Enemy
bool Alive(); bool Alive();
int _pos; int _pos;
int _wobble; int _wobble;
int playerSide; int playersSide[4]; // This should be MAX_PLAYERS
private: private:
int _dir; int _dir;
int _speed; int _speed;

View File

@@ -3,38 +3,54 @@
class Player class Player
{ {
public: public:
Player(int[3], int, int); Player(int, int, int);
int Id() const;
int Lives() const;
void Lives(int); void Lives(int);
int Lives(); bool Alive() const;
void Kill(); void Kill();
bool Alive(); void setColor(int, int, int);
void moveto(int);
void moveby(int); void moveby(int);
int color[3]; void moveto(int);
int position; void Spawn(int);
bool attacking;
int last_attack;
void updateState(bool tilted, bool wobbled, int time); void updateState(bool tilted, bool wobbled, int time);
void startAttack(int time); void startAttack(int time);
int _lastState; // 0-> idle; 1-> move; 2 -> attack // public variables
int position;
int speed;
bool attacking;
bool captured;
int last_attack;
int color[3];
private: private:
int _id;
int _lives; int _lives;
int _state; int _state;
int _lastState; // 0-> idle; 1-> move; 2 -> attack
int _attackDuration; int _attackDuration;
}; };
Player::Player(int player_color[3], int lives, int attackDuration){ Player::Player(int id, int lives, int attackDuration){
_lives = lives; _lives = lives;
_id = id;
attacking = false; attacking = false;
color[0] = player_color[0];
color[1] = player_color[1];
color[2] = player_color[2];
_attackDuration = attackDuration; _attackDuration = attackDuration;
_lastState = 0; // start as idle; _lastState = 0; // start as idle;
captured = false;
}
void Player::setColor(int r, int g, int b){
color[0] = r;
color[1] = g;
color[2] = b;
}
int Player::Id() const
{
return _id;
} }
void Player::updateState(bool tilted, bool wobbled, int time) { void Player::updateState(bool tilted, bool wobbled, int time) {
@@ -65,16 +81,23 @@ void Player::Lives(int n){
_lives = n; _lives = n;
} }
bool Player::Alive(){ bool Player::Alive() const{
return _lives > 0; return _lives > 0;
} }
void Player::Kill(){ void Player::Kill(){
captured = true;
_lives--; _lives--;
if (_lives < 0) _lives = 0; if (_lives < 0) _lives = 0;
} }
int Player::Lives(){ void Player::Spawn(int pos){
captured = false;
moveto(pos);
}
int Player::Lives() const{
return _lives; return _lives;
} }

View File

@@ -64,22 +64,24 @@
#define USE_GRAVITY 0 // 0/1 use gravity (LED strip going up wall) #define USE_GRAVITY 0 // 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 BEND_POINT 550 // 0/1000 point at which the LED strip goes up the wall
// Players settings
#define MAX_PLAYERS 4
// this can become a setting in the future
#define NUM_PLAYERS 2
// GAME // GAME
long previousMillis = 0; // Time of the last redraw long previousMillis = 0; // Time of the last redraw
int levelNumber = 0; int levelNumber = 0;
#define TIMEOUT 60000 // time until screen saver in milliseconds #define TIMEOUT 60000 // time until screen saver in milliseconds
int joystickTilt = 0; // Stores the angle of the joystick int joystickTilt[NUM_PLAYERS] = {0}; // Stores the angle of the joystick
int joystickWobble = 0; // Stores the max amount of acceleration (wobble) int joystickWobble[NUM_PLAYERS] = {0}; // Stores the max amount of acceleration (wobble)
// WOBBLE ATTACK // WOBBLE ATTACK
#define DEFAULT_ATTACK_WIDTH 70 // Width of the wobble attack, world is 1000 wide #define DEFAULT_ATTACK_WIDTH 70 // Width of the wobble attack, world is 1000 wide
int attack_width = DEFAULT_ATTACK_WIDTH; int attack_width = DEFAULT_ATTACK_WIDTH;
#define ATTACK_DURATION 1000 // Duration of a wobble attack (ms) #define ATTACK_DURATION 1000 // Duration of a wobble attack (ms)
//long attackMillis = 0; // Time the attack started
//bool attacking = 0; // Is the attack in progress?
//bool canAttackAgain = 0; // Have I finished my previous attack?
#define BOSS_WIDTH 40 #define BOSS_WIDTH 40
// TODO all animation durations should be defined rather than literals // TODO all animation durations should be defined rather than literals
@@ -137,8 +139,10 @@ Conveyor conveyorPool[CONVEYOR_COUNT] = {
Boss boss = Boss(); Boss boss = Boss();
int pcolor[3] = {0,255,0}; Player player[NUM_PLAYERS] = {
Player player = Player(pcolor, LIVES_PER_LEVEL, ATTACK_DURATION); Player(0, LIVES_PER_LEVEL, ATTACK_DURATION),
Player(1, LIVES_PER_LEVEL, ATTACK_DURATION)
};
enum stages { enum stages {
STARTUP, STARTUP,
@@ -151,7 +155,6 @@ enum stages {
} stage; } stage;
long stageStartTime; // Stores the time the stage changed for stages that are time based long stageStartTime; // Stores the time the stage changed for stages that are time based
int playerPositionModifier; // +/- adjustment to player position
long killTime; long killTime;
bool lastLevel = false; bool lastLevel = false;
@@ -233,17 +236,29 @@ void setup() {
sound_init(DAC_AUDIO_PIN); sound_init(DAC_AUDIO_PIN);
#ifndef USE_GYRO_JOYSTICK #ifndef USE_GYRO_JOYSTICK
pinMode(upButtonPinNumber, INPUT_PULLUP); pinMode(upButtonPinNumber1, INPUT_PULLUP);
pinMode(downButtonPinNumber, INPUT_PULLUP); pinMode(downButtonPinNumber1, INPUT_PULLUP);
pinMode(leftButtonPinNumber, INPUT_PULLUP); pinMode(leftButtonPinNumber1, INPUT_PULLUP);
pinMode(rightButtonPinNumber, INPUT_PULLUP); //pinMode(rightButtonPinNumber, INPUT_PULLUP);
pinMode(upButtonPinNumber2, INPUT_PULLUP);
pinMode(downButtonPinNumber2, INPUT_PULLUP);
pinMode(leftButtonPinNumber2, INPUT_PULLUP);
//pinMode(rightButtonPinNumber, INPUT_PULLUP);
#endif #endif
ap_setup(); ap_setup();
stage = STARTUP; stage = STARTUP;
stageStartTime = millis(); stageStartTime = millis();
player.Lives ( user_settings.lives_per_level );
// set players color
player[0].setColor(0,255,0);
player[1].setColor(127,2,255);
for (Player& p : player)
p.Lives ( 2 );
//p.Lives ( user_settings.lives_per_level );
} }
void loop() { void loop() {
@@ -254,10 +269,10 @@ void loop() {
checkSerialInput(); checkSerialInput();
if(stage == PLAY){ if(stage == PLAY){
if(player.attacking){ if(player[0].attacking){
SFXattacking(); SFXattacking();
}else{ }else{
SFXtilt(joystickTilt); SFXtilt(joystickTilt[0]);
} }
}else if(stage == DEAD){ }else if(stage == DEAD){
SFXdead(); SFXdead();
@@ -266,13 +281,18 @@ void loop() {
if (mm - previousMillis >= MIN_REDRAW_INTERVAL) { if (mm - previousMillis >= MIN_REDRAW_INTERVAL) {
getInput(); getInput();
long frameTimer = mm; long frameTimer = mm;
previousMillis = mm; previousMillis = mm;
if((abs(joystickTilt) > user_settings.joystick_deadzone) || // check activity of any player
(abs(joystickWobble) >= user_settings.attack_threshold)) bool isMoving = false;
for (int i=0; i < NUM_PLAYERS; i++) {
isMoving = isMoving &&
(abs(joystickTilt[i]) > user_settings.joystick_deadzone) ||
(abs(joystickWobble[i]) >= user_settings.attack_threshold);
}
if(isMoving)
{ {
lastInputTime = mm; lastInputTime = mm;
if(stage == SCREENSAVER){ if(stage == SCREENSAVER){
@@ -286,8 +306,6 @@ void loop() {
} }
} }
if(stage == SCREENSAVER){ if(stage == SCREENSAVER){
screenSaverTick(); screenSaverTick();
}else if(stage == STARTUP){ }else if(stage == STARTUP){
@@ -303,53 +321,64 @@ void loop() {
}else if(stage == PLAY){ }else if(stage == PLAY){
// PLAYING // PLAYING
if (joystickWobble >= user_settings.attack_threshold) player.startAttack(mm); for (Player& p : player) {
if (p.captured) continue;
player.updateState( if (joystickWobble[p.Id()] >= user_settings.attack_threshold) p.startAttack(mm);
abs(joystickTilt) > user_settings.joystick_deadzone,
abs(joystickWobble) >= user_settings.attack_threshold,
mm);
// If still not attacking, move! p.updateState(
player.moveby(playerPositionModifier); abs(joystickTilt[p.Id()]) > user_settings.joystick_deadzone,
if(!player.attacking){ abs(joystickWobble[p.Id()]) >= user_settings.attack_threshold,
SFXtilt(joystickTilt); mm);
//int moveAmount = (joystickTilt/6.0); // 6.0 is ideal at 16ms interval (6.0 / (16.0 / MIN_REDRAW_INTERVAL))
int moveAmount = (joystickTilt/(6.0)); // 6.0 is ideal at 16ms interval
if(DIRECTION) moveAmount = -moveAmount;
moveAmount = constrain(moveAmount, -MAX_PLAYER_SPEED, MAX_PLAYER_SPEED);
player.moveby( -moveAmount ); // If still not attacking, move!
p.moveby(p.speed);
if(!p.attacking){
SFXtilt(joystickTilt[p.Id()]);
//int moveAmount = (joystickTilt/6.0); // 6.0 is ideal at 16ms interval (6.0 / (16.0 / MIN_REDRAW_INTERVAL))
int moveAmount = (joystickTilt[p.Id()]/(6.0)); // 6.0 is ideal at 16ms interval
if(DIRECTION) moveAmount = -moveAmount;
moveAmount = constrain(moveAmount, -MAX_PLAYER_SPEED, MAX_PLAYER_SPEED);
// stop player from leaving if boss is alive p.moveby( -moveAmount );
if (boss.Alive() && player.position >= VIRTUAL_LED_COUNT) // move player back
player.moveto( 999 ); //(user_settings.led_count - 1) * (1000.0/user_settings.led_count);
if(player.position >= VIRTUAL_LED_COUNT && !boss.Alive()) { // stop player from leaving if boss is alive
// Reached exit! if (boss.Alive() && p.position >= VIRTUAL_LED_COUNT) // move player back
levelComplete(); p.moveto( 999 ); //(user_settings.led_count - 1) * (1000.0/user_settings.led_count);
return;
if(p.position >= VIRTUAL_LED_COUNT && !boss.Alive()) {
// Reached exit!
levelComplete();
return;
}
} }
}
if(inLava(player.position)){ if(inLava(p.position)){
die(player); die(p);
}
} }
// Ticks and draw calls // Ticks and draw calls
FastLED.clear(); FastLED.clear();
tickConveyors(); for (Player& p : player)
tickConveyors(p);
tickSpawners(); tickSpawners();
tickBoss(); tickBoss();
tickLava(); tickLava();
tickEnemies(); for (Player& p : player)
drawPlayer(player); if (!p.captured) tickEnemies(p);
drawAttack(player); for (const Player& p : player) {
if (p.Alive()) {
drawPlayer(p);
drawAttack(p);
}
}
drawExit(); drawExit();
}else if(stage == DEAD){ }else if(stage == DEAD){
// DEAD // DEAD
FastLED.clear(); FastLED.clear();
tickDie(player, mm); tickDie(player[0], mm); // TODO: fix me! Add whoDied function!
if(!tickParticles()){ if(!tickParticles()){
loadLevel(); loadLevel();
} }
@@ -369,7 +398,8 @@ void loop() {
// restart from the beginning // restart from the beginning
stage = STARTUP; stage = STARTUP;
stageStartTime = millis(); stageStartTime = millis();
player.Lives ( user_settings.lives_per_level ); for (Player& p : player)
p.Lives ( user_settings.lives_per_level );
} }
} }
//FastLED.show(); //FastLED.show();
@@ -389,7 +419,8 @@ void loadLevel(){
/// Defaults...OK to change the following items in the levels below /// Defaults...OK to change the following items in the levels below
attack_width = DEFAULT_ATTACK_WIDTH; attack_width = DEFAULT_ATTACK_WIDTH;
player.moveto( 0 ); for (Player& p : player)
p.Spawn( 0 + p.Id()*10 ); // slightly displace players
/* ==== Level Editing Guide =============== /* ==== Level Editing Guide ===============
Level creation is done by adding to or editing the switch statement below Level creation is done by adding to or editing the switch statement below
@@ -442,7 +473,7 @@ void loadLevel(){
===== Other things you can adjust per level ================ ===== Other things you can adjust per level ================
Player Start position: Player Start position:
player.moveto( xxx ); player.Spawn( xxx );
The size of the TWANG attack The size of the TWANG attack
@@ -452,7 +483,8 @@ void loadLevel(){
*/ */
switch(levelNumber){ switch(levelNumber){
case 0: // basic introduction case 0: // basic introduction
player.moveto( 200 ); for (Player& p : player)
p.Spawn( 200 + p.Id()*10 );
spawnEnemy(1, 0, 0, 0); spawnEnemy(1, 0, 0, 0);
break; break;
case 1: case 1:
@@ -499,7 +531,8 @@ void loadLevel(){
break; break;
case 8: case 8:
// lava moving up // lava moving up
player.moveto( 200 ); for (Player& p : player)
p.Spawn( 200 + p.Id()*10 );
spawnLava(10, 180, 2000, 2000, 0, Lava::OFF, 0, 0.5); spawnLava(10, 180, 2000, 2000, 0, Lava::OFF, 0, 0.5);
spawnEnemy(350, 0, 1, 0); spawnEnemy(350, 0, 1, 0);
spawnPool[0].Spawn(1000, 5500, 3, 0, 0); spawnPool[0].Spawn(1000, 5500, 3, 0, 0);
@@ -601,7 +634,9 @@ void spawnEnemy(int pos, int dir, int speed, int wobble){
for(int e = 0; e<ENEMY_COUNT; e++){ // look for one that is not alive for a place to add one for(int e = 0; e<ENEMY_COUNT; e++){ // look for one that is not alive for a place to add one
if(!enemyPool[e].Alive()){ if(!enemyPool[e].Alive()){
enemyPool[e].Spawn(pos, dir, speed, wobble); enemyPool[e].Spawn(pos, dir, speed, wobble);
enemyPool[e].playerSide = pos > player.position?1:-1; // for each player, save if it is above or below
for (const Player& p : player)
enemyPool[e].playersSide[p.Id()] = pos > p.position?1:-1;
return; return;
} }
} }
@@ -653,7 +688,7 @@ void levelComplete(){
} }
if (levelNumber != 0) // no points for the first level if (levelNumber != 0) // no points for the first level
{ {
score = score + (player.Lives() * 10); // score = score + (player[0].Lives() * 10); //
} }
} }
@@ -663,11 +698,16 @@ void nextLevel(){
if(lastLevel) { if(lastLevel) {
stage = STARTUP; stage = STARTUP;
stageStartTime = millis(); stageStartTime = millis();
player.Lives ( user_settings.lives_per_level );
for (Player& p : player)
p.Lives ( user_settings.lives_per_level );
} }
else { else {
player.Lives ( user_settings.lives_per_level ); for (Player& p : player) {
if (!p.Alive()) p.Lives ( user_settings.lives_per_level );
}
loadLevel(); loadLevel();
} }
} }
@@ -678,16 +718,30 @@ void gameOver(){
loadLevel(); loadLevel();
} }
void die(Player p){ void die(Player& p){
if(levelNumber > 0) //if(levelNumber > 0) // This messes up everything in 2-players mode, but can be restored...
p.Kill(); p.Kill();
if(!player.Alive()){ // how many players are still playing the level?
int stillPlaying = 0;
// how many players have lives?
bool allDead = true;
for (const Player& pp : player) {
stillPlaying += !pp.captured;
allDead &= !pp.Alive();
}
// someone is still playing the level, continue (an animation for the dead player should be added)
if(stillPlaying>0) return;
// all players have been captured, do any of them have lives left?
// This has already been checked in allDead above.
if( allDead ){
stage = GAMEOVER; stage = GAMEOVER;
stageStartTime = millis(); stageStartTime = millis();
} }
else else // some still have lifes to live, go to DEAD stage and repat the level
{ {
for(int ip = 0; ip < PARTICLE_COUNT; ip++){ for(int ip = 0; ip < PARTICLE_COUNT; ip++){
particlePool[ip].Spawn(p.position); particlePool[ip].Spawn(p.position);
@@ -740,13 +794,13 @@ void tickStartup(long mm)
} }
void tickEnemies(){ void tickEnemies(Player& p){
for(int i = 0; i<ENEMY_COUNT; i++){ for(int i = 0; i<ENEMY_COUNT; i++){
if(enemyPool[i].Alive()){ if(enemyPool[i].Alive()){
enemyPool[i].Tick(); enemyPool[i].Tick();
// Hit attack? // Hit attack?
if(player.attacking){ if(p.attacking){
if(enemyPool[i]._pos > player.position-(attack_width/2) && enemyPool[i]._pos < player.position+(attack_width/2)){ if(enemyPool[i]._pos > p.position-(attack_width/2) && enemyPool[i]._pos < p.position+(attack_width/2)){
enemyPool[i].Kill(); enemyPool[i].Kill();
SFXkill(); SFXkill();
} }
@@ -761,10 +815,12 @@ void tickEnemies(){
} }
// Hit player? // Hit player?
if( if(
(enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= player.position) || (enemyPool[i].playersSide[p.Id()] == 1 && enemyPool[i]._pos <= p.position) ||
(enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= player.position) (enemyPool[i].playersSide[p.Id()] == -1 && enemyPool[i]._pos >= p.position)
){ ){
die(player); Serial.println("Killing player\n");
Serial.println(p.Id());
die(p);
return; return;
} }
} }
@@ -779,30 +835,36 @@ void tickBoss(){
leds[i] = CRGB::DarkRed; leds[i] = CRGB::DarkRed;
leds[i] %= 100; leds[i] %= 100;
} }
// CHECK COLLISION
if(getLED(player.position) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(player.position) < getLED(boss._pos + BOSS_WIDTH)){ for (Player& p : player)
die(player); {
return; // CHECK COLLISION
} if(getLED(p.position) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(p.position) < getLED(boss._pos + BOSS_WIDTH)){
// CHECK FOR ATTACK die(p);
if(player.attacking){ return;
if( }
(getLED(player.position+(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2) && getLED(player.position+(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2)) || // CHECK FOR ATTACK
(getLED(player.position-(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2) && getLED(player.position-(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2)) if(p.attacking){
){ if(
boss.Hit(); (getLED(p.position+(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2) && getLED(p.position+(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2)) ||
if(boss.Alive()){ (getLED(p.position-(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2) && getLED(p.position-(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2))
moveBoss(); ){
}else{ boss.Hit();
spawnPool[0].Kill(); if(boss.Alive()){
spawnPool[1].Kill(); moveBoss();
}else{
spawnPool[0].Kill();
spawnPool[1].Kill();
}
} }
} }
} }
} }
} }
void drawPlayer(Player p){ void drawPlayer(const Player& p){
if (p.captured) return;
if (!p.Alive()) return;
leds[getLED(p.position)] = CRGB(p.color[0], p.color[1], p.color[2]); leds[getLED(p.position)] = CRGB(p.color[0], p.color[1], p.color[2]);
} }
@@ -882,13 +944,13 @@ bool tickParticles(){
return stillActive; return stillActive;
} }
void tickConveyors(){ void tickConveyors(Player& p){
//TODO should the visual speed be proportional to the conveyor speed? //TODO should the visual speed be proportional to the conveyor speed?
int b, speed, n, i, ss, ee, led; int b, speed, n, i, ss, ee, led;
long m = 10000+millis(); long m = 10000+millis();
playerPositionModifier = 0; p.speed = 0;
int levels = 5; // brightness levels in conveyor int levels = 5; // brightness levels in conveyor
@@ -909,8 +971,8 @@ void tickConveyors(){
leds[led] = CRGB(0, 0, b); leds[led] = CRGB(0, 0, b);
} }
if(player.position > conveyorPool[i]._startPoint && player.position < conveyorPool[i]._endPoint){ if(p.position > conveyorPool[i]._startPoint && p.position < conveyorPool[i]._endPoint){
playerPositionModifier = speed; p.speed = speed;
} }
} }
} }
@@ -971,7 +1033,7 @@ void tickBossKilled(long mm) // boss funeral
} }
} }
void tickDie(Player p, long mm) { // a short bright explosion...particles persist after it. void tickDie(const Player& p, long mm) { // a short bright explosion...particles persist after it.
const int duration = 200; // milliseconds const int duration = 200; // milliseconds
const int width = 20; // half width of the explosion const int width = 20; // half width of the explosion
@@ -1000,13 +1062,13 @@ void tickGameover(long mm) {
if(stageStartTime+GAMEOVER_SPREAD_DURATION > mm) // Spread red from player position to top and bottom if(stageStartTime+GAMEOVER_SPREAD_DURATION > mm) // Spread red from player position to top and bottom
{ {
// fill to top // fill to top
int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), user_settings.led_count), 0); int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player[0].position), user_settings.led_count), 0);
for(int i = getLED(player.position); i<= n; i++){ for(int i = getLED(player[0].position); i<= n; i++){
leds[i] = CRGB(255, 0, 0); leds[i] = CRGB(255, 0, 0);
} }
// fill to bottom // fill to bottom
n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), 0), 0); n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player[0].position), 0), 0);
for(int i = getLED(player.position); i>= n; i--){ for(int i = getLED(player[0].position); i>= n; i--){
leds[i] = CRGB(255, 0, 0); leds[i] = CRGB(255, 0, 0);
} }
SFXgameover(); SFXgameover();
@@ -1053,29 +1115,32 @@ void tickWin(long mm) {
void drawLives() void drawLives()
{ {
// show how many lives are left by drawing a short line of green leds for each life // show how many lives are left by drawing a short line of green leds for each life
SFXcomplete(); // stop any sounds SFXcomplete(); // stop any sounds
FastLED.clear(); FastLED.clear();
int pos = 0; for (const Player& p : player) {
for (int i = 0; i < player.Lives(); i++) int pos = 0;
{ for (int i = 0; i < p.Lives(); i++)
for (int j=0; j<4; j++) {
{ for (int j=0; j<4; j++)
leds[pos++] = CRGB(0, 255, 0); {
leds[pos++] = CRGB(p.color[0], p.color[1], p.color[2]);
FastLED.show();
}
leds[pos++] = CRGB(0, 0, 0);
delay(20);
}
FastLED.show(); FastLED.show();
} delay(400);
leds[pos++] = CRGB(0, 0, 0); FastLED.clear();
delay(20); }
}
FastLED.show();
delay(400);
FastLED.clear();
} }
void drawAttack(Player p){ void drawAttack(const Player& p){
if(p.captured) return;
if(!p.attacking) return; if(!p.attacking) return;
int n = map(millis() - p.last_attack, 0, ATTACK_DURATION, 100, 5); int n = map(millis() - p.last_attack, 0, ATTACK_DURATION, 100, 5);
for(int i = getLED(p.position-(attack_width/2))+1; i<=getLED(p.position+(attack_width/2))-1; i++){ for(int i = getLED(p.position-(attack_width/2))+1; i<=getLED(p.position+(attack_width/2))-1; i++){
@@ -1086,7 +1151,7 @@ void drawAttack(Player p){
leds[getLED(p.position)] = CRGB(255, 255, 255); leds[getLED(p.position)] = CRGB(255, 255, 255);
}else{ }else{
n = 0; n = 0;
leds[getLED(p.position)] = CRGB(0, 255, 0); leds[getLED(p.position)] = CRGB(p.color[0], p.color[1], p.color[2]);
} }
leds[getLED(p.position-(attack_width/2))] = CRGB(n, n, 255); leds[getLED(p.position-(attack_width/2))] = CRGB(n, n, 255);
leds[getLED(p.position+(attack_width/2))] = CRGB(n, n, 255); leds[getLED(p.position+(attack_width/2))] = CRGB(n, n, 255);
@@ -1202,22 +1267,45 @@ void getInput() {
// and any value to joystickWobble that is greater than ATTACK_THRESHOLD (defined at start) // and any value to joystickWobble that is greater than ATTACK_THRESHOLD (defined at start)
// For example you could use 3 momentary buttons: // For example you could use 3 momentary buttons:
bool up = digitalRead(upButtonPinNumber) == LOW; bool up = digitalRead(upButtonPinNumber1) == LOW;
bool down = digitalRead(downButtonPinNumber) == LOW; bool down = digitalRead(downButtonPinNumber1) == LOW;
bool left = digitalRead(leftButtonPinNumber) == LOW; bool left = digitalRead(leftButtonPinNumber1) == LOW;
bool right = digitalRead(rightButtonPinNumber) == LOW; //bool right = digitalRead(rightButtonPinNumber) == LOW;
bool right = left;
joystickTilt = 0; joystickTilt[0] = 0;
if (up) { if (up) {
joystickTilt = 90 ; joystickTilt[0] = 90 ;
} else if (down) { } else if (down) {
joystickTilt = -90; joystickTilt[0] = -90;
} }
if (left || right) { if (left || right) {
joystickWobble = user_settings.attack_threshold; joystickWobble[0] = user_settings.attack_threshold;
} else { } else {
joystickWobble = 0; joystickWobble[0] = 0;
}
up = digitalRead(upButtonPinNumber2) == LOW;
down = digitalRead(downButtonPinNumber2) == LOW;
left = digitalRead(leftButtonPinNumber2) == LOW;
//bool right = digitalRead(rightButtonPinNumber) == LOW;
right = left;
joystickTilt[1] = 0;
if (up) {
joystickTilt[1] = 90 ;
} else if (down) {
joystickTilt[1] = -90;
}
if (left || right) {
joystickWobble[1] = user_settings.attack_threshold;
} else {
joystickWobble[1] = 0;
} }
} }
@@ -1286,9 +1374,10 @@ void SFXtilt(int amount){
} }
int f = map(abs(amount), 0, 90, 80, 900)+random8(100); int f = map(abs(amount), 0, 90, 80, 900)+random8(100);
if(playerPositionModifier < 0) f -= 500; // TODO, how to modify for multiplayer?
if(playerPositionModifier > 0) f += 200; //if(playerPositionModifier < 0) f -= 500;
int vol = map(abs(amount), 0, 90, user_settings.audio_volume / 2, user_settings.audio_volume * 3/4); //if(playerPositionModifier > 0) f += 200;
int vol = map(abs(amount), 0, 90, user_settings.audio_volume / 2, user_settings.audio_volume * 3/4);
sound(f,vol); sound(f,vol);
} }
void SFXattacking(){ void SFXattacking(){

View File

@@ -29,17 +29,17 @@
/* Uncomment below to use the accelerometer base joystic of original implementation */ /* Uncomment below to use the accelerometer base joystic of original implementation */
//#define USE_GYRO_JOYSTICK //#define USE_GYRO_JOYSTICK
#define DATA_PIN 16 #define DATA_PIN 0
#define CLOCK_PIN 17 #define CLOCK_PIN 9
/* Game is rendered to this and scaled down to your strip. /* Game is rendered to this and scaled down to your strip.
This allows level definitions to work on all strip lengths */ This allows level definitions to work on all strip lengths */
#define VIRTUAL_LED_COUNT 1000 #define VIRTUAL_LED_COUNT 1000
// what type of LED Strip....uncomment to define only one of these // what type of LED Strip....uncomment to define only one of these
#define USE_APA102 //#define USE_APA102
//#define USE_NEOPIXEL #define USE_NEOPIXEL
// Check to make sure LED choice was done right // Check to make sure LED choice was done right
#if !defined(USE_NEOPIXEL) && !defined(USE_APA102) #if !defined(USE_NEOPIXEL) && !defined(USE_APA102)

View File

@@ -1,5 +1,5 @@
#ifndef SETTINGS_H #ifndef SETTINGS_H
#define SETTINGS_H #define SETTINGS_H
#include <EEPROM.h> #include <EEPROM.h>
#include "sound.h" #include "sound.h"
@@ -12,7 +12,7 @@
#define EEPROM_SIZE 256 #define EEPROM_SIZE 256
// LEDS // LEDS
#define NUM_LEDS 144 #define NUM_LEDS 288
#define MIN_LEDS 60 #define MIN_LEDS 60
@@ -34,10 +34,16 @@ const uint8_t LIVES_PER_LEVEL = 3; // default lives per level
#define MIN_ATTACK_THRESHOLD 20000 #define MIN_ATTACK_THRESHOLD 20000
#define MAX_ATTACK_THRESHOLD 30000 #define MAX_ATTACK_THRESHOLD 30000
#define leftButtonPinNumber 26 #define leftButtonPinNumber1 5
#define rightButtonPinNumber 18 //define rightButtonPinNumber1 6
#define upButtonPinNumber 19 #define upButtonPinNumber1 6
#define downButtonPinNumber 23 #define downButtonPinNumber1 7
#define leftButtonPinNumber2 1
//define rightButtonPinNumber 2
#define upButtonPinNumber2 3
#define downButtonPinNumber2 4
#define DEFAULT_JOYSTICK_DEADZONE 8 // Angle to ignore #define DEFAULT_JOYSTICK_DEADZONE 8 // Angle to ignore
@@ -49,7 +55,7 @@ const uint8_t LIVES_PER_LEVEL = 3; // default lives per level
#define MIN_VOLUME 0 #define MIN_VOLUME 0
#define MAX_VOLUME 255 #define MAX_VOLUME 255
#define DAC_AUDIO_PIN 25 // should be 25 or 26 only #define DAC_AUDIO_PIN 20 // should be 25 or 26 only
enum ErrorNums{ enum ErrorNums{
ERR_SETTING_NUM, ERR_SETTING_NUM,

View File

@@ -36,17 +36,18 @@ int dac_pin;
void IRAM_ATTR onSoundTimer() void IRAM_ATTR onSoundTimer()
{ {
if (sound_on) { if (sound_on) {
dacWrite(dac_pin, (sound_wave_high?sound_volume:0)); //dacWrite(dac_pin, (sound_wave_high?sound_volume:0));
sound_wave_high = ! sound_wave_high; sound_wave_high = ! sound_wave_high;
} }
else //else
dacWrite(dac_pin, 0); // dacWrite(dac_pin, 0);
} }
void sound_init(int pin){ // pin must be a DAC pin number !! (typically 25 or 26) void sound_init(int pin){ // pin must be a DAC pin number !! (typically 25 or 26)
return;
dac_pin = pin; dac_pin = pin;
sound_on = false; sound_on = false;
pinMode(dac_pin, OUTPUT); //pinMode(dac_pin, OUTPUT);
sound_volume = 0; sound_volume = 0;
#ifdef OLD_CODE #ifdef OLD_CODE
@@ -76,6 +77,7 @@ void sound_resume() // resume from pause ... after eeprom write
} }
bool sound(uint16_t freq, uint8_t volume){ bool sound(uint16_t freq, uint8_t volume){
return true;
if (volume == 0) { if (volume == 0) {
soundOff(); soundOff();
return false; return false;
@@ -91,6 +93,7 @@ bool sound(uint16_t freq, uint8_t volume){
} }
void soundOff(){ void soundOff(){
return;
sound_on = false; sound_on = false;
sound_volume = 0; sound_volume = 0;
//timerAlarmWrite(sndTimer, ESP32_F_CPU/AUDIO_INTERRUPT_PRESCALER/(MIN_FREQ), true); // lower timer freq //timerAlarmWrite(sndTimer, ESP32_F_CPU/AUDIO_INTERRUPT_PRESCALER/(MIN_FREQ), true); // lower timer freq

View File

@@ -24,7 +24,7 @@ void Twang_MPU::initialize()
{ {
Wire.beginTransmission(MPU_ADDR); Wire.beginTransmission(MPU_ADDR);
Wire.write(PWR_MGMT_1); // PWR_MGMT_1 register Wire.write(PWR_MGMT_1); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050) Wire.write(1); // wakes up the MPU-6050
Wire.endTransmission(true); Wire.endTransmission(true);
} }