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