]> git.lizzy.rs Git - nothing.git/blob - src/game/level/goals.c
Fixed point issue
[nothing.git] / src / game / level / goals.c
1 #include <SDL2/SDL.h>
2 #include "system/stacktrace.h"
3 #include <math.h>
4
5 #include "goals.h"
6 #include "math/pi.h"
7 #include "math/triangle.h"
8 #include "system/str.h"
9 #include "system/line_stream.h"
10 #include "system/lt.h"
11 #include "system/nth_alloc.h"
12 #include "system/log.h"
13 #include "ebisp/interpreter.h"
14 #include "broadcast.h"
15 #include "game/level/level_editor/point_layer.h"
16
17 #define GOAL_RADIUS 10.0f
18 #define GOAL_MAX_ID_SIZE 36
19
20 static int goals_is_goal_hidden(const Goals *goals, size_t i);
21
22 typedef enum Cue_state {
23     CUE_STATE_VIRGIN = 0,
24     CUE_STATE_HIT_NOTHING,
25     CUE_STATE_SEEN_NOTHING
26 } Cue_state;
27
28 struct Goals {
29     Lt *lt;
30     char **ids;
31     Point *points;
32     Color *colors;
33     Cue_state *cue_states;
34     bool *visible;
35     size_t count;
36     Rect player_hitbox;
37     float angle;
38 };
39
40 Goals *create_goals_from_line_stream(LineStream *line_stream)
41 {
42     trace_assert(line_stream);
43
44     Lt *const lt = create_lt();
45     if (lt == NULL) {
46         return NULL;
47     }
48
49     Goals *const goals = PUSH_LT(lt, nth_calloc(1, sizeof(Goals)), free);
50     if (goals == NULL) {
51         RETURN_LT(lt, NULL);
52     }
53
54     goals->count = 0;
55     if (sscanf(
56             line_stream_next(line_stream),
57             "%lu",
58             &goals->count) == EOF) {
59         log_fail("Could not read amount of goals\n");
60         RETURN_LT(lt, NULL);
61     }
62
63     goals->ids = PUSH_LT(
64         lt,
65         nth_calloc(1, sizeof(char*) * goals->count),
66         free);
67     if (goals->ids == NULL) {
68         RETURN_LT(lt, NULL);
69     }
70     for (size_t i = 0; i < goals->count; ++i) {
71         goals->ids[i] = PUSH_LT(lt, nth_calloc(1, sizeof(char) * GOAL_MAX_ID_SIZE), free);
72         if (goals->ids[i] == NULL) {
73             RETURN_LT(lt, NULL);
74         }
75     }
76
77     goals->points = PUSH_LT(lt, nth_calloc(1, sizeof(Point) * goals->count), free);
78     if (goals->points == NULL) {
79         RETURN_LT(lt, NULL);
80     }
81
82     goals->colors = PUSH_LT(lt, nth_calloc(1, sizeof(Color) * goals->count), free);
83     if (goals->colors == NULL) {
84         RETURN_LT(lt, NULL);
85     }
86
87     goals->cue_states = PUSH_LT(lt, nth_calloc(1, sizeof(int) * goals->count), free);
88     if (goals->cue_states == NULL) {
89         RETURN_LT(lt, NULL);
90     }
91
92     goals->visible = PUSH_LT(lt, nth_calloc(1, sizeof(bool) * goals->count), free);
93     if (goals->visible == NULL) {
94         RETURN_LT(lt, NULL);
95     }
96
97     char color[7];
98     for (size_t i = 0; i < goals->count; ++i) {
99         if (sscanf(
100                 line_stream_next(line_stream),
101                 "%" STRINGIFY(GOAL_MAX_ID_SIZE) "s%f%f%6s",
102                 goals->ids[i],
103                 &goals->points[i].x,
104                 &goals->points[i].y,
105                 color) < 0) {
106             log_fail("Could not read %dth goal\n", i);
107             RETURN_LT(lt, NULL);
108         }
109         goals->colors[i] = hexstr(color);
110         goals->cue_states[i] = CUE_STATE_VIRGIN;
111         goals->visible[i] = true;
112     }
113
114     goals->lt = lt;
115     goals->angle = 0.0f;
116
117     return goals;
118 }
119
120 Goals *create_goals_from_point_rect_layer(PointLayer *point_rect_layer)
121 {
122     trace_assert(point_rect_layer);
123
124     Lt *const lt = create_lt();
125     if (lt == NULL) {
126         return NULL;
127     }
128
129     Goals *const goals = PUSH_LT(lt, nth_calloc(1, sizeof(Goals)), free);
130     if (goals == NULL) {
131         RETURN_LT(lt, NULL);
132     }
133
134     goals->count = point_layer_count(point_rect_layer);
135
136     goals->ids = PUSH_LT(
137         lt,
138         nth_calloc(1, sizeof(char*) * goals->count),
139         free);
140     if (goals->ids == NULL) {
141         RETURN_LT(lt, NULL);
142     }
143     for (size_t i = 0; i < goals->count; ++i) {
144         goals->ids[i] = PUSH_LT(lt, nth_calloc(1, sizeof(char) * GOAL_MAX_ID_SIZE), free);
145         if (goals->ids[i] == NULL) {
146             RETURN_LT(lt, NULL);
147         }
148     }
149
150     goals->points = PUSH_LT(lt, nth_calloc(1, sizeof(Point) * goals->count), free);
151     if (goals->points == NULL) {
152         RETURN_LT(lt, NULL);
153     }
154
155     goals->colors = PUSH_LT(lt, nth_calloc(1, sizeof(Color) * goals->count), free);
156     if (goals->colors == NULL) {
157         RETURN_LT(lt, NULL);
158     }
159
160     goals->cue_states = PUSH_LT(lt, nth_calloc(1, sizeof(int) * goals->count), free);
161     if (goals->cue_states == NULL) {
162         RETURN_LT(lt, NULL);
163     }
164
165     goals->visible = PUSH_LT(lt, nth_calloc(1, sizeof(bool) * goals->count), free);
166     if (goals->visible == NULL) {
167         RETURN_LT(lt, NULL);
168     }
169
170     const Point *points = point_layer_points(point_rect_layer);
171     const Color *colors = point_layer_colors(point_rect_layer);
172     const char *ids = point_layer_ids(point_rect_layer);
173
174     // TODO(#835): we could use memcpy in create_goals_from_point_rect_layer
175     for (size_t i = 0; i < goals->count; ++i) {
176         goals->points[i] = points[i];
177         goals->colors[i] = colors[i];
178         memcpy(goals->ids[i], ids + ID_MAX_SIZE * i, ID_MAX_SIZE);
179         goals->cue_states[i] = CUE_STATE_VIRGIN;
180         goals->visible[i] = true;
181     }
182
183     goals->lt = lt;
184     goals->angle = 0.0f;
185
186     return goals;
187 }
188
189 void destroy_goals(Goals *goals)
190 {
191     trace_assert(goals);
192     RETURN_LT0(goals->lt);
193 }
194
195 static int goals_render_core(const Goals *goals,
196                              size_t goal_index,
197                              Camera *camera)
198 {
199     trace_assert(goals);
200     trace_assert(camera);
201
202     const Point position = vec_sum(
203         goals->points[goal_index],
204         vec(0.0f, sinf(goals->angle) * 10.0f));
205
206     if (camera_fill_triangle(
207             camera,
208             triangle_mat3x3_product(
209                 equilateral_triangle(),
210                 mat3x3_product2(
211                     trans_mat(position.x, position.y),
212                     rot_mat(PI * -0.5f + goals->angle),
213                     scale_mat(GOAL_RADIUS))),
214             goals->colors[goal_index]) < 0) {
215         return -1;
216     }
217
218     if (camera_render_debug_text(
219             camera,
220             goals->ids[goal_index],
221             position) < 0) {
222         return -1;
223     }
224
225     return 0;
226 }
227
228 int goals_render(const Goals *goals,
229                  Camera *camera)
230 {
231     trace_assert(goals);
232     trace_assert(camera);
233
234     for (size_t i = 0; i < goals->count; ++i) {
235         if (!goals_is_goal_hidden(goals, i)) {
236             if (goals_render_core(goals, i, camera) < 0) {
237                 return -1;
238             }
239         }
240     }
241
242     return 0;
243 }
244
245 void goals_update(Goals *goals,
246                   float delta_time)
247 {
248     trace_assert(goals);
249     trace_assert(delta_time > 0.0f);
250     goals->angle = fmodf(goals->angle + 2.0f * delta_time, 2.0f * PI);
251 }
252
253 void goals_hide_from_player(Goals *goals,
254                             Rect player_hitbox)
255 {
256     goals->player_hitbox = player_hitbox;
257
258 }
259
260 int goals_sound(Goals *goals,
261                 Sound_samples *sound_samples)
262 {
263     for (size_t i = 0; i < goals->count; ++i) {
264         switch (goals->cue_states[i]) {
265         case CUE_STATE_HIT_NOTHING:
266             sound_samples_play_sound(sound_samples, 0, 0);
267             goals->cue_states[i] = CUE_STATE_SEEN_NOTHING;
268             break;
269
270         default: {}
271         }
272     }
273
274     return 0;
275 }
276
277 void goals_cue(Goals *goals,
278                const Camera *camera)
279 {
280     for (size_t i = 0; i < goals->count; ++i) {
281         switch (goals->cue_states[i]) {
282         case CUE_STATE_VIRGIN:
283             if (goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->points[i])) {
284                 goals->cue_states[i] = CUE_STATE_HIT_NOTHING;
285             }
286
287             break;
288
289         case CUE_STATE_SEEN_NOTHING:
290             if (!goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->points[i])) {
291                 goals->cue_states[i] = CUE_STATE_VIRGIN;
292             }
293             break;
294
295         default: {}
296         }
297     }
298 }
299
300 void goals_checkpoint(const Goals *goals,
301                       Player *player)
302 {
303     trace_assert(goals);
304     trace_assert(player);
305
306     for (size_t i = 0; i < goals->count; ++i) {
307         if (goals->cue_states[i] == CUE_STATE_HIT_NOTHING) {
308             player_checkpoint(player, goals->points[i]);
309         }
310     }
311 }
312
313 static struct EvalResult
314 goals_action(Goals *goals, size_t index, Gc *gc, struct Scope *scope, struct Expr path)
315 {
316     trace_assert(goals);
317     trace_assert(gc);
318     trace_assert(scope);
319
320     const char *target = NULL;
321     struct EvalResult res = match_list(gc, "q*", path, &target, NULL);
322     if (res.is_error) {
323         return res;
324     }
325
326     if (strcmp(target, "show") == 0) {
327         goals->visible[index] = true;
328         return eval_success(NIL(gc));
329     } else if (strcmp(target, "hide") == 0) {
330         goals->visible[index] = false;
331         return eval_success(NIL(gc));
332     }
333
334     return unknown_target(gc, goals->ids[index], target);
335 }
336
337 struct EvalResult
338 goals_send(Goals *goals, Gc *gc, struct Scope *scope, struct Expr path)
339 {
340     trace_assert(goals);
341     trace_assert(gc);
342     trace_assert(scope);
343
344     const char *target = NULL;
345     struct Expr rest = void_expr();
346     struct EvalResult res = match_list(gc, "s*", path, &target, &rest);
347     if (res.is_error) {
348         return res;
349     }
350
351     for (size_t i = 0; i < goals->count; ++i) {
352         if (strcmp(target, goals->ids[i]) == 0) {
353             return goals_action(goals, i, gc, scope, rest);
354         }
355     }
356
357     return unknown_target(gc, "goals", target);
358 }
359
360 /* Private Functions */
361
362 static int goals_is_goal_hidden(const Goals *goals, size_t i)
363 {
364     return !goals->visible[i];
365 }