#include "jumpnrun.h"
#include "collision.h"
#include "levels.h"
+#include "stats.h"
#include "../ui/display.h"
#include "../ui/event.h"
static fixed_point const drag_factor = FIXED_POINT_I(0, 854);
static fixed_point const speed_jump_x = FIXED_POINT_I(0, 600);
-static vec2d const hacker_extent = { FIXED_INT_I(5), FIXED_INT_I(8) };
+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" }
-*/
+ /*
+ { 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" },
*/
};
+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 inline int imax(int x, int y) {
return x < y ? y : x;
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_extent.x); }
-static inline fixed_point hacker_bottom(vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_top (pos, state), hacker_extent.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); }
int jumpnrun_level_assert_left_side(jumpnrun_game_state const *state) {
static int const lmargin = 20;
static void jumpnrun_apply_movement(jumpnrun_level const *lv,
jumpnrun_tile_range const *tilerange,
jumpnrun_game_state *state,
- vec2d *inertia_mod) {
+ vec2d *inertia_mod) {
switch(badge_event_current_input_state() &
(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->anim_direction = BADGE_BLT_MIRRORED;
+ state->player.anim_direction = BADGE_BLT_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->anim_direction = 0;
+ state->player.anim_direction = 0;
break;
default:
if(state->player.touching_ground) {
}
*inertia_mod = state->player.inertia;
- collisions_tiles_displace(&new_pos, &state->player, lv, tilerange, inertia_mod);
+ bool killed = collisions_tiles_displace(&new_pos, &state->player, lv, tilerange, inertia_mod);
state->player.inertia = *inertia_mod;
- if(fixed_point_gt(state->player.current_box.pos.y, FIXED_INT(BADGE_DISPLAY_HEIGHT))) {
+ if(killed || fixed_point_gt(state->player.current_box.pos.y, FIXED_INT(BADGE_DISPLAY_HEIGHT))) {
state->status = JUMPNRUN_DEAD;
}
}
}
for(size_t item = 0; item < lv->header.item_count; ++item) {
- int screenpos = fixed_point_cast_int(lv->items[item].pos.x) - state->left;
- if(screenpos > -lv->items[item].type->sprite.width &&
+ 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;
+ if(screenpos > -item_obj->type->sprite.width &&
screenpos < BADGE_DISPLAY_WIDTH) {
- rectangle item_rect = rect_from_item(&lv->items[item]);
+ rectangle item_rect = rect_from_item(item_obj);
if(rectangle_intersect(&state->player.current_box, &item_rect)) {
- lv->items[item].type->on_collect(state);
+ item_obj->type->on_collect(item_obj, state, lv);
}
badge_framebuffer_blt(&fb,
screenpos,
- fixed_point_cast_int(lv->items[item].pos.y),
- &lv->items[item].type->sprite,
+ fixed_point_cast_int(item_obj->pos.y),
+ &item_obj->type->sprite,
0);
}
}
+ 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);
+ }
+
+ /* 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;
+ }
+ }
+ }
+
for(size_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);
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->anim_direction);
+ state->player.anim_direction);
badge_framebuffer_flush(&fb);
state->player.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(size_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);
jumpnrun_game_state gs;
memset(&gs, 0, sizeof(gs));
- gs.player.current_box = rectangle_new(lv.start_pos, hacker_extent);
+ for(gs.lives = 99; gs.status != JUMPNRUN_WON && gs.lives != 0; --gs.lives) {
+ jumpnrun_show_lives_screen(&gs);
- while(gs.status == JUMPNRUN_PLAYING) {
- badge_event_t ev = badge_event_wait();
+ 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);
- switch(badge_event_type(ev)) {
- case BADGE_EVENT_USER_INPUT:
+ if(new_buttons != 0) break;
+ }
+ }
+
+ gs.status = JUMPNRUN_PLAYING;
+ gs.left = 0;
+ memset(&gs.player, 0, sizeof(gs.player));
+ memset(&gs.shots , 0, sizeof(gs.shots ));
+ 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;
+ }
+
+ while(gs.status == JUMPNRUN_PLAYING) {
+ badge_event_t ev = badge_event_wait();
+
+ switch(badge_event_type(ev)) {
+ case BADGE_EVENT_USER_INPUT:
{
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);
if((new_buttons & BADGE_EVENT_KEY_BTN_A) && gs.player.touching_ground) {
- gs.player.jumpable_frames = 12;
+ gs.player.jumpable_frames = 12;
+ }
+
+ if((new_buttons & BADGE_EVENT_KEY_BTN_B)) {
+ uint8_t i;
+ for(i = 0; i < JUMPNRUN_MAX_SHOTS && jumpnrun_shot_spawned(&gs.shots[i]); ++i)
+ ;
+
+ if(i < JUMPNRUN_MAX_SHOTS) {
+ jumpnrun_shot_spawn(gs.shots + i, &gs);
+ }
}
break;
}
- case BADGE_EVENT_GAME_TICK:
+ case BADGE_EVENT_GAME_TICK:
{
jumpnrun_level_tick(&lv, &gs);
break;
}
+ }
}
}