diff --git a/firmware/l0dable/mp0ng.c b/firmware/l0dable/mp0ng.c new file mode 100644 index 0000000..416c620 --- /dev/null +++ b/firmware/l0dable/mp0ng.c @@ -0,0 +1,591 @@ + +#include +#include + +#include "basic/basic.h" +#include "basic/config.h" + +#include "lcd/display.h" +#include "lcd/print.h" + +#include "funk/nrf24l01p.h" + +#include "usetable.h" + +#define RESX 96 +#define RESY 68 + +#define CHANNEL 82 +#define ANNOUNCEMENT_MAC "MP0NG" + +#define MAX_POINTS 10 +#define BALL_SIZE 6 +#define PADDLE_OFFSET 10 +#define PADDLE_WIDTH 3 +#define PADDLE_HEIGHT 20 + +#define OFFSET_L PADDLE_OFFSET +#define OFFSET_R RESX - PADDLE_WIDTH - PADDLE_OFFSET + +// Modes the program can be in +#define MODE_SERVER 1 +#define MODE_INIT 0 +#define MODE_CLIENT 2 + +// Packet types +#define PKT_ANNOUNCE 'a' +#define PKT_JOIN 'j' +#define PKT_ACK 'A' +#define PKT_GAMESTATE 's' +#define PKT_MOVEMENT 'm' +#define PKT_EXIT 'e' + +// Directions +#define DIR_STAY BTN_NONE +#define DIR_UP BTN_UP +#define DIR_RIGHT BTN_RIGHT +#define DIR_DOWN BTN_DOWN +#define DIR_LEFT BTN_LEFT + +struct packet{ + uint16_t gameid; // a (hopefully) unique number identifiyng the game + uint8_t seq; // sequential number of packet - to be increased for every packet sent + uint8_t type; // the type of the packet - defined above + + union content{ + struct announce{ + uint8_t gameid; //again the game id - needet for saving open games as announcement structs + uint8_t nick[16]; //nickname of the user offering the game + uint8_t mac[5]; //the mac to use for transmitting the game packets + uint8_t channel; //the channel on which the game packets will be transferred + uint8_t reserved[3];//array to pad the rest of the unuses bytes in the packet + }__attribute__((packed)) announce; + struct join{ + uint8_t nick[16]; + uint8_t reserved[10]; + }__attribute__((packed)) join; + struct ack{ + uint8_t reserved[26]; + }__attribute__((packed)) ack; + struct gamestate{ + uint8_t pad1; //Position of the paddle for player 1 + uint8_t pad2; //same for p2 + uint8_t ball_x; //x positoin of the ball + uint8_t ball_y; //y position of the ball + uint8_t score1; //points for player 1 + uint8_t score2; //same for p2 + uint8_t reserved[20]; + }__attribute__((packed)) state; + struct movement{ + uint8_t dir; //direction in which th player wants to move - defined above + uint8_t reserved[25]; + }__attribute__((packed)) movement; + struct exit{ + uint8_t reserved[26]; + }__attribute__((packed)) exit; + } c; + + uint16_t crc; //checksum of the packet, automatically generated and verfied by the library +}__attribute__((packed)); + +struct player_t{ + int8_t paddle_pos; + int8_t score; + bool second; +}; + +struct ball_t{ + int8_t x; + int8_t y; + int8_t speed_x; + int8_t speed_y; + //bool moving; + int8_t player_has_ball; +}; + +struct ball_t ball1; +struct player_t player1; +struct player_t player2; + +void print_score(); +void move_player(struct player_t *player, uint8_t dir); +void draw_paddles(void); +void draw_ball(); +void shoot_ball(); +void just_draw_ball(); +void check_border(); +//void check_collision_player(struct player_t *player); +void check_collision_players(); + +void init(); + +uint16_t gameid; + +uint8_t next_move; + +uint8_t mode; + +uint8_t started; + +uint8_t receive; +uint8_t player_joined; + +struct packet latest_packet; + +void startServer(void); +void startClient(void); +void handle_packet(struct packet *p); +uint8_t check_end(void); + +void main(void){ + + started = 0; + receive = 1; + + next_move = DIR_STAY; + + mode = MODE_INIT; + + player_joined = 0; + + lcdClear(); + + lcdPrintln("Up: server"); + lcdPrintln("Down: client"); + lcdPrintln("Enter: exit"); + + lcdRefresh(); + + int8_t priv = GLOBAL(privacy); + GLOBAL(privacy) = 3; + + uint8_t btn; + + uint8_t cycles_wait; + cycles_wait = 0; + struct packet announce; + do{ + btn = getInputRaw(); + + if(mode == MODE_INIT){ + if(btn == BTN_UP){ + startServer(); + } else { + if(btn == BTN_DOWN){ + startClient(); + } + } + } + + if(!started){ + if(mode == MODE_SERVER && !player_joined){ + memset((void*)&announce, 0, sizeof(announce)); + announce.gameid = gameid; + announce.type = PKT_ANNOUNCE; + memcpy(announce.c.announce.nick, GLOBAL(nickname), 16); + announce.seq = 0; + nrf_snd_pkt_crc(sizeof(announce), (uint8_t *)&announce); + + struct packet p; + if(nrf_rcv_pkt_time(1000, sizeof(p), (uint8_t *)&p) == sizeof(p)){ + handle_packet(&p); + } + + } else if (mode == MODE_CLIENT){ + struct packet p; + if(nrf_rcv_pkt_time(100, sizeof(p), (uint8_t *)&p) == sizeof(p)){ + handle_packet(&p); + } + } + } else { + if(receive){ + struct packet p; + if(nrf_rcv_pkt_time(50, sizeof(p), (uint8_t *)&p) == sizeof(p)){ + cycles_wait = 0; + handle_packet(&p); + } else { + cycles_wait++; + } + + if(cycles_wait >= 3){ + nrf_snd_pkt_crc(sizeof(latest_packet), (uint8_t *)&latest_packet); + cycles_wait = 0; + } + } else { + delayms(50); + } + + next_move = btn; + } + + }while(btn != BTN_ENTER); + + + struct packet p; + p.type = PKT_EXIT; + for(uint8_t i = 0; i < 3; i++){ + delayms(50); + nrf_snd_pkt_crc(sizeof(p), (uint8_t *)&p); + } + + GLOBAL(privacy) = priv; +} + +void configNrf(){ + struct NRF_CFG config; + config.nrmacs = 1; + config.maclen[0] = 32; + config.channel = CHANNEL; + memcpy(config.mac0, ANNOUNCEMENT_MAC, 5); + memcpy(config.txmac, ANNOUNCEMENT_MAC, 5); + nrf_config_set(&config); + nrf_set_strength(3); +} + +void startServer(void){ + mode = MODE_SERVER; + lcdClear(); + lcdPrintln("Server started"); + lcdRefresh(); + + configNrf(); +} + +void startClient(void){ + mode = MODE_CLIENT; + lcdClear(); + lcdPrintln("Client started"); + lcdRefresh(); + + configNrf(); + + receive = 1; +} + +void handle_packet(struct packet *p){ + switch(p->type){ + case PKT_ANNOUNCE: + if(mode == MODE_CLIENT){ + if(!started){ + gameid = p->gameid; + + struct packet join; + memset((void*)&join, 0, sizeof(join)); + join.gameid = gameid; + join.type = PKT_JOIN; + memcpy(join.c.join.nick, GLOBAL(nickname), 16); + join.seq = p->seq+1; + + nrf_snd_pkt_crc(sizeof(join), (uint8_t *)&join); + + player_joined = 1; + } + } + break; + case PKT_JOIN: + if(mode == MODE_SERVER){ + if(!started){ + if(!player_joined){ + started = 1; + lcdClear(); + lcdPrintln("Player joined"); + lcdPrintln((char *)&p->c.join.nick); + lcdRefresh(); + + struct packet ack; + memset((void*)&ack, 0, sizeof(ack)); + ack.gameid = gameid; + ack.type = PKT_ACK; + //memcpy(ack.c.ack.nick, GLOBAL(nickname), 16); + ack.seq = p->seq+1; + nrf_snd_pkt_crc(sizeof(ack), (uint8_t *)&ack); + + player_joined = 1; + + delayms(1000); + + init(); + + struct packet state; + memset((void*)&state, 0, sizeof(state)); + state.gameid = gameid; + state.type = PKT_GAMESTATE; + state.c.state.score1 = player1.score; + state.c.state.score2 = player2.score; + state.c.state.pad1 = player1.paddle_pos; + state.c.state.pad2 = player2.paddle_pos; + + state.c.state.ball_x = ball1.x; + state.c.state.ball_y = ball1.y; + state.seq = p->seq+2; + nrf_snd_pkt_crc(sizeof(state), (uint8_t *)&state); + } + } + } + break; + case PKT_ACK: + if(mode == MODE_CLIENT){ + if(!started){ + started = 1; + lcdClear(); + lcdPrintln("Joined!"); + //lcdPrintln((char *)&p->c.announce.nick); + + init(); + + lcdRefresh(); + } + } + break; + case PKT_GAMESTATE: + if(mode == MODE_CLIENT){ + if(started){ + player1.score = p->c.state.score1; + player2.score = p->c.state.score2; + player1.paddle_pos = p->c.state.pad1; + player2.paddle_pos = p->c.state.pad2; + + ball1.x = p->c.state.ball_x; + ball1.y = p->c.state.ball_y; + + if(!check_end()){ + lcdClear(); + print_score(); + draw_paddles(); + just_draw_ball(); + lcdDisplay(); + + struct packet move; + memset((void*)&move, 0, sizeof(move)); + move.gameid = gameid; + move.type = PKT_MOVEMENT; + move.c.movement.dir = next_move; + move.seq = p->seq+1; + delayms(50); + nrf_snd_pkt_crc(sizeof(move), (uint8_t *)&move); + latest_packet = move; + + next_move = DIR_STAY; + receive = 1; + } + } + } + break; + case PKT_MOVEMENT: + if(mode == MODE_SERVER){ + if(started){ + if(next_move != DIR_LEFT) // Only let valid keys through + move_player(&player1, next_move); + if(p->c.movement.dir != DIR_RIGHT) // Only let valid keys through + move_player(&player2, p->c.movement.dir); + + check_collision_players(); + draw_ball(); + + lcdClear(); + print_score(); + //check_collision_player(&player1); + //check_collision_player(&player2); + draw_paddles(); + just_draw_ball(); + lcdDisplay(); + + struct packet state; + memset((void*)&state, 0, sizeof(state)); + state.gameid = gameid; + state.type = PKT_GAMESTATE; + + state.c.state.score1 = player1.score; + state.c.state.score2 = player2.score; + state.c.state.pad1 = player1.paddle_pos; + state.c.state.pad2 = player2.paddle_pos; + + state.c.state.ball_x = ball1.x; + state.c.state.ball_y = ball1.y; + + state.seq = p->seq+1; + delayms(50); + nrf_snd_pkt_crc(sizeof(state), (uint8_t *)&state); + latest_packet = state; + + next_move = DIR_STAY; + receive = 1; + + check_end(); + } + } + break; + case PKT_EXIT: + if(started){ + if(mode == MODE_CLIENT) + player2.score = MAX_POINTS; + else + player1.score = MAX_POINTS; + + check_end(); + } + break; + } + +} + +void init() +{ + // init ball + ball1.x = 15; + ball1.y = 15; + ball1.speed_x = 5; + ball1.speed_y = 2; + //ball1.moving = false; + ball1.player_has_ball = 1; + + // init player1 + player1.paddle_pos = 10; + player1.second = false; + player1.score = 0; + + // init player2 + player2.paddle_pos = 10; + player2.second = true; + player2.score = 0; +} + +void print_score(void) +{ + lcdPrint(" "); + lcdPrintInt(player1.score); + lcdPrint(" "); + lcdPrintInt(player2.score); +} + +void move_player(struct player_t *player, uint8_t dir) +{ + if(dir == DIR_UP && player->paddle_pos > 0 ) + { + player->paddle_pos-=3; + } + + if(dir == DIR_DOWN && player->paddle_pos < (RESY - PADDLE_HEIGHT)) + { + player->paddle_pos+=3; + } + + if(dir == DIR_RIGHT && ball1.player_has_ball == 1){ + //ball1.moving = true; + ball1.player_has_ball = 0; + } + + if(dir == DIR_LEFT && ball1.player_has_ball == 2){ + //ball1.moving = true; + ball1.player_has_ball = 0; + } +} + +void draw_paddles(void) +{ + for(int8_t i = 0; i < PADDLE_HEIGHT; i++) + { + for(int8_t j = 0; j < PADDLE_WIDTH; j++) + { + lcdSetPixel(OFFSET_L - j, i+player1.paddle_pos, 1); + lcdSetPixel(OFFSET_R - j, i+player2.paddle_pos, 1); + } + } +} + +void draw_ball(void) +{ + //if(ball1.moving == true) + if(ball1.player_has_ball == 0) + { + ball1.x += ball1.speed_x; + ball1.y += ball1.speed_y; + check_border(); + } + else + { + if(ball1.player_has_ball == 1) + { + ball1.y = player1.paddle_pos + PADDLE_HEIGHT/2 - BALL_SIZE/2; + ball1.x = OFFSET_L + 3; + } + else + { + ball1.y = player2.paddle_pos + PADDLE_HEIGHT/2 - BALL_SIZE/2; + ball1.x = OFFSET_R - 3 - BALL_SIZE; + if(ball1.speed_x > 0) { + ball1.speed_x = -ball1.speed_x; + } + } + } +} + +void just_draw_ball(void){ + for(int8_t i = 0; i < BALL_SIZE; i++) + { + for(int8_t j = 0; j < BALL_SIZE; j++) + { + lcdSetPixel(ball1.x + j, ball1.y + i, 1); + } + } +} + +void check_collision_players(void) { + if (ball1.player_has_ball == 0) { + //Player 2 + if(ball1.x >= (OFFSET_R - PADDLE_WIDTH - 5) ) { + if(ball1.y + BALL_SIZE > player2.paddle_pos && ball1.y < (player2.paddle_pos + PADDLE_HEIGHT) ) { + ball1.speed_x = - ball1.speed_x; + } else { + ball1.speed_x = - ball1.speed_x; + player1.score++; + //ball1.moving = false; + ball1.player_has_ball = 2; + } + } + + //Player 1 + if(ball1.x < (OFFSET_L + PADDLE_WIDTH) ) { + if(ball1.y + BALL_SIZE > player1.paddle_pos && ball1.y < (player1.paddle_pos + PADDLE_HEIGHT) ) { + ball1.speed_x = - ball1.speed_x; + } else { + ball1.speed_x = - ball1.speed_x; + player2.score++; + //ball1.moving=false; + ball1.player_has_ball = 1; + } + } + } +} + +uint8_t check_end(void){ + if(player1.score >= MAX_POINTS || player2.score >= MAX_POINTS){ + receive = 0; + lcdClear(); + lcdNl(); + lcdPrintln(" GAME OVER"); + lcdNl(); + lcdPrint(" Player "); + lcdPrintInt(2 - (player1.score > player2.score)); + lcdPrintln(" won"); + + lcdRefresh(); + + return 1; + } + + return 0; +} + +void check_border() { + if(ball1.speed_y < 0) { + if (ball1.y < 0) { + ball1.speed_y = - ball1.speed_y; + } + } else if (ball1.speed_y > 0) { + if(ball1.y > (RESY - BALL_SIZE) ) { + ball1.speed_y = - ball1.speed_y; + } + } + +}