Hoppelnde Hasen.
[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_bunny[] = {
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 badge_sprite const anim_rotor[] = {
48 /*
49 { 9, 9, (uint8_t const *) "\x00\x00\x00\x04\x0e\x1f\x02\x0c\x18\x70\x00" },
50 { 9, 9, (uint8_t const *) "\x1c\x30\x60\x84\x0e\x1f\x00\x00\x00\x00\x00" },
51 { 9, 9, (uint8_t const *) "\x1c\x30\x60\x80\xf0\xe1\x40\x00\x00\x00\x00" },
52 { 9, 9, (uint8_t const *) "\x00\x00\x00\x00\xf0\xe1\x42\x0c\x18\x70\x00" },
53 */
54 { 9, 9, (uint8_t const *) "\x00\x00\x00\x00\xf0\xe1\x42\x0c\x18\x70\x00" },
55 { 9, 9, (uint8_t const *) "\x1c\x30\x60\x80\xf0\xe1\x40\x00\x00\x00\x00" },
56 { 9, 9, (uint8_t const *) "\x1c\x30\x60\x84\x0e\x1f\x00\x00\x00\x00\x00" },
57 { 9, 9, (uint8_t const *) "\x00\x00\x00\x04\x0e\x1f\x02\x0c\x18\x70\x00" }
58 /*
59 { 9, 9, (uint8_t const *) "\x00\x00\x00\x00\xf0\xe1\x40\x00\x00\x00\x00" },
60 { 9, 9, (uint8_t const *) "\x1c\x30\x60\x80\x00\x01\x00\x00\x00\x00\x00" },
61 { 9, 9, (uint8_t const *) "\x00\x00\x00\x04\x0e\x1f\x00\x00\x00\x00\x00" },
62 { 9, 9, (uint8_t const *) "\x00\x00\x00\x00\x00\x01\x02\x0c\x18\x70\x00" }
63 */
64 };
65
66 static void enemy_animation_advance(jumpnrun_enemy *enemy) {
67 ++enemy->base.tick_minor;
68 if(enemy->base.tick_minor == enemy->type->animation_ticks_per_frame) {
69 enemy->base.tick_minor = 0;
70
71 ++enemy->base.anim_frame;
72 if(enemy->base.anim_frame >= enemy->type->animation_length) {
73 enemy->base.anim_frame = 0;
74 }
75 }
76 }
77
78 void jumpnrun_process_enemy(jumpnrun_enemy *self,
79 badge_framebuffer *fb,
80 struct jumpnrun_game_state *state,
81 struct jumpnrun_level *lv,
82 struct jumpnrun_tile_range const *visible_tiles,
83 vec2d *player_inertia_mod) {
84 int const spawn_margin = 1 + self->type->animation_frames[self->base.anim_frame].width;
85
86 if(self->flags & JUMPNRUN_ENEMY_SPAWNED) {
87 if(fixed_point_lt(rectangle_left(enemy_box(self)), FIXED_POINT(state->left - spawn_margin, 0)) ||
88 fixed_point_gt(rectangle_left(enemy_box(self)), FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH + spawn_margin, 0)) ||
89 fixed_point_gt(rectangle_top (enemy_box(self)), FIXED_POINT(BADGE_DISPLAY_HEIGHT , 0))) {
90 self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
91 } else {
92 self->type->game_tick(self, state, lv, visible_tiles, player_inertia_mod);
93
94 if(fb) {
95 badge_framebuffer_blt(fb,
96 fixed_point_cast_int(rectangle_left(enemy_box(self))) - state->left,
97 fixed_point_cast_int(rectangle_top (enemy_box(self))),
98 enemy_sprite(self),
99 enemy_render_flags(self));
100 }
101 }
102 } else if(self->flags & JUMPNRUN_ENEMY_UNAVAILABLE) {
103 if(state->left > fixed_point_cast_int(self->spawn_pos.x) + spawn_margin ||
104 state->left + BADGE_DISPLAY_WIDTH + spawn_margin < fixed_point_cast_int(self->spawn_pos.x)) {
105 self->flags &= ~JUMPNRUN_ENEMY_UNAVAILABLE;
106 }
107 } else if((fixed_point_gt(self->spawn_pos.x, FIXED_POINT(state->left - spawn_margin, 0)) &&
108 fixed_point_lt(self->spawn_pos.x, FIXED_POINT(state->left - spawn_margin / 2, 0))) ||
109 (fixed_point_lt(self->spawn_pos.x, FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH + spawn_margin, 0)) &&
110 fixed_point_gt(self->spawn_pos.x, FIXED_POINT(state->left + BADGE_DISPLAY_WIDTH, 0)))) {
111 // enemy unspawned, available and in spawn zone.
112 self->flags |= JUMPNRUN_ENEMY_SPAWNED | JUMPNRUN_ENEMY_UNAVAILABLE;
113 self->base.current_box = rectangle_new(self->spawn_pos, self->type->extent);
114 self->base.inertia = self->type->spawn_inertia;
115 self->base.anim_frame = 0;
116 self->base.tick_minor = 0;
117 self->base.touching_ground = false;
118 self->base.jumpable_frames = 0;
119 }
120 }
121
122 void enemy_collision_tiles_bounce_horiz(jumpnrun_enemy *self,
123 vec2d *desired_position,
124 jumpnrun_level *lv,
125 jumpnrun_tile_range const *visible_tiles) {
126 vec2d inertia_mod = self->base.inertia;
127
128 collisions_tiles_displace(desired_position,
129 &self->base,
130 lv,
131 visible_tiles,
132 &inertia_mod);
133
134 if(fixed_point_ne(inertia_mod.x, self->base.inertia.x)) {
135 self->base.inertia.x = fixed_point_neg(self->base.inertia.x);
136 }
137 }
138
139 void enemy_collision_player_deadly(struct jumpnrun_enemy *self,
140 struct jumpnrun_game_state *state,
141 vec2d *player_inertia_mod) {
142 (void) player_inertia_mod;
143
144 rectangle rect_self = enemy_hitbox(self);
145
146 if(rectangle_intersect(&rect_self, &state->player.current_box)) {
147 state->status = JUMPNRUN_DEAD;
148 }
149 }
150
151 void enemy_collision_player_jumpable(jumpnrun_enemy *self,
152 jumpnrun_game_state *state,
153 vec2d *player_inertia_mod)
154 {
155 rectangle rect_self = enemy_hitbox(self);
156
157 if(rectangle_intersect(&rect_self, &state->player.current_box)) {
158 if(fixed_point_gt(state->player.inertia.y, FIXED_POINT(0, 0))) {
159 self->flags &= ~JUMPNRUN_ENEMY_SPAWNED;
160 player_inertia_mod->y = FIXED_POINT(0, -250);
161 state->player.jumpable_frames = 12;
162 } else {
163 state->status = JUMPNRUN_DEAD;
164 }
165 }
166 }
167
168 void enemy_collision_tiles_pass_through(struct jumpnrun_enemy *self,
169 vec2d *desired_position,
170 struct jumpnrun_level *lv,
171 struct jumpnrun_tile_range const *visible_tiles) {
172 (void) self;
173 (void) desired_position;
174 (void) lv;
175 (void) visible_tiles;
176 return;
177 }
178
179 void enemy_tick_straight_ahead(jumpnrun_enemy *self,
180 jumpnrun_game_state *state,
181 jumpnrun_level *lv,
182 jumpnrun_tile_range const *visible_tiles,
183 vec2d *player_inertia_mod) {
184 int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
185
186 if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN < state->left ||
187 screenpos >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
188 return;
189 }
190
191 jumpnrun_passive_movement(&self->base.inertia);
192
193 vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
194 self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
195 self->type->collision_player(self, state, player_inertia_mod);
196 rectangle_move_to(&self->base.current_box, new_pos);
197
198 enemy_animation_advance(self);
199 }
200
201 void enemy_tick_swing_up_and_down(struct jumpnrun_enemy *self,
202 struct jumpnrun_game_state *state,
203 struct jumpnrun_level *lv,
204 struct jumpnrun_tile_range const *visible_tiles,
205 vec2d *player_inertia_mod) {
206 int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
207
208 if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN < state->left ||
209 screenpos >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
210 return;
211 }
212
213 vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
214 self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
215 self->type->collision_player(self, state, player_inertia_mod);
216 rectangle_move_to(&self->base.current_box, new_pos);
217
218 self->base.inertia.y =
219 fixed_point_add(fixed_point_add(self->base.inertia.y,
220 fixed_point_div(self->type->spawn_inertia.y, FIXED_INT(3))),
221 fixed_point_mul(FIXED_POINT(0, 5),
222 fixed_point_sub(self->spawn_pos.y,
223 enemy_position(self).y)));
224
225 enemy_animation_advance(self);
226 }
227
228 void enemy_tick_stationary(struct jumpnrun_enemy *self,
229 struct jumpnrun_game_state *state,
230 struct jumpnrun_level *lv,
231 struct jumpnrun_tile_range const *visible_tiles,
232 vec2d *player_inertia_mod) {
233 int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
234
235 if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN < state->left ||
236 screenpos >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
237 return;
238 }
239
240 self->type->collision_tiles(self, &self->base.current_box.pos, lv, visible_tiles);
241 self->type->collision_player(self, state, player_inertia_mod);
242
243 enemy_animation_advance(self);
244 }
245
246 void enemy_tick_jumper(jumpnrun_enemy *self,
247 jumpnrun_game_state *state,
248 jumpnrun_level *lv,
249 jumpnrun_tile_range const *visible_tiles,
250 vec2d *player_inertia_mod) {
251 int screenpos = fixed_point_cast_int(rectangle_left(&self->base.current_box));
252
253 if(screenpos + JUMPNRUN_MAX_SPAWN_MARGIN < state->left ||
254 screenpos >= state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN) {
255 return;
256 }
257
258 jumpnrun_passive_movement(&self->base.inertia);
259
260 vec2d new_pos = vec2d_add(enemy_position(self), self->base.inertia);
261 self->type->collision_tiles(self, &new_pos, lv, visible_tiles);
262 self->type->collision_player(self, state, player_inertia_mod);
263 rectangle_move_to(&self->base.current_box, new_pos);
264
265 if(self->base.touching_ground) {
266 self->base.inertia.y = self->type->spawn_inertia.y;
267 }
268
269 enemy_animation_advance(self);
270 }
271
272
273 jumpnrun_enemy_type const jumpnrun_enemy_type_data[JUMPNRUN_ENEMY_TYPE_COUNT] = {
274 {
275 .animation_ticks_per_frame = 16,
276 .animation_length = ARRAY_SIZE(anim_cat),
277 .animation_frames = anim_cat,
278 .extent = { FIXED_INT_I(8), FIXED_INT_I(5) },
279 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(2) },
280 { FIXED_INT_I(6), FIXED_INT_I(3) } },
281 .spawn_inertia = { FIXED_POINT_I(0, -100), FIXED_INT_I(0) },
282 .collision_tiles = enemy_collision_tiles_bounce_horiz,
283 .collision_player = enemy_collision_player_jumpable,
284 .game_tick = enemy_tick_straight_ahead
285 }, {
286 .animation_ticks_per_frame = 12,
287 .animation_length = ARRAY_SIZE(anim_mushroom),
288 .animation_frames = anim_mushroom,
289 .extent = { FIXED_INT_I(7), FIXED_INT_I(7) },
290 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(1) },
291 { FIXED_INT_I(5), FIXED_INT_I(4) } },
292 .spawn_inertia = { FIXED_POINT_I(0, -50), FIXED_INT_I(0) },
293 .collision_tiles = enemy_collision_tiles_bounce_horiz,
294 .collision_player = enemy_collision_player_jumpable,
295 .game_tick = enemy_tick_straight_ahead
296 }, {
297 .animation_ticks_per_frame = 9,
298 .animation_length = ARRAY_SIZE(anim_bunny),
299 .animation_frames = anim_bunny,
300 .extent = { FIXED_INT_I(7), FIXED_INT_I(5) },
301 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(2) },
302 { FIXED_INT_I(5), FIXED_INT_I(3) } },
303 .spawn_inertia = { FIXED_POINT_I(0, -80), FIXED_POINT_I(0, -800) },
304 .collision_tiles = enemy_collision_tiles_bounce_horiz,
305 .collision_player = enemy_collision_player_jumpable,
306 .game_tick = enemy_tick_jumper
307 }, {
308 .animation_ticks_per_frame = 6,
309 .animation_length = ARRAY_SIZE(anim_snake),
310 .animation_frames = anim_snake,
311 .extent = { FIXED_INT_I(10), FIXED_INT_I(6) },
312 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(4) },
313 { FIXED_INT_I(8), FIXED_INT_I(2) } },
314 .spawn_inertia = { FIXED_POINT_I(0, -150), FIXED_INT_I(0) },
315 .collision_tiles = enemy_collision_tiles_bounce_horiz,
316 .collision_player = enemy_collision_player_jumpable,
317 .game_tick = enemy_tick_straight_ahead
318 }, {
319 .animation_ticks_per_frame = 6,
320 .animation_length = ARRAY_SIZE(anim_spiral),
321 .animation_frames = anim_spiral,
322 .extent = { FIXED_INT_I(10), FIXED_INT_I(10) },
323 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(1) },
324 { FIXED_INT_I(8), FIXED_INT_I(8) } },
325 .spawn_inertia = { FIXED_INT_I(0), FIXED_POINT_I(0, -200) },
326 .collision_tiles = enemy_collision_tiles_pass_through,
327 .collision_player = enemy_collision_player_deadly,
328 .game_tick = enemy_tick_swing_up_and_down
329 }, {
330 .animation_ticks_per_frame = 5,
331 .animation_length = ARRAY_SIZE(anim_rotor),
332 .animation_frames = anim_rotor,
333 .extent = { FIXED_INT_I(9), FIXED_INT_I(9) },
334 .hitbox = { { FIXED_INT_I(1), FIXED_INT_I(1) },
335 { FIXED_INT_I(7), FIXED_INT_I(7) } },
336 .spawn_inertia = { FIXED_INT_I(0), FIXED_POINT_I(0, 0) },
337 .collision_tiles = enemy_collision_tiles_pass_through,
338 .collision_player = enemy_collision_player_deadly,
339 .game_tick = enemy_tick_stationary
340 }
341 };
This page took 0.082724 seconds and 5 git commands to generate.