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