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