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