]> git.lizzy.rs Git - shadowclad.git/blobdiff - src/game/player.c
Implement sliding and crossing grid cell boundaries
[shadowclad.git] / src / game / player.c
index 4e12613f1948a9c41e08398bd2dceaff33ce3250..861b655b6a188f4807ee5bdf91bad11eb8276849 100644 (file)
-#include "level.h"
-#include "logger.h"
+/**
+ * Copyright 2019-2020 Iwo 'Outfrost' Bujkiewicz
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
 #include "player.h"
 
-Character playerCharacter = { .asset3D = NULL };
+#include "engine/asset.h"
+#include "engine/logger.h"
+#include "engine/render.h"
+
+#include "level.h"
+
+static const float movementSpeed = 0.5f;
+static const float collisionRadius = 0.5f;
+
+Scene* playerCharacter;
+Scene* playerProjectedMovement;
+static Transform screenToWorldMovementTransform;
+static Vector worldMovementUp;
+static Vector worldMovementDown;
+static Vector worldMovementLeft;
+static Vector worldMovementRight;
+static Direction movementDirection;
+
+static void movePlayer(Vector direction, float delta);
+static Vector worldMovementDirection(float x, float y);
 
 
 
 void initPlayer() {
-       playerCharacter.asset3D = importAsset("assets/playercharacter.3ds");
+       screenToWorldMovementTransform = identity();
+       rotate(&screenToWorldMovementTransform, (Vector) { 0.0f, 1.0f, 0.0f }, - TAU / 8.0f);
+
+       worldMovementUp = worldMovementDirection(0.0f, 1.0f);
+       worldMovementDown = worldMovementDirection(0.0f, -1.0f);
+       worldMovementLeft = worldMovementDirection(-1.0f, 0.0f);
+       worldMovementRight = worldMovementDirection(1.0f, 0.0f);
+
+       playerCharacter = newScene();
+       cameraAnchor = playerCharacter;
+       playerCharacter->solid = importSolid("assets/playercharacter.3ds");
+
+       playerProjectedMovement = newScene();
+}
+
+void spawnPlayer(Transform transform) {
+       playerCharacter->transform = transform;
+       reparentScene(playerCharacter, currentScene);
+       reparentScene(playerProjectedMovement, currentScene);
+}
+
+void updatePlayer(float delta) {
+       Vector direction = zeroVector();
+       if (movementDirection & DIRECTION_UP) {
+               direction = addVectors(direction, worldMovementUp);
+       }
+       if (movementDirection & DIRECTION_DOWN) {
+               direction = addVectors(direction, worldMovementDown);
+       }
+       if (movementDirection & DIRECTION_LEFT) {
+               direction = addVectors(direction, worldMovementLeft);
+       }
+       if (movementDirection & DIRECTION_RIGHT) {
+               direction = addVectors(direction, worldMovementRight);
+       }
+       movePlayer(direction, delta);
+}
+
+void startMovement(Direction direction) {
+       movementDirection |= direction;
+}
+
+void stopMovement(Direction direction) {
+       movementDirection &= ~direction;
+}
+
+static void movePlayer(Vector direction, float delta) {
+       direction = clampMagnitude(direction, 1.0f);
+       Vector displacement = scaleVector(direction, delta * movementSpeed);
+
+{
+Vector displacement = scaleVector(direction, 0.006944f * movementSpeed * 1000.0f);
+
+       playerProjectedMovement->transform = playerCharacter->transform;
+
+       Vector initialPosition = translationOf(playerCharacter->transform);
+       Vector position = initialPosition;
+
+       GridLocation location = gridLocationFromPosition(position);
+       bool enteredNewCell = true;
+
+       while (enteredNewCell) {
+               enteredNewCell = false;
+               Obstacle obstacle = getObstacles(location);
+
+               // Eliminate redundant corner checks
+               if (obstacle & OBSTACLE_XP) {
+                       obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN);
+               }
+               if (obstacle & OBSTACLE_XN) {
+                       obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN);
+               }
+               if (obstacle & OBSTACLE_ZP) {
+                       obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP);
+               }
+               if (obstacle & OBSTACLE_ZN) {
+                       obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN);
+               }
+
+               float edgeXp = cellBoundaryCoord(location.x + 1);
+               float edgeXn = cellBoundaryCoord(location.x);
+               float edgeZp = cellBoundaryCoord(location.z + 1);
+               float edgeZn = cellBoundaryCoord(location.z);
+               float distanceXp = edgeXp - position.x;
+               if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius;
+               float distanceXn = edgeXn - position.x;
+               if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius;
+               float distanceZp = edgeZp - position.z;
+               if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius;
+               float distanceZn = edgeZn - position.z;
+               if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius;
+
+               // Check all edges for intersecting already
+               if (distanceXp < 0.0f) {
+                       position.x += distanceXp;
+                       displacement = growVectorNoFlip(displacement, distanceXp);
+                       distanceXp = 0.0f;
+               }
+               if (distanceXn > 0.0f) {
+                       position.x += distanceXn;
+                       displacement = growVectorNoFlip(displacement, - distanceXn);
+                       distanceXn = 0.0f;
+               }
+               if (distanceZp < 0.0f) {
+                       position.z += distanceZp;
+                       displacement = growVectorNoFlip(displacement, distanceZp);
+                       distanceZp = 0.0f;
+               }
+               if (distanceZn > 0.0f) {
+                       position.z += distanceZn;
+                       displacement = growVectorNoFlip(displacement, - distanceZn);
+                       distanceZn = 0.0f;
+               }
+
+               // Calculate direct movement limits
+               Vector displacementToLimit = displacement;
+               bool reachedXp = false;
+               bool reachedXn = false;
+               bool reachedZp = false;
+               bool reachedZn = false;
+               if (displacementToLimit.x > distanceXp) {
+                       displacementToLimit = scaleVector(
+                               displacementToLimit, distanceXp / displacementToLimit.x);
+                       reachedXp = true;
+               }
+               if (displacementToLimit.x < distanceXn) {
+                       displacementToLimit = scaleVector(
+                               displacementToLimit, distanceXn / displacementToLimit.x);
+                       reachedXn = true;
+               }
+               if (displacementToLimit.z > distanceZp) {
+                       displacementToLimit = scaleVector(
+                               displacementToLimit, distanceZp / displacementToLimit.z);
+                       reachedZp = true;
+               }
+               if (displacementToLimit.z < distanceZn) {
+                       displacementToLimit = scaleVector(
+                               displacementToLimit, distanceZn / displacementToLimit.z);
+                       reachedZn = true;
+               }
+               // This is how far we can move until we collide or leave the grid cell
+               position = addVectors(position, displacementToLimit);
+               displacement = subtractVectors(displacement, displacementToLimit);
+               // Update distances
+               distanceXp -= displacementToLimit.x;
+               distanceXn -= displacementToLimit.x;
+               distanceZp -= displacementToLimit.z;
+               distanceZn -= displacementToLimit.z;
+
+               // Resolve slides and crossing cell boundaries
+               // in reverse order to direct movement limits, because
+               // we only want to cross the closest cell boundary.
+               if (reachedZn) {
+                       if (obstacle & OBSTACLE_ZN) {
+                               float dx = clamp(displacement.x, distanceXn, distanceXp);
+                               position.x += dx;
+                               displacement = scaleVector(displacement, 1.0f - (dx / displacement.x));
+                       }
+                       else {
+                               location.z -= 1;
+                               enteredNewCell = true;
+                       }
+               }
+
+               if (reachedZp) {
+                       if (obstacle & OBSTACLE_ZP) {
+                               float dx = clamp(displacement.x, distanceXn, distanceXp);
+                               position.x += dx;
+                               displacement = scaleVector(displacement, 1.0f - (dx / displacement.x));
+                       }
+                       else if (!enteredNewCell) {
+                               location.z += 1;
+                               enteredNewCell = true;
+                       }
+               }
+
+               if (reachedXn) {
+                       if (obstacle & OBSTACLE_XN) {
+                               float dz = clamp(displacement.z, distanceZn, distanceZp);
+                               position.z += dz;
+                               displacement = scaleVector(displacement, 1.0f - (dz / displacement.z));
+                       }
+                       else if (!enteredNewCell) {
+                               location.x -= 1;
+                               enteredNewCell = true;
+                       }
+               }
+
+               if (reachedXp) {
+                       if (obstacle & OBSTACLE_XP) {
+                               // Slide
+                               float dz = clamp(displacement.z, distanceZn, distanceZp);
+                               position.z += dz;
+                               displacement = scaleVector(displacement, 1.0f - (dz / displacement.z));
+                       }
+                       else if (!enteredNewCell) {
+                               // Enter new grid cell
+                               location.x += 1;
+                               enteredNewCell = true;
+                       }
+               }
+       }
+       translate(&playerProjectedMovement->transform, subtractVectors(position, initialPosition));
+
+       //translate(&playerCharacter->transform, subtractVectors(position, initialPosition));
+}
+
+
+       translate(&playerCharacter->transform, displacement);
 }
 
-void spawnPlayer() {
-       playerPos = playerSpawnPos;
+static Vector worldMovementDirection(float x, float y) {
+       Vector direction = (Vector) { x, 0.0f, -y };
+       direction = normalized(
+               applyTransform(screenToWorldMovementTransform, direction));
+       return direction;
 }