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:
- Changes attack model and added Player class
- Changes to LED strip and other hardware are in config.h
- Change the strip type to what you are using and compile/load firmware
- Use Serial port or Wifi to set your strip length.
@@ -42,6 +43,7 @@
#include "twang_mpu.h"
#endif
#include "Enemy.h"
#include "Player.h"
#include "Particle.h"
#include "Spawner.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
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?
//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
@@ -135,6 +137,9 @@ Conveyor conveyorPool[CONVEYOR_COUNT] = {
Boss boss = Boss();
int pcolor[3] = {0,255,0};
Player player = Player(pcolor, LIVES_PER_LEVEL, ATTACK_DURATION);
enum stages {
STARTUP,
PLAY,
@@ -146,11 +151,8 @@ enum stages {
} stage;
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
bool playerAlive;
long killTime;
int lives = LIVES_PER_LEVEL;
bool lastLevel = false;
int score = 0;
@@ -241,7 +243,7 @@ void setup() {
stage = STARTUP;
stageStartTime = millis();
lives = user_settings.lives_per_level;
player.Lives ( user_settings.lives_per_level );
}
void loop() {
@@ -252,7 +254,7 @@ void loop() {
checkSerialInput();
if(stage == PLAY){
if(attacking){
if(player.attacking){
SFXattacking();
}else{
SFXtilt(joystickTilt);
@@ -270,7 +272,7 @@ void loop() {
previousMillis = mm;
if((abs(joystickTilt) > user_settings.joystick_deadzone) ||
(joystickWobble >= user_settings.attack_threshold))
(abs(joystickWobble) >= user_settings.attack_threshold))
{
lastInputTime = mm;
if(stage == SCREENSAVER){
@@ -300,48 +302,38 @@ void loop() {
}
}else if(stage == PLAY){
// PLAYING
if(attacking && attackMillis+ATTACK_DURATION < mm) {
attacking = 0;
canAttackAgain = 0;
}
if(joystickWobble < user_settings.attack_threshold){
attacking = 0;
canAttackAgain = 1;
}
if (joystickWobble >= user_settings.attack_threshold) player.startAttack(mm);
// If not attacking, check if they should be
if(!attacking && canAttackAgain && (joystickWobble >= user_settings.attack_threshold)){
attackMillis = mm;
attacking = 1;
}
player.updateState(
abs(joystickTilt) > user_settings.joystick_deadzone,
abs(joystickWobble) >= user_settings.attack_threshold,
mm);
// If still not attacking, move!
playerPosition += playerPositionModifier;
if(!attacking){
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);
playerPosition -= moveAmount;
if(playerPosition < 0)
playerPosition = 0;
player.moveby( -moveAmount );
// stop player from leaving if boss is alive
if (boss.Alive() && playerPosition >= VIRTUAL_LED_COUNT) // move player back
playerPosition = 999; //(user_settings.led_count - 1) * (1000.0/user_settings.led_count);
if (boss.Alive() && player.position >= VIRTUAL_LED_COUNT) // move player back
player.moveto( 999 ); //(user_settings.led_count - 1) * (1000.0/user_settings.led_count);
if(playerPosition >= VIRTUAL_LED_COUNT && !boss.Alive()) {
if(player.position >= VIRTUAL_LED_COUNT && !boss.Alive()) {
// Reached exit!
levelComplete();
return;
}
}
if(inLava(playerPosition)){
die();
if(inLava(player.position)){
die(player);
}
// Ticks and draw calls
@@ -351,13 +343,13 @@ void loop() {
tickBoss();
tickLava();
tickEnemies();
drawPlayer();
drawAttack();
drawPlayer(player);
drawAttack(player);
drawExit();
}else if(stage == DEAD){
// DEAD
FastLED.clear();
tickDie(mm);
tickDie(player, mm);
if(!tickParticles()){
loadLevel();
}
@@ -377,7 +369,7 @@ void loop() {
// restart from the beginning
stage = STARTUP;
stageStartTime = millis();
lives = user_settings.lives_per_level;
player.Lives ( user_settings.lives_per_level );
}
}
//FastLED.show();
@@ -393,12 +385,11 @@ void loadLevel(){
FastLED.setBrightness(user_settings.led_brightness);
updateLives();
cleanupLevel();
playerAlive = 1;
lastLevel = false; // this gets changed on the boss level
/// Defaults...OK to change the following items in the levels below
attack_width = DEFAULT_ATTACK_WIDTH;
playerPosition = 0;
player.moveto( 0 );
/* ==== Level Editing Guide ===============
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 ================
Player Start position:
playerPosition = xxx;
player.moveto( xxx );
The size of the TWANG attack
@@ -461,7 +452,7 @@ void loadLevel(){
*/
switch(levelNumber){
case 0: // basic introduction
playerPosition = 200;
player.moveto( 200 );
spawnEnemy(1, 0, 0, 0);
break;
case 1:
@@ -508,7 +499,7 @@ void loadLevel(){
break;
case 8:
// lava moving up
playerPosition = 200;
player.moveto( 200 );
spawnLava(10, 180, 2000, 2000, 0, Lava::OFF, 0, 0.5);
spawnEnemy(350, 0, 1, 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
if(!enemyPool[e].Alive()){
enemyPool[e].Spawn(pos, dir, speed, wobble);
enemyPool[e].playerSide = pos > playerPosition?1:-1;
enemyPool[e].playerSide = pos > player.position?1:-1;
return;
}
}
@@ -662,21 +653,21 @@ void levelComplete(){
}
if (levelNumber != 0) // no points for the first level
{
score = score + (lives * 10); //
score = score + (player.Lives() * 10); //
}
}
void nextLevel(){
levelNumber ++;
if(lastLevel) {
stage = STARTUP;
stageStartTime = millis();
lives = user_settings.lives_per_level;
player.Lives ( user_settings.lives_per_level );
}
else {
lives = user_settings.lives_per_level;
player.Lives ( user_settings.lives_per_level );
loadLevel();
}
}
@@ -687,19 +678,19 @@ void gameOver(){
loadLevel();
}
void die(){
playerAlive = 0;
if(levelNumber > 0)
lives --;
void die(Player p){
if(lives == 0){
if(levelNumber > 0)
p.Kill();
if(!player.Alive()){
stage = GAMEOVER;
stageStartTime = millis();
}
else
{
for(int p = 0; p < PARTICLE_COUNT; p++){
particlePool[p].Spawn(playerPosition);
for(int ip = 0; ip < PARTICLE_COUNT; ip++){
particlePool[ip].Spawn(p.position);
}
stageStartTime = millis();
stage = DEAD;
@@ -754,8 +745,8 @@ void tickEnemies(){
if(enemyPool[i].Alive()){
enemyPool[i].Tick();
// Hit attack?
if(attacking){
if(enemyPool[i]._pos > playerPosition-(attack_width/2) && enemyPool[i]._pos < playerPosition+(attack_width/2)){
if(player.attacking){
if(enemyPool[i]._pos > player.position-(attack_width/2) && enemyPool[i]._pos < player.position+(attack_width/2)){
enemyPool[i].Kill();
SFXkill();
}
@@ -770,10 +761,10 @@ void tickEnemies(){
}
// Hit player?
if(
(enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= playerPosition) ||
(enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= playerPosition)
(enemyPool[i].playerSide == 1 && enemyPool[i]._pos <= player.position) ||
(enemyPool[i].playerSide == -1 && enemyPool[i]._pos >= player.position)
){
die();
die(player);
return;
}
}
@@ -789,15 +780,15 @@ void tickBoss(){
leds[i] %= 100;
}
// CHECK COLLISION
if(getLED(playerPosition) > getLED(boss._pos - BOSS_WIDTH/2) && getLED(playerPosition) < getLED(boss._pos + BOSS_WIDTH)){
die();
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(attacking){
if(player.attacking){
if(
(getLED(playerPosition+(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(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(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()){
@@ -811,8 +802,8 @@ void tickBoss(){
}
}
void drawPlayer(){
leds[getLED(playerPosition)] = CRGB(0, 255, 0);
void drawPlayer(Player p){
leds[getLED(p.position)] = CRGB(p.color[0], p.color[1], p.color[2]);
}
void drawExit(){
@@ -918,7 +909,7 @@ void tickConveyors(){
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;
}
}
@@ -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 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
// fill up
int n = _max(map(((mm-stageStartTime)), 0, duration, getLED(playerPosition), getLED(playerPosition)+width), 0);
for(int i = getLED(playerPosition); i<= n; i++){
int n = _max(map(((mm-stageStartTime)), 0, duration, getLED(p.position), getLED(p.position)+width), 0);
for(int i = getLED(p.position); i<= n; i++){
leds[i] = CRGB(255, brightness, brightness);
}
// fill to down
n = _max(map(((mm-stageStartTime)), 0, duration, getLED(playerPosition), getLED(playerPosition)-width), 0);
for(int i = getLED(playerPosition); i>= n; i--){
n = _max(map(((mm-stageStartTime)), 0, duration, getLED(p.position), getLED(p.position)-width), 0);
for(int i = getLED(p.position); i>= n; i--){
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
{
// fill to top
int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(playerPosition), user_settings.led_count), 0);
for(int i = getLED(playerPosition); i<= n; i++){
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++){
leds[i] = CRGB(255, 0, 0);
}
// fill to bottom
n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(playerPosition), 0), 0);
for(int i = getLED(playerPosition); i>= n; i--){
n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(player.position), 0), 0);
for(int i = getLED(player.position); i>= n; i--){
leds[i] = CRGB(255, 0, 0);
}
SFXgameover();
@@ -1067,7 +1058,7 @@ void drawLives()
FastLED.clear();
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++)
{
@@ -1084,21 +1075,21 @@ void drawLives()
void drawAttack(){
if(!attacking) return;
int n = map(millis() - attackMillis, 0, ATTACK_DURATION, 100, 5);
for(int i = getLED(playerPosition-(attack_width/2))+1; i<=getLED(playerPosition+(attack_width/2))-1; i++){
void drawAttack(Player p){
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++){
leds[i] = CRGB(0, 0, n);
}
if(n > 90) {
n = 255;
leds[getLED(playerPosition)] = CRGB(255, 255, 255);
leds[getLED(p.position)] = CRGB(255, 255, 255);
}else{
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(playerPosition+(attack_width/2))] = CRGB(n, n, 255);
leds[getLED(p.position-(attack_width/2))] = CRGB(n, n, 255);
leds[getLED(p.position+(attack_width/2))] = CRGB(n, n, 255);
}
int getLED(int pos){