389 lines
8.2 KiB
C
389 lines
8.2 KiB
C
/**
|
|
* r0ket JUMP!
|
|
*
|
|
* Author: webholics
|
|
*/
|
|
|
|
#include <sysinit.h>
|
|
#include <string.h>
|
|
#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();
|
|
randomInit();
|
|
|
|
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())
|
|
return;
|
|
init_game();
|
|
}
|
|
|
|
delayms(12);
|
|
}
|
|
}
|
|
|
|
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() {
|
|
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);
|
|
}
|