Mirai's Miscellaneous Misadventures

M9 / game.c

1#include "game.h"
2#include "math.c"
3
4// types
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	// speed
38	int dx, dy;
39	// acceleration
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// constants
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// state
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// functions
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			// fall
226			sprite->airborne = 1;
227		else
228			// step down
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// constant exports
480
481int mirai_game_size = sizeof (struct mirai_game);
482int mirai_width = 512;
483int mirai_height = 256;
484
485
486// function exports
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}