+/**
+ * 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"
+
#include "engine/asset.h"
+#include "engine/logger.h"
#include "engine/render.h"
-#include "player.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() {
+ 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);
+}
+
+static Vector worldMovementDirection(float x, float y) {
+ Vector direction = (Vector) { x, 0.0f, -y };
+ direction = normalized(
+ applyTransform(screenToWorldMovementTransform, direction));
+ return direction;
}