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