]> git.lizzy.rs Git - nothing.git/blob - src/game/level/goals.c
Merge pull request #595 from tsoding/594
[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 "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     Cue_state *cue_states;
31     bool *visible;
32     size_t count;
33     Rect player_hitbox;
34     float angle;
35 };
36
37 Goals *create_goals_from_line_stream(LineStream *line_stream)
38 {
39     trace_assert(line_stream);
40
41     Lt *const lt = create_lt();
42     if (lt == NULL) {
43         return NULL;
44     }
45
46     Goals *const goals = PUSH_LT(lt, nth_alloc(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_alloc(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_alloc(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_alloc(sizeof(Point) * goals->count), free);
75     if (goals->points == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     goals->colors = PUSH_LT(lt, nth_alloc(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_alloc(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_alloc(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 void destroy_goals(Goals *goals)
118 {
119     trace_assert(goals);
120     RETURN_LT0(goals->lt);
121 }
122
123 static int goals_render_core(const Goals *goals,
124                              size_t goal_index,
125                              Camera *camera)
126 {
127     trace_assert(goals);
128     trace_assert(camera);
129
130     const Point position = vec_sum(
131         goals->points[goal_index],
132         vec(0.0f, sinf(goals->angle) * 10.0f));
133
134     if (camera_fill_triangle(
135             camera,
136             triangle_mat3x3_product(
137                 equilateral_triangle(),
138                 mat3x3_product2(
139                     trans_mat(position.x, position.y),
140                     rot_mat(PI * -0.5f + goals->angle),
141                     scale_mat(GOAL_RADIUS))),
142             goals->colors[goal_index]) < 0) {
143         return -1;
144     }
145
146     if (camera_render_debug_text(
147             camera,
148             goals->ids[goal_index],
149             position) < 0) {
150         return -1;
151     }
152
153     return 0;
154 }
155
156 int goals_render(const Goals *goals,
157                  Camera *camera)
158 {
159     trace_assert(goals);
160     trace_assert(camera);
161
162     for (size_t i = 0; i < goals->count; ++i) {
163         if (!goals_is_goal_hidden(goals, i)) {
164             if (goals_render_core(goals, i, camera) < 0) {
165                 return -1;
166             }
167         }
168     }
169
170     return 0;
171 }
172
173 void goals_update(Goals *goals,
174                   float delta_time)
175 {
176     trace_assert(goals);
177     trace_assert(delta_time > 0.0f);
178     goals->angle = fmodf(goals->angle + 2.0f * delta_time, 2.0f * PI);
179 }
180
181 void goals_hide_from_player(Goals *goals,
182                             Rect player_hitbox)
183 {
184     goals->player_hitbox = player_hitbox;
185
186 }
187
188 int goals_sound(Goals *goals,
189                 Sound_samples *sound_samples)
190 {
191     for (size_t i = 0; i < goals->count; ++i) {
192         switch (goals->cue_states[i]) {
193         case CUE_STATE_HIT_NOTHING:
194             sound_samples_play_sound(sound_samples, 0, 0);
195             goals->cue_states[i] = CUE_STATE_SEEN_NOTHING;
196             break;
197
198         default: {}
199         }
200     }
201
202     return 0;
203 }
204
205 void goals_cue(Goals *goals,
206                const Camera *camera)
207 {
208     for (size_t i = 0; i < goals->count; ++i) {
209         switch (goals->cue_states[i]) {
210         case CUE_STATE_VIRGIN:
211             if (goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->points[i])) {
212                 goals->cue_states[i] = CUE_STATE_HIT_NOTHING;
213             }
214
215             break;
216
217         case CUE_STATE_SEEN_NOTHING:
218             if (!goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->points[i])) {
219                 goals->cue_states[i] = CUE_STATE_VIRGIN;
220             }
221             break;
222
223         default: {}
224         }
225     }
226 }
227
228 void goals_checkpoint(const Goals *goals,
229                       Player *player)
230 {
231     trace_assert(goals);
232     trace_assert(player);
233
234     for (size_t i = 0; i < goals->count; ++i) {
235         if (goals->cue_states[i] == CUE_STATE_HIT_NOTHING) {
236             player_checkpoint(player, goals->points[i]);
237         }
238     }
239 }
240
241 void goals_hide(Goals *goals, const char *id)
242 {
243     trace_assert(goals);
244     trace_assert(id);
245
246     for (size_t i = 0; i < goals->count; ++i) {
247         if (strcmp(id, goals->ids[i]) == 0) {
248             goals->visible[i] = false;
249             return;
250         }
251     }
252 }
253
254 void goals_show(Goals *goals, const char *id)
255 {
256     trace_assert(goals);
257     trace_assert(id);
258
259     for (size_t i = 0; i < goals->count; ++i) {
260         if (strcmp(id, goals->ids[i]) == 0) {
261             goals->visible[i] = true;
262             return;
263         }
264     }
265 }
266
267 /* Private Functions */
268
269 static int goals_is_goal_hidden(const Goals *goals, size_t i)
270 {
271     return !goals->visible[i];
272 }