erst
This commit is contained in:
commit
6a77947381
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,98 @@
|
|||
/*--------------------------------------------------------------------
|
||||
This file is part of the Adafruit NeoPixel library.
|
||||
|
||||
NeoPixel is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
--------------------------------------------------------------------*/
|
||||
|
||||
#ifndef ADAFRUIT_NEOPIXEL_H
|
||||
#define ADAFRUIT_NEOPIXEL_H
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#include <pins_arduino.h>
|
||||
#endif
|
||||
|
||||
// 'type' flags for LED pixels (third parameter to constructor):
|
||||
#define NEO_RGB 0x00 // Wired for RGB data order
|
||||
#define NEO_GRB 0x01 // Wired for GRB data order
|
||||
#define NEO_BRG 0x04
|
||||
|
||||
#define NEO_COLMASK 0x01
|
||||
#define NEO_KHZ800 0x02 // 800 KHz datastream
|
||||
#define NEO_SPDMASK 0x02
|
||||
// Trinket flash space is tight, v1 NeoPixels aren't handled by default.
|
||||
// Remove the ifndef/endif to add support -- but code will be bigger.
|
||||
// Conversely, can comment out the #defines to save space on other MCUs.
|
||||
#ifndef __AVR_ATtiny85__
|
||||
#define NEO_KHZ400 0x00 // 400 KHz datastream
|
||||
#endif
|
||||
|
||||
class Adafruit_NeoPixel {
|
||||
|
||||
public:
|
||||
|
||||
// Constructor: number of LEDs, pin number, LED type
|
||||
Adafruit_NeoPixel(uint16_t n, uint8_t p=6, uint8_t t=NEO_GRB + NEO_KHZ800);
|
||||
~Adafruit_NeoPixel();
|
||||
|
||||
void
|
||||
begin(void),
|
||||
show(void),
|
||||
setPin(uint8_t p),
|
||||
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
|
||||
setPixelColor(uint16_t n, uint32_t c),
|
||||
setPixelColorHsv(uint16_t n, uint16_t h, uint8_t s, uint8_t v),
|
||||
setBrightness(uint8_t),
|
||||
clear();
|
||||
uint8_t
|
||||
*getPixels(void) const,
|
||||
getBrightness(void) const;
|
||||
uint16_t
|
||||
numPixels(void) const;
|
||||
static uint32_t
|
||||
Color(uint8_t r, uint8_t g, uint8_t b);
|
||||
uint32_t
|
||||
getPixelColor(uint16_t n) const;
|
||||
inline bool
|
||||
canShow(void) { return (micros() - endTime) >= 50L; }
|
||||
|
||||
private:
|
||||
|
||||
const uint16_t
|
||||
numLEDs, // Number of RGB LEDs in strip
|
||||
numBytes; // Size of 'pixels' buffer below
|
||||
uint8_t
|
||||
pin, // Output pin number
|
||||
brightness,
|
||||
*pixels, // Holds LED color values (3 bytes each)
|
||||
rOffset, // Index of red byte within each 3-byte pixel
|
||||
gOffset, // Index of green byte
|
||||
bOffset; // Index of blue byte
|
||||
const uint8_t
|
||||
type; // Pixel flags (400 vs 800 KHz, RGB vs GRB color)
|
||||
uint32_t
|
||||
endTime; // Latch timing reference
|
||||
#ifdef __AVR__
|
||||
const volatile uint8_t
|
||||
*port; // Output PORT register
|
||||
uint8_t
|
||||
pinMask; // Output PORT bitmask
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // ADAFRUIT_NEOPIXEL_H
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Arduino 1D Pong Game with (60) WS2812B LEDs
|
||||
*
|
||||
* Copyright (C) 2015 B.Stultiens
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __ARDUINO_1DP_NOTES_H
|
||||
#define __ARDUINO_1DP_NOTES_H
|
||||
|
||||
/*
|
||||
* Frequencies of all notes playable by an Arduino spanning the most used
|
||||
* misical range.
|
||||
*/
|
||||
#define NTONE_PITCH 61 /* Number of pitches */
|
||||
|
||||
/* These are offset+1 into the pitch table */
|
||||
#define NOTE_C3 1
|
||||
#define NOTE_Cs3 2
|
||||
#define NOTE_Df3 NOTE_Cs3
|
||||
#define NOTE_D3 3
|
||||
#define NOTE_Ds3 4
|
||||
#define NOTE_Ef3 NOTE_Ds3
|
||||
#define NOTE_E3 5
|
||||
#define NOTE_F3 6
|
||||
#define NOTE_Fs3 7
|
||||
#define NOTE_Gf3 NOTE_Fs3
|
||||
#define NOTE_G3 8
|
||||
#define NOTE_Gs3 9
|
||||
#define NOTE_Af3 NOTE_Gs3
|
||||
#define NOTE_A3 10
|
||||
#define NOTE_As3 11
|
||||
#define NOTE_Bf3 NOTE_As3
|
||||
#define NOTE_B3 12
|
||||
#define NOTE_C4 13
|
||||
#define NOTE_Cs4 14
|
||||
#define NOTE_Df4 NOTE_Cs4
|
||||
#define NOTE_D4 15
|
||||
#define NOTE_Ds4 16
|
||||
#define NOTE_Ef4 NOTE_Ds4
|
||||
#define NOTE_E4 17
|
||||
#define NOTE_F4 18
|
||||
#define NOTE_Fs4 19
|
||||
#define NOTE_Gf4 NOTE_Fs4
|
||||
#define NOTE_G4 20
|
||||
#define NOTE_Gs4 21
|
||||
#define NOTE_Af4 NOTE_Gs4
|
||||
#define NOTE_A4 22 /* This should be 440Hz */
|
||||
#define NOTE_As4 23
|
||||
#define NOTE_Bf4 NOTE_As4
|
||||
#define NOTE_B4 24
|
||||
#define NOTE_C5 25
|
||||
#define NOTE_Cs5 26
|
||||
#define NOTE_Df5 NOTE_Cs5
|
||||
#define NOTE_D5 27
|
||||
#define NOTE_Ds5 28
|
||||
#define NOTE_Ef5 NOTE_Ds5
|
||||
#define NOTE_E5 29
|
||||
#define NOTE_F5 30
|
||||
#define NOTE_Fs5 31
|
||||
#define NOTE_Gf5 NOTE_Fs5
|
||||
#define NOTE_G5 32
|
||||
#define NOTE_Gs5 33
|
||||
#define NOTE_Af5 NOTE_Gs5
|
||||
#define NOTE_A5 34
|
||||
#define NOTE_As5 35
|
||||
#define NOTE_Bf5 NOTE_As5
|
||||
#define NOTE_B5 36
|
||||
#define NOTE_C6 37
|
||||
#define NOTE_Cs6 38
|
||||
#define NOTE_Df6 NOTE_Cs6
|
||||
#define NOTE_D6 39
|
||||
#define NOTE_Ds6 40
|
||||
#define NOTE_Ef6 NOTE_Ds6
|
||||
#define NOTE_E6 41
|
||||
#define NOTE_F6 42
|
||||
#define NOTE_Fs6 43
|
||||
#define NOTE_Gf6 NOTE_Fs6
|
||||
#define NOTE_G6 44
|
||||
#define NOTE_Gs6 45
|
||||
#define NOTE_Af6 NOTE_Gs6
|
||||
#define NOTE_A6 46
|
||||
#define NOTE_As6 47
|
||||
#define NOTE_Bf6 NOTE_As6
|
||||
#define NOTE_B6 48
|
||||
#define NOTE_C7 49
|
||||
#define NOTE_Cs7 50
|
||||
#define NOTE_Df7 NOTE_Cs7
|
||||
#define NOTE_D7 51
|
||||
#define NOTE_Ds7 52
|
||||
#define NOTE_Ef7 NOTE_Ds7
|
||||
#define NOTE_E7 53
|
||||
#define NOTE_F7 54
|
||||
#define NOTE_Fs7 55
|
||||
#define NOTE_Gf7 NOTE_Fs7
|
||||
#define NOTE_G7 56
|
||||
#define NOTE_Gs7 57
|
||||
#define NOTE_Af7 NOTE_Gs7
|
||||
#define NOTE_A7 58
|
||||
#define NOTE_As7 59
|
||||
#define NOTE_Bf7 NOTE_As7
|
||||
#define NOTE_B7 60
|
||||
#define NOTE_C8 61
|
||||
|
||||
/* Duration in ms at 120 BPM */
|
||||
#define DUR_1_1 2000
|
||||
#define DUR_1_2 1000
|
||||
#define DUR_2_2 DUR_1_1
|
||||
#define DUR_1_4 500
|
||||
#define DUR_2_4 DUR_1_2
|
||||
#define DUR_3_4 1500
|
||||
#define DUR_4_4 DUR_2_2
|
||||
#define DUR_1_8 250
|
||||
#define DUR_2_8 DUR_1_4
|
||||
#define DUR_3_8 750
|
||||
#define DUR_4_8 DUR_2_4
|
||||
#define DUR_5_8 1250
|
||||
#define DUR_6_8 DUR_3_4
|
||||
#define DUR_7_8 1750
|
||||
#define DUR_8_8 DUR_4_4
|
||||
#define DUR_1_16 125
|
||||
#define DUR_2_16 DUR_1_8
|
||||
#define DUR_3_16 375
|
||||
#define DUR_4_16 DUR_2_8
|
||||
#define DUR_5_16 625
|
||||
#define DUR_6_16 DUR_3_8
|
||||
#define DUR_7_16 875
|
||||
#define DUR_8_16 DUR_4_8
|
||||
#define DUR_9_16 1125
|
||||
#define DUR_10_16 DUR_5_8
|
||||
#define DUR_11_16 1375
|
||||
#define DUR_12_16 DUR_6_8
|
||||
#define DUR_13_16 1625
|
||||
#define DUR_14_16 DUR_7_8
|
||||
#define DUR_15_16 1875
|
||||
#define DUR_16_16 DUR_8_8
|
||||
#define DUR_1_32 63
|
||||
#define DUR_2_32 DUR_1_16
|
||||
#define DUR_3_32 188
|
||||
#define DUR_4_32 DUR_2_16
|
||||
#define DUR_5_32 313
|
||||
#define DUR_6_32 DUR_3_16
|
||||
#define DUR_7_32 438
|
||||
#define DUR_8_32 DUR_4_16
|
||||
#define DUR_9_32 563
|
||||
#define DUR_10_32 DUR_5_16
|
||||
#define DUR_11_32 688
|
||||
#define DUR_12_32 DUR_6_16
|
||||
#define DUR_13_32 813
|
||||
#define DUR_14_32 DUR_7_16
|
||||
#define DUR_15_32 938
|
||||
#define DUR_16_32 DUR_8_16
|
||||
#define DUR_17_32 1063
|
||||
#define DUR_18_32 DUR_9_16
|
||||
#define DUR_19_32 1188
|
||||
#define DUR_20_32 DUR_10_16
|
||||
#define DUR_21_32 1313
|
||||
#define DUR_22_32 DUR_11_16
|
||||
#define DUR_23_32 1438
|
||||
#define DUR_24_32 DUR_12_16
|
||||
#define DUR_25_32 1563
|
||||
#define DUR_26_32 DUR_13_16
|
||||
#define DUR_27_32 1688
|
||||
#define DUR_28_32 DUR_14_16
|
||||
#define DUR_29_32 1813
|
||||
#define DUR_30_32 DUR_15_16
|
||||
#define DUR_31_32 1938
|
||||
#define DUR_32_32 DUR_16_16
|
||||
|
||||
#endif
|
|
@ -0,0 +1,929 @@
|
|||
/*
|
||||
* Arduino 1D Pong Game with (60) WS2812B LEDs
|
||||
*
|
||||
* Copyright (C) 2015 B.Stultiens
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include "notes.h"
|
||||
#include "pins_arduino.h"
|
||||
|
||||
//DIO
|
||||
#define GPIO23 23
|
||||
#define GPIO22 22
|
||||
#define GPIO18 18 // SPI CLK
|
||||
#define GPIO19 19
|
||||
#define GPIO21 21
|
||||
|
||||
//ADIO
|
||||
#define GPIO25 25
|
||||
#define GPIO26 26
|
||||
#define GPIO27 27
|
||||
#define GPIO32 32
|
||||
#define GPIO33 33
|
||||
#define GPIO34 34
|
||||
#define GPIO35 35
|
||||
#define GPIO36 36
|
||||
#define GPIO39 39
|
||||
|
||||
|
||||
|
||||
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define PIN_WSDATA GPIO21 // LED data
|
||||
#define PIN_BUT_RS GPIO35 // Right start/hit button
|
||||
#define PIN_BUT_RP GPIO33 // Right power-up button
|
||||
#define PIN_BUT_LS GPIO19 // Left start/hit button
|
||||
#define PIN_BUT_LP GPIO18 // Left power-up button
|
||||
#define PIN_SOUND GPIO32 // Buzzer output (PB1/OC1A)
|
||||
|
||||
#define NPIXELS 60 // Number of pixels to handle
|
||||
|
||||
#define ZONE_SIZE 7 // Bounce-back zone size
|
||||
#define SHOW_LO 12 // Score dots intensity background
|
||||
#define SHOW_HI 48 // Score dots intensity foreground
|
||||
#define WIN_POINTS 10 // Points needed to win
|
||||
#define TONE_INTERVAL 5 // Not every ball move should give a sound
|
||||
|
||||
Adafruit_NeoPixel one_d = Adafruit_NeoPixel(NPIXELS, PIN_WSDATA, NEO_GRB | NEO_KHZ800);
|
||||
|
||||
// Events from buttons and timers
|
||||
#define EV_BUT_LS_PRESS 0x01
|
||||
#define EV_BUT_RS_PRESS 0x02
|
||||
#define EV_BUT_LP_PRESS 0x04
|
||||
#define EV_BUT_RP_PRESS 0x08
|
||||
#define EV_TIMER 0x10
|
||||
#define EV_TIMEOUT 0x20
|
||||
#define EV_TONETIMER 0x40
|
||||
|
||||
#define TIME_DEBOUNCE 8
|
||||
#define TIME_IDLE 40
|
||||
#define TIME_START_TIMEOUT 20000 // Go idle if nothing happens
|
||||
#define TIME_RESUME_TIMEOUT 7500 // Auto-fire after timeout
|
||||
#define TIME_BALL_BLINK 150
|
||||
#define TIME_SPEED_MIN 10
|
||||
#define TIME_SPEED_INTERVAL 3
|
||||
#define TIME_POINT_BLINK 233
|
||||
#define TIME_WIN_BLINK 85
|
||||
#define TIME_LOCKOUT 250 // Prevent fast button-press to max. 4 times/s
|
||||
|
||||
#define TIME_TONE_SERVE 50 // Sound durations
|
||||
#define TIME_TONE_BOUNCE 50
|
||||
#define TIME_TONE_MOVE 25
|
||||
#define TIME_TONE_SCORE 50
|
||||
|
||||
enum {
|
||||
ST_IDLE = 0,
|
||||
ST_START_L,
|
||||
ST_START_R,
|
||||
ST_MOVE_LR,
|
||||
ST_MOVE_RL,
|
||||
ST_ZONE_L,
|
||||
ST_ZONE_R,
|
||||
ST_POINT_L,
|
||||
ST_POINT_R,
|
||||
ST_RESUME_L,
|
||||
ST_RESUME_R,
|
||||
ST_WIN_L,
|
||||
ST_WIN_R,
|
||||
};
|
||||
|
||||
static uint32_t oldtime; // Previous' loop millis() value
|
||||
static uint8_t thestate; // Game state
|
||||
|
||||
static uint8_t bstate_ls; // Button states
|
||||
static uint8_t bstate_rs;
|
||||
static uint8_t bstate_lp;
|
||||
static uint8_t bstate_rp;
|
||||
static uint8_t debtmr_ls; // Button debounce timers
|
||||
static uint8_t debtmr_rs;
|
||||
static uint8_t debtmr_lp;
|
||||
static uint8_t debtmr_rp;
|
||||
static uint16_t timer; // General timer
|
||||
static uint16_t timeout; // Timeout timer (auto-start and goto idle)
|
||||
static uint16_t tonetimer; // Tone duration timer
|
||||
static uint16_t lockout_l; // Lockout timer to prevent pushing too often
|
||||
static uint16_t lockout_r;
|
||||
static uint8_t ballblinkstate; // Blinking ball at edge on/off
|
||||
static uint8_t pointblinkcount; // Blinking point when a side scores
|
||||
static uint8_t ballpos; // Current position of the ball
|
||||
static uint16_t speed; // Time between ball moves
|
||||
static uint8_t speedup; // Faster and faster replies counter
|
||||
static uint8_t points_l; // Score
|
||||
static uint8_t points_r;
|
||||
static uint8_t zone_l; // Hit back zone
|
||||
static uint8_t zone_r;
|
||||
static uint8_t boost_l; // Set if user boosted speed last round
|
||||
static uint8_t boost_r;
|
||||
static uint8_t boosted; // Set if any user boosted until the ball reaches opposite side
|
||||
static uint8_t tonecount; // Interval counter for sound during move
|
||||
static uint8_t tuneidx; // Index to the running tune
|
||||
|
||||
struct tnote {
|
||||
uint8_t note;
|
||||
uint16_t duration;
|
||||
};
|
||||
|
||||
/* Tone pitch table for 16MHz crystal */
|
||||
static const uint16_t tone_pitch[NTONE_PITCH] PROGMEM = {
|
||||
61155, 57722, 54482, 51424, 48538, 45814, 43242, 40815, 38524, 36362 /* == 220Hz */, 34321, 32395,
|
||||
30577, 28860, 27240, 25711, 24268, 22906, 21620, 20407, 19261, 18180 /* == 440Hz */, 17160, 16197,
|
||||
15288, 14429, 13619, 12855, 12133, 11452, 10809, 10203, 9630, 9089 /* == 880Hz */, 8579, 8098,
|
||||
7643, 7214, 6809, 6427, 6066, 5725, 5404, 5101, 4814, 4544, 4289, 4048,
|
||||
3821, 3606, 3404, 3213, 3032, 2862, 2701, 2550, 2406, 2271, 2144, 2023,
|
||||
1910,
|
||||
};
|
||||
static const tnote tune_win[] PROGMEM = {
|
||||
{ NOTE_Gs6, DUR_1_16 },
|
||||
{ NOTE_A6, DUR_1_16 },
|
||||
{ NOTE_Gs6, DUR_1_16 },
|
||||
{ NOTE_E6, DUR_1_16 },
|
||||
{ NOTE_Gs6, DUR_1_16 },
|
||||
{ NOTE_A6, DUR_1_16 },
|
||||
{ NOTE_Gs6, DUR_1_16 },
|
||||
{ NOTE_E6, DUR_1_16 },
|
||||
{ 0, DUR_1_8 },
|
||||
{ NOTE_D4, DUR_1_8 },
|
||||
{ NOTE_D4, DUR_1_8 },
|
||||
{ NOTE_B3, DUR_1_8 },
|
||||
{ NOTE_E4, DUR_1_8 },
|
||||
{ NOTE_D4, DUR_1_4 },
|
||||
{ NOTE_B3, DUR_1_4 },
|
||||
{ NOTE_D4, DUR_1_8 },
|
||||
{ NOTE_D4, DUR_1_8 },
|
||||
{ NOTE_B3, DUR_1_8 },
|
||||
{ NOTE_E4, DUR_1_8 },
|
||||
{ NOTE_D4, DUR_1_4 },
|
||||
{ NOTE_B3, DUR_1_4 },
|
||||
};
|
||||
|
||||
//#define sound_off() do { TCCR1A = _BV(COM1A1); /* Set clear output */ } while(0)
|
||||
void sound_off()
|
||||
{
|
||||
// nix tun - muss erfinden
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current state of a button.
|
||||
* Returns non-zero on button pressed.
|
||||
*/
|
||||
static inline uint8_t button_is_down(uint8_t pin)
|
||||
{
|
||||
switch(pin) {
|
||||
case PIN_BUT_LS: return !debtmr_ls && !bstate_ls;
|
||||
case PIN_BUT_RS: return !debtmr_rs && !bstate_rs;
|
||||
case PIN_BUT_LP: return !debtmr_lp && !bstate_lp;
|
||||
case PIN_BUT_RP: return !debtmr_rp && !bstate_rp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debounce a button and return an event at the rising edge of the detection.
|
||||
* The rising edge ensures that there is no delay from pressing the button and
|
||||
* the event propagating. It is a prerequisite that the input line is not
|
||||
* glitchy.
|
||||
* A release event may be generated if the routine is slightly modified.
|
||||
*/
|
||||
static inline uint8_t do_debounce(uint8_t tdiff, uint8_t *bstate, uint8_t *debtmr, uint8_t pin, uint8_t ev)
|
||||
{
|
||||
if(0 == *debtmr) {
|
||||
uint8_t state = digitalRead(pin);
|
||||
if(state != *bstate) {
|
||||
*debtmr = TIME_DEBOUNCE;
|
||||
if(!(*bstate = state))
|
||||
return ev; // Event on High-to-Low transition of input
|
||||
// else
|
||||
// return release_event_value
|
||||
}
|
||||
} else {
|
||||
if(*debtmr >= tdiff)
|
||||
*debtmr -= tdiff;
|
||||
else
|
||||
*debtmr = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Timer countdown and return an event on timer reaching zero.
|
||||
*/
|
||||
static inline uint8_t do_timer(uint8_t tdiff, uint16_t *tmr, uint8_t ev)
|
||||
{
|
||||
if(0 != *tmr) {
|
||||
if(*tmr >= tdiff)
|
||||
*tmr -= tdiff; // Timer countdown
|
||||
else
|
||||
*tmr = 0;
|
||||
// Set event when done counting
|
||||
if(0 == *tmr)
|
||||
return ev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the speaker to generate a tone
|
||||
*/
|
||||
static inline void set_tone(uint16_t note, uint16_t duration)
|
||||
{
|
||||
tonetimer = duration;
|
||||
if(note && note <= NTONE_PITCH)
|
||||
{
|
||||
//OCR1A = pgm_read_word(&tone_pitch[note-1]);
|
||||
//TCCR1A = _BV(COM1A0); /* Set toggle output */
|
||||
//TCNT1 = 0;
|
||||
} else
|
||||
sound_off();
|
||||
}
|
||||
|
||||
/*
|
||||
* Play the next note of the tune
|
||||
*/
|
||||
static inline void tune_next()
|
||||
{
|
||||
if(tuneidx < NELEM(tune_win)) {
|
||||
uint16_t n = pgm_read_byte(&tune_win[tuneidx].note);
|
||||
uint16_t d = pgm_read_word(&tune_win[tuneidx].duration);
|
||||
set_tone(n, d);
|
||||
tuneidx++;
|
||||
} else
|
||||
set_tone(0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the left and right zones where the ball may be hit back.
|
||||
*/
|
||||
static void draw_sides()
|
||||
{
|
||||
for(uint8_t i = 0; i < zone_l-1; i++) {
|
||||
one_d.setPixelColor(i, 0, 64, 64);
|
||||
}
|
||||
one_d.setPixelColor(0, 0, 64, 64);
|
||||
for(uint8_t i = 0; i < zone_r-1; i++) {
|
||||
one_d.setPixelColor(NPIXELS-1-i, 0, 64, 64);
|
||||
}
|
||||
one_d.setPixelColor(NPIXELS-1, 0, 64, 64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the ball with a tail of five pixels in diminishing intensity.
|
||||
*/
|
||||
static void draw_ball(int8_t dir, uint8_t pos)
|
||||
{
|
||||
uint8_t c = 255;
|
||||
for(uint8_t i = 0; i < 5 && pos >= 0 && pos < NPIXELS; i++) {
|
||||
one_d.setPixelColor(pos, c, c, 0);
|
||||
c >>= 1;
|
||||
pos -= dir;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the playing field consisting of the zones and the points scored so far.
|
||||
*/
|
||||
static void draw_course(uint8_t v)
|
||||
{
|
||||
one_d.clear();
|
||||
draw_sides();
|
||||
if(v) {
|
||||
for(uint8_t i = 0; i < points_l; i++) {
|
||||
one_d.setPixelColor(NPIXELS/2-1-(2*i+0), v, 0, 0);
|
||||
one_d.setPixelColor(NPIXELS/2-1-(2*i+1), v, 0, 0);
|
||||
}
|
||||
for(uint8_t i = 0; i < points_r; i++) {
|
||||
one_d.setPixelColor(NPIXELS/2+(2*i+0), 0, v, 0);
|
||||
one_d.setPixelColor(NPIXELS/2+(2*i+1), 0, v, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Animate the game idle situation with following content:
|
||||
* - A rainbow pattern
|
||||
* - Ball bouncing left-right-left-right
|
||||
* - Score animation
|
||||
*/
|
||||
static uint16_t ai_h;
|
||||
static uint8_t ai_state;
|
||||
static uint8_t ai_pos;
|
||||
|
||||
static void animate_idle_init(void)
|
||||
{
|
||||
ai_h = 0;
|
||||
ai_state = 0;
|
||||
}
|
||||
|
||||
#define H_STEPS 1542
|
||||
|
||||
static void animate_idle(void)
|
||||
{
|
||||
switch(ai_state) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
/* Rainbow pattern */
|
||||
for(uint8_t i = 0; i < NPIXELS; i++) {
|
||||
uint16_t h = ai_h + (i << 4);
|
||||
if(h >= H_STEPS)
|
||||
h -= H_STEPS;
|
||||
//*one_d.setPixelColorHsv(i, h, 255, 128);
|
||||
}
|
||||
ai_h += H_STEPS/60;
|
||||
if(ai_h >= H_STEPS) {
|
||||
ai_h -= H_STEPS;
|
||||
ai_pos = 0;
|
||||
ai_state++;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
/* Ball left-to-right */
|
||||
draw_course(0);
|
||||
draw_ball(1, ai_pos++);
|
||||
if(ai_pos >= NPIXELS) {
|
||||
ai_state++;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
/* Ball right-to-left */
|
||||
draw_course(0);
|
||||
draw_ball(-1, --ai_pos);
|
||||
if(!ai_pos) {
|
||||
ai_state++;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
case 10:
|
||||
/* Score blinkenlights */
|
||||
draw_course(0);
|
||||
for(uint8_t i = 0; i < ai_pos; i++) {
|
||||
one_d.setPixelColor(NPIXELS/2-1-i, 255, 0, 0);
|
||||
one_d.setPixelColor(NPIXELS/2+i, 0, 255, 0);
|
||||
}
|
||||
if(++ai_pos >= NPIXELS/2) {
|
||||
ai_state++;
|
||||
ai_pos = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 9:
|
||||
case 11:
|
||||
draw_course(0);
|
||||
for(uint8_t i = 0; i < NPIXELS/2-ai_pos; i++) {
|
||||
one_d.setPixelColor(NPIXELS/2-1-i, 255, 0, 0);
|
||||
one_d.setPixelColor(NPIXELS/2+i, 0, 255, 0);
|
||||
}
|
||||
if(++ai_pos >= NPIXELS/2) {
|
||||
ai_state++;
|
||||
ai_pos = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ai_state = 0;
|
||||
break;
|
||||
}
|
||||
one_d.show();
|
||||
}
|
||||
|
||||
/*
|
||||
* Animate a winner. Flash the winning side's points.
|
||||
*/
|
||||
static uint8_t aw_state;
|
||||
static void animate_win_init()
|
||||
{
|
||||
aw_state = 0;
|
||||
}
|
||||
|
||||
static uint8_t animate_win(uint8_t side)
|
||||
{
|
||||
uint32_t clr;
|
||||
uint8_t pos;
|
||||
|
||||
if(side) {
|
||||
clr = Adafruit_NeoPixel::Color(0, 255, 0);
|
||||
pos = NPIXELS/2;
|
||||
} else {
|
||||
clr = Adafruit_NeoPixel::Color(255, 0, 0);
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
one_d.clear();
|
||||
if(aw_state < 20) {
|
||||
if(aw_state & 0x01) {
|
||||
for(uint8_t i = 0; i < NPIXELS/2; i++) {
|
||||
one_d.setPixelColor(pos+i, clr);
|
||||
}
|
||||
}
|
||||
} else if(aw_state < 50) {
|
||||
for(uint8_t i = 0; i < aw_state - 20; i++) {
|
||||
one_d.setPixelColor(pos+i, clr);
|
||||
}
|
||||
} else if(aw_state < 80) {
|
||||
for(uint8_t i = aw_state - 50; i < NPIXELS/2; i++) {
|
||||
one_d.setPixelColor(pos+i, clr);
|
||||
}
|
||||
} else if(aw_state < 110) {
|
||||
for(uint8_t i = 0; i < aw_state - 80; i++) {
|
||||
one_d.setPixelColor(NPIXELS/2-1-i+pos, clr);
|
||||
}
|
||||
} else if(aw_state < 140) {
|
||||
for(uint8_t i = aw_state - 110; i < NPIXELS/2; i++) {
|
||||
one_d.setPixelColor(NPIXELS/2-1-i+pos, clr);
|
||||
}
|
||||
}
|
||||
one_d.show();
|
||||
return ++aw_state < 140;
|
||||
}
|
||||
|
||||
/*
|
||||
* Active game states suppress fast button pushes
|
||||
*/
|
||||
static uint8_t is_game_state(uint8_t s)
|
||||
{
|
||||
switch(s) {
|
||||
case ST_MOVE_LR: // If you press too soon
|
||||
case ST_MOVE_RL:
|
||||
case ST_ZONE_R: // In the zone
|
||||
case ST_ZONE_L:
|
||||
case ST_POINT_L: // Just got a point, delay resume
|
||||
case ST_POINT_R:
|
||||
case ST_WIN_R: // Delay to activate the win sequence
|
||||
case ST_WIN_L:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the timer to the speed of the ball and the current boost-state
|
||||
*/
|
||||
static inline void speed_to_timer()
|
||||
{
|
||||
if(boosted)
|
||||
timer = speed * 3 / 4;
|
||||
else
|
||||
timer = speed;
|
||||
if(timer < 2)
|
||||
timer = 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* State transition routine. Setup prerequisites for the new state to function
|
||||
* properly.
|
||||
* - Handle a state's exit actions
|
||||
* - Handle a state's entry actions
|
||||
*/
|
||||
static void set_state(uint8_t newstate)
|
||||
{
|
||||
/* State exit actions */
|
||||
switch(thestate) {
|
||||
case ST_IDLE:
|
||||
case ST_WIN_L:
|
||||
case ST_WIN_R:
|
||||
points_l = points_r = 0;
|
||||
boost_l = boost_r = 0;
|
||||
zone_l = zone_r = ZONE_SIZE;
|
||||
speedup = 0;
|
||||
boosted = 0;
|
||||
break;
|
||||
|
||||
case ST_START_L:
|
||||
case ST_POINT_L:
|
||||
case ST_RESUME_L:
|
||||
ballpos = 0;
|
||||
/* Serve speed not too fast */
|
||||
speed = TIME_SPEED_MIN + 5*TIME_SPEED_INTERVAL;
|
||||
speedup = 0;
|
||||
break;
|
||||
|
||||
case ST_START_R:
|
||||
case ST_POINT_R:
|
||||
case ST_RESUME_R:
|
||||
ballpos = NPIXELS-1;
|
||||
/* Serve speed not too fast */
|
||||
speed = TIME_SPEED_MIN + 5*TIME_SPEED_INTERVAL;
|
||||
speedup = 0;
|
||||
break;
|
||||
|
||||
case ST_ZONE_L:
|
||||
/* Calculate the speed for the return */
|
||||
speed = TIME_SPEED_MIN + TIME_SPEED_INTERVAL * ballpos;
|
||||
if(++speedup / 2 >= speed)
|
||||
speed = 2;
|
||||
else
|
||||
speed -= speedup / 2;
|
||||
boosted = 0;
|
||||
break;
|
||||
|
||||
case ST_ZONE_R:
|
||||
/* Calculate the speed for the return */
|
||||
speed = TIME_SPEED_MIN + TIME_SPEED_INTERVAL * (NPIXELS-1 - ballpos);
|
||||
if(++speedup / 2 >= speed)
|
||||
speed = 2;
|
||||
else
|
||||
speed -= speedup / 2;
|
||||
boosted = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
thestate = newstate;
|
||||
/* State entry actions */
|
||||
switch(thestate) {
|
||||
case ST_IDLE:
|
||||
boost_l = boost_r = 0;
|
||||
zone_l = zone_r = ZONE_SIZE;
|
||||
animate_idle_init();
|
||||
timer = TIME_IDLE;
|
||||
break;
|
||||
|
||||
case ST_START_L:
|
||||
case ST_START_R:
|
||||
draw_course(SHOW_HI);
|
||||
one_d.show();
|
||||
timer = TIME_BALL_BLINK;
|
||||
timeout = TIME_START_TIMEOUT;
|
||||
ballblinkstate = 0;
|
||||
ballpos = thestate == ST_START_L ? 0 : NPIXELS-1;
|
||||
break;
|
||||
|
||||
case ST_MOVE_LR:
|
||||
case ST_MOVE_RL:
|
||||
speed_to_timer();
|
||||
tonecount = TONE_INTERVAL;
|
||||
break;
|
||||
|
||||
case ST_POINT_L:
|
||||
case ST_POINT_R:
|
||||
pointblinkcount = 7;
|
||||
/* Recover the zone next round */
|
||||
if(!boost_l && zone_l < ZONE_SIZE)
|
||||
zone_l++;
|
||||
if(!boost_r && zone_r < ZONE_SIZE)
|
||||
zone_r++;
|
||||
timer = TIME_POINT_BLINK;
|
||||
if(boost_l)
|
||||
boost_l--;
|
||||
if(boost_r)
|
||||
boost_r--;
|
||||
// Ensure we get to the score display before continuing
|
||||
lockout_l = lockout_r = TIME_LOCKOUT;
|
||||
break;
|
||||
|
||||
case ST_RESUME_L:
|
||||
case ST_RESUME_R:
|
||||
draw_course(SHOW_HI);
|
||||
one_d.show();
|
||||
timer = TIME_BALL_BLINK;
|
||||
timeout = TIME_RESUME_TIMEOUT;
|
||||
ballblinkstate = 0;
|
||||
break;
|
||||
|
||||
case ST_WIN_L:
|
||||
case ST_WIN_R:
|
||||
// Ensure we get to the winner display before continuing
|
||||
lockout_l = lockout_r = 2 * TIME_LOCKOUT;
|
||||
animate_win_init();
|
||||
timer = TIME_WIN_BLINK;
|
||||
tuneidx = 0;
|
||||
tune_next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Arduino setup
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
//PORTB = PORTC = PORTD = 0xff; // Enable all pull-ups so we don't have undef inputs hanging
|
||||
|
||||
pinMode(PIN_BUT_LS, INPUT_PULLUP);
|
||||
pinMode(PIN_BUT_RS, INPUT_PULLUP);
|
||||
pinMode(PIN_BUT_LP, INPUT_PULLUP);
|
||||
pinMode(PIN_BUT_RP, INPUT_PULLUP);
|
||||
digitalWrite(PIN_SOUND, 0);
|
||||
pinMode(PIN_SOUND, OUTPUT);
|
||||
|
||||
one_d.begin(); // Setup IO
|
||||
one_d.setBrightness(60);
|
||||
one_d.setPixelColor(6, one_d.Color(200,200,0));
|
||||
one_d.show(); // All leds off
|
||||
|
||||
thestate = ST_IDLE;
|
||||
set_state(ST_IDLE); // To run both exit and entry actions
|
||||
|
||||
/*
|
||||
* Setup sound hardware with Timer1 manually. The disabled interrupts
|
||||
* in the pixel-update causes interference in the timing resulting in
|
||||
* clicks in the sound output.
|
||||
*/
|
||||
/* geht nicht bei ESP32 - müsen was eigenes finden
|
||||
TCCR1A = 0;
|
||||
TCCR1B = _BV(WGM12) | _BV(CS10);
|
||||
OCR1A = NOTE_C4; // Just a value
|
||||
TCNT1 = 0;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Main program, called constantly and forever.
|
||||
*
|
||||
* - Handle timing and generate events
|
||||
* - Run the game's state machine
|
||||
*/
|
||||
#define chk_ev(ev) (events & (ev))
|
||||
|
||||
void loop()
|
||||
{
|
||||
uint32_t now;
|
||||
uint8_t tdiff = (now = millis()) - oldtime;
|
||||
uint8_t events = 0;
|
||||
|
||||
/* Handle buttons and timers on (just about) every millisecond */
|
||||
if(tdiff) {
|
||||
oldtime = now;
|
||||
events |= do_debounce(tdiff, &bstate_ls, &debtmr_ls, PIN_BUT_LS, EV_BUT_LS_PRESS);
|
||||
events |= do_debounce(tdiff, &bstate_rs, &debtmr_rs, PIN_BUT_RS, EV_BUT_RS_PRESS);
|
||||
events |= do_debounce(tdiff, &bstate_lp, &debtmr_lp, PIN_BUT_LP, EV_BUT_LP_PRESS);
|
||||
events |= do_debounce(tdiff, &bstate_rp, &debtmr_rp, PIN_BUT_RP, EV_BUT_RP_PRESS);
|
||||
events |= do_timer(tdiff, &timer, EV_TIMER);
|
||||
events |= do_timer(tdiff, &timeout, EV_TIMEOUT);
|
||||
events |= do_timer(tdiff, &tonetimer, EV_TONETIMER);
|
||||
do_timer(tdiff, &lockout_l, 0);
|
||||
do_timer(tdiff, &lockout_r, 0);
|
||||
}
|
||||
|
||||
if(is_game_state(thestate)) {
|
||||
// If the lockout timer is running, squash the button event
|
||||
if(lockout_l)
|
||||
events &= ~EV_BUT_LS_PRESS;
|
||||
if(lockout_r)
|
||||
events &= ~EV_BUT_RS_PRESS;
|
||||
}
|
||||
|
||||
// A button press activates the lockout timer
|
||||
if(chk_ev(EV_BUT_LS_PRESS))
|
||||
lockout_l = TIME_LOCKOUT;
|
||||
if(chk_ev(EV_BUT_RS_PRESS))
|
||||
lockout_r = TIME_LOCKOUT;
|
||||
|
||||
switch(thestate) {
|
||||
// Nothing to do
|
||||
case ST_IDLE:
|
||||
if(chk_ev(EV_BUT_LS_PRESS)) {
|
||||
set_state(ST_START_L);
|
||||
} else if(chk_ev(EV_BUT_RS_PRESS)) {
|
||||
set_state(ST_START_R);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_IDLE;
|
||||
animate_idle();
|
||||
}
|
||||
break;
|
||||
|
||||
// Game is started, waiting for left player to serve the ball
|
||||
case ST_START_L:
|
||||
if(chk_ev(EV_BUT_LS_PRESS)) {
|
||||
set_state(ST_MOVE_LR);
|
||||
} else if(chk_ev(EV_TIMEOUT)) {
|
||||
set_state(ST_IDLE);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_BALL_BLINK;
|
||||
if(ballblinkstate)
|
||||
one_d.setPixelColor(ballpos, 255, 128, 0);
|
||||
else
|
||||
one_d.setPixelColor(ballpos, 0, 0, 0);
|
||||
one_d.show();
|
||||
ballblinkstate = !ballblinkstate;
|
||||
}
|
||||
break;
|
||||
|
||||
// Game is started, waiting for right player to serve the ball
|
||||
case ST_START_R:
|
||||
if(chk_ev(EV_BUT_RS_PRESS)) {
|
||||
set_state(ST_MOVE_RL);
|
||||
} else if(chk_ev(EV_TIMEOUT)) {
|
||||
set_state(ST_IDLE);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_BALL_BLINK;
|
||||
if(ballblinkstate)
|
||||
one_d.setPixelColor(ballpos, 255, 128, 0);
|
||||
else
|
||||
one_d.setPixelColor(ballpos, 0, 0, 0);
|
||||
one_d.show();
|
||||
ballblinkstate = !ballblinkstate;
|
||||
}
|
||||
break;
|
||||
|
||||
// Ball is moving left-to-right outside the playback zone
|
||||
case ST_MOVE_LR:
|
||||
if(chk_ev(EV_TIMER)) {
|
||||
if(!--tonecount) {
|
||||
set_tone(NOTE_G4, TIME_TONE_MOVE);
|
||||
tonecount = TONE_INTERVAL;
|
||||
}
|
||||
speed_to_timer();
|
||||
draw_course(SHOW_LO);
|
||||
draw_ball(1, ballpos);
|
||||
one_d.show();
|
||||
ballpos++;
|
||||
if(NPIXELS-1 - ballpos <= zone_r)
|
||||
set_state(ST_ZONE_R);
|
||||
}
|
||||
break;
|
||||
|
||||
// Ball is moving right-to-left outside the playback zone
|
||||
case ST_MOVE_RL:
|
||||
if(chk_ev(EV_TIMER)) {
|
||||
if(!--tonecount) {
|
||||
set_tone(NOTE_G4, TIME_TONE_MOVE);
|
||||
tonecount = TONE_INTERVAL;
|
||||
}
|
||||
speed_to_timer();
|
||||
draw_course(SHOW_LO);
|
||||
draw_ball(-1, ballpos);
|
||||
one_d.show();
|
||||
ballpos--;
|
||||
if(ballpos <= zone_l)
|
||||
set_state(ST_ZONE_L);
|
||||
}
|
||||
break;
|
||||
|
||||
// Ball is in the left playback zone, waiting for hit/score
|
||||
case ST_ZONE_L:
|
||||
if(chk_ev(EV_BUT_LS_PRESS)) {
|
||||
set_tone(NOTE_G3, TIME_TONE_BOUNCE);
|
||||
set_state(ST_MOVE_LR);
|
||||
// Changing speed is done after the state-change's exit/entry action
|
||||
if(zone_l > 1 && button_is_down(PIN_BUT_LP)) {
|
||||
zone_l--;
|
||||
boosted = 1;
|
||||
speed_to_timer();
|
||||
boost_l++;
|
||||
}
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
if(!ballpos) {
|
||||
set_tone(NOTE_C5, TIME_TONE_SCORE);
|
||||
if(++points_r >= WIN_POINTS)
|
||||
set_state(ST_WIN_R);
|
||||
else
|
||||
set_state(ST_POINT_R);
|
||||
} else {
|
||||
speed_to_timer();
|
||||
ballpos--;
|
||||
}
|
||||
draw_course(SHOW_LO);
|
||||
draw_ball(-1, ballpos);
|
||||
one_d.show();
|
||||
}
|
||||
break;
|
||||
|
||||
// Ball is in the right playback zone, waiting for hit/score
|
||||
case ST_ZONE_R:
|
||||
if(chk_ev(EV_BUT_RS_PRESS)) {
|
||||
set_tone(NOTE_G3, TIME_TONE_BOUNCE);
|
||||
set_state(ST_MOVE_RL);
|
||||
// Changing speed is done after the state-change's exit/entry action
|
||||
if(zone_r > 1 && button_is_down(PIN_BUT_RP)) {
|
||||
zone_r--;
|
||||
speed_to_timer();
|
||||
boosted = 1;
|
||||
boost_r++;
|
||||
}
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
if(ballpos == NPIXELS-1) {
|
||||
set_tone(NOTE_C5, TIME_TONE_SCORE);
|
||||
if(++points_l >= WIN_POINTS)
|
||||
set_state(ST_WIN_L);
|
||||
else
|
||||
set_state(ST_POINT_L);
|
||||
} else {
|
||||
speed_to_timer();
|
||||
ballpos++;
|
||||
}
|
||||
draw_course(SHOW_LO);
|
||||
draw_ball(1, ballpos);
|
||||
one_d.show();
|
||||
}
|
||||
break;
|
||||
|
||||
// Left player scored, animate point
|
||||
case ST_POINT_L:
|
||||
if(chk_ev(EV_BUT_LS_PRESS)) {
|
||||
set_state(ST_RESUME_L);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_POINT_BLINK;
|
||||
draw_course(SHOW_HI);
|
||||
if(!(pointblinkcount & 0x01)) {
|
||||
one_d.setPixelColor(NPIXELS/2-1-(2*(points_l-1)+0), 0, 0, 0);
|
||||
one_d.setPixelColor(NPIXELS/2-1-(2*(points_l-1)+1), 0, 0, 0);
|
||||
} else {
|
||||
one_d.setPixelColor(NPIXELS/2-1-(2*(points_l-1)+0), 255, 0, 0);
|
||||
one_d.setPixelColor(NPIXELS/2-1-(2*(points_l-1)+1), 255, 0, 0);
|
||||
}
|
||||
one_d.show();
|
||||
if(!--pointblinkcount)
|
||||
set_state(ST_RESUME_L);
|
||||
}
|
||||
break;
|
||||
|
||||
// Right player scored, animate point
|
||||
case ST_POINT_R:
|
||||
if(chk_ev(EV_BUT_RS_PRESS)) {
|
||||
set_state(ST_RESUME_R);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_POINT_BLINK;
|
||||
draw_course(SHOW_HI);
|
||||
if(!(pointblinkcount & 0x01)) {
|
||||
one_d.setPixelColor(NPIXELS/2+(2*(points_r-1)+0), 0, 0, 0);
|
||||
one_d.setPixelColor(NPIXELS/2+(2*(points_r-1)+1), 0, 0, 0);
|
||||
} else {
|
||||
one_d.setPixelColor(NPIXELS/2+(2*(points_r-1)+0), 0, 255, 0);
|
||||
one_d.setPixelColor(NPIXELS/2+(2*(points_r-1)+1), 0, 255, 0);
|
||||
}
|
||||
one_d.show();
|
||||
if(!--pointblinkcount)
|
||||
set_state(ST_RESUME_R);
|
||||
}
|
||||
break;
|
||||
|
||||
// Left player previously scored and must serve again (or timeout to auto-serve)
|
||||
case ST_RESUME_L:
|
||||
if(chk_ev(EV_BUT_LS_PRESS | EV_TIMEOUT)) {
|
||||
set_state(ST_MOVE_LR);
|
||||
set_tone(NOTE_F3, TIME_TONE_SERVE);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_BALL_BLINK;
|
||||
if(ballblinkstate)
|
||||
one_d.setPixelColor(ballpos, 255, 128, 0);
|
||||
else
|
||||
one_d.setPixelColor(ballpos, 0, 0, 0);
|
||||
one_d.show();
|
||||
ballblinkstate = !ballblinkstate;
|
||||
}
|
||||
break;
|
||||
|
||||
// Right player previously scored and must serve again (or timeout to auto-serve)
|
||||
case ST_RESUME_R:
|
||||
if(chk_ev(EV_BUT_RS_PRESS | EV_TIMEOUT)) {
|
||||
set_state(ST_MOVE_RL);
|
||||
set_tone(NOTE_F3, TIME_TONE_SERVE);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_BALL_BLINK;
|
||||
if(ballblinkstate)
|
||||
one_d.setPixelColor(ballpos, 255, 128, 0);
|
||||
else
|
||||
one_d.setPixelColor(ballpos, 0, 0, 0);
|
||||
one_d.show();
|
||||
ballblinkstate = !ballblinkstate;
|
||||
}
|
||||
break;
|
||||
|
||||
// A player won the game, animate the winning side
|
||||
case ST_WIN_L:
|
||||
case ST_WIN_R:
|
||||
if(chk_ev(EV_TONETIMER)) {
|
||||
events &= ~EV_TONETIMER; // Remove the event so we don't get messed up with a set_tone(0, 0) below call
|
||||
tune_next();
|
||||
}
|
||||
if(chk_ev(EV_BUT_LS_PRESS)) {
|
||||
set_state(ST_START_L);
|
||||
} else if(chk_ev(EV_BUT_RS_PRESS)) {
|
||||
set_state(ST_START_R);
|
||||
} else if(chk_ev(EV_TIMER)) {
|
||||
timer = TIME_WIN_BLINK;
|
||||
if(!animate_win(thestate == ST_WIN_R))
|
||||
set_state(ST_IDLE);
|
||||
}
|
||||
break;
|
||||
|
||||
// If we get confused, start at idle...
|
||||
default:
|
||||
set_state(ST_IDLE);
|
||||
break;
|
||||
}
|
||||
|
||||
/* The sound timer is async to the rest */
|
||||
/* Alternative is to handle it in each and every state */
|
||||
if(chk_ev(EV_TONETIMER))
|
||||
set_tone(0, 0);
|
||||
|
||||
}
|
||||
|
||||
// vim: syn=cpp
|
Loading…
Reference in New Issue