]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
(#477) Replace all instances of malloc with nth_alloc
[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/line_stream.h"
14 #include "system/lt.h"
15 #include "system/nth_alloc.h"
16
17 #define PLAYER_WIDTH 25.0f
18 #define PLAYER_HEIGHT 25.0f
19 #define PLAYER_SPEED 500.0f
20 #define PLAYER_JUMP 32000.0f
21 #define PLAYER_DEATH_DURATION 0.75f
22 #define PLAYER_MAX_JUMP_COUNT 2
23
24 typedef enum Player_state {
25     PLAYER_STATE_ALIVE = 0,
26     PLAYER_STATE_DYING
27 } Player_state;
28
29 struct Player {
30     Lt *lt;
31     Player_state state;
32
33     Rigid_rect *alive_body;
34     Dying_rect *dying_body;
35
36     int jump_count;
37     Color color;
38
39     Vec checkpoint;
40
41     int play_die_cue;
42 };
43
44 Player *create_player(float x, float y, Color color)
45 {
46     Lt *lt = create_lt();
47
48     if (lt == NULL) {
49         return NULL;
50     }
51
52     Player *player = PUSH_LT(lt, nth_alloc(sizeof(Player)), free);
53     if (player == NULL) {
54         throw_error(ERROR_TYPE_LIBC);
55         RETURN_LT(lt, NULL);
56     }
57
58     player->state = PLAYER_STATE_ALIVE;
59
60     player->alive_body = PUSH_LT(
61         lt,
62         create_rigid_rect(
63             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
64             color,
65             "player"),
66         destroy_rigid_rect);
67     if (player->alive_body == NULL) {
68         RETURN_LT(lt, NULL);
69     }
70
71     player->dying_body = PUSH_LT(
72         lt,
73         create_dying_rect(
74             color,
75             PLAYER_DEATH_DURATION),
76         destroy_dying_rect);
77     if (player->dying_body == NULL) {
78         RETURN_LT(lt, NULL);
79     }
80
81     player->lt = lt;
82     player->jump_count = 0;
83     player->color = color;
84     player->checkpoint = vec(x, y);
85     player->play_die_cue = 0;
86
87     return player;
88 }
89
90 Player *create_player_from_line_stream(LineStream *line_stream)
91 {
92     float x = 0.0f, y = 0.0f;
93     char color[7];
94
95     if (sscanf(
96             line_stream_next(line_stream),
97             "%f%f%6s",
98             &x, &y, color) == EOF) {
99         throw_error(ERROR_TYPE_LIBC);
100         return NULL;
101     }
102
103     return create_player(x, y, color_from_hexstr(color));
104 }
105
106 void destroy_player(Player * player)
107 {
108     RETURN_LT0(player->lt);
109 }
110
111 Solid_ref player_as_solid(Player *player)
112 {
113     Solid_ref ref = {
114         .tag = SOLID_PLAYER,
115         .ptr = (void*) player
116     };
117
118     return ref;
119 }
120
121 int player_render(const Player * player,
122                   Camera *camera)
123 {
124     assert(player);
125     assert(camera);
126
127     switch (player->state) {
128     case PLAYER_STATE_ALIVE:
129         return rigid_rect_render(player->alive_body, camera);
130
131     case PLAYER_STATE_DYING:
132         return dying_rect_render(player->dying_body, camera);
133
134     default: {}
135     }
136
137     return 0;
138 }
139
140 void player_update(Player *player,
141                    float delta_time)
142 {
143     assert(player);
144
145     switch (player->state) {
146     case PLAYER_STATE_ALIVE: {
147         rigid_rect_update(player->alive_body, delta_time);
148
149         const Rect hitbox = rigid_rect_hitbox(player->alive_body);
150
151         if (hitbox.y > 1000.0f) {
152             player_die(player);
153         }
154     } break;
155
156     case PLAYER_STATE_DYING: {
157         dying_rect_update(player->dying_body, delta_time);
158
159         if (dying_rect_is_dead(player->dying_body)) {
160             rigid_rect_transform_velocity(
161                 player->alive_body,
162                 make_mat3x3(0.0f, 0.0f, 0.0f,
163                             0.0f, 0.0f, 0.0f,
164                             0.0f, 0.0f, 1.0f));
165             rigid_rect_teleport_to(player->alive_body, player->checkpoint);
166             player->state = PLAYER_STATE_ALIVE;
167         }
168     } break;
169
170     default: {}
171     }
172 }
173
174 void player_collide_with_solid(Player *player, Solid_ref solid)
175 {
176     if (player->state == PLAYER_STATE_ALIVE) {
177         rigid_rect_collide_with_solid(player->alive_body, solid);
178
179         if (rigid_rect_touches_ground(player->alive_body)) {
180             player->jump_count = 0;
181         }
182     }
183 }
184
185 void player_move_left(Player *player)
186 {
187     assert(player);
188     rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
189 }
190
191 void player_move_right(Player *player)
192 {
193     assert(player);
194
195     rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
196 }
197
198 void player_stop(Player *player)
199 {
200     assert(player);
201
202     rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
203 }
204
205 void player_jump(Player *player)
206 {
207     assert(player);
208     if (player->jump_count < PLAYER_MAX_JUMP_COUNT) {
209         rigid_rect_transform_velocity(player->alive_body,
210                                       make_mat3x3(1.0f, 0.0f, 0.0f,
211                                                   0.0f, 0.0f, 0.0f,
212                                                   0.0f, 0.0f, 1.0f));
213         rigid_rect_apply_force(player->alive_body,
214                                vec(0.0f, -PLAYER_JUMP));
215         player->jump_count++;
216     }
217 }
218
219 void player_die(Player *player)
220 {
221     assert(player);
222
223     if (player->state == PLAYER_STATE_ALIVE) {
224         const Rect hitbox =
225             rigid_rect_hitbox(player->alive_body);
226
227         player->play_die_cue = 1;
228         dying_rect_start_dying(player->dying_body, vec(hitbox.x, hitbox.y));
229         player->state = PLAYER_STATE_DYING;
230     }
231 }
232
233 void player_focus_camera(Player *player,
234                          Camera *camera)
235 {
236     assert(player);
237     assert(camera);
238
239     const Rect player_hitbox = rigid_rect_hitbox(player->alive_body);
240
241     camera_center_at(
242         camera,
243         vec_sum(
244             vec(player_hitbox.x, player_hitbox.y),
245             vec(0.0f, -player_hitbox.h * 0.5f)));
246 }
247
248 void player_hide_goals(const Player *player,
249                        Goals *goals)
250 {
251     assert(player);
252     assert(goals);
253     goals_hide(goals, rigid_rect_hitbox(player->alive_body));
254 }
255
256 void player_die_from_lava(Player *player,
257                           const Lava *lava)
258 {
259     if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
260         player_die(player);
261     }
262 }
263
264 void player_checkpoint(Player *player, Vec checkpoint)
265 {
266     player->checkpoint = checkpoint;
267 }
268
269 int player_sound(Player *player,
270                  Sound_samples *sound_samples)
271 {
272     if (player->play_die_cue) {
273         player->play_die_cue = 0;
274
275         if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
276             return -1;
277         }
278     }
279
280     return 0;
281 }
282
283 void player_touches_rect_sides(Player *player,
284                                Rect object,
285                                int sides[RECT_SIDE_N])
286 {
287     if (player->state == PLAYER_STATE_ALIVE) {
288         rigid_rect_touches_rect_sides(player->alive_body, object, sides);
289     }
290 }
291
292 void player_apply_force(Player *player, Vec force)
293 {
294     if (player->state == PLAYER_STATE_ALIVE) {
295         rigid_rect_apply_force(player->alive_body, force);
296     }
297 }
298
299 Rigid_rect *player_rigid_rect(Player *player, const char *id)
300 {
301     assert(player);
302     assert(id);
303
304     if (player->state == PLAYER_STATE_ALIVE) {
305         if (rigid_rect_has_id(player->alive_body, id)) {
306             return player->alive_body;
307         }
308     }
309
310     return NULL;
311 }
312
313 bool player_overlaps_rect(const Player *player,
314                           Rect rect)
315 {
316     assert(player);
317
318     return player->state == PLAYER_STATE_ALIVE
319         && rects_overlap(
320             rect, rigid_rect_hitbox(
321                 player->alive_body));
322 }