]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/client/game.cpp
Merge branch 'master' of https://github.com/minetest/minetest
[dragonfireclient.git] / src / client / game.cpp
index 44105463147eefa227ce2d27e3896a55bade3e90..888191f4a406ad39ddf7402e0474bea7783d0b64 100644 (file)
@@ -77,846 +77,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #else
        #include "client/sound.h"
 #endif
-/*
-       Text input system
-*/
-
-struct TextDestNodeMetadata : public TextDest
-{
-       TextDestNodeMetadata(v3s16 p, Client *client)
-       {
-               m_p = p;
-               m_client = client;
-       }
-       // This is deprecated I guess? -celeron55
-       void gotText(const std::wstring &text)
-       {
-               std::string ntext = wide_to_utf8(text);
-               infostream << "Submitting 'text' field of node at (" << m_p.X << ","
-                          << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
-               StringMap fields;
-               fields["text"] = ntext;
-               m_client->sendNodemetaFields(m_p, "", fields);
-       }
-       void gotText(const StringMap &fields)
-       {
-               m_client->sendNodemetaFields(m_p, "", fields);
-       }
-
-       v3s16 m_p;
-       Client *m_client;
-};
-
-struct TextDestPlayerInventory : public TextDest
-{
-       TextDestPlayerInventory(Client *client)
-       {
-               m_client = client;
-               m_formname = "";
-       }
-       TextDestPlayerInventory(Client *client, const std::string &formname)
-       {
-               m_client = client;
-               m_formname = formname;
-       }
-       void gotText(const StringMap &fields)
-       {
-               m_client->sendInventoryFields(m_formname, fields);
-       }
-
-       Client *m_client;
-};
-
-struct LocalFormspecHandler : public TextDest
-{
-       LocalFormspecHandler(const std::string &formname)
-       {
-               m_formname = formname;
-       }
-
-       LocalFormspecHandler(const std::string &formname, Client *client):
-               m_client(client)
-       {
-               m_formname = formname;
-       }
-
-       void gotText(const StringMap &fields)
-       {
-               if (m_formname == "MT_PAUSE_MENU") {
-                       if (fields.find("btn_sound") != fields.end()) {
-                               g_gamecallback->changeVolume();
-                               return;
-                       }
-
-                       if (fields.find("btn_key_config") != fields.end()) {
-                               g_gamecallback->keyConfig();
-                               return;
-                       }
-
-                       if (fields.find("btn_exit_menu") != fields.end()) {
-                               g_gamecallback->disconnect();
-                               return;
-                       }
-
-                       if (fields.find("btn_exit_os") != fields.end()) {
-                               g_gamecallback->exitToOS();
-#ifndef __ANDROID__
-                               RenderingEngine::get_raw_device()->closeDevice();
-#endif
-                               return;
-                       }
-
-                       if (fields.find("btn_change_password") != fields.end()) {
-                               g_gamecallback->changePassword();
-                               return;
-                       }
-
-                       return;
-               }
-
-               if (m_formname == "MT_DEATH_SCREEN") {
-                       assert(m_client != 0);
-                       m_client->sendRespawn();
-                       return;
-               }
-
-               if (m_client->modsLoaded())
-                       m_client->getScript()->on_formspec_input(m_formname, fields);
-       }
-
-       Client *m_client = nullptr;
-};
-
-/* Form update callback */
-
-class NodeMetadataFormSource: public IFormSource
-{
-public:
-       NodeMetadataFormSource(ClientMap *map, v3s16 p):
-               m_map(map),
-               m_p(p)
-       {
-       }
-       const std::string &getForm() const
-       {
-               static const std::string empty_string = "";
-               NodeMetadata *meta = m_map->getNodeMetadata(m_p);
-
-               if (!meta)
-                       return empty_string;
-
-               return meta->getString("formspec");
-       }
-
-       virtual std::string resolveText(const std::string &str)
-       {
-               NodeMetadata *meta = m_map->getNodeMetadata(m_p);
-
-               if (!meta)
-                       return str;
-
-               return meta->resolveString(str);
-       }
-
-       ClientMap *m_map;
-       v3s16 m_p;
-};
-
-class PlayerInventoryFormSource: public IFormSource
-{
-public:
-       PlayerInventoryFormSource(Client *client):
-               m_client(client)
-       {
-       }
-
-       const std::string &getForm() const
-       {
-               LocalPlayer *player = m_client->getEnv().getLocalPlayer();
-               return player->inventory_formspec;
-       }
-
-       Client *m_client;
-};
-
-class NodeDugEvent: public MtEvent
-{
-public:
-       v3s16 p;
-       MapNode n;
-
-       NodeDugEvent(v3s16 p, MapNode n):
-               p(p),
-               n(n)
-       {}
-       MtEvent::Type getType() const
-       {
-               return MtEvent::NODE_DUG;
-       }
-};
-
-class SoundMaker
-{
-       ISoundManager *m_sound;
-       const NodeDefManager *m_ndef;
-public:
-       bool makes_footstep_sound;
-       float m_player_step_timer;
-       float m_player_jump_timer;
-
-       SimpleSoundSpec m_player_step_sound;
-       SimpleSoundSpec m_player_leftpunch_sound;
-       SimpleSoundSpec m_player_rightpunch_sound;
-
-       SoundMaker(ISoundManager *sound, const NodeDefManager *ndef):
-               m_sound(sound),
-               m_ndef(ndef),
-               makes_footstep_sound(true),
-               m_player_step_timer(0.0f),
-               m_player_jump_timer(0.0f)
-       {
-       }
-
-       void playPlayerStep()
-       {
-               if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
-                       m_player_step_timer = 0.03;
-                       if (makes_footstep_sound)
-                               m_sound->playSound(m_player_step_sound, false);
-               }
-       }
-
-       void playPlayerJump()
-       {
-               if (m_player_jump_timer <= 0.0f) {
-                       m_player_jump_timer = 0.2f;
-                       m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false);
-               }
-       }
-
-       static void viewBobbingStep(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->playPlayerStep();
-       }
-
-       static void playerRegainGround(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->playPlayerStep();
-       }
-
-       static void playerJump(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->playPlayerJump();
-       }
-
-       static void cameraPunchLeft(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
-       }
-
-       static void cameraPunchRight(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
-       }
-
-       static void nodeDug(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               NodeDugEvent *nde = (NodeDugEvent *)e;
-               sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
-       }
-
-       static void playerDamage(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
-       }
-
-       static void playerFallingDamage(MtEvent *e, void *data)
-       {
-               SoundMaker *sm = (SoundMaker *)data;
-               sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
-       }
-
-       void registerReceiver(MtEventManager *mgr)
-       {
-               mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this);
-               mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this);
-               mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this);
-               mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this);
-               mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this);
-               mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this);
-               mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this);
-               mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this);
-       }
-
-       void step(float dtime)
-       {
-               m_player_step_timer -= dtime;
-               m_player_jump_timer -= dtime;
-       }
-};
-
-// Locally stored sounds don't need to be preloaded because of this
-class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
-{
-       std::set<std::string> m_fetched;
-private:
-       void paths_insert(std::set<std::string> &dst_paths,
-               const std::string &base,
-               const std::string &name)
-       {
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg");
-               dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg");
-       }
-public:
-       void fetchSounds(const std::string &name,
-               std::set<std::string> &dst_paths,
-               std::set<std::string> &dst_datas)
-       {
-               if (m_fetched.count(name))
-                       return;
-
-               m_fetched.insert(name);
-
-               paths_insert(dst_paths, porting::path_share, name);
-               paths_insert(dst_paths, porting::path_user,  name);
-       }
-};
-
-
-typedef s32 SamplerLayer_t;
-
-
-class GameGlobalShaderConstantSetter : public IShaderConstantSetter
-{
-       Sky *m_sky;
-       bool *m_force_fog_off;
-       f32 *m_fog_range;
-       bool m_fog_enabled;
-       CachedPixelShaderSetting<float, 4> m_sky_bg_color;
-       CachedPixelShaderSetting<float> m_fog_distance;
-       CachedVertexShaderSetting<float> m_animation_timer_vertex;
-       CachedPixelShaderSetting<float> m_animation_timer_pixel;
-       CachedPixelShaderSetting<float, 3> m_day_light;
-       CachedPixelShaderSetting<float, 4> m_star_color;
-       CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
-       CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
-       CachedPixelShaderSetting<float, 3> m_minimap_yaw;
-       CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
-       CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
-       CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
-       CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture;
-       Client *m_client;
-
-public:
-       void onSettingsChange(const std::string &name)
-       {
-               if (name == "enable_fog")
-                       m_fog_enabled = g_settings->getBool("enable_fog");
-       }
-
-       static void settingsCallback(const std::string &name, void *userdata)
-       {
-               reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
-       }
-
-       void setSky(Sky *sky) { m_sky = sky; }
-
-       GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
-                       f32 *fog_range, Client *client) :
-               m_sky(sky),
-               m_force_fog_off(force_fog_off),
-               m_fog_range(fog_range),
-               m_sky_bg_color("skyBgColor"),
-               m_fog_distance("fogDistance"),
-               m_animation_timer_vertex("animationTimer"),
-               m_animation_timer_pixel("animationTimer"),
-               m_day_light("dayLight"),
-               m_star_color("starColor"),
-               m_eye_position_pixel("eyePosition"),
-               m_eye_position_vertex("eyePosition"),
-               m_minimap_yaw("yawVec"),
-               m_camera_offset_pixel("cameraOffset"),
-               m_camera_offset_vertex("cameraOffset"),
-               m_base_texture("baseTexture"),
-               m_normal_texture("normalTexture"),
-               m_client(client)
-       {
-               g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
-               m_fog_enabled = g_settings->getBool("enable_fog");
-       }
-
-       ~GameGlobalShaderConstantSetter()
-       {
-               g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this);
-       }
-
-       void onSetConstants(video::IMaterialRendererServices *services) override
-       {
-               // Background color
-               video::SColor bgcolor = m_sky->getBgColor();
-               video::SColorf bgcolorf(bgcolor);
-               float bgcolorfa[4] = {
-                       bgcolorf.r,
-                       bgcolorf.g,
-                       bgcolorf.b,
-                       bgcolorf.a,
-               };
-               m_sky_bg_color.set(bgcolorfa, services);
-
-               // Fog distance
-               float fog_distance = 10000 * BS;
-
-               if (m_fog_enabled && !*m_force_fog_off)
-                       fog_distance = *m_fog_range;
-
-               m_fog_distance.set(&fog_distance, services);
-
-               u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
-               video::SColorf sunlight;
-               get_sunlight_color(&sunlight, daynight_ratio);
-               float dnc[3] = {
-                       sunlight.r,
-                       sunlight.g,
-                       sunlight.b };
-               m_day_light.set(dnc, services);
-
-               video::SColorf star_color = m_sky->getCurrentStarColor();
-               float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
-               m_star_color.set(clr, services);
-
-               u32 animation_timer = porting::getTimeMs() % 1000000;
-               float animation_timer_f = (float)animation_timer / 100000.f;
-               m_animation_timer_vertex.set(&animation_timer_f, services);
-               m_animation_timer_pixel.set(&animation_timer_f, services);
-
-               float eye_position_array[3];
-               v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
-               epos.getAs3Values(eye_position_array);
-               m_eye_position_pixel.set(eye_position_array, services);
-               m_eye_position_vertex.set(eye_position_array, services);
-
-               if (m_client->getMinimap()) {
-                       float minimap_yaw_array[3];
-                       v3f minimap_yaw = m_client->getMinimap()->getYawVec();
-                       minimap_yaw.getAs3Values(minimap_yaw_array);
-                       m_minimap_yaw.set(minimap_yaw_array, services);
-               }
-
-               float camera_offset_array[3];
-               v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS);
-               offset.getAs3Values(camera_offset_array);
-               m_camera_offset_pixel.set(camera_offset_array, services);
-               m_camera_offset_vertex.set(camera_offset_array, services);
-
-               SamplerLayer_t base_tex = 0, normal_tex = 1;
-               m_base_texture.set(&base_tex, services);
-               m_normal_texture.set(&normal_tex, services);
-       }
-};
-
-
-class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory
-{
-       Sky *m_sky;
-       bool *m_force_fog_off;
-       f32 *m_fog_range;
-       Client *m_client;
-       std::vector<GameGlobalShaderConstantSetter *> created_nosky;
-public:
-       GameGlobalShaderConstantSetterFactory(bool *force_fog_off,
-                       f32 *fog_range, Client *client) :
-               m_sky(NULL),
-               m_force_fog_off(force_fog_off),
-               m_fog_range(fog_range),
-               m_client(client)
-       {}
-
-       void setSky(Sky *sky) {
-               m_sky = sky;
-               for (GameGlobalShaderConstantSetter *ggscs : created_nosky) {
-                       ggscs->setSky(m_sky);
-               }
-               created_nosky.clear();
-       }
-
-       virtual IShaderConstantSetter* create()
-       {
-               auto *scs = new GameGlobalShaderConstantSetter(
-                               m_sky, m_force_fog_off, m_fog_range, m_client);
-               if (!m_sky)
-                       created_nosky.push_back(scs);
-               return scs;
-       }
-};
-
-#ifdef HAVE_TOUCHSCREENGUI
-#define SIZE_TAG "size[11,5.5]"
-#else
-#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
-#endif
-
-/****************************************************************************
- ****************************************************************************/
-
-const static float object_hit_delay = 0.2;
-
-struct FpsControl {
-       FpsControl() : last_time(0), busy_time(0), sleep_time(0) {}
-
-       void reset();
-
-       void limit(IrrlichtDevice *device, f32 *dtime);
-
-       u32 getBusyMs() const { return busy_time / 1000; }
-
-       // all values in microseconds (us)
-       u64 last_time, busy_time, sleep_time;
-};
-
-
-/* The reason the following structs are not anonymous structs within the
- * class is that they are not used by the majority of member functions and
- * many functions that do require objects of thse types do not modify them
- * (so they can be passed as a const qualified parameter)
- */
-
-struct GameRunData {
-       u16 dig_index;
-       u16 new_playeritem;
-       PointedThing pointed_old;
-       bool digging;
-       bool punching;
-       bool btn_down_for_dig;
-       bool dig_instantly;
-       bool digging_blocked;
-       bool reset_jump_timer;
-       float nodig_delay_timer;
-       float dig_time;
-       float dig_time_complete;
-       float repeat_place_timer;
-       float object_hit_delay_timer;
-       float time_from_last_punch;
-       ClientActiveObject *selected_object;
-
-       float jump_timer;
-       float damage_flash;
-       float update_draw_list_timer;
-
-       f32 fog_range;
-
-       v3f update_draw_list_last_cam_dir;
-
-       float time_of_day_smooth;
-};
-
-class Game;
-
-struct ClientEventHandler
-{
-       void (Game::*handler)(ClientEvent *, CameraOrientation *);
-};
-
-/****************************************************************************
- THE GAME
- ****************************************************************************/
-
-using PausedNodesList = std::vector<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, float>>;
-
-/* This is not intended to be a public class. If a public class becomes
- * desirable then it may be better to create another 'wrapper' class that
- * hides most of the stuff in this class (nothing in this class is required
- * by any other file) but exposes the public methods/data only.
- */
-class Game {
-public:
-       Game();
-       ~Game();
-
-       bool startup(bool *kill,
-                       InputHandler *input,
-                       RenderingEngine *rendering_engine,
-                       const GameStartData &game_params,
-                       std::string &error_message,
-                       bool *reconnect,
-                       ChatBackend *chat_backend);
-
-       void run();
-       void shutdown();
-
-protected:
-
-       // Basic initialisation
-       bool init(const std::string &map_dir, const std::string &address,
-                       u16 port, const SubgameSpec &gamespec);
-       bool initSound();
-       bool createSingleplayerServer(const std::string &map_dir,
-                       const SubgameSpec &gamespec, u16 port);
-
-       // Client creation
-       bool createClient(const GameStartData &start_data);
-       bool initGui();
-
-       // Client connection
-       bool connectToServer(const GameStartData &start_data,
-                       bool *connect_ok, bool *aborted);
-       bool getServerContent(bool *aborted);
-
-       // Main loop
-
-       void updateInteractTimers(f32 dtime);
-       bool checkConnection();
-       bool handleCallbacks();
-       void processQueues();
-       void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime);
-       void updateDebugState();
-       void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
-       void updateProfilerGraphs(ProfilerGraph *graph);
-
-       // Input related
-       void processUserInput(f32 dtime);
-       void processKeyInput();
-       void processItemSelection(u16 *new_playeritem);
-
-       void dropSelectedItem(bool single_item = false);
-       void openInventory();
-       void openConsole(float scale, const wchar_t *line=NULL);
-       void toggleFreeMove();
-       void toggleFreeMoveAlt();
-       void togglePitchMove();
-       void toggleFast();
-       void toggleNoClip();
-       void toggleCinematic();
-       void toggleBlockBounds();
-       void toggleAutoforward();
-
-       void toggleMinimap(bool shift_pressed);
-       void toggleFog();
-       void toggleDebug();
-       void toggleUpdateCamera();
-
-       void increaseViewRange();
-       void decreaseViewRange();
-       void toggleFullViewRange();
-       void checkZoomEnabled();
-
-       void updateCameraDirection(CameraOrientation *cam, float dtime);
-       void updateCameraOrientation(CameraOrientation *cam, float dtime);
-       void updatePlayerControl(const CameraOrientation &cam);
-       void step(f32 *dtime);
-       void processClientEvents(CameraOrientation *cam);
-       void updateCamera(f32 dtime);
-       void updateSound(f32 dtime);
-       void processPlayerInteraction(f32 dtime, bool show_hud);
-       /*!
-        * Returns the object or node the player is pointing at.
-        * Also updates the selected thing in the Hud.
-        *
-        * @param[in]  shootline         the shootline, starting from
-        * the camera position. This also gives the maximal distance
-        * of the search.
-        * @param[in]  liquids_pointable if false, liquids are ignored
-        * @param[in]  look_for_object   if false, objects are ignored
-        * @param[in]  camera_offset     offset of the camera
-        * @param[out] selected_object   the selected object or
-        * NULL if not found
-        */
-       PointedThing updatePointedThing(
-                       const core::line3d<f32> &shootline, bool liquids_pointable,
-                       bool look_for_object, const v3s16 &camera_offset);
-       void handlePointingAtNothing(const ItemStack &playerItem);
-       void handlePointingAtNode(const PointedThing &pointed,
-                       const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
-       void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
-                       const v3f &player_position, bool show_debug);
-       void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
-                       const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
-       void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
-                       const CameraOrientation &cam);
-       void updateShadows();
-
-       // Misc
-       void showOverlayMessage(const char *msg, float dtime, int percent,
-                       bool draw_clouds = true);
-
-       static void settingChangedCallback(const std::string &setting_name, void *data);
-       void readSettings();
-
-       inline bool isKeyDown(GameKeyType k)
-       {
-               return input->isKeyDown(k);
-       }
-       inline bool wasKeyDown(GameKeyType k)
-       {
-               return input->wasKeyDown(k);
-       }
-       inline bool wasKeyPressed(GameKeyType k)
-       {
-               return input->wasKeyPressed(k);
-       }
-       inline bool wasKeyReleased(GameKeyType k)
-       {
-               return input->wasKeyReleased(k);
-       }
-
-#ifdef __ANDROID__
-       void handleAndroidChatInput();
-#endif
-
-private:
-       struct Flags {
-               bool force_fog_off = false;
-               bool disable_camera_update = false;
-       };
-
-       void showDeathFormspec();
-       void showPauseMenu();
-
-       void pauseAnimation();
-       void resumeAnimation();
-
-       // ClientEvent handlers
-       void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_HandleParticleEvent(ClientEvent *event,
-               CameraOrientation *cam);
-       void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam);
-       void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
-               CameraOrientation *cam);
-       void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
-
-       void updateChat(f32 dtime);
-
-       bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item,
-               const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed,
-               const NodeMetadata *meta);
-       static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
-
-       f32 getSensitivityScaleFactor() const;
-
-       InputHandler *input = nullptr;
-
-       Client *client = nullptr;
-       Server *server = nullptr;
-
-       IWritableTextureSource *texture_src = nullptr;
-       IWritableShaderSource *shader_src = nullptr;
-
-       // When created, these will be filled with data received from the server
-       IWritableItemDefManager *itemdef_manager = nullptr;
-       NodeDefManager *nodedef_manager = nullptr;
-
-       GameOnDemandSoundFetcher soundfetcher; // useful when testing
-       ISoundManager *sound = nullptr;
-       bool sound_is_dummy = false;
-       SoundMaker *soundmaker = nullptr;
-
-       ChatBackend *chat_backend = nullptr;
-       LogOutputBuffer m_chat_log_buf;
-
-       EventManager *eventmgr = nullptr;
-       QuicktuneShortcutter *quicktune = nullptr;
-       bool registration_confirmation_shown = false;
-
-       std::unique_ptr<GameUI> m_game_ui;
-       GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
-       MapDrawControl *draw_control = nullptr;
-       Camera *camera = nullptr;
-       Clouds *clouds = nullptr;                         // Free using ->Drop()
-       Sky *sky = nullptr;                         // Free using ->Drop()
-       Hud *hud = nullptr;
-       Minimap *mapper = nullptr;
-
-       // Map server hud ids to client hud ids
-       std::unordered_map<u32, u32> m_hud_server_to_client;
-
-       GameRunData runData;
-       Flags m_flags;
-
-       /* 'cache'
-          This class does take ownership/responsibily for cleaning up etc of any of
-          these items (e.g. device)
-       */
-       IrrlichtDevice *device;
-       RenderingEngine *m_rendering_engine;
-       video::IVideoDriver *driver;
-       scene::ISceneManager *smgr;
-       bool *kill;
-       std::string *error_message;
-       bool *reconnect_requested;
-       scene::ISceneNode *skybox;
-       PausedNodesList paused_animated_nodes;
-
-       bool simple_singleplayer_mode;
-       /* End 'cache' */
-
-       /* Pre-calculated values
-        */
-       int crack_animation_length;
-
-       IntervalLimiter profiler_interval;
-
-       /*
-        * TODO: Local caching of settings is not optimal and should at some stage
-        *       be updated to use a global settings object for getting thse values
-        *       (as opposed to the this local caching). This can be addressed in
-        *       a later release.
-        */
-       bool m_cache_doubletap_jump;
-       bool m_cache_enable_clouds;
-       bool m_cache_enable_joysticks;
-       bool m_cache_enable_particles;
-       bool m_cache_enable_fog;
-       bool m_cache_enable_noclip;
-       bool m_cache_enable_free_move;
-       f32  m_cache_mouse_sensitivity;
-       f32  m_cache_joystick_frustum_sensitivity;
-       f32  m_repeat_place_time;
-       f32  m_cache_cam_smoothing;
-       f32  m_cache_fog_start;
-
-       bool m_invert_mouse = false;
-       bool m_first_loop_after_window_activation = false;
-       bool m_camera_offset_changed = false;
-
-       bool m_does_lost_focus_pause_game = false;
-
-#if IRRLICHT_VERSION_MT_REVISION < 5
-       int m_reset_HW_buffer_counter = 0;
-#endif
-
-#ifdef HAVE_TOUCHSCREENGUI
-       bool m_cache_hold_aux1;
-#endif
-#ifdef __ANDROID__
-       bool m_android_chat_open;
-#endif
-};
 
 Game::Game() :
        m_chat_log_buf(g_logger),
@@ -948,6 +108,16 @@ Game::Game() :
                &settingChangedCallback, this);
        g_settings->registerChangedCallback("camera_smoothing",
                &settingChangedCallback, this);
+       g_settings->registerChangedCallback("freecam",
+               &freecamChangedCallback, this);
+       g_settings->registerChangedCallback("xray",
+               &updateAllMapBlocksCallback, this);
+       g_settings->registerChangedCallback("xray_nodes",
+               &updateAllMapBlocksCallback, this);
+       g_settings->registerChangedCallback("fullbright",
+               &updateAllMapBlocksCallback, this);
+       g_settings->registerChangedCallback("node_esp_nodes",
+               &updateAllMapBlocksCallback, this);
 
        readSettings();
 
@@ -1006,6 +176,16 @@ Game::~Game()
                &settingChangedCallback, this);
        g_settings->deregisterChangedCallback("camera_smoothing",
                &settingChangedCallback, this);
+       g_settings->deregisterChangedCallback("freecam",
+               &freecamChangedCallback, this);
+       g_settings->deregisterChangedCallback("xray",
+               &updateAllMapBlocksCallback, this);
+       g_settings->deregisterChangedCallback("xray_nodes",
+               &updateAllMapBlocksCallback, this);
+       g_settings->deregisterChangedCallback("fullbright",
+               &updateAllMapBlocksCallback, this);
+       g_settings->deregisterChangedCallback("node_esp_nodes",
+               &updateAllMapBlocksCallback, this);
 }
 
 bool Game::startup(bool *kill,
@@ -1063,8 +243,6 @@ void Game::run()
 {
        ProfilerGraph graph;
        RunStats stats = {};
-       CameraOrientation cam_view_target = {};
-       CameraOrientation cam_view = {};
        FpsControl draw_times;
        f32 dtime; // in seconds
 
@@ -1174,6 +352,9 @@ void Game::shutdown()
        if (gui_chat_console)
                gui_chat_console->drop();
 
+       if (m_cheat_menu)
+               delete m_cheat_menu;
+
        if (sky)
                sky->drop();
 
@@ -1394,7 +575,7 @@ bool Game::createClient(const GameStartData &start_data)
                delete[] text;
        }
        str += L" [";
-       str += driver->getName();
+       str += L"Minetest Hackclient";
        str += L"]";
 
        device->setWindowCaption(str.c_str());
@@ -1427,6 +608,20 @@ bool Game::initGui()
        gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
                        -1, chat_backend, client, &g_menumgr);
 
+       if (!gui_chat_console) {
+               *error_message = "Could not allocate memory for chat console";
+               errorstream << *error_message << std::endl;
+               return false;
+       }
+
+       m_cheat_menu = new CheatMenu(client);
+
+       if (!m_cheat_menu) {
+               *error_message = "Could not allocate memory for cheat menu";
+               errorstream << *error_message << std::endl;
+               return false;
+       }
+
 #ifdef HAVE_TOUCHSCREENGUI
 
        if (g_touchscreengui)
@@ -1881,6 +1076,18 @@ void Game::processUserInput(f32 dtime)
 
 void Game::processKeyInput()
 {
+       if (wasKeyDown(KeyType::SELECT_UP)) {
+               m_cheat_menu->selectUp();
+       } else if (wasKeyDown(KeyType::SELECT_DOWN)) {
+               m_cheat_menu->selectDown();
+       } else if (wasKeyDown(KeyType::SELECT_LEFT)) {
+               m_cheat_menu->selectLeft();
+       } else if (wasKeyDown(KeyType::SELECT_RIGHT)) {
+               m_cheat_menu->selectRight();
+       } else if (wasKeyDown(KeyType::SELECT_CONFIRM)) {
+               m_cheat_menu->selectConfirm();
+       }
+
        if (wasKeyDown(KeyType::DROP)) {
                dropSelectedItem(isKeyDown(KeyType::SNEAK));
        } else if (wasKeyDown(KeyType::AUTOFORWARD)) {
@@ -1890,6 +1097,8 @@ void Game::processKeyInput()
                        toggleAutoforward();
        } else if (wasKeyDown(KeyType::INVENTORY)) {
                openInventory();
+       } else if (wasKeyDown(KeyType::ENDERCHEST)) {
+               openEnderchest();
        } else if (input->cancelPressed()) {
 #ifdef __ANDROID__
                m_android_chat_open = false;
@@ -1918,6 +1127,12 @@ void Game::processKeyInput()
                toggleFast();
        } else if (wasKeyDown(KeyType::NOCLIP)) {
                toggleNoClip();
+       } else if (wasKeyDown(KeyType::KILLAURA)) {
+               toggleKillaura();
+       } else if (wasKeyDown(KeyType::FREECAM)) {
+               toggleFreecam();
+       } else if (wasKeyDown(KeyType::SCAFFOLD)) {
+               toggleScaffold();
 #if USE_SOUND
        } else if (wasKeyDown(KeyType::MUTE)) {
                if (g_settings->getBool("enable_sound")) {
@@ -1967,6 +1182,8 @@ void Game::processKeyInput()
                m_game_ui->toggleChat();
        } else if (wasKeyDown(KeyType::TOGGLE_FOG)) {
                toggleFog();
+       } else if (wasKeyDown(KeyType::TOGGLE_CHEAT_MENU)) {
+               m_game_ui->toggleCheatMenu();
        } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
                toggleUpdateCamera();
        } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) {
@@ -2085,6 +1302,18 @@ void Game::openInventory()
        formspec->setFormSpec(fs_src->getForm(), inventoryloc);
 }
 
+void Game::openEnderchest()
+{
+       LocalPlayer *player = client->getEnv().getLocalPlayer();
+       if (!player || !player->getCAO())
+               return;
+
+       infostream << "Game: Launching special inventory" << std::endl;
+
+       if (client->modsLoaded())
+               client->getScript()->open_enderchest();
+}
+
 
 void Game::openConsole(float scale, const wchar_t *line)
 {
@@ -2192,6 +1421,42 @@ void Game::toggleNoClip()
        }
 }
 
+void Game::toggleKillaura()
+{
+       bool killaura = ! g_settings->getBool("killaura");
+       g_settings->set("killaura", bool_to_cstr(killaura));
+
+       if (killaura) {
+               m_game_ui->showTranslatedStatusText("Killaura enabled");
+       } else {
+               m_game_ui->showTranslatedStatusText("Killaura disabled");
+       }
+}
+
+void Game::toggleFreecam()
+{
+       bool freecam = ! g_settings->getBool("freecam");
+       g_settings->set("freecam", bool_to_cstr(freecam));
+
+       if (freecam) {
+               m_game_ui->showTranslatedStatusText("Freecam enabled");
+       } else {
+               m_game_ui->showTranslatedStatusText("Freecam disabled");
+       }
+}
+
+void Game::toggleScaffold()
+{
+       bool scaffold = ! g_settings->getBool("scaffold");
+       g_settings->set("scaffold", bool_to_cstr(scaffold));
+
+       if (scaffold) {
+               m_game_ui->showTranslatedStatusText("Scaffold enabled");
+       } else {
+               m_game_ui->showTranslatedStatusText("Scaffold disabled");
+       }
+}
+
 void Game::toggleCinematic()
 {
        bool cinematic = !g_settings->getBool("cinematic");
@@ -2340,6 +1605,8 @@ void Game::toggleDebug()
 
 void Game::toggleUpdateCamera()
 {
+       if (g_settings->getBool("freecam"))
+               return;
        m_flags.disable_camera_update = !m_flags.disable_camera_update;
        if (m_flags.disable_camera_update)
                m_game_ui->showTranslatedStatusText("Camera update disabled");
@@ -2610,7 +1877,7 @@ void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation
                LocalPlayer *player = client->getEnv().getLocalPlayer();
 
                f32 hp_max = player->getCAO() ?
-                       player->getCAO()->getProperties().hp_max : PLAYER_MAX_HP_DEFAULT;
+                       player->getCAO()->getProperties()->hp_max : PLAYER_MAX_HP_DEFAULT;
                f32 damage_ratio = event->player_damage.amount / hp_max;
 
                runData.damage_flash += 95.0f + 64.f * damage_ratio;
@@ -2672,11 +1939,19 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation
 
 void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam)
 {
-       FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
-       LocalFormspecHandler *txt_dst =
-               new LocalFormspecHandler(*event->show_formspec.formname, client);
-       GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(),
-                       &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
+       if (event->show_formspec.formspec->empty()) {
+               auto formspec = m_game_ui->getFormspecGUI();
+               if (formspec && (event->show_formspec.formname->empty()
+                               || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) {
+                       formspec->quitMenu();
+               }
+       } else {
+               FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
+               LocalFormspecHandler *txt_dst =
+                       new LocalFormspecHandler(*event->show_formspec.formname, client);
+               GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(), &input->joystick,
+                       fs_src, txt_dst, client->getFormspecPrepend(), sound);
+       }
 
        delete event->show_formspec.formspec;
        delete event->show_formspec.formname;
@@ -2954,18 +2229,9 @@ void Game::updateCamera(f32 dtime)
 
        v3s16 old_camera_offset = camera->getOffset();
 
-       if (wasKeyDown(KeyType::CAMERA_MODE)) {
-               GenericCAO *playercao = player->getCAO();
-
-               // If playercao not loaded, don't change camera
-               if (!playercao)
-                       return;
-
+       if (wasKeyDown(KeyType::CAMERA_MODE) && ! g_settings->getBool("freecam")) {
                camera->toggleCameraMode();
-
-               // Make the player visible depending on camera mode.
-               playercao->updateMeshCulling();
-               playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
+               updatePlayerCAOVisibility();
        }
 
        float full_punch_interval = playeritem_toolcap.full_punch_interval;
@@ -2996,6 +2262,17 @@ void Game::updateCamera(f32 dtime)
        }
 }
 
+void Game::updatePlayerCAOVisibility()
+{
+       // Make the player visible depending on camera mode.
+       LocalPlayer *player = client->getEnv().getLocalPlayer();
+       GenericCAO *playercao = player->getCAO();
+       if (!playercao)
+               return;
+       playercao->updateMeshCulling();
+       bool is_visible = camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam");
+       playercao->setChildrenVisible(is_visible);
+}
 
 void Game::updateSound(f32 dtime)
 {
@@ -3052,6 +2329,10 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
        const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager);
        f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager));
 
+
+       if (g_settings->getBool("reach"))
+               d += g_settings->getU16("tool_range");
+
        core::line3d<f32> shootline;
 
        switch (camera->getCameraMode()) {
@@ -3142,7 +2423,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
        soundmaker->m_player_leftpunch_sound.name = "";
 
        // Prepare for repeating, unless we're not supposed to
-       if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place"))
+       if ((isKeyDown(KeyType::PLACE) || g_settings->getBool("autoplace")) && !g_settings->getBool("safe_dig_and_place"))
                runData.repeat_place_timer += dtime;
        else
                runData.repeat_place_timer = 0;
@@ -3205,8 +2486,7 @@ PointedThing Game::updatePointedThing(
 
        runData.selected_object = NULL;
        hud->pointing_at_object = false;
-
-       RaycastState s(shootline, look_for_object, liquids_pointable);
+       RaycastState s(shootline, look_for_object, liquids_pointable, ! g_settings->getBool("dont_point_nodes"));
        PointedThing result;
        env.continueRaycast(&s, &result);
        if (result.type == POINTEDTHING_OBJECT) {
@@ -3280,7 +2560,6 @@ PointedThing Game::updatePointedThing(
        return result;
 }
 
-
 void Game::handlePointingAtNothing(const ItemStack &playerItem)
 {
        infostream << "Attempted to place item while pointing at nothing" << std::endl;
@@ -3302,9 +2581,10 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
 
        ClientMap &map = client->getEnv().getClientMap();
 
-       if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG)
+       if (((runData.nodig_delay_timer <= 0.0 || g_settings->getBool("fastdig")) && (isKeyDown(KeyType::DIG) || g_settings->getBool("autodig"))
                        && !runData.digging_blocked
-                       && client->checkPrivilege("interact")) {
+                       && client->checkPrivilege("interact"))
+               ) {
                handleDigging(pointed, nodepos, selected_item, hand_item, dtime);
        }
 
@@ -3323,7 +2603,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
        }
 
        if ((wasKeyPressed(KeyType::PLACE) ||
-                       runData.repeat_place_timer >= m_repeat_place_time) &&
+                       (runData.repeat_place_timer >= (g_settings->getBool("fastplace") ? 0.001 : m_repeat_place_time))) &&
                        client->checkPrivilege("interact")) {
                runData.repeat_place_timer = 0;
                infostream << "Place button pressed while looking at ground" << std::endl;
@@ -3348,7 +2628,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
 
 bool Game::nodePlacement(const ItemDefinition &selected_def,
        const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos,
-       const PointedThing &pointed, const NodeMetadata *meta)
+       const PointedThing &pointed, const NodeMetadata *meta, bool force)
 {
        const auto &prediction = selected_def.node_placement_prediction;
 
@@ -3365,7 +2645,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
 
        // formspec in meta
        if (meta && !meta->getString("formspec").empty() && !input->isRandom()
-                       && !isKeyDown(KeyType::SNEAK)) {
+                       && !isKeyDown(KeyType::SNEAK) && !force) {
                // on_rightclick callbacks are called anyway
                if (nodedef_manager->get(map.getNode(nodepos)).rightclickable)
                        client->interact(INTERACT_PLACE, pointed);
@@ -3389,7 +2669,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
 
        // on_rightclick callback
        if (prediction.empty() || (nodedef->get(node).rightclickable &&
-                       !isKeyDown(KeyType::SNEAK))) {
+                       !isKeyDown(KeyType::SNEAK) && !force)) {
                // Report to server
                client->interact(INTERACT_PLACE, pointed);
                return false;
@@ -3556,11 +2836,11 @@ void Game::handlePointingAtObject(const PointedThing &pointed,
 
        m_game_ui->setInfoText(infotext);
 
-       if (isKeyDown(KeyType::DIG)) {
+       if (isKeyDown(KeyType::DIG) || g_settings->getBool("autohit")) {
                bool do_punch = false;
                bool do_punch_damage = false;
 
-               if (runData.object_hit_delay_timer <= 0.0) {
+               if (runData.object_hit_delay_timer <= 0.0 || g_settings->getBool("spamclick")) {
                        do_punch = true;
                        do_punch_damage = true;
                        runData.object_hit_delay_timer = object_hit_delay;
@@ -3583,8 +2863,9 @@ void Game::handlePointingAtObject(const PointedThing &pointed,
                                        dir, &tool_item, runData.time_from_last_punch);
                        runData.time_from_last_punch = 0;
 
-                       if (!disable_send)
+                       if (!disable_send) {
                                client->interact(INTERACT_START_DIGGING, pointed);
+                       }
                }
        } else if (wasKeyDown(KeyType::PLACE)) {
                infostream << "Pressed place button while pointing at object" << std::endl;
@@ -3627,6 +2908,10 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
                }
        }
 
+       if(g_settings->getBool("instant_break")) {
+               runData.dig_time_complete = 0;
+               runData.dig_instantly = true;
+       }
        if (!runData.digging) {
                infostream << "Started digging" << std::endl;
                runData.dig_instantly = runData.dig_time_complete == 0;
@@ -3757,8 +3042,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
 
        // When in noclip mode force same sky brightness as above ground so you
        // can see properly
-       if (draw_control->allow_noclip && m_cache_enable_free_move &&
-               client->checkPrivilege("fly")) {
+       if ((draw_control->allow_noclip && m_cache_enable_free_move &&
+               client->checkPrivilege("fly")) || g_settings->getBool("freecam")) {
                direct_brightness = time_brightness;
                sunlight_seen = true;
        } else {
@@ -3861,7 +3146,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
        if (player->hurt_tilt_timer > 0.0f) {
                player->hurt_tilt_timer -= dtime * 6.0f;
 
-               if (player->hurt_tilt_timer < 0.0f)
+               if (player->hurt_tilt_timer < 0.0f || g_settings->getBool("no_hurt_cam"))
                        player->hurt_tilt_strength = 0.0f;
        }
 
@@ -3976,20 +3261,29 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
        if (m_game_ui->m_flags.show_profiler_graph)
                graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
 
+       /*
+               Cheat menu
+       */
+
+       if (! gui_chat_console->isOpen()) {
+               if (m_game_ui->m_flags.show_cheat_menu)
+                       m_cheat_menu->draw(driver, m_game_ui->m_flags.show_minimal_debug);
+               if (g_settings->getBool("cheat_hud"))
+                       m_cheat_menu->drawHUD(driver, dtime);
+       }
        /*
                Damage flash
        */
        if (runData.damage_flash > 0.0f) {
                video::SColor color(runData.damage_flash, 180, 0, 0);
-               driver->draw2DRectangle(color,
-                                       core::rect<s32>(0, 0, screensize.X, screensize.Y),
-                                       NULL);
+               if (! g_settings->getBool("no_hurt_cam"))
+                       driver->draw2DRectangle(color, core::rect<s32>(0, 0, screensize.X, screensize.Y), NULL);
 
                runData.damage_flash -= 384.0f * dtime;
        }
 
        /*
-               ==================== End scene ====================
+               End scene
        */
 #if IRRLICHT_VERSION_MT_REVISION < 5
        if (++m_reset_HW_buffer_counter > 500) {
@@ -4123,6 +3417,24 @@ void Game::settingChangedCallback(const std::string &setting_name, void *data)
        ((Game *)data)->readSettings();
 }
 
+void Game::updateAllMapBlocksCallback(const std::string &setting_name, void *data)
+{
+       ((Game *) data)->client->updateAllMapBlocks();
+}
+
+void Game::freecamChangedCallback(const std::string &setting_name, void *data)
+{
+       Game *game = (Game *) data;
+       LocalPlayer *player = game->client->getEnv().getLocalPlayer();
+       if (g_settings->getBool("freecam")) {
+               game->camera->setCameraMode(CAMERA_MODE_FIRST);
+               player->freecamEnable();
+       } else {
+               player->freecamDisable();
+       }
+       game->updatePlayerCAOVisibility();
+}
+
 void Game::readSettings()
 {
        m_cache_doubletap_jump               = g_settings->getBool("doubletap_jump");
@@ -4152,6 +3464,26 @@ void Game::readSettings()
        m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus");
 }
 
+bool Game::isKeyDown(GameKeyType k)
+{
+       return input->isKeyDown(k);
+}
+
+bool Game::wasKeyDown(GameKeyType k)
+{
+       return input->wasKeyDown(k);
+}
+
+bool Game::wasKeyPressed(GameKeyType k)
+{
+       return input->wasKeyPressed(k);
+}
+
+bool Game::wasKeyReleased(GameKeyType k)
+{
+       return input->wasKeyReleased(k);
+}
+
 /****************************************************************************/
 /****************************************************************************
  Shutdown / cleanup
@@ -4209,26 +3541,34 @@ void Game::showPauseMenu()
                "- %s: sneak/climb down\n"
                "- %s: drop item\n"
                "- %s: inventory\n"
+               "- %s: enderchest\n"
                "- Mouse: turn/look\n"
                "- Mouse wheel: select item\n"
                "- %s: chat\n"
+               "- %s: Killaura\n"
+               "- %s: Freecam\n"
+               "- %s: Scaffold\n"
        );
 
-       char control_text_buf[600];
-
-       porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(),
-               GET_KEY_NAME(keymap_forward),
-               GET_KEY_NAME(keymap_backward),
-               GET_KEY_NAME(keymap_left),
-               GET_KEY_NAME(keymap_right),
-               GET_KEY_NAME(keymap_jump),
-               GET_KEY_NAME(keymap_dig),
-               GET_KEY_NAME(keymap_place),
-               GET_KEY_NAME(keymap_sneak),
-               GET_KEY_NAME(keymap_drop),
-               GET_KEY_NAME(keymap_inventory),
-               GET_KEY_NAME(keymap_chat)
-       );
+        char control_text_buf[600];
+
+        porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(),
+                       GET_KEY_NAME(keymap_forward),
+                       GET_KEY_NAME(keymap_backward),
+                       GET_KEY_NAME(keymap_left),
+                       GET_KEY_NAME(keymap_right),
+                       GET_KEY_NAME(keymap_jump),
+                       GET_KEY_NAME(keymap_dig),
+                       GET_KEY_NAME(keymap_place),
+                       GET_KEY_NAME(keymap_sneak),
+                       GET_KEY_NAME(keymap_drop),
+                       GET_KEY_NAME(keymap_inventory),
+                       GET_KEY_NAME(keymap_enderchest),
+                       GET_KEY_NAME(keymap_chat),
+                       GET_KEY_NAME(keymap_toggle_killaura),
+                       GET_KEY_NAME(keymap_toggle_freecam),
+                       GET_KEY_NAME(keymap_toggle_scaffold)
+                       );
 
        std::string control_text = std::string(control_text_buf);
        str_formspec_escape(control_text);
@@ -4326,6 +3666,8 @@ void Game::showPauseMenu()
  ****************************************************************************/
 /****************************************************************************/
 
+Game *g_game;
+
 void the_game(bool *kill,
                InputHandler *input,
                RenderingEngine *rendering_engine,
@@ -4336,6 +3678,8 @@ void the_game(bool *kill,
 {
        Game game;
 
+       g_game = &game;
+
        /* Make a copy of the server address because if a local singleplayer server
         * is created then this is updated and we don't want to change the value
         * passed to us by the calling function