Mirai's Miscellaneous Misadventures

M53 / core / sprites.c

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