]> git.lizzy.rs Git - nothing.git/blob - src/game/level/goals.c
67c810842c4e280bed4b5469f4c46f8aa93885a6
[nothing.git] / src / game / level / goals.c
1 #include <SDL2/SDL.h>
2 #include <assert.h>
3 #include <math.h>
4
5 #include "goals.h"
6 #include "math/pi.h"
7 #include "math/triangle.h"
8 #include "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
14 #define GOAL_RADIUS 10.0f
15 #define GOAL_MAX_ID_SIZE 36
16
17 static int goals_is_goal_hidden(const Goals *goals, size_t i);
18
19 typedef enum Cue_state {
20     CUE_STATE_VIRGIN = 0,
21     CUE_STATE_HIT_NOTHING,
22     CUE_STATE_SEEN_NOTHING
23 } Cue_state;
24
25 struct Goals {
26     Lt *lt;
27     char **ids;
28     Point *points;
29     Color *colors;
30     /* TODO(#494): it is not clear how to maintain Cue_state from the scripting language */
31     Cue_state *cue_states;
32     bool *visible;
33     size_t count;
34     Rect player_hitbox;
35     float angle;
36 };
37
38 Goals *create_goals_from_line_stream(LineStream *line_stream)
39 {
40     assert(line_stream);
41
42     Lt *const lt = create_lt();
43     if (lt == NULL) {
44         return NULL;
45     }
46
47     Goals *const goals = PUSH_LT(lt, nth_alloc(sizeof(Goals)), free);
48     if (goals == NULL) {
49         RETURN_LT(lt, NULL);
50     }
51
52     goals->count = 0;
53     if (sscanf(
54             line_stream_next(line_stream),
55             "%lu",
56             &goals->count) == EOF) {
57         log_fail("Could not read amount of goals\n");
58         RETURN_LT(lt, NULL);
59     }
60
61     goals->ids = PUSH_LT(
62         lt,
63         nth_alloc(sizeof(char*) * goals->count),
64         free);
65     if (goals->ids == NULL) {
66         RETURN_LT(lt, NULL);
67     }
68     for (size_t i = 0; i < goals->count; ++i) {
69         goals->ids[i] = PUSH_LT(lt, nth_alloc(sizeof(char) * GOAL_MAX_ID_SIZE), free);
70         if (goals->ids[i] == NULL) {
71             RETURN_LT(lt, NULL);
72         }
73     }
74
75     goals->points = PUSH_LT(lt, nth_alloc(sizeof(Point) * goals->count), free);
76     if (goals->points == NULL) {
77         RETURN_LT(lt, NULL);
78     }
79
80     goals->colors = PUSH_LT(lt, nth_alloc(sizeof(Color) * goals->count), free);
81     if (goals->colors == NULL) {
82         RETURN_LT(lt, NULL);
83     }
84
85     goals->cue_states = PUSH_LT(lt, nth_alloc(sizeof(int) * goals->count), free);
86     if (goals->cue_states == NULL) {
87         RETURN_LT(lt, NULL);
88     }
89
90     goals->visible = PUSH_LT(lt, nth_alloc(sizeof(bool) * goals->count), free);
91     if (goals->visible == NULL) {
92         RETURN_LT(lt, NULL);
93     }
94
95     char color[7];
96     for (size_t i = 0; i < goals->count; ++i) {
97         if (sscanf(
98                 line_stream_next(line_stream),
99                 "%" STRINGIFY(GOAL_MAX_ID_SIZE) "s%f%f%6s",
100                 goals->ids[i],
101                 &goals->points[i].x,
102                 &goals->points[i].y,
103                 color) < 0) {
104             log_fail("Could not read %dth goal\n", i);
105             RETURN_LT(lt, NULL);
106         }
107         goals->colors[i] = hexstr(color);
108         goals->cue_states[i] = CUE_STATE_VIRGIN;
109         goals->visible[i] = true;
110     }
111
112     goals->lt = lt;
113     goals->angle = 0.0f;
114
115     return goals;
116 }
117
118 void destroy_goals(Goals *goals)
119 {
120     assert(goals);
121     RETURN_LT0(goals->lt);
122 }
123
124 static int goals_render_core(const Goals *goals,
125                              size_t goal_index,
126                              Camera *camera)
127 {
128     assert(goals);
129     assert(camera);
130
131     const Point position = vec_sum(
132         goals->points[goal_index],
133         vec(0.0f, sinf(goals->angle) * 10.0f));
134
135     if (camera_fill_triangle(
136             camera,
137             triangle_mat3x3_product(
138                 equilateral_triangle(),
139                 mat3x3_product2(
140                     trans_mat(position.x, position.y),
141                     rot_mat(PI * -0.5f + goals->angle),
142                     scale_mat(GOAL_RADIUS))),
143             goals->colors[goal_index]) < 0) {
144         return -1;
145     }
146
147     if (camera_render_debug_text(
148             camera,
149             goals->ids[goal_index],
150             position) < 0) {
151         return -1;
152     }
153
154     return 0;
155 }
156
157 int goals_render(const Goals *goals,
158                  Camera *camera)
159 {
160     assert(goals);
161     assert(camera);
162
163     for (size_t i = 0; i < goals->count; ++i) {
164         if (!goals_is_goal_hidden(goals, i)) {
165             if (goals_render_core(goals, i, camera) < 0) {
166                 return -1;
167             }
168         }
169     }
170
171     return 0;
172 }
173
174 void goals_update(Goals *goals,
175                   float delta_time)
176 {
177     assert(goals);
178     assert(delta_time > 0.0f);
179     goals->angle = fmodf(goals->angle + 2.0f * delta_time, 2.0f * PI);
180 }
181
182 void goals_hide_from_player(Goals *goals,
183                             Rect player_hitbox)
184 {
185     goals->player_hitbox = player_hitbox;
186
187 }
188
189 int goals_sound(Goals *goals,
190                 Sound_samples *sound_samples)
191 {
192     for (size_t i = 0; i < goals->count; ++i) {
193         switch (goals->cue_states[i]) {
194         case CUE_STATE_HIT_NOTHING:
195             sound_samples_play_sound(sound_samples, 0, 0);
196             goals->cue_states[i] = CUE_STATE_SEEN_NOTHING;
197             break;
198
199         default: {}
200         }
201     }
202
203     return 0;
204 }
205
206 void goals_cue(Goals *goals,
207                const Camera *camera)
208 {
209     for (size_t i = 0; i < goals->count; ++i) {
210         switch (goals->cue_states[i]) {
211         case CUE_STATE_VIRGIN:
212             if (goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->points[i])) {
213                 goals->cue_states[i] = CUE_STATE_HIT_NOTHING;
214             }
215
216             break;
217
218         case CUE_STATE_SEEN_NOTHING:
219             if (!goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->points[i])) {
220                 goals->cue_states[i] = CUE_STATE_VIRGIN;
221             }
222             break;
223
224         default: {}
225         }
226     }
227 }
228
229 void goals_checkpoint(const Goals *goals,
230                       Player *player)
231 {
232     assert(goals);
233     assert(player);
234
235     for (size_t i = 0; i < goals->count; ++i) {
236         if (goals->cue_states[i] == CUE_STATE_HIT_NOTHING) {
237             player_checkpoint(player, goals->points[i]);
238         }
239     }
240 }
241
242 void goals_hide(Goals *goals, const char *id)
243 {
244     assert(goals);
245     assert(id);
246
247     for (size_t i = 0; i < goals->count; ++i) {
248         if (strcmp(id, goals->ids[i]) == 0) {
249             goals->visible[i] = false;
250             return;
251         }
252     }
253 }
254
255 void goals_show(Goals *goals, const char *id)
256 {
257     assert(goals);
258     assert(id);
259
260     for (size_t i = 0; i < goals->count; ++i) {
261         if (strcmp(id, goals->ids[i]) == 0) {
262             goals->visible[i] = true;
263             return;
264         }
265     }
266 }
267
268 /* Private Functions */
269
270 static int goals_is_goal_hidden(const Goals *goals, size_t i)
271 {
272     return !goals->visible[i];
273 }