Mirai's Miscellaneous Misadventures

M43 / core / sprites.c

1/* license: AGPLv3 or later */
2/* copyright 2023 zamfofex */
3
4#include <mimimi.h>
5
6/*
7	todo: Physics behaves strangely near the left and top edges of a given ground.
8	That is because division rounds towards zero rather than towards negative infinity.
9	Is this worhtwhile fixing?
10	It is usually easy to avoid this by simply having the leftmost and topmost edges be zeroed out or inaccessible.
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, bottom;
19	int top2, bottom2;
20	
21	x0 = position->x;
22	y0 = position->y;
23	x1 = physics->width / 2;
24	y1 = physics->height;
25	
26	top = (y0 - y1) / 128;
27	top2 = top - 1;
28	bottom = (y0 - 127) / 128;
29	bottom2 = bottom - 1;
30	left = (x0 - x1) / 128;
31	right = (x0 + x1) / 128;
32	
33	if (
34		(mimimi_ground_tile(ground, left, bottom) != 0 && mimimi_ground_tile(ground, left, bottom2) != 0) ||
35		(mimimi_ground_tile(ground, left, top) != 0 && mimimi_ground_tile(ground, left, top2) != 0)
36	)
37	{
38		if (physics->dx < 0) physics->dx = 0;
39		position->x = (left + 1) * 128 + x1;
40	}
41	
42	if (
43		(mimimi_ground_tile(ground, right, bottom) != 0 && mimimi_ground_tile(ground, right, bottom2) != 0) ||
44		(mimimi_ground_tile(ground, right, top) != 0 && mimimi_ground_tile(ground, right, top2) != 0)
45	)
46	{
47		if (physics->dx > 0) physics->dx = 0;
48		position->x = right * 128 - x1 - 1;
49	}
50}
51
52static void mimimi_slope_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
53{
54	int x0, y0;
55	int x1, y1;
56	int top, bottom;
57	int left, right;
58	
59	x0 = position->x;
60	y0 = position->y;
61	x1 = physics->width / 2;
62	y1 = physics->height;
63	
64	top = (y0 - y1) / 128;
65	bottom = (y0 - 127) / 128;
66	left = (x0 - x1) / 128;
67	right = (x0 + x1) / 128;
68	
69	if (mimimi_ground_tile(ground, left, bottom) != 0)
70	if (mimimi_ground_tile(ground, left, top) == 0)
71		position->y = bottom * 128;
72	
73	if (mimimi_ground_tile(ground, right, bottom) != 0)
74	if (mimimi_ground_tile(ground, right, top) == 0)
75		position->y = bottom * 128;
76}
77
78static void mimimi_ceiling_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
79{
80	int x0, y0;
81	int x1, y1;
82	int top, top2;
83	int left, right;
84	
85	x0 = position->x;
86	y0 = position->y;
87	x1 = physics->width / 2;
88	y1 = physics->height;
89	
90	top = (y0 - y1) / 128;
91	top2 = top - 1;
92	left = (x0 - x1) / 128;
93	right = (x0 + x1) / 128;
94	
95	if (mimimi_ground_tile(ground, left, top) != 0 && mimimi_ground_tile(ground, right, top) != 0)
96	if (mimimi_ground_tile(ground, left, top2) != 0 || mimimi_ground_tile(ground, right, top2) != 0)
97	{
98		if (physics->dy < 0) physics->dy = 0;
99		position->y = (top + 1) * 128 + y1;
100	}
101}
102
103static void mimimi_landing_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
104{
105	int x0, y0;
106	int x1;
107	int left, right;
108	int bottom;
109	
110	x0 = position->x;
111	y0 = position->y;
112	x1 = physics->width / 2;
113	
114	bottom = y0 / 128;
115	left = (x0 - x1) / 128;
116	right = (x0 + x1) / 128;
117	
118	if (physics->dy < 0) return;
119	
120	if (mimimi_ground_tile(ground, left, bottom) != 0 || mimimi_ground_tile(ground, right, bottom) != 0)
121	{
122		physics->airborne = 0;
123		physics->dy = 0;
124		position->y = bottom * 128;
125	}
126}
127
128static void mimimi_fall_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
129{
130	int x0, y0;
131	int x1;
132	int bottom, bottom2;
133	int center;
134	int left, right;
135	
136	x0 = position->x;
137	y0 = position->y;
138	x1 = physics->width / 2;
139	
140	bottom = y0 / 128;
141	bottom2 = bottom + 1;
142	center = x0 / 128;
143	left = (x0 - x1) / 128;
144	right = (x0 + x1) / 128;
145	
146	if (mimimi_ground_tile(ground, left, bottom) == 0 && mimimi_ground_tile(ground, right, bottom) == 0)
147	{
148		if (mimimi_ground_tile(ground, center, bottom2) == 0)
149			/* fall */
150			physics->airborne = 1;
151		else
152			/* step down */
153			position->y = bottom2 * 128;
154	}
155}
156
157static void mimimi_physics_dynamics(struct mimimi_physics *physics, struct mimimi_position *position)
158{
159	position->x += physics->dx;
160	position->y += physics->dy;
161}
162
163static void mimimi_ground_collision_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
164{
165	mimimi_slope_physics(physics, position, ground);
166	mimimi_wall_physics(physics, position, ground);
167	mimimi_fall_physics(physics, position, ground);
168	
169	if (physics->airborne == 0)
170	{
171		position->y /= 128;
172		position->y *= 128;
173	}
174}
175
176static void mimimi_airborne_collision_physics(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
177{
178	mimimi_ceiling_physics(physics, position, ground);
179	mimimi_wall_physics(physics, position, ground);
180	mimimi_landing_physics(physics, position, ground);
181}
182
183static void mimimi_ground_physics(struct mimimi_physics *physics, struct mimimi_position *position)
184{
185	physics->dx *= 5;
186	physics->dx /= 6;
187	physics->dy = 0;
188	mimimi_physics_dynamics(physics, position);
189}
190
191static void mimimi_airborne_physics(struct mimimi_physics *physics, struct mimimi_position *position)
192{
193	physics->dx *= 17;
194	physics->dx /= 18;
195	physics->dy += physics->gravity;
196	mimimi_physics_dynamics(physics, position);
197}
198
199void mimimi_collision_physics_tick(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
200{
201	if (physics->airborne == 0)
202		mimimi_ground_collision_physics(physics, position, ground);
203	else
204		mimimi_airborne_collision_physics(physics, position, ground);
205}
206
207void mimimi_dynamics_tick(struct mimimi_physics *physics, struct mimimi_position *position)
208{
209	if (physics->airborne == 0)
210		mimimi_ground_physics(physics, position);
211	else
212		mimimi_airborne_physics(physics, position);
213}
214
215void mimimi_physics_tick(struct mimimi_physics *physics, struct mimimi_position *position, struct mimimi_ground *ground)
216{
217	mimimi_dynamics_tick(physics, position);
218	mimimi_collision_physics_tick(physics, position, ground);
219}
220
221void mimimi_life_tick(struct mimimi_life *life)
222{
223	if (life->knocked_time != 0)
224	{
225		life->knocked_time--;
226		life->immunity_time = life->recovery_time;
227	}
228	
229	if (life->immunity_time != 0)
230	{
231		life->pristinity = life->max_pristinity;
232		life->immunity_time--;
233	}
234	
235	if (life->pristinity < life->max_pristinity)
236		life->pristinity++;
237}
238
239void mimimi_fall_tick(struct mimimi_fall *fall, struct mimimi_physics *physics, struct mimimi_life *life)
240{
241	if (physics->dy > fall->max_speed && life->knocked_time < fall->knocked_time)
242		life->knocked_time = fall->knocked_time;
243}
244
245void mimimi_walk_tick(struct mimimi_walk *walk, struct mimimi_physics *physics)
246{
247	int ax;
248	
249	if (walk->direction == 0) return;
250	
251	if (physics->airborne == 0)
252		ax = walk->ground_speed;
253	else
254		ax = walk->airborne_speed;
255	
256	if (walk->direction == 1)
257		physics->dx -= ax;
258	if (walk->direction == 2)
259		physics->dx += ax;
260}
261
262void mimimi_sprite(struct mimimi_sprite *sprite, struct mimimi_ground *ground, int x, int y, int width, int height)
263{
264	sprite->position.x = x;
265	sprite->position.y = y;
266	
267	sprite->physics.dx = 0;
268	sprite->physics.dy = 0;
269	sprite->physics.airborne = 1;
270	sprite->physics.width = width;
271	sprite->physics.height = height;
272	sprite->physics.gravity = 3;
273	
274	sprite->life.knocked_time = 0;
275	sprite->life.immunity_time = 0;
276	sprite->life.pristinity = 256;
277	sprite->life.max_pristinity = 256;
278	sprite->life.recovery_time = 64;
279	
280	sprite->fall.max_speed = 128;
281	sprite->fall.knocked_time = 64;
282	
283	sprite->walk.ground_speed = 6;
284	sprite->walk.airborne_speed = 3;
285	sprite->walk.direction = 0;
286	
287	sprite->ground = ground;
288}
289
290void mimimi_sprite_tick(struct mimimi_sprite *sprite)
291{
292	mimimi_life_tick(&sprite->life);
293	if (sprite->life.knocked_time == 0) mimimi_walk_tick(&sprite->walk, &sprite->physics);
294	mimimi_physics_tick(&sprite->physics, &sprite->position, sprite->ground);
295	mimimi_fall_tick(&sprite->fall, &sprite->physics, &sprite->life);
296}
297
298void mimimi_jump(void *data)
299{
300	struct mimimi_sprite *sprite;
301	sprite = data;
302	if (sprite->life.knocked_time != 0) return;
303	if (sprite->physics.airborne != 0) return;
304	sprite->physics.airborne = 1;
305	sprite->physics.dy = -56;
306}