]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
(#199) rigid_body_object_collide -> rigid_rect_touches_rect_sides
[nothing.git] / src / game / level / player.c
1 #include <SDL2/SDL.h>
2 #include <assert.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include "game/level/player/dying_rect.h"
7 #include "game/level/player/rigid_rect.h"
8 #include "goals.h"
9 #include "math/point.h"
10 #include "platforms.h"
11 #include "player.h"
12 #include "system/error.h"
13 #include "system/lt.h"
14
15 #define PLAYER_WIDTH 25.0f
16 #define PLAYER_HEIGHT 25.0f
17 #define PLAYER_SPEED 500.0f
18 #define PLAYER_JUMP 550.0f
19 #define PLAYER_DEATH_DURATION 0.75f
20
21 typedef enum player_state_t {
22     PLAYER_STATE_ALIVE = 0,
23     PLAYER_STATE_DYING
24 } player_state_t;
25
26 struct player_t {
27     lt_t *lt;
28     player_state_t state;
29
30     rigid_rect_t *alive_body;
31     dying_rect_t *dying_body;
32
33     int jump_count;
34     color_t color;
35
36     vec_t checkpoint;
37
38     int play_die_cue;
39 };
40
41 player_t *create_player(float x, float y, color_t color)
42 {
43     lt_t *lt = create_lt();
44
45     if (lt == NULL) {
46         return NULL;
47     }
48
49     player_t *player = PUSH_LT(lt, malloc(sizeof(player_t)), free);
50     if (player == NULL) {
51         throw_error(ERROR_TYPE_LIBC);
52         RETURN_LT(lt, NULL);
53     }
54
55     player->state = PLAYER_STATE_ALIVE;
56
57     player->alive_body = PUSH_LT(
58         lt,
59         create_rigid_rect(
60             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
61             color),
62         destroy_rigid_rect);
63     if (player->alive_body == NULL) {
64         RETURN_LT(lt, NULL);
65     }
66
67     player->dying_body = PUSH_LT(
68         lt,
69         create_dying_rect(
70             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
71             color,
72             PLAYER_DEATH_DURATION),
73         destroy_dying_rect);
74     if (player->dying_body == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     player->lt = lt;
79     player->jump_count = 0;
80     player->color = color;
81     player->checkpoint = vec(x, y);
82     player->play_die_cue = 0;
83
84     return player;
85 }
86
87 player_t *create_player_from_stream(FILE *stream)
88 {
89     float x = 0.0f, y = 0.0f;
90
91     char color[7];
92     if (fscanf(stream, "%f%f%6s", &x, &y, color) == EOF) {
93         throw_error(ERROR_TYPE_LIBC);
94         return NULL;
95     }
96
97     return create_player(x, y, color_from_hexstr(color));
98 }
99
100 void destroy_player(player_t * player)
101 {
102     RETURN_LT0(player->lt);
103 }
104
105 solid_ref_t player_as_solid(player_t *player)
106 {
107     solid_ref_t ref = {
108         .tag = SOLID_PLAYER,
109         .ptr = (void*) player
110     };
111
112     return ref;
113 }
114
115 int player_render(const player_t * player,
116                   const camera_t *camera)
117 {
118     assert(player);
119     assert(camera);
120
121     switch (player->state) {
122     case PLAYER_STATE_ALIVE:
123         return rigid_rect_render(player->alive_body, camera);
124
125     case PLAYER_STATE_DYING:
126         return dying_rect_render(player->dying_body, camera);
127
128     default: {}
129     }
130
131     return 0;
132 }
133
134 void player_update(player_t *player,
135                    float delta_time)
136 {
137     assert(player);
138
139     switch (player->state) {
140     case PLAYER_STATE_ALIVE: {
141         rigid_rect_update(player->alive_body, delta_time);
142
143         const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
144
145         if (hitbox.y > 1000.0f) {
146             player_die(player);
147         }
148     } break;
149
150     case PLAYER_STATE_DYING: {
151         dying_rect_update(player->dying_body, delta_time);
152
153         if (dying_rect_is_dead(player->dying_body)) {
154             player->alive_body = RESET_LT(
155                 player->lt,
156                 player->alive_body,
157                 create_rigid_rect(
158                     rect_from_vecs(
159                         player->checkpoint,
160                         vec(PLAYER_WIDTH, PLAYER_HEIGHT)),
161                     player->color));
162             player->state = PLAYER_STATE_ALIVE;
163         }
164     } break;
165
166     default: {}
167     }
168 }
169
170 void player_collide_with_solid(player_t *player, solid_ref_t solid)
171 {
172     if (player->state == PLAYER_STATE_ALIVE) {
173         rigid_rect_collide_with_solid(player->alive_body, solid);
174
175         if (rigid_rect_touches_ground(player->alive_body)) {
176             player->jump_count = 0;
177         }
178     }
179 }
180
181 void player_impact_rigid_rect(player_t * player,
182                               rigid_rect_t *rigid_rect)
183 {
184     if (player->state == PLAYER_STATE_ALIVE) {
185         rigid_rect_impact_rigid_rect(player->alive_body, rigid_rect);
186     }
187 }
188
189 void player_move_left(player_t *player)
190 {
191     assert(player);
192     rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
193 }
194
195 void player_move_right(player_t *player)
196 {
197     assert(player);
198
199     rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
200 }
201
202 void player_stop(player_t *player)
203 {
204     assert(player);
205
206     rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
207 }
208
209 void player_jump(player_t *player)
210 {
211     assert(player);
212     if (player->jump_count < 2) {
213         rigid_rect_jump(player->alive_body, PLAYER_JUMP);
214         player->jump_count++;
215     }
216 }
217
218 void player_die(player_t *player)
219 {
220     assert(player);
221
222     if (player->state == PLAYER_STATE_ALIVE) {
223         player->play_die_cue = 1;
224         player->dying_body = RESET_LT(
225             player->lt,
226             player->dying_body,
227             create_dying_rect(
228                 rigid_rect_hitbox(player->alive_body),
229                 player->color,
230                 PLAYER_DEATH_DURATION));
231         player->state = PLAYER_STATE_DYING;
232     }
233 }
234
235 void player_focus_camera(player_t *player,
236                          camera_t *camera)
237 {
238     assert(player);
239     assert(camera);
240
241     const rect_t player_hitbox = rigid_rect_hitbox(player->alive_body);
242
243     camera_center_at(
244         camera,
245         vec_sum(
246             vec(player_hitbox.x, player_hitbox.y),
247             vec(0.0f, -player_hitbox.h * 0.5f)));
248 }
249
250 void player_hide_goals(const player_t *player,
251                        goals_t *goals)
252 {
253     assert(player);
254     assert(goals);
255     goals_hide(goals, rigid_rect_hitbox(player->alive_body));
256 }
257
258 void player_die_from_lava(player_t *player,
259                           const lava_t *lava)
260 {
261     if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
262         player_die(player);
263     }
264 }
265
266 void player_checkpoint(player_t *player, vec_t checkpoint)
267 {
268     player->checkpoint = checkpoint;
269 }
270
271 int player_sound(player_t *player,
272                  sound_samples_t *sound_samples)
273 {
274     if (player->play_die_cue) {
275         player->play_die_cue = 0;
276
277         if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
278             return -1;
279         }
280     }
281
282     return 0;
283 }
284
285 void player_rect_object_collide(player_t *player,
286                                 rect_t object,
287                                 int sides[RECT_SIDE_N])
288 {
289     if (player->state == PLAYER_STATE_ALIVE) {
290         rigid_rect_touches_rect_sides(player->alive_body, object, sides);
291     }
292 }