Mirai's Miscellaneous Misadventures

M45 / 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_physics(struct mimimi_physics *physics, int width, int height)
222{
223	physics->dx = 0;
224	physics->dy = 0;
225	physics->airborne = 1;
226	physics->width = width;
227	physics->height = height;
228	physics->gravity = 3;
229}
230
231void mimimi_life_tick(struct mimimi_life *life)
232{
233	if (life->knocked_time != 0)
234	{
235		life->knocked_time--;
236		life->immunity_time = life->recovery_time;
237	}
238	
239	if (life->immunity_time != 0)
240	{
241		life->pristinity = life->max_pristinity;
242		life->immunity_time--;
243	}
244	
245	if (life->pristinity < life->max_pristinity)
246		life->pristinity++;
247}
248
249void mimimi_fall_tick(struct mimimi_fall *fall, struct mimimi_physics *physics, struct mimimi_life *life)
250{
251	if (physics->dy > fall->max_speed && life->knocked_time < fall->knocked_time)
252		life->knocked_time = fall->knocked_time;
253}
254
255void mimimi_walk_tick(struct mimimi_walk *walk, struct mimimi_physics *physics)
256{
257	int ax;
258	
259	if (walk->direction == 0) return;
260	
261	if (physics->airborne == 0)
262		ax = walk->ground_speed;
263	else
264		ax = walk->airborne_speed;
265	
266	if (walk->direction == 1)
267		physics->dx -= ax;
268	if (walk->direction == 2)
269		physics->dx += ax;
270}
271
272void mimimi_sprite(struct mimimi_sprite *sprite, struct mimimi_ground *ground, int x, int y, int width, int height)
273{
274	sprite->position.x = x;
275	sprite->position.y = y;
276	
277	mimimi_physics(&sprite->physics, width, height);
278	
279	sprite->life.knocked_time = 0;
280	sprite->life.immunity_time = 0;
281	sprite->life.pristinity = 256;
282	sprite->life.max_pristinity = 256;
283	sprite->life.recovery_time = 64;
284	
285	sprite->fall.max_speed = 128;
286	sprite->fall.knocked_time = 64;
287	
288	sprite->walk.ground_speed = 6;
289	sprite->walk.airborne_speed = 3;
290	sprite->walk.direction = 0;
291	
292	sprite->offset.x = 0;
293	sprite->offset.y = 0;
294	
295	sprite->ground = ground;
296}
297
298void mimimi_sprite_tick(struct mimimi_sprite *sprite)
299{
300	struct mimimi_position position;
301	
302	mimimi_life_tick(&sprite->life);
303	if (sprite->life.knocked_time == 0) mimimi_walk_tick(&sprite->walk, &sprite->physics);
304	
305	position = sprite->position;
306	position.x -= sprite->offset.x;
307	position.y -= sprite->offset.y;
308	mimimi_physics_tick(&sprite->physics, &position, sprite->ground);
309	position.x += sprite->offset.x;
310	position.y += sprite->offset.y;
311	sprite->position = position;
312	
313	mimimi_fall_tick(&sprite->fall, &sprite->physics, &sprite->life);
314}
315
316void mimimi_jump(void *data)
317{
318	struct mimimi_sprite *sprite;
319	sprite = data;
320	if (sprite->life.knocked_time != 0) return;
321	if (sprite->physics.airborne != 0) return;
322	sprite->physics.airborne = 1;
323	sprite->physics.dy = -56;
324}
325
326void mimimi_offset_tick(struct mimimi_offset *offset, int x, int y)
327{
328	int i;
329	int count;
330	struct mimimi_offset_threshold *thresholds;
331	struct mimimi_sprite *sprite;
332	
333	count = offset->count;
334	sprite = offset->sprite;
335	thresholds = offset->thresholds;
336	
337	for (i = 0 ; i < count ; i++)
338	{
339		if (sprite->position.x < thresholds[i].x * 128)
340			break;
341	}
342	
343	sprite->offset = thresholds[i].offset;
344	sprite->offset.x -= x;
345	sprite->offset.y -= y;
346}