diff options
author | Quentin Bazin <quent42340@gmail.com> | 2018-11-28 20:01:49 +0100 |
---|---|---|
committer | SmallJoker <SmallJoker@users.noreply.github.com> | 2018-11-28 20:01:49 +0100 |
commit | 5f1cd555cd9d1c64426e173b30b5b792d211c835 (patch) | |
tree | 2c8508467d3bf28d549cce2d25144fa8ef42beae /src/game.cpp | |
parent | ddd9317b733857630499972179caebc236b4d991 (diff) | |
download | minetest-5f1cd555cd9d1c64426e173b30b5b792d211c835.tar.gz minetest-5f1cd555cd9d1c64426e173b30b5b792d211c835.tar.bz2 minetest-5f1cd555cd9d1c64426e173b30b5b792d211c835.zip |
Move client-specific files to 'src/client' (#7902)
Update Android.mk
Remove 'src/client' from include_directories
Diffstat (limited to 'src/game.cpp')
-rw-r--r-- | src/game.cpp | 4218 |
1 files changed, 0 insertions, 4218 deletions
diff --git a/src/game.cpp b/src/game.cpp deleted file mode 100644 index 6cf6723e9..000000000 --- a/src/game.cpp +++ /dev/null @@ -1,4218 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "game.h" - -#include <iomanip> -#include <cmath> -#include "client/renderingengine.h" -#include "camera.h" -#include "client.h" -#include "client/clientevent.h" -#include "client/gameui.h" -#include "client/inputhandler.h" -#include "client/tile.h" // For TextureSource -#include "client/keys.h" -#include "client/joystick_controller.h" -#include "clientmap.h" -#include "clouds.h" -#include "config.h" -#include "content_cao.h" -#include "client/event_manager.h" -#include "fontengine.h" -#include "itemdef.h" -#include "log.h" -#include "filesys.h" -#include "gettext.h" -#include "gui/guiChatConsole.h" -#include "gui/guiConfirmRegistration.h" -#include "gui/guiFormSpecMenu.h" -#include "gui/guiKeyChangeMenu.h" -#include "gui/guiPasswordChange.h" -#include "gui/guiVolumeChange.h" -#include "gui/mainmenumanager.h" -#include "gui/profilergraph.h" -#include "mapblock.h" -#include "minimap.h" -#include "nodedef.h" // Needed for determining pointing to nodes -#include "nodemetadata.h" -#include "particles.h" -#include "porting.h" -#include "profiler.h" -#include "quicktune_shortcutter.h" -#include "raycast.h" -#include "server.h" -#include "settings.h" -#include "shader.h" -#include "sky.h" -#include "translation.h" -#include "util/basic_macros.h" -#include "util/directiontables.h" -#include "util/pointedthing.h" -#include "irrlicht_changes/static_text.h" -#include "version.h" -#include "script/scripting_client.h" - -#if USE_SOUND - #include "client/sound_openal.h" -#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; - } - - if (fields.find("quit") != fields.end()) { - return; - } - - if (fields.find("btn_continue") != fields.end()) { - return; - } - } - - if (m_formname == "MT_DEATH_SCREEN") { - assert(m_client != 0); - m_client->sendRespawn(); - return; - } - - if (m_client && m_client->moddingEnabled()) - 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; - - 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) - { - } - - 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); - } - } - - 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; - } - - 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; - } -}; - -// 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); - } -}; - - -// before 1.8 there isn't a "integer interface", only float -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) -typedef f32 SamplerLayer_t; -#else -typedef s32 SamplerLayer_t; -#endif - - -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, 3> m_eye_position_pixel; - CachedVertexShaderSetting<float, 3> m_eye_position_vertex; - CachedPixelShaderSetting<float, 3> m_minimap_yaw; - CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; - CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture; - CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags; - 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_eye_position_pixel("eyePosition"), - m_eye_position_vertex("eyePosition"), - m_minimap_yaw("yawVec"), - m_base_texture("baseTexture"), - m_normal_texture("normalTexture"), - m_texture_flags("textureFlags"), - 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); - } - - virtual void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel) - { - if (!is_highlevel) - return; - - // 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); - - u32 animation_timer = porting::getTimeMs() % 100000; - 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(); -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - eye_position_array[0] = epos.X; - eye_position_array[1] = epos.Y; - eye_position_array[2] = epos.Z; -#else - epos.getAs3Values(eye_position_array); -#endif - 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(); -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - minimap_yaw_array[0] = minimap_yaw.X; - minimap_yaw_array[1] = minimap_yaw.Y; - minimap_yaw_array[2] = minimap_yaw.Z; -#else - minimap_yaw.getAs3Values(minimap_yaw_array); -#endif - m_minimap_yaw.set(minimap_yaw_array, services); - } - - SamplerLayer_t base_tex = 0, - normal_tex = 1, - flags_tex = 2; - m_base_texture.set(&base_tex, services); - m_normal_texture.set(&normal_tex, services); - m_texture_flags.set(&flags_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() - { - GameGlobalShaderConstantSetter *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 __ANDROID__ -#define SIZE_TAG "size[11,5.5]" -#else -#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop -#endif - -/**************************************************************************** - - ****************************************************************************/ - -const float object_hit_delay = 0.2; - -struct FpsControl { - u32 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 ldown_for_dig; - bool dig_instantly; - bool digging_blocked; - bool left_punch; - bool update_wielded_item_trigger; - bool reset_jump_timer; - float nodig_delay_timer; - float dig_time; - float dig_time_complete; - float repeat_rightclick_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 - ****************************************************************************/ - -/* 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, - bool random_input, - InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - // If address is "", local server is used and address is updated - std::string *address, - u16 port, - std::string &error_message, - bool *reconnect, - ChatBackend *chat_backend, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode); - - void run(); - void shutdown(); - -protected: - - void extendedResourceCleanup(); - - // Basic initialisation - bool init(const std::string &map_dir, std::string *address, - u16 port, - const SubgameSpec &gamespec); - bool initSound(); - bool createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port, std::string *address); - - // Client creation - bool createClient(const std::string &playername, - const std::string &password, std::string *address, u16 port); - bool initGui(); - - // Client connection - bool connectToServer(const std::string &playername, - const std::string &password, std::string *address, u16 port, - 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 addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times, f32 dtime); - void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime); - - // 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 toggleFast(); - void toggleNoClip(); - void toggleCinematic(); - 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(u32 busy_time, f32 dtime); - void updateSound(f32 dtime); - void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug); - /*! - * 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 ItemDefinition &playeritem_def, const ItemStack &playeritem, - const ToolCapabilities &playeritem_toolcap, 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 ToolCapabilities &playeritem_toolcap, f32 dtime); - void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, - const CameraOrientation &cam); - void updateProfilerGraphs(ProfilerGraph *graph); - - // Misc - void limitFps(FpsControl *fps_timings, f32 *dtime); - - 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); - } - -#ifdef __ANDROID__ - void handleAndroidChatInput(); -#endif - -private: - struct Flags { - bool force_fog_off = false; - bool disable_camera_update = false; - }; - - void showDeathFormspec(); - void showPauseMenu(); - - // 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_OverrideDayNigthRatio(ClientEvent *event, - CameraOrientation *cam); - void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam); - - void updateChat(f32 dtime, const v2u32 &screensize); - - bool nodePlacementPrediction(const ItemDefinition &playeritem_def, - const ItemStack &playeritem, const v3s16 &nodepos, const v3s16 &neighbourpos); - static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; - - 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; - - GUIFormSpecMenu *current_formspec = nullptr; - //default: "". If other than "", empty show_formspec packets will only close the formspec when the formname matches - std::string cur_formname; - - 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() - Inventory *local_inventory = nullptr; - Hud *hud = nullptr; - Minimap *mapper = nullptr; - - 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; - video::IVideoDriver *driver; - scene::ISceneManager *smgr; - bool *kill; - std::string *error_message; - bool *reconnect_requested; - scene::ISceneNode *skybox; - - bool random_input; - 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_right_click_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; - -#ifdef __ANDROID__ - bool m_cache_hold_aux1; - bool m_android_chat_open; -#endif -}; - -Game::Game() : - m_game_ui(new GameUI()) -{ - g_settings->registerChangedCallback("doubletap_jump", - &settingChangedCallback, this); - g_settings->registerChangedCallback("enable_clouds", - &settingChangedCallback, this); - g_settings->registerChangedCallback("doubletap_joysticks", - &settingChangedCallback, this); - g_settings->registerChangedCallback("enable_particles", - &settingChangedCallback, this); - g_settings->registerChangedCallback("enable_fog", - &settingChangedCallback, this); - g_settings->registerChangedCallback("mouse_sensitivity", - &settingChangedCallback, this); - g_settings->registerChangedCallback("joystick_frustum_sensitivity", - &settingChangedCallback, this); - g_settings->registerChangedCallback("repeat_rightclick_time", - &settingChangedCallback, this); - g_settings->registerChangedCallback("noclip", - &settingChangedCallback, this); - g_settings->registerChangedCallback("free_move", - &settingChangedCallback, this); - g_settings->registerChangedCallback("cinematic", - &settingChangedCallback, this); - g_settings->registerChangedCallback("cinematic_camera_smoothing", - &settingChangedCallback, this); - g_settings->registerChangedCallback("camera_smoothing", - &settingChangedCallback, this); - - readSettings(); - -#ifdef __ANDROID__ - m_cache_hold_aux1 = false; // This is initialised properly later -#endif - -} - - - -/**************************************************************************** - MinetestApp Public - ****************************************************************************/ - -Game::~Game() -{ - delete client; - delete soundmaker; - if (!sound_is_dummy) - delete sound; - - delete server; // deleted first to stop all server threads - - delete hud; - delete local_inventory; - delete camera; - delete quicktune; - delete eventmgr; - delete texture_src; - delete shader_src; - delete nodedef_manager; - delete itemdef_manager; - delete draw_control; - - extendedResourceCleanup(); - - g_settings->deregisterChangedCallback("doubletap_jump", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("enable_clouds", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("enable_particles", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("enable_fog", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("mouse_sensitivity", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("repeat_rightclick_time", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("noclip", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("free_move", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("cinematic", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("cinematic_camera_smoothing", - &settingChangedCallback, this); - g_settings->deregisterChangedCallback("camera_smoothing", - &settingChangedCallback, this); -} - -bool Game::startup(bool *kill, - bool random_input, - InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - std::string *address, // can change if simple_singleplayer_mode - u16 port, - std::string &error_message, - bool *reconnect, - ChatBackend *chat_backend, - const SubgameSpec &gamespec, - bool simple_singleplayer_mode) -{ - // "cache" - this->device = RenderingEngine::get_raw_device(); - this->kill = kill; - this->error_message = &error_message; - this->reconnect_requested = reconnect; - this->random_input = random_input; - this->input = input; - this->chat_backend = chat_backend; - this->simple_singleplayer_mode = simple_singleplayer_mode; - - input->keycache.populate(); - - driver = device->getVideoDriver(); - smgr = RenderingEngine::get_scene_manager(); - - RenderingEngine::get_scene_manager()->getParameters()-> - setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true); - - // Reinit runData - runData = GameRunData(); - runData.time_from_last_punch = 10.0; - runData.update_wielded_item_trigger = true; - - m_game_ui->initFlags(); - - m_invert_mouse = g_settings->getBool("invert_mouse"); - m_first_loop_after_window_activation = true; - - g_translations->clear(); - - if (!init(map_dir, address, port, gamespec)) - return false; - - if (!createClient(playername, password, address, port)) - return false; - - RenderingEngine::initialize(client, hud); - - return true; -} - - -void Game::run() -{ - ProfilerGraph graph; - RunStats stats = { 0 }; - CameraOrientation cam_view_target = { 0 }; - CameraOrientation cam_view = { 0 }; - FpsControl draw_times = { 0 }; - f32 dtime; // in seconds - - /* Clear the profiler */ - Profiler::GraphValues dummyvalues; - g_profiler->graphGet(dummyvalues); - - draw_times.last_time = RenderingEngine::get_timer_time(); - - set_light_table(g_settings->getFloat("display_gamma")); - -#ifdef __ANDROID__ - m_cache_hold_aux1 = g_settings->getBool("fast_move") - && client->checkPrivilege("fast"); -#endif - - irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"), - g_settings->getU16("screen_h")); - - while (RenderingEngine::run() - && !(*kill || g_gamecallback->shutdown_requested - || (server && server->isShutdownRequested()))) { - - const irr::core::dimension2d<u32> ¤t_screen_size = - RenderingEngine::get_video_driver()->getScreenSize(); - // Verify if window size has changed and save it if it's the case - // Ensure evaluating settings->getBool after verifying screensize - // First condition is cheaper - if (previous_screen_size != current_screen_size && - current_screen_size != irr::core::dimension2d<u32>(0,0) && - g_settings->getBool("autosave_screensize")) { - g_settings->setU16("screen_w", current_screen_size.Width); - g_settings->setU16("screen_h", current_screen_size.Height); - previous_screen_size = current_screen_size; - } - - /* Must be called immediately after a device->run() call because it - * uses device->getTimer()->getTime() - */ - limitFps(&draw_times, &dtime); - - updateStats(&stats, draw_times, dtime); - updateInteractTimers(dtime); - - if (!checkConnection()) - break; - if (!handleCallbacks()) - break; - - processQueues(); - - m_game_ui->clearInfoText(); - hud->resizeHotbar(); - - updateProfilers(stats, draw_times, dtime); - processUserInput(dtime); - // Update camera before player movement to avoid camera lag of one frame - updateCameraDirection(&cam_view_target, dtime); - cam_view.camera_yaw += (cam_view_target.camera_yaw - - cam_view.camera_yaw) * m_cache_cam_smoothing; - cam_view.camera_pitch += (cam_view_target.camera_pitch - - cam_view.camera_pitch) * m_cache_cam_smoothing; - updatePlayerControl(cam_view); - step(&dtime); - processClientEvents(&cam_view_target); - updateCamera(draw_times.busy_time, dtime); - updateSound(dtime); - processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud, - m_game_ui->m_flags.show_debug); - updateFrame(&graph, &stats, dtime, cam_view); - updateProfilerGraphs(&graph); - - // Update if minimap has been disabled by the server - m_game_ui->m_flags.show_minimap &= client->shouldShowMinimap(); - - if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) { - showPauseMenu(); - } - } -} - - -void Game::shutdown() -{ - RenderingEngine::finalize(); -#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8 - if (g_settings->get("3d_mode") == "pageflip") { - driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS); - } -#endif - if (current_formspec) - current_formspec->quitMenu(); - - showOverlayMessage("Shutting down...", 0, 0, false); - - if (clouds) - clouds->drop(); - - if (gui_chat_console) - gui_chat_console->drop(); - - if (sky) - sky->drop(); - - /* cleanup menus */ - while (g_menumgr.menuCount() > 0) { - g_menumgr.m_stack.front()->setVisible(false); - g_menumgr.deletingMenu(g_menumgr.m_stack.front()); - } - - if (current_formspec) { - current_formspec->drop(); - current_formspec = NULL; - } - - chat_backend->addMessage(L"", L"# Disconnected."); - chat_backend->addMessage(L"", L""); - - if (client) { - client->Stop(); - while (!client->isShutdown()) { - assert(texture_src != NULL); - assert(shader_src != NULL); - texture_src->processQueue(); - shader_src->processQueue(); - sleep_ms(100); - } - } -} - - -/****************************************************************************/ -/**************************************************************************** - Startup - ****************************************************************************/ -/****************************************************************************/ - -bool Game::init( - const std::string &map_dir, - std::string *address, - u16 port, - const SubgameSpec &gamespec) -{ - texture_src = createTextureSource(); - - showOverlayMessage("Loading...", 0, 0); - - shader_src = createShaderSource(); - - itemdef_manager = createItemDefManager(); - nodedef_manager = createNodeDefManager(); - - eventmgr = new EventManager(); - quicktune = new QuicktuneShortcutter(); - - if (!(texture_src && shader_src && itemdef_manager && nodedef_manager - && eventmgr && quicktune)) - return false; - - if (!initSound()) - return false; - - // Create a server if not connecting to an existing one - if (address->empty()) { - if (!createSingleplayerServer(map_dir, gamespec, port, address)) - return false; - } - - return true; -} - -bool Game::initSound() -{ -#if USE_SOUND - if (g_settings->getBool("enable_sound")) { - infostream << "Attempting to use OpenAL audio" << std::endl; - sound = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher); - if (!sound) - infostream << "Failed to initialize OpenAL audio" << std::endl; - } else - infostream << "Sound disabled." << std::endl; -#endif - - if (!sound) { - infostream << "Using dummy audio." << std::endl; - sound = &dummySoundManager; - sound_is_dummy = true; - } - - soundmaker = new SoundMaker(sound, nodedef_manager); - if (!soundmaker) - return false; - - soundmaker->registerReceiver(eventmgr); - - return true; -} - -bool Game::createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port, std::string *address) -{ - showOverlayMessage("Creating server...", 0, 5); - - std::string bind_str = g_settings->get("bind_address"); - Address bind_addr(0, 0, 0, 0, port); - - if (g_settings->getBool("ipv6_server")) { - bind_addr.setAddress((IPv6AddressBytes *) NULL); - } - - try { - bind_addr.Resolve(bind_str.c_str()); - } catch (ResolveError &e) { - infostream << "Resolving bind address \"" << bind_str - << "\" failed: " << e.what() - << " -- Listening on all addresses." << std::endl; - } - - if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) { - *error_message = "Unable to listen on " + - bind_addr.serializeString() + - " because IPv6 is disabled"; - errorstream << *error_message << std::endl; - return false; - } - - server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); - server->init(); - server->start(); - - return true; -} - -bool Game::createClient(const std::string &playername, - const std::string &password, std::string *address, u16 port) -{ - showOverlayMessage("Creating client...", 0, 10); - - draw_control = new MapDrawControl; - if (!draw_control) - return false; - - bool could_connect, connect_aborted; -#ifdef HAVE_TOUCHSCREENGUI - if (g_touchscreengui) { - g_touchscreengui->init(texture_src); - g_touchscreengui->hide(); - } -#endif - if (!connectToServer(playername, password, address, port, - &could_connect, &connect_aborted)) - return false; - - if (!could_connect) { - if (error_message->empty() && !connect_aborted) { - // Should not happen if error messages are set properly - *error_message = "Connection failed for unknown reason"; - errorstream << *error_message << std::endl; - } - return false; - } - - if (!getServerContent(&connect_aborted)) { - if (error_message->empty() && !connect_aborted) { - // Should not happen if error messages are set properly - *error_message = "Connection failed for unknown reason"; - errorstream << *error_message << std::endl; - } - return false; - } - - GameGlobalShaderConstantSetterFactory *scsf = new GameGlobalShaderConstantSetterFactory( - &m_flags.force_fog_off, &runData.fog_range, client); - shader_src->addShaderConstantSetterFactory(scsf); - - // Update cached textures, meshes and materials - client->afterContentReceived(); - - /* Camera - */ - camera = new Camera(*draw_control, client); - if (!camera || !camera->successfullyCreated(*error_message)) - return false; - client->setCamera(camera); - - /* Clouds - */ - if (m_cache_enable_clouds) { - clouds = new Clouds(smgr, -1, time(0)); - if (!clouds) { - *error_message = "Memory allocation error (clouds)"; - errorstream << *error_message << std::endl; - return false; - } - } - - /* Skybox - */ - sky = new Sky(-1, texture_src); - scsf->setSky(sky); - skybox = NULL; // This is used/set later on in the main run loop - - local_inventory = new Inventory(itemdef_manager); - - if (!(sky && local_inventory)) { - *error_message = "Memory allocation error (sky or local inventory)"; - errorstream << *error_message << std::endl; - return false; - } - - /* Pre-calculated values - */ - video::ITexture *t = texture_src->getTexture("crack_anylength.png"); - if (t) { - v2u32 size = t->getOriginalSize(); - crack_animation_length = size.Y / size.X; - } else { - crack_animation_length = 5; - } - - if (!initGui()) - return false; - - /* Set window caption - */ - std::wstring str = utf8_to_wide(PROJECT_NAME_C); - str += L" "; - str += utf8_to_wide(g_version_hash); - str += L" ["; - str += driver->getName(); - str += L"]"; - device->setWindowCaption(str.c_str()); - - LocalPlayer *player = client->getEnv().getLocalPlayer(); - player->hurt_tilt_timer = 0; - player->hurt_tilt_strength = 0; - - hud = new Hud(guienv, client, player, local_inventory); - - if (!hud) { - *error_message = "Memory error: could not create HUD"; - errorstream << *error_message << std::endl; - return false; - } - - mapper = client->getMinimap(); - if (mapper) - mapper->setMinimapMode(MINIMAP_MODE_OFF); - - return true; -} - -bool Game::initGui() -{ - m_game_ui->init(); - - // Remove stale "recent" chat messages from previous connections - chat_backend->clearRecentChat(); - - // Make sure the size of the recent messages buffer is right - chat_backend->applySettings(); - - // Chat backend and console - 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; - } - -#ifdef HAVE_TOUCHSCREENGUI - - if (g_touchscreengui) - g_touchscreengui->show(); - -#endif - - return true; -} - -bool Game::connectToServer(const std::string &playername, - const std::string &password, std::string *address, u16 port, - bool *connect_ok, bool *connection_aborted) -{ - *connect_ok = false; // Let's not be overly optimistic - *connection_aborted = false; - bool local_server_mode = false; - - showOverlayMessage("Resolving address...", 0, 15); - - Address connect_address(0, 0, 0, 0, port); - - try { - connect_address.Resolve(address->c_str()); - - if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY - //connect_address.Resolve("localhost"); - if (connect_address.isIPv6()) { - IPv6AddressBytes addr_bytes; - addr_bytes.bytes[15] = 1; - connect_address.setAddress(&addr_bytes); - } else { - connect_address.setAddress(127, 0, 0, 1); - } - local_server_mode = true; - } - } catch (ResolveError &e) { - *error_message = std::string("Couldn't resolve address: ") + e.what(); - errorstream << *error_message << std::endl; - return false; - } - - if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) { - *error_message = "Unable to connect to " + - connect_address.serializeString() + - " because IPv6 is disabled"; - errorstream << *error_message << std::endl; - return false; - } - - client = new Client(playername.c_str(), password, *address, - *draw_control, texture_src, shader_src, - itemdef_manager, nodedef_manager, sound, eventmgr, - connect_address.isIPv6(), m_game_ui.get()); - - if (!client) - return false; - - client->m_simple_singleplayer_mode = simple_singleplayer_mode; - - infostream << "Connecting to server at "; - connect_address.print(&infostream); - infostream << std::endl; - - client->connect(connect_address, - simple_singleplayer_mode || local_server_mode); - - /* - Wait for server to accept connection - */ - - try { - input->clear(); - - FpsControl fps_control = { 0 }; - f32 dtime; - f32 wait_time = 0; // in seconds - - fps_control.last_time = RenderingEngine::get_timer_time(); - - while (RenderingEngine::run()) { - - limitFps(&fps_control, &dtime); - - // Update client and server - client->step(dtime); - - if (server != NULL) - server->step(dtime); - - // End condition - if (client->getState() == LC_Init) { - *connect_ok = true; - break; - } - - // Break conditions - if (*connection_aborted) - break; - - if (client->accessDenied()) { - *error_message = "Access denied. Reason: " - + client->accessDeniedReason(); - *reconnect_requested = client->reconnectRequested(); - errorstream << *error_message << std::endl; - break; - } - - if (input->cancelPressed()) { - *connection_aborted = true; - infostream << "Connect aborted [Escape]" << std::endl; - break; - } - - if (client->m_is_registration_confirmation_state) { - if (registration_confirmation_shown) { - // Keep drawing the GUI - RenderingEngine::draw_menu_scene(guienv, dtime, true); - } else { - registration_confirmation_shown = true; - (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, *address, connection_aborted))->drop(); - } - } else { - wait_time += dtime; - // Only time out if we aren't waiting for the server we started - if (!address->empty() && wait_time > 10) { - *error_message = "Connection timed out."; - errorstream << *error_message << std::endl; - break; - } - - // Update status - showOverlayMessage("Connecting to server...", dtime, 20); - } - } - } catch (con::PeerNotFoundException &e) { - // TODO: Should something be done here? At least an info/error - // message? - return false; - } - - return true; -} - -bool Game::getServerContent(bool *aborted) -{ - input->clear(); - - FpsControl fps_control = { 0 }; - f32 dtime; // in seconds - - fps_control.last_time = RenderingEngine::get_timer_time(); - - while (RenderingEngine::run()) { - - limitFps(&fps_control, &dtime); - - // Update client and server - client->step(dtime); - - if (server != NULL) - server->step(dtime); - - // End condition - if (client->mediaReceived() && client->itemdefReceived() && - client->nodedefReceived()) { - break; - } - - // Error conditions - if (!checkConnection()) - return false; - - if (client->getState() < LC_Init) { - *error_message = "Client disconnected"; - errorstream << *error_message << std::endl; - return false; - } - - if (input->cancelPressed()) { - *aborted = true; - infostream << "Connect aborted [Escape]" << std::endl; - return false; - } - - // Display status - int progress = 25; - - if (!client->itemdefReceived()) { - const wchar_t *text = wgettext("Item definitions..."); - progress = 25; - RenderingEngine::draw_load_screen(text, guienv, texture_src, - dtime, progress); - delete[] text; - } else if (!client->nodedefReceived()) { - const wchar_t *text = wgettext("Node definitions..."); - progress = 30; - RenderingEngine::draw_load_screen(text, guienv, texture_src, - dtime, progress); - delete[] text; - } else { - std::stringstream message; - std::fixed(message); - message.precision(0); - message << gettext("Media...") << " " << (client->mediaReceiveProgress()*100) << "%"; - message.precision(2); - - if ((USE_CURL == 0) || - (!g_settings->getBool("enable_remote_media_server"))) { - float cur = client->getCurRate(); - std::string cur_unit = gettext("KiB/s"); - - if (cur > 900) { - cur /= 1024.0; - cur_unit = gettext("MiB/s"); - } - - message << " (" << cur << ' ' << cur_unit << ")"; - } - - progress = 30 + client->mediaReceiveProgress() * 35 + 0.5; - RenderingEngine::draw_load_screen(utf8_to_wide(message.str()), guienv, - texture_src, dtime, progress); - } - } - - return true; -} - - -/****************************************************************************/ -/**************************************************************************** - Run - ****************************************************************************/ -/****************************************************************************/ - -inline void Game::updateInteractTimers(f32 dtime) -{ - if (runData.nodig_delay_timer >= 0) - runData.nodig_delay_timer -= dtime; - - if (runData.object_hit_delay_timer >= 0) - runData.object_hit_delay_timer -= dtime; - - runData.time_from_last_punch += dtime; -} - - -/* returns false if game should exit, otherwise true - */ -inline bool Game::checkConnection() -{ - if (client->accessDenied()) { - *error_message = "Access denied. Reason: " - + client->accessDeniedReason(); - *reconnect_requested = client->reconnectRequested(); - errorstream << *error_message << std::endl; - return false; - } - - return true; -} - - -/* returns false if game should exit, otherwise true - */ -inline bool Game::handleCallbacks() -{ - if (g_gamecallback->disconnect_requested) { - g_gamecallback->disconnect_requested = false; - return false; - } - - if (g_gamecallback->changepassword_requested) { - (new GUIPasswordChange(guienv, guiroot, -1, - &g_menumgr, client))->drop(); - g_gamecallback->changepassword_requested = false; - } - - if (g_gamecallback->changevolume_requested) { - (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr))->drop(); - g_gamecallback->changevolume_requested = false; - } - - if (g_gamecallback->keyconfig_requested) { - (new GUIKeyChangeMenu(guienv, guiroot, -1, - &g_menumgr))->drop(); - g_gamecallback->keyconfig_requested = false; - } - - if (g_gamecallback->keyconfig_changed) { - input->keycache.populate(); // update the cache with new settings - g_gamecallback->keyconfig_changed = false; - } - - return true; -} - - -void Game::processQueues() -{ - texture_src->processQueue(); - itemdef_manager->processQueue(client); - shader_src->processQueue(); -} - - -void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime) -{ - float profiler_print_interval = - g_settings->getFloat("profiler_print_interval"); - bool print_to_log = true; - - if (profiler_print_interval == 0) { - print_to_log = false; - profiler_print_interval = 5; - } - - if (profiler_interval.step(dtime, profiler_print_interval)) { - if (print_to_log) { - infostream << "Profiler:" << std::endl; - g_profiler->print(infostream); - } - - m_game_ui->updateProfiler(); - g_profiler->clear(); - } - - addProfilerGraphs(stats, draw_times, dtime); -} - - -void Game::addProfilerGraphs(const RunStats &stats, - const FpsControl &draw_times, f32 dtime) -{ - g_profiler->graphAdd("mainloop_other", - draw_times.busy_time / 1000.0f - stats.drawtime / 1000.0f); - - if (draw_times.sleep_time != 0) - g_profiler->graphAdd("mainloop_sleep", draw_times.sleep_time / 1000.0f); - g_profiler->graphAdd("mainloop_dtime", dtime); - - g_profiler->add("Elapsed time", dtime); - g_profiler->avg("FPS", 1. / dtime); -} - - -void Game::updateStats(RunStats *stats, const FpsControl &draw_times, - f32 dtime) -{ - - f32 jitter; - Jitter *jp; - - /* Time average and jitter calculation - */ - jp = &stats->dtime_jitter; - jp->avg = jp->avg * 0.96 + dtime * 0.04; - - jitter = dtime - jp->avg; - - if (jitter > jp->max) - jp->max = jitter; - - jp->counter += dtime; - - if (jp->counter > 0.0) { - jp->counter -= 3.0; - jp->max_sample = jp->max; - jp->max_fraction = jp->max_sample / (jp->avg + 0.001); - jp->max = 0.0; - } - - /* Busytime average and jitter calculation - */ - jp = &stats->busy_time_jitter; - jp->avg = jp->avg + draw_times.busy_time * 0.02; - - jitter = draw_times.busy_time - jp->avg; - - if (jitter > jp->max) - jp->max = jitter; - if (jitter < jp->min) - jp->min = jitter; - - jp->counter += dtime; - - if (jp->counter > 0.0) { - jp->counter -= 3.0; - jp->max_sample = jp->max; - jp->min_sample = jp->min; - jp->max = 0.0; - jp->min = 0.0; - } -} - - - -/**************************************************************************** - Input handling - ****************************************************************************/ - -void Game::processUserInput(f32 dtime) -{ - // Reset input if window not active or some menu is active - if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) { - input->clear(); -#ifdef HAVE_TOUCHSCREENGUI - g_touchscreengui->hide(); -#endif - } -#ifdef HAVE_TOUCHSCREENGUI - else if (g_touchscreengui) { - /* on touchscreengui step may generate own input events which ain't - * what we want in case we just did clear them */ - g_touchscreengui->step(dtime); - } -#endif - - if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) { - gui_chat_console->closeConsoleAtOnce(); - } - - // Input handler step() (used by the random input generator) - input->step(dtime); - -#ifdef __ANDROID__ - if (current_formspec != NULL) - current_formspec->getAndroidUIInput(); - else - handleAndroidChatInput(); -#endif - - // Increase timer for double tap of "keymap_jump" - if (m_cache_doubletap_jump && runData.jump_timer <= 0.2f) - runData.jump_timer += dtime; - - processKeyInput(); - processItemSelection(&runData.new_playeritem); -} - - -void Game::processKeyInput() -{ - if (wasKeyDown(KeyType::DROP)) { - dropSelectedItem(isKeyDown(KeyType::SNEAK)); - } else if (wasKeyDown(KeyType::AUTOFORWARD)) { - toggleAutoforward(); - } else if (wasKeyDown(KeyType::BACKWARD)) { - if (g_settings->getBool("continuous_forward")) - toggleAutoforward(); - } else if (wasKeyDown(KeyType::INVENTORY)) { - openInventory(); - } else if (input->cancelPressed()) { - if (!gui_chat_console->isOpenInhibited()) { - showPauseMenu(); - } - } else if (wasKeyDown(KeyType::CHAT)) { - openConsole(0.2, L""); - } else if (wasKeyDown(KeyType::CMD)) { - openConsole(0.2, L"/"); - } else if (wasKeyDown(KeyType::CMD_LOCAL)) { - if (client->moddingEnabled()) - openConsole(0.2, L"."); - else - m_game_ui->showStatusText(wgettext("CSM is disabled")); - } else if (wasKeyDown(KeyType::CONSOLE)) { - openConsole(core::clamp(g_settings->getFloat("console_height"), 0.1f, 1.0f)); - } else if (wasKeyDown(KeyType::FREEMOVE)) { - toggleFreeMove(); - } else if (wasKeyDown(KeyType::JUMP)) { - toggleFreeMoveAlt(); - } else if (wasKeyDown(KeyType::FASTMOVE)) { - toggleFast(); - } else if (wasKeyDown(KeyType::NOCLIP)) { - toggleNoClip(); - } else if (wasKeyDown(KeyType::MUTE)) { - bool new_mute_sound = !g_settings->getBool("mute_sound"); - g_settings->setBool("mute_sound", new_mute_sound); - if (new_mute_sound) - m_game_ui->showTranslatedStatusText("Sound muted"); - else - m_game_ui->showTranslatedStatusText("Sound unmuted"); - } else if (wasKeyDown(KeyType::INC_VOLUME)) { - float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f); - wchar_t buf[100]; - g_settings->setFloat("sound_volume", new_volume); - const wchar_t *str = wgettext("Volume changed to %d%%"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100)); - delete[] str; - m_game_ui->showStatusText(buf); - } else if (wasKeyDown(KeyType::DEC_VOLUME)) { - float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f); - wchar_t buf[100]; - g_settings->setFloat("sound_volume", new_volume); - const wchar_t *str = wgettext("Volume changed to %d%%"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100)); - delete[] str; - m_game_ui->showStatusText(buf); - } else if (wasKeyDown(KeyType::CINEMATIC)) { - toggleCinematic(); - } else if (wasKeyDown(KeyType::SCREENSHOT)) { - client->makeScreenshot(); - } else if (wasKeyDown(KeyType::TOGGLE_HUD)) { - m_game_ui->toggleHud(); - } else if (wasKeyDown(KeyType::MINIMAP)) { - toggleMinimap(isKeyDown(KeyType::SNEAK)); - } else if (wasKeyDown(KeyType::TOGGLE_CHAT)) { - m_game_ui->toggleChat(); - } else if (wasKeyDown(KeyType::TOGGLE_FOG)) { - toggleFog(); - } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) { - toggleUpdateCamera(); - } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) { - toggleDebug(); - } else if (wasKeyDown(KeyType::TOGGLE_PROFILER)) { - m_game_ui->toggleProfiler(); - } else if (wasKeyDown(KeyType::INCREASE_VIEWING_RANGE)) { - increaseViewRange(); - } else if (wasKeyDown(KeyType::DECREASE_VIEWING_RANGE)) { - decreaseViewRange(); - } else if (wasKeyDown(KeyType::RANGESELECT)) { - toggleFullViewRange(); - } else if (wasKeyDown(KeyType::ZOOM)) { - checkZoomEnabled(); - } else if (wasKeyDown(KeyType::QUICKTUNE_NEXT)) { - quicktune->next(); - } else if (wasKeyDown(KeyType::QUICKTUNE_PREV)) { - quicktune->prev(); - } else if (wasKeyDown(KeyType::QUICKTUNE_INC)) { - quicktune->inc(); - } else if (wasKeyDown(KeyType::QUICKTUNE_DEC)) { - quicktune->dec(); - } - - if (!isKeyDown(KeyType::JUMP) && runData.reset_jump_timer) { - runData.reset_jump_timer = false; - runData.jump_timer = 0.0f; - } - - if (quicktune->hasMessage()) { - m_game_ui->showStatusText(utf8_to_wide(quicktune->getMessage())); - } -} - -void Game::processItemSelection(u16 *new_playeritem) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - /* Item selection using mouse wheel - */ - *new_playeritem = client->getPlayerItem(); - - s32 wheel = input->getMouseWheel(); - u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1, - player->hud_hotbar_itemcount - 1); - - s32 dir = wheel; - - if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN) || - wasKeyDown(KeyType::HOTBAR_NEXT)) { - dir = -1; - } - - if (input->joystick.wasKeyDown(KeyType::SCROLL_UP) || - wasKeyDown(KeyType::HOTBAR_PREV)) { - dir = 1; - } - - if (dir < 0) - *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0; - else if (dir > 0) - *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item; - // else dir == 0 - - /* Item selection using hotbar slot keys - */ - for (u16 i = 0; i < 23; i++) { - if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) { - if (i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount) { - *new_playeritem = i; - infostream << "Selected item: " << new_playeritem << std::endl; - } - break; - } - } -} - - -void Game::dropSelectedItem(bool single_item) -{ - IDropAction *a = new IDropAction(); - a->count = single_item ? 1 : 0; - a->from_inv.setCurrentPlayer(); - a->from_list = "main"; - a->from_i = client->getPlayerItem(); - client->inventoryAction(a); -} - - -void Game::openInventory() -{ - /* - * Don't permit to open inventory is CAO or player doesn't exists. - * This prevent showing an empty inventory at player load - */ - - LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (!player || !player->getCAO()) - return; - - infostream << "the_game: " << "Launching inventory" << std::endl; - - PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); - - InventoryLocation inventoryloc; - inventoryloc.setCurrentPlayer(); - - if (!client->moddingEnabled() - || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { - TextDest *txt_dst = new TextDestPlayerInventory(client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - cur_formname = ""; - current_formspec->setFormSpec(fs_src->getForm(), inventoryloc); - } -} - - -void Game::openConsole(float scale, const wchar_t *line) -{ - assert(scale > 0.0f && scale <= 1.0f); - -#ifdef __ANDROID__ - porting::showInputDialog(gettext("ok"), "", "", 2); - m_android_chat_open = true; -#else - if (gui_chat_console->isOpenInhibited()) - return; - gui_chat_console->openConsole(scale); - if (line) { - gui_chat_console->setCloseOnEnter(true); - gui_chat_console->replaceAndAddToHistory(line); - } -#endif -} - -#ifdef __ANDROID__ -void Game::handleAndroidChatInput() -{ - if (m_android_chat_open && porting::getInputDialogState() == 0) { - std::string text = porting::getInputDialogValue(); - client->typeChatMessage(utf8_to_wide(text)); - } -} -#endif - - -void Game::toggleFreeMove() -{ - bool free_move = !g_settings->getBool("free_move"); - g_settings->set("free_move", bool_to_cstr(free_move)); - - if (free_move) { - if (client->checkPrivilege("fly")) { - m_game_ui->showTranslatedStatusText("Fly mode enabled"); - } else { - m_game_ui->showTranslatedStatusText("Fly mode enabled (note: no 'fly' privilege)"); - } - } else { - m_game_ui->showTranslatedStatusText("Fly mode disabled"); - } -} - -void Game::toggleFreeMoveAlt() -{ - if (m_cache_doubletap_jump && runData.jump_timer < 0.2f) - toggleFreeMove(); - - runData.reset_jump_timer = true; -} - - -void Game::toggleFast() -{ - bool fast_move = !g_settings->getBool("fast_move"); - bool has_fast_privs = client->checkPrivilege("fast"); - g_settings->set("fast_move", bool_to_cstr(fast_move)); - - if (fast_move) { - if (has_fast_privs) { - m_game_ui->showTranslatedStatusText("Fast mode enabled"); - } else { - m_game_ui->showTranslatedStatusText("Fast mode enabled (note: no 'fast' privilege)"); - } - } else { - m_game_ui->showTranslatedStatusText("Fast mode disabled"); - } - -#ifdef __ANDROID__ - m_cache_hold_aux1 = fast_move && has_fast_privs; -#endif -} - - -void Game::toggleNoClip() -{ - bool noclip = !g_settings->getBool("noclip"); - g_settings->set("noclip", bool_to_cstr(noclip)); - - if (noclip) { - if (client->checkPrivilege("noclip")) { - m_game_ui->showTranslatedStatusText("Noclip mode enabled"); - } else { - m_game_ui->showTranslatedStatusText("Noclip mode enabled (note: no 'noclip' privilege)"); - } - } else { - m_game_ui->showTranslatedStatusText("Noclip mode disabled"); - } -} - -void Game::toggleCinematic() -{ - bool cinematic = !g_settings->getBool("cinematic"); - g_settings->set("cinematic", bool_to_cstr(cinematic)); - - if (cinematic) - m_game_ui->showTranslatedStatusText("Cinematic mode enabled"); - else - m_game_ui->showTranslatedStatusText("Cinematic mode disabled"); -} - -// Autoforward by toggling continuous forward. -void Game::toggleAutoforward() -{ - bool autorun_enabled = !g_settings->getBool("continuous_forward"); - g_settings->set("continuous_forward", bool_to_cstr(autorun_enabled)); - - if (autorun_enabled) - m_game_ui->showTranslatedStatusText("Automatic forwards enabled"); - else - m_game_ui->showTranslatedStatusText("Automatic forwards disabled"); -} - -void Game::toggleMinimap(bool shift_pressed) -{ - if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap")) - return; - - if (shift_pressed) { - mapper->toggleMinimapShape(); - return; - } - - u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags; - - MinimapMode mode = MINIMAP_MODE_OFF; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) { - mode = mapper->getMinimapMode(); - mode = (MinimapMode)((int)mode + 1); - // If radar is disabled and in, or switching to, radar mode - if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE) && mode > 3) - mode = MINIMAP_MODE_OFF; - } - - m_game_ui->m_flags.show_minimap = true; - switch (mode) { - case MINIMAP_MODE_SURFACEx1: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x1"); - break; - case MINIMAP_MODE_SURFACEx2: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x2"); - break; - case MINIMAP_MODE_SURFACEx4: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x4"); - break; - case MINIMAP_MODE_RADARx1: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x1"); - break; - case MINIMAP_MODE_RADARx2: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x2"); - break; - case MINIMAP_MODE_RADARx4: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x4"); - break; - default: - mode = MINIMAP_MODE_OFF; - m_game_ui->m_flags.show_minimap = false; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) - m_game_ui->showTranslatedStatusText("Minimap hidden"); - else - m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); - } - - mapper->setMinimapMode(mode); -} - -void Game::toggleFog() -{ - bool fog_enabled = g_settings->getBool("enable_fog"); - g_settings->setBool("enable_fog", !fog_enabled); - if (fog_enabled) - m_game_ui->showTranslatedStatusText("Fog disabled"); - else - m_game_ui->showTranslatedStatusText("Fog enabled"); -} - - -void Game::toggleDebug() -{ - // Initial / 4x toggle: Chat only - // 1x toggle: Debug text with chat - // 2x toggle: Debug text with profiler graph - // 3x toggle: Debug text and wireframe - if (!m_game_ui->m_flags.show_debug) { - m_game_ui->m_flags.show_debug = true; - m_game_ui->m_flags.show_profiler_graph = false; - draw_control->show_wireframe = false; - m_game_ui->showTranslatedStatusText("Debug info shown"); - } else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) { - m_game_ui->m_flags.show_profiler_graph = true; - m_game_ui->showTranslatedStatusText("Profiler graph shown"); - } else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) { - m_game_ui->m_flags.show_profiler_graph = false; - draw_control->show_wireframe = true; - m_game_ui->showTranslatedStatusText("Wireframe shown"); - } else { - m_game_ui->m_flags.show_debug = false; - m_game_ui->m_flags.show_profiler_graph = false; - draw_control->show_wireframe = false; - if (client->checkPrivilege("debug")) { - m_game_ui->showTranslatedStatusText("Debug info, profiler graph, and wireframe hidden"); - } else { - m_game_ui->showTranslatedStatusText("Debug info and profiler graph hidden"); - } - } -} - - -void Game::toggleUpdateCamera() -{ - m_flags.disable_camera_update = !m_flags.disable_camera_update; - if (m_flags.disable_camera_update) - m_game_ui->showTranslatedStatusText("Camera update disabled"); - else - m_game_ui->showTranslatedStatusText("Camera update enabled"); -} - - -void Game::increaseViewRange() -{ - s16 range = g_settings->getS16("viewing_range"); - s16 range_new = range + 10; - - wchar_t buf[255]; - const wchar_t *str; - if (range_new > 4000) { - range_new = 4000; - str = wgettext("Viewing range is at maximum: %d"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new); - delete[] str; - m_game_ui->showStatusText(buf); - - } else { - str = wgettext("Viewing range changed to %d"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new); - delete[] str; - m_game_ui->showStatusText(buf); - } - g_settings->set("viewing_range", itos(range_new)); -} - - -void Game::decreaseViewRange() -{ - s16 range = g_settings->getS16("viewing_range"); - s16 range_new = range - 10; - - wchar_t buf[255]; - const wchar_t *str; - if (range_new < 20) { - range_new = 20; - str = wgettext("Viewing range is at minimum: %d"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new); - delete[] str; - m_game_ui->showStatusText(buf); - } else { - str = wgettext("Viewing range changed to %d"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new); - delete[] str; - m_game_ui->showStatusText(buf); - } - g_settings->set("viewing_range", itos(range_new)); -} - - -void Game::toggleFullViewRange() -{ - draw_control->range_all = !draw_control->range_all; - if (draw_control->range_all) - m_game_ui->showTranslatedStatusText("Enabled unlimited viewing range"); - else - m_game_ui->showTranslatedStatusText("Disabled unlimited viewing range"); -} - - -void Game::checkZoomEnabled() -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (player->getZoomFOV() < 0.001f) - m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod"); -} - - -void Game::updateCameraDirection(CameraOrientation *cam, float dtime) -{ - if ((device->isWindowActive() && device->isWindowFocused() - && !isMenuActive()) || random_input) { - -#ifndef __ANDROID__ - if (!random_input) { - // Mac OSX gets upset if this is set every frame - if (device->getCursorControl()->isVisible()) - device->getCursorControl()->setVisible(false); - } -#endif - - if (m_first_loop_after_window_activation) { - m_first_loop_after_window_activation = false; - - input->setMousePos(driver->getScreenSize().Width / 2, - driver->getScreenSize().Height / 2); - } else { - updateCameraOrientation(cam, dtime); - } - - } else { - -#ifndef ANDROID - // Mac OSX gets upset if this is set every frame - if (!device->getCursorControl()->isVisible()) - device->getCursorControl()->setVisible(true); -#endif - - m_first_loop_after_window_activation = true; - - } -} - -void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) -{ -#ifdef HAVE_TOUCHSCREENGUI - if (g_touchscreengui) { - cam->camera_yaw += g_touchscreengui->getYawChange(); - cam->camera_pitch = g_touchscreengui->getPitch(); - } else { -#endif - v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2); - v2s32 dist = input->getMousePos() - center; - - if (m_invert_mouse || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) { - dist.Y = -dist.Y; - } - - cam->camera_yaw -= dist.X * m_cache_mouse_sensitivity; - cam->camera_pitch += dist.Y * m_cache_mouse_sensitivity; - - if (dist.X != 0 || dist.Y != 0) - input->setMousePos(center.X, center.Y); -#ifdef HAVE_TOUCHSCREENGUI - } -#endif - - if (m_cache_enable_joysticks) { - f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime; - cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c; - cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c; - } - - cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5); -} - - -void Game::updatePlayerControl(const CameraOrientation &cam) -{ - //TimeTaker tt("update player control", NULL, PRECISION_NANO); - - // DO NOT use the isKeyDown method for the forward, backward, left, right - // buttons, as the code that uses the controls needs to be able to - // distinguish between the two in order to know when to use joysticks. - - PlayerControl control( - input->isKeyDown(KeyType::FORWARD), - input->isKeyDown(KeyType::BACKWARD), - input->isKeyDown(KeyType::LEFT), - input->isKeyDown(KeyType::RIGHT), - isKeyDown(KeyType::JUMP), - isKeyDown(KeyType::SPECIAL1), - isKeyDown(KeyType::SNEAK), - isKeyDown(KeyType::ZOOM), - input->getLeftState(), - input->getRightState(), - cam.camera_pitch, - cam.camera_yaw, - input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), - input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) - ); - - u32 keypress_bits = - ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | - ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | - ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | - ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) | - ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | - ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | - ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | - ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 - ); - -#ifdef ANDROID - /* For Android, simulate holding down AUX1 (fast move) if the user has - * the fast_move setting toggled on. If there is an aux1 key defined for - * Android then its meaning is inverted (i.e. holding aux1 means walk and - * not fast) - */ - if (m_cache_hold_aux1) { - control.aux1 = control.aux1 ^ true; - keypress_bits ^= ((u32)(1U << 5)); - } -#endif - - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - // autojump if set: simulate "jump" key - if (player->getAutojump()) { - control.jump = true; - keypress_bits |= 1U << 4; - } - - client->setPlayerControl(control); - player->keyPressed = keypress_bits; - - //tt.stop(); -} - - -inline void Game::step(f32 *dtime) -{ - bool can_be_and_is_paused = - (simple_singleplayer_mode && g_menumgr.pausesGame()); - - if (can_be_and_is_paused) { // This is for a singleplayer server - *dtime = 0; // No time passes - } else { - if (server) - server->step(*dtime); - - client->step(*dtime); - } -} - -const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = { - {&Game::handleClientEvent_None}, - {&Game::handleClientEvent_PlayerDamage}, - {&Game::handleClientEvent_PlayerForceMove}, - {&Game::handleClientEvent_Deathscreen}, - {&Game::handleClientEvent_ShowFormSpec}, - {&Game::handleClientEvent_ShowLocalFormSpec}, - {&Game::handleClientEvent_HandleParticleEvent}, - {&Game::handleClientEvent_HandleParticleEvent}, - {&Game::handleClientEvent_HandleParticleEvent}, - {&Game::handleClientEvent_HudAdd}, - {&Game::handleClientEvent_HudRemove}, - {&Game::handleClientEvent_HudChange}, - {&Game::handleClientEvent_SetSky}, - {&Game::handleClientEvent_OverrideDayNigthRatio}, - {&Game::handleClientEvent_CloudParams}, -}; - -void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam) -{ - FATAL_ERROR("ClientEvent type None received"); -} - -void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam) -{ - if (client->moddingEnabled()) { - client->getScript()->on_damage_taken(event->player_damage.amount); - } - - // Damage flash and hurt tilt are not used at death - if (client->getHP() > 0) { - runData.damage_flash += 95.0f + 3.2f * event->player_damage.amount; - runData.damage_flash = MYMIN(runData.damage_flash, 127.0f); - - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - player->hurt_tilt_timer = 1.5f; - player->hurt_tilt_strength = - rangelim(event->player_damage.amount / 4.0f, 1.0f, 4.0f); - } - - // Play damage sound - client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_DAMAGE)); -} - -void Game::handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam) -{ - cam->camera_yaw = event->player_force_move.yaw; - cam->camera_pitch = event->player_force_move.pitch; -} - -void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam) -{ - // If CSM enabled, deathscreen is handled by CSM code in - // builtin/client/init.lua - if (client->moddingEnabled()) - client->getScript()->on_death(); - else - showDeathFormspec(); - - /* Handle visualization */ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - runData.damage_flash = 0; - player->hurt_tilt_timer = 0; - player->hurt_tilt_strength = 0; -} - -void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam) -{ - if (event->show_formspec.formspec->empty()) { - if (current_formspec && (event->show_formspec.formname->empty() - || *(event->show_formspec.formname) == cur_formname)) { - current_formspec->quitMenu(); - } - } else { - FormspecFormSource *fs_src = - new FormspecFormSource(*(event->show_formspec.formspec)); - TextDestPlayerInventory *txt_dst = - new TextDestPlayerInventory(client, *(event->show_formspec.formname)); - - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); - cur_formname = *(event->show_formspec.formname); - } - - delete event->show_formspec.formspec; - delete event->show_formspec.formname; -} - -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(current_formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); - - delete event->show_formspec.formspec; - delete event->show_formspec.formname; -} - -void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event, - CameraOrientation *cam) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - client->getParticleManager()->handleParticleEvent(event, client, player); -} - -void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - auto &hud_server_to_client = client->getHUDTranslationMap(); - - u32 server_id = event->hudadd.server_id; - // ignore if we already have a HUD with that ID - auto i = hud_server_to_client.find(server_id); - if (i != hud_server_to_client.end()) { - delete event->hudadd.pos; - delete event->hudadd.name; - delete event->hudadd.scale; - delete event->hudadd.text; - delete event->hudadd.align; - delete event->hudadd.offset; - delete event->hudadd.world_pos; - delete event->hudadd.size; - return; - } - - HudElement *e = new HudElement; - e->type = (HudElementType)event->hudadd.type; - e->pos = *event->hudadd.pos; - e->name = *event->hudadd.name; - e->scale = *event->hudadd.scale; - e->text = *event->hudadd.text; - e->number = event->hudadd.number; - e->item = event->hudadd.item; - e->dir = event->hudadd.dir; - e->align = *event->hudadd.align; - e->offset = *event->hudadd.offset; - e->world_pos = *event->hudadd.world_pos; - e->size = *event->hudadd.size; - hud_server_to_client[server_id] = player->addHud(e); - - delete event->hudadd.pos; - delete event->hudadd.name; - delete event->hudadd.scale; - delete event->hudadd.text; - delete event->hudadd.align; - delete event->hudadd.offset; - delete event->hudadd.world_pos; - delete event->hudadd.size; -} - -void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - HudElement *e = player->removeHud(event->hudrm.id); - delete e; -} - -void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - u32 id = event->hudchange.id; - HudElement *e = player->getHud(id); - - if (e == NULL) { - delete event->hudchange.v3fdata; - delete event->hudchange.v2fdata; - delete event->hudchange.sdata; - delete event->hudchange.v2s32data; - return; - } - - switch (event->hudchange.stat) { - case HUD_STAT_POS: - e->pos = *event->hudchange.v2fdata; - break; - - case HUD_STAT_NAME: - e->name = *event->hudchange.sdata; - break; - - case HUD_STAT_SCALE: - e->scale = *event->hudchange.v2fdata; - break; - - case HUD_STAT_TEXT: - e->text = *event->hudchange.sdata; - break; - - case HUD_STAT_NUMBER: - e->number = event->hudchange.data; - break; - - case HUD_STAT_ITEM: - e->item = event->hudchange.data; - break; - - case HUD_STAT_DIR: - e->dir = event->hudchange.data; - break; - - case HUD_STAT_ALIGN: - e->align = *event->hudchange.v2fdata; - break; - - case HUD_STAT_OFFSET: - e->offset = *event->hudchange.v2fdata; - break; - - case HUD_STAT_WORLD_POS: - e->world_pos = *event->hudchange.v3fdata; - break; - - case HUD_STAT_SIZE: - e->size = *event->hudchange.v2s32data; - break; - } - - delete event->hudchange.v3fdata; - delete event->hudchange.v2fdata; - delete event->hudchange.sdata; - delete event->hudchange.v2s32data; -} - -void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) -{ - sky->setVisible(false); - // Whether clouds are visible in front of a custom skybox - sky->setCloudsEnabled(event->set_sky.clouds); - - if (skybox) { - skybox->remove(); - skybox = NULL; - } - - // Handle according to type - if (*event->set_sky.type == "regular") { - sky->setVisible(true); - sky->setCloudsEnabled(true); - } else if (*event->set_sky.type == "skybox" && - event->set_sky.params->size() == 6) { - sky->setFallbackBgColor(*event->set_sky.bgcolor); - skybox = RenderingEngine::get_scene_manager()->addSkyBoxSceneNode( - texture_src->getTextureForMesh((*event->set_sky.params)[0]), - texture_src->getTextureForMesh((*event->set_sky.params)[1]), - texture_src->getTextureForMesh((*event->set_sky.params)[2]), - texture_src->getTextureForMesh((*event->set_sky.params)[3]), - texture_src->getTextureForMesh((*event->set_sky.params)[4]), - texture_src->getTextureForMesh((*event->set_sky.params)[5])); - } - // Handle everything else as plain color - else { - if (*event->set_sky.type != "plain") - infostream << "Unknown sky type: " - << (*event->set_sky.type) << std::endl; - - sky->setFallbackBgColor(*event->set_sky.bgcolor); - } - - delete event->set_sky.bgcolor; - delete event->set_sky.type; - delete event->set_sky.params; -} - -void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, - CameraOrientation *cam) -{ - client->getEnv().setDayNightRatioOverride( - event->override_day_night_ratio.do_override, - event->override_day_night_ratio.ratio_f * 1000.0f); -} - -void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam) -{ - if (!clouds) - return; - - clouds->setDensity(event->cloud_params.density); - clouds->setColorBright(video::SColor(event->cloud_params.color_bright)); - clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient)); - clouds->setHeight(event->cloud_params.height); - clouds->setThickness(event->cloud_params.thickness); - clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y)); -} - -void Game::processClientEvents(CameraOrientation *cam) -{ - while (client->hasClientEvents()) { - std::unique_ptr<ClientEvent> event(client->getClientEvent()); - FATAL_ERROR_IF(event->type >= CLIENTEVENT_MAX, "Invalid clientevent type"); - const ClientEventHandler& evHandler = clientEventHandler[event->type]; - (this->*evHandler.handler)(event.get(), cam); - } -} - -void Game::updateChat(f32 dtime, const v2u32 &screensize) -{ - // Add chat log output for errors to be shown in chat - static LogOutputBuffer chat_log_error_buf(g_logger, LL_ERROR); - - // Get new messages from error log buffer - while (!chat_log_error_buf.empty()) { - std::wstring error_message = utf8_to_wide(chat_log_error_buf.get()); - if (!g_settings->getBool("disable_escape_sequences")) { - error_message.insert(0, L"\x1b(c@red)"); - error_message.append(L"\x1b(c@white)"); - } - chat_backend->addMessage(L"", error_message); - } - - // Get new messages from client - std::wstring message; - while (client->getChatMessage(message)) { - chat_backend->addUnparsedMessage(message); - } - - // Remove old messages - chat_backend->step(dtime); - - // Display all messages in a static text element - m_game_ui->setChatText(chat_backend->getRecentChat(), - chat_backend->getRecentBuffer().getLineCount()); -} - -void Game::updateCamera(u32 busy_time, f32 dtime) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - /* - For interaction purposes, get info about the held item - - What item is it? - - Is it a usable item? - - Can it point to liquids? - */ - ItemStack playeritem; - { - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && client->getPlayerItem() < mlist->getSize()) - playeritem = mlist->getItem(client->getPlayerItem()); - } - - if (playeritem.getDefinition(itemdef_manager).name.empty()) { // override the hand - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) - playeritem = hlist->getItem(0); - } - - - ToolCapabilities playeritem_toolcap = - playeritem.getToolCapabilities(itemdef_manager); - - 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; - - camera->toggleCameraMode(); - - playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); - playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); - } - - float full_punch_interval = playeritem_toolcap.full_punch_interval; - float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval; - - tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0); - camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio); - camera->step(dtime); - - v3f camera_position = camera->getPosition(); - v3f camera_direction = camera->getDirection(); - f32 camera_fov = camera->getFovMax(); - v3s16 camera_offset = camera->getOffset(); - - m_camera_offset_changed = (camera_offset != old_camera_offset); - - if (!m_flags.disable_camera_update) { - client->getEnv().getClientMap().updateCamera(camera_position, - camera_direction, camera_fov, camera_offset); - - if (m_camera_offset_changed) { - client->updateCameraOffset(camera_offset); - client->getEnv().updateCameraOffset(camera_offset); - - if (clouds) - clouds->updateCameraOffset(camera_offset); - } - } -} - - -void Game::updateSound(f32 dtime) -{ - // Update sound listener - v3s16 camera_offset = camera->getOffset(); - sound->updateListener(camera->getCameraNode()->getPosition() + intToFloat(camera_offset, BS), - v3f(0, 0, 0), // velocity - camera->getDirection(), - camera->getCameraNode()->getUpVector()); - - bool mute_sound = g_settings->getBool("mute_sound"); - if (mute_sound) { - sound->setListenerGain(0.0f); - } else { - // Check if volume is in the proper range, else fix it. - float old_volume = g_settings->getFloat("sound_volume"); - float new_volume = rangelim(old_volume, 0.0f, 1.0f); - sound->setListenerGain(new_volume); - - if (old_volume != new_volume) { - g_settings->setFloat("sound_volume", new_volume); - } - } - - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - // Tell the sound maker whether to make footstep sounds - soundmaker->makes_footstep_sound = player->makes_footstep_sound; - - // Update sound maker - if (player->makes_footstep_sound) - soundmaker->step(dtime); - - ClientMap &map = client->getEnv().getClientMap(); - MapNode n = map.getNodeNoEx(player->getFootstepNodePos()); - soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep; -} - - -void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - ItemStack playeritem; - { - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && client->getPlayerItem() < mlist->getSize()) - playeritem = mlist->getItem(client->getPlayerItem()); - } - - const ItemDefinition &playeritem_def = - playeritem.getDefinition(itemdef_manager); - InventoryList *hlist = local_inventory->getList("hand"); - const ItemDefinition &hand_def = - hlist ? hlist->getItem(0).getDefinition(itemdef_manager) : itemdef_manager->get(""); - - v3f player_position = player->getPosition(); - v3f camera_position = camera->getPosition(); - v3f camera_direction = camera->getDirection(); - v3s16 camera_offset = camera->getOffset(); - - - /* - Calculate what block is the crosshair pointing to - */ - - f32 d = playeritem_def.range; // max. distance - f32 d_hand = hand_def.range; - - if (d < 0 && d_hand >= 0) - d = d_hand; - else if (d < 0) - d = 4.0; - - core::line3d<f32> shootline; - - if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d<f32>(camera_position, - camera_position + camera_direction * BS * d); - } else { - // prevent player pointing anything in front-view - shootline = core::line3d<f32>(camera_position,camera_position); - } - -#ifdef HAVE_TOUCHSCREENGUI - - if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) { - shootline = g_touchscreengui->getShootline(); - // Scale shootline to the acual distance the player can reach - shootline.end = shootline.start - + shootline.getVector().normalize() * BS * d; - shootline.start += intToFloat(camera_offset, BS); - shootline.end += intToFloat(camera_offset, BS); - } - -#endif - - PointedThing pointed = updatePointedThing(shootline, - playeritem_def.liquids_pointable, - !runData.ldown_for_dig, - camera_offset); - - if (pointed != runData.pointed_old) { - infostream << "Pointing at " << pointed.dump() << std::endl; - hud->updateSelectionMesh(camera_offset); - } - - if (runData.digging_blocked && !input->getLeftState()) { - // allow digging again if button is not pressed - runData.digging_blocked = false; - } - - /* - Stop digging when - - releasing left mouse button - - pointing away from node - */ - if (runData.digging) { - if (input->getLeftReleased()) { - infostream << "Left button released" - << " (stopped digging)" << std::endl; - runData.digging = false; - } else if (pointed != runData.pointed_old) { - if (pointed.type == POINTEDTHING_NODE - && runData.pointed_old.type == POINTEDTHING_NODE - && pointed.node_undersurface - == runData.pointed_old.node_undersurface) { - // Still pointing to the same node, but a different face. - // Don't reset. - } else { - infostream << "Pointing away from node" - << " (stopped digging)" << std::endl; - runData.digging = false; - hud->updateSelectionMesh(camera_offset); - } - } - - if (!runData.digging) { - client->interact(1, runData.pointed_old); - client->setCrack(-1, v3s16(0, 0, 0)); - runData.dig_time = 0.0; - } - } else if (runData.dig_instantly && input->getLeftReleased()) { - // Remove e.g. torches faster when clicking instead of holding LMB - runData.nodig_delay_timer = 0; - runData.dig_instantly = false; - } - - if (!runData.digging && runData.ldown_for_dig && !input->getLeftState()) { - runData.ldown_for_dig = false; - } - - runData.left_punch = false; - - soundmaker->m_player_leftpunch_sound.name = ""; - - // Prepare for repeating, unless we're not supposed to - if (input->getRightState() && !g_settings->getBool("safe_dig_and_place")) - runData.repeat_rightclick_timer += dtime; - else - runData.repeat_rightclick_timer = 0; - - if (playeritem_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->moddingEnabled() - || !client->getScript()->on_item_use(playeritem, pointed))) - client->interact(4, pointed); - } else if (pointed.type == POINTEDTHING_NODE) { - ToolCapabilities playeritem_toolcap = - playeritem.getToolCapabilities(itemdef_manager); - if (playeritem.name.empty()) { - const ToolCapabilities *handToolcap = hlist - ? &hlist->getItem(0).getToolCapabilities(itemdef_manager) - : itemdef_manager->get("").tool_capabilities; - - if (handToolcap != nullptr) - playeritem_toolcap = *handToolcap; - } - handlePointingAtNode(pointed, playeritem_def, playeritem, - playeritem_toolcap, dtime); - } else if (pointed.type == POINTEDTHING_OBJECT) { - handlePointingAtObject(pointed, playeritem, player_position, show_debug); - } else if (input->getLeftState()) { - // When button is held down in air, show continuous animation - runData.left_punch = true; - } else if (input->getRightClicked()) { - handlePointingAtNothing(playeritem); - } - - runData.pointed_old = pointed; - - if (runData.left_punch || input->getLeftClicked()) - camera->setDigging(0); // left click animation - - input->resetLeftClicked(); - input->resetRightClicked(); - - input->resetLeftReleased(); - input->resetRightReleased(); -} - - -PointedThing Game::updatePointedThing( - const core::line3d<f32> &shootline, - bool liquids_pointable, - bool look_for_object, - const v3s16 &camera_offset) -{ - std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes(); - selectionboxes->clear(); - hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0)); - static thread_local const bool show_entity_selectionbox = g_settings->getBool( - "show_entity_selectionbox"); - - ClientEnvironment &env = client->getEnv(); - ClientMap &map = env.getClientMap(); - const NodeDefManager *nodedef = map.getNodeDefManager(); - - runData.selected_object = NULL; - - RaycastState s(shootline, look_for_object, liquids_pointable); - PointedThing result; - env.continueRaycast(&s, &result); - if (result.type == POINTEDTHING_OBJECT) { - runData.selected_object = client->getEnv().getActiveObject(result.object_id); - aabb3f selection_box; - if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() && - runData.selected_object->getSelectionBox(&selection_box)) { - v3f pos = runData.selected_object->getPosition(); - selectionboxes->push_back(aabb3f(selection_box)); - hud->setSelectionPos(pos, camera_offset); - } - } else if (result.type == POINTEDTHING_NODE) { - // Update selection boxes - MapNode n = map.getNodeNoEx(result.node_undersurface); - std::vector<aabb3f> boxes; - n.getSelectionBoxes(nodedef, &boxes, - n.getNeighbors(result.node_undersurface, &map)); - - f32 d = 0.002 * BS; - for (std::vector<aabb3f>::const_iterator i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge -= v3f(d, d, d); - box.MaxEdge += v3f(d, d, d); - selectionboxes->push_back(box); - } - hud->setSelectionPos(intToFloat(result.node_undersurface, BS), - camera_offset); - hud->setSelectedFaceNormal(v3f( - result.intersection_normal.X, - result.intersection_normal.Y, - result.intersection_normal.Z)); - } - - // Update selection mesh light level and vertex colors - if (!selectionboxes->empty()) { - v3f pf = hud->getSelectionPos(); - v3s16 p = floatToInt(pf, BS); - - // Get selection mesh light level - MapNode n = map.getNodeNoEx(p); - u16 node_light = getInteriorLight(n, -1, nodedef); - u16 light_level = node_light; - - for (const v3s16 &dir : g_6dirs) { - n = map.getNodeNoEx(p + dir); - node_light = getInteriorLight(n, -1, nodedef); - if (node_light > light_level) - light_level = node_light; - } - - u32 daynight_ratio = client->getEnv().getDayNightRatio(); - video::SColor c; - final_color_blend(&c, light_level, daynight_ratio); - - // Modify final color a bit with time - u32 timer = porting::getTimeMs() % 5000; - float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5)); - float sin_r = 0.08f * std::sin(timerf); - float sin_g = 0.08f * std::sin(timerf + irr::core::PI * 0.5f); - float sin_b = 0.08f * std::sin(timerf + irr::core::PI); - c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255)); - c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255)); - c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255)); - - // Set mesh final color - hud->setSelectionMeshColor(c); - } - return result; -} - - -void Game::handlePointingAtNothing(const ItemStack &playerItem) -{ - infostream << "Right Clicked in Air" << std::endl; - PointedThing fauxPointed; - fauxPointed.type = POINTEDTHING_NOTHING; - client->interact(5, fauxPointed); -} - - -void Game::handlePointingAtNode(const PointedThing &pointed, - const ItemDefinition &playeritem_def, const ItemStack &playeritem, - const ToolCapabilities &playeritem_toolcap, f32 dtime) -{ - v3s16 nodepos = pointed.node_undersurface; - v3s16 neighbourpos = pointed.node_abovesurface; - - /* - Check information text of node - */ - - ClientMap &map = client->getEnv().getClientMap(); - - if (runData.nodig_delay_timer <= 0.0 && input->getLeftState() - && !runData.digging_blocked - && client->checkPrivilege("interact")) { - handleDigging(pointed, nodepos, playeritem_toolcap, dtime); - } - - // This should be done after digging handling - NodeMetadata *meta = map.getNodeMetadata(nodepos); - - if (meta) { - m_game_ui->setInfoText(unescape_translate(utf8_to_wide( - meta->getString("infotext")))); - } else { - MapNode n = map.getNodeNoEx(nodepos); - - if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") { - m_game_ui->setInfoText(L"Unknown node: " + - utf8_to_wide(nodedef_manager->get(n).name)); - } - } - - if ((input->getRightClicked() || - runData.repeat_rightclick_timer >= m_repeat_right_click_time) && - client->checkPrivilege("interact")) { - runData.repeat_rightclick_timer = 0; - infostream << "Ground right-clicked" << std::endl; - - if (meta && !meta->getString("formspec").empty() && !random_input - && !isKeyDown(KeyType::SNEAK)) { - // Report right click to server - if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { - client->interact(3, pointed); - } - - infostream << "Launching custom inventory view" << std::endl; - - InventoryLocation inventoryloc; - inventoryloc.setNodeMeta(nodepos); - - NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( - &client->getEnv().getClientMap(), nodepos); - TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); - - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - cur_formname.clear(); - - current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); - } else { - // Report right click to server - - camera->setDigging(1); // right click animation (always shown for feedback) - - // If the wielded item has node placement prediction, - // make that happen - bool placed = nodePlacementPrediction(playeritem_def, playeritem, nodepos, - neighbourpos); - - if (placed) { - // Report to server - client->interact(3, pointed); - // Read the sound - soundmaker->m_player_rightpunch_sound = - playeritem_def.sound_place; - - if (client->moddingEnabled()) - client->getScript()->on_placenode(pointed, playeritem_def); - } else { - soundmaker->m_player_rightpunch_sound = - SimpleSoundSpec(); - - if (playeritem_def.node_placement_prediction.empty() || - nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { - client->interact(3, pointed); // Report to server - } else { - soundmaker->m_player_rightpunch_sound = - playeritem_def.sound_place_failed; - } - } - } - } -} - -bool Game::nodePlacementPrediction(const ItemDefinition &playeritem_def, - const ItemStack &playeritem, const v3s16 &nodepos, const v3s16 &neighbourpos) -{ - std::string prediction = playeritem_def.node_placement_prediction; - const NodeDefManager *nodedef = client->ndef(); - ClientMap &map = client->getEnv().getClientMap(); - MapNode node; - bool is_valid_position; - - node = map.getNodeNoEx(nodepos, &is_valid_position); - if (!is_valid_position) - return false; - - if (!prediction.empty() && !nodedef->get(node).rightclickable) { - verbosestream << "Node placement prediction for " - << playeritem_def.name << " is " - << prediction << std::endl; - v3s16 p = neighbourpos; - - // Place inside node itself if buildable_to - MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position); - if (is_valid_position) - { - if (nodedef->get(n_under).buildable_to) - p = nodepos; - else { - node = map.getNodeNoEx(p, &is_valid_position); - if (is_valid_position &&!nodedef->get(node).buildable_to) - return false; - } - } - - // Find id of predicted node - content_t id; - bool found = nodedef->getId(prediction, id); - - if (!found) { - errorstream << "Node placement prediction failed for " - << playeritem_def.name << " (places " - << prediction - << ") - Name not known" << std::endl; - return false; - } - - const ContentFeatures &predicted_f = nodedef->get(id); - - // Predict param2 for facedir and wallmounted nodes - u8 param2 = 0; - - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || - predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { - v3s16 dir = nodepos - neighbourpos; - - if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { - param2 = dir.Y < 0 ? 1 : 0; - } else if (abs(dir.X) > abs(dir.Z)) { - param2 = dir.X < 0 ? 3 : 2; - } else { - param2 = dir.Z < 0 ? 5 : 4; - } - } - - if (predicted_f.param_type_2 == CPT2_FACEDIR || - predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { - v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS); - - if (abs(dir.X) > abs(dir.Z)) { - param2 = dir.X < 0 ? 3 : 1; - } else { - param2 = dir.Z < 0 ? 2 : 0; - } - } - - assert(param2 <= 5); - - //Check attachment if node is in group attached_node - if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { - static v3s16 wallmounted_dirs[8] = { - v3s16(0, 1, 0), - v3s16(0, -1, 0), - v3s16(1, 0, 0), - v3s16(-1, 0, 0), - v3s16(0, 0, 1), - v3s16(0, 0, -1), - }; - v3s16 pp; - - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || - predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) - pp = p + wallmounted_dirs[param2]; - else - pp = p + v3s16(0, -1, 0); - - if (!nodedef->get(map.getNodeNoEx(pp)).walkable) - return false; - } - - // Apply color - if ((predicted_f.param_type_2 == CPT2_COLOR - || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR - || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) { - const std::string &indexstr = playeritem.metadata.getString( - "palette_index", 0); - if (!indexstr.empty()) { - s32 index = mystoi(indexstr); - if (predicted_f.param_type_2 == CPT2_COLOR) { - param2 = index; - } else if (predicted_f.param_type_2 - == CPT2_COLORED_WALLMOUNTED) { - // param2 = pure palette index + other - param2 = (index & 0xf8) | (param2 & 0x07); - } else if (predicted_f.param_type_2 - == CPT2_COLORED_FACEDIR) { - // param2 = pure palette index + other - param2 = (index & 0xe0) | (param2 & 0x1f); - } - } - } - - // Add node to client map - MapNode n(id, 0, param2); - - try { - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - // Dont place node when player would be inside new node - // NOTE: This is to be eventually implemented by a mod as client-side Lua - if (!nodedef->get(n).walkable || - g_settings->getBool("enable_build_where_you_stand") || - (client->checkPrivilege("noclip") && g_settings->getBool("noclip")) || - (nodedef->get(n).walkable && - neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) && - neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) { - - // This triggers the required mesh update too - client->addNode(p, n); - return true; - } - } catch (InvalidPositionException &e) { - errorstream << "Node placement prediction failed for " - << playeritem_def.name << " (places " - << prediction - << ") - Position not loaded" << std::endl; - } - } - - return false; -} - -void Game::handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, - const v3f &player_position, bool show_debug) -{ - std::wstring infotext = unescape_translate( - utf8_to_wide(runData.selected_object->infoText())); - - if (show_debug) { - if (!infotext.empty()) { - infotext += L"\n"; - } - infotext += utf8_to_wide(runData.selected_object->debugInfoText()); - } - - m_game_ui->setInfoText(infotext); - - if (input->getLeftState()) { - bool do_punch = false; - bool do_punch_damage = false; - - if (runData.object_hit_delay_timer <= 0.0) { - do_punch = true; - do_punch_damage = true; - runData.object_hit_delay_timer = object_hit_delay; - } - - if (input->getLeftClicked()) - do_punch = true; - - if (do_punch) { - infostream << "Left-clicked object" << std::endl; - runData.left_punch = true; - } - - if (do_punch_damage) { - // Report direct punch - v3f objpos = runData.selected_object->getPosition(); - v3f dir = (objpos - player_position).normalize(); - ItemStack item = playeritem; - if (playeritem.name.empty()) { - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) { - item = hlist->getItem(0); - } - } - - bool disable_send = runData.selected_object->directReportPunch( - dir, &item, runData.time_from_last_punch); - runData.time_from_last_punch = 0; - - if (!disable_send) - client->interact(0, pointed); - } - } else if (input->getRightClicked()) { - infostream << "Right-clicked object" << std::endl; - client->interact(3, pointed); // place - } -} - - -void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, - const ToolCapabilities &playeritem_toolcap, f32 dtime) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - ClientMap &map = client->getEnv().getClientMap(); - MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos); - - // NOTE: Similar piece of code exists on the server side for - // cheat detection. - // Get digging parameters - DigParams params = getDigParams(nodedef_manager->get(n).groups, - &playeritem_toolcap); - - // If can't dig, try hand - if (!params.diggable) { - InventoryList *hlist = local_inventory->getList("hand"); - const ToolCapabilities *tp = hlist - ? &hlist->getItem(0).getToolCapabilities(itemdef_manager) - : itemdef_manager->get("").tool_capabilities; - - if (tp) - params = getDigParams(nodedef_manager->get(n).groups, tp); - } - - if (!params.diggable) { - // I guess nobody will wait for this long - runData.dig_time_complete = 10000000.0; - } else { - runData.dig_time_complete = params.time; - - if (m_cache_enable_particles) { - const ContentFeatures &features = client->getNodeDefManager()->get(n); - client->getParticleManager()->addNodeParticle(client, - player, nodepos, n, features); - } - } - - if (!runData.digging) { - infostream << "Started digging" << std::endl; - runData.dig_instantly = runData.dig_time_complete == 0; - if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n)) - return; - client->interact(0, pointed); - runData.digging = true; - runData.ldown_for_dig = true; - } - - if (!runData.dig_instantly) { - runData.dig_index = (float)crack_animation_length - * runData.dig_time - / runData.dig_time_complete; - } else { - // This is for e.g. torches - runData.dig_index = crack_animation_length; - } - - SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig; - - if (sound_dig.exists() && params.diggable) { - if (sound_dig.name == "__group") { - if (!params.main_group.empty()) { - soundmaker->m_player_leftpunch_sound.gain = 0.5; - soundmaker->m_player_leftpunch_sound.name = - std::string("default_dig_") + - params.main_group; - } - } else { - soundmaker->m_player_leftpunch_sound = sound_dig; - } - } - - // Don't show cracks if not diggable - if (runData.dig_time_complete >= 100000.0) { - } else if (runData.dig_index < crack_animation_length) { - //TimeTaker timer("client.setTempMod"); - //infostream<<"dig_index="<<dig_index<<std::endl; - client->setCrack(runData.dig_index, nodepos); - } else { - infostream << "Digging completed" << std::endl; - client->setCrack(-1, v3s16(0, 0, 0)); - - runData.dig_time = 0; - runData.digging = false; - // we successfully dug, now block it from repeating if we want to be safe - if (g_settings->getBool("safe_dig_and_place")) - runData.digging_blocked = true; - - runData.nodig_delay_timer = - runData.dig_time_complete / (float)crack_animation_length; - - // We don't want a corresponding delay to very time consuming nodes - // and nodes without digging time (e.g. torches) get a fixed delay. - if (runData.nodig_delay_timer > 0.3) - runData.nodig_delay_timer = 0.3; - else if (runData.dig_instantly) - runData.nodig_delay_timer = 0.15; - - bool is_valid_position; - MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position); - if (is_valid_position) { - if (client->moddingEnabled() && - client->getScript()->on_dignode(nodepos, wasnode)) { - return; - } - - const ContentFeatures &f = client->ndef()->get(wasnode); - if (f.node_dig_prediction == "air") { - client->removeNode(nodepos); - } else if (!f.node_dig_prediction.empty()) { - content_t id; - bool found = client->ndef()->getId(f.node_dig_prediction, id); - if (found) - client->addNode(nodepos, id, true); - } - // implicit else: no prediction - } - - client->interact(2, pointed); - - if (m_cache_enable_particles) { - const ContentFeatures &features = - client->getNodeDefManager()->get(wasnode); - client->getParticleManager()->addDiggingParticles(client, - player, nodepos, wasnode, features); - } - - - // Send event to trigger sound - client->getEventManager()->put(new NodeDugEvent(nodepos, wasnode)); - } - - if (runData.dig_time_complete < 100000.0) { - runData.dig_time += dtime; - } else { - runData.dig_time = 0; - client->setCrack(-1, nodepos); - } - - camera->setDigging(0); // left click animation -} - - -void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, - const CameraOrientation &cam) -{ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - - /* - Fog range - */ - - if (draw_control->range_all) { - runData.fog_range = 100000 * BS; - } else { - runData.fog_range = draw_control->wanted_range * BS; - } - - /* - Calculate general brightness - */ - u32 daynight_ratio = client->getEnv().getDayNightRatio(); - float time_brightness = decode_light_f((float)daynight_ratio / 1000.0); - float direct_brightness; - bool sunlight_seen; - - if (m_cache_enable_noclip && m_cache_enable_free_move) { - direct_brightness = time_brightness; - sunlight_seen = true; - } else { - ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG); - float old_brightness = sky->getBrightness(); - direct_brightness = client->getEnv().getClientMap() - .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS), - daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen) - / 255.0; - } - - float time_of_day_smooth = runData.time_of_day_smooth; - float time_of_day = client->getEnv().getTimeOfDayF(); - - static const float maxsm = 0.05f; - static const float todsm = 0.05f; - - if (std::fabs(time_of_day - time_of_day_smooth) > maxsm && - std::fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm && - std::fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm) - time_of_day_smooth = time_of_day; - - if (time_of_day_smooth > 0.8 && time_of_day < 0.2) - time_of_day_smooth = time_of_day_smooth * (1.0 - todsm) - + (time_of_day + 1.0) * todsm; - else - time_of_day_smooth = time_of_day_smooth * (1.0 - todsm) - + time_of_day * todsm; - - runData.time_of_day_smooth = time_of_day_smooth; - - sky->update(time_of_day_smooth, time_brightness, direct_brightness, - sunlight_seen, camera->getCameraMode(), player->getYaw(), - player->getPitch()); - - /* - Update clouds - */ - if (clouds) { - if (sky->getCloudsVisible()) { - clouds->setVisible(true); - clouds->step(dtime); - // camera->getPosition is not enough for 3rd person views - v3f camera_node_position = camera->getCameraNode()->getPosition(); - v3s16 camera_offset = camera->getOffset(); - camera_node_position.X = camera_node_position.X + camera_offset.X * BS; - camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS; - camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS; - clouds->update(camera_node_position, - sky->getCloudColor()); - if (clouds->isCameraInsideCloud() && m_cache_enable_fog) { - // if inside clouds, and fog enabled, use that as sky - // color(s) - video::SColor clouds_dark = clouds->getColor() - .getInterpolated(video::SColor(255, 0, 0, 0), 0.9); - sky->overrideColors(clouds_dark, clouds->getColor()); - sky->setBodiesVisible(false); - runData.fog_range = std::fmin(runData.fog_range * 0.5f, 32.0f * BS); - // do not draw clouds after all - clouds->setVisible(false); - } - } else { - clouds->setVisible(false); - } - } - - /* - Update particles - */ - client->getParticleManager()->step(dtime); - - /* - Fog - */ - - if (m_cache_enable_fog) { - driver->setFog( - sky->getBgColor(), - video::EFT_FOG_LINEAR, - runData.fog_range * m_cache_fog_start, - runData.fog_range * 1.0, - 0.01, - false, // pixel fog - true // range fog - ); - } else { - driver->setFog( - sky->getBgColor(), - video::EFT_FOG_LINEAR, - 100000 * BS, - 110000 * BS, - 0.01f, - false, // pixel fog - false // range fog - ); - } - - /* - Get chat messages from client - */ - - v2u32 screensize = driver->getScreenSize(); - - updateChat(dtime, screensize); - - /* - Inventory - */ - - if (client->getPlayerItem() != runData.new_playeritem) - client->selectPlayerItem(runData.new_playeritem); - - // Update local inventory if it has changed - if (client->getLocalInventoryUpdated()) { - //infostream<<"Updating local inventory"<<std::endl; - client->getLocalInventory(*local_inventory); - runData.update_wielded_item_trigger = true; - } - - if (runData.update_wielded_item_trigger) { - // Update wielded tool - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && (client->getPlayerItem() < mlist->getSize())) { - ItemStack item = mlist->getItem(client->getPlayerItem()); - if (item.getDefinition(itemdef_manager).name.empty()) { // override the hand - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) - item = hlist->getItem(0); - } - camera->wield(item); - } - - runData.update_wielded_item_trigger = false; - } - - /* - Update block draw list every 200ms or when camera direction has - changed much - */ - runData.update_draw_list_timer += dtime; - - v3f camera_direction = camera->getDirection(); - if (runData.update_draw_list_timer >= 0.2 - || runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2 - || m_camera_offset_changed) { - runData.update_draw_list_timer = 0; - client->getEnv().getClientMap().updateDrawList(); - runData.update_draw_list_last_cam_dir = camera_direction; - } - - m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, dtime); - - /* - make sure menu is on top - 1. Delete formspec menu reference if menu was removed - 2. Else, make sure formspec menu is on top - */ - if (current_formspec) { - if (current_formspec->getReferenceCount() == 1) { - current_formspec->drop(); - current_formspec = NULL; - } else if (isMenuActive()) { - guiroot->bringToFront(current_formspec); - } - } - - /* - Drawing begins - */ - const video::SColor &skycolor = sky->getSkyColor(); - - TimeTaker tt_draw("mainloop: draw"); - driver->beginScene(true, true, skycolor); - - bool draw_wield_tool = (m_game_ui->m_flags.show_hud && - (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) && - (camera->getCameraMode() == CAMERA_MODE_FIRST)); - bool draw_crosshair = ( - (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) && - (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT)); -#ifdef HAVE_TOUCHSCREENGUI - try { - draw_crosshair = !g_settings->getBool("touchtarget"); - } catch (SettingNotFoundException) { - } -#endif - RenderingEngine::draw_scene(skycolor, m_game_ui->m_flags.show_hud, - m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair); - - /* - Profiler graph - */ - if (m_game_ui->m_flags.show_profiler_graph) - graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont()); - - /* - 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); - - runData.damage_flash -= 384.0f * dtime; - } - - /* - Damage camera tilt - */ - if (player->hurt_tilt_timer > 0.0f) { - player->hurt_tilt_timer -= dtime * 6.0f; - - if (player->hurt_tilt_timer < 0.0f) - player->hurt_tilt_strength = 0.0f; - } - - /* - Update minimap pos and rotation - */ - if (mapper && m_game_ui->m_flags.show_minimap && m_game_ui->m_flags.show_hud) { - mapper->setPos(floatToInt(player->getPosition(), BS)); - mapper->setAngle(player->getYaw()); - } - - /* - End scene - */ - driver->endScene(); - - stats->drawtime = tt_draw.stop(true); - g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f); -} - -/* Log times and stuff for visualization */ -inline void Game::updateProfilerGraphs(ProfilerGraph *graph) -{ - Profiler::GraphValues values; - g_profiler->graphGet(values); - graph->put(values); -} - - - -/**************************************************************************** - Misc - ****************************************************************************/ - -/* On some computers framerate doesn't seem to be automatically limited - */ -inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) -{ - // not using getRealTime is necessary for wine - device->getTimer()->tick(); // Maker sure device time is up-to-date - u32 time = device->getTimer()->getTime(); - u32 last_time = fps_timings->last_time; - - if (time > last_time) // Make sure time hasn't overflowed - fps_timings->busy_time = time - last_time; - else - fps_timings->busy_time = 0; - - u32 frametime_min = 1000 / (g_menumgr.pausesGame() - ? g_settings->getFloat("pause_fps_max") - : g_settings->getFloat("fps_max")); - - if (fps_timings->busy_time < frametime_min) { - fps_timings->sleep_time = frametime_min - fps_timings->busy_time; - device->sleep(fps_timings->sleep_time); - } else { - fps_timings->sleep_time = 0; - } - - /* Get the new value of the device timer. Note that device->sleep() may - * not sleep for the entire requested time as sleep may be interrupted and - * therefore it is arguably more accurate to get the new time from the - * device rather than calculating it by adding sleep_time to time. - */ - - device->getTimer()->tick(); // Update device timer - time = device->getTimer()->getTime(); - - if (time > last_time) // Make sure last_time hasn't overflowed - *dtime = (time - last_time) / 1000.0; - else - *dtime = 0; - - fps_timings->last_time = time; -} - -void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_clouds) -{ - const wchar_t *wmsg = wgettext(msg); - RenderingEngine::draw_load_screen(wmsg, guienv, texture_src, dtime, percent, - draw_clouds); - delete[] wmsg; -} - -void Game::settingChangedCallback(const std::string &setting_name, void *data) -{ - ((Game *)data)->readSettings(); -} - -void Game::readSettings() -{ - m_cache_doubletap_jump = g_settings->getBool("doubletap_jump"); - m_cache_enable_clouds = g_settings->getBool("enable_clouds"); - m_cache_enable_joysticks = g_settings->getBool("enable_joysticks"); - m_cache_enable_particles = g_settings->getBool("enable_particles"); - m_cache_enable_fog = g_settings->getBool("enable_fog"); - m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity"); - m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity"); - m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); - - m_cache_enable_noclip = g_settings->getBool("noclip"); - m_cache_enable_free_move = g_settings->getBool("free_move"); - - m_cache_fog_start = g_settings->getFloat("fog_start"); - - m_cache_cam_smoothing = 0; - if (g_settings->getBool("cinematic")) - m_cache_cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing"); - else - m_cache_cam_smoothing = 1 - g_settings->getFloat("camera_smoothing"); - - m_cache_fog_start = rangelim(m_cache_fog_start, 0.0f, 0.99f); - m_cache_cam_smoothing = rangelim(m_cache_cam_smoothing, 0.01f, 1.0f); - m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0); - - m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus"); -} - -/****************************************************************************/ -/**************************************************************************** - Shutdown / cleanup - ****************************************************************************/ -/****************************************************************************/ - -void Game::extendedResourceCleanup() -{ - // Extended resource accounting - infostream << "Irrlicht resources after cleanup:" << std::endl; - infostream << "\tRemaining meshes : " - << RenderingEngine::get_mesh_cache()->getMeshCount() << std::endl; - infostream << "\tRemaining textures : " - << driver->getTextureCount() << std::endl; - - for (unsigned int i = 0; i < driver->getTextureCount(); i++) { - irr::video::ITexture *texture = driver->getTextureByIndex(i); - infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str() - << std::endl; - } - - clearTextureNameCache(); - infostream << "\tRemaining materials: " - << driver-> getMaterialRendererCount() - << " (note: irrlicht doesn't support removing renderers)" << std::endl; -} - -void Game::showDeathFormspec() -{ - static std::string formspec = - std::string(FORMSPEC_VERSION_STRING) + - SIZE_TAG - "bgcolor[#320000b4;true]" - "label[4.85,1.35;" + gettext("You died") + "]" - "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]" - ; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler * - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(formspec); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - current_formspec->setFocus("btn_respawn"); -} - -#define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name()) -void Game::showPauseMenu() -{ -#ifdef __ANDROID__ - static const std::string control_text = strgettext("Default Controls:\n" - "No menu visible:\n" - "- single tap: button activate\n" - "- double tap: place/use\n" - "- slide finger: look around\n" - "Menu/Inventory visible:\n" - "- double tap (outside):\n" - " -->close\n" - "- touch stack, touch slot:\n" - " --> move stack\n" - "- touch&drag, tap 2nd finger\n" - " --> place single item to slot\n" - ); -#else - static const std::string control_text_template = strgettext("Controls:\n" - "- %s: move forwards\n" - "- %s: move backwards\n" - "- %s: move left\n" - "- %s: move right\n" - "- %s: jump/climb\n" - "- %s: sneak/go down\n" - "- %s: drop item\n" - "- %s: inventory\n" - "- Mouse: turn/look\n" - "- Mouse left: dig/punch\n" - "- Mouse right: place/use\n" - "- Mouse wheel: select item\n" - "- %s: chat\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_sneak), - GET_KEY_NAME(keymap_drop), - GET_KEY_NAME(keymap_inventory), - GET_KEY_NAME(keymap_chat) - ); - - std::string control_text = std::string(control_text_buf); - str_formspec_escape(control_text); -#endif - - float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; - std::ostringstream os; - - os << FORMSPEC_VERSION_STRING << SIZE_TAG - << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" - << strgettext("Continue") << "]"; - - if (!simple_singleplayer_mode) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;" - << strgettext("Change Password") << "]"; - } else { - os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]"; - } - -#ifndef __ANDROID__ - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << strgettext("Sound Volume") << "]"; - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << strgettext("Change Keys") << "]"; -#endif - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" - << strgettext("Exit to Menu") << "]"; - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" - << strgettext("Exit to OS") << "]" - << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]" - << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n" - << "\n" - << strgettext("Game info:") << "\n"; - const std::string &address = client->getAddressName(); - static const std::string mode = strgettext("- Mode: "); - if (!simple_singleplayer_mode) { - Address serverAddress = client->getServerAddress(); - if (!address.empty()) { - os << mode << strgettext("Remote server") << "\n" - << strgettext("- Address: ") << address; - } else { - os << mode << strgettext("Hosting server"); - } - os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n"; - } else { - os << mode << strgettext("Singleplayer") << "\n"; - } - if (simple_singleplayer_mode || address.empty()) { - static const std::string on = strgettext("On"); - static const std::string off = strgettext("Off"); - const std::string &damage = g_settings->getBool("enable_damage") ? on : off; - const std::string &creative = g_settings->getBool("creative_mode") ? on : off; - const std::string &announced = g_settings->getBool("server_announce") ? on : off; - os << strgettext("- Damage: ") << damage << "\n" - << strgettext("- Creative Mode: ") << creative << "\n"; - if (!simple_singleplayer_mode) { - const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off; - os << strgettext("- PvP: ") << pvp << "\n" - << strgettext("- Public: ") << announced << "\n"; - std::string server_name = g_settings->get("server_name"); - str_formspec_escape(server_name); - if (announced == on && !server_name.empty()) - os << strgettext("- Server Name: ") << server_name; - - } - } - os << ";]"; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler * - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(os.str()); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); - current_formspec->setFocus("btn_continue"); - current_formspec->doPause = true; -} - -/****************************************************************************/ -/**************************************************************************** - extern function for launching the game - ****************************************************************************/ -/****************************************************************************/ - -void the_game(bool *kill, - bool random_input, - InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - const std::string &address, // If empty local server is created - u16 port, - - std::string &error_message, - ChatBackend &chat_backend, - bool *reconnect_requested, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode) -{ - 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 - */ - std::string server_address = address; - - try { - - if (game.startup(kill, random_input, input, map_dir, - playername, password, &server_address, port, error_message, - reconnect_requested, &chat_backend, gamespec, - simple_singleplayer_mode)) { - game.run(); - game.shutdown(); - } - - } catch (SerializationError &e) { - error_message = std::string("A serialization error occurred:\n") - + e.what() + "\n\nThe server is probably " - " running a different version of " PROJECT_NAME_C "."; - errorstream << error_message << std::endl; - } catch (ServerError &e) { - error_message = e.what(); - errorstream << "ServerError: " << error_message << std::endl; - } catch (ModError &e) { - error_message = e.what() + strgettext("\nCheck debug.txt for details."); - errorstream << "ModError: " << error_message << std::endl; - } -} |