+PlayerController::PlayerController(World &world, Player &player)
+: world(world)
+, player(player)
+, move_dir(0.0f)
+, pitch(0.0f)
+, yaw(0.0f)
+, dirty(true)
+, aim_world()
+, aim_entity() {
+
+}
+
+void PlayerController::SetMovement(const glm::vec3 &m) noexcept {
+ if (dot(m, m) > 1.0f) {
+ move_dir = normalize(m);
+ } else {
+ move_dir = m;
+ }
+ Invalidate();
+}
+
+void PlayerController::TurnHead(float dp, float dy) noexcept {
+ pitch += dp;
+ if (pitch > PI / 2) {
+ pitch = PI / 2;
+ } else if (pitch < -PI / 2) {
+ pitch = -PI / 2;
+ }
+ yaw += dy;
+ if (yaw > PI) {
+ yaw -= PI * 2;
+ } else if (yaw < -PI) {
+ yaw += PI * 2;
+ }
+ Invalidate();
+}
+
+void PlayerController::SelectInventory(int i) noexcept {
+ player.SetInventorySlot(i);
+}
+
+int PlayerController::InventorySlot() const noexcept {
+ return player.GetInventorySlot();
+}
+
+void PlayerController::Invalidate() noexcept {
+ dirty = true;
+}
+
+void PlayerController::UpdatePlayer() noexcept {
+ constexpr float max_vel = 0.005f;
+ if (dirty) {
+ player.GetEntity().Orientation(glm::quat(glm::vec3(pitch, yaw, 0.0f)));
+ player.GetEntity().Velocity(glm::rotateY(move_dir * max_vel, yaw));
+
+ Ray aim = player.Aim();
+ if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity().ChunkCoords(), aim_world)) {
+ aim_world = WorldCollision();
+ }
+ if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity(), aim_entity)) {
+ aim_entity = EntityCollision();
+ }
+ if (aim_world && aim_entity) {
+ // got both, pick the closest one
+ if (aim_world.depth < aim_entity.depth) {
+ aim_entity = EntityCollision();
+ } else {
+ aim_world = WorldCollision();
+ }
+ }
+ dirty = false;
+ }
+}
+
+
+DirectInput::DirectInput(World &world, Player &player, WorldManipulator &manip)
+: PlayerController(world, player)
+, manip(manip)
+, place_timer(256)
+, remove_timer(256) {
+
+}
+
+void DirectInput::Update(int dt) {
+ Invalidate(); // world has changed in the meantime
+ UpdatePlayer();
+
+ remove_timer.Update(dt);
+ if (remove_timer.Hit()) {
+ RemoveBlock();
+ }
+
+ place_timer.Update(dt);
+ if (place_timer.Hit()) {
+ PlaceBlock();
+ }
+}
+
+void DirectInput::StartPrimaryAction() {
+ if (!remove_timer.Running()) {
+ RemoveBlock();
+ remove_timer.Start();
+ }
+}
+
+void DirectInput::StopPrimaryAction() {
+ remove_timer.Stop();
+}
+
+void DirectInput::StartSecondaryAction() {
+ if (!place_timer.Running()) {
+ PlaceBlock();
+ place_timer.Start();
+ }
+}
+
+void DirectInput::StopSecondaryAction() {
+ place_timer.Stop();
+}
+
+void DirectInput::StartTertiaryAction() {
+ PickBlock();
+}
+
+void DirectInput::StopTertiaryAction() {
+ // nothing
+}
+
+void DirectInput::PickBlock() {
+ UpdatePlayer();
+ if (!BlockFocus()) return;
+ SelectInventory(BlockFocus().GetBlock().type - 1);
+}
+
+void DirectInput::PlaceBlock() {
+ UpdatePlayer();
+ if (!BlockFocus()) return;
+
+ BlockLookup next_block(BlockFocus().chunk, BlockFocus().BlockPos(), Block::NormalFace(BlockFocus().normal));
+ if (!next_block) {
+ return;
+ }
+ manip.SetBlock(next_block.GetChunk(), next_block.GetBlockIndex(), Block(InventorySlot() + 1));
+ Invalidate();
+}
+
+void DirectInput::RemoveBlock() {
+ UpdatePlayer();
+ if (!BlockFocus()) return;
+ manip.SetBlock(BlockFocus().GetChunk(), BlockFocus().block, Block(0));
+ Invalidate();
+}
+
+
+HUD::HUD(Environment &env, Config &config, const Player &player)
+: env(env)
+, config(config)
+, player(player)
+// block focus
+, outline()
+, outline_transform(1.0f)
+, outline_visible(false)
+// "inventory"