+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;
+}
+
+static void jumpnrun_enemy_kill(jumpnrun_enemy *self) {
+ self->base.flags |= JUMPNRUN_MOVEABLE_DYING;
+ self->base.tick_minor = 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 | JUMPNRUN_ENEMY_MOVING | JUMPNRUN_MOVEABLE_DYING);
+ 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, self->type->animation_frames[self->base.anim_frame].width);
+}
+
+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);
+}
+