X-Git-Url: https://git.rohieb.name/hackover2013-badge-firmware.git/blobdiff_plain/21faf6a1e98788aa0fbf73d97b6d59101fbe400d..25091c2575125996bfc535e9e2d6b7b5a9f977ee:/badge/jumpnrun/jumpnrun.c?ds=sidebyside diff --git a/badge/jumpnrun/jumpnrun.c b/badge/jumpnrun/jumpnrun.c index dc1dc6c..c91aac1 100644 --- a/badge/jumpnrun/jumpnrun.c +++ b/badge/jumpnrun/jumpnrun.c @@ -1,6 +1,9 @@ +#include "game_state.h" #include "jumpnrun.h" #include "collision.h" #include "levels.h" +#include "player.h" +#include "render.h" #include "stats.h" #include "../ui/display.h" @@ -13,77 +16,11 @@ #include #include -static vec2d const gravity = { FIXED_POINT_I(0, 0), FIXED_POINT_I(0, 56) }; -static vec2d const move_max = { FIXED_POINT_I(0, 600), FIXED_POINT_I(1, 300) }; -static fixed_point const accel_horiz = FIXED_POINT_I(0, 50); -static fixed_point const accel_vert = FIXED_POINT_I(0, 167); -static fixed_point const drag_factor = FIXED_POINT_I(0, 854); -static fixed_point const speed_jump_x = FIXED_POINT_I(0, 600); - -vec2d hacker_extents(void) { return (vec2d) { FIXED_INT_I(5), FIXED_INT_I(8) }; } -static vec2d const shot_spawn_inertia = { FIXED_POINT_I(0, 800), FIXED_POINT_I(0, -800) }; - -static badge_sprite const anim_hacker[] = { - { 5, 8, (uint8_t const *) "\x1c\xff\xfd\x04\x04" }, - { 5, 8, (uint8_t const *) "\x1c\xff\x3d\xc4\x04" }, - { 5, 8, (uint8_t const *) "\xdc\x3f\x1d\x24\xc4" }, - { 5, 8, (uint8_t const *) "\x1c\xff\x3d\xc4\x04" } - /* - { 5, 8, (uint8_t const *) "\x46\xfc\x73\x8c\x31" }, - { 5, 8, (uint8_t const *) "\x46\xfc\x73\x8c\x52" }, - { 5, 8, (uint8_t const *) "\x46\xfc\x73\x94\x8c" }, - { 5, 8, (uint8_t const *) "\x46\xfc\x73\x8c\x52" } - */ - /* - { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\xc3\x30" }, - { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\x43\x51" }, - { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\x35\x82" }, - { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\x43\x51" } - */ - /* - { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" }, - { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" }, - { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" }, - { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" } - */ -}; - -badge_sprite const *jumpnrun_hacker_symbol(void) { - return &anim_hacker[0]; -} - -static badge_sprite const anim_sickle[] = { - { 3, 3, (uint8_t const *) "\xab\x01" }, - { 3, 3, (uint8_t const *) "\xee\x00" } -/* - { 3, 3, (uint8_t const *) "\x8a\x01" }, - { 3, 3, (uint8_t const *) "\x6a" }, - { 3, 3, (uint8_t const *) "\xa3" }, - { 3, 3, (uint8_t const *) "\xac" } -*/ -}; - -enum { - JUMPNRUN_SHOT_EXTENT = 3, - JUMPNRUN_SHOT_TICKS_PER_FRAME = 36 -}; - -static void jumpnrun_shot_spawn(jumpnrun_shot *shot, jumpnrun_game_state const *state) { - shot->tick = 0; - shot->inertia = shot_spawn_inertia; - - if(state->player.anim_direction == BADGE_BLT_MIRRORED) { - shot->current_box = rectangle_new((vec2d) { fixed_point_sub(rectangle_left(&state->player.current_box), FIXED_INT(JUMPNRUN_SHOT_EXTENT)), rectangle_top(&state->player.current_box) }, - (vec2d) { FIXED_INT(JUMPNRUN_SHOT_EXTENT), FIXED_INT(JUMPNRUN_SHOT_EXTENT) }); - shot->inertia.x = fixed_point_neg(shot->inertia.x); - } else { - shot->current_box = rectangle_new((vec2d) { rectangle_right(&state->player.current_box), rectangle_top(&state->player.current_box) }, - (vec2d) { FIXED_INT(JUMPNRUN_SHOT_EXTENT), FIXED_INT(JUMPNRUN_SHOT_EXTENT) }); - } - - shot->old_box = shot->current_box; - shot->inertia = vec2d_add(shot->inertia, state->player.inertia); -} +static vec2d gravity () { return (vec2d) { FIXED_POINT(0, 0), FIXED_POINT(0, 56) }; } +static vec2d move_max () { return (vec2d) { FIXED_POINT(0, 600), FIXED_POINT(1, 300) }; } +static fixed_point accel_horiz () { return FIXED_POINT(0, 50); } +static fixed_point accel_vert () { return FIXED_POINT(0, 167); } +static fixed_point drag_factor () { return FIXED_POINT(0, 854); } static inline int imax(int x, int y) { return x < y ? y : x; @@ -91,15 +28,15 @@ static inline int imax(int x, int y) { static inline fixed_point hacker_left (vec2d const *pos, jumpnrun_game_state const *state) { (void) state; return pos->x; } static inline fixed_point hacker_top (vec2d const *pos, jumpnrun_game_state const *state) { (void) state; return pos->y; } -static inline fixed_point hacker_right (vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_left(pos, state), hacker_extents().x); } -static inline fixed_point hacker_bottom(vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_top (pos, state), hacker_extents().y); } +static inline fixed_point hacker_right (vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_left(pos, state), jumpnrun_player_extents().x); } +static inline fixed_point hacker_bottom(vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_top (pos, state), jumpnrun_player_extents().y); } int jumpnrun_level_assert_left_side(jumpnrun_game_state const *state) { static int const lmargin = 20; static int const rmargin = 50; - int pos_cur = fixed_point_cast_int(state->player.current_box.pos.x); - int pos_rel = pos_cur - state->left; + int pos_cur = fixed_point_cast_int(state->player.base.hitbox.pos.x); + int pos_rel = pos_cur - state->screen_left; if(pos_rel < lmargin) { return imax(0, pos_cur - lmargin); @@ -107,7 +44,7 @@ int jumpnrun_level_assert_left_side(jumpnrun_game_state const *state) { return pos_cur - (BADGE_DISPLAY_WIDTH - rmargin); } - return state->left; + return state->screen_left; } static int jumpnrun_bsearch_tile(jumpnrun_level const *lv, jumpnrun_game_state const *state) { @@ -117,7 +54,7 @@ static int jumpnrun_bsearch_tile(jumpnrun_level const *lv, jumpnrun_game_state c while(len > 0) { int mid = front + len / 2; - if(fixed_point_lt(tile_right(&lv->tiles[mid]), FIXED_INT(state->left - JUMPNRUN_MAX_SPAWN_MARGIN))) { + if(fixed_point_lt(tile_right(&lv->tiles[mid]), FIXED_INT(state->screen_left - JUMPNRUN_MAX_SPAWN_MARGIN))) { front = mid + 1; len -= len / 2 + 1; } else { @@ -135,19 +72,23 @@ jumpnrun_tile_range jumpnrun_visible_tiles(jumpnrun_level const *lv, r.first = jumpnrun_bsearch_tile(lv, state); for(r.last = r.first; - r.last < lv->header.tile_count && lv->tiles[r.last].pos.x * JUMPNRUN_TILE_PIXEL_WIDTH < state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN; + r.last < lv->header.tile_count && lv->tiles[r.last].pos.x * JUMPNRUN_TILE_PIXEL_WIDTH < state->screen_left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN; ++r.last) ; return r; } +void jumpnrun_apply_gravity(vec2d *inertia) { + *inertia = vec2d_add(*inertia, gravity()); +} + void jumpnrun_passive_movement(vec2d *inertia) { - *inertia = vec2d_add(*inertia, gravity); + jumpnrun_apply_gravity(inertia); - inertia->x = fixed_point_min(fixed_point_max(fixed_point_neg(move_max.x), inertia->x), move_max.x); - inertia->y = fixed_point_min(fixed_point_max(fixed_point_neg(move_max.y), inertia->y), move_max.y); + inertia->x = fixed_point_min(fixed_point_max(fixed_point_neg(move_max().x), inertia->x), move_max().x); + inertia->y = fixed_point_min(fixed_point_max(fixed_point_neg(move_max().y), inertia->y), move_max().y); } static void jumpnrun_apply_movement(jumpnrun_level const *lv, @@ -158,49 +99,51 @@ static void jumpnrun_apply_movement(jumpnrun_level const *lv, (BADGE_EVENT_KEY_LEFT | BADGE_EVENT_KEY_RIGHT)) { case BADGE_EVENT_KEY_LEFT: - // state->player.inertia.x = state->player.touching_ground ? fixed_point_sub(state->player.inertia.x, accel_horiz) : fixed_point_neg(speed_jump_x); - state->player.inertia.x = fixed_point_sub(state->player.inertia.x, accel_horiz); - state->player.anim_direction = BADGE_BLT_MIRRORED; + // state->player.base.inertia.x = state->player.touching_ground ? fixed_point_sub(state->player.base.inertia.x, accel_horiz()) : fixed_point_neg(speed_jump_x()); + state->player.base.inertia.x = fixed_point_sub(state->player.base.inertia.x, accel_horiz()); + state->player.base.flags |= JUMPNRUN_MOVEABLE_MIRRORED; break; case BADGE_EVENT_KEY_RIGHT: - // state->player.inertia.x = state->player.touching_ground ? fixed_point_add(state->player.inertia.x, accel_horiz) : speed_jump_x; - state->player.inertia.x = fixed_point_add(state->player.inertia.x, accel_horiz); - state->player.anim_direction = 0; + // state->player.base.inertia.x = state->player.touching_ground ? fixed_point_add(state->player.base.inertia.x, accel_horiz()) : speed_jump_x(); + state->player.base.inertia.x = fixed_point_add(state->player.base.inertia.x, accel_horiz()); + state->player.base.flags &= ~JUMPNRUN_MOVEABLE_MIRRORED; break; default: - if(state->player.touching_ground) { - state->player.inertia.x = fixed_point_mul(state->player.inertia.x, drag_factor); + if(jumpnrun_moveable_touching_ground(&state->player.base)) { + state->player.base.inertia.x = fixed_point_mul(state->player.base.inertia.x, drag_factor()); } //else { - //state->player.inertia.x = FIXED_INT(0); + //state->player.base.inertia.x = FIXED_INT(0); //} break; } - if(state->player.jumpable_frames == 0) { + if(state->player.base.jumpable_frames == 0) { // intentionally left blank. } else if(badge_event_current_input_state() & BADGE_EVENT_KEY_BTN_A) { - state->player.inertia.y = fixed_point_sub(state->player.inertia.y, accel_vert); - --state->player.jumpable_frames; + state->player.base.inertia.y = fixed_point_sub(state->player.base.inertia.y, accel_vert()); + --state->player.base.jumpable_frames; } else { - state->player.jumpable_frames = 0; + state->player.base.jumpable_frames = 0; } - jumpnrun_passive_movement(&state->player.inertia); + jumpnrun_passive_movement(&state->player.base.inertia); - vec2d new_pos = vec2d_add(state->player.current_box.pos, state->player.inertia); + vec2d new_pos = vec2d_add(state->player.base.hitbox.pos, state->player.base.inertia); - if(fixed_point_lt(new_pos.x, FIXED_INT(state->left))) { - new_pos.x = FIXED_INT(state->left); - state->player.inertia.x = FIXED_INT(0); + if(fixed_point_lt(new_pos.x, FIXED_INT(state->screen_left))) { + new_pos.x = FIXED_INT(state->screen_left); + state->player.base.inertia.x = FIXED_INT(0); } - *inertia_mod = state->player.inertia; - bool killed = collisions_tiles_displace(&new_pos, &state->player, lv, tilerange, inertia_mod); - state->player.inertia = *inertia_mod; + *inertia_mod = state->player.base.inertia; + bool killed = collisions_tiles_displace(&new_pos, &state->player.base, lv, tilerange, inertia_mod); + state->player.base.inertia = *inertia_mod; - if(killed || fixed_point_gt(state->player.current_box.pos.y, FIXED_INT(BADGE_DISPLAY_HEIGHT))) { - state->status = JUMPNRUN_DEAD; + if(fixed_point_gt(rectangle_top(&state->player.base.hitbox), FIXED_INT(BADGE_DISPLAY_HEIGHT))) { + jumpnrun_player_despawn(&state->player); + } else if(killed) { + jumpnrun_player_kill (&state->player); } } @@ -208,168 +151,106 @@ void jumpnrun_level_tick(jumpnrun_level *lv, jumpnrun_game_state *state) { jumpnrun_tile_range tilerange = jumpnrun_visible_tiles(lv, state); - vec2d inertia_mod = state->player.inertia; + vec2d inertia_mod = state->player.base.inertia; + + if(jumpnrun_player_alive(&state->player)) { + jumpnrun_apply_movement(lv, &tilerange, state, &inertia_mod); + } - jumpnrun_apply_movement(lv, &tilerange, state, &inertia_mod); - state->left = jumpnrun_level_assert_left_side(state); + state->screen_left = jumpnrun_level_assert_left_side(state); - if(state->player.tick_minor == 0) { - badge_framebuffer fb; - badge_framebuffer_clear(&fb); + if(state->tick == 0) { + badge_framebuffer fb = { { { 0 } } }; - for(size_t tile = tilerange.first; tile < tilerange.last; ++tile) { - badge_framebuffer_blt(&fb, - fixed_point_cast_int(tile_left(&lv->tiles[tile])) - state->left, - fixed_point_cast_int(tile_top (&lv->tiles[tile])), - &tile_type(&lv->tiles[tile])->sprite, - 0); + for(uint16_t tile = tilerange.first; tile < tilerange.last; ++tile) { + jumpnrun_render_tile(&fb, state, &lv->tiles[tile]); } - for(size_t item = 0; item < lv->header.item_count; ++item) { + for(uint16_t item = 0; item < lv->header.item_count; ++item) { jumpnrun_item *item_obj = &lv->items[item]; if(item_obj->flags & JUMPNRUN_ITEM_COLLECTED) { continue; } - int screenpos = fixed_point_cast_int(item_obj->pos.x) - state->left; + int screenpos = fixed_point_cast_int(item_obj->pos.x) - state->screen_left; if(screenpos > -item_obj->type->sprite.width && screenpos < BADGE_DISPLAY_WIDTH) { rectangle item_rect = rect_from_item(item_obj); - if(rectangle_intersect(&state->player.current_box, &item_rect)) { + if(rectangle_intersect(&state->player.base.hitbox, &item_rect)) { item_obj->type->on_collect(item_obj, state, lv); } - badge_framebuffer_blt(&fb, - screenpos, - fixed_point_cast_int(item_obj->pos.y), - &item_obj->type->sprite, - 0); + jumpnrun_render_item(&fb, state, item_obj); } } - for(size_t shot_ix = 0; shot_ix < JUMPNRUN_MAX_SHOTS; ++shot_ix) { + for(uint16_t shot_ix = 0; shot_ix < JUMPNRUN_MAX_SHOTS; ++shot_ix) { jumpnrun_shot *shot = &state->shots[shot_ix]; - + jumpnrun_shot_process(shot); if(jumpnrun_shot_spawned(shot)) { - rectangle_move_rel(&shot->current_box, shot->inertia); - shot->inertia = vec2d_add(shot->inertia, gravity); - - if(fixed_point_gt(rectangle_top(&shot->current_box), FIXED_INT(BADGE_DISPLAY_HEIGHT))) { - jumpnrun_shot_despawn(shot); - } - - /* show every position twice, because LCD switching time. This makes the shots more - * visible on the nokia lcds. - */ - badge_framebuffer_blt(&fb, - fixed_point_cast_int(shot->old_box.pos.x) - state->left, - fixed_point_cast_int(shot->old_box.pos.y), - &anim_sickle[shot->tick / JUMPNRUN_SHOT_TICKS_PER_FRAME], - fixed_point_lt(shot->inertia.x, FIXED_INT(0)) ? BADGE_BLT_MIRRORED : 0); - badge_framebuffer_blt(&fb, - fixed_point_cast_int(shot->current_box.pos.x) - state->left, - fixed_point_cast_int(shot->current_box.pos.y), - &anim_sickle[shot->tick / JUMPNRUN_SHOT_TICKS_PER_FRAME], - fixed_point_lt(shot->inertia.x, FIXED_INT(0)) ? BADGE_BLT_MIRRORED : 0); - - shot->old_box = shot->current_box; - - ++shot->tick; - if(shot->tick == ARRAY_SIZE(anim_sickle) * JUMPNRUN_SHOT_TICKS_PER_FRAME) { - shot->tick = 0; - } + jumpnrun_render_shot(&fb, state, shot); } } - for(size_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) { + for(uint16_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) { jumpnrun_enemy *enemy = &lv->enemies[enemy_ix]; jumpnrun_process_enemy(enemy, &fb, state, lv, &tilerange, &inertia_mod); } - badge_framebuffer_blt(&fb, - fixed_point_cast_int(state->player.current_box.pos.x) - state->left, - fixed_point_cast_int(state->player.current_box.pos.y), - &anim_hacker[state->player.anim_frame], - state->player.anim_direction); + if(jumpnrun_player_alive(&state->player)) { + jumpnrun_render_player(&fb, state); + jumpnrun_player_advance_animation(&state->player); + } else if(jumpnrun_moveable_finished_dying(&state->player.base)) { + jumpnrun_player_despawn(&state->player); + } else if(jumpnrun_moveable_dying(&state->player.base)) { + jumpnrun_render_splosion(&fb, state, &state->player.base); + state->player.base.tick_minor += JUMPNRUN_STATE_TICKS_PER_FRAME; + } badge_framebuffer_flush(&fb); - if(!state->player.touching_ground) { - state->player.anim_frame = 2; - } else if(fixed_point_gt(fixed_point_abs(state->player.inertia.x), FIXED_POINT(0, 200))) { - state->player.anim_frame = (state->player.anim_frame + 1) % ARRAY_SIZE(anim_hacker); + if(!jumpnrun_moveable_touching_ground(&state->player.base)) { + state->player.base.anim_frame = 2; + } else if(fixed_point_gt(fixed_point_abs(state->player.base.inertia.x), FIXED_POINT(0, 200))) { + state->player.base.anim_frame = (state->player.base.anim_frame + 1) % JUMPNRUN_PLAYER_FRAMES; } else { - state->player.anim_frame = 0; + state->player.base.anim_frame = 0; } } else { - for(size_t shot_ix = 0; shot_ix < JUMPNRUN_MAX_SHOTS; ++shot_ix) { - jumpnrun_shot *shot = &state->shots[shot_ix]; - - if(jumpnrun_shot_spawned(shot)) { - rectangle_move_rel(&shot->current_box, shot->inertia); - shot->inertia = vec2d_add(shot->inertia, gravity); - - if(fixed_point_gt(rectangle_top(&shot->current_box), FIXED_INT(BADGE_DISPLAY_HEIGHT))) { - jumpnrun_shot_despawn(shot); - } - - ++shot->tick; - if(shot->tick == ARRAY_SIZE(anim_sickle) * JUMPNRUN_SHOT_TICKS_PER_FRAME) { - shot->tick = 0; - } - } + for(uint16_t shot_ix = 0; shot_ix < JUMPNRUN_MAX_SHOTS; ++shot_ix) { + jumpnrun_shot_process(&state->shots[shot_ix]); } - for(size_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) { + for(uint16_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) { jumpnrun_enemy *enemy = &lv->enemies[enemy_ix]; jumpnrun_process_enemy(enemy, NULL, state, lv, &tilerange, &inertia_mod); } } - state->player.inertia = inertia_mod; - ++state->player.tick_minor; - if(state->player.tick_minor == 3) { - state->player.tick_minor = 0; + state->player.base.inertia = inertia_mod; + ++state->tick; + if(state->tick == JUMPNRUN_STATE_TICKS_PER_FRAME) { + state->tick = 0; } } -uint8_t jumpnrun_play(char const *lvname) { +uint8_t jumpnrun_play_level(char const *lvname) { jumpnrun_level lv; JUMPNRUN_LEVEL_LOAD(lv, lvname); jumpnrun_game_state gs; - memset(&gs, 0, sizeof(gs)); - - for(gs.lives = 99; gs.status != JUMPNRUN_WON && gs.lives != 0; --gs.lives) { - jumpnrun_show_lives_screen(&gs); - for(uint8_t i = 0; i < 75; ) { - badge_event_t ev = badge_event_wait(); - if(badge_event_type(ev) == BADGE_EVENT_GAME_TICK) { - ++i; - } else if(i > 25) { - uint8_t old_state = badge_event_old_input_state(ev); - uint8_t new_state = badge_event_new_input_state(ev); - uint8_t new_buttons = new_state & (old_state ^ new_state); + jumpnrun_game_state_init(&gs, &lv); - if(new_buttons != 0) break; - } - } - - gs.status = JUMPNRUN_PLAYING; - gs.left = 0; - memset(&gs.player, 0, sizeof(gs.player)); - gs.player.current_box = rectangle_new(lv.start_pos, - hacker_extents()); - - for(size_t i = 0; i < lv.header.enemy_count; ++i) { - lv.enemies[i].flags = 0; - } + do { + jumpnrun_show_lives_screen(&gs); + jumpnrun_game_state_respawn(&gs, &lv); - while(gs.status == JUMPNRUN_PLAYING) { + while((gs.player.base.flags & JUMPNRUN_PLAYER_DEAD) == 0 && + (gs. flags & JUMPNRUN_STATE_WON ) == 0) { badge_event_t ev = badge_event_wait(); switch(badge_event_type(ev)) { @@ -379,8 +260,8 @@ uint8_t jumpnrun_play(char const *lvname) { uint8_t new_state = badge_event_new_input_state(ev); uint8_t new_buttons = new_state & (old_state ^ new_state); - if((new_buttons & BADGE_EVENT_KEY_BTN_A) && gs.player.touching_ground) { - gs.player.jumpable_frames = 12; + if((new_buttons & BADGE_EVENT_KEY_BTN_A) && jumpnrun_moveable_touching_ground(&gs.player.base)) { + gs.player.base.jumpable_frames = 12; } if((new_buttons & BADGE_EVENT_KEY_BTN_B)) { @@ -402,7 +283,17 @@ uint8_t jumpnrun_play(char const *lvname) { } } } + } while((gs.flags & JUMPNRUN_STATE_WON) == 0 && gs.player.lives-- != 0); + + if(gs.flags & JUMPNRUN_STATE_WON) { + jumpnrun_show_you_rock(); + return JUMPNRUN_WON; + } + + if(++gs.player.lives == 0) { + jumpnrun_show_game_over(); + return JUMPNRUN_LOST; } - return gs.status; + return JUMPNRUN_ERROR; }