First working multiplayer version
This commit is contained in:
@@ -9,7 +9,7 @@ class Enemy
|
||||
bool Alive();
|
||||
int _pos;
|
||||
int _wobble;
|
||||
int playerSide;
|
||||
int playersSide[4]; // This should be MAX_PLAYERS
|
||||
private:
|
||||
int _dir;
|
||||
int _speed;
|
||||
|
||||
@@ -3,38 +3,54 @@
|
||||
class Player
|
||||
{
|
||||
public:
|
||||
Player(int[3], int, int);
|
||||
Player(int, int, int);
|
||||
int Id() const;
|
||||
int Lives() const;
|
||||
void Lives(int);
|
||||
int Lives();
|
||||
bool Alive() const;
|
||||
void Kill();
|
||||
bool Alive();
|
||||
void moveto(int);
|
||||
void setColor(int, int, int);
|
||||
void moveby(int);
|
||||
int color[3];
|
||||
int position;
|
||||
bool attacking;
|
||||
int last_attack;
|
||||
void moveto(int);
|
||||
void Spawn(int);
|
||||
void updateState(bool tilted, bool wobbled, 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:
|
||||
int _id;
|
||||
int _lives;
|
||||
int _state;
|
||||
|
||||
int _lastState; // 0-> idle; 1-> move; 2 -> attack
|
||||
int _attackDuration;
|
||||
};
|
||||
|
||||
Player::Player(int player_color[3], int lives, int attackDuration){
|
||||
Player::Player(int id, int lives, int attackDuration){
|
||||
_lives = lives;
|
||||
_id = id;
|
||||
|
||||
attacking = false;
|
||||
|
||||
color[0] = player_color[0];
|
||||
color[1] = player_color[1];
|
||||
color[2] = player_color[2];
|
||||
|
||||
_attackDuration = attackDuration;
|
||||
|
||||
_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) {
|
||||
@@ -65,16 +81,23 @@ void Player::Lives(int n){
|
||||
_lives = n;
|
||||
}
|
||||
|
||||
bool Player::Alive(){
|
||||
bool Player::Alive() const{
|
||||
return _lives > 0;
|
||||
}
|
||||
|
||||
void Player::Kill(){
|
||||
captured = true;
|
||||
|
||||
_lives--;
|
||||
if (_lives < 0) _lives = 0;
|
||||
}
|
||||
|
||||
int Player::Lives(){
|
||||
void Player::Spawn(int pos){
|
||||
captured = false;
|
||||
moveto(pos);
|
||||
}
|
||||
|
||||
int Player::Lives() const{
|
||||
return _lives;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,22 +64,24 @@
|
||||
#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
|
||||
|
||||
// Players settings
|
||||
#define MAX_PLAYERS 4
|
||||
// this can become a setting in the future
|
||||
#define NUM_PLAYERS 2
|
||||
|
||||
// GAME
|
||||
long previousMillis = 0; // Time of the last redraw
|
||||
int levelNumber = 0;
|
||||
|
||||
#define TIMEOUT 60000 // time until screen saver in milliseconds
|
||||
|
||||
int joystickTilt = 0; // Stores the angle of the joystick
|
||||
int joystickWobble = 0; // Stores the max amount of acceleration (wobble)
|
||||
int joystickTilt[NUM_PLAYERS] = {0}; // Stores the angle of the joystick
|
||||
int joystickWobble[NUM_PLAYERS] = {0}; // Stores the max amount of acceleration (wobble)
|
||||
|
||||
// WOBBLE ATTACK
|
||||
#define DEFAULT_ATTACK_WIDTH 70 // Width of the wobble attack, world is 1000 wide
|
||||
int attack_width = DEFAULT_ATTACK_WIDTH;
|
||||
#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
|
||||
|
||||
// TODO all animation durations should be defined rather than literals
|
||||
@@ -137,8 +139,10 @@ Conveyor conveyorPool[CONVEYOR_COUNT] = {
|
||||
|
||||
Boss boss = Boss();
|
||||
|
||||
int pcolor[3] = {0,255,0};
|
||||
Player player = Player(pcolor, LIVES_PER_LEVEL, ATTACK_DURATION);
|
||||
Player player[NUM_PLAYERS] = {
|
||||
Player(0, LIVES_PER_LEVEL, ATTACK_DURATION),
|
||||
Player(1, LIVES_PER_LEVEL, ATTACK_DURATION)
|
||||
};
|
||||
|
||||
enum stages {
|
||||
STARTUP,
|
||||
@@ -151,7 +155,6 @@ enum stages {
|
||||
} stage;
|
||||
|
||||
long stageStartTime; // Stores the time the stage changed for stages that are time based
|
||||
int playerPositionModifier; // +/- adjustment to player position
|
||||
long killTime;
|
||||
bool lastLevel = false;
|
||||
|
||||
@@ -233,17 +236,29 @@ void setup() {
|
||||
sound_init(DAC_AUDIO_PIN);
|
||||
|
||||
#ifndef USE_GYRO_JOYSTICK
|
||||
pinMode(upButtonPinNumber, INPUT_PULLUP);
|
||||
pinMode(downButtonPinNumber, INPUT_PULLUP);
|
||||
pinMode(leftButtonPinNumber, INPUT_PULLUP);
|
||||
pinMode(rightButtonPinNumber, INPUT_PULLUP);
|
||||
pinMode(upButtonPinNumber1, INPUT_PULLUP);
|
||||
pinMode(downButtonPinNumber1, INPUT_PULLUP);
|
||||
pinMode(leftButtonPinNumber1, INPUT_PULLUP);
|
||||
//pinMode(rightButtonPinNumber, INPUT_PULLUP);
|
||||
|
||||
pinMode(upButtonPinNumber2, INPUT_PULLUP);
|
||||
pinMode(downButtonPinNumber2, INPUT_PULLUP);
|
||||
pinMode(leftButtonPinNumber2, INPUT_PULLUP);
|
||||
//pinMode(rightButtonPinNumber, INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
ap_setup();
|
||||
|
||||
stage = STARTUP;
|
||||
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() {
|
||||
@@ -254,10 +269,10 @@ void loop() {
|
||||
checkSerialInput();
|
||||
|
||||
if(stage == PLAY){
|
||||
if(player.attacking){
|
||||
if(player[0].attacking){
|
||||
SFXattacking();
|
||||
}else{
|
||||
SFXtilt(joystickTilt);
|
||||
SFXtilt(joystickTilt[0]);
|
||||
}
|
||||
}else if(stage == DEAD){
|
||||
SFXdead();
|
||||
@@ -266,13 +281,18 @@ void loop() {
|
||||
if (mm - previousMillis >= MIN_REDRAW_INTERVAL) {
|
||||
getInput();
|
||||
|
||||
|
||||
|
||||
long frameTimer = mm;
|
||||
previousMillis = mm;
|
||||
|
||||
if((abs(joystickTilt) > user_settings.joystick_deadzone) ||
|
||||
(abs(joystickWobble) >= user_settings.attack_threshold))
|
||||
// check activity of any player
|
||||
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;
|
||||
if(stage == SCREENSAVER){
|
||||
@@ -286,8 +306,6 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(stage == SCREENSAVER){
|
||||
screenSaverTick();
|
||||
}else if(stage == STARTUP){
|
||||
@@ -303,53 +321,64 @@ void loop() {
|
||||
}else if(stage == PLAY){
|
||||
// PLAYING
|
||||
|
||||
if (joystickWobble >= user_settings.attack_threshold) player.startAttack(mm);
|
||||
for (Player& p : player) {
|
||||
if (p.captured) continue;
|
||||
|
||||
player.updateState(
|
||||
abs(joystickTilt) > user_settings.joystick_deadzone,
|
||||
abs(joystickWobble) >= user_settings.attack_threshold,
|
||||
mm);
|
||||
if (joystickWobble[p.Id()] >= user_settings.attack_threshold) p.startAttack(mm);
|
||||
|
||||
// If still not attacking, move!
|
||||
player.moveby(playerPositionModifier);
|
||||
if(!player.attacking){
|
||||
SFXtilt(joystickTilt);
|
||||
//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);
|
||||
p.updateState(
|
||||
abs(joystickTilt[p.Id()]) > user_settings.joystick_deadzone,
|
||||
abs(joystickWobble[p.Id()]) >= user_settings.attack_threshold,
|
||||
mm);
|
||||
|
||||
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
|
||||
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);
|
||||
p.moveby( -moveAmount );
|
||||
|
||||
if(player.position >= VIRTUAL_LED_COUNT && !boss.Alive()) {
|
||||
// Reached exit!
|
||||
levelComplete();
|
||||
return;
|
||||
// stop player from leaving if boss is alive
|
||||
if (boss.Alive() && p.position >= VIRTUAL_LED_COUNT) // move player back
|
||||
p.moveto( 999 ); //(user_settings.led_count - 1) * (1000.0/user_settings.led_count);
|
||||
|
||||
if(p.position >= VIRTUAL_LED_COUNT && !boss.Alive()) {
|
||||
// Reached exit!
|
||||
levelComplete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(inLava(player.position)){
|
||||
die(player);
|
||||
if(inLava(p.position)){
|
||||
die(p);
|
||||
}
|
||||
}
|
||||
|
||||
// Ticks and draw calls
|
||||
FastLED.clear();
|
||||
tickConveyors();
|
||||
for (Player& p : player)
|
||||
tickConveyors(p);
|
||||
|
||||
tickSpawners();
|
||||
tickBoss();
|
||||
tickLava();
|
||||
tickEnemies();
|
||||
drawPlayer(player);
|
||||
drawAttack(player);
|
||||
for (Player& p : player)
|
||||
if (!p.captured) tickEnemies(p);
|
||||
for (const Player& p : player) {
|
||||
if (p.Alive()) {
|
||||
drawPlayer(p);
|
||||
drawAttack(p);
|
||||
}
|
||||
}
|
||||
drawExit();
|
||||
}else if(stage == DEAD){
|
||||
// DEAD
|
||||
FastLED.clear();
|
||||
tickDie(player, mm);
|
||||
tickDie(player[0], mm); // TODO: fix me! Add whoDied function!
|
||||
if(!tickParticles()){
|
||||
loadLevel();
|
||||
}
|
||||
@@ -369,7 +398,8 @@ void loop() {
|
||||
// restart from the beginning
|
||||
stage = STARTUP;
|
||||
stageStartTime = millis();
|
||||
player.Lives ( user_settings.lives_per_level );
|
||||
for (Player& p : player)
|
||||
p.Lives ( user_settings.lives_per_level );
|
||||
}
|
||||
}
|
||||
//FastLED.show();
|
||||
@@ -389,7 +419,8 @@ void loadLevel(){
|
||||
|
||||
/// Defaults...OK to change the following items in the levels below
|
||||
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 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 ================
|
||||
|
||||
Player Start position:
|
||||
player.moveto( xxx );
|
||||
player.Spawn( xxx );
|
||||
|
||||
|
||||
The size of the TWANG attack
|
||||
@@ -452,7 +483,8 @@ void loadLevel(){
|
||||
*/
|
||||
switch(levelNumber){
|
||||
case 0: // basic introduction
|
||||
player.moveto( 200 );
|
||||
for (Player& p : player)
|
||||
p.Spawn( 200 + p.Id()*10 );
|
||||
spawnEnemy(1, 0, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
@@ -499,7 +531,8 @@ void loadLevel(){
|
||||
break;
|
||||
case 8:
|
||||
// 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);
|
||||
spawnEnemy(350, 0, 1, 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
|
||||
if(!enemyPool[e].Alive()){
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -653,7 +688,7 @@ void levelComplete(){
|
||||
}
|
||||
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) {
|
||||
stage = STARTUP;
|
||||
stageStartTime = millis();
|
||||
player.Lives ( user_settings.lives_per_level );
|
||||
|
||||
for (Player& p : player)
|
||||
p.Lives ( user_settings.lives_per_level );
|
||||
|
||||
}
|
||||
else {
|
||||
player.Lives ( user_settings.lives_per_level );
|
||||
for (Player& p : player) {
|
||||
if (!p.Alive()) p.Lives ( user_settings.lives_per_level );
|
||||
}
|
||||
|
||||
loadLevel();
|
||||
}
|
||||
}
|
||||
@@ -678,16 +718,30 @@ void gameOver(){
|
||||
loadLevel();
|
||||
}
|
||||
|
||||
void die(Player p){
|
||||
void die(Player& p){
|
||||
|
||||
if(levelNumber > 0)
|
||||
p.Kill();
|
||||
//if(levelNumber > 0) // This messes up everything in 2-players mode, but can be restored...
|
||||
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;
|
||||
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++){
|
||||
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++){
|
||||
if(enemyPool[i].Alive()){
|
||||
enemyPool[i].Tick();
|
||||
// Hit attack?
|
||||
if(player.attacking){
|
||||
if(enemyPool[i]._pos > player.position-(attack_width/2) && enemyPool[i]._pos < player.position+(attack_width/2)){
|
||||
if(p.attacking){
|
||||
if(enemyPool[i]._pos > p.position-(attack_width/2) && enemyPool[i]._pos < p.position+(attack_width/2)){
|
||||
enemyPool[i].Kill();
|
||||
SFXkill();
|
||||
}
|
||||
@@ -761,10 +815,12 @@ void tickEnemies(){
|
||||
}
|
||||
// Hit player?
|
||||
if(
|
||||
(enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= player.position) ||
|
||||
(enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= player.position)
|
||||
(enemyPool[i].playersSide[p.Id()] == 1 && enemyPool[i]._pos <= p.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;
|
||||
}
|
||||
}
|
||||
@@ -779,30 +835,36 @@ void tickBoss(){
|
||||
leds[i] = CRGB::DarkRed;
|
||||
leds[i] %= 100;
|
||||
}
|
||||
// CHECK COLLISION
|
||||
if(getLED(player.position) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(player.position) < getLED(boss._pos + BOSS_WIDTH)){
|
||||
die(player);
|
||||
return;
|
||||
}
|
||||
// CHECK FOR ATTACK
|
||||
if(player.attacking){
|
||||
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)) ||
|
||||
(getLED(player.position-(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2) && getLED(player.position-(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2))
|
||||
){
|
||||
boss.Hit();
|
||||
if(boss.Alive()){
|
||||
moveBoss();
|
||||
}else{
|
||||
spawnPool[0].Kill();
|
||||
spawnPool[1].Kill();
|
||||
|
||||
for (Player& p : player)
|
||||
{
|
||||
// CHECK COLLISION
|
||||
if(getLED(p.position) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(p.position) < getLED(boss._pos + BOSS_WIDTH)){
|
||||
die(p);
|
||||
return;
|
||||
}
|
||||
// CHECK FOR ATTACK
|
||||
if(p.attacking){
|
||||
if(
|
||||
(getLED(p.position+(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2) && getLED(p.position+(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2)) ||
|
||||
(getLED(p.position-(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2) && getLED(p.position-(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2))
|
||||
){
|
||||
boss.Hit();
|
||||
if(boss.Alive()){
|
||||
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]);
|
||||
}
|
||||
|
||||
@@ -882,13 +944,13 @@ bool tickParticles(){
|
||||
return stillActive;
|
||||
}
|
||||
|
||||
void tickConveyors(){
|
||||
void tickConveyors(Player& p){
|
||||
|
||||
//TODO should the visual speed be proportional to the conveyor speed?
|
||||
|
||||
int b, speed, n, i, ss, ee, led;
|
||||
long m = 10000+millis();
|
||||
playerPositionModifier = 0;
|
||||
p.speed = 0;
|
||||
|
||||
int levels = 5; // brightness levels in conveyor
|
||||
|
||||
@@ -909,8 +971,8 @@ void tickConveyors(){
|
||||
leds[led] = CRGB(0, 0, b);
|
||||
}
|
||||
|
||||
if(player.position > conveyorPool[i]._startPoint && player.position < conveyorPool[i]._endPoint){
|
||||
playerPositionModifier = speed;
|
||||
if(p.position > conveyorPool[i]._startPoint && p.position < conveyorPool[i]._endPoint){
|
||||
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 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
|
||||
{
|
||||
// fill to top
|
||||
int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), user_settings.led_count), 0);
|
||||
for(int i = getLED(player.position); i<= n; i++){
|
||||
int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player[0].position), user_settings.led_count), 0);
|
||||
for(int i = getLED(player[0].position); i<= n; i++){
|
||||
leds[i] = CRGB(255, 0, 0);
|
||||
}
|
||||
// fill to bottom
|
||||
n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), 0), 0);
|
||||
for(int i = getLED(player.position); i>= n; i--){
|
||||
n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player[0].position), 0), 0);
|
||||
for(int i = getLED(player[0].position); i>= n; i--){
|
||||
leds[i] = CRGB(255, 0, 0);
|
||||
}
|
||||
SFXgameover();
|
||||
@@ -1053,29 +1115,32 @@ void tickWin(long mm) {
|
||||
|
||||
void drawLives()
|
||||
{
|
||||
// show how many lives are left by drawing a short line of green leds for each life
|
||||
SFXcomplete(); // stop any sounds
|
||||
FastLED.clear();
|
||||
// show how many lives are left by drawing a short line of green leds for each life
|
||||
SFXcomplete(); // stop any sounds
|
||||
FastLED.clear();
|
||||
|
||||
int pos = 0;
|
||||
for (int i = 0; i < player.Lives(); i++)
|
||||
{
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
leds[pos++] = CRGB(0, 255, 0);
|
||||
for (const Player& p : player) {
|
||||
int pos = 0;
|
||||
for (int i = 0; i < p.Lives(); i++)
|
||||
{
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
leds[pos++] = CRGB(p.color[0], p.color[1], p.color[2]);
|
||||
FastLED.show();
|
||||
}
|
||||
leds[pos++] = CRGB(0, 0, 0);
|
||||
delay(20);
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
leds[pos++] = CRGB(0, 0, 0);
|
||||
delay(20);
|
||||
}
|
||||
FastLED.show();
|
||||
delay(400);
|
||||
FastLED.clear();
|
||||
delay(400);
|
||||
FastLED.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void drawAttack(Player p){
|
||||
void drawAttack(const Player& p){
|
||||
if(p.captured) return;
|
||||
if(!p.attacking) return;
|
||||
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++){
|
||||
@@ -1086,7 +1151,7 @@ void drawAttack(Player p){
|
||||
leds[getLED(p.position)] = CRGB(255, 255, 255);
|
||||
}else{
|
||||
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);
|
||||
@@ -1202,22 +1267,45 @@ void getInput() {
|
||||
// and any value to joystickWobble that is greater than ATTACK_THRESHOLD (defined at start)
|
||||
// For example you could use 3 momentary buttons:
|
||||
|
||||
bool up = digitalRead(upButtonPinNumber) == LOW;
|
||||
bool down = digitalRead(downButtonPinNumber) == LOW;
|
||||
bool left = digitalRead(leftButtonPinNumber) == LOW;
|
||||
bool right = digitalRead(rightButtonPinNumber) == LOW;
|
||||
bool up = digitalRead(upButtonPinNumber1) == LOW;
|
||||
bool down = digitalRead(downButtonPinNumber1) == LOW;
|
||||
bool left = digitalRead(leftButtonPinNumber1) == LOW;
|
||||
//bool right = digitalRead(rightButtonPinNumber) == LOW;
|
||||
bool right = left;
|
||||
|
||||
joystickTilt = 0;
|
||||
joystickTilt[0] = 0;
|
||||
if (up) {
|
||||
joystickTilt = 90 ;
|
||||
joystickTilt[0] = 90 ;
|
||||
} else if (down) {
|
||||
joystickTilt = -90;
|
||||
joystickTilt[0] = -90;
|
||||
}
|
||||
|
||||
if (left || right) {
|
||||
joystickWobble = user_settings.attack_threshold;
|
||||
joystickWobble[0] = user_settings.attack_threshold;
|
||||
} 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);
|
||||
if(playerPositionModifier < 0) f -= 500;
|
||||
if(playerPositionModifier > 0) f += 200;
|
||||
int vol = map(abs(amount), 0, 90, user_settings.audio_volume / 2, user_settings.audio_volume * 3/4);
|
||||
// TODO, how to modify for multiplayer?
|
||||
//if(playerPositionModifier < 0) f -= 500;
|
||||
//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);
|
||||
}
|
||||
void SFXattacking(){
|
||||
|
||||
@@ -29,17 +29,17 @@
|
||||
/* Uncomment below to use the accelerometer base joystic of original implementation */
|
||||
//#define USE_GYRO_JOYSTICK
|
||||
|
||||
#define DATA_PIN 16
|
||||
#define CLOCK_PIN 17
|
||||
#define DATA_PIN 0
|
||||
#define CLOCK_PIN 9
|
||||
|
||||
/* Game is rendered to this and scaled down to your strip.
|
||||
This allows level definitions to work on all strip lengths */
|
||||
#define VIRTUAL_LED_COUNT 1000
|
||||
|
||||
// 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
|
||||
#if !defined(USE_NEOPIXEL) && !defined(USE_APA102)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <EEPROM.h>
|
||||
#include "sound.h"
|
||||
@@ -12,7 +12,7 @@
|
||||
#define EEPROM_SIZE 256
|
||||
|
||||
// LEDS
|
||||
#define NUM_LEDS 144
|
||||
#define NUM_LEDS 288
|
||||
#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 MAX_ATTACK_THRESHOLD 30000
|
||||
|
||||
#define leftButtonPinNumber 26
|
||||
#define rightButtonPinNumber 18
|
||||
#define upButtonPinNumber 19
|
||||
#define downButtonPinNumber 23
|
||||
#define leftButtonPinNumber1 5
|
||||
//define rightButtonPinNumber1 6
|
||||
#define upButtonPinNumber1 6
|
||||
#define downButtonPinNumber1 7
|
||||
|
||||
#define leftButtonPinNumber2 1
|
||||
//define rightButtonPinNumber 2
|
||||
#define upButtonPinNumber2 3
|
||||
#define downButtonPinNumber2 4
|
||||
|
||||
|
||||
|
||||
#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 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{
|
||||
ERR_SETTING_NUM,
|
||||
|
||||
@@ -36,17 +36,18 @@ int dac_pin;
|
||||
void IRAM_ATTR onSoundTimer()
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
dacWrite(dac_pin, 0);
|
||||
//else
|
||||
// dacWrite(dac_pin, 0);
|
||||
}
|
||||
|
||||
void sound_init(int pin){ // pin must be a DAC pin number !! (typically 25 or 26)
|
||||
return;
|
||||
dac_pin = pin;
|
||||
sound_on = false;
|
||||
pinMode(dac_pin, OUTPUT);
|
||||
//pinMode(dac_pin, OUTPUT);
|
||||
sound_volume = 0;
|
||||
|
||||
#ifdef OLD_CODE
|
||||
@@ -76,6 +77,7 @@ void sound_resume() // resume from pause ... after eeprom write
|
||||
}
|
||||
|
||||
bool sound(uint16_t freq, uint8_t volume){
|
||||
return true;
|
||||
if (volume == 0) {
|
||||
soundOff();
|
||||
return false;
|
||||
@@ -91,6 +93,7 @@ bool sound(uint16_t freq, uint8_t volume){
|
||||
}
|
||||
|
||||
void soundOff(){
|
||||
return;
|
||||
sound_on = false;
|
||||
sound_volume = 0;
|
||||
//timerAlarmWrite(sndTimer, ESP32_F_CPU/AUDIO_INTERRUPT_PRESCALER/(MIN_FREQ), true); // lower timer freq
|
||||
|
||||
@@ -24,7 +24,7 @@ void Twang_MPU::initialize()
|
||||
{
|
||||
Wire.beginTransmission(MPU_ADDR);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user