Laser tag guns code https://os.mbed.com/users/ddakev/notebook/laser-tag-system/

Dependencies:   mbed 4DGL-uLCD-SE PinDetect SoftI2C

main.cpp

Committer:
ddakev
Date:
2019-04-21
Revision:
0:4c644bb83761
Child:
1:7d957da791a5

File content as of revision 0:4c644bb83761:

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "MMA8452.h"
#include "PinDetect.h"
#include "sfx.h"


uLCD_4DGL uLCD(p9,p10,p11); // LCD (serial tx, serial rx, reset pin;)
Serial pc(USBTX,USBRX);
PinDetect trigger(p12, PullUp);
Ticker acc_reader;
Ticker reloader;
Ticker reload_animation;
Ticker countdown_ticker;
Ticker timeRemaining_ticker;
Ticker red_ticker, green_ticker;
Timeout red_timeout, green_timeout;
Timeout message_timeout;
Timeout vibrator_timeout;
MMA8452 acc(p6, p7, 100000);
Serial xbee(p13, p14);
DigitalOut rst1(p15);
Serial IR(p28, p27);
PwmOut IRLED(p21);
AnalogOut speaker(p18);
PwmOut vibrator(p24);
DigitalOut greenLED(p22);
DigitalOut redLED(p23);

Ticker sound_ticker;
unsigned char* sound_data;
unsigned int num_elements = 0;
int sound_i = 0;
   
int xbeeId = -1;
char playerName[11] = "N/A";
bool waitingForId = false;
int timeRemaining = 0;
int countdown = 0;
bool dead = false;

const unsigned int MAX_BULLETS = 8;
volatile unsigned int bullets;
volatile bool reloading = false;

enum GameState {NOT_STARTED, STARTING, RUNNING, ENDED};

GameState gameState = NOT_STARTED;
volatile unsigned int score = 0;

void switchState(GameState newGameState);
void shot_detected(int shooterId);


void flash_red() {
    redLED = !redLED;
}

void flash_green() {
    greenLED = !greenLED;
}

void stop_red() {
    redLED = 0;
    red_ticker.detach();
}

void stop_green() {
    greenLED = 0;
    green_ticker.detach();
}

void play_sound() {
    speaker = sound_data[sound_i] / 256.0f;
    sound_i++;
    if (sound_i>= num_elements) {
        sound_i = 0;
        //speaker = 0.0f;
        sound_ticker.detach();
    }
}

void clear_header() {
    uLCD.filled_rectangle(0, 0, 128, 10, 0x000000);
}

void clear_middle() {
    uLCD.filled_rectangle(0, 30, 128, 95, 0x000000);
}

void clear_footer() {   
    uLCD.filled_rectangle(0, 95, 128, 128, 0x000000);
}

void redraw_header() {
    clear_header();
    uLCD.color(GREEN);
    uLCD.text_width(1);
    uLCD.text_height(1);
    uLCD.locate(0, 0);
    uLCD.printf("%s", playerName);
    uLCD.locate(13, 0);
    int minutes = timeRemaining / 60;
    int seconds = timeRemaining % 60;
    uLCD.printf("%02d:%02d", minutes, seconds);
}

void redraw_score() {
    clear_middle();
    uLCD.color(GREEN);
    uLCD.text_width(4);
    uLCD.text_height(4);
    uLCD.locate(1, 1);
    uLCD.printf("%03d", score);
}

void redraw_middle_message(char* msg) {
    clear_middle();
    uLCD.color(GREEN);
    uLCD.text_width(1);
    uLCD.text_height(1);
    uLCD.locate(0, 4);
    uLCD.printf("%s", msg);
}

void redraw_footer_message(char* msg) {
    clear_footer();
    uLCD.color(GREEN);
    uLCD.text_width(2);
    uLCD.text_height(2);
    uLCD.locate(0, 6);
    uLCD.printf("%s", msg);
}

void redraw_bullets() {
    clear_footer();
    for(int i=0; i<bullets; i++) {
        int offset = 6 * (i+1) + 9 * i;
        uLCD.filled_rectangle(offset + 1, 95, offset + 8, 113, 0xFF0000);
        uLCD.filled_rectangle(offset + 1, 113, offset + 8, 118, 0xFFDF00);
        uLCD.filled_rectangle(offset, 118, offset + 9, 120, 0xFFDF00);
    }
}

void draw_reload() {
    if(gameState != RUNNING) {
        reload_animation.detach();
        return;
    }
    uLCD.text_width(3);
    uLCD.text_height(3);
    uLCD.locate(0, 4);
    uLCD.color(RED);
    uLCD.printf("RELOAD");
    wait(0.25);
    uLCD.locate(0, 4);
    uLCD.color(WHITE);
    uLCD.printf("RELOAD");
}

void redraw_countdown() {
    clear_middle();
    clear_footer();
    uLCD.text_width(1);
    uLCD.text_height(1);
    uLCD.locate(7, 6);
    uLCD.text_width(4);
    uLCD.text_height(4);
    uLCD.printf("%d", countdown);
}

void start_countdown() {
    countdown --;
    if(countdown == 0) {
        // switch to running
        countdown_ticker.detach();
        switchState(RUNNING);
        return;
    }
    redraw_countdown();
}

void start_tr_countdown() {
    timeRemaining --;
    redraw_header();
    if(timeRemaining == 0) {
        timeRemaining_ticker.detach();
        switchState(ENDED);
        return;
    }
}

void turnOffVibrator() {
    vibrator = 0.0;
}

void fire() {
    if(gameState == NOT_STARTED && xbeeId == -1) {
        waitingForId = true;
        xbee.printf("%s", "nrj\n");
    }
    else if(gameState == RUNNING && !dead) {
        if(bullets > 0) {
            IR.putc('0');   // change to xbeeId
            bullets--;
            redraw_bullets();
            sound_i = 0;
            sound_data = Sounds::SHOT;
            num_elements = SHOT_NUM_ELEMENTS;
            sound_ticker.attach(&play_sound, 1.0 / SAMPLE_RATE);
            vibrator = 0.7;
            vibrator_timeout.attach(&turnOffVibrator, 0.5);
            if(bullets == 0) {
                draw_reload();
                reload_animation.attach(&draw_reload, 0.5);
            }
            xbee.printf("gh%d\n", xbeeId);
        }
        else {
            sound_i = 0;
            sound_data = Sounds::OUT_OF_BULLETS;
            num_elements = OUT_OF_BULLETS_NUM_ELEMENTS;
            sound_ticker.attach(&play_sound, 1.0 / SAMPLE_RATE);
        }
    }
}

void reload() {
    if(bullets < MAX_BULLETS) {
        bullets ++;
        if(bullets == 1) {
            reload_animation.detach();
        }
        redraw_bullets();
        sound_i = 0;
        sound_data = Sounds::RELOADING;
        num_elements = RELOADING_NUM_ELEMENTS;
        sound_ticker.attach(&play_sound, 1.0 / SAMPLE_RATE);
    }
    else {
        reloading = false;
        reloader.detach();
        sound_i = 0;
        sound_data = Sounds::RELOAD_DONE;
        num_elements = RELOAD_DONE_NUM_ELEMENTS;
        sound_ticker.attach(&play_sound, 1.0 / SAMPLE_RATE);
    }
}

void read_acc() {
    double ax, ay, az;
    acc.readXYZGravity(&ax, &ay, &az);
    double acc = ax*ax + ay*ay + az*az;
    double percent_y = ay * ay / acc;
    if(percent_y > 0.56 && ay < 0 && reloading == false && bullets < MAX_BULLETS) {
        pc.printf("RELOADING\r\n");
        reloading = true;
        reloader.attach(&reload, 0.5);
    }
    else if(!(percent_y > 0.56 && ay < 0) && reloading == true) {
        reloading = false;
        reloader.detach();
        sound_i = 0;
        sound_data = Sounds::RELOAD_DONE;
        num_elements = RELOAD_DONE_NUM_ELEMENTS;
        sound_ticker.attach(&play_sound, 1.0 / SAMPLE_RATE);
    }
}

void readMessage(char* buffer) {
    int len = 0;
    while(1) {
        if(xbee.readable()) {
            char c = xbee.getc();
            if(c == '\n') {
                buffer[len++] = '\0';
                return;
            }
            buffer[len++] = c;
        }
        if(IR.readable()) {
            char c = IR.getc();
            pc.putc(c);
            if(c >= '0' && c <= '9') {
                shot_detected(c - '0');
            }
        }
    }
}

void shot_detected(int shooterId) {
    if(!dead && shooterId != xbeeId) {
        xbee.printf("gk%ds%d\n", shooterId, xbeeId);
    }
}

void respawn() {
    dead = false;
    redraw_score();
}

void switchState(GameState newGameState) {
    switch(newGameState) {
        case NOT_STARTED:
            gameState = NOT_STARTED;
            redraw_header();
            redraw_middle_message("Press the trigger to register device");
            redraw_footer_message("Game Not Started");
            break;
        case STARTING:
            gameState = STARTING;
            countdown = 3;
            redraw_countdown();
            countdown_ticker.attach(&start_countdown, 1.0);
            break;
        case RUNNING:
            gameState = RUNNING;
            bullets = MAX_BULLETS;
            score = 0;
            redraw_score();
            redraw_bullets();
            timeRemaining_ticker.attach(&start_tr_countdown, 1.0);
            greenLED = 1;
            redLED = 0;
            green_ticker.attach(&flash_green, 0.1);
            red_ticker.attach(&flash_red, 0.1);
            green_timeout.attach(&stop_green, 1.0);
            red_timeout.attach(&stop_red, 1.0);
            vibrator = 1;
            vibrator_timeout.attach(&turnOffVibrator, 1.0);
            break;
        case ENDED:
            gameState = ENDED;
            clear_footer();
            redraw_middle_message("Game Ended");
            greenLED = 1;
            redLED = 0;
            green_ticker.attach(&flash_green, 0.1);
            red_ticker.attach(&flash_red, 0.1);
            green_timeout.attach(&stop_green, 1.0);
            red_timeout.attach(&stop_red, 1.0);
            vibrator = 1;
            vibrator_timeout.attach(&turnOffVibrator, 1.0);
            break;
        default: break;
    }
}

int main() {
    uLCD.baudrate(3000000);
    pc.baud(9600);
    xbee.baud(9600);
    trigger.attach_deasserted(&fire);
    trigger.setSampleFrequency();
    acc_reader.attach(&read_acc, 0.05);
    rst1 = 0; //Set reset pin to 0
    wait_ms(1);//Wait at least one millisecond
    rst1 = 1;//Set reset pin to 1
    wait_ms(1);//Wait another millisecond
    IRLED.period(1.0/38000.0);
    IRLED = 0.5;
    IR.baud(2400);
    
    redraw_header();
    
    switchState(NOT_STARTED);

    while(1) {
        char msg[255];
        readMessage(msg);
        printf("%s\r\n", msg);
        if(msg[0] == 'n') {
            // action in not started
            if(gameState != NOT_STARTED) continue;
            if(msg[1] == 'i') {
                // receiving id
                if(waitingForId) {
                    xbeeId = msg[2] - '0';
                    pc.printf("ID: %d\n", xbeeId);
                    waitingForId = false;
                }
            }
            else if(msg[1] == 'n') {
                // receiving name
                if(msg[2] - '0' == xbeeId) {
                    // changing name of current gun
                    char name[11];
                    int index = 0;
                    int msgIndex = 3;
                    while(msg[msgIndex] != '\0') {
                        if(index < 10) {
                            name[index++] = msg[msgIndex];
                        }
                        msgIndex++;
                    }
                    name[index] = '\0';
                    strcpy(playerName, name);
                    pc.printf("Name: %s\n", name);
                    redraw_header();
                }
            }
        }
        if(msg[0] == 'g') {
            // game action
            if(msg[1] == 's') {
                // game start initiated
                if(xbeeId != -1) {
                    int duration = 0;
                    int index = 2;
                    while(msg[index] != '\0') {
                        duration = duration * 10 + (msg[index++] - '0');
                    }
                    timeRemaining = duration;
                    redraw_header();
                    switchState(STARTING);
                }
            }
            if(msg[1] == 'e') {
                // game ended
                if(xbeeId != -1) {
                    switchState(ENDED);
                }
            }
            if(msg[1] == 'i') {
                // kill info from server
                if(xbeeId != -1) {
                    int shooterId = msg[2] - '0';
                    int index = 3;
                    char shooterName[11];
                    int nameIndex = 0;
                    while(msg[index] < '0' || msg[index] > '9') {
                        if(nameIndex < 10) {
                            shooterName[nameIndex++] = msg[index];
                        }
                        index++;
                    }
                    shooterName[nameIndex] = '\0';
                    int shooteeId = msg[index++] - '0';
                    char shooteeName[11];
                    nameIndex = 0;
                    while(msg[index] != '\0') {
                        if(nameIndex < 10) {
                            shooteeName[nameIndex++] = msg[index];
                        }
                        index++;
                    }
                    if(shooterId == xbeeId) {
                        char msg[80];
                        strcpy(msg, "You shot ");
                        strcat(msg, shooteeName);
                        strcat(msg, "!");
                        redraw_middle_message(msg);
                        score += 200;
                        message_timeout.attach(&redraw_score, 3.0);
                        green_ticker.attach(&flash_green, 0.05);
                        green_timeout.attach(&stop_green, 3.0);
                        vibrator = 1;
                        vibrator_timeout.attach(&turnOffVibrator, 1.0);
                    }
                    else if(shooteeId == xbeeId) {
                        char msg[80];
                        strcpy(msg, shooterName);
                        strcat(msg, " shot you!");
                        redraw_middle_message(msg);
                        dead = true;
                        if(score < 100) score = 0;
                        else score -= 100;
                        message_timeout.attach(&respawn, 5.0);
                        red_ticker.attach(&flash_red, 0.05);
                        red_timeout.attach(&stop_red, 5.0);
                        vibrator = 1;
                        vibrator_timeout.attach(&turnOffVibrator, 5.0);
                    }
                }
            }
            if(msg[1] == 'r') {
                xbeeId = -1;
                strcpy(playerName, "N/A");
                waitingForId = false;
                dead = false;
                switchState(NOT_STARTED);
                score = 0;
            }
        }
    }
}