]> git.lizzy.rs Git - nothing.git/blob - src/game/level/goals.c
885383d0e064e3fc1b591e3cf9d1bcc28a183387
[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_point_layer(const PointLayer *point_layer)
39 {
40     trace_assert(point_layer);
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 = point_layer_count(point_layer);
50
51     goals->ids = PUSH_LT(
52         lt,
53         nth_calloc(1, sizeof(char*) * goals->count),
54         free);
55     if (goals->ids == NULL) {
56         RETURN_LT(lt, NULL);
57     }
58     for (size_t i = 0; i < goals->count; ++i) {
59         goals->ids[i] = PUSH_LT(lt, nth_calloc(1, sizeof(char) * ENTITY_MAX_ID_SIZE), free);
60         if (goals->ids[i] == NULL) {
61             RETURN_LT(lt, NULL);
62         }
63     }
64
65     goals->positions = PUSH_LT(lt, nth_calloc(1, sizeof(Vec2f) * goals->count), free);
66     if (goals->positions == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69
70     goals->colors = PUSH_LT(lt, nth_calloc(1, sizeof(Color) * goals->count), free);
71     if (goals->colors == NULL) {
72         RETURN_LT(lt, NULL);
73     }
74
75     goals->cue_states = PUSH_LT(lt, nth_calloc(1, sizeof(int) * goals->count), free);
76     if (goals->cue_states == NULL) {
77         RETURN_LT(lt, NULL);
78     }
79
80     goals->visible = PUSH_LT(lt, nth_calloc(1, sizeof(bool) * goals->count), free);
81     if (goals->visible == NULL) {
82         RETURN_LT(lt, NULL);
83     }
84
85     const Vec2f *positions = point_layer_positions(point_layer);
86     const Color *colors = point_layer_colors(point_layer);
87     const char *ids = point_layer_ids(point_layer);
88
89     // TODO(#835): we could use memcpy in create_goals_from_point_layer
90     for (size_t i = 0; i < goals->count; ++i) {
91         goals->positions[i] = positions[i];
92         goals->colors[i] = colors[i];
93         memcpy(goals->ids[i], ids + ID_MAX_SIZE * i, ID_MAX_SIZE);
94         goals->cue_states[i] = CUE_STATE_VIRGIN;
95         goals->visible[i] = true;
96     }
97
98     goals->lt = lt;
99     goals->angle = 0.0f;
100
101     return goals;
102 }
103
104 void destroy_goals(Goals *goals)
105 {
106     trace_assert(goals);
107     RETURN_LT0(goals->lt);
108 }
109
110 static int goals_render_core(const Goals *goals,
111                              size_t goal_index,
112                              const Camera *camera)
113 {
114     trace_assert(goals);
115     trace_assert(camera);
116
117     const Vec2f position = vec_sum(
118         goals->positions[goal_index],
119         vec(0.0f, sinf(goals->angle) * 10.0f));
120
121     if (camera_fill_triangle(
122             camera,
123             triangle_mat3x3_product(
124                 equilateral_triangle(),
125                 mat3x3_product2(
126                     trans_mat(position.x, position.y),
127                     rot_mat(PI * -0.5f + goals->angle),
128                     scale_mat(GOAL_RADIUS))),
129             goals->colors[goal_index]) < 0) {
130         return -1;
131     }
132
133     if (camera_render_debug_text(
134             camera,
135             goals->ids[goal_index],
136             position) < 0) {
137         return -1;
138     }
139
140     return 0;
141 }
142
143 int goals_render(const Goals *goals,
144                  const Camera *camera)
145 {
146     trace_assert(goals);
147     trace_assert(camera);
148
149     for (size_t i = 0; i < goals->count; ++i) {
150         if (!goals_is_goal_hidden(goals, i)) {
151             if (goals_render_core(goals, i, camera) < 0) {
152                 return -1;
153             }
154         }
155     }
156
157     return 0;
158 }
159
160 void goals_update(Goals *goals,
161                   float delta_time)
162 {
163     trace_assert(goals);
164     trace_assert(delta_time > 0.0f);
165     goals->angle = fmodf(goals->angle + 2.0f * delta_time, 2.0f * PI);
166 }
167
168 int goals_sound(Goals *goals,
169                 Sound_samples *sound_samples)
170 {
171     for (size_t i = 0; i < goals->count; ++i) {
172         switch (goals->cue_states[i]) {
173         case CUE_STATE_HIT_NOTHING:
174             sound_samples_play_sound(sound_samples, 0);
175             goals->cue_states[i] = CUE_STATE_SEEN_NOTHING;
176             break;
177
178         default: {}
179         }
180     }
181
182     return 0;
183 }
184
185 void goals_cue(Goals *goals,
186                const Camera *camera)
187 {
188     for (size_t i = 0; i < goals->count; ++i) {
189         switch (goals->cue_states[i]) {
190         case CUE_STATE_VIRGIN:
191             if (goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->positions[i])) {
192                 goals->cue_states[i] = CUE_STATE_HIT_NOTHING;
193             }
194
195             break;
196
197         case CUE_STATE_SEEN_NOTHING:
198             if (!goals_is_goal_hidden(goals, i) && camera_is_point_visible(camera, goals->positions[i])) {
199                 goals->cue_states[i] = CUE_STATE_VIRGIN;
200             }
201             break;
202
203         default: {}
204         }
205     }
206 }
207
208 void goals_checkpoint(const Goals *goals,
209                       Player *player)
210 {
211     trace_assert(goals);
212     trace_assert(player);
213
214     for (size_t i = 0; i < goals->count; ++i) {
215         if (goals->cue_states[i] == CUE_STATE_HIT_NOTHING) {
216             player_checkpoint(player, goals->positions[i]);
217         }
218     }
219 }
220
221 /* Private Functions */
222
223 static int goals_is_goal_hidden(const Goals *goals, size_t i)
224 {
225     return !goals->visible[i];
226 }
227
228 void goals_hide(Goals *goals, char goal_id[ENTITY_MAX_ID_SIZE])
229 {
230     trace_assert(goals);
231     trace_assert(goal_id);
232
233     for (size_t i = 0; i < goals->count; ++i) {
234         if (strncmp(goal_id, goals->ids[i], ENTITY_MAX_ID_SIZE) == 0) {
235             goals->visible[i] = false;
236         }
237     }
238 }
239
240 void goals_show(Goals *goals, char goal_id[ENTITY_MAX_ID_SIZE])
241 {
242     trace_assert(goals);
243     trace_assert(goal_id);
244     for (size_t i = 0; i < goals->count; ++i) {
245         if (strncmp(goal_id, goals->ids[i], ENTITY_MAX_ID_SIZE) == 0) {
246             goals->visible[i] = true;
247         }
248     }
249 }