/** * r0ket JUMP! * * Author: webholics */ #include #include #include "basic/basic.h" #include "basic/config.h" #include "basic/random.h" #include "lcd/render.h" #include "lcd/display.h" #include "funk/mesh.h" #include "usetable.h" #define PLAYER_SPRITE_WIDTH 9 #define PLAYER_SPRITE_HEIGHT 10 static const bool PLAYER_SPRITE_DOWN[] = { 0,0,0,1,1,1,0,0,0, 1,0,0,1,1,1,0,0,1, 1,1,0,0,1,0,0,1,1, 0,1,1,1,1,1,1,1,0, 0,0,1,1,1,1,1,0,0, 0,0,0,1,1,1,0,0,0, 0,0,0,1,1,1,0,0,0, 0,0,1,1,0,1,1,0,0, 0,0,1,1,0,1,1,0,0, 0,0,1,1,0,1,1,0,0 }; static const bool PLAYER_SPRITE_UP[] = { 0,0,0,1,1,1,0,0,0, 0,0,0,1,1,1,0,0,0, 0,0,0,0,1,0,0,0,0, 0,0,1,1,1,1,1,0,0, 0,1,1,1,1,1,1,1,0, 1,1,0,1,1,1,0,1,1, 0,0,0,1,1,1,0,0,0, 0,0,1,1,0,1,1,0,0, 0,0,1,1,0,1,1,0,0, 0,0,1,1,0,1,1,0,0 }; #define GRAVITY 1 #define JUMP_FORCE -9 #define MAX_VEL_Y 5 #define MAX_VEL_X 2 #define NUM_PLATFORMS 30 #define PLATFORM_MARGIN_Y 14 #define PLATFORM_HEIGHT 3 #define PLATFORM_WIDTH 20 #define SPEEDUP_EVERY_FPS 1000 #define REDUCE_PLATFORM_EVERY_FPS 500 #define POS_PLAYER_Y RESY-PLATFORM_HEIGHT #define POS_PLAYER_X (RESX+PLAYER_SPRITE_WIDTH)/2 struct gamestate { bool running; char player_x; int player_y; int player_y_vel; int player_x_vel; bool player_ground; char scroll_speed; uint32_t scroll_pos; char platform_width; int platform_index; int platforms_y[NUM_PLATFORMS]; char platforms_x1[NUM_PLATFORMS]; char platforms_x2[NUM_PLATFORMS]; } game; static void splash_scene(); static void init_game(); static void draw_player(); static void move_player(long frame_count); static void update_platforms(long frame_count); static void draw_platforms(); static void draw_hud(); static bool gameover_scene(); static void blink_led(); static bool highscore_set(uint32_t score, char nick[]); static uint32_t highscore_get(char nick[]); void ram(void) { splash_scene(); long frame_count = 0; init_game(); while(1) { frame_count++; lcdFill(0); update_platforms(frame_count); move_player(frame_count); draw_platforms(); draw_player(); draw_hud(); blink_led(); lcdDisplay(); if(!game.running) { if(!gameover_scene()){ delayms_queue_plus(10,1); return; } init_game(); } delayms_queue_plus(24,0); } } static void splash_scene() { uint32_t highscore; char highnick[20]; char key = 0; while(key == 0) { getInputWaitRelease(); int dy = getFontHeight(); int s1x = DoString(0, 0, "r0ket"); s1x = (RESX-s1x)/2; int s2x = DoString(0, 0, "JUMP!"); s2x = (RESX-s2x)/2; lcdFill(0); DoString(s1x, 3*dy, "r0ket"); DoString(s2x, 4*dy, "JUMP!"); DoString(0, 7*dy, "by webholics"); highscore = highscore_get(highnick); int s3x = DoInt(0, 0, highscore); DoChar(s3x, 0, 'm'); DoString (0, dy, highnick); lcdDisplay(); key = getInputWaitTimeout(1000); } } static void init_game() { game.running = true; game.player_x = POS_PLAYER_X; game.player_y = POS_PLAYER_Y; game.player_y_vel = 0; game.player_x_vel = 0; game.player_ground = true; game.scroll_speed = 8; game.platform_width = 20; game.scroll_pos = 0; game.platform_index = 0; game.platforms_y[0] = RESY - PLATFORM_HEIGHT; game.platforms_x1[0] = 0; game.platforms_x2[0] = RESX; for(int i = 1; i < NUM_PLATFORMS; i++) { game.platforms_y[i] = RESY+1; } } static void update_platforms(long frame_count) { // create new platforms while(1) { int y = game.platforms_y[game.platform_index]; if(y <= 0) { break; } y -= PLATFORM_MARGIN_Y; int x = getRandom() % (RESX-game.platform_width); game.platform_index = (game.platform_index+1)%NUM_PLATFORMS; game.platforms_y[game.platform_index] = y; game.platforms_x1[game.platform_index] = x; game.platforms_x2[game.platform_index] = x + game.platform_width-1; } if(game.scroll_speed > 1 && frame_count % SPEEDUP_EVERY_FPS == 0) { game.scroll_speed--; } if(game.platform_width > 5 && frame_count % REDUCE_PLATFORM_EVERY_FPS == 0) { game.platform_width--; } // move platforms int scroll_speed = game.scroll_speed; if(game.player_y-PLAYER_SPRITE_HEIGHT < 0) { scroll_speed += (game.player_y-PLAYER_SPRITE_HEIGHT) / 5 - 1; if(scroll_speed <= 0) scroll_speed = 1; } if(frame_count % scroll_speed == 0) { game.scroll_pos++; for(int i = 0; i < NUM_PLATFORMS; i++) { game.platforms_y[i]++; } } } static void draw_platforms() { for(int i = 0; i < NUM_PLATFORMS; i++) { if(game.platforms_y[i] <= RESY) { for(int x = game.platforms_x1[i]; x <= game.platforms_x2[i]; x++) { for(int y = game.platforms_y[i]; y < game.platforms_y[i]+PLATFORM_HEIGHT; y++) { if(y >= 0 && y <= RESY) { lcdSetPixel(x, y, 1); } } } } } } static void draw_player() { const bool* sprite; if(game.player_y_vel > 0) { sprite = PLAYER_SPRITE_DOWN; } else { sprite = PLAYER_SPRITE_UP; } for(int x = 0; x < PLAYER_SPRITE_WIDTH; x++) { for(int y = 0; y < PLAYER_SPRITE_HEIGHT; y++) { int py = game.player_y + y - PLAYER_SPRITE_HEIGHT; if(sprite[x + y*PLAYER_SPRITE_WIDTH] && py >= 0 && py < RESY) { lcdSetPixel( (game.player_x + x)%RESX, py, 1); } } } if(game.player_y < 0) { int player_x_center = game.player_x + PLAYER_SPRITE_WIDTH/2; for(int y = 0; y < 2; y++) { for(int x = player_x_center-2; x <= player_x_center+2; x++) { if(x >= 0 && x < RESX && y >= 0 && y < RESY) { lcdSetPixel(x, y, 1); } } } } } static void move_player(long frame_count) { // move x if(gpioGetValue(RB_BTN0) == 0) { game.player_x_vel--; } else if(gpioGetValue(RB_BTN1) == 0) { game.player_x_vel++; } else { game.player_x_vel = 0; } if(game.player_x_vel > MAX_VEL_X) { game.player_x_vel = MAX_VEL_X; } else if(game.player_x_vel < -1*MAX_VEL_X) { game.player_x_vel = -1*MAX_VEL_X; } game.player_x += game.player_x_vel + RESX; game.player_x %= RESX; // move y (jump) // half the speed if(frame_count%2 == 0) { int old_y = game.player_y; if(game.player_ground) { game.player_y_vel = JUMP_FORCE; game.player_ground = false; } game.player_y_vel += GRAVITY; game.player_y_vel = game.player_y_vel > MAX_VEL_Y ? MAX_VEL_Y : game.player_y_vel; int new_y = old_y + game.player_y_vel; // collision detection int player_x_center = game.player_x + PLAYER_SPRITE_WIDTH/2; for(int i = 0; i < NUM_PLATFORMS; i++) { if(game.player_y_vel > 0 && old_y < game.platforms_y[i] && new_y >= game.platforms_y[i] && game.platforms_x1[i] <= player_x_center+2 && game.platforms_x2[i] >= player_x_center-2) { game.player_y = game.platforms_y[i]-1; game.player_y_vel = 0; game.player_ground = true; break; } } game.player_y = new_y; if(game.player_y > RESY + PLAYER_SPRITE_HEIGHT) { game.running = false; } } } static void draw_hud() { int x = DoInt(0, 0, game.scroll_pos / 15); DoChar(x, 0, 'm'); } static void blink_led() { // this is r0ket booooost! if(game.player_y < 0) { gpioSetValue(RB_LED0, 1); gpioSetValue(RB_LED1, 1); gpioSetValue(RB_LED2, 1); gpioSetValue(RB_LED3, 1); return; } gpioSetValue(RB_LED0, game.player_ground); gpioSetValue(RB_LED1, game.player_ground); gpioSetValue(RB_LED2, game.player_ground); gpioSetValue(RB_LED3, game.player_ground); } static bool gameover_scene() { int dy = getFontHeight(); int s1x = DoString(0, 0, "GAME OVER!"); s1x = (RESX-s1x)/2; int s2x = DoString(0, 0, "HIGHTSCORE!"); s2x = (RESX-s2x)/2; char key = 0; while(key != BTN_UP && key != BTN_DOWN) { lcdClear(); if(highscore_set(game.scroll_pos / 15, GLOBAL(nickname))) DoString (s2x, dy, "HIGHSCORE!"); else DoString(s1x, dy, "GAME OVER!"); int x = DoInt(0, 3*dy, game.scroll_pos / 15); DoChar(x, 3*dy, 'm'); DoString(0, 5*dy, "UP to play"); DoString(0, 6*dy, "DOWN to quit"); lcdDisplay(); key = getInputWaitTimeout(5000); } return !(key==BTN_DOWN); } // thank you space invaders ;) static bool highscore_set(uint32_t score, char nick[]) { MPKT * mpkt= meshGetMessage('j'); if(MO_TIME(mpkt->pkt)>score) return false; MO_TIME_set(mpkt->pkt,score); strcpy((char*)MO_BODY(mpkt->pkt),nick); if(GLOBAL(privacy)==0){ uint32touint8p(GetUUID32(),mpkt->pkt+26); mpkt->pkt[25]=0; }; return true; } static uint32_t highscore_get(char nick[]){ MPKT * mpkt= meshGetMessage('j'); strcpy(nick,(char*)MO_BODY(mpkt->pkt)); return MO_TIME(mpkt->pkt); }