]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
Delete LineStream from the existance
[nothing.git] / src / game / level / player.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include <SDL.h>
5
6 #include "game/level/explosion.h"
7 #include "game/level/rigid_bodies.h"
8 #include "goals.h"
9 #include "math/vec.h"
10 #include "platforms.h"
11 #include "player.h"
12 #include "system/log.h"
13 #include "system/lt.h"
14 #include "system/nth_alloc.h"
15 #include "system/stacktrace.h"
16 #include "config.h"
17
18 #define PLAYER_WIDTH 25.0f
19 #define PLAYER_HEIGHT 25.0f
20 #define PLAYER_SPEED 500.0f
21 #define PLAYER_JUMP 32000.0f
22 #define PLAYER_DEATH_DURATION 0.75f
23 #define PLAYER_MAX_JUMP_THRESHOLD 2
24
25 typedef enum Player_state {
26     PLAYER_STATE_ALIVE = 0,
27     PLAYER_STATE_DYING
28 } Player_state;
29
30 struct Player {
31     Lt *lt;
32     Player_state state;
33
34     RigidBodies *rigid_bodies;
35
36     RigidBodyId alive_body_id;
37     Explosion *dying_body;
38
39     int jump_threshold;
40     Color color;
41
42     Vec2f checkpoint;
43
44     int play_die_cue;
45 };
46
47 Player *create_player_from_player_layer(const PlayerLayer *player_layer,
48                                         RigidBodies *rigid_bodies)
49 {
50     trace_assert(player_layer);
51     trace_assert(rigid_bodies);
52
53     Lt *lt = create_lt();
54
55     Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
56     if (player == NULL) {
57         RETURN_LT(lt, NULL);
58     }
59     player->lt = lt;
60
61     player->rigid_bodies = rigid_bodies;
62
63     player->alive_body_id = rigid_bodies_add(
64         rigid_bodies,
65         rect(
66             player_layer->position.x,
67             player_layer->position.y,
68             PLAYER_WIDTH,
69             PLAYER_HEIGHT));
70
71     player->dying_body = PUSH_LT(
72         lt,
73         create_explosion(
74             color_picker_rgba(&player_layer->color_picker),
75             PLAYER_DEATH_DURATION),
76         destroy_explosion);
77     if (player->dying_body == NULL) {
78         RETURN_LT(lt, NULL);
79     }
80
81     player->jump_threshold = 0;
82     player->color = color_picker_rgba(&player_layer->color_picker);
83     player->checkpoint = player_layer->position;
84     player->play_die_cue = 0;
85     player->state = PLAYER_STATE_ALIVE;
86
87     return player;
88 }
89
90 void destroy_player(Player * player)
91 {
92     rigid_bodies_remove(player->rigid_bodies, player->alive_body_id);
93     RETURN_LT0(player->lt);
94 }
95
96 int player_render(const Player * player,
97                   const Camera *camera)
98 {
99     trace_assert(player);
100     trace_assert(camera);
101
102     char debug_text[256];
103
104     switch (player->state) {
105     case PLAYER_STATE_ALIVE: {
106         snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
107         Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
108
109         if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
110             return -1;
111         }
112
113         return rigid_bodies_render(
114             player->rigid_bodies,
115             player->alive_body_id,
116             player->color,
117             camera);
118     }
119
120     case PLAYER_STATE_DYING:
121         return explosion_render(player->dying_body, camera);
122
123     default: {}
124     }
125
126     return 0;
127 }
128
129 void player_update(Player *player,
130                    float delta_time)
131 {
132     trace_assert(player);
133
134     switch (player->state) {
135     case PLAYER_STATE_ALIVE: {
136         rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
137
138         const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
139
140
141         if (hitbox.y > PLAYER_DEATH_LEVEL) {
142             player_die(player);
143         }
144     } break;
145
146     case PLAYER_STATE_DYING: {
147         explosion_update(player->dying_body, delta_time);
148
149         if (explosion_is_done(player->dying_body)) {
150             rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
151             rigid_bodies_transform_velocity(
152                 player->rigid_bodies,
153                 player->alive_body_id,
154                 make_mat3x3(0.0f, 0.0f, 0.0f,
155                             0.0f, 0.0f, 0.0f,
156                             0.0f, 0.0f, 1.0f));
157             rigid_bodies_teleport_to(
158                 player->rigid_bodies,
159                 player->alive_body_id,
160                 player->checkpoint);
161             player->state = PLAYER_STATE_ALIVE;
162         }
163     } break;
164
165     default: {}
166     }
167 }
168
169 void player_move_left(Player *player)
170 {
171     trace_assert(player);
172     rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
173 }
174
175 void player_move_right(Player *player)
176 {
177     trace_assert(player);
178
179     rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
180 }
181
182 void player_stop(Player *player)
183 {
184     trace_assert(player);
185
186     rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
187 }
188
189 void player_jump(Player *player)
190 {
191     trace_assert(player);
192
193     if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
194         player->jump_threshold = 0;
195     }
196
197     if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
198         rigid_bodies_transform_velocity(
199             player->rigid_bodies,
200             player->alive_body_id,
201             make_mat3x3(1.0f, 0.0f, 0.0f,
202                         0.0f, 0.0f, 0.0f,
203                         0.0f, 0.0f, 1.0f));
204         rigid_bodies_apply_force(
205             player->rigid_bodies,
206             player->alive_body_id,
207             vec(0.0f, -PLAYER_JUMP));
208         player->jump_threshold++;
209     }
210 }
211
212 void player_die(Player *player)
213 {
214     trace_assert(player);
215
216     if (player->state == PLAYER_STATE_ALIVE) {
217         const Rect hitbox =
218             rigid_bodies_hitbox(
219                 player->rigid_bodies,
220                 player->alive_body_id);
221
222         player->play_die_cue = 1;
223         player->jump_threshold = 0;
224         explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
225         player->state = PLAYER_STATE_DYING;
226         rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
227     }
228 }
229
230 void player_focus_camera(Player *player,
231                          Camera *camera)
232 {
233     trace_assert(player);
234     trace_assert(camera);
235
236     const Rect player_hitbox = rigid_bodies_hitbox(
237         player->rigid_bodies,
238         player->alive_body_id);
239
240     camera_center_at(
241         camera,
242         vec_sum(
243             vec(player_hitbox.x, player_hitbox.y),
244             vec(0.0f, -player_hitbox.h * 0.5f)));
245 }
246
247 void player_die_from_lava(Player *player,
248                           const Lava *lava)
249 {
250     if (lava_overlaps_rect(
251             lava,
252             rigid_bodies_hitbox(
253                 player->rigid_bodies,
254                 player->alive_body_id))) {
255         player_die(player);
256     }
257 }
258
259 void player_checkpoint(Player *player, Vec2f checkpoint)
260 {
261     player->checkpoint = checkpoint;
262 }
263
264 int player_sound(Player *player,
265                  Sound_samples *sound_samples)
266 {
267     if (player->play_die_cue) {
268         player->play_die_cue = 0;
269
270         if (sound_samples_play_sound(sound_samples, 0) < 0) {
271             return -1;
272         }
273     }
274
275     return 0;
276 }
277
278 bool player_overlaps_rect(const Player *player,
279                           Rect rect)
280 {
281     trace_assert(player);
282
283     return player->state == PLAYER_STATE_ALIVE
284         && rects_overlap(
285             rect, rigid_bodies_hitbox(
286                 player->rigid_bodies,
287                 player->alive_body_id));
288 }
289
290 Rect player_hitbox(const Player *player)
291 {
292     return rigid_bodies_hitbox(
293         player->rigid_bodies,
294         player->alive_body_id);
295 }