Mirai's Miscellaneous Misadventures

M55 / core / sprites.c

1/* license: AGPLv3 or later */
2/* copyright 2024 zamfofex */
3
4#include <stdlib.h>
5#include "../mimimi.h"
6
7static int mimimi_wall_tile(struct mimimi_ground *ground, int x, int y)
8{
9	if (mimimi_ground_tile(ground, x, y + 1) == 0 && mimimi_ground_tile(ground, x, y - 1) == 0) return 0;
10	return mimimi_ground_tile(ground, x, y);
11}
12
13static void mimimi_wall_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
14{
15	int x0, y0;
16	int x1, y1;
17	int left, right;
18	int top;
19	
20	x0 = position->x;
21	y0 = position->y;
22	x1 = physics->width / 2;
23	y1 = physics->height;
24	
25	top = mimimi_div_down(y0 - y1, 128);
26	left = mimimi_div_down(x0 - x1, 128);
27	right = mimimi_div_down(x0 + x1, 128);
28	
29	if (mimimi_wall_tile(ground, left, top) != 0) {
30		if (physics->dx < 0) physics->dx = 0;
31		position->x = (left + 1) * 128 + x1;
32	}
33	
34	if (mimimi_wall_tile(ground, right, top) != 0) {
35		if (physics->dx > 0) physics->dx = 0;
36		position->x = right * 128 - x1 - 1;
37	}
38}
39
40static void mimimi_slope_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
41{
42	int x0, y0;
43	int x1, y1;
44	int top, bottom;
45	int left, right;
46	
47	x0 = position->x;
48	y0 = position->y;
49	x1 = physics->width / 2;
50	y1 = physics->height;
51	
52	top = mimimi_div_down(y0 - y1, 128);
53	bottom = mimimi_div_down(y0 - 1, 128);
54	left = mimimi_div_down(x0 - x1, 128);
55	right = mimimi_div_down(x0 + x1, 128);
56	
57	if (mimimi_ground_tile(ground, left, bottom) != 0) {
58		if (mimimi_ground_tile(ground, left, top) == 0) {
59			position->y = bottom * 128;
60			if (physics->sprite != NULL) physics->sprite->trail = 0xFF;
61		}
62	}
63	
64	if (mimimi_ground_tile(ground, right, bottom) != 0) {
65		if (mimimi_ground_tile(ground, right, top) == 0) {
66			position->y = bottom * 128;
67			if (physics->sprite != NULL) physics->sprite->trail = 0xFF;
68		}
69	}
70}
71
72static void mimimi_ceiling_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
73{
74	int x0, y0;
75	int x1, y1;
76	int top;
77	int left, right;
78	
79	x0 = position->x;
80	y0 = position->y;
81	x1 = physics->width / 2;
82	y1 = physics->height;
83	
84	top = mimimi_div_down(y0 - y1, 128);
85	left = mimimi_div_down(x0 - x1, 128);
86	right = mimimi_div_down(x0 + x1, 128);
87	
88	if (mimimi_wall_tile(ground, left, top) != 0 && mimimi_wall_tile(ground, right, top) != 0) {
89		if (physics->dy < 0) physics->dy = 0;
90		position->y = (top + 1) * 128 + y1;
91	}
92}
93
94static void mimimi_landing_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
95{
96	int x0, y0;
97	int x1, y1;
98	int left, right;
99	int bottom, bottom1;
100	unsigned char tile1, tile2;
101	int restitution;
102	
103	if (physics->dy < 0) return;
104	
105	y0 = position->y - mimimi_div(physics->dy, 256) - 1;
106	x0 = position->x;
107	x1 = physics->width / 2;
108	y1 = position->y;
109	
110	bottom = mimimi_div_down(y0, 128);
111	bottom1 = mimimi_div_down(y1, 128);
112	left = mimimi_div_down(x0 - x1, 128);
113	right = mimimi_div_down(x0 + x1, 128);
114	
115	while (bottom <= bottom1) {
116		
117		tile1 = mimimi_ground_tile(ground, left, bottom);
118		tile2 = mimimi_ground_tile(ground, right, bottom);
119		if (tile1 == 0 && tile2 == 0) {
120			bottom++;
121			continue;
122		}
123		
124		if (ground->restitution[tile1] < ground->restitution[tile2]) {
125			restitution = ground->restitution[tile1];
126		}
127		else {
128			restitution = ground->restitution[tile2];
129		}
130		
131		position->y = bottom * 128;
132		physics->dy = -mimimi_div(physics->dy * restitution, 256);
133		if (physics->dy > -2048) {
134			physics->airborne = 0;
135			physics->dy = 0;
136		}
137		
138		break;
139	}
140}
141
142static void mimimi_fall_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
143{
144	int x0, y0;
145	int x1;
146	int bottom, bottom2;
147	int center;
148	int left, right;
149	
150	x0 = position->x;
151	y0 = position->y;
152	x1 = physics->width / 2;
153	
154	bottom = mimimi_div_down(y0, 128);
155	bottom2 = bottom + 1;
156	center = mimimi_div_down(x0, 128);
157	left = mimimi_div_down(x0 - x1, 128);
158	right = mimimi_div_down(x0 + x1, 128);
159	
160	if (mimimi_ground_tile(ground, left, bottom) == 0 && mimimi_ground_tile(ground, right, bottom) == 0) {
161		if (mimimi_ground_tile(ground, center, bottom2) == 0) {
162			physics->airborne = 1;
163		}
164		else {
165			if (physics->sprite != NULL) physics->sprite->trail = 0xFF;
166			position->y = bottom2 * 128;
167		}
168	}
169}
170
171static void mimimi_physics_dynamics(struct mimimi_physics *physics, struct mimimi_position *position)
172{
173	position->x += mimimi_div(physics->dx, 256);
174	position->y += mimimi_div(physics->dy, 256);
175}
176
177static void mimimi_ground_collision_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
178{
179	mimimi_slope_physics(physics, position, ground);
180	mimimi_wall_physics(physics, position, ground);
181	mimimi_fall_physics(physics, position, ground);
182	if (physics->airborne == 0) position->y = mimimi_div_down(position->y, 128) * 128;
183}
184
185static void mimimi_airborne_collision_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
186{
187	mimimi_ceiling_physics(physics, position, ground);
188	mimimi_wall_physics(physics, position, ground);
189	mimimi_landing_physics(physics, position, ground);
190}
191
192static void mimimi_ground_physics(struct mimimi_physics *physics)
193{
194	physics->dx -= mimimi_div(physics->dx, 5);
195	physics->dy = 0;
196}
197
198static void mimimi_airborne_physics(struct mimimi_physics *physics)
199{
200	physics->dy += physics->gravity;
201	physics->dx -= mimimi_div(physics->dx, 7);
202	physics->dy -= mimimi_div(physics->dy, 32);
203}
204
205void mimimi_collision_physics_tick(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
206{
207	if (physics->airborne == 0) mimimi_ground_collision_physics(physics, position, ground);
208	else mimimi_airborne_collision_physics(physics, position, ground);
209}
210
211void mimimi_physics_tick(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
212{
213	if (physics->airborne == 0) mimimi_ground_physics(physics);
214	else mimimi_airborne_physics(physics);
215	mimimi_physics_dynamics(physics, position);
216	mimimi_collision_physics_tick(physics, position, ground);
217}
218
219void mimimi_physics(struct mimimi_physics *physics, int width, int height)
220{
221	physics->dx = 0;
222	physics->dy = 0;
223	physics->airborne = 1;
224	physics->width = width;
225	physics->height = height;
226	physics->gravity = 2048;
227	physics->sprite = NULL;
228}
229
230static void mimimi_walk_tick(struct mimimi_walk *walk, struct mimimi_physics *physics)
231{
232	int ax;
233	
234	if (walk->direction == 0) return;
235	
236	if (physics->airborne == 0) ax = walk->ground_speed;
237	else ax = walk->airborne_speed;
238	
239	if (walk->direction == 1) physics->dx -= ax;
240	if (walk->direction == 2) physics->dx += ax;
241}
242
243void mimimi_sprite(struct mimimi_sprite *sprite, struct mimimi_model *model, struct mimimi_ground *ground, int x, int y, int width, int height)
244{
245	sprite->position.x = x;
246	sprite->position.y = y;
247	
248	sprite->trailing_y = y;
249	sprite->trail = 0;
250	
251	sprite->ground = ground;
252	mimimi_physics(&sprite->physics, width, height);
253	sprite->physics.sprite = sprite;
254	
255	sprite->walk.ground_speed = 4096;
256	sprite->walk.airborne_speed = 2048;
257	sprite->walk.direction = 0;
258	
259	sprite->offset.x = 0;
260	sprite->offset.y = 0;
261	
262	sprite->animation_time = 0;
263	sprite->landing_time = 0;
264	sprite->direction = 1;
265	sprite->model = model;
266}
267
268void mimimi_sprite_tick(struct mimimi_sprite *sprite)
269{
270	mimimi_walk_tick(&sprite->walk, &sprite->physics);
271	sprite->position.x -= sprite->offset.x;
272	sprite->position.y -= sprite->offset.y;
273	mimimi_physics_tick(&sprite->physics, &sprite->position, sprite->ground);
274	sprite->position.x += sprite->offset.x;
275	sprite->position.y += sprite->offset.y;
276}
277
278void mimimi_jump(void *data)
279{
280	struct mimimi_sprite *sprite;
281	sprite = data;
282	if (sprite->physics.airborne != 0) return;
283	sprite->physics.airborne = 1;
284	sprite->physics.dy = -0x6800;
285}