#include "../app/init.hpp"
#include "../audio/Audio.hpp"
#include "../audio/SoundBank.hpp"
+#include "../geometry/distance.hpp"
#include "../graphics/Font.hpp"
#include "../graphics/Viewport.hpp"
#include "../io/TokenStreamReader.hpp"
#include <map>
#include <sstream>
#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtx/projection.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/io.hpp>
, aim_world()
, aim_entity() {
player.GetEntity().SetController(*this);
+ player.GetEntity().GetSteering().SetAcceleration(5.0f);
}
PlayerController::~PlayerController() {
}
void PlayerController::SetMovement(const glm::vec3 &m) noexcept {
- if (dot(m, m) > 1.0f) {
- move_dir = normalize(m);
+ if (glm::dot(m, m) > 1.0f) {
+ move_dir = glm::normalize(m);
} else {
move_dir = m;
}
Invalidate();
}
-glm::vec3 PlayerController::ControlForce(const Entity &e, const EntityState &s) const {
- return TargetVelocity(rotateY(move_dir * e.MaxVelocity(), s.yaw), s, 5.0f);
-}
-
void PlayerController::TurnHead(float dp, float dy) noexcept {
player.GetEntity().TurnHead(dp, dy);
}
void PlayerController::UpdatePlayer() noexcept {
if (dirty) {
Ray aim = player.Aim();
- if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity().ChunkCoords(), aim_world)) {
+ Entity &entity = player.GetEntity();
+ if (!world.Intersection(aim, entity.ChunkCoords(), aim_world)) {
aim_world = WorldCollision();
}
- if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity(), aim_entity)) {
+ if (!world.Intersection(aim, entity, aim_entity)) {
aim_entity = EntityCollision();
}
if (aim_world && aim_entity) {
aim_world = WorldCollision();
}
}
+ Steering &steering = entity.GetSteering();
+ if (!iszero(move_dir)) {
+ // scale input by max velocity, apply yaw, and transform to world space
+ steering.SetTargetVelocity(glm::vec3(
+ glm::vec4(glm::rotateY(move_dir * entity.MaxVelocity(), entity.Yaw()), 0.0f)
+ * glm::transpose(entity.Transform())
+ ));
+ steering.Enable(Steering::TARGET_VELOCITY);
+ steering.Disable(Steering::HALT);
+ } else {
+ // target velocity of 0 is the same as halt
+ steering.Enable(Steering::HALT);
+ steering.Disable(Steering::TARGET_VELOCITY);
+ }
dirty = false;
}
}
}
void DirectInput::PlaceBlock() {
+ // update block focus
UpdatePlayer();
+ // do nothing if not looking at any block
if (!BlockFocus()) return;
+ // determine block adjacent to the face the player is looking at
BlockLookup next_block(BlockFocus().chunk, BlockFocus().BlockPos(), Block::NormalFace(BlockFocus().normal));
+ // abort if it's unavailable
if (!next_block) {
return;
}
- manip.SetBlock(next_block.GetChunk(), next_block.GetBlockIndex(), Block(InventorySlot() + 1));
+
+ // "can replace" check
+ // this prevents players from replacing solid blocks e.g. by looking through slabs
+ // simple for now, should be expanded to include things like
+ // entities in the way or replacable blocks like water and stuff
+ if (next_block.GetBlock().type != 0) {
+ return;
+ }
+
+ Block new_block(InventorySlot() + 1);
+
+ // block's up vector
+ // align with player's up
+ const glm::vec3 player_up(GetPlayer().GetEntity().Up());
+ new_block.SetFace(Block::NormalFace(player_up));
+ // to align with player's local up/down look (like stairs in minecraft), just invert
+ // it if pitch is positive
+ // or, align with focus normal (like logs in minecraft)
+
+ // determine block's turn (local rotation about up axis)
+ // when aligned with player's up (first mode, and currently the only one implemented)
+ // project the player's view forward onto his entity's XZ plane and
+ // use the closest cardinal direction it's pointing in
+ const glm::vec3 view_forward(-GetPlayer().GetEntity().ViewTransform(GetPlayer().GetEntity().ChunkCoords())[2]);
+ // if view is straight up or down, this will be a null vector (NaN after normalization)
+ // in that case maybe the model forward should be used?
+ // the current implementation implicitly falls back to TURN_NONE which is -Z
+ const glm::vec3 local_forward(glm::normalize(view_forward - glm::proj(view_forward, player_up)));
+ // FIXME: I suspect this only works when player_up is positive Y
+ if (local_forward.x > 0.707f) {
+ new_block.SetTurn(Block::TURN_RIGHT);
+ } else if (local_forward.z > 0.707f) {
+ new_block.SetTurn(Block::TURN_AROUND);
+ } else if (local_forward.x < -0.707f) {
+ new_block.SetTurn(Block::TURN_LEFT);
+ }
+ // for mode two ("minecraft stairs") it should work the same, but I haven't properly
+ // thought that through (well, that's also true about the whole face/turn thing, but oh well)
+ // mode three I have absoloutely no clue. that placement would be appropriate for pipe-like
+ // blocks, where turn shouldn't make a difference, but what if it does?
+
+ manip.SetBlock(next_block.GetChunk(), next_block.GetBlockIndex(), new_block);
Invalidate();
}
buf.indices = std::vector<PrimitiveMesh::Index>({
0, 1, 2, 3
});
- buf.colors.resize(4, { 10.0f, 10.0f, 10.0f, 1.0f });
+ buf.colors.resize(4, { 255, 255, 255, 255 });
crosshair.Update(buf);
}
void HUD::UpdateOrientation() {
std::stringstream s;
- s << std::setprecision(3) << "pitch: " << rad2deg(player.GetEntity().Pitch())
- << ", yaw: " << rad2deg(player.GetEntity().Yaw());
+ s << std::setprecision(3) << "pitch: " << glm::degrees(player.GetEntity().Pitch())
+ << ", yaw: " << glm::degrees(player.GetEntity().Yaw());
orientation_text.Set(env.assets.small_ui_font, s.str());
}
, client_ctrl(cc)
, fwd(0)
, rev(0)
-, slot(0)
, num_slots(10)
, locked(false) {
break;
case Keymap::TOGGLE_AUDIO:
- config.audio.enabled = !config.audio.enabled;
- client_ctrl.SetAudio(config.audio.enabled);
+ client_ctrl.SetAudio(!config.audio.enabled);
break;
case Keymap::TOGGLE_VIDEO:
- config.video.world = !config.video.world;
- client_ctrl.SetVideo(config.video.world);
+ client_ctrl.SetVideo(!config.video.world);
break;
case Keymap::TOGGLE_HUD:
- config.video.hud = !config.video.hud;
- client_ctrl.SetHUD(config.video.hud);
+ client_ctrl.SetHUD(!config.video.hud);
break;
case Keymap::TOGGLE_DEBUG:
- config.video.debug = !config.video.debug;
- client_ctrl.SetDebug(config.video.debug);
+ client_ctrl.SetDebug(!config.video.debug);
+ break;
+ case Keymap::CAMERA_NEXT:
+ client_ctrl.NextCamera();
break;
default:
}
void Interface::InvAbs(int s) {
- slot = s % num_slots;
+ int slot = s % num_slots;
while (slot < 0) {
slot += num_slots;
}
}
void Interface::InvRel(int delta) {
- InvAbs(slot + delta);
+ InvAbs(player_ctrl.GetPlayer().GetInventorySlot() + delta);
}
Map(SDL_SCANCODE_0, INV_10);
Map(SDL_SCANCODE_INSERT, SECONDARY);
- Map(SDL_SCANCODE_RETURN, SECONDARY);
Map(SDL_SCANCODE_MENU, TERTIARY);
Map(SDL_SCANCODE_DELETE, PRIMARY);
Map(SDL_SCANCODE_BACKSPACE, PRIMARY);
Map(SDL_SCANCODE_F2, TOGGLE_VIDEO);
Map(SDL_SCANCODE_F3, TOGGLE_DEBUG);
Map(SDL_SCANCODE_F4, TOGGLE_AUDIO);
+ Map(SDL_SCANCODE_F5, CAMERA_NEXT);
Map(SDL_SCANCODE_ESCAPE, EXIT);
}
{ "toggle_video", Keymap::TOGGLE_VIDEO },
{ "toggle_hud", Keymap::TOGGLE_HUD },
{ "toggle_debug", Keymap::TOGGLE_DEBUG },
+ { "camera_next", Keymap::CAMERA_NEXT },
{ "exit", Keymap::EXIT },
};