diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 58763cec..8d5c3add 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -20,10 +20,14 @@ #include "../core_defs.h" +#define _USE_MATH_DEFINES +#include + const float CAM_SHAKE_OFFSET = 0.025f; const float CAM_SHAKE_OFFSET_Y = 0.031f; const float CAM_SHAKE_SPEED = 1.75f; const float CAM_SHAKE_DELTA_K = 10.0f; +const float STEPS_SPEED = 1.75f; const float ZOOM_SPEED = 16.0f; const float CROUCH_ZOOM = 0.9f; const float RUN_ZOOM = 1.1f; @@ -33,8 +37,7 @@ const float CROUCH_SHIFT_Y = -0.2f; CameraControl::CameraControl(std::shared_ptr player, const CameraSettings& settings) : player(player), - camera(player->camera), - currentViewCamera(player->currentCamera), + camera(player->camera), settings(settings), offset(0.0f, 0.7f, 0.0f) { } @@ -46,7 +49,10 @@ void CameraControl::refresh() { void CameraControl::updateMouse(PlayerInput& input) { glm::vec2& cam = player->cam; - float sensitivity = (input.zoom ? settings.sensitivity / 4.f : settings.sensitivity); + float sensitivity = (input.zoom + ? settings.sensitivity / 4.f + : settings.sensitivity); + cam -= glm::degrees(Events::delta / (float)Window::height * sensitivity); if (cam.y < -89.9f) { @@ -66,66 +72,93 @@ void CameraControl::updateMouse(PlayerInput& input) { camera->rotate(glm::radians(cam.y), glm::radians(cam.x), 0); } -void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { - Hitbox* hitbox = player->hitbox.get(); +glm::vec3 CameraControl::updateCameraShaking(float delta) { + glm::vec3 offset {}; + auto hitbox = player->hitbox.get(); + const float k = CAM_SHAKE_DELTA_K; + const float oh = CAM_SHAKE_OFFSET; + const float ov = CAM_SHAKE_OFFSET_Y; + const glm::vec3& vel = hitbox->velocity; + interpVel = interpVel * (1.0f - delta * 5) + vel * delta * 0.1f; + if (hitbox->grounded && interpVel.y < 0.0f){ + interpVel.y *= -30.0f; + } + shake = shake * (1.0f - delta * k); + if (hitbox->grounded) { + float f = glm::length(glm::vec2(vel.x, vel.z)); + shakeTimer += delta * f * CAM_SHAKE_SPEED; + shake += f * delta * k; + } + offset += camera->right * glm::sin(shakeTimer) * oh * shake; + offset += camera->up * glm::abs(glm::cos(shakeTimer)) * ov * shake; + offset -= glm::min(interpVel * 0.05f, 1.0f); + return offset; +} + +void CameraControl::updateFovEffects(const PlayerInput& input, float delta) { + auto hitbox = player->hitbox.get(); + bool crouch = input.shift && hitbox->grounded && !input.sprint; + + float dt = fmin(1.0f, delta * ZOOM_SPEED); + float zoomValue = 1.0f; + if (crouch){ + offset += glm::vec3(0.f, CROUCH_SHIFT_Y, 0.f); + zoomValue = CROUCH_ZOOM; + } else if (input.sprint){ + zoomValue = RUN_ZOOM; + } + if (input.zoom) + zoomValue *= C_ZOOM; + camera->zoom = zoomValue * dt + camera->zoom * (1.0f - dt); +} + +// temporary solution +// more extensible but uglier +void CameraControl::switchCamera() { + const std::vector> playerCameras { + camera, player->tpCamera, player->spCamera + }; + + auto index = std::distance( + playerCameras.begin(), + std::find_if( + playerCameras.begin(), + playerCameras.end(), + [=](auto ptr) { + return ptr.get() == player->currentCamera.get(); + } + ) + ); + if (static_cast(index) != playerCameras.size()) { + index = (index + 1) % playerCameras.size(); + player->currentCamera = playerCameras.at(index); + } +} + +void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { offset = glm::vec3(0.0f, 0.7f, 0.0f); if (settings.shaking && !input.cheat) { - const float k = CAM_SHAKE_DELTA_K; - const float oh = CAM_SHAKE_OFFSET; - const float ov = CAM_SHAKE_OFFSET_Y; - const glm::vec3& vel = hitbox->velocity; - - interpVel = interpVel * (1.0f - delta * 5) + vel * delta * 0.1f; - if (hitbox->grounded && interpVel.y < 0.0f){ - interpVel.y *= -30.0f; - } - shake = shake * (1.0f - delta * k); - if (hitbox->grounded) { - float f = glm::length(glm::vec2(vel.x, vel.z)); - shakeTimer += delta * f * CAM_SHAKE_SPEED; - shake += f * delta * k; - } - offset += camera->right * glm::sin(shakeTimer) * oh * shake; - offset += camera->up * glm::abs(glm::cos(shakeTimer)) * ov * shake; - offset -= glm::min(interpVel * 0.05f, 1.0f); + offset += updateCameraShaking(delta); } - if (settings.fovEvents){ - bool crouch = input.shift && hitbox->grounded && !input.sprint; - - float dt = fmin(1.0f, delta * ZOOM_SPEED); - float zoomValue = 1.0f; - if (crouch){ - offset += glm::vec3(0.f, CROUCH_SHIFT_Y, 0.f); - zoomValue = CROUCH_ZOOM; - } else if (input.sprint){ - zoomValue = RUN_ZOOM; - } - if (input.zoom) - zoomValue *= C_ZOOM; - camera->zoom = zoomValue * dt + camera->zoom * (1.0f - dt); + updateFovEffects(input, delta); + } + if (input.cameraMode) { + switchCamera(); } auto spCamera = player->spCamera; auto tpCamera = player->tpCamera; - if (input.cameraMode) { //ugly but effective - if (player->currentCamera == camera) - player->currentCamera = tpCamera; - else if (player->currentCamera == spCamera) - player->currentCamera = camera; - else if (player->currentCamera == tpCamera) - player->currentCamera = spCamera; - } if (player->currentCamera == spCamera) { - spCamera->position = chunks->rayCastToObstacle(camera->position, camera->front, 3.0f) - 0.2f*(camera->front); + spCamera->position = chunks->rayCastToObstacle(camera->position, camera->front, 3.0f) - 0.2f * camera->front; spCamera->dir = -camera->dir; spCamera->front = -camera->front; } else if (player->currentCamera == tpCamera) { - tpCamera->position = chunks->rayCastToObstacle(camera->position, -camera->front, 3.0f) + 0.2f * (camera->front); + tpCamera->position = chunks->rayCastToObstacle(camera->position, -camera->front, 3.0f) + 0.2f * camera->front; tpCamera->dir = camera->dir; tpCamera->front = camera->front; } @@ -140,11 +173,60 @@ int PlayerController::selectedBlockStates = 0; PlayerController::PlayerController( Level* level, const EngineSettings& settings, - BlocksController* blocksController) - : level(level), - player(level->getObject(0)), - camControl(player, settings.camera), - blocksController(blocksController) { + BlocksController* blocksController +) : level(level), + player(level->getObject(0)), + camControl(player, settings.camera), + blocksController(blocksController) +{} + +void PlayerController::onBlockInteraction( + glm::ivec3 pos, + const Block* def, + BlockInteraction type +) { + for (auto callback : blockInteractionCallbacks) { + callback(player.get(), pos, def, type); + } +} + +void PlayerController::onFootstep() { + auto hitbox = player->hitbox.get(); + glm::vec3 pos = hitbox->position; + glm::vec3 half = hitbox->halfsize; + + for (int offsetZ = -1; offsetZ <= 1; offsetZ++) { + for (int offsetX = -1; offsetX <= 1; offsetX++) { + int x = std::floor(pos.x+half.x*offsetX); + int y = std::floor(pos.y-half.y*1.1f); + int z = std::floor(pos.z+half.z*offsetZ); + auto vox = level->chunks->get(x, y, z); + if (vox) { + auto def = level->content->getIndices()->getBlockDef(vox->id); + if (!def->obstacle) + continue; + onBlockInteraction( + glm::ivec3(x, y, z), def, + BlockInteraction::step + ); + return; + } + } + } +} + +void PlayerController::updateFootsteps(float delta) { + auto hitbox = player->hitbox.get(); + + if (hitbox->grounded) { + const glm::vec3& vel = hitbox->velocity; + float f = glm::length(glm::vec2(vel.x, vel.z)); + stepsTimer += delta * f * STEPS_SPEED; + if (stepsTimer >= M_PI) { + stepsTimer = fmod(stepsTimer, M_PI); + onFootstep(); + } + } } void PlayerController::update(float delta, bool input, bool pause) { @@ -154,6 +236,7 @@ void PlayerController::update(float delta, bool input, bool pause) { } else { resetKeyboard(); } + updateFootsteps(delta); updateCamera(delta, input); updateControls(delta); @@ -289,6 +372,11 @@ void PlayerController::updateInteraction(){ uint8_t states = determine_rotation(def, norm, camera->dir); if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) { + onBlockInteraction( + glm::ivec3(x, y, z), def, + BlockInteraction::destruction + ); + // TODO: move scripting to interaction callbacks if (scripting::on_item_break_block(player.get(), item, x, y, z)) return; } @@ -334,6 +422,11 @@ void PlayerController::updateInteraction(){ chunks->set(x, y, z, chosenBlock, states); lighting->onBlockSet(x,y,z, chosenBlock); if (def->rt.funcsset.onplaced) { + onBlockInteraction( + glm::ivec3(x, y, z), def, + BlockInteraction::placing + ); + // TODO: move scripting to interaction callbacks scripting::on_block_placed(player.get(), def, x, y, z); } blocksController->updateSides(x, y, z); @@ -358,3 +451,7 @@ void PlayerController::updateInteraction(){ Player* PlayerController::getPlayer() { return player.get(); } + +void PlayerController::listenBlockInteraction(on_block_interaction callback) { + blockInteractionCallbacks.push_back(callback); +} diff --git a/src/logic/PlayerController.h b/src/logic/PlayerController.h index e1ae47e4..3dd6a337 100644 --- a/src/logic/PlayerController.h +++ b/src/logic/PlayerController.h @@ -2,6 +2,8 @@ #define PLAYER_CONTROL_H_ #include +#include +#include #include #include "../settings.h" @@ -9,16 +11,30 @@ class Camera; class Level; +class Block; class BlocksController; class CameraControl { std::shared_ptr player; - std::shared_ptr camera, currentViewCamera; + std::shared_ptr camera; const CameraSettings& settings; glm::vec3 offset; float shake = 0.0f; float shakeTimer = 0.0f; glm::vec3 interpVel {0.0f}; + + /// @brief Update shaking timer and calculate camera offset + /// @param delta delta time + /// @return camera offset + glm::vec3 updateCameraShaking(float delta); + + /// @brief Update field-of-view effects + /// @param input player inputs + /// @param delta delta time + void updateFovEffects(const PlayerInput& input, float delta); + + /// @brief Switch active player camera + void switchCamera(); public: CameraControl(std::shared_ptr player, const CameraSettings& settings); void updateMouse(PlayerInput& input); @@ -26,6 +42,16 @@ public: void refresh(); }; +enum class BlockInteraction { + step, + destruction, + placing +}; + +using on_block_interaction = std::function; + class PlayerController { Level* level; std::shared_ptr player; @@ -33,11 +59,22 @@ class PlayerController { CameraControl camControl; BlocksController* blocksController; + std::vector blockInteractionCallbacks; + void updateKeyboard(); void updateCamera(float delta, bool movement); void resetKeyboard(); void updateControls(float delta); void updateInteraction(); + void onBlockInteraction( + glm::ivec3 pos, + const Block* def, + BlockInteraction type + ); + + float stepsTimer = 0.0f; + void onFootstep(); + void updateFootsteps(float delta); public: static glm::vec3 selectedBlockPosition; static glm::ivec3 selectedBlockNormal; @@ -45,12 +82,16 @@ public: static int selectedBlockId; static int selectedBlockStates; - PlayerController(Level* level, - const EngineSettings& settings, - BlocksController* blocksController); + PlayerController( + Level* level, + const EngineSettings& settings, + BlocksController* blocksController + ); void update(float delta, bool input, bool pause); Player* getPlayer(); + + void listenBlockInteraction(on_block_interaction callback); }; #endif /* PLAYER_CONTROL_H_ */