Neue Gegnertypen: Schlange, Spirale.
[hackover2013-badge-firmware.git] / badge / jumpnrun / enemies.c
1 #include "enemies.h"
2
3 #include "collision.h"
4 #include "tiles.h"
5 #include "jumpnrun.h"
6
7 static badge_sprite const anim_cat[] = {
8 { 8, 5, (uint8_t const *) "\xc7\x3f\xce\x38\x11" },
9 { 8, 5, (uint8_t const *) "\xd7\x7d\xc6\x19\x25" }
10 };
11
12 static badge_sprite const anim_mushroom[] = {
13 { 7, 7, (uint8_t const *) "\x10\x0c\x9f\xcf\xc7\x40" },
14 { 7, 7, (uint8_t const *) "\x20\x18\x1e\x8f\x87\x81" },
15 { 7, 7, (uint8_t const *) "\x10\x0c\x9f\xcf\xc7\x40" },
16 { 7, 7, (uint8_t const *) "\x08\x86\xdf\xef\x67\x20" },
17 { 7, 7, (uint8_t const *) "\x04\xc3\xef\xf7\x33\x10" },
18 { 7, 7, (uint8_t const *) "\x04\xc3\xe7\xf3\x31\x10" }
19 };
20
21 static badge_sprite const anim_kaninchen[] = {
22 { 7, 5, (uint8_t const *) "\x60\x30\xbe\x31\x02" },
23 { 7, 5, (uint8_t const *) "\x42\x30\xbe\x31\x01" },
24 { 7, 5, (uint8_t const *) "\x42\x30\xae\x35\x01" },
25 { 7, 5, (uint8_t const *) "\x60\x30\xae\x35\x02" },
26 { 7, 5, (uint8_t const *) "\x60\x30\xbe\x31\x01" }
27 };
28
29 static badge_sprite const anim_snake[] = {
30 { 10, 6, (uint8_t const *) "\x10\x86\x83\x30\x04\x83\xa2\x0f" },
31 { 10, 6, (uint8_t const *) "\x10\x86\x83\x20\x0c\xc1\xa2\x0f" },
32 { 10, 6, (uint8_t const *) "\x10\x86\x83\x20\x08\x82\xe0\x0f" },
33 { 10, 6, (uint8_t const *) "\x10\x86\x83\x20\x08\x86\xe1\x0f" }
34 };
35
36 static badge_sprite const anim_spiral[] = {
37 { 10, 10, (uint8_t const *) "\xff\x07\xd8\x6f\xa1\xb5\xf6\x1a\xe8\xbf\x00\xfe\x0f" },
38 { 10, 10, (uint8_t const *) "\xff\x07\xd8\x6f\xa1\xb5\xd6\xda\x69\xb0\x7f\x02\x0c" },
39 { 10, 10, (uint8_t const *) "\xff\x07\xd8\x6f\xa1\xb5\xd6\x5a\x6a\xaf\x81\xfe\x0b" },
40 { 10, 10, (uint8_t const *) "\xff\x07\xd8\x4f\x61\x35\xd5\x55\x54\x5f\x01\xfd\x07" },
41 { 10, 10, (uint8_t const *) "\xff\x07\xd0\x7f\x81\xf5\xd6\x5a\x68\xbf\x01\xfe\x0f" },
42 { 10, 10, (uint8_t const *) "\x03\xe4\xdf\x60\xb9\xb5\xd6\x5a\x68\xbf\x01\xfe\x0f" },
43 { 10, 10, (uint8_t const *) "\xfd\x17\x58\x6f\xa5\xb5\xd6\x5a\x68\xbf\x01\xfe\x0f" },
44 { 10, 10, (uint8_t const *) "\xfe\x0b\xa8\xaf\xa2\xba\xca\x6a\x28\xbf\x01\xfe\x0f" }
45 };
46
47 static void enemy_animation_advance(jumpnrun_enemy *enemy) {
48 ++enemy->base.tick_minor;
49 if(enemy->base.tick_minor == enemy->type->animation_ticks_per_frame) {
50 enemy->base.tick_minor = 0;
51
52 ++enemy->base.anim_frame;
53 if(enemy->base.anim_frame >= enemy->type->animation_length) {
54 enemy->base.anim_frame = 0;
55 }
56 }
57 }
58
59 void jumpnrun_process_enemy(jumpnrun_enemy *self,
60 badge_framebuffer *fb,
61 struct jumpnrun_game_state *state,
62 struct jumpnrun_level *lv,
63 struct jumpnrun_tile_range const *visible_tiles,
64 vec2d *player_inertia_mod) {
65 int const spawn_margin = 1 + self->type->animation_frames[self->base.anim_frame].width;
66
67 if(self->flags & JUMPNRUN_ENEMY_SPAWNED) {
68 if(fixed_point_lt(rectangle_left(enemy_box(self)), FIXED_POINT(state->left - spawn_margin, 0)) ||
69 fixed_point_gt(rectangle_left(enemy_box(self)), FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH + spawn_margin, 0)) ||
70 fixed_point_gt(rectangle_top (enemy_box(self)), FIXED_POINT(BADGE_DISPLAY_HEIGHT , 0))) {
71 self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
72 } else {
73 self->type->game_tick(self, state, lv, visible_tiles, player_inertia_mod);
74
75 if(fb) {
76 badge_framebuffer_blt(fb,
77 fixed_point_cast_int(rectangle_left(enemy_box(self))) - state->left,
78 fixed_point_cast_int(rectangle_top (enemy_box(self))),
79 enemy_sprite(self),
80 enemy_render_flags(self));
81 }
82 }
83 } else if(self->flags & JUMPNRUN_ENEMY_UNAVAILABLE) {
84 if(state->left > fixed_point_cast_int(self->spawn_pos.x) + spawn_margin ||
85 state->left + BADGE_DISPLAY_WIDTH + spawn_margin < fixed_point_cast_int(self->spawn_pos.x)) {
86 self->flags &= ~JUMPNRUN_ENEMY_UNAVAILABLE;
87 }
88 } else if((fixed_point_gt(self->spawn_pos.x, FIXED_POINT(state->left - spawn_margin, 0)) &&
89 fixed_point_lt(self->spawn_pos.x, FIXED_POINT(state->left - spawn_margin / 2, 0))) ||
90 (fixed_point_lt(self->spawn_pos.x, FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH + spawn_margin, 0)) &&
91 fixed_point_gt(self->spawn_pos.x, FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH, 0)))) {
92 // enemy unspawned, available and in spawn zone.
93 self->flags |= JUMPNRUN_ENEMY_SPAWNED | JUMPNRUN_ENEMY_UNAVAILABLE;
94 self->base.current_box = rectangle_new(self->spawn_pos, self->type->extent);
95 self->base.inertia = self->type->spawn_inertia;
96 self->base.anim_frame = 0;
97 self->base.tick_minor = 0;
98 self->base.touching_ground = false;
99 self->base.jumpable_frames = 0;
100 }
101 }
102
103 void enemy_collision_tiles_bounce_horiz(jumpnrun_enemy *self,
104 vec2d *desired_position,
105 jumpnrun_level *lv,
106 jumpnrun_tile_range const *visible_tiles) {
107 vec2d inertia_mod = self->base.inertia;
108
109 collisions_tiles_displace(desired_position,
110 &self->base,
111 lv,
112 visible_tiles,
113 &inertia_mod);
114
115 if(fixed_point_ne(inertia_mod.x, self->base.inertia.x)) {
116 self->base.inertia.x = fixed_point_neg(self->base.inertia.x);
117 }
118 }
119
120 void enemy_collision_player_jumpable(jumpnrun_enemy *self,
121 jumpnrun_game_state *state,
122 vec2d *player_inertia_mod)
123 {
124 rectangle rect_self = enemy_hitbox(self);
125
126 if(rectangle_intersect(&rect_self, &state->player.current_box)) {
127 if(fixed_point_gt(state->player.inertia.y, FIXED_POINT(0, 0))) {
128 self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
129 player_inertia_mod->y = FIXED_POINT(0, -250);
130 state->player.jumpable_frames = 12;
131 } else {
132 state->status = JUMPNRUN_DEAD;
133 }
134 }
135 }
136
137 void enemy_tick_straight_ahead(jumpnrun_enemy *self,
138 jumpnrun_game_state *state,
139 jumpnrun_level *lv,
140 jumpnrun_tile_range const *visible_tiles,
141 vec2d *player_inertia_mod) {
142 int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
143
144 if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN < state->left ||
145 screenpos >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
146 return;
147 }
148
149 jumpnrun_passive_movement(&self->base.inertia);
150
151 vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
152 self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
153 self->type->collision_player(self, state, player_inertia_mod);
154 rectangle_move_to(&self->base.current_box, new_pos);
155
156 enemy_animation_advance(self);
157 }
158
159 void enemy_collision_tiles_pass_through(struct jumpnrun_enemy *self,
160 vec2d *desired_position,
161 struct jumpnrun_level *lv,
162 struct jumpnrun_tile_range const *visible_tiles) {
163 (void) self;
164 (void) desired_position;
165 (void) lv;
166 (void) visible_tiles;
167 return;
168 }
169
170 void enemy_collision_player_deadly(struct jumpnrun_enemy *self,
171 struct jumpnrun_game_state *state,
172 vec2d *player_inertia_mod) {
173 (void) player_inertia_mod;
174
175 rectangle rect_self = enemy_hitbox(self);
176
177 if(rectangle_intersect(&rect_self, &state->player.current_box)) {
178 state->status = JUMPNRUN_DEAD;
179 }
180 }
181
182 void enemy_tick_swing_up_and_down(struct jumpnrun_enemy *self,
183 struct jumpnrun_game_state *state,
184 struct jumpnrun_level *lv,
185 struct jumpnrun_tile_range const *visible_tiles,
186 vec2d *player_inertia_mod) {
187 int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
188
189 if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN < state->left ||
190 screenpos >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
191 return;
192 }
193
194 self->base.inertia.y = fixed_point_add(self->base.inertia.y,
195 fixed_point_mul(FIXED_POINT(0, 5),
196 fixed_point_sub(self->spawn_pos.y,
197 self->base.current_box.pos.y)));
198
199 jumpnrun_passive_movement(&self->base.inertia);
200
201 vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
202 self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
203 self->type->collision_player(self, state, player_inertia_mod);
204 rectangle_move_to(&self->base.current_box, new_pos);
205
206 enemy_animation_advance(self);
207 }
208
209 jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] = {
210 {
211 .animation_ticks_per_frame = 16,
212 .animation_length = ARRAY_SIZE(anim_cat),
213 .animation_frames = anim_cat,
214 .extent = { FIXED_INT_I(8), FIXED_INT_I(5) },
215 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(2) },
216 { FIXED_INT_I(6), FIXED_INT_I(3) } },
217 .spawn_inertia = { FIXED_POINT_I(0, -100), FIXED_INT_I(0) },
218 .collision_tiles = enemy_collision_tiles_bounce_horiz,
219 .collision_player = enemy_collision_player_jumpable,
220 .game_tick = enemy_tick_straight_ahead
221 }, {
222 .animation_ticks_per_frame = 12,
223 .animation_length = ARRAY_SIZE(anim_mushroom),
224 .animation_frames = anim_mushroom,
225 .extent = { FIXED_INT_I(7), FIXED_INT_I(7) },
226 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(1) },
227 { FIXED_INT_I(5), FIXED_INT_I(4) } },
228 .spawn_inertia = { FIXED_POINT_I(0, -50), FIXED_INT_I(0) },
229 .collision_tiles = enemy_collision_tiles_bounce_horiz,
230 .collision_player = enemy_collision_player_jumpable,
231 .game_tick = enemy_tick_straight_ahead
232 }, {
233 .animation_ticks_per_frame = 9,
234 .animation_length = ARRAY_SIZE(anim_kaninchen),
235 .animation_frames = anim_kaninchen,
236 .extent = { FIXED_INT_I(7), FIXED_INT_I(5) },
237 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(2) },
238 { FIXED_INT_I(5), FIXED_INT_I(3) } },
239 .spawn_inertia = { FIXED_POINT_I(0, -80), FIXED_POINT_I(0, 0) },
240 .collision_tiles = enemy_collision_tiles_bounce_horiz,
241 .collision_player = enemy_collision_player_jumpable,
242 .game_tick = enemy_tick_straight_ahead
243 }, {
244 .animation_ticks_per_frame = 6,
245 .animation_length = ARRAY_SIZE(anim_snake),
246 .animation_frames = anim_snake,
247 .extent = { FIXED_INT_I(10), FIXED_INT_I(6) },
248 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(4) },
249 { FIXED_INT_I(8), FIXED_INT_I(2) } },
250 .spawn_inertia = { FIXED_POINT_I(0, -150), FIXED_INT_I(0) },
251 .collision_tiles = enemy_collision_tiles_bounce_horiz,
252 .collision_player = enemy_collision_player_jumpable,
253 .game_tick = enemy_tick_straight_ahead
254 }, {
255 .animation_ticks_per_frame = 6,
256 .animation_length = ARRAY_SIZE(anim_spiral),
257 .animation_frames = anim_spiral,
258 .extent = { FIXED_INT_I(10), FIXED_INT_I(10) },
259 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(1) },
260 { FIXED_INT_I(8), FIXED_INT_I(8) } },
261 .spawn_inertia = { FIXED_INT_I(0), FIXED_POINT_I(0, 200) },
262 .collision_tiles = enemy_collision_tiles_pass_through,
263 .collision_player = enemy_collision_player_deadly,
264 .game_tick = enemy_tick_swing_up_and_down
265 }
266 };
This page took 0.056398 seconds and 5 git commands to generate.