Mirai's Miscellaneous Misadventures
M9 / game.c
1#include "game.h"
2#include "math.c"
3
4
5
6struct mirai_animation
7{
8 int count;
9 struct mirai_image *images;
10};
11
12struct mirai_direction_animation_set
13{
14 struct mirai_animation idling;
15 struct mirai_animation moving;
16 struct mirai_animation walking;
17 struct mirai_animation sprinting;
18};
19
20struct mirai_animation_set
21{
22 struct mirai_direction_animation_set left, right;
23};
24
25struct mirai_sprite_type
26{
27 struct mirai_animation_set animations;
28 int width;
29 int height;
30};
31
32struct mirai_sprite
33{
34 struct mirai_sprite_type *type;
35
36 int x, y;
37
38 int dx, dy;
39
40 int ax;
41
42 char airborne;
43 char moving_direction;
44 char last_moving_direction;
45
46 int attack_y;
47 int attack_width;
48 int attack_height;
49 int attack_time;
50
51 void (*behave)(struct mirai_game *game, struct mirai_sprite *sprite);
52
53 unsigned char animation_time;
54};
55
56struct mirai_rotating_image_row
57{
58 int size;
59 unsigned char count;
60 unsigned char colors[64];
61};
62
63struct mirai_rotating_image
64{
65 int count;
66 struct mirai_rotating_image_row rows[];
67};
68
69
70
71
72#include "assets.c"
73
74static int mirai_gravity = 4;
75
76static unsigned char mirai_ground_colors[16 * 16];
77
78static unsigned char mirai_colors[8 * 8 * 26 * 36];
79
80static struct mirai_image mirai_images[66] =
81{
82 {16, 16, mirai_ground_colors},
83};
84
85static struct mirai_image *mirai_ground_image = mirai_images;
86
87static struct mirai_sprite_type mirai_mirai = {{}, 80, 250};
88
89
90
91
92struct mirai_game
93{
94 struct mirai_engine engine;
95 struct mirai_sprite sprites[256];
96 unsigned char sprite_count;
97
98 unsigned int left_history:16;
99 unsigned int right_history:16;
100
101 struct mirai_sprite *camera_sprite;
102 int camera_x;
103 int camera_y;
104};
105
106
107
108
109static void mirai_stamp(struct mirai_game *game, int x, int y, struct mirai_image *image)
110{
111 x -= game->camera_x;
112 x += mirai_width * 4;
113 y -= game->camera_y;
114 y += mirai_height * 4;
115 (*game->engine.stamp)(game->engine.data, x / 8, y / 8, image);
116}
117
118static void mirai_wall_physics(struct mirai_sprite *sprite)
119{
120 int x0 = sprite->x;
121 int x1 = sprite->type->width / 2;
122 int y0 = sprite->y;
123 int y1 = sprite->type->height;
124
125 int top = (y0 - y1) / 128;
126 int top2 = top - 1;
127 int bottom = (y0 - 127) / 128;
128 int bottom2 = bottom - 1;
129 int left = (x0 - x1) / 128;
130 int right = (x0 + x1) / 128;
131
132 if (
133 mirai_ground[bottom][left] != 0 && mirai_ground[bottom2][left] != 0 ||
134 mirai_ground[top][left] != 0 && mirai_ground[top2][left]
135 )
136 {
137 if (sprite->dx < 0) sprite->dx = 0;
138 sprite->x = (left + 1) * 128 + x1;
139 }
140 if (
141 mirai_ground[bottom][right] != 0 && mirai_ground[bottom2][right] != 0 ||
142 mirai_ground[top][right] != 0 && mirai_ground[top2][right]
143 )
144 {
145 if (sprite->dx > 0) sprite->dx = 0;
146 sprite->x = right * 128 - x1 - 1;
147 }
148}
149
150static void mirai_step_physics(struct mirai_sprite *sprite)
151{
152 int x0 = sprite->x;
153 int x1 = sprite->type->width / 2;
154 int y0 = sprite->y;
155 int y1 = sprite->type->height;
156
157 int top = (y0 - y1) / 128;
158 int bottom = (y0 - 127) / 128;
159 int left = (x0 - x1) / 128;
160 int right = (x0 + x1) / 128;
161
162 if (mirai_ground[bottom][left] != 0 && mirai_ground[top][left] == 0)
163 sprite->y = bottom * 128;
164 if (mirai_ground[bottom][right] != 0 && mirai_ground[top][right] == 0)
165 sprite->y = bottom * 128;
166}
167
168static void mirai_ceiling_physics(struct mirai_sprite *sprite)
169{
170 int x0 = sprite->x;
171 int x1 = sprite->type->width / 2;
172 int y0 = sprite->y;
173 int y1 = sprite->type->height;
174
175 int top = (y0 - y1) / 128;
176 int top2 = top - 1;
177 int left = (x0 - x1) / 128;
178 int right = (x0 + x1) / 128;
179
180 if (sprite->dy > 0) return;
181
182 if (mirai_ground[top][left] != 0)
183 if (mirai_ground[top][right] != 0)
184 if (mirai_ground[top2][left] != 0 || mirai_ground[top2][right] != 0)
185 {
186 sprite->dy = 0;
187 sprite->y = (top + 1) * 128 + y1;
188 }
189}
190
191static void mirai_landing_physics(struct mirai_sprite *sprite)
192{
193 int x0 = sprite->x;
194 int x1 = sprite->type->width / 2;
195 int y0 = sprite->y;
196
197 int bottom = y0 / 128;
198 int left = (x0 - x1) / 128;
199 int right = (x0 + x1) / 128;
200
201 if (sprite->dy < 0) return;
202
203 if (mirai_ground[bottom][left] != 0 || mirai_ground[bottom][right] != 0)
204 {
205 sprite->airborne = 0;
206 sprite->dy = 0;
207 sprite->y = bottom * 128;
208 }
209}
210
211static void mirai_fall_physics(struct mirai_sprite *sprite)
212{
213 int x0 = sprite->x;
214 int x1 = sprite->type->width / 2;
215 int y0 = sprite->y;
216
217 int bottom = y0 / 128;
218 int center = x0 / 128;
219 int left = (x0 - x1) / 128;
220 int right = (x0 + x1) / 128;
221
222 if (mirai_ground[bottom][left] == 0 && mirai_ground[bottom][right] == 0)
223 {
224 if (mirai_ground[bottom + 1][center] == 0)
225
226 sprite->airborne = 1;
227 else
228
229 sprite->y = (bottom + 1) * 128;
230 }
231}
232
233static void mirai_physics_dynamics(struct mirai_sprite *sprite)
234{
235 sprite->dx += sprite->ax;
236 sprite->x += sprite->dx;
237 sprite->y += sprite->dy;
238}
239
240static void mirai_ground_physics(struct mirai_sprite *sprite)
241{
242 sprite->dx *= 7;
243 sprite->dx /= 8;
244 sprite->dy = 0;
245
246 mirai_physics_dynamics(sprite);
247
248 mirai_step_physics(sprite);
249 mirai_wall_physics(sprite);
250 mirai_fall_physics(sprite);
251}
252
253static void mirai_airborne_physics(struct mirai_sprite *sprite)
254{
255 sprite->dx *= 15;
256 sprite->dx /= 16;
257 sprite->dy += mirai_gravity;
258
259 mirai_physics_dynamics(sprite);
260
261 mirai_ceiling_physics(sprite);
262 mirai_wall_physics(sprite);
263 mirai_landing_physics(sprite);
264}
265
266static void mirai_jump(struct mirai_sprite *sprite)
267{
268 if (sprite->airborne) return;
269 sprite->airborne = 1;
270 sprite->dy = -64;
271}
272
273static void mirai_damage(struct mirai_game *game, struct mirai_sprite *sprite)
274{
275
276 struct mirai_image area = {sprite->attack_width / 16, sprite->attack_height / 8};
277 int x = sprite->x + sprite->attack_width / 4;
278 int y = (sprite->y + sprite->attack_y);
279
280 if (sprite->moving_direction == 1) x -= sprite->attack_width / 2;
281
282 mirai_stamp(game, x, y, &area);
283 */
284}
285
286static void mirai_attack(struct mirai_game *game, struct mirai_sprite *sprite)
287{
288 sprite->attack_y = -16;
289 sprite->attack_width = 512;
290 sprite->attack_height = 256;
291 mirai_damage(game, sprite);
292}
293
294static void mirai_rapid_attack(struct mirai_game *game, struct mirai_sprite *sprite)
295{
296 sprite->attack_y = -128;
297 sprite->attack_width = sprite->attack_time * 16 + 64;
298 sprite->attack_height = 64;
299 if (sprite->attack_time != 0x7F) sprite->attack_time++;
300 mirai_damage(game, sprite);
301}
302
303static unsigned char mirai_count_oscillations(unsigned int history)
304{
305 unsigned char count = 0;
306 unsigned char prev = history&1;
307 for (int i = 0 ; i < 16 ; i++)
308 {
309 if ((history&1) != prev) count++;
310 prev = history&1;
311 history >>= 1;
312 }
313 return count;
314}
315
316static void mirai_controls(struct mirai_game *game, struct mirai_sprite *sprite)
317{
318 unsigned char left_oscillations = mirai_count_oscillations(game->left_history);
319 unsigned char right_oscillations = mirai_count_oscillations(game->right_history);
320
321 switch (sprite->moving_direction)
322 {
323 case 0:
324 if ((game->left_history&1) != 0)
325 sprite->moving_direction = 1;
326 if ((game->right_history&1) != 0)
327 sprite->moving_direction = 2;
328 break;
329 case 1:
330 if ((game->left_history&1) == 0)
331 sprite->moving_direction = 0;
332 else if (left_oscillations > 1)
333 mirai_jump(sprite);
334 if ((game->right_history&1) != 0)
335 {
336 if (right_oscillations == 0)
337 mirai_rapid_attack(game, sprite);
338 else
339 sprite->attack_time = 0;
340 }
341 if ((game->right_history&1) != 0 && (game->right_history&2) == 0)
342 mirai_attack(game, sprite);
343 break;
344 case 2:
345 if ((game->right_history&1) == 0)
346 sprite->moving_direction = 0;
347 else if (right_oscillations > 1)
348 mirai_jump(sprite);
349 if ((game->left_history&1) != 0)
350 {
351 if (left_oscillations == 0)
352 mirai_rapid_attack(game, sprite);
353 else
354 sprite->attack_time = 0;
355 }
356 if ((game->left_history&1) != 0 && (game->left_history&2) == 0)
357 mirai_attack(game, sprite);
358 break;
359 }
360}
361
362static void mirai_spawn(struct mirai_game *game, int x, int y, struct mirai_sprite_type *type, void (*behave)(struct mirai_game *game, struct mirai_sprite *sprite))
363{
364 if (game->sprite_count == 0xFF) return;
365 struct mirai_sprite *sprite = game->sprites + game->sprite_count;
366 game->sprite_count++;
367 sprite->type = type;
368 sprite->x = x;
369 sprite->y = y;
370 sprite->dx = 0;
371 sprite->dy = 0;
372 sprite->ax = 0;
373 sprite->airborne = 1;
374 sprite->moving_direction = 0;
375 sprite->last_moving_direction = 1;
376 sprite->attack_time = 0;
377 sprite->animation_time = 0;
378 sprite->behave = behave;
379}
380
381static void mirai_image_rotation(struct mirai_image *image, struct mirai_rotating_image *rotating_image, unsigned char angle, int x0, int y0, int tilt, int tilt_origin, char flip)
382{
383 if (flip != 0) angle = 0x100 - angle;
384
385 int width = image->width;
386 int height = image->height;
387
388 for (int y = 0 ; y < rotating_image->count ; y++)
389 for (int x = 0 ; x < rotating_image->rows[y].size ; x++)
390 {
391 int count = rotating_image->rows[y].count;
392
393 int w = rotating_image->rows[y].size;
394 int h = rotating_image->count;
395
396 unsigned int a = angle;
397 a *= count;
398 a += 128;
399 a /= 256;
400 a += x;
401 a %= count;
402
403 if (flip) a = count - a - 1;
404
405 int x1 = x + x0 + (width - w) / 2 + tilt * (tilt_origin - y) / 256;
406 int y1 = y - y0 + height - h;
407
408 unsigned char color = rotating_image->rows[y].colors[a];
409 if (color == 0) continue;
410
411 image->colors[x1 + y1 * width] = color;
412 }
413}
414
415static void mirai_mirai_animation_frame(struct mirai_image *image, unsigned char **colors_pointer, int angle, int arm_tilt)
416{
417 unsigned char *colors = *colors_pointer;
418
419 image->colors = colors;
420 image->width = 26;
421 image->height = 36;
422
423 (*colors_pointer) += image->width * image->height;
424
425 for (int x = 0 ; x < image->width ; x++)
426 for (int y = 0 ; y < image->height ; y++)
427 image->colors[x + y * image->width] = 0;
428
429 int tilt = (64 - angle);
430 if (tilt < 0) tilt += 12;
431 else tilt -= 12;
432
433 int x = mirai_sine[angle];
434 x *= 5;
435 x /= 128;
436 int left_x = x + (mirai_mirai_left_arm.count + 10) * tilt / 256;
437 int right_x = x - (mirai_mirai_right_arm.count + 10) * tilt / 256;
438
439 int height = mirai_mirai_torso.count;
440
441 mirai_image_rotation(image, &mirai_mirai_hair, angle, 0, 7, tilt, height, 1);
442 if ((angle + 64) % 256 < 128)
443 {
444 mirai_image_rotation(image, &mirai_mirai_left_arm, angle, left_x, 10, tilt - arm_tilt, 0, 0);
445 mirai_image_rotation(image, &mirai_mirai_torso, angle, 0, 0, tilt, height, 0);
446 mirai_image_rotation(image, &mirai_mirai_right_arm, angle, -right_x, 10, tilt + arm_tilt, 0, 0);
447 }
448 else
449 {
450 mirai_image_rotation(image, &mirai_mirai_right_arm, angle, -right_x, 10, tilt + arm_tilt, 0, 0);
451 mirai_image_rotation(image, &mirai_mirai_torso, angle, 0, 0, tilt, height, 0);
452 mirai_image_rotation(image, &mirai_mirai_left_arm, angle, left_x, 10, tilt - arm_tilt, 0, 0);
453 }
454 mirai_image_rotation(image, &mirai_mirai_hair, angle, 0, 7, tilt, height, 0);
455}
456
457static void mirai_mirai_animation(struct mirai_animation *animation, struct mirai_image **images, unsigned char **colors, int angle)
458{
459 animation->count = 8;
460 animation->images = *images;
461
462 int tilt = (64 - angle);
463 if (tilt < 0) tilt += 12;
464 else tilt -= 12;
465
466 tilt *= 4;
467
468 mirai_mirai_animation_frame((*images)++, colors, angle, 0);
469 mirai_mirai_animation_frame((*images)++, colors, angle, tilt / 2);
470 mirai_mirai_animation_frame((*images)++, colors, angle, tilt);
471 mirai_mirai_animation_frame((*images)++, colors, angle, tilt / 2);
472 mirai_mirai_animation_frame((*images)++, colors, angle, 0);
473 mirai_mirai_animation_frame((*images)++, colors, angle, -tilt / 2);
474 mirai_mirai_animation_frame((*images)++, colors, angle, -tilt);
475 mirai_mirai_animation_frame((*images)++, colors, angle, -tilt / 2);
476}
477
478
479
480
481int mirai_game_size = sizeof (struct mirai_game);
482int mirai_width = 512;
483int mirai_height = 256;
484
485
486
487
488void mirai_start(struct mirai_game *game, struct mirai_engine *engine)
489{
490 game->engine = *engine;
491 game->engine.stamp = engine->stamp;
492
493 game->left_history = 0;
494 game->right_history = 0;
495 game->sprite_count = 0;
496
497 for (int y = 0 ; y < sizeof mirai_ground / sizeof *mirai_ground ; y++)
498 for (int x = 0 ; x < sizeof *mirai_ground / sizeof **mirai_ground ; x++)
499 {
500 if (mirai_ground[y][x] == 2)
501 {
502 game->camera_x = x * 128;
503 game->camera_y = y * 128 - 512;
504 game->camera_sprite = game->sprites + game->sprite_count;
505 mirai_spawn(game, x * 128, y * 128, &mirai_mirai, &mirai_controls);
506 }
507 }
508
509 mirai_assets();
510}
511
512void mirai_step(struct mirai_game *game, struct mirai_keys keys)
513{
514 game->left_history <<= 1;
515 game->left_history |= keys.left;
516 game->right_history <<= 1;
517 game->right_history |= keys.right;
518
519 game->camera_x *= 3;
520 game->camera_y *= 3;
521 game->camera_x += game->camera_sprite->x;
522 game->camera_y += game->camera_sprite->y - 512;
523 game->camera_x /= 4;
524 game->camera_y /= 4;
525
526 for (int x0 = -16 ; x0 <= 16 ; x0++)
527 for (int y0 = -8 ; y0 <= 8 ; y0++)
528 {
529 int x = x0 + game->camera_x / 128;
530 int y = y0 + game->camera_y / 128;
531 if (mirai_ground[y][x] != 0)
532 mirai_stamp(game, x * 128 + 64, y * 128 + 128, mirai_ground_image);
533 }
534
535 for (int i = 0 ; i < game->sprite_count ; i++)
536 {
537 struct mirai_sprite *sprite = game->sprites + i;
538
539 (*sprite->behave)(game, sprite);
540
541 if (sprite->moving_direction != 0)
542 sprite->last_moving_direction = sprite->moving_direction;
543
544 sprite->ax = 0;
545 if (sprite->airborne)
546 {
547 if (sprite->moving_direction == 1)
548 sprite->ax = -3;
549 if (sprite->moving_direction == 2)
550 sprite->ax = 3;
551 }
552 else
553 {
554 if (sprite->moving_direction == 1)
555 sprite->ax = -4;
556 if (sprite->moving_direction == 2)
557 sprite->ax = 4;
558 }
559
560 if (sprite->airborne == 0)
561 mirai_ground_physics(sprite);
562 else
563 mirai_airborne_physics(sprite);
564
565 int dx;
566 struct mirai_direction_animation_set *animations;
567
568 if (sprite->last_moving_direction == 1)
569 {
570 dx = -sprite->dx;
571 animations = &sprite->type->animations.left;
572 }
573 if (sprite->last_moving_direction == 2)
574 {
575 dx = sprite->dx;
576 animations = &sprite->type->animations.right;
577 }
578
579 struct mirai_animation *animation;
580
581 animation = &animations->sprinting;
582 if (dx < 16) animation = &animations->walking;
583 if (dx < 12) animation = &animations->moving;
584 if (dx < 8) animation = &animations->idling;
585
586 mirai_stamp(game, sprite->x, sprite->y, animation->images + sprite->animation_time * animation->count / 256);
587
588 sprite->animation_time += dx / 4;
589 }
590}
591
592struct mirai_image *mirai_assets(void)
593{
594 static char done = 0;
595 if (done != 0) return mirai_images;
596
597 int image_count = sizeof mirai_images / sizeof *mirai_images;
598 struct mirai_image *last_image = mirai_images + image_count;
599
600 int color_count = sizeof mirai_colors / sizeof *mirai_colors;
601 unsigned char *last_color = mirai_colors + color_count;
602
603 last_image->width = 0;
604 last_image->height = 0;
605
606 for (int i = 0 ; i < 256 ; i++)
607 mirai_ground_colors[i] = 0x3B;
608
609 struct mirai_image *images = mirai_images + 1;
610 unsigned char *colors = mirai_colors;
611
612 mirai_mirai_animation(&mirai_mirai.animations.left.idling, &images, &colors, 64 + 12);
613 mirai_mirai_animation(&mirai_mirai.animations.left.moving, &images, &colors, 64 + 16);
614 mirai_mirai_animation(&mirai_mirai.animations.left.walking, &images, &colors, 64 + 32);
615 mirai_mirai_animation(&mirai_mirai.animations.left.sprinting, &images, &colors, 64 + 48);
616
617 mirai_mirai_animation(&mirai_mirai.animations.right.idling, &images, &colors, 64 - 12);
618 mirai_mirai_animation(&mirai_mirai.animations.right.moving, &images, &colors, 64 - 16);
619 mirai_mirai_animation(&mirai_mirai.animations.right.walking, &images, &colors, 64 - 32);
620 mirai_mirai_animation(&mirai_mirai.animations.right.sprinting, &images, &colors, 64 - 48);
621
622 if (images != last_image - 1) return 0;
623 if (colors != last_color) return 0;
624
625 done = 1;
626 return mirai_images;
627}