aboutsummaryrefslogtreecommitdiff
path: root/player.c
diff options
context:
space:
mode:
Diffstat (limited to 'player.c')
-rw-r--r--player.c184
1 files changed, 63 insertions, 121 deletions
diff --git a/player.c b/player.c
index 01c56ca..52788f1 100644
--- a/player.c
+++ b/player.c
@@ -1,55 +1,24 @@
1#include "all.h" 1#include "all.h"
2#include "raymath.h" 2#include "raymath.h"
3
3#include <float.h> 4#include <float.h>
4 5
5static void PlayerRotate(void) { 6static void PlayerRotate(void) {
6 if (!game.cursor_captured) return; 7 if (!game.cursor_captured) return;
7 8
8 Vector2 mouseDelta = GetMouseDelta(); 9 Vector2 mouseDelta = GetMouseDelta();
9 game.yaw += -mouseDelta.x * PLAYER_MOUSE_SENSITIVITY; 10 game.player.yaw += -mouseDelta.x * PLAYER_MOUSE_SENSITIVITY;
10 game.pitch += mouseDelta.y * PLAYER_MOUSE_SENSITIVITY; 11 game.player.pitch += mouseDelta.y * PLAYER_MOUSE_SENSITIVITY;
11 12
12 // Clamp pitch to avoid gimbal lock/flipping (approx 89 degrees) 13 if (game.player.pitch > 89.0f * DEG2RAD) game.player.pitch = 89.0f * DEG2RAD;
13 if (game.pitch > 89.0f * DEG2RAD) game.pitch = 89.0f * DEG2RAD; 14 if (game.player.pitch < -89.0f * DEG2RAD) game.player.pitch = -89.0f * DEG2RAD;
14 if (game.pitch < -89.0f * DEG2RAD) game.pitch = -89.0f * DEG2RAD;
15}
16
17static bool CheckWorldCollision(Vector3 start, Vector3 end, RayCollision *outCollision) {
18 Vector3 diff = Vector3Subtract(end, start);
19 float maxDist = Vector3Length(diff);
20 if (maxDist < 0.001f) return false;
21
22 Ray ray = { 0 };
23 ray.position = start;
24 ray.direction = Vector3Scale(diff, 1.0f / maxDist);
25
26 RayCollision closest = { 0 };
27 closest.distance = FLT_MAX;
28 closest.hit = false;
29
30 for (int i = 0; i < game.world_model_count; i++) {
31 Model model = game.world_models[i];
32 for (int m = 0; m < model.meshCount; m++) {
33 RayCollision col = GetRayCollisionMesh(ray, model.meshes[m], model.transform);
34 if (col.hit && col.distance < closest.distance && col.distance <= maxDist) {
35 closest = col;
36 }
37 }
38 }
39
40 if (closest.hit) {
41 if (outCollision) *outCollision = closest;
42 return true;
43 }
44 return false;
45} 15}
46 16
47static void MoveNormal(float dt) { 17static void MoveNormal(float dt) {
48 float eyeHeight = PLAYER_EYE_HEIGHT - (game.crouch_amount * PLAYER_CROUCH_OFFSET); 18 float eyeHeight = PLAYER_EYE_HEIGHT - (game.player.crouch_amount * PLAYER_CROUCH_OFFSET);
49 19
50 // Movement vectors based on yaw 20 Vector3 forward = { sinf(game.player.yaw), 0, cosf(game.player.yaw) };
51 Vector3 forward = { sinf(game.yaw), 0, cosf(game.yaw) }; 21 Vector3 right = { sinf(game.player.yaw - PI/2.0f), 0, cosf(game.player.yaw - PI/2.0f) };
52 Vector3 right = { sinf(game.yaw - PI/2.0f), 0, cosf(game.yaw - PI/2.0f) };
53 22
54 Vector3 moveDir = { 0 }; 23 Vector3 moveDir = { 0 };
55 if (IsKeyDown(KEY_W)) moveDir = Vector3Add(moveDir, forward); 24 if (IsKeyDown(KEY_W)) moveDir = Vector3Add(moveDir, forward);
@@ -59,24 +28,19 @@ static void MoveNormal(float dt) {
59 28
60 float speed = PLAYER_MOVE_SPEED; 29 float speed = PLAYER_MOVE_SPEED;
61 if (IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)) speed *= PLAYER_SPRINT_MULTIPLIER; 30 if (IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)) speed *= PLAYER_SPRINT_MULTIPLIER;
62 31 speed *= Lerp(1.0f, PLAYER_CROUCH_MOVE_MULTIPLIER, game.player.crouch_amount);
63 // Apply crouch speed penalty
64 speed *= Lerp(1.0f, PLAYER_CROUCH_MOVE_MULTIPLIER, game.crouch_amount);
65 32
66 if (Vector3Length(moveDir) > 0) { 33 if (Vector3Length(moveDir) > 0) {
67 moveDir = Vector3Scale(Vector3Normalize(moveDir), speed * dt); 34 moveDir = Vector3Scale(Vector3Normalize(moveDir), speed * dt);
68 } 35 }
69 36
70 // Apply gravity 37 game.player.velocity.y -= PLAYER_GRAVITY * dt;
71 game.velocity.y -= PLAYER_GRAVITY * dt;
72 38
73 // Jump 39 if (game.player.is_grounded && IsKeyPressed(KEY_SPACE)) {
74 if (game.is_grounded && IsKeyPressed(KEY_SPACE)) { 40 game.player.velocity.y = PLAYER_JUMP_FORCE;
75 game.velocity.y = PLAYER_JUMP_FORCE; 41 game.player.is_grounded = false;
76 game.is_grounded = false;
77 } 42 }
78 43
79 // Horizontal movement with sliding collision
80 Vector3 remainingMove = moveDir; 44 Vector3 remainingMove = moveDir;
81 for (int iter = 0; iter < 4 && Vector3Length(remainingMove) > 0.001f; iter++) { 45 for (int iter = 0; iter < 4 && Vector3Length(remainingMove) > 0.001f; iter++) {
82 RayCollision closestHit = { 0 }; 46 RayCollision closestHit = { 0 };
@@ -84,7 +48,7 @@ static void MoveNormal(float dt) {
84 bool hitFound = false; 48 bool hitFound = false;
85 49
86 float heights[] = { -eyeHeight + 10.0f, -eyeHeight / 2.0f, 0.0f }; 50 float heights[] = { -eyeHeight + 10.0f, -eyeHeight / 2.0f, 0.0f };
87 Vector3 currentPos = game.pos; 51 Vector3 currentPos = game.player.pos;
88 52
89 for (int i = 0; i < 3; i++) { 53 for (int i = 0; i < 3; i++) {
90 Vector3 start = Vector3Add(currentPos, (Vector3){0, heights[i], 0}); 54 Vector3 start = Vector3Add(currentPos, (Vector3){0, heights[i], 0});
@@ -93,8 +57,7 @@ static void MoveNormal(float dt) {
93 Vector3 end = Vector3Add(start, Vector3Scale(dir, dist)); 57 Vector3 end = Vector3Add(start, Vector3Scale(dir, dist));
94 58
95 RayCollision col; 59 RayCollision col;
96 if (CheckWorldCollision(start, end, &col)) { 60 if (CheckMapCollision(start, end, &col)) {
97 // Adjust distance to be relative to the player boundary
98 float adjustedDist = col.distance - PLAYER_RADIUS; 61 float adjustedDist = col.distance - PLAYER_RADIUS;
99 if (adjustedDist < closestHit.distance) { 62 if (adjustedDist < closestHit.distance) {
100 closestHit = col; 63 closestHit = col;
@@ -105,13 +68,11 @@ static void MoveNormal(float dt) {
105 } 68 }
106 69
107 if (hitFound) { 70 if (hitFound) {
108 // Move as far as possible
109 float moveDist = fmaxf(0, closestHit.distance - 0.1f); 71 float moveDist = fmaxf(0, closestHit.distance - 0.1f);
110 Vector3 moveStep = Vector3Scale(Vector3Normalize(remainingMove), moveDist); 72 Vector3 moveStep = Vector3Scale(Vector3Normalize(remainingMove), moveDist);
111 game.pos.x += moveStep.x; 73 game.player.pos.x += moveStep.x;
112 game.pos.z += moveStep.z; 74 game.player.pos.z += moveStep.z;
113 75
114 // Project remaining movement onto the plane of the wall
115 Vector3 slideNormal = { closestHit.normal.x, 0, closestHit.normal.z }; 76 Vector3 slideNormal = { closestHit.normal.x, 0, closestHit.normal.z };
116 if (Vector3Length(slideNormal) > 0.001f) { 77 if (Vector3Length(slideNormal) > 0.001f) {
117 slideNormal = Vector3Normalize(slideNormal); 78 slideNormal = Vector3Normalize(slideNormal);
@@ -122,68 +83,61 @@ static void MoveNormal(float dt) {
122 remainingMove = (Vector3){0, 0, 0}; 83 remainingMove = (Vector3){0, 0, 0};
123 } 84 }
124 } else { 85 } else {
125 // No collision found, move the rest of the way 86 game.player.pos.x += remainingMove.x;
126 game.pos.x += remainingMove.x; 87 game.player.pos.z += remainingMove.z;
127 game.pos.z += remainingMove.z;
128 break; 88 break;
129 } 89 }
130 } 90 }
131 91
132 // Vertical movement with collision 92 float verticalMove = game.player.velocity.y * dt;
133 float verticalMove = game.velocity.y * dt; 93 Vector3 vStart = game.player.pos;
134 Vector3 vStart = game.pos;
135 94
136 if (verticalMove < 0) { // Falling/Down 95 if (verticalMove < 0) {
137 Vector3 start = vStart; 96 Vector3 start = vStart;
138 // Check slightly below feet
139 Vector3 end = Vector3Add(vStart, (Vector3){0, verticalMove - eyeHeight, 0}); 97 Vector3 end = Vector3Add(vStart, (Vector3){0, verticalMove - eyeHeight, 0});
140 RayCollision vCol; 98 RayCollision vCol;
141 if (CheckWorldCollision(start, end, &vCol) && vCol.normal.y > 0.5f) { 99 if (CheckMapCollision(start, end, &vCol) && vCol.normal.y > 0.5f) {
142 game.pos.y = vCol.point.y + eyeHeight; 100 game.player.pos.y = vCol.point.y + eyeHeight;
143 game.velocity.y = 0; 101 game.player.velocity.y = 0;
144 game.is_grounded = true; 102 game.player.is_grounded = true;
145 } else { 103 } else {
146 game.pos.y += verticalMove; 104 game.player.pos.y += verticalMove;
147 game.is_grounded = false; 105 game.player.is_grounded = false;
148 } 106 }
149 } else if (verticalMove > 0) { // Jumping/Up 107 } else if (verticalMove > 0) {
150 Vector3 start = vStart; 108 Vector3 start = vStart;
151 // Check above head
152 float headHeight = PLAYER_HEIGHT - PLAYER_EYE_HEIGHT; 109 float headHeight = PLAYER_HEIGHT - PLAYER_EYE_HEIGHT;
153 Vector3 end = Vector3Add(vStart, (Vector3){0, verticalMove + headHeight, 0}); 110 Vector3 end = Vector3Add(vStart, (Vector3){0, verticalMove + headHeight, 0});
154 RayCollision vCol; 111 RayCollision vCol;
155 if (CheckWorldCollision(start, end, &vCol)) { 112 if (CheckMapCollision(start, end, &vCol)) {
156 game.pos.y = vCol.point.y - headHeight - 1.0f; 113 game.player.pos.y = vCol.point.y - headHeight - 1.0f;
157 game.velocity.y = 0; 114 game.player.velocity.y = 0;
158 } else { 115 } else {
159 game.pos.y += verticalMove; 116 game.player.pos.y += verticalMove;
160 } 117 }
161 game.is_grounded = false; 118 game.player.is_grounded = false;
162 } else { 119 } else {
163 // Not moving vertically, but check if we are still on ground
164 Vector3 start = vStart; 120 Vector3 start = vStart;
165 Vector3 end = Vector3Add(vStart, (Vector3){0, -eyeHeight - 2.0f, 0}); 121 Vector3 end = Vector3Add(vStart, (Vector3){0, -eyeHeight - 2.0f, 0});
166 RayCollision vCol; 122 RayCollision vCol;
167 if (CheckWorldCollision(start, end, &vCol) && vCol.normal.y > 0.5f) { 123 if (CheckMapCollision(start, end, &vCol) && vCol.normal.y > 0.5f) {
168 game.is_grounded = true; 124 game.player.is_grounded = true;
169 // Snap to floor if very close
170 if (vCol.distance < eyeHeight + 1.0f) { 125 if (vCol.distance < eyeHeight + 1.0f) {
171 game.pos.y = vCol.point.y + eyeHeight; 126 game.player.pos.y = vCol.point.y + eyeHeight;
172 } 127 }
173 } else { 128 } else {
174 game.is_grounded = false; 129 game.player.is_grounded = false;
175 } 130 }
176 } 131 }
177} 132}
178 133
179static void MoveFly(float dt) { 134static void MoveFly(float dt) {
180 // Full 3D movement based on yaw/pitch
181 Vector3 forward = { 135 Vector3 forward = {
182 cosf(game.pitch) * sinf(game.yaw), 136 cosf(game.player.pitch) * sinf(game.player.yaw),
183 -sinf(game.pitch), 137 -sinf(game.player.pitch),
184 cosf(game.pitch) * cosf(game.yaw) 138 cosf(game.player.pitch) * cosf(game.player.yaw)
185 }; 139 };
186 Vector3 right = { sinf(game.yaw - PI/2.0f), 0, cosf(game.yaw - PI/2.0f) }; 140 Vector3 right = { sinf(game.player.yaw - PI/2.0f), 0, cosf(game.player.yaw - PI/2.0f) };
187 141
188 Vector3 move = { 0 }; 142 Vector3 move = { 0 };
189 if (IsKeyDown(KEY_W)) move = Vector3Add(move, forward); 143 if (IsKeyDown(KEY_W)) move = Vector3Add(move, forward);
@@ -191,7 +145,6 @@ static void MoveFly(float dt) {
191 if (IsKeyDown(KEY_D)) move = Vector3Add(move, right); 145 if (IsKeyDown(KEY_D)) move = Vector3Add(move, right);
192 if (IsKeyDown(KEY_A)) move = Vector3Subtract(move, right); 146 if (IsKeyDown(KEY_A)) move = Vector3Subtract(move, right);
193 147
194 // Fly up/down
195 if (IsKeyDown(KEY_SPACE)) move.y += 1.0f; 148 if (IsKeyDown(KEY_SPACE)) move.y += 1.0f;
196 if (IsKeyDown(KEY_LEFT_CONTROL)) move.y -= 1.0f; 149 if (IsKeyDown(KEY_LEFT_CONTROL)) move.y -= 1.0f;
197 150
@@ -200,86 +153,75 @@ static void MoveFly(float dt) {
200 153
201 if (Vector3Length(move) > 0) { 154 if (Vector3Length(move) > 0) {
202 move = Vector3Scale(Vector3Normalize(move), speed * dt); 155 move = Vector3Scale(Vector3Normalize(move), speed * dt);
203 game.pos = Vector3Add(game.pos, move); 156 game.player.pos = Vector3Add(game.player.pos, move);
204 } 157 }
205 158
206 game.velocity = (Vector3){0, 0, 0}; 159 game.player.velocity = (Vector3){0, 0, 0};
207 game.is_grounded = false; 160 game.player.is_grounded = false;
208} 161}
209 162
210void UpdatePlayer(void) { 163void UpdatePlayer(void) {
211 float dt = GetFrameTime(); 164 float dt = GetFrameTime();
212 Vector3 oldPos = game.pos; 165 Vector3 oldPos = game.player.pos;
213 166
214 if (IsKeyPressed(KEY_F)) { 167 if (IsKeyPressed(KEY_F)) {
215 game.move_mode = (game.move_mode == MOVE_NORMAL) ? MOVE_FLY : MOVE_NORMAL; 168 game.player.move_mode = (game.player.move_mode == MOVE_NORMAL) ? MOVE_FLY : MOVE_NORMAL;
216 TraceLog(LOG_INFO, "Movement mode: %s", (game.move_mode == MOVE_NORMAL) ? "NORMAL" : "FLY");
217 } 169 }
218 170
219 PlayerRotate(); 171 PlayerRotate();
220 172
221 // Crouching logic
222 bool canStand = true; 173 bool canStand = true;
223 if (game.crouch_amount > 0.1f) { 174 if (game.player.crouch_amount > 0.1f) {
224 Vector3 headPos = game.pos; 175 Vector3 headPos = game.player.pos;
225 Vector3 headEnd = Vector3Add(headPos, (Vector3){0, PLAYER_HEIGHT - PLAYER_EYE_HEIGHT + PLAYER_CROUCH_OFFSET, 0}); 176 Vector3 headEnd = Vector3Add(headPos, (Vector3){0, PLAYER_HEIGHT - PLAYER_EYE_HEIGHT + PLAYER_CROUCH_OFFSET, 0});
226 RayCollision col; 177 RayCollision col;
227 if (CheckWorldCollision(headPos, headEnd, &col)) { 178 if (CheckMapCollision(headPos, headEnd, &col)) {
228 canStand = false; 179 canStand = false;
229 } 180 }
230 } 181 }
231 182
232 float targetCrouch = 0.0f; 183 float targetCrouch = 0.0f;
233 if (IsKeyDown(KEY_LEFT_CONTROL) || !canStand) targetCrouch = 1.0f; 184 if (IsKeyDown(KEY_LEFT_CONTROL) || !canStand) targetCrouch = 1.0f;
234 game.crouch_amount = Lerp(game.crouch_amount, targetCrouch, PLAYER_CROUCH_SPEED * dt); 185 game.player.crouch_amount = Lerp(game.player.crouch_amount, targetCrouch, PLAYER_CROUCH_SPEED * dt);
235 186
236 // Leaning logic
237 float targetLean = 0.0f; 187 float targetLean = 0.0f;
238 if (IsKeyDown(KEY_Q)) targetLean -= 1.0f; 188 if (IsKeyDown(KEY_Q)) targetLean -= 1.0f;
239 if (IsKeyDown(KEY_E)) targetLean += 1.0f; 189 if (IsKeyDown(KEY_E)) targetLean += 1.0f;
190 game.player.lean_amount = Lerp(game.player.lean_amount, targetLean, PLAYER_LEAN_SPEED * dt);
240 191
241 game.lean_amount = Lerp(game.lean_amount, targetLean, PLAYER_LEAN_SPEED * dt); 192 if (game.player.move_mode == MOVE_FLY) {
242
243 if (game.move_mode == MOVE_FLY) {
244 MoveFly(dt); 193 MoveFly(dt);
245 } else { 194 } else {
246 MoveNormal(dt); 195 MoveNormal(dt);
247 } 196 }
248 197
249 // Apply lean as a pivot from the neck/waist 198 float leanAngle = game.player.lean_amount * PLAYER_LEAN_ANGLE * DEG2RAD;
250 float leanAngle = game.lean_amount * PLAYER_LEAN_ANGLE * DEG2RAD; 199 Vector3 bodyForward = { sinf(game.player.yaw), 0, cosf(game.player.yaw) };
251 Vector3 bodyForward = { sinf(game.yaw), 0, cosf(game.yaw) };
252 200
253 float currentEyeHeight = PLAYER_EYE_HEIGHT - (game.crouch_amount * PLAYER_CROUCH_OFFSET); 201 float currentEyeHeight = PLAYER_EYE_HEIGHT - (game.player.crouch_amount * PLAYER_CROUCH_OFFSET);
254 float pivotDist = fminf(PLAYER_LEAN_PIVOT_DISTANCE, currentEyeHeight * 0.8f); 202 float pivotDist = fminf(PLAYER_LEAN_PIVOT_DISTANCE, currentEyeHeight * 0.8f);
255 203
256 Vector3 neckToEye = { 0, pivotDist, 0 }; 204 Vector3 neckToEye = { 0, pivotDist, 0 };
257 Vector3 rotatedOffset = Vector3RotateByAxisAngle(neckToEye, bodyForward, leanAngle); 205 Vector3 rotatedOffset = Vector3RotateByAxisAngle(neckToEye, bodyForward, leanAngle);
258 Vector3 pivot = Vector3Subtract(game.pos, (Vector3){ 0, pivotDist, 0 }); 206 Vector3 pivot = Vector3Subtract(game.player.pos, (Vector3){ 0, pivotDist, 0 });
259 207
260 // Update camera based on physical pos + visual lean pivot
261 game.camera.position = Vector3Add(pivot, rotatedOffset); 208 game.camera.position = Vector3Add(pivot, rotatedOffset);
262 209
263 // Apply roll to up vector
264 Vector3 forward = { 210 Vector3 forward = {
265 cosf(game.pitch) * sinf(game.yaw), 211 cosf(game.player.pitch) * sinf(game.player.yaw),
266 -sinf(game.pitch), 212 -sinf(game.player.pitch),
267 cosf(game.pitch) * cosf(game.yaw) 213 cosf(game.player.pitch) * cosf(game.player.yaw)
268 }; 214 };
269 215
270 // Use a stable world-up vector and apply lean roll
271 Vector3 up = { 0, 1, 0 }; 216 Vector3 up = { 0, 1, 0 };
272 up = Vector3RotateByAxisAngle(up, forward, leanAngle); 217 up = Vector3RotateByAxisAngle(up, forward, leanAngle);
273 game.camera.up = up; 218 game.camera.up = up;
274
275 // Use a longer target distance for better precision in the projection matrix
276 game.camera.target = Vector3Add(game.camera.position, Vector3Scale(forward, 20.0f)); 219 game.camera.target = Vector3Add(game.camera.position, Vector3Scale(forward, 20.0f));
277 220
278 // Calculate horizontal speed for UI
279 if (dt > 0) { 221 if (dt > 0) {
280 Vector2 velH = { game.pos.x - oldPos.x, game.pos.z - oldPos.z }; 222 Vector2 velH = { game.player.pos.x - oldPos.x, game.player.pos.z - oldPos.z };
281 game.horizontal_speed = Vector2Length(velH) / dt; 223 game.player.horizontal_speed = Vector2Length(velH) / dt;
282 } else { 224 } else {
283 game.horizontal_speed = 0; 225 game.player.horizontal_speed = 0;
284 } 226 }
285} 227}