]> git.lizzy.rs Git - nothing.git/blob - src/game/level/regions.c
276c41f0c2fff90ea00f924a28905552b183fc5e
[nothing.git] / src / game / level / regions.c
1 #include "system/stacktrace.h"
2
3 #include "config.h"
4 #include "player.h"
5 #include "regions.h"
6 #include "system/str.h"
7 #include "system/line_stream.h"
8 #include "system/log.h"
9 #include "system/lt.h"
10 #include "system/nth_alloc.h"
11 #include "game/level/level_editor/rect_layer.h"
12 #include "game/level/labels.h"
13 #include "game/level/goals.h"
14
15 enum RegionState {
16     RS_PLAYER_OUTSIDE = 0,
17     RS_PLAYER_INSIDE
18 };
19
20 struct Regions {
21     Lt *lt;
22     size_t count;
23     char *ids;
24     Rect *rects;
25     Color *colors;
26     enum RegionState *states;
27     Action *actions;
28
29     Labels *labels;
30     Goals *goals;
31 };
32
33 Regions *create_regions_from_rect_layer(const RectLayer *rect_layer,
34                                         Labels *labels,
35                                         Goals *goals)
36 {
37     trace_assert(rect_layer);
38     trace_assert(labels);
39
40     Lt *lt = create_lt();
41
42     Regions *regions = PUSH_LT(
43         lt,
44         nth_calloc(1, sizeof(Regions)),
45         free);
46     if (regions == NULL) {
47         RETURN_LT(lt, NULL);
48     }
49     regions->lt = lt;
50
51     regions->count = rect_layer_count(rect_layer);
52
53     regions->ids = PUSH_LT(
54         lt,
55         nth_calloc(regions->count * ENTITY_MAX_ID_SIZE, sizeof(char)),
56         free);
57     if (regions->ids == NULL) {
58         RETURN_LT(lt, NULL);
59     }
60     memcpy(regions->ids,
61            rect_layer_ids(rect_layer),
62            regions->count * ENTITY_MAX_ID_SIZE * sizeof(char));
63
64
65     regions->rects = PUSH_LT(
66         lt,
67         nth_calloc(1, sizeof(Rect) * regions->count),
68         free);
69     if (regions->rects == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72     memcpy(regions->rects,
73            rect_layer_rects(rect_layer),
74            regions->count * sizeof(Rect));
75
76
77     regions->colors = PUSH_LT(
78         lt,
79         nth_calloc(1, sizeof(Color) * regions->count),
80         free);
81     if (regions->colors == NULL) {
82         RETURN_LT(lt, NULL);
83     }
84     memcpy(regions->colors,
85            rect_layer_colors(rect_layer),
86            regions->count * sizeof(Color));
87
88     regions->states = PUSH_LT(
89         lt,
90         nth_calloc(1, sizeof(enum RegionState) * regions->count),
91         free);
92     if (regions->states == NULL) {
93         RETURN_LT(lt, NULL);
94     }
95
96     regions->actions = PUSH_LT(
97         lt,
98         nth_calloc(1, sizeof(Action) * regions->count),
99         free);
100     if (regions->actions == NULL) {
101         RETURN_LT(lt, NULL);
102     }
103     memcpy(regions->actions,
104            rect_layer_actions(rect_layer),
105            regions->count * sizeof(Action));
106
107     // TODO(#1108): impossible to change the region action from the Level Editor
108
109
110     regions->labels = labels;
111     regions->goals = goals;
112
113     return regions;
114 }
115
116 void destroy_regions(Regions *regions)
117 {
118     trace_assert(regions);
119     RETURN_LT0(regions->lt);
120 }
121
122 void regions_player_enter(Regions *regions, Player *player)
123 {
124     trace_assert(regions);
125     trace_assert(player);
126
127     for (size_t i = 0; i < regions->count; ++i) {
128         if (regions->states[i] == RS_PLAYER_OUTSIDE &&
129             player_overlaps_rect(player, regions->rects[i])) {
130             regions->states[i] = RS_PLAYER_INSIDE;
131
132             switch (regions->actions[i].type) {
133             case ACTION_HIDE_LABEL: {
134                 labels_hide(regions->labels, regions->actions[i].entity_id);
135             } break;
136
137             case ACTION_TOGGLE_GOAL: {
138                 goals_hide(regions->goals, regions->actions[i].entity_id);
139             } break;
140
141             default: {}
142             }
143         }
144     }
145 }
146
147 void regions_player_leave(Regions *regions, Player *player)
148 {
149     trace_assert(regions);
150     trace_assert(player);
151
152     for (size_t i = 0; i < regions->count; ++i) {
153         if (regions->states[i] == RS_PLAYER_INSIDE &&
154             !player_overlaps_rect(player, regions->rects[i])) {
155             regions->states[i] = RS_PLAYER_OUTSIDE;
156
157             switch (regions->actions[i].type) {
158             case ACTION_TOGGLE_GOAL: {
159                 goals_show(regions->goals, regions->actions[i].entity_id);
160             } break;
161
162             default: {}
163             }
164         }
165     }
166 }
167
168 int regions_render(Regions *regions, const Camera *camera)
169 {
170     trace_assert(regions);
171     trace_assert(camera);
172
173     for (size_t i = 0; i < regions->count; ++i) {
174         if (camera_render_debug_rect(
175                 camera,
176                 regions->rects[i],
177                 regions->colors[i]) < 0) {
178             return -1;
179         }
180     }
181
182     return 0;
183 }