Updates and New Features

- JOYSTICK_DEBUG was left on...turned it off
 - Web page fields now have
type='number' so they bring up a number keyboard
 - Refresh the
brightness when it changes.
 - More Screen Savers
 - Better looking
reset after gameover
 - Better looking restart after boss kill
 -
Updated readme with video and latest info
This commit is contained in:
bdring
2018-05-01 12:49:05 -05:00
parent 56d8127345
commit 2a480255e0
2 changed files with 322 additions and 167 deletions

View File

@@ -9,13 +9,13 @@
It was inspired by Robin Baumgarten's Line Wobbler Game
Recent Changes
- fixed bug in settings when initial read failed. It was
trying to turn off the sound timer while it was still NULL
- Number of LEDs is now adjustable via serial and wifi. Woot, no
recompile just to change strip length.
- A little refactoring so Wifi and Serial interfaces can share
some code.
- Added Refresh button to web page
- JOYSTICK_DEBUG was left on...turned it off
- Web page fields now have type='number' so they bring up a number keyboard
- Refresh the brightness when it changes.
- More Screen Savers
- Better looking reset after gameover
- Better looking restart after boss kill
- Updated readme with video and latest info
*/
@@ -45,7 +45,7 @@
#define VIRTUAL_LED_COUNT 1000
// what type of LED Strip....pick one
// what type of LED Strip....uncomment only one
#define USE_APA102
//#define USE_NEOPIXEL
@@ -303,10 +303,12 @@ void loop() {
{
FastLED.clear();
save_game_stats(false); // boss not killed
//score = 0; // reset the score
levelNumber = 0;
lives = user_settings.lives_per_level;
loadLevel();
// restart from the beginning
stage = STARTUP;
stageStartTime = millis();
lives = user_settings.lives_per_level;
}
}
@@ -322,9 +324,10 @@ void loop() {
// ---------------------------------
void loadLevel(){
// leave these alone
FastLED.setBrightness(user_settings.led_brightness);
updateLives();
cleanupLevel();
playerAlive = 1;
cleanupLevel();
playerAlive = 1;
lastLevel = false; // this gets changed on the boss level
/// Defaults...OK to change the following items in the levels below
@@ -388,119 +391,120 @@ void loadLevel(){
*/
switch(levelNumber){
case 0: // basic introduction
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(VIRTUAL_LED_COUNT, 3000, 2, 0, 0);
break;
case 3:
// Lava intro
spawnLava(400, 490, 2000, 2000, 0, Lava::OFF);
spawnEnemy(350, 0, 1, 0);
spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 5500, 3, 0, 0);
break;
case 4:
// Sin enemy
spawnEnemy(700, 1, 7, 275);
spawnEnemy(500, 1, 5, 250);
break;
case 5:
// Sin enemy swarm
spawnEnemy(700, 1, 7, 275);
spawnEnemy(500, 1, 5, 250);
spawnEnemy(600, 1, 7, 200);
spawnEnemy(800, 1, 5, 350);
spawnEnemy(400, 1, 7, 150);
spawnEnemy(450, 1, 5, 400);
break;
case 6:
// Conveyor
spawnConveyor(100, 600, -6);
spawnEnemy(800, 0, 0, 0);
break;
case 7:
// Conveyor of enemies
spawnConveyor(50, VIRTUAL_LED_COUNT, 6);
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 8: // spawn train;
spawnPool[0].Spawn(900, VIRTUAL_LED_COUNT, 3, 0, 0);
break;
case 9: // spawn train skinny width;
attack_width = 32;
spawnPool[0].Spawn(900, 1800, 2, 0, 0);
break;
case 10: // evil fast split spawner
spawnPool[0].Spawn(550, 1100, 3, 0, 0);
spawnPool[1].Spawn(550, 1100, 3, 1, 0);
break;
case 11: // split spawner with exit blocking lava
spawnPool[0].Spawn(500, 1100, 3, 0, 0);
spawnPool[1].Spawn(500, 1100, 3, 1, 0);
spawnLava(900, 950, 2200, 800, 2000, Lava::OFF);
break;
case 12:
// Lava run
spawnLava(195, 300, 2000, 2000, 0, Lava::OFF);
spawnLava(400, 500, 2000, 2000, 0, Lava::OFF);
spawnLava(600, 700, 2000, 2000, 0, Lava::OFF);
spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 3800, 4, 0, 0);
break;
case 13:
// Sin enemy #2 practice (slow conveyor)
spawnEnemy(700, 1, 7, 275);
spawnEnemy(500, 1, 5, 250);
spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 5500, 4, 0, 3000);
spawnPool[1].Spawn(0, 5500, 5, 1, 10000);
spawnConveyor(100, 900, -4);
break;
case 14:
// Sin enemy #2 (fast conveyor)
spawnEnemy(700, 1, 7, 275);
spawnEnemy(500, 1, 5, 250);
spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 5500, 4, 0, 3000);
spawnPool[1].Spawn(0, 5500, 5, 1, 10000);
spawnConveyor(100, 900, -6);
break;
case 15: // (don't edit last level)
// Boss this should always be the last level
spawnBoss();
break;
}
stageStartTime = millis();
stage = PLAY;
switch(levelNumber){
case 0: // basic introduction
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, Lava::OFF);
spawnEnemy(350, 0, 1, 0);
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:
// Sin enemy swarm
spawnEnemy(700, 1, 7, 275);
spawnEnemy(500, 1, 5, 250);
spawnEnemy(600, 1, 7, 200);
spawnEnemy(800, 1, 5, 350);
spawnEnemy(400, 1, 7, 150);
spawnEnemy(450, 1, 5, 400);
break;
case 6:
// Conveyor
spawnConveyor(100, 600, -6);
spawnEnemy(800, 0, 0, 0);
break;
case 7:
// Conveyor of enemies
spawnConveyor(50, 1000, 6);
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 8: // spawn train;
spawnPool[0].Spawn(900, 1300, 2, 0, 0);
break;
case 9: // spawn train skinny attack width;
attack_width = 32;
spawnPool[0].Spawn(900, 1800, 2, 0, 0);
break;
case 10: // evil fast split spawner
spawnPool[0].Spawn(550, 1500, 2, 0, 0);
spawnPool[1].Spawn(550, 1500, 2, 1, 0);
break;
case 11: // split spawner with exit blocking lava
spawnPool[0].Spawn(500, 1200, 2, 0, 0);
spawnPool[1].Spawn(500, 1200, 2, 1, 0);
spawnLava(900, 950, 2200, 800, 2000, Lava::OFF);
break;
case 12:
// Lava run
spawnLava(195, 300, 2000, 2000, 0, Lava::OFF);
spawnLava(400, 500, 2000, 2000, 0, Lava::OFF);
spawnLava(600, 700, 2000, 2000, 0, Lava::OFF);
spawnPool[0].Spawn(1000, 3800, 4, 0, 0);
break;
case 13:
// Sin enemy #2 practice (slow conveyor)
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, -4);
break;
case 14:
// Sin enemy #2 (fast conveyor)
spawnEnemy(800, 1, 7, 275);
spawnEnemy(700, 1, 7, 275);
spawnEnemy(500, 1, 5, 250);
spawnPool[0].Spawn(1000, 3000, 4, 0, 3000);
spawnPool[1].Spawn(0, 5500, 5, 1, 10000);
spawnConveyor(100, 900, -6);
break;
case 15: // (don't edit last level)
// Boss this should always be the last level
spawnBoss();
break;
}
stageStartTime = millis();
stage = PLAY;
}
void spawnBoss(){
lastLevel = true;
boss.Spawn();
moveBoss();
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);
int spawnSpeed = 1800;
if(boss._lives == 2) spawnSpeed = 1600;
if(boss._lives == 1) spawnSpeed = 1000;
spawnPool[0].Spawn(boss._pos, spawnSpeed, 3, 0, 0);
spawnPool[1].Spawn(boss._pos, spawnSpeed, 3, 1, 0);
}
/* ======================== spawn Functions =====================================
@@ -572,13 +576,18 @@ void levelComplete(){
}
void nextLevel(){
levelNumber ++;
levelNumber ++;
if(lastLevel)
levelNumber = 0;
lives = user_settings.lives_per_level;
loadLevel();
if(lastLevel) {
stage = STARTUP;
stageStartTime = millis();
lives = user_settings.lives_per_level;
}
else {
lives = user_settings.lives_per_level;
loadLevel();
}
}
void gameOver(){
@@ -856,19 +865,18 @@ void tickComplete(long mm) // the boss is dead
void tickBossKilled(long mm) // boss funeral
{
static uint8_t gHue = 0;
FastLED.setBrightness(255); // super bright!
int brightness = 0;
FastLED.clear();
if(stageStartTime+500 > mm){
int n = _max(map(((mm-stageStartTime)), 0, 500, user_settings.led_count, 0), 0);
for(int i = user_settings.led_count; i>= n; i--){
brightness = (sin(((i*10)+mm)/500.0)+1)*255;
leds[i].setHSV(brightness, 255, 50);
}
SFXbosskilled();
}else if(stageStartTime+6500 > mm){
for(int i = user_settings.led_count; i>= 0; i--){
brightness = (sin(((i*10)+mm)/500.0)+1)*255;
leds[i].setHSV(brightness, 255, 50);
if(stageStartTime+6500 > mm){
gHue++;
fill_rainbow( leds, user_settings.led_count, gHue, 7); // FastLED's built in rainbow
if( random8() < 200) { // add glitter
leds[ random16(user_settings.led_count) ] += CRGB::White;
}
SFXbosskilled();
}else if(stageStartTime+7000 > mm){
@@ -879,7 +887,6 @@ void tickBossKilled(long mm) // boss funeral
}
SFXcomplete();
}else{
save_game_stats(true); // true = boss was killed
nextLevel();
}
}
@@ -1044,37 +1051,24 @@ void save_game_stats(bool bossKill)
// --------- SCREENSAVER -----------
// ---------------------------------
void screenSaverTick(){
int n, b, c, i;
long mm = millis();
int mode = (mm/30000)%5;
SFXcomplete(); // turn off sound...play testing showed this to be a problem
for(i = 0; i<user_settings.led_count; 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<user_settings.led_count; i++){
if(i%10 == n){
leds[i] = CHSV( c, 255, 150);
}
}
}else if(mode >= 1){
// Random flashes
randomSeed(mm);
for(i = 0; i<user_settings.led_count; i++){
if(random8(20) == 0){
leds[i] = CHSV( 25, 255, 100);
}
}
}
if (mode == 0) {
LED_march();
}
else if (mode == 1) {
random_LED_flashes();
}
else if (mode == 2)
sinelon();
else if (mode == 3)
juggle();
else {
Fire2012();
}
}
@@ -1239,4 +1233,144 @@ long map_constrain(long x, long in_min, long in_max, long out_min, long out_max)
return map(x, in_min, in_max, out_min, out_max);
}
// Fire2012 by Mark Kriegsman, July 2012
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
////
// This basic one-dimensional 'fire' simulation works roughly as follows:
// There's a underlying array of 'heat' cells, that model the temperature
// at each point along the line. Every cycle through the simulation,
// four steps are performed:
// 1) All cells cool down a little bit, losing heat to the air
// 2) The heat from each cell drifts 'up' and diffuses a little
// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
// 4) The heat from each cell is rendered as a color into the leds array
// The heat-to-color mapping uses a black-body radiation approximation.
//
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
//
// This simulation scales it self a bit depending on NUM_LEDS; it should look
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
//
// I recommend running this simulation at anywhere from 30-100 frames per second,
// meaning an interframe delay of about 10-35 milliseconds.
//
// Looks best on a high-density LED setup (60+ pixels/meter).
//
//
// There are two main parameters you can play with to control the look and
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
// in step 3 above).
//
// COOLING: How much does the air cool as it rises?
// Less cooling = taller flames. More cooling = shorter flames.
// Default 50, suggested range 20-100
#define COOLING 75
// SPARKING: What chance (out of 255) is there that a new spark will be lit?
// Higher chance = more roaring fire. Lower chance = more flickery fire.
// Default 120, suggested range 50-200.
#define SPARKING 40
//======================================== SCREEN SAVERS =================
void Fire2012()
{
// Array of temperature readings at each simulation cell
static byte heat[VIRTUAL_LED_COUNT]; // the most possible
bool gReverseDirection = false;
// Step 1. Cool down every cell a little
for( int i = 0; i < user_settings.led_count; i++) {
heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / user_settings.led_count) + 2));
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for( int k= user_settings.led_count - 1; k >= 2; k--) {
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
if( random8() < SPARKING ) {
int y = random8(7);
heat[y] = qadd8( heat[y], random8(160,255) );
}
// Step 4. Map from heat cells to LED colors
for( int j = 0; j < user_settings.led_count; j++) {
CRGB color = HeatColor( heat[j]);
int pixelnumber;
if( gReverseDirection ) {
pixelnumber = (user_settings.led_count-1) - j;
} else {
pixelnumber = j;
}
leds[pixelnumber] = color;
}
}
void LED_march() {
int n, b, c, i;
long mm = millis();
for(i = 0; i<user_settings.led_count; i++){
leds[i].nscale8(250);
}
// 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<user_settings.led_count; i++){
if(i%10 == n){
leds[i] = CHSV( c, 255, 150);
}
}
}
void random_LED_flashes() {
long mm = millis();
int i;
for(i = 0; i<user_settings.led_count; i++){
leds[i].nscale8(250);
}
randomSeed(mm);
for(i = 0; i<user_settings.led_count; i++){
if(random8(20) == 0){
leds[i] = CHSV( 25, 255, 100);
}
}
}
void sinelon()
{
static uint8_t gHue = 0; // rotating "base color" used by many of the patterns
gHue++;
// a colored dot sweeping back and forth, with fading trails
fadeToBlackBy( leds, user_settings.led_count, 20);
int pos = beatsin16(13,0,user_settings.led_count);
leds[pos] += CHSV( gHue, 255, 192);
}
void juggle() {
// eight colored dots, weaving in and out of sync with each other
fadeToBlackBy( leds, user_settings.led_count, 20);
byte dothue = 0;
for( int i = 0; i < 4; i++) {
leds[beatsin16( i+7, 0, user_settings.led_count-1 )] |= CHSV(dothue, 200, 255);
dothue += 64;
}
}