Kräftig aufgeräumt. Kollisions- und Sprungtötungsphysik jetzt allein auf
authorWintermate <wintermute@hannover.ccc.de>
Sun, 20 Oct 2013 02:07:20 +0000 (04:07 +0200)
committerWintermate <wintermute@hannover.ccc.de>
Sun, 20 Oct 2013 02:07:20 +0000 (04:07 +0200)
Hitboxbasis. TODO: Wenn man von unten angesprungen wird, wie verhalten?

21 files changed:
badge/jumpnrun/collision.c
badge/jumpnrun/enemies.c
badge/jumpnrun/enemies.h
badge/jumpnrun/game_state.h [new file with mode: 0644]
badge/jumpnrun/gnobbel.lv
badge/jumpnrun/items.c
badge/jumpnrun/jumpnrun.c
badge/jumpnrun/jumpnrun.h
badge/jumpnrun/level_load.c
badge/jumpnrun/levels.txt
badge/jumpnrun/moveable.c [new file with mode: 0644]
badge/jumpnrun/moveable.h
badge/jumpnrun/player.h [new file with mode: 0644]
badge/jumpnrun/render.c [new file with mode: 0644]
badge/jumpnrun/shots.c [new file with mode: 0644]
badge/jumpnrun/shots.h [new file with mode: 0644]
badge/jumpnrun/stats.c
badge/jumpnrun/stats.h
badge/jumpnrun/tiles.h
badge/util/rectangle.h
mock/Makefile

index 0f254e0..20617bb 100644 (file)
@@ -4,7 +4,7 @@ uint8_t collision_displace(vec2d             *desired_pos,
                            jumpnrun_moveable *current,
                            rectangle   const *obstacle,
                            vec2d             *inertia_mod) {
-  rectangle desired = current->current_box;
+  rectangle desired = current->hitbox;
   rectangle_move_to(&desired, *desired_pos);
 
   if(!rectangle_intersect(obstacle, &desired)) {
@@ -21,7 +21,7 @@ uint8_t collision_displace(vec2d             *desired_pos,
 
   if(fixed_point_le(rectangle_top   ( obstacle), rectangle_top(&desired)) &&
      fixed_point_gt(rectangle_bottom( obstacle), rectangle_top(&desired)) &&
-     fixed_point_lt(rectangle_top   (&desired ), rectangle_top(&current->current_box))) {
+     fixed_point_lt(rectangle_top   (&desired ), rectangle_top(&current->hitbox))) {
 
     coll_y = JUMPNRUN_COLLISION_BOTTOM;
     y      = fixed_point_sub(rectangle_bottom(obstacle), rectangle_top(&desired));
@@ -29,7 +29,7 @@ uint8_t collision_displace(vec2d             *desired_pos,
 
   } else if(fixed_point_gt(rectangle_bottom( obstacle), rectangle_bottom(&desired)) &&
             fixed_point_le(rectangle_top   ( obstacle), rectangle_bottom(&desired)) &&
-            fixed_point_gt(rectangle_top   (&desired ), rectangle_top   (&current->current_box))) {
+            fixed_point_gt(rectangle_top   (&desired ), rectangle_top   (&current->hitbox))) {
 
     coll_y = JUMPNRUN_COLLISION_TOP;
     y      = fixed_point_sub(rectangle_bottom(&desired ), rectangle_top   ( obstacle));
@@ -40,7 +40,7 @@ uint8_t collision_displace(vec2d             *desired_pos,
 
   if(fixed_point_le(rectangle_left ( obstacle), rectangle_left(&desired)) &&
      fixed_point_gt(rectangle_right( obstacle), rectangle_left(&desired)) &&
-     fixed_point_lt(rectangle_left (&desired ), rectangle_left(&current->current_box))) {
+     fixed_point_lt(rectangle_left (&desired ), rectangle_left(&current->hitbox))) {
 
     coll_x = JUMPNRUN_COLLISION_RIGHT;
     x      = fixed_point_sub(rectangle_right(obstacle), rectangle_left(&desired));
@@ -48,7 +48,7 @@ uint8_t collision_displace(vec2d             *desired_pos,
 
   } else if(fixed_point_gt(rectangle_right( obstacle), rectangle_right(&desired)) &&
             fixed_point_le(rectangle_left ( obstacle), rectangle_right(&desired)) &&
-            fixed_point_gt(rectangle_left (&desired ), rectangle_left (&current->current_box))) {
+            fixed_point_gt(rectangle_left (&desired ), rectangle_left (&current->hitbox))) {
 
     coll_x = JUMPNRUN_COLLISION_LEFT;
     x      = fixed_point_sub(rectangle_right(&desired ), rectangle_left ( obstacle));
@@ -63,7 +63,13 @@ uint8_t collision_displace(vec2d             *desired_pos,
   } else if(fixed_point_gt(x, y)) {
     desired_pos->y = dy;
     inertia_mod->y = FIXED_INT(0);
-    current->touching_ground = bottom_collision;
+
+    if(bottom_collision) {
+      current->flags |=  JUMPNRUN_MOVEABLE_TOUCHING_GROUND;
+    } else {
+      current->flags &= ~JUMPNRUN_MOVEABLE_TOUCHING_GROUND;
+    }
+
     coll = coll_y;
   } else {
     desired_pos->x = dx;
@@ -86,7 +92,7 @@ bool collisions_tiles_displace(vec2d                     *desired_position,
   };
   static int const collision_order[] = { 7, 1, 3, 5, 6, 8, 0, 2 };
 
-  vec2d midpoint = rectangle_mid(&thing->current_box);
+  vec2d midpoint = rectangle_mid(&thing->hitbox);
 
   jumpnrun_tile_position midtile_pos = {
     fixed_point_cast_int(fixed_point_div(midpoint.x, FIXED_INT(JUMPNRUN_TILE_PIXEL_WIDTH ))),
@@ -134,7 +140,7 @@ bool collisions_tiles_displace(vec2d                     *desired_position,
 
   /* collision: sort by priority (top/bottom, left/right, then diagonal) */
   bool lethal = false;
-  thing->touching_ground = false;
+  thing->flags &= ~JUMPNRUN_MOVEABLE_TOUCHING_GROUND;
 
   for(size_t collision_index = 0; collision_index < ARRAY_SIZE(collision_order); ++collision_index) {
     if(collision_tile[collision_order[collision_index]] == -1) {
@@ -150,7 +156,7 @@ bool collisions_tiles_displace(vec2d                     *desired_position,
     }
   }
 
-  rectangle_move_to(&thing->current_box, *desired_position);
+  rectangle_move_to(&thing->hitbox, *desired_position);
 
   return lethal;
 }
index b518b51..61859a8 100644 (file)
@@ -1,8 +1,11 @@
 #include "enemies.h"
 
 #include "collision.h"
+#include "game_state.h"
 #include "tiles.h"
 #include "jumpnrun.h"
+#include "moveable.h"
+#include "render.h"
 
 static badge_sprite const anim_cat[] = {
   { 8, 5, (uint8_t const *) "\xc7\x3f\xce\x38\x11" },
@@ -88,46 +91,79 @@ static void enemy_animation_advance(jumpnrun_enemy *enemy) {
   }
 }
 
+static void enemy_spawn(jumpnrun_enemy *self) {
+  // enemy unspawned, available and in spawn zone.
+  self->base.flags           = JUMPNRUN_ENEMY_SPAWNED | JUMPNRUN_ENEMY_UNAVAILABLE;
+  self->base.anim_frame      = 0;
+  self->base.tick_minor      = 0;
+  self->base.jumpable_frames = 0;
+}
+
+void jumpnrun_enemy_despawn(jumpnrun_enemy *self) {
+  // Despawned enemies are reset to their spawn position, so enemy_in_spawn_area will determine whether the spawn point is in the spawn area.
+  self->base.flags  &= ~JUMPNRUN_ENEMY_SPAWNED;
+  self->base.hitbox  = rectangle_new(self->spawn_pos, self->type->hitbox.extent);
+  self->base.inertia = self->type->spawn_inertia;
+}
+
+void jumpnrun_enemy_reset  (jumpnrun_enemy *self) {
+  jumpnrun_enemy_despawn(self);
+  self->base.flags &= ~JUMPNRUN_ENEMY_UNAVAILABLE;
+}
+
+static void enemy_bounce(jumpnrun_enemy *self) {
+  self->base.inertia.x = fixed_point_neg(self->base.inertia.x);
+}
+
+static inline vec2d enemy_pos_hitbox (jumpnrun_enemy const *self) { return self->base.hitbox.pos; }
+static inline vec2d enemy_pos_display(jumpnrun_enemy const *self) { return vec2d_sub(enemy_pos_hitbox(self), self->type->hitbox.pos); }
+static inline vec2d enemy_pos_desired(jumpnrun_enemy const *self) { return vec2d_add(enemy_pos_hitbox(self), self->base.inertia); }
+
+static inline void enemy_move_to(jumpnrun_enemy *self, vec2d new_pos) {
+  rectangle_move_to(&self->base.hitbox, new_pos);
+}
+
+static inline bool enemy_in_area(jumpnrun_enemy const *self, jumpnrun_game_state *state, int margin) {
+  return
+    fixed_point_gt(rectangle_left (enemy_hitbox(self)), FIXED_INT(jumpnrun_screen_left (state) - margin)) &&
+    fixed_point_lt(rectangle_right(enemy_hitbox(self)), FIXED_INT(jumpnrun_screen_right(state) + margin));
+}
+
+static inline bool enemy_on_screen(jumpnrun_enemy const *self, jumpnrun_game_state *state) {
+  return enemy_in_area(self, state, 0);
+}
+
+static inline bool enemy_in_spawn_area(jumpnrun_enemy const *self, jumpnrun_game_state *state) {
+  return enemy_in_area(self, state, JUMPNRUN_MAX_SPAWN_MARGIN);
+}
+
 void jumpnrun_process_enemy(jumpnrun_enemy                   *self,
                             badge_framebuffer                *fb,
                             struct jumpnrun_game_state       *state,
                             struct jumpnrun_level            *lv,
                             struct jumpnrun_tile_range const *visible_tiles,
                             vec2d                            *player_inertia_mod) {
-  int const spawn_margin = 1 + self->type->animation_frames[self->base.anim_frame].width;
-
-  if(self->flags & JUMPNRUN_ENEMY_SPAWNED) {
-    if(fixed_point_lt(rectangle_left(enemy_box(self)), FIXED_POINT(state->left                       - spawn_margin, 0)) ||
-       fixed_point_gt(rectangle_left(enemy_box(self)), FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH + spawn_margin, 0)) ||
-       fixed_point_gt(rectangle_top (enemy_box(self)), FIXED_POINT(BADGE_DISPLAY_HEIGHT                            , 0))) {
-      self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
+  // Despawned enemies are reset to their spawn position, so enemy_in_spawn_area will determine whether the spawn point is in the spawn area.
+  if(self->base.flags & JUMPNRUN_ENEMY_SPAWNED) {
+    if(!enemy_in_spawn_area(self, state) || fixed_point_gt(rectangle_top (enemy_hitbox(self)), FIXED_INT(BADGE_DISPLAY_HEIGHT))) {
+      jumpnrun_enemy_despawn(self);
     } else {
       self->type->move_tick(self, state, lv, visible_tiles, player_inertia_mod);
       self->type->collision_shots(self, state);
+      if     (fixed_point_lt(self->base.inertia.x, FIXED_INT(0))) { self->base.flags &= ~JUMPNRUN_MOVEABLE_MIRRORED; }
+      else if(fixed_point_ne(self->base.inertia.x, FIXED_INT(0))) { self->base.flags |=  JUMPNRUN_MOVEABLE_MIRRORED; }
 
       if(fb) {
-        badge_framebuffer_blt(fb,
-                              fixed_point_cast_int(rectangle_left(enemy_box(self))) - state->left,
-                              fixed_point_cast_int(rectangle_top (enemy_box(self))),
-                              enemy_sprite(self),
-                              enemy_render_flags(self));
+        jumpnrun_render_enemy(fb, state, self);
       }
     }
-  } else if(self->flags & JUMPNRUN_ENEMY_UNAVAILABLE) {
-    if(state->left                                      > fixed_point_cast_int(self->spawn_pos.x) + spawn_margin ||
-       state->left + BADGE_DISPLAY_WIDTH + spawn_margin < fixed_point_cast_int(self->spawn_pos.x)) {
-      self->flags &= ~JUMPNRUN_ENEMY_UNAVAILABLE;
+  } else if(self->base.flags & JUMPNRUN_ENEMY_UNAVAILABLE) {
+    if(!enemy_in_spawn_area(self, state)) {
+      self->base.flags &= ~JUMPNRUN_ENEMY_UNAVAILABLE;
     }
-  } else if(fixed_point_gt(self->spawn_pos.x, FIXED_POINT(state->left                       - spawn_margin, 0)) &&
-            fixed_point_lt(self->spawn_pos.x, FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH + spawn_margin, 0))) {
+  } else if(enemy_in_spawn_area(self, state)) {
     // enemy unspawned, available and in spawn zone.
-    self->flags                = JUMPNRUN_ENEMY_SPAWNED | JUMPNRUN_ENEMY_UNAVAILABLE;
-    self->base.current_box     = rectangle_new(self->spawn_pos, self->type->extent);
-    self->base.inertia         = self->type->spawn_inertia;
-    self->base.anim_frame      = 0;
-    self->base.tick_minor      = 0;
-    self->base.touching_ground = false;
-    self->base.jumpable_frames = 0;
+    enemy_spawn(self);
   }
 }
 
@@ -144,11 +180,9 @@ void enemy_collision_tiles_bounce_horiz(jumpnrun_enemy            *self,
                                           &inertia_mod);
 
   if(killed) {
-    self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
-  }
-
-  if(fixed_point_ne(inertia_mod.x, self->base.inertia.x)) {
-    self->base.inertia.x = fixed_point_neg(self->base.inertia.x);
+    jumpnrun_enemy_despawn(self);
+  } else if(fixed_point_ne(inertia_mod.x, self->base.inertia.x)) {
+    enemy_bounce(self);
   }
 }
 
@@ -157,10 +191,8 @@ void enemy_collision_player_deadly(struct jumpnrun_enemy      *self,
                                    vec2d                      *player_inertia_mod) {
   (void) player_inertia_mod;
 
-  rectangle rect_self = enemy_hitbox(self);
-
-  if(rectangle_intersect(&rect_self, &state->player.current_box)) {
-    state->status = JUMPNRUN_DEAD;
+  if(rectangle_intersect(enemy_hitbox(self), &state->player.base.hitbox)) {
+    state->player.base.flags |= JUMPNRUN_PLAYER_DEAD;
   }
 }
 
@@ -168,17 +200,15 @@ void enemy_collision_player_jumpable(jumpnrun_enemy      *self,
                                      jumpnrun_game_state *state,
                                      vec2d               *player_inertia_mod)
 {
-  rectangle rect_self = enemy_hitbox(self);
-
-  if(rectangle_intersect(&rect_self, &state->player.current_box)) {
-    if(fixed_point_lt(rectangle_top(&state->player.current_box), rectangle_top(&rect_self)) &&
-       fixed_point_gt(state->player.inertia.y, FIXED_POINT(0, 0)))
+  if(rectangle_intersect(enemy_hitbox(self), &state->player.base.hitbox)) {
+    if(fixed_point_lt(rectangle_top(&state->player.base.hitbox), rectangle_top(enemy_hitbox(self))) &&
+       fixed_point_gt(state->player.base.inertia.y, FIXED_INT(0)))
     {
-      self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
+      jumpnrun_enemy_despawn(self);
       player_inertia_mod->y = FIXED_POINT(0, -250);
-      state->player.jumpable_frames = 12;
+      state->player.base.jumpable_frames = 12;
     } else {
-      state->status = JUMPNRUN_DEAD;
+      state->player.base.flags |= JUMPNRUN_PLAYER_DEAD;
     }
   }
 }
@@ -196,14 +226,12 @@ void enemy_collision_tiles_pass_through(struct jumpnrun_enemy             *self,
 
 void enemy_collision_shots_die(struct jumpnrun_enemy      *self,
                                struct jumpnrun_game_state *state) {
-  rectangle rect_self = enemy_hitbox(self);
-
   for(uint8_t i = 0; i < JUMPNRUN_MAX_SHOTS; ++i) {
     jumpnrun_shot *shot = &state->shots[i];
 
     if(jumpnrun_shot_spawned(shot)) {
-      if(rectangle_intersect(&rect_self, &shot->current_box)) {
-        self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
+      if(rectangle_intersect(enemy_hitbox(self), &shot->current_box)) {
+        self->base.flags &= ~JUMPNRUN_ENEMY_SPAWNED;
         jumpnrun_shot_despawn(shot);
       }
     }
@@ -212,17 +240,15 @@ void enemy_collision_shots_die(struct jumpnrun_enemy      *self,
 
 void enemy_collision_shots_bounce(struct jumpnrun_enemy      *self,
                                   struct jumpnrun_game_state *state) {
-  rectangle rect_self = enemy_hitbox(self);
-
   for(uint8_t i = 0; i < JUMPNRUN_MAX_SHOTS; ++i) {
     jumpnrun_shot *shot = &state->shots[i];
 
     if(jumpnrun_shot_spawned(shot)) {
-      if(rectangle_intersect(&rect_self, &shot->current_box)) {
+      if(rectangle_intersect(enemy_hitbox(self), &shot->current_box)) {
         if(fixed_point_gt(shot->inertia.x, FIXED_INT(0))) {
-          rectangle_move_to_x(&shot->current_box, fixed_point_sub(rectangle_left(&rect_self), rectangle_width(&shot->current_box)));
+          rectangle_move_to_x(&shot->current_box, fixed_point_sub(rectangle_left(enemy_hitbox(self)), rectangle_width(&shot->current_box)));
         } else {
-          rectangle_move_to_x(&shot->current_box, rectangle_right(&rect_self));
+          rectangle_move_to_x(&shot->current_box, rectangle_right(enemy_hitbox(self)));
         }
 
         shot->inertia.x = fixed_point_neg(shot->inertia.x);
@@ -237,25 +263,24 @@ void enemy_collision_shots_dontcare(struct jumpnrun_enemy      *self,
   (void) state;
 }
 
+static void enemy_tick_common(jumpnrun_enemy            *self,
+                               jumpnrun_game_state       *state,
+                               jumpnrun_level            *lv,
+                               jumpnrun_tile_range const *visible_tiles,
+                                vec2d                     *player_inertia_mod) {
+  vec2d new_pos = enemy_pos_desired(self);
+  self->type->collision_tiles (self, &new_pos, lv, visible_tiles);
+  self->type->collision_player(self, state, player_inertia_mod);
+  enemy_move_to(self, new_pos);
+}
+
 void enemy_tick_straight_ahead(jumpnrun_enemy            *self,
                                jumpnrun_game_state       *state,
                                jumpnrun_level            *lv,
                                jumpnrun_tile_range const *visible_tiles,
                                vec2d                     *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
   jumpnrun_passive_movement(&self->base.inertia);
-
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
-
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
   enemy_animation_advance(self);
 }
 
@@ -264,23 +289,12 @@ void enemy_tick_straight_follow(jumpnrun_enemy            *self,
                                 jumpnrun_level            *lv,
                                 jumpnrun_tile_range const *visible_tiles,
                                 vec2d                     *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
   jumpnrun_passive_movement(&self->base.inertia);
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
 
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
-
-  if(fixed_point_lt(rectangle_right(&state->player.current_box), rectangle_left(enemy_box(self)))) {
+  if(fixed_point_lt(rectangle_right(&state->player.base.hitbox), rectangle_left(enemy_hitbox(self)))) {
     self->base.inertia.x = self->type->spawn_inertia.x;
-  } else if(fixed_point_gt(rectangle_left(&state->player.current_box), rectangle_right(enemy_box(self)))) {
+  } else if(fixed_point_gt(rectangle_left(&state->player.base.hitbox), rectangle_right(enemy_hitbox(self)))) {
     self->base.inertia.x = fixed_point_neg(self->type->spawn_inertia.x);
   }
 
@@ -292,24 +306,14 @@ void enemy_tick_swing_up_and_down(struct jumpnrun_enemy            *self,
                                   struct jumpnrun_level            *lv,
                                   struct jumpnrun_tile_range const *visible_tiles,
                                   vec2d                            *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
 
   self->base.inertia.y =
     fixed_point_add(fixed_point_add(self->base.inertia.y,
                                     fixed_point_div(self->type->spawn_inertia.y, FIXED_INT(3))),
                     fixed_point_mul(FIXED_POINT(0, 5),
                                     fixed_point_sub(self->spawn_pos.y,
-                                                    enemy_position(self).y)));
+                                                    enemy_pos_display(self).y)));
 
   enemy_animation_advance(self);
 }
@@ -319,14 +323,7 @@ void enemy_tick_stationary(struct jumpnrun_enemy            *self,
                            struct jumpnrun_level            *lv,
                            struct jumpnrun_tile_range const *visible_tiles,
                            vec2d                            *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
-  self->type->collision_tiles(self, &self->base.current_box.pos, lv, visible_tiles);
+  self->type->collision_tiles(self, &self->base.hitbox.pos, lv, visible_tiles);
   self->type->collision_player(self, state, player_inertia_mod);
 
   enemy_animation_advance(self);
@@ -337,21 +334,10 @@ void enemy_tick_jumper(jumpnrun_enemy            *self,
                        jumpnrun_level            *lv,
                        jumpnrun_tile_range const *visible_tiles,
                        vec2d                     *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
   jumpnrun_passive_movement(&self->base.inertia);
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
 
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
-
-  if(self->base.touching_ground) {
+  if(jumpnrun_moveable_touching_ground(&self->base)) {
     self->base.inertia.y = self->type->spawn_inertia.y;
   }
 
@@ -363,19 +349,8 @@ void enemy_tick_dog(jumpnrun_enemy            *self,
                     jumpnrun_level            *lv,
                     jumpnrun_tile_range const *visible_tiles,
                     vec2d                     *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
   jumpnrun_passive_movement(&self->base.inertia);
-
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
 
   if(self->base.tick_minor % self->type->animation_ticks_per_frame == 0) {
     switch(self->base.tick_minor / self->type->animation_ticks_per_frame) {
@@ -386,7 +361,7 @@ void enemy_tick_dog(jumpnrun_enemy            *self,
     case 4:
     case 6:
       self->base.anim_frame = 0;
-      if(self->flags & JUMPNRUN_ENEMY_FACING_RIGHT) {
+      if(self->base.flags & JUMPNRUN_MOVEABLE_MIRRORED) {
         self->base.inertia.x = fixed_point_neg(self->type->spawn_inertia.x);
       } else {
         self->base.inertia.x = self->type->spawn_inertia.x;
@@ -399,7 +374,7 @@ void enemy_tick_dog(jumpnrun_enemy            *self,
     case 5:
     case 7:
       self->base.anim_frame = 1;
-      if(self->flags & JUMPNRUN_ENEMY_FACING_RIGHT) {
+      if(self->base.flags & JUMPNRUN_MOVEABLE_MIRRORED) {
         self->base.inertia.x = fixed_point_neg(self->type->spawn_inertia.x);
       } else {
         self->base.inertia.x = self->type->spawn_inertia.x;
@@ -421,8 +396,6 @@ void enemy_tick_dog(jumpnrun_enemy            *self,
   }
 
   ++self->base.tick_minor;
-  if     (fixed_point_lt(self->base.inertia.x, FIXED_INT(0))) { self->flags &= ~JUMPNRUN_ENEMY_FACING_RIGHT; }
-  else if(fixed_point_ne(self->base.inertia.x, FIXED_INT(0))) { self->flags |=  JUMPNRUN_ENEMY_FACING_RIGHT; }
 }
 
 void enemy_tick_giraffe(jumpnrun_enemy            *self,
@@ -430,29 +403,18 @@ void enemy_tick_giraffe(jumpnrun_enemy            *self,
                         jumpnrun_level            *lv,
                         jumpnrun_tile_range const *visible_tiles,
                         vec2d                     *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
-  bool was_on_ground = self->base.touching_ground;
+  bool was_on_ground = jumpnrun_moveable_touching_ground(&self->base);
 
   jumpnrun_passive_movement(&self->base.inertia);
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
 
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
-
-  if(self->base.touching_ground) {
+  if(jumpnrun_moveable_touching_ground(&self->base)) {
     if(was_on_ground) {
       enemy_animation_advance(self);
       if(self->base.anim_frame == 0) {
         self->base.inertia = self->type->spawn_inertia;
 
-        if(fixed_point_gt(rectangle_mid_x(&state->player.current_box), rectangle_mid_x(enemy_box(self)))) {
+        if(fixed_point_gt(rectangle_mid_x(&state->player.base.hitbox), rectangle_mid_x(enemy_hitbox(self)))) {
           self->base.inertia.x = fixed_point_neg(self->base.inertia.x);
         }
       }
@@ -469,8 +431,6 @@ void enemy_tick_giraffe(jumpnrun_enemy            *self,
   }
 
   ++self->base.tick_minor;
-  if     (fixed_point_lt(self->base.inertia.x, FIXED_INT(0))) { self->flags &= ~JUMPNRUN_ENEMY_FACING_RIGHT; }
-  else if(fixed_point_ne(self->base.inertia.x, FIXED_INT(0))) { self->flags |=  JUMPNRUN_ENEMY_FACING_RIGHT; }
 }
 
 void enemy_tick_fly_straight(struct jumpnrun_enemy            *self,
@@ -478,18 +438,7 @@ void enemy_tick_fly_straight(struct jumpnrun_enemy            *self,
                              struct jumpnrun_level            *lv,
                              struct jumpnrun_tile_range const *visible_tiles,
                              vec2d                            *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
-
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
   enemy_animation_advance(self);
 }
 
@@ -498,35 +447,24 @@ void enemy_tick_fly_straight_and_dip(struct jumpnrun_enemy            *self,
                                      struct jumpnrun_level            *lv,
                                      struct jumpnrun_tile_range const *visible_tiles,
                                      vec2d                            *player_inertia_mod) {
-  int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
-
-  if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN <  state->left ||
-     screenpos                             >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
-    return;
-  }
-
-  if(fixed_point_lt(fixed_point_abs(fixed_point_sub(enemy_position(self).x,
-                                                    state->player.current_box.pos.x)),
+  if(fixed_point_lt(fixed_point_abs(fixed_point_sub(enemy_pos_hitbox(self).x,
+                                                    state->player.base.hitbox.pos.x)),
                     FIXED_INT(20))) {
-    self->flags |= JUMPNRUN_ENEMY_EVENT_TRIGGER1;
+    self->base.flags |= JUMPNRUN_ENEMY_EVENT_TRIGGER1;
   }
 
-  if(self->flags & JUMPNRUN_ENEMY_EVENT_TRIGGER1) {
+  if(self->base.flags & JUMPNRUN_ENEMY_EVENT_TRIGGER1) {
     self->base.inertia.y =
       fixed_point_add(fixed_point_add(self->base.inertia.y,
                                       fixed_point_div(self->type->spawn_inertia.y, FIXED_INT(3))),
                       fixed_point_mul(FIXED_POINT(0, 5),
                                       fixed_point_sub(self->spawn_pos.y,
-                                                      enemy_position(self).y)));
+                                                      enemy_pos_hitbox(self).y)));
   } else {
     self->base.inertia.y = FIXED_INT(0);
   }
 
-  vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);;
-  self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
-  self->type->collision_player(self, state, player_inertia_mod);
-  rectangle_move_to(&self->base.current_box, new_pos);
-
+  enemy_tick_common(self, state, lv, visible_tiles, player_inertia_mod);
   enemy_animation_advance(self);
 }
 
@@ -535,7 +473,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 18,
     .animation_length          = ARRAY_SIZE(anim_cat),
     .animation_frames          = anim_cat,
-    .extent                    = { FIXED_INT_I(8), FIXED_INT_I(5) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(2) },
                                    { FIXED_INT_I(6), FIXED_INT_I(3) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -200), FIXED_INT_I(0) },
@@ -547,7 +484,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 12,
     .animation_length          = ARRAY_SIZE(anim_mushroom),
     .animation_frames          = anim_mushroom,
-    .extent                    = { FIXED_INT_I(7), FIXED_INT_I(7) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(1) },
                                    { FIXED_INT_I(5), FIXED_INT_I(4) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -80), FIXED_INT_I(0) },
@@ -559,7 +495,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 9,
     .animation_length          = ARRAY_SIZE(anim_bunny),
     .animation_frames          = anim_bunny,
-    .extent                    = { FIXED_INT_I(7), FIXED_INT_I(5) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(2) },
                                    { FIXED_INT_I(5), FIXED_INT_I(3) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -80), FIXED_POINT_I(0, -800) },
@@ -571,7 +506,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 6,
     .animation_length          = ARRAY_SIZE(anim_snake),
     .animation_frames          = anim_snake,
-    .extent                    = { FIXED_INT_I(10), FIXED_INT_I(6) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(4) },
                                    { FIXED_INT_I(8), FIXED_INT_I(2) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -150), FIXED_INT_I(0) },
@@ -583,7 +517,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 6,
     .animation_length          = ARRAY_SIZE(anim_spiral),
     .animation_frames          = anim_spiral,
-    .extent                    = { FIXED_INT_I(10), FIXED_INT_I(10) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(1) },
                                    { FIXED_INT_I(8), FIXED_INT_I(8) } },
     .spawn_inertia             = { FIXED_INT_I(0), FIXED_POINT_I(0, -200) },
@@ -595,7 +528,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 5,
     .animation_length          = ARRAY_SIZE(anim_rotor),
     .animation_frames          = anim_rotor,
-    .extent                    = { FIXED_INT_I(9), FIXED_INT_I(9) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(1) },
                                    { FIXED_INT_I(7), FIXED_INT_I(7) } },
     .spawn_inertia             = { FIXED_INT_I(0), FIXED_POINT_I(0, 0) },
@@ -607,7 +539,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 18,
     .animation_length          = ARRAY_SIZE(anim_dog),
     .animation_frames          = anim_dog,
-    .extent                    = { FIXED_INT_I(8), FIXED_INT_I(5) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(1) },
                                    { FIXED_INT_I(6), FIXED_INT_I(4) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -200), FIXED_POINT_I(0, 0) },
@@ -619,7 +550,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 36,
     .animation_length          = ARRAY_SIZE(anim_giraffe),
     .animation_frames          = anim_giraffe,
-    .extent                    = { FIXED_INT_I(7), FIXED_INT_I(10) },
     .hitbox                    = { { FIXED_INT_I(2), FIXED_INT_I(1) },
                                    { FIXED_INT_I(4), FIXED_INT_I(9) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -150), FIXED_POINT_I(-1, -200) },
@@ -631,7 +561,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 24,
     .animation_length          = ARRAY_SIZE(anim_bird),
     .animation_frames          = anim_bird,
-    .extent                    = { FIXED_INT_I(9), FIXED_INT_I(7) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(3) },
                                    { FIXED_INT_I(7), FIXED_INT_I(3) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -400), FIXED_POINT_I(0, -150) },
@@ -643,7 +572,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 24,
     .animation_length          = ARRAY_SIZE(anim_bird),
     .animation_frames          = anim_bird,
-    .extent                    = { FIXED_INT_I(9), FIXED_INT_I(7) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(3) },
                                    { FIXED_INT_I(7), FIXED_INT_I(3) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -400), FIXED_INT_I(0) },
@@ -655,7 +583,6 @@ jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] =
     .animation_ticks_per_frame = 24,
     .animation_length          = ARRAY_SIZE(anim_bird),
     .animation_frames          = anim_bird,
-    .extent                    = { FIXED_INT_I(9), FIXED_INT_I(7) },
     .hitbox                    = { { FIXED_INT_I(1), FIXED_INT_I(3) },
                                    { FIXED_INT_I(7), FIXED_INT_I(3) } },
     .spawn_inertia             = { FIXED_POINT_I(0, -400), FIXED_POINT_I(0, 200) },
index 5b6da64..1cf8f06 100644 (file)
@@ -18,7 +18,6 @@ typedef struct jumpnrun_enemy_type {
   size_t              animation_length;
   badge_sprite const *animation_frames;
 
-  vec2d               extent;
   rectangle           hitbox;
   vec2d               spawn_inertia;
 
@@ -42,25 +41,20 @@ typedef struct jumpnrun_enemy_type {
 typedef struct jumpnrun_enemy {
   jumpnrun_moveable base;
   vec2d             spawn_pos;
-  unsigned          flags;
 
   jumpnrun_enemy_type const *type;
 } jumpnrun_enemy;
 
 enum {
-  JUMPNRUN_ENEMY_SPAWNED      = 1,
-  JUMPNRUN_ENEMY_UNAVAILABLE  = 2,
-  JUMPNRUN_ENEMY_FACING_RIGHT = 4,
+// Do not collide with JUMPNRUN_MOVEABLE_* flags
+  JUMPNRUN_ENEMY_SPAWNED        = 4,
+  JUMPNRUN_ENEMY_UNAVAILABLE    = 8,
   JUMPNRUN_ENEMY_EVENT_TRIGGER1 = 128
 };
 
-static inline rectangle    const *enemy_box         (jumpnrun_enemy const *enemy) { return &enemy->base.current_box   ; }
-static inline vec2d               enemy_position    (jumpnrun_enemy const *enemy) { return enemy->base.current_box.pos; }
-static inline rectangle           enemy_hitbox      (jumpnrun_enemy const *enemy) { rectangle r = enemy->type->hitbox; rectangle_move_rel(&r, enemy_position(enemy)); return r; }
-static inline badge_sprite const *enemy_sprite      (jumpnrun_enemy const *enemy) { return &enemy->type->animation_frames[enemy->base.anim_frame]; }
-
-static inline bool                enemy_facing_right(jumpnrun_enemy const *enemy) { return (enemy->flags & JUMPNRUN_ENEMY_FACING_RIGHT) || fixed_point_gt(enemy->base.inertia.x, FIXED_INT(0)); }
-static inline uint8_t             enemy_render_flags(jumpnrun_enemy const *enemy) { return enemy_facing_right(enemy) ? BADGE_BLT_MIRRORED : 0; }
+static inline rectangle    const *enemy_hitbox(jumpnrun_enemy const *enemy) { return &enemy->base.hitbox; }
+void jumpnrun_enemy_despawn(jumpnrun_enemy *self);
+void jumpnrun_enemy_reset  (jumpnrun_enemy *self);
 
 enum {
   JUMPNRUN_ENEMY_TYPE_CAT,
diff --git a/badge/jumpnrun/game_state.h b/badge/jumpnrun/game_state.h
new file mode 100644 (file)
index 0000000..2ebe4d0
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef INCLUDED_BADGE_JUMPNRUN_GAME_STATE_H
+#define INCLUDED_BADGE_JUMPNRUN_GAME_STATE_H
+
+#include "player.h"
+#include "levels.h"
+#include "shots.h"
+#include "../ui/display.h"
+
+enum {
+  JUMPNRUN_MAX_SHOTS = 2,
+
+  JUMPNRUN_STATE_WON = 1
+};
+
+typedef struct jumpnrun_game_state {
+  jumpnrun_player   player;
+  int               screen_left;
+  uint8_t           flags;
+  jumpnrun_shot     shots[JUMPNRUN_MAX_SHOTS];
+} jumpnrun_game_state;
+
+static inline int jumpnrun_screen_left (jumpnrun_game_state const *state) { return state->screen_left; }
+static inline int jumpnrun_screen_right(jumpnrun_game_state const *state) { return jumpnrun_screen_left(state) + BADGE_DISPLAY_WIDTH; }
+
+void jumpnrun_game_state_init   (jumpnrun_game_state *state, jumpnrun_level const *lv);
+void jumpnrun_game_state_respawn(jumpnrun_game_state *state, jumpnrun_level const *lv);
+
+void jumpnrun_shot_spawn(jumpnrun_shot *shot, jumpnrun_game_state const *state);
+
+#endif
index f6cf1dc..f06baaa 100644 (file)
@@ -1,5 +1,5 @@
-                                                                                                                           X
-
+                                                                                                                            
+                                                                                                                            X
                                                                                                                                                                       
 
                                                                                                                                           ###    #   ###              
index d4fb281..42e83b1 100644 (file)
@@ -1,12 +1,13 @@
 #include "items.h"
 #include "jumpnrun.h"
+#include "game_state.h"
 
 static void on_collect_win(jumpnrun_item       *self,
                            jumpnrun_game_state *state,
                            jumpnrun_level      *lv) {
   (void) lv;
   self->flags |= JUMPNRUN_ITEM_COLLECTED;
-  state->status = JUMPNRUN_WON;
+  state->flags |= JUMPNRUN_STATE_WON;
 }
 
 static void on_collect_checkpoint(jumpnrun_item       *self,
@@ -15,7 +16,7 @@ static void on_collect_checkpoint(jumpnrun_item       *self,
   (void) state;
   self->flags |= JUMPNRUN_ITEM_COLLECTED;
   lv->start_pos = (vec2d) { self->pos.x,
-                            fixed_point_sub(fixed_point_add(self->pos.y, FIXED_INT(self->type->sprite.height)), hacker_extents().y)
+                            fixed_point_sub(fixed_point_add(self->pos.y, FIXED_INT(self->type->sprite.height)), jumpnrun_player_extents().y)
   };
 }
 
@@ -24,13 +25,13 @@ static void on_collect_key(jumpnrun_item       *self,
                            jumpnrun_level      *lv) {
   (void) lv;
   self->flags |= JUMPNRUN_ITEM_COLLECTED;
-  ++state->keys;
+  ++state->player.keys;
 }
 
 static void on_collect_encrypted(jumpnrun_item       *self,
                                  jumpnrun_game_state *state,
                                  jumpnrun_level      *lv) {
-  if(state->keys != 0) {
+  if(state->player.keys != 0) {
     on_collect_win(self, state, lv);
   }
 }
index 79fa6b3..7dc2c22 100644 (file)
@@ -1,6 +1,8 @@
+#include "game_state.h"
 #include "jumpnrun.h"
 #include "collision.h"
 #include "levels.h"
+#include "render.h"
 #include "stats.h"
 
 #include "../ui/display.h"
@@ -20,86 +22,21 @@ 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 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_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,16 +72,20 @@ 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);
@@ -158,49 +99,49 @@ 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(killed || fixed_point_gt(state->player.base.hitbox.pos.y, FIXED_INT(BADGE_DISPLAY_HEIGHT))) {
+    state->player.base.flags |= JUMPNRUN_PLAYER_DEAD;
   }
 }
 
@@ -208,21 +149,17 @@ 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;
 
   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) {
+  if(state->player.base.tick_minor == 0) {
     badge_framebuffer fb;
     badge_framebuffer_clear(&fb);
 
     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);
+      jumpnrun_render_tile(&fb, state, &lv->tiles[tile]);
     }
 
     for(size_t item = 0; item < lv->header.item_count; ++item) {
@@ -232,54 +169,24 @@ void jumpnrun_level_tick(jumpnrun_level      *lv,
         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) {
       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);
       }
     }
 
@@ -288,38 +195,20 @@ void jumpnrun_level_tick(jumpnrun_level      *lv,
       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);
+    jumpnrun_render_player(&fb, state);
 
     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;
-        }
-      }
+      jumpnrun_shot_process(&state->shots[shot_ix]);
     }
 
     for(size_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) {
@@ -328,10 +217,10 @@ void jumpnrun_level_tick(jumpnrun_level      *lv,
     }
   }
 
-  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->player.base.tick_minor;
+  if(state->player.base.tick_minor == 3) {
+    state->player.base.tick_minor = 0;
   }
 }
 
@@ -341,36 +230,13 @@ uint8_t jumpnrun_play(char const *lvname) {
   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) {
+  for(jumpnrun_game_state_init(&gs, &lv); (gs.flags & JUMPNRUN_STATE_WON) == 0 && gs.player.lives != 0; --gs.player.lives) {
     jumpnrun_show_lives_screen(&gs);
+    jumpnrun_game_state_respawn(&gs, &lv);
 
-    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);
-
-        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) {
+    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)) {
@@ -380,8 +246,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)) {
@@ -405,5 +271,7 @@ uint8_t jumpnrun_play(char const *lvname) {
     }
   }
 
-  return gs.status;
+  if(gs.flags & JUMPNRUN_STATE_WON) { return JUMPNRUN_WON; }
+  if(gs.player.lives == 0) return JUMPNRUN_LOST;
+  return JUMPNRUN_ERROR;
 }
index f2933d9..5c4024b 100644 (file)
@@ -4,6 +4,7 @@
 #include "enemies.h"
 #include "items.h"
 #include "levels.h"
+#include "shots.h"
 #include "tiles.h"
 
 #include "../ui/sprite.h"
@@ -14,8 +15,8 @@
 
 enum {
   JUMPNRUN_PLAYING,
-  JUMPNRUN_DEAD,
   JUMPNRUN_WON,
+  JUMPNRUN_LOST,
   JUMPNRUN_ERROR
 };
 
@@ -24,32 +25,8 @@ enum {
   JUMPNRUN_MAX_SPAWNED_ENEMIES = 10
 };
 
-typedef struct jumpnrun_shot {
-  rectangle old_box;
-  rectangle current_box;
-  vec2d     inertia;
-  uint8_t   tick;
-} jumpnrun_shot;
-
-static inline bool jumpnrun_shot_spawned(jumpnrun_shot const *shot) { return fixed_point_ne(shot->inertia.x, FIXED_INT(0)); }
-static inline void jumpnrun_shot_despawn(jumpnrun_shot       *shot) { shot->inertia.x = FIXED_INT(0); }
-
-enum {
-  JUMPNRUN_MAX_SHOTS = 2
-};
-
-typedef struct jumpnrun_game_state {
-  jumpnrun_moveable player;
-
-  uint8_t status;
-  int     left;
-  uint8_t lives;
-  uint8_t keys;
-
-  jumpnrun_shot shots[JUMPNRUN_MAX_SHOTS];
-} jumpnrun_game_state;
-
-vec2d hacker_extents(void);
+vec2d jumpnrun_player_extents(void);
+void jumpnrun_apply_gravity(vec2d *inertia);
 void jumpnrun_passive_movement(vec2d *inertia);
 badge_sprite const *jumpnrun_hacker_symbol(void);
 
index 21e2541..4b1e499 100644 (file)
@@ -51,10 +51,9 @@ static void jumpnrun_level_make_enemy(jumpnrun_enemy *dest, level_thing thing) {
 
   dest->type = &jumpnrun_enemy_type_data[thing.type];
 
-  dest->spawn_pos.x          = FIXED_POINT( thing.x      * JUMPNRUN_TILE_PIXEL_WIDTH                                          , 0);
-  dest->spawn_pos.y          = FIXED_POINT((thing.y + 1) * JUMPNRUN_TILE_PIXEL_HEIGHT - dest->type->animation_frames[0].height, 0);
-  dest->base.current_box     = rectangle_new(dest->spawn_pos, dest->type->extent);
-  dest->base.inertia         = dest->type->spawn_inertia;
+  dest->spawn_pos.x = FIXED_POINT( thing.x      * JUMPNRUN_TILE_PIXEL_WIDTH                                          , 0);
+  dest->spawn_pos.y = FIXED_POINT((thing.y + 1) * JUMPNRUN_TILE_PIXEL_HEIGHT - dest->type->animation_frames[0].height, 0);
+  jumpnrun_enemy_despawn(dest);
 }
 
 #ifdef __linux__
@@ -97,7 +96,7 @@ int jumpnrun_load_level_from_file(jumpnrun_level *dest, FIL *fd) {
 #endif
     return JUMPNRUN_LEVEL_LOAD_ERROR;
   } else {
-    dest->start_pos.x = fixed_point_sub(FIXED_INT((spos[0] + 1) * JUMPNRUN_TILE_PIXEL_WIDTH ), hacker_extents().x);
+    dest->start_pos.x = fixed_point_sub(FIXED_INT((spos[0] + 1) * JUMPNRUN_TILE_PIXEL_WIDTH ), jumpnrun_player_extents().x);
     dest->start_pos.y =                 FIXED_INT( spos[1]      * JUMPNRUN_TILE_PIXEL_HEIGHT);
   }
 
index 2ea1909..6c940c8 100644 (file)
@@ -1,7 +1,7 @@
+smb
 lubiXOXO
 lubilove
 gnobbel
-smb
 wrongturn
 foo
 mean
diff --git a/badge/jumpnrun/moveable.c b/badge/jumpnrun/moveable.c
new file mode 100644 (file)
index 0000000..26789d3
--- /dev/null
@@ -0,0 +1 @@
+#include "moveable.h"
index 5e7514b..7b680d2 100644 (file)
@@ -2,17 +2,26 @@
 #define INCLUDED_BADGE_JUMPNRUN_MOVEABLE_H
 
 #include "../util/util.h"
+#include <stdint.h>
+
+enum {
+  // Do not collide with JUMPNRUN_ENEMY_* and JUMPNRUN_PLAYER_* flags.
+  JUMPNRUN_MOVEABLE_TOUCHING_GROUND = 1,
+  JUMPNRUN_MOVEABLE_MIRRORED        = 2
+};
 
 typedef struct jumpnrun_moveable {
-  rectangle current_box;
+  rectangle hitbox;
   vec2d     inertia;
 
   uint8_t   tick_minor;
   uint8_t   anim_frame;
-  uint8_t   anim_direction;
 
-  bool      touching_ground;
+  uint8_t   flags;
   uint8_t   jumpable_frames;
 } jumpnrun_moveable;
 
+static inline bool jumpnrun_moveable_touching_ground(jumpnrun_moveable const *self) { return self->flags & JUMPNRUN_MOVEABLE_TOUCHING_GROUND; }
+static inline bool jumpnrun_moveable_mirrored       (jumpnrun_moveable const *self) { return self->flags & JUMPNRUN_MOVEABLE_MIRRORED       ; }
+
 #endif
diff --git a/badge/jumpnrun/player.h b/badge/jumpnrun/player.h
new file mode 100644 (file)
index 0000000..1b193b5
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef INCLUDED_BADGE_JUMPNRUN_PLAYER_H
+#define INCLUDED_BADGE_JUMPNRUN_PLAYER_H
+
+#include "moveable.h"
+#include "../ui/display.h"
+#include "../util/util.h"
+
+enum {
+  // Do not collide with JUMPNRUN_MOVEABLE_* flags
+  JUMPNRUN_PLAYER_DEAD = 4
+};
+
+enum {
+  JUMPNRUN_PLAYER_FRAMES = 4
+};
+
+typedef struct jumpnrun_player {
+  jumpnrun_moveable base;
+
+  uint8_t lives;
+  uint8_t keys;
+} jumpnrun_player;
+
+void jumpnrun_player_spawn        (jumpnrun_player *self, vec2d spawn_pos, uint8_t lives);
+void jumpnrun_player_respawn      (jumpnrun_player *self, vec2d spawn_pos);
+
+#endif
diff --git a/badge/jumpnrun/render.c b/badge/jumpnrun/render.c
new file mode 100644 (file)
index 0000000..3755aa6
--- /dev/null
@@ -0,0 +1,74 @@
+#include "render.h"
+
+static badge_sprite const anim_sickle[JUMPNRUN_SHOT_FRAMES] = {
+  { 3, 3, (uint8_t const *) "\xab\x01" },
+  { 3, 3, (uint8_t const *) "\xee" }
+};
+
+static badge_sprite const anim_player[JUMPNRUN_PLAYER_FRAMES ] = {
+  { 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" }
+};
+
+vec2d jumpnrun_player_extents(void) { return (vec2d) { FIXED_INT_I(5), FIXED_INT_I(8) }; }
+
+void jumpnrun_render_moveable     (badge_framebuffer *fb, jumpnrun_game_state const *state, jumpnrun_moveable const *moveable, badge_sprite const *animation, vec2d sprite_offset) {
+  vec2d render_pos = vec2d_sub(moveable->hitbox.pos, sprite_offset);
+  badge_framebuffer_blt(fb,
+                        fixed_point_cast_int(render_pos.x) - jumpnrun_screen_left(state),
+                        fixed_point_cast_int(render_pos.y),
+                        &animation[moveable->anim_frame],
+                        (moveable->flags & JUMPNRUN_MOVEABLE_MIRRORED) ? BADGE_BLT_MIRRORED : 0);
+}
+
+void jumpnrun_render_player       (badge_framebuffer *fb, jumpnrun_game_state const *state) {
+  jumpnrun_render_moveable(fb, state, &state->player.base, anim_player, (vec2d) { FIXED_INT(0), FIXED_INT(0) });
+}
+
+void jumpnrun_render_shot         (badge_framebuffer *fb, jumpnrun_game_state const *state, jumpnrun_shot           *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) - jumpnrun_screen_left(state),
+                        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) - jumpnrun_screen_left(state),
+                        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;
+}
+
+void jumpnrun_render_enemy        (badge_framebuffer *fb, jumpnrun_game_state const *state, jumpnrun_enemy    const *enemy   ) {
+  jumpnrun_render_moveable(fb, state, &enemy->base, enemy->type->animation_frames, enemy->type->hitbox.pos);
+}
+
+void jumpnrun_render_tile         (badge_framebuffer *fb, jumpnrun_game_state const *state, jumpnrun_tile     const *tile    ) {
+  badge_framebuffer_blt(fb,
+                        tile->pos.x * JUMPNRUN_TILE_PIXEL_WIDTH - jumpnrun_screen_left(state),
+                        tile->pos.y * JUMPNRUN_TILE_PIXEL_HEIGHT,
+                        &tile_type(tile)->sprite,
+                        0);
+}
+
+void jumpnrun_render_item         (badge_framebuffer *fb, jumpnrun_game_state const *state, jumpnrun_item     const *item    ) {
+  badge_framebuffer_blt(fb,
+                        fixed_point_cast_int(item->pos.x) - jumpnrun_screen_left(state),
+                        fixed_point_cast_int(item->pos.y),
+                        &item->type->sprite,
+                        0);
+}
+
+void jumpnrun_render_player_symbol(badge_framebuffer *fb, int8_t x, int8_t y) {
+  badge_framebuffer_blt(fb, x, y, &anim_player[0], 0);
+}
+
+void jumpnrun_render_key_symbol   (badge_framebuffer *fb, int8_t x, int8_t y) {
+  badge_framebuffer_blt(fb, x, y, &jumpnrun_item_type_data[JUMPNRUN_ITEM_TYPE_KEY].sprite, 0);
+}
diff --git a/badge/jumpnrun/shots.c b/badge/jumpnrun/shots.c
new file mode 100644 (file)
index 0000000..275682d
--- /dev/null
@@ -0,0 +1,19 @@
+#include "shots.h"
+#include "jumpnrun.h"
+#include "render.h"
+
+void jumpnrun_shot_process(jumpnrun_shot *shot) {
+  if(jumpnrun_shot_spawned(shot)) {
+    rectangle_move_rel(&shot->current_box, shot->inertia);
+    jumpnrun_apply_gravity(&shot->inertia);
+
+    if(fixed_point_gt(rectangle_top(&shot->current_box), FIXED_INT(BADGE_DISPLAY_HEIGHT))) {
+      jumpnrun_shot_despawn(shot);
+    }
+
+    ++shot->tick;
+    if(shot->tick == JUMPNRUN_SHOT_FRAMES * JUMPNRUN_SHOT_TICKS_PER_FRAME) {
+      shot->tick = 0;
+    }
+  }
+}
diff --git a/badge/jumpnrun/shots.h b/badge/jumpnrun/shots.h
new file mode 100644 (file)
index 0000000..66e0501
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef INCLUDED_BADGE_JUMPNRUN_SHOTS_H
+#define INCLUDED_BADGE_JUMPNRUN_SHOTS_H
+
+#include "../ui/display.h"
+#include "../util/util.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct jumpnrun_shot {
+  rectangle old_box;
+  rectangle current_box;
+  vec2d     inertia;
+  uint8_t   tick;
+} jumpnrun_shot;
+
+enum {
+  JUMPNRUN_SHOT_EXTENT = 3,
+  JUMPNRUN_SHOT_TICKS_PER_FRAME = 36,
+  JUMPNRUN_SHOT_FRAMES = 2
+};
+
+static inline bool jumpnrun_shot_spawned(jumpnrun_shot const *shot) { return fixed_point_ne(shot->inertia.x, FIXED_INT(0)); }
+static inline void jumpnrun_shot_despawn(jumpnrun_shot       *shot) { shot->inertia.x = FIXED_INT(0); }
+
+void jumpnrun_shot_process(jumpnrun_shot *shot);
+
+#endif
index 74338ec..cbcb24b 100644 (file)
@@ -1,35 +1,49 @@
 #include "stats.h"
-#include "jumpnrun.h"
+
 #include "items.h"
+#include "jumpnrun.h"
+#include "render.h"
+
 #include "../ui/display.h"
+#include "../ui/event.h"
 #include "../ui/font.h"
 
 void jumpnrun_show_lives_screen(jumpnrun_game_state const *state) {
   badge_framebuffer fb = { { { 0 } } };
 
-  badge_sprite const *hacker =  jumpnrun_hacker_symbol();
-  badge_sprite const *key    = &jumpnrun_item_type_data[JUMPNRUN_ITEM_TYPE_KEY].sprite;
-
   int8_t y_upper = BADGE_DISPLAY_HEIGHT / 2 - BADGE_FONT_HEIGHT;
   int8_t y_lower = (BADGE_DISPLAY_HEIGHT + BADGE_FONT_HEIGHT) / 2;
-  int8_t x_left  = BADGE_DISPLAY_WIDTH / 2 - hacker->width - BADGE_FONT_WIDTH - 5;
+  int8_t x_left  = BADGE_DISPLAY_WIDTH / 2 - fixed_point_cast_int(jumpnrun_player_extents().y) - BADGE_FONT_WIDTH - 5;
   int8_t x_mid   = (BADGE_DISPLAY_WIDTH - BADGE_FONT_WIDTH) / 2;
   int8_t x_right = x_mid + BADGE_FONT_WIDTH + 5;
 
   char buf[] = "x";
-  if(state->keys != 0) {
+  if(state->player.keys != 0) {
     y_upper -= BADGE_FONT_HEIGHT;
   }
 
-  badge_framebuffer_blt          (&fb, x_left , y_upper, hacker, 0);
+  jumpnrun_render_player_symbol(&fb, x_left, y_upper);
   badge_framebuffer_render_text  (&fb, x_mid  , y_upper, buf);
-  badge_framebuffer_render_number(&fb, x_right, y_upper, state->lives);
+  badge_framebuffer_render_number(&fb, x_right, y_upper, state->player.lives);
 
-  if(state->keys != 0) {
-    badge_framebuffer_blt          (&fb, x_left , y_lower, key, 0);
+  if(state->player.keys != 0) {
+    jumpnrun_render_key_symbol(&fb, x_left, y_lower);
     badge_framebuffer_render_text  (&fb, x_mid  , y_lower, buf);
-    badge_framebuffer_render_number(&fb, x_right, y_lower, state->keys);
+    badge_framebuffer_render_number(&fb, x_right, y_lower, state->player.keys);
   }
 
   badge_framebuffer_flush(&fb);
+
+  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);
+
+      if(new_buttons != 0) break;
+    }
+  }
 }
index e9e8950..170e130 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef INCLUDED_BADGE_JUMPNRUN_STATS_H
 #define INCLUDED_BADGE_JUMPNRUN_STATS_H
 
-#include "jumpnrun.h"
+#include "game_state.h"
 
 void jumpnrun_show_lives_screen(jumpnrun_game_state const *state);
 
index a6a5f70..3ce7869 100644 (file)
@@ -39,10 +39,10 @@ typedef struct jumpnrun_tile_range {
   size_t last; // actually one past last.
 } jumpnrun_tile_range;
 
-static inline fixed_point tile_left  (jumpnrun_tile const *tile) { return FIXED_POINT(tile->pos.x * JUMPNRUN_TILE_PIXEL_WIDTH , 0); }
-static inline fixed_point tile_top   (jumpnrun_tile const *tile) { return FIXED_POINT(tile->pos.y * JUMPNRUN_TILE_PIXEL_HEIGHT, 0); }
-static inline fixed_point tile_right (jumpnrun_tile const *tile) { return fixed_point_add(tile_left(tile), FIXED_POINT(JUMPNRUN_TILE_PIXEL_WIDTH , 0)); }
-static inline fixed_point tile_bottom(jumpnrun_tile const *tile) { return fixed_point_add(tile_top (tile), FIXED_POINT(JUMPNRUN_TILE_PIXEL_HEIGHT, 0)); }
+static inline fixed_point tile_left  (jumpnrun_tile const *tile) { return FIXED_INT(tile->pos.x * JUMPNRUN_TILE_PIXEL_WIDTH ); }
+static inline fixed_point tile_top   (jumpnrun_tile const *tile) { return FIXED_INT(tile->pos.y * JUMPNRUN_TILE_PIXEL_HEIGHT); }
+static inline fixed_point tile_right (jumpnrun_tile const *tile) { return fixed_point_add(tile_left(tile), FIXED_INT(JUMPNRUN_TILE_PIXEL_WIDTH )); }
+static inline fixed_point tile_bottom(jumpnrun_tile const *tile) { return fixed_point_add(tile_top (tile), FIXED_INT(JUMPNRUN_TILE_PIXEL_HEIGHT)); }
 
 /************************************/
 
index 4fe5c0e..70d43ba 100644 (file)
@@ -13,7 +13,14 @@ static inline vec2d vec2d_add(vec2d v1, vec2d v2) {
     fixed_point_add(v1.x, v2.x),
     fixed_point_add(v1.y, v2.y)
   };
+  return r;
+}
 
+static inline vec2d vec2d_sub(vec2d v1, vec2d v2) {
+  vec2d r = {
+    fixed_point_sub(v1.x, v2.x),
+    fixed_point_sub(v1.y, v2.y)
+  };
   return r;
 }
 
index 93a7210..33b4ca5 100644 (file)
@@ -21,11 +21,16 @@ BADGE_CSRCS   = badge_main_loop.c \
                 ui/sprite.c \
                 jumpnrun/collision.c \
                 jumpnrun/enemies.c \
+                jumpnrun/game_state.c \
                 jumpnrun/items.c \
                 jumpnrun/jumpnrun.c \
-                jumpnrun/tiles.c \
                 jumpnrun/level_load.c \
-                jumpnrun/stats.c
+                jumpnrun/moveable.c \
+                jumpnrun/player.c \
+                jumpnrun/render.c \
+                jumpnrun/shots.c \
+                jumpnrun/stats.c \
+                jumpnrun/tiles.c
 
 BADGE_CXXOBJS = $(BADGE_CXXSRCS:%.cc=%.o)
 BADGE_COBJS   = $(BADGE_CSRCS:%.c=%.o)
This page took 0.072478 seconds and 4 git commands to generate.