]> git.localhorst.tv Git - blank.git/blobdiff - src/standalone/standalone.cpp
make command output visible to player(s)
[blank.git] / src / standalone / standalone.cpp
diff --git a/src/standalone/standalone.cpp b/src/standalone/standalone.cpp
new file mode 100644 (file)
index 0000000..ad6450d
--- /dev/null
@@ -0,0 +1,323 @@
+#include "DirectCLIFeedback.hpp"
+#include "MasterState.hpp"
+#include "PreloadState.hpp"
+#include "UnloadState.hpp"
+
+#include "../app/Config.hpp"
+#include "../app/Environment.hpp"
+#include "../app/init.hpp"
+#include "../geometry/distance.hpp"
+#include "../io/WorldSave.hpp"
+#include "../world/ChunkLoader.hpp"
+#include "../world/ChunkRenderer.hpp"
+
+#include <SDL.h>
+
+
+namespace blank {
+namespace standalone {
+
+DirectCLIFeedback::DirectCLIFeedback(Player &p, HUD &h)
+: CLIContext(p)
+, hud(h) {
+
+}
+
+void DirectCLIFeedback::Error(const std::string &msg) {
+       hud.PostMessage(msg);
+}
+
+void DirectCLIFeedback::Message(const std::string &msg) {
+       hud.PostMessage(msg);
+}
+
+void DirectCLIFeedback::Broadcast(const std::string &msg) {
+       hud.PostMessage(msg);
+}
+
+
+MasterState::MasterState(
+       Environment &env,
+       Config &config,
+       const Generator::Config &gc,
+       const World::Config &wc,
+       const WorldSave &save
+)
+: config(config)
+, env(env)
+, res()
+, sounds()
+, save(save)
+, world(res.block_types, wc)
+, spawn_index(world.Chunks().MakeIndex(wc.spawn, 3))
+, player(*world.AddPlayer(config.player.name))
+, spawn_player(false)
+, hud(env, config, player)
+, manip(env.audio, sounds, player.GetEntity())
+, input(world, player, manip)
+, interface(config, env.keymap, input, *this)
+, generator(gc)
+, chunk_loader(world.Chunks(), generator, save)
+, chunk_renderer(player.GetChunks())
+, spawner(world, res.models, env.rng)
+, sky(env.loader.LoadCubeMap("skybox"))
+, cli(world)
+, cli_ctx(player, hud)
+, preload(env, chunk_loader, chunk_renderer)
+, unload(env, world.Chunks(), save)
+, chat(env, *this, *this) {
+       res.Load(env.loader, "default");
+       if (res.models.size() < 2) {
+               throw std::runtime_error("need at least two models to run");
+       }
+       res.models[0].Instantiate(player.GetEntity().GetModel());
+       sounds.Load(env.loader, res.snd_index);
+       spawner.LimitModels(1, res.models.size());
+       interface.SetInventorySlots(res.block_types.size() - 1);
+       generator.LoadTypes(res.block_types);
+       chunk_renderer.LoadTextures(env.loader, res.tex_index);
+       chunk_renderer.FogDensity(wc.fog_density);
+       if (save.Exists(player)) {
+               save.Read(player);
+       } else {
+               spawn_player = true;
+       }
+}
+
+MasterState::~MasterState() {
+       world.Chunks().UnregisterIndex(spawn_index);
+}
+
+
+void MasterState::OnResume() {
+       if (spawn_index.MissingChunks() > 0) {
+               env.state.Push(&preload);
+               return;
+       }
+       if (spawn_player) {
+               // TODO: spawn
+               spawn_player = false;
+       }
+       hud.KeepMessages(false);
+       OnFocus();
+}
+
+void MasterState::OnPause() {
+       OnBlur();
+}
+
+void MasterState::OnFocus() {
+       if (config.input.mouse) {
+               env.window.GrabMouse();
+       }
+       interface.Unlock();
+}
+
+void MasterState::OnBlur() {
+       env.window.ReleaseMouse();
+       interface.Lock();
+}
+
+
+void MasterState::Handle(const SDL_Event &event) {
+       switch (event.type) {
+               case SDL_KEYDOWN:
+                       // TODO: move to interface
+                       if (event.key.keysym.sym == SDLK_RETURN) {
+                               chat.Clear();
+                               env.state.Push(&chat);
+                               hud.KeepMessages(true);
+                       } else if (event.key.keysym.sym == SDLK_SLASH) {
+                               chat.Preset("/");
+                               env.state.Push(&chat);
+                               hud.KeepMessages(true);
+                       } else {
+                               interface.HandlePress(event.key);
+                       }
+                       break;
+               case SDL_KEYUP:
+                       interface.HandleRelease(event.key);
+                       break;
+               case SDL_MOUSEBUTTONDOWN:
+                       interface.HandlePress(event.button);
+                       break;
+               case SDL_MOUSEBUTTONUP:
+                       interface.HandleRelease(event.button);
+                       break;
+               case SDL_MOUSEMOTION:
+                       interface.Handle(event.motion);
+                       break;
+               case SDL_MOUSEWHEEL:
+                       interface.Handle(event.wheel);
+                       break;
+               case SDL_QUIT:
+                       Exit();
+                       break;
+               default:
+                       break;
+       }
+}
+
+void MasterState::Update(int dt) {
+       spawner.Update(dt);
+       world.Update(dt);
+       if (input.BlockFocus()) {
+               hud.FocusBlock(input.BlockFocus().GetChunk(), input.BlockFocus().block);
+       } else if (input.EntityFocus()) {
+               hud.FocusEntity(*input.EntityFocus().entity);
+       } else {
+               hud.FocusNone();
+       }
+       hud.Display(res.block_types[player.GetInventorySlot() + 1]);
+       hud.Update(dt);
+       chunk_loader.Update(dt);
+       chunk_renderer.Update(dt);
+
+       glm::mat4 trans = player.GetEntity().Transform(player.GetEntity().ChunkCoords());
+       glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
+       glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
+       env.audio.Position(player.GetEntity().Position());
+       env.audio.Velocity(player.GetEntity().Velocity());
+       env.audio.Orientation(dir, up);
+}
+
+void MasterState::Render(Viewport &viewport) {
+       viewport.WorldPosition(player.GetEntity().ViewTransform(player.GetEntity().ChunkCoords()));
+       if (config.video.world) {
+               chunk_renderer.Render(viewport);
+               world.Render(viewport);
+               if (config.video.debug) {
+                       world.RenderDebug(viewport);
+               }
+               sky.Render(viewport);
+       }
+       hud.Render(viewport);
+}
+
+
+void MasterState::SetAudio(bool b) {
+       config.audio.enabled = b;
+       if (b) {
+               hud.PostMessage("Audio enabled");
+       } else {
+               hud.PostMessage("Audio disabled");
+       }
+}
+
+void MasterState::SetVideo(bool b) {
+       config.video.world = b;
+       if (b) {
+               hud.PostMessage("World rendering enabled");
+       } else {
+               hud.PostMessage("World rendering disabled");
+       }
+}
+
+void MasterState::SetHUD(bool b) {
+       config.video.hud = b;
+       if (b) {
+               hud.PostMessage("HUD rendering enabled");
+       } else {
+               hud.PostMessage("HUD rendering disabled");
+       }
+}
+
+void MasterState::SetDebug(bool b) {
+       config.video.debug = b;
+       if (b) {
+               hud.PostMessage("Debug rendering enabled");
+       } else {
+               hud.PostMessage("Debug rendering disabled");
+       }
+}
+
+void MasterState::NextCamera() {
+       if (iszero(env.viewport.CameraOffset())) {
+               env.viewport.OffsetCamera(glm::vec3(0.0f, 0.0f, -5.0f));
+       } else {
+               env.viewport.OffsetCamera(glm::vec3(0.0f, 0.0f, 0.0f));
+       }
+}
+
+void MasterState::Exit() {
+       save.Write(player);
+       env.state.Switch(&unload);
+}
+
+void MasterState::OnLineSubmit(const std::string &line) {
+       if (line.empty()) {
+               return;
+       }
+       if (line[0] == '/' && line.size() > 1 && line[1] != '/') {
+               cli.Execute(cli_ctx, line.substr(1));
+       } else {
+               hud.PostMessage(line);
+       }
+}
+
+
+PreloadState::PreloadState(Environment &env, ChunkLoader &loader, ChunkRenderer &render)
+: ProgressState(env, "Preloading chunks: %d/%d (%d%%)")
+, env(env)
+, loader(loader)
+, render(render)
+, total(loader.ToLoad())
+, per_update(64) {
+
+}
+
+void PreloadState::Update(int dt) {
+       loader.LoadN(per_update);
+       if (loader.ToLoad() <= 0) {
+               env.state.Pop();
+               render.Update(render.MissingChunks());
+       } else {
+               SetProgress(total - loader.ToLoad(), total);
+       }
+}
+
+
+UnloadState::UnloadState(
+       Environment &env,
+       ChunkStore &chunks,
+       const WorldSave &save)
+: ProgressState(env, "Unloading chunks: %d/%d (%d%%)")
+, env(env)
+, chunks(chunks)
+, save(save)
+, cur(chunks.begin())
+, end(chunks.end())
+, done(0)
+, total(chunks.NumLoaded())
+, per_update(64) {
+
+}
+
+
+void UnloadState::OnResume() {
+       cur = chunks.begin();
+       end = chunks.end();
+       done = 0;
+       total = chunks.NumLoaded();
+}
+
+
+void UnloadState::Handle(const SDL_Event &) {
+       // ignore everything
+}
+
+void UnloadState::Update(int dt) {
+       for (std::size_t i = 0; i < per_update && cur != end; ++i, ++cur, ++done) {
+               if (cur->ShouldUpdateSave()) {
+                       save.Write(*cur);
+               }
+       }
+       if (cur == end) {
+               env.state.Pop();
+       } else {
+               SetProgress(done, total);
+       }
+}
+
+}
+}