]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player/rigid_rect.c
(#348) Stringify MAX_ID_SIZE
[nothing.git] / src / game / level / player / rigid_rect.c
1 #include <SDL2/SDL.h>
2 #include <assert.h>
3 #include <stdio.h>
4 #include <string.h>
5
6 #include "color.h"
7 #include "game/level/boxes.h"
8 #include "game/level/solid.h"
9 #include "math/minmax.h"
10 #include "rigid_rect.h"
11 #include "system/error.h"
12 #include "system/lt.h"
13
14 #define MAX_ID_SIZE 36
15 #define STRINGIFY(x) STRINGIFY2(x)
16 #define STRINGIFY2(x) #x
17
18 struct Rigid_rect {
19     Lt *lt;
20     char *id;
21     Vec position;
22     Vec velocity;
23     Vec movement;
24     Vec size;
25     Color color;
26     int touches_ground;
27     Vec forces;
28 };
29
30 static const Vec opposing_rect_side_forces[RECT_SIDE_N] = {
31     { .x = 1.0f,  .y =  0.0f  },  /* RECT_SIDE_LEFT = 0, */
32     { .x = -1.0f, .y =  0.0f  },  /* RECT_SIDE_RIGHT, */
33     { .x = 0.0f,  .y =  1.0f, },  /* RECT_SIDE_TOP, */
34     { .x = 0.0f,  .y = -1.0f, }   /* RECT_SIDE_BOTTOM, */
35 };
36
37 static Vec opposing_force_by_sides(int sides[RECT_SIDE_N])
38 {
39     Vec opposing_force = {
40         .x = 0.0f,
41         .y = 0.0f
42     };
43
44     for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
45         if (sides[side]) {
46             vec_add(
47                 &opposing_force,
48                 opposing_rect_side_forces[side]);
49         }
50     }
51
52     return opposing_force;
53 }
54
55 Rigid_rect *create_rigid_rect(Rect rect, Color color, const char *id)
56 {
57     assert(id);
58
59     Lt *lt = create_lt();
60
61     if (lt == NULL) {
62         return NULL;
63     }
64
65     Rigid_rect *rigid_rect = PUSH_LT(lt, malloc(sizeof(Rigid_rect)), free);
66     if (rigid_rect == NULL) {
67         throw_error(ERROR_TYPE_LIBC);
68         RETURN_LT(lt, NULL);
69     }
70     rigid_rect->lt = lt;
71
72     rigid_rect->id = malloc(sizeof(char) * MAX_ID_SIZE);
73     if (rigid_rect->id == NULL) {
74         throw_error(ERROR_TYPE_LIBC);
75         RETURN_LT(lt, NULL);
76     }
77
78     const size_t len_id = min_size(MAX_ID_SIZE - 1, strlen(id));
79     memcpy(rigid_rect->id, id, len_id);
80     rigid_rect->id[len_id] = 0;
81
82     rigid_rect->position = vec(rect.x, rect.y);
83     rigid_rect->velocity = vec(0.0f, 0.0f);
84     rigid_rect->movement = vec(0.0f, 0.0f);
85     rigid_rect->size = vec(rect.w, rect.h);
86     rigid_rect->color = color;
87     rigid_rect->touches_ground = 0;
88     rigid_rect->forces = vec(0.0f, 0.0f);
89
90     return rigid_rect;
91 }
92
93 Rigid_rect *create_rigid_rect_from_stream(FILE *stream)
94 {
95     assert(stream);
96
97     char color[7];
98     Rect rect;
99     char id[MAX_ID_SIZE];
100
101     if (fscanf(stream, "%" STRINGIFY(MAX_ID_SIZE) "s%f%f%f%f%6s\n",
102                id,
103                &rect.x, &rect.y,
104                &rect.w, &rect.h,
105                color) < 0) {
106         throw_error(ERROR_TYPE_LIBC);
107         return NULL;
108     }
109
110     return create_rigid_rect(rect, color_from_hexstr(color), id);
111 }
112
113 void destroy_rigid_rect(Rigid_rect *rigid_rect)
114 {
115     RETURN_LT0(rigid_rect->lt);
116 }
117
118 Solid_ref rigid_rect_as_solid(Rigid_rect *rigid_rect)
119 {
120     const Solid_ref ref = {
121         .tag = SOLID_RIGID_RECT,
122         .ptr = rigid_rect
123     };
124
125     return ref;
126 }
127
128 void rigid_rect_touches_rect_sides(Rigid_rect *rigid_rect,
129                                    Rect object,
130                                    int sides[RECT_SIDE_N])
131 {
132     rect_object_impact(object, rigid_rect_hitbox(rigid_rect), sides);
133 }
134
135 int rigid_rect_render(const Rigid_rect *rigid_rect,
136                       Camera *camera)
137 {
138     if (camera_fill_rect(
139             camera,
140             rigid_rect_hitbox(rigid_rect),
141             rigid_rect->color) < 0) {
142         return -1;
143     }
144
145     return 0;
146 }
147
148 int rigid_rect_update(Rigid_rect * rigid_rect,
149                       float delta_time)
150 {
151     assert(rigid_rect);
152
153     rigid_rect->touches_ground = 0;
154
155     /* TODO(#207): rigid_rect floating in lava is broken */
156     rigid_rect->velocity = vec_sum(
157         rigid_rect->velocity,
158         vec_scala_mult(
159             rigid_rect->forces,
160             delta_time));
161
162     rigid_rect->position = vec_sum(
163         rigid_rect->position,
164         vec_scala_mult(
165             vec_sum(
166                 rigid_rect->velocity,
167                 rigid_rect->movement),
168             delta_time));
169
170     rigid_rect->forces = vec(0.0f, 0.0f);
171
172     return 0;
173 }
174
175 void rigid_rect_collide_with_solid(Rigid_rect * rigid_rect,
176                                    Solid_ref solid)
177 {
178     assert(rigid_rect);
179     assert(rigid_rect != solid.ptr);
180
181     int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
182
183     solid_touches_rect_sides(solid, rigid_rect_hitbox(rigid_rect), sides);
184
185     if (sides[RECT_SIDE_BOTTOM]) {
186         rigid_rect->touches_ground = 1;
187     }
188
189     Vec opforce_direction = opposing_force_by_sides(sides);
190
191     solid_apply_force(
192         solid,
193         vec_scala_mult(
194             vec_neg(vec_norm(opforce_direction)),
195             vec_length(
196                 vec_sum(
197                     rigid_rect->velocity,
198                     rigid_rect->movement)) * 8.0f));
199
200     if (fabs(opforce_direction.x) > 1e-6 && (opforce_direction.x < 0.0f) != ((rigid_rect->velocity.x + rigid_rect->movement.x) < 0.0f)) {
201         rigid_rect->velocity.x = 0.0f;
202         rigid_rect->movement.x = 0.0f;
203     }
204
205     if (fabs(opforce_direction.y) > 1e-6 && (opforce_direction.y < 0.0f) != ((rigid_rect->velocity.y + rigid_rect->movement.y) < 0.0f)) {
206         rigid_rect->velocity.y = 0.0f;
207         rigid_rect->movement.y = 0.0f;
208
209         if (vec_length(rigid_rect->velocity) > 1e-6) {
210             rigid_rect_apply_force(
211                 rigid_rect,
212                 vec_scala_mult(
213                     vec_neg(rigid_rect->velocity),
214                     16.0f));
215         }
216     }
217
218
219     for (int i = 0; i < 1000 && vec_length(opforce_direction) > 1e-6; ++i) {
220         rigid_rect->position = vec_sum(
221             rigid_rect->position,
222             vec_scala_mult(
223                 opforce_direction,
224                 1e-2f));
225
226         memset(sides, 0, sizeof(int) * RECT_SIDE_N);
227         solid_touches_rect_sides(
228             solid,
229             rigid_rect_hitbox(rigid_rect),
230             sides);
231         opforce_direction = opposing_force_by_sides(sides);
232     }
233 }
234
235 Rect rigid_rect_hitbox(const Rigid_rect *rigid_rect)
236 {
237     return rect_from_vecs(
238         rigid_rect->position,
239         rigid_rect->size);
240 }
241
242 void rigid_rect_move(Rigid_rect *rigid_rect,
243                            Vec movement)
244 {
245     rigid_rect->movement = movement;
246 }
247
248 int rigid_rect_touches_ground(const Rigid_rect *rigid_rect)
249 {
250     return rigid_rect->touches_ground;
251 }
252
253 void rigid_rect_apply_force(Rigid_rect * rigid_rect,
254                             Vec force)
255 {
256     rigid_rect->forces = vec_sum(rigid_rect->forces, force);
257 }
258
259 void rigid_rect_transform_velocity(Rigid_rect *rigid_rect,
260                                    mat3x3 trans_mat)
261 {
262     rigid_rect->velocity = point_mat3x3_product(rigid_rect->velocity,
263                                                 trans_mat);
264 }
265
266 void rigid_rect_teleport_to(Rigid_rect *rigid_rect,
267                             Vec position)
268 {
269     rigid_rect->position = position;
270 }
271
272 void rigid_rect_damper(Rigid_rect *rigid_rect, Vec v)
273 {
274     rigid_rect_apply_force(
275         rigid_rect,
276         vec(rigid_rect->velocity.x * v.x, rigid_rect->velocity.y * v.y));
277 }
278
279 bool rigid_rect_has_id(Rigid_rect *rigid_rect,
280                        const char *id)
281 {
282     assert(rigid_rect);
283     assert(id);
284
285     return strcmp(rigid_rect->id, id) == 0;
286 }