Started encpsulation of player logic

This commit is contained in:
Phew
2026-01-06 23:04:15 +01:00
parent b9939bbc2d
commit 08686e0fd6
2 changed files with 164 additions and 84 deletions

89
TWANG32/Player.h Normal file
View File

@@ -0,0 +1,89 @@
#include "Arduino.h"
class Player
{
public:
Player(int[3], int, int);
void Lives(int);
int Lives();
void Kill();
bool Alive();
void moveto(int);
void moveby(int);
int color[3];
int position;
bool attacking;
int last_attack;
void updateState(bool tilted, bool wobbled, int time);
void startAttack(int time);
int _lastState; // 0-> idle; 1-> move; 2 -> attack
private:
int _lives;
int _state;
int _attackDuration;
};
Player::Player(int player_color[3], int lives, int attackDuration){
_lives = lives;
attacking = false;
color[0] = player_color[0];
color[1] = player_color[1];
color[2] = player_color[2];
_attackDuration = attackDuration;
_lastState = 0; // start as idle;
}
void Player::updateState(bool tilted, bool wobbled, int time) {
// attack timed out
if(attacking && last_attack+_attackDuration < time) attacking = false;
// no longer attacking
if (!wobbled) attacking = false;
_lastState = 0;
if (tilted) _lastState = 1; // moving
if (wobbled) _lastState = 2; // attacking
}
void Player::startAttack(int time){
// if already attacking just return
if (attacking) return;
// I got tired of attacking, I should be idle or moving for a little before attacking again.
if (_lastState == 2) return;
last_attack = time;
attacking = true;
}
void Player::Lives(int n){
_lives = n;
}
bool Player::Alive(){
return _lives > 0;
}
void Player::Kill(){
_lives--;
if (_lives < 0) _lives = 0;
}
int Player::Lives(){
return _lives;
}
void Player::moveby(int amount){
position += amount;
if (position<0) position=0;
}
void Player::moveto(int amount){
position = amount;
if (position<0) position=0;
}

View File

@@ -22,6 +22,7 @@
Usage Notes: Usage Notes:
- Changes attack model and added Player class
- Changes to LED strip and other hardware are in config.h - Changes to LED strip and other hardware are in config.h
- Change the strip type to what you are using and compile/load firmware - Change the strip type to what you are using and compile/load firmware
- Use Serial port or Wifi to set your strip length. - Use Serial port or Wifi to set your strip length.
@@ -42,6 +43,7 @@
#include "twang_mpu.h" #include "twang_mpu.h"
#endif #endif
#include "Enemy.h" #include "Enemy.h"
#include "Player.h"
#include "Particle.h" #include "Particle.h"
#include "Spawner.h" #include "Spawner.h"
#include "Lava.h" #include "Lava.h"
@@ -75,9 +77,9 @@ int joystickWobble = 0; // Stores the max amount of acceleration (wob
#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 //long attackMillis = 0; // Time the attack started
bool attacking = 0; // Is the attack in progress? //bool attacking = 0; // Is the attack in progress?
bool canAttackAgain = 0; // Have I finished my previous attack? //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
@@ -135,6 +137,9 @@ Conveyor conveyorPool[CONVEYOR_COUNT] = {
Boss boss = Boss(); Boss boss = Boss();
int pcolor[3] = {0,255,0};
Player player = Player(pcolor, LIVES_PER_LEVEL, ATTACK_DURATION);
enum stages { enum stages {
STARTUP, STARTUP,
PLAY, PLAY,
@@ -146,11 +151,8 @@ 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 playerPosition; // Stores the player position
int playerPositionModifier; // +/- adjustment to player position int playerPositionModifier; // +/- adjustment to player position
bool playerAlive;
long killTime; long killTime;
int lives = LIVES_PER_LEVEL;
bool lastLevel = false; bool lastLevel = false;
int score = 0; int score = 0;
@@ -241,7 +243,7 @@ void setup() {
stage = STARTUP; stage = STARTUP;
stageStartTime = millis(); stageStartTime = millis();
lives = user_settings.lives_per_level; player.Lives ( user_settings.lives_per_level );
} }
void loop() { void loop() {
@@ -252,7 +254,7 @@ void loop() {
checkSerialInput(); checkSerialInput();
if(stage == PLAY){ if(stage == PLAY){
if(attacking){ if(player.attacking){
SFXattacking(); SFXattacking();
}else{ }else{
SFXtilt(joystickTilt); SFXtilt(joystickTilt);
@@ -270,7 +272,7 @@ void loop() {
previousMillis = mm; previousMillis = mm;
if((abs(joystickTilt) > user_settings.joystick_deadzone) || if((abs(joystickTilt) > user_settings.joystick_deadzone) ||
(joystickWobble >= user_settings.attack_threshold)) (abs(joystickWobble) >= user_settings.attack_threshold))
{ {
lastInputTime = mm; lastInputTime = mm;
if(stage == SCREENSAVER){ if(stage == SCREENSAVER){
@@ -300,48 +302,38 @@ void loop() {
} }
}else if(stage == PLAY){ }else if(stage == PLAY){
// PLAYING // PLAYING
if(attacking && attackMillis+ATTACK_DURATION < mm) {
attacking = 0;
canAttackAgain = 0;
}
if(joystickWobble < user_settings.attack_threshold){ if (joystickWobble >= user_settings.attack_threshold) player.startAttack(mm);
attacking = 0;
canAttackAgain = 1;
}
// If not attacking, check if they should be player.updateState(
if(!attacking && canAttackAgain && (joystickWobble >= user_settings.attack_threshold)){ abs(joystickTilt) > user_settings.joystick_deadzone,
attackMillis = mm; abs(joystickWobble) >= user_settings.attack_threshold,
attacking = 1; mm);
}
// If still not attacking, move! // If still not attacking, move!
playerPosition += playerPositionModifier; player.moveby(playerPositionModifier);
if(!attacking){ if(!player.attacking){
SFXtilt(joystickTilt); 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 (6.0 / (16.0 / MIN_REDRAW_INTERVAL))
int moveAmount = (joystickTilt/(6.0)); // 6.0 is ideal at 16ms interval int moveAmount = (joystickTilt/(6.0)); // 6.0 is ideal at 16ms interval
if(DIRECTION) moveAmount = -moveAmount; if(DIRECTION) moveAmount = -moveAmount;
moveAmount = constrain(moveAmount, -MAX_PLAYER_SPEED, MAX_PLAYER_SPEED); moveAmount = constrain(moveAmount, -MAX_PLAYER_SPEED, MAX_PLAYER_SPEED);
playerPosition -= moveAmount; player.moveby( -moveAmount );
if(playerPosition < 0)
playerPosition = 0;
// stop player from leaving if boss is alive // stop player from leaving if boss is alive
if (boss.Alive() && playerPosition >= VIRTUAL_LED_COUNT) // move player back if (boss.Alive() && player.position >= VIRTUAL_LED_COUNT) // move player back
playerPosition = 999; //(user_settings.led_count - 1) * (1000.0/user_settings.led_count); player.moveto( 999 ); //(user_settings.led_count - 1) * (1000.0/user_settings.led_count);
if(playerPosition >= VIRTUAL_LED_COUNT && !boss.Alive()) { if(player.position >= VIRTUAL_LED_COUNT && !boss.Alive()) {
// Reached exit! // Reached exit!
levelComplete(); levelComplete();
return; return;
} }
} }
if(inLava(playerPosition)){ if(inLava(player.position)){
die(); die(player);
} }
// Ticks and draw calls // Ticks and draw calls
@@ -351,13 +343,13 @@ void loop() {
tickBoss(); tickBoss();
tickLava(); tickLava();
tickEnemies(); tickEnemies();
drawPlayer(); drawPlayer(player);
drawAttack(); drawAttack(player);
drawExit(); drawExit();
}else if(stage == DEAD){ }else if(stage == DEAD){
// DEAD // DEAD
FastLED.clear(); FastLED.clear();
tickDie(mm); tickDie(player, mm);
if(!tickParticles()){ if(!tickParticles()){
loadLevel(); loadLevel();
} }
@@ -377,7 +369,7 @@ void loop() {
// restart from the beginning // restart from the beginning
stage = STARTUP; stage = STARTUP;
stageStartTime = millis(); stageStartTime = millis();
lives = user_settings.lives_per_level; player.Lives ( user_settings.lives_per_level );
} }
} }
//FastLED.show(); //FastLED.show();
@@ -393,12 +385,11 @@ void loadLevel(){
FastLED.setBrightness(user_settings.led_brightness); FastLED.setBrightness(user_settings.led_brightness);
updateLives(); updateLives();
cleanupLevel(); cleanupLevel();
playerAlive = 1;
lastLevel = false; // this gets changed on the boss level lastLevel = false; // this gets changed on the boss level
/// 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;
playerPosition = 0; player.moveto( 0 );
/* ==== 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
@@ -451,7 +442,7 @@ void loadLevel(){
===== Other things you can adjust per level ================ ===== Other things you can adjust per level ================
Player Start position: Player Start position:
playerPosition = xxx; player.moveto( xxx );
The size of the TWANG attack The size of the TWANG attack
@@ -461,7 +452,7 @@ void loadLevel(){
*/ */
switch(levelNumber){ switch(levelNumber){
case 0: // basic introduction case 0: // basic introduction
playerPosition = 200; player.moveto( 200 );
spawnEnemy(1, 0, 0, 0); spawnEnemy(1, 0, 0, 0);
break; break;
case 1: case 1:
@@ -508,7 +499,7 @@ void loadLevel(){
break; break;
case 8: case 8:
// lava moving up // lava moving up
playerPosition = 200; player.moveto( 200 );
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);
@@ -610,7 +601,7 @@ 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 > playerPosition?1:-1; enemyPool[e].playerSide = pos > player.position?1:-1;
return; return;
} }
} }
@@ -662,7 +653,7 @@ void levelComplete(){
} }
if (levelNumber != 0) // no points for the first level if (levelNumber != 0) // no points for the first level
{ {
score = score + (lives * 10); // score = score + (player.Lives() * 10); //
} }
} }
@@ -672,11 +663,11 @@ void nextLevel(){
if(lastLevel) { if(lastLevel) {
stage = STARTUP; stage = STARTUP;
stageStartTime = millis(); stageStartTime = millis();
lives = user_settings.lives_per_level; player.Lives ( user_settings.lives_per_level );
} }
else { else {
lives = user_settings.lives_per_level; player.Lives ( user_settings.lives_per_level );
loadLevel(); loadLevel();
} }
} }
@@ -687,19 +678,19 @@ void gameOver(){
loadLevel(); loadLevel();
} }
void die(){ void die(Player p){
playerAlive = 0;
if(levelNumber > 0)
lives --;
if(lives == 0){ if(levelNumber > 0)
p.Kill();
if(!player.Alive()){
stage = GAMEOVER; stage = GAMEOVER;
stageStartTime = millis(); stageStartTime = millis();
} }
else else
{ {
for(int p = 0; p < PARTICLE_COUNT; p++){ for(int ip = 0; ip < PARTICLE_COUNT; ip++){
particlePool[p].Spawn(playerPosition); particlePool[ip].Spawn(p.position);
} }
stageStartTime = millis(); stageStartTime = millis();
stage = DEAD; stage = DEAD;
@@ -754,8 +745,8 @@ void tickEnemies(){
if(enemyPool[i].Alive()){ if(enemyPool[i].Alive()){
enemyPool[i].Tick(); enemyPool[i].Tick();
// Hit attack? // Hit attack?
if(attacking){ if(player.attacking){
if(enemyPool[i]._pos > playerPosition-(attack_width/2) && enemyPool[i]._pos < playerPosition+(attack_width/2)){ if(enemyPool[i]._pos > player.position-(attack_width/2) && enemyPool[i]._pos < player.position+(attack_width/2)){
enemyPool[i].Kill(); enemyPool[i].Kill();
SFXkill(); SFXkill();
} }
@@ -770,10 +761,10 @@ void tickEnemies(){
} }
// Hit player? // Hit player?
if( if(
(enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= playerPosition) || (enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= player.position) ||
(enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= playerPosition) (enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= player.position)
){ ){
die(); die(player);
return; return;
} }
} }
@@ -789,15 +780,15 @@ void tickBoss(){
leds[i] %= 100; leds[i] %= 100;
} }
// CHECK COLLISION // CHECK COLLISION
if(getLED(playerPosition) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(playerPosition) < getLED(boss._pos + BOSS_WIDTH)){ if(getLED(player.position) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(player.position) < getLED(boss._pos + BOSS_WIDTH)){
die(); die(player);
return; return;
} }
// CHECK FOR ATTACK // CHECK FOR ATTACK
if(attacking){ if(player.attacking){
if( if(
(getLED(playerPosition+(attack_width/2)) >= getLED(boss._pos - BOSS_WIDTH/2) && getLED(playerPosition+(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(playerPosition-(attack_width/2)) <= getLED(boss._pos + BOSS_WIDTH/2) && getLED(playerPosition-(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(); boss.Hit();
if(boss.Alive()){ if(boss.Alive()){
@@ -811,8 +802,8 @@ void tickBoss(){
} }
} }
void drawPlayer(){ void drawPlayer(Player p){
leds[getLED(playerPosition)] = CRGB(0, 255, 0); leds[getLED(p.position)] = CRGB(p.color[0], p.color[1], p.color[2]);
} }
void drawExit(){ void drawExit(){
@@ -918,7 +909,7 @@ void tickConveyors(){
leds[led] = CRGB(0, 0, b); leds[led] = CRGB(0, 0, b);
} }
if(playerPosition > conveyorPool[i]._startPoint && playerPosition < conveyorPool[i]._endPoint){ if(player.position > conveyorPool[i]._startPoint && player.position < conveyorPool[i]._endPoint){
playerPositionModifier = speed; playerPositionModifier = speed;
} }
} }
@@ -980,7 +971,7 @@ void tickBossKilled(long mm) // boss funeral
} }
} }
void tickDie(long mm) { // a short bright explosion...particles persist after it. void tickDie(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
@@ -989,14 +980,14 @@ void tickDie(long mm) { // a short bright explosion...particles persist after it
int brightness = map((mm-stageStartTime), 0, duration, 255, 150); // this allows a fade from white to red int brightness = map((mm-stageStartTime), 0, duration, 255, 150); // this allows a fade from white to red
// fill up // fill up
int n = _max(map(((mm-stageStartTime)), 0, duration, getLED(playerPosition), getLED(playerPosition)+width), 0); int n = _max(map(((mm-stageStartTime)), 0, duration, getLED(p.position), getLED(p.position)+width), 0);
for(int i = getLED(playerPosition); i<= n; i++){ for(int i = getLED(p.position); i<= n; i++){
leds[i] = CRGB(255, brightness, brightness); leds[i] = CRGB(255, brightness, brightness);
} }
// fill to down // fill to down
n = _max(map(((mm-stageStartTime)), 0, duration, getLED(playerPosition), getLED(playerPosition)-width), 0); n = _max(map(((mm-stageStartTime)), 0, duration, getLED(p.position), getLED(p.position)-width), 0);
for(int i = getLED(playerPosition); i>= n; i--){ for(int i = getLED(p.position); i>= n; i--){
leds[i] = CRGB(255, brightness, brightness); leds[i] = CRGB(255, brightness, brightness);
} }
} }
@@ -1009,13 +1000,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(playerPosition), user_settings.led_count), 0); int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), user_settings.led_count), 0);
for(int i = getLED(playerPosition); i<= n; i++){ for(int i = getLED(player.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(playerPosition), 0), 0); n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), 0), 0);
for(int i = getLED(playerPosition); i>= n; i--){ for(int i = getLED(player.position); i>= n; i--){
leds[i] = CRGB(255, 0, 0); leds[i] = CRGB(255, 0, 0);
} }
SFXgameover(); SFXgameover();
@@ -1067,7 +1058,7 @@ void drawLives()
FastLED.clear(); FastLED.clear();
int pos = 0; int pos = 0;
for (int i = 0; i < lives; i++) for (int i = 0; i < player.Lives(); i++)
{ {
for (int j=0; j<4; j++) for (int j=0; j<4; j++)
{ {
@@ -1084,21 +1075,21 @@ void drawLives()
void drawAttack(){ void drawAttack(Player p){
if(!attacking) return; if(!p.attacking) return;
int n = map(millis() - attackMillis, 0, ATTACK_DURATION, 100, 5); int n = map(millis() - p.last_attack, 0, ATTACK_DURATION, 100, 5);
for(int i = getLED(playerPosition-(attack_width/2))+1; i<=getLED(playerPosition+(attack_width/2))-1; i++){ for(int i = getLED(p.position-(attack_width/2))+1; i<=getLED(p.position+(attack_width/2))-1; i++){
leds[i] = CRGB(0, 0, n); leds[i] = CRGB(0, 0, n);
} }
if(n > 90) { if(n > 90) {
n = 255; n = 255;
leds[getLED(playerPosition)] = CRGB(255, 255, 255); leds[getLED(p.position)] = CRGB(255, 255, 255);
}else{ }else{
n = 0; n = 0;
leds[getLED(playerPosition)] = CRGB(0, 255, 0); leds[getLED(p.position)] = CRGB(0, 255, 0);
} }
leds[getLED(playerPosition-(attack_width/2))] = CRGB(n, n, 255); leds[getLED(p.position-(attack_width/2))] = CRGB(n, n, 255);
leds[getLED(playerPosition+(attack_width/2))] = CRGB(n, n, 255); leds[getLED(p.position+(attack_width/2))] = CRGB(n, n, 255);
} }
int getLED(int pos){ int getLED(int pos){