From: Elias Fleckenstein Date: Tue, 17 May 2022 20:12:00 +0000 (+0200) Subject: Merge branch 'master' of https://github.com/minetest/minetest X-Git-Tag: 2022.05~5 X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=21df26984da91143c15587f5a03c98d68c3adc4e;p=dragonfireclient.git Merge branch 'master' of https://github.com/minetest/minetest --- 21df26984da91143c15587f5a03c98d68c3adc4e diff --cc CMakeLists.txt index deb327c5b,d8dd85af6..018e233da --- a/CMakeLists.txt +++ b/CMakeLists.txt @@@ -9,20 -9,21 +9,21 @@@ endif( # This can be read from ${PROJECT_NAME} after project() is called project(minetest) -set(PROJECT_NAME_CAPITALIZED "Minetest") +set(PROJECT_NAME_CAPITALIZED "Dragonfire") - set(CMAKE_CXX_STANDARD 11) - set(GCC_MINIMUM_VERSION "4.8") - set(CLANG_MINIMUM_VERSION "3.4") + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD_REQUIRED TRUE) + set(GCC_MINIMUM_VERSION "5.1") + set(CLANG_MINIMUM_VERSION "3.5") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 5) - set(VERSION_MINOR 5) + set(VERSION_MINOR 6) set(VERSION_PATCH 0) -set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") +set(VERSION_EXTRA "dragonfire" CACHE STRING "Stuff to append to version string") # Change to false for releases -set(DEVELOPMENT_BUILD TRUE) +set(DEVELOPMENT_BUILD FALSE) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") if(VERSION_EXTRA) diff --cc builtin/async/game.lua index 000000000,8cb9720b6..e3119d8cb mode 000000,100644..100644 --- a/builtin/async/game.lua +++ b/builtin/async/game.lua @@@ -1,0 -1,45 +1,46 @@@ + core.log("info", "Initializing asynchronous environment (game)") + + local function pack2(...) + return {n=select('#', ...), ...} + end + + -- Entrypoint to run async jobs, called by C++ + function core.job_processor(func, params) + local retval = pack2(func(unpack(params, 1, params.n))) + + return retval + end + + -- Import a bunch of individual files from builtin/game/ + local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM ++local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM + + dofile(gamepath .. "constants.lua") + dofile(gamepath .. "item_s.lua") + dofile(gamepath .. "misc_s.lua") + dofile(gamepath .. "features.lua") -dofile(gamepath .. "voxelarea.lua") ++dofile(commonpath .. "voxelarea.lua") + + -- Transfer of globals + do + local all = assert(core.transferred_globals) + core.transferred_globals = nil + + -- reassemble other tables + all.registered_nodes = {} + all.registered_craftitems = {} + all.registered_tools = {} + for k, v in pairs(all.registered_items) do + if v.type == "node" then + all.registered_nodes[k] = v + elseif v.type == "craftitem" then + all.registered_craftitems[k] = v + elseif v.type == "tool" then + all.registered_tools[k] = v + end + end + + for k, v in pairs(all) do + core[k] = v + end + end diff --cc builtin/common/chatcommands.lua index 62f9eacd0,7c3da0601..817f1f526 --- a/builtin/common/chatcommands.lua +++ b/builtin/common/chatcommands.lua @@@ -33,67 -69,30 +69,75 @@@ function core.override_chatcommand(name core.registered_chatcommands[name] = chatcommand end +if INIT == "client" then + function core.register_list_command(command, desc, setting) + local def = {} + def.description = desc + def.params = "del | add | list" + function def.func(param) + local list = (minetest.settings:get(setting) or ""):split(",") + if param == "list" then + return true, table.concat(list, ", ") + else + local sparam = param:split(" ") + local cmd = sparam[1] + local item = sparam[2] + if cmd == "del" then + if not item then + return false, "Missing item." + end + local i = table.indexof(list, item) + if i == -1 then + return false, item .. " is not on the list." + else + table.remove(list, i) + core.settings:set(setting, table.concat(list, ",")) + return true, "Removed " .. item .. " from the list." + end + elseif cmd == "add" then + if not item then + return false, "Missing item." + end + local i = table.indexof(list, item) + if i ~= -1 then + return false, item .. " is already on the list." + else + table.insert(list, item) + core.settings:set(setting, table.concat(list, ",")) + return true, "Added " .. item .. " to the list." + end + end + end + return false, "Invalid usage. (See .help " .. command .. ")" + end + core.register_chatcommand(command, def) + end +end + + local function format_help_line(cmd, def) + local cmd_marker = INIT == "client" and "." or "/" + local msg = core.colorize("#00ffff", cmd_marker .. cmd) + if def.params and def.params ~= "" then + msg = msg .. " " .. def.params + end + if def.description and def.description ~= "" then + msg = msg .. ": " .. def.description + end + return msg + end + local function do_help_cmd(name, param) - local function format_help_line(cmd, def) - local cmd_marker = "/" - if INIT == "client" then - cmd_marker = "." - end - local msg = core.colorize("#00ffff", cmd_marker .. cmd) - if def.params and def.params ~= "" then - msg = msg .. " " .. def.params - end - if def.description and def.description ~= "" then - msg = msg .. ": " .. def.description - end - return msg + local opts, args = getopts("help", param) + if not opts then + return false, args + end + if #args > 1 then + return false, S("Too many arguments, try using just /help ") end - if param == "" then + local use_gui = INIT ~= "client" and core.get_player_by_name(name) + use_gui = use_gui and not opts:find("t") + + if #args == 0 and not use_gui then local cmds = {} for cmd, def in pairs(core.registered_chatcommands) do if INIT == "client" or core.check_player_privs(name, def.privs) then diff --cc builtin/game/init.lua index 9a9966d7e,68d6a10f8..b9ab97b58 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@@ -16,9 -17,9 +17,10 @@@ if core.settings:get_bool("profiler.loa end dofile(commonpath .. "after.lua") +dofile(commonpath .. "voxelarea.lua") dofile(gamepath .. "item_entity.lua") dofile(gamepath .. "deprecated.lua") + dofile(gamepath .. "misc_s.lua") dofile(gamepath .. "misc.lua") dofile(gamepath .. "privileges.lua") dofile(gamepath .. "auth.lua") diff --cc builtin/mainmenu/dlg_settings_advanced.lua index 38a658969,320db7e40..d6f485cf7 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@@ -404,36 -405,6 +405,36 @@@ local function parse_config_file(read_a file:close() end end + + -- Parse clientmods + local clientmods_category_initialized = false + local clientmods = {} - get_mods(core.get_clientmodpath(), clientmods) ++ get_mods(core.get_clientmodpath(), "clientmods", clientmods) + for _, clientmod in ipairs(clientmods) do + local path = clientmod.path .. DIR_DELIM .. FILENAME + local file = io.open(path, "r") + if file then + if not clientmods_category_initialized then + fgettext_ne("Clientmods") -- not used, but needed for xgettext + table.insert(settings, { + name = "Clientmods", + level = 0, + type = "category", + }) + clientmods_category_initialized = true + end + + table.insert(settings, { + name = clientmod.name, + level = 1, + type = "category", + }) + + parse_single_file(file, path, read_all, settings, 2, false) + + file:close() + end + end end return settings diff --cc builtin/mainmenu/pkgmgr.lua index 58a4ed8c1,334fcf5f8..072c41f0c --- a/builtin/mainmenu/pkgmgr.lua +++ b/builtin/mainmenu/pkgmgr.lua @@@ -657,72 -663,7 +663,54 @@@ function pkgmgr.install_dir(type, path return targetpath, nil end - -------------------------------------------------------------------------------- - function pkgmgr.install(type, modfilename, basename, dest) - local archive_info = pkgmgr.identify_filetype(modfilename) - local path = pkgmgr.extract(archive_info) - - if path == nil then - return nil, - fgettext("Install: file: \"$1\"", archive_info.name) .. "\n" .. - fgettext("Install: Unsupported file type \"$1\" or broken archive", - archive_info.type) - end - - local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest) - core.delete_dir(path) - return targetpath, msg - end - -------------------------------------------------------------------------------- +function pkgmgr.prepareclientmodlist(data) + local retval = {} + + local clientmods = {} + + --read clientmods + local modpath = core.get_clientmodpath() + - if modpath ~= nil and - modpath ~= "" then - get_mods(modpath,clientmods) ++ if modpath ~= nil and modpath ~= "" then ++ get_mods(modpath, "clientmods", clientmods) + end + + for i=1,#clientmods,1 do + clientmods[i].type = "mod" + clientmods[i].loc = "global" + clientmods[i].is_clientside = true + retval[#retval + 1] = clientmods[i] + end + + --read mods configuration + local filename = modpath .. + DIR_DELIM .. "mods.conf" + + local conffile = Settings(filename) + + for key,value in pairs(conffile:to_table()) do + if key:sub(1, 9) == "load_mod_" then + key = key:sub(10) + local element = nil + for i=1,#retval,1 do + if retval[i].name == key and + not retval[i].is_modpack then + element = retval[i] + break + end + end + if element ~= nil then + element.enabled = value ~= "false" and value ~= "nil" and value + else + core.log("info", "Clientmod: " .. key .. " " .. dump(value) .. " but not found") + end + end + end + + return retval +end + function pkgmgr.preparemodlist(data) local retval = {} @@@ -865,51 -820,8 +867,12 @@@ function pkgmgr.refresh_globals( pkgmgr.comparemod, is_equal, nil, {}) pkgmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list) pkgmgr.global_mods:set_sortmode("alphabetic") + pkgmgr.clientmods = filterlist.create(pkgmgr.prepareclientmodlist, + pkgmgr.comparemod, is_equal, nil, {}) + pkgmgr.clientmods:add_sort_mechanism("alphabetic", sort_mod_list) + pkgmgr.clientmods:set_sortmode("alphabetic") end - -------------------------------------------------------------------------------- - function pkgmgr.identify_filetype(name) - - if name:sub(-3):lower() == "zip" then - return { - name = name, - type = "zip" - } - end - - if name:sub(-6):lower() == "tar.gz" or - name:sub(-3):lower() == "tgz"then - return { - name = name, - type = "tgz" - } - end - - if name:sub(-6):lower() == "tar.bz2" then - return { - name = name, - type = "tbz" - } - end - - if name:sub(-2):lower() == "7z" then - return { - name = name, - type = "7z" - } - end - - return { - name = name, - type = "ukn" - } - end - - -------------------------------------------------------------------------------- function pkgmgr.find_by_gameid(gameid) for i=1,#pkgmgr.games,1 do diff --cc builtin/mainmenu/tab_content.lua index 1bffeeb22,5e14d1902..a366d4ab4 --- a/builtin/mainmenu/tab_content.lua +++ b/builtin/mainmenu/tab_content.lua @@@ -214,9 -164,9 +224,12 @@@ local function handle_doubleclick(pkg, core.settings:set("texture_path", pkg.path) end packages = nil + + mm_game_theme.init() + mm_game_theme.reset() + elseif pkg.is_clientside then + pkgmgr.enable_mod({data = {list = packages, selected_mod = pkg_name}}) + packages = nil end end diff --cc builtin/settingtypes.txt index e023aeab7,ff69d9741..2e0bb560a --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@@ -2266,114 -2275,3 +2287,114 @@@ contentdb_flag_blacklist (ContentDB Fla # Maximum number of concurrent downloads. Downloads exceeding this limit will be queued. # This should be lower than curl_parallel_limit. contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 + +[Cheat Menu] + +# Font to use for cheat menu - cheat_menu_font (MenuFont) enum FM_Mono FM_Standard,FM_Mono,FM_Fallback,FM_Simple,FM_SimpleMono,FM_MaxMode,FM_Unspecified ++cheat_menu_font (MenuFont) enum FM_Mono FM_Standard,FM_Mono,FM_Fallback,FM_MaxMode,FM_Unspecified + +# (RGB value) +cheat_menu_bg_color (Cell background color) v3f 255, 145, 88 + +cheat_menu_bg_color_alpha (Cell background color alpha) int 192 + +# (RGB value) +cheat_menu_active_bg_color (Active cell background color) v3f 255, 87, 53 + +cheat_menu_active_bg_color_alpha (Active cell background color alpha) int 192 + +# (RGB value) +cheat_menu_font_color (Font color) v3f 0, 0, 0 + +cheat_menu_font_color_alpha (Font color alpha) int 255 + +# (RGB value) +cheat_menu_selected_font_color (Selected font color) v3f 255, 252, 88 + +cheat_menu_selected_font_color_alpha (Selected font color alpha) int 255 + +cheat_menu_head_height (Head height) int 50 + +cheat_menu_entry_height (Entry height) int 40 + +cheat_menu_entry_width (Entry width) int 200 + +[Cheats] + +fullbright (Fullbright) bool false + +xray (XRay) bool false + +xray_nodes (XRay Nodes) string default:stone,mcl_core:stone + +priv_bypass (PrivBypass) bool true + +fastdig (FastDig) bool false + +fastplace (FastPlace) bool false + +autodig (AutoDig) bool false + +autoplace (AutoPlace) bool false + +prevent_natural_damage (NoFallDamage) bool true + +freecam (Freecam) bool false + +no_hurt_cam (NoHurtCam) bool false + +hud_flags_bypass (HUDBypass) bool true + +antiknockback (AntiKnockback) bool false + +entity_speed (EntitySpeed) bool false + +jesus (Jesus) bool false + +instant_break (InstantBreak) bool false + +no_night (BrightNight) bool false + +coords (Coords) bool false + +point_liquids (PointLiquids) bool false + +spamclick (FastHit) bool false + +no_force_rotate (NoForceRotate) bool false + +no_slow (NoSlow) bool false + +cheat_hud (CheatHUD) bool true + +node_esp_nodes (NodeESP Nodes) string + +jetpack (JetPack) bool false + +autohit (AutoHit) bool false + +antislip (AntiSlip) bool false + +enable_entity_tracers (EntityTracers) bool false + +enable_entity_esp (EntityESP) bool false + +enable_player_tracers (PlayerTracers) bool false + +enable_player_esp (PlayerESP) bool false + +enable_node_esp (NodeESP) bool false + +enable_node_tracers (NodeTracers) bool false + +entity_esp_color (EntityESP Color) v3f 255, 255, 255 + +player_esp_color (PlayerESP Color) v3f 0, 255, 0 + +tool_range (Additional Tool Range) int 2 + +reach (Reach) bool false + +airjump (AirJump) bool false + +spider (Spider) bool false diff --cc src/client/camera.cpp index 52bedcba9,d1f19adb3..0c387262e --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@@ -35,8 -36,8 +36,9 @@@ with this program; if not, write to th #include "util/numeric.h" #include "constants.h" #include "fontengine.h" +#include "guiscalingfilter.h" #include "script/scripting_client.h" + #include "gettext.h" #define CAMERA_OFFSET_STEP 200 #define WIELDMESH_OFFSET_X 55.0f @@@ -342,15 -319,18 +320,18 @@@ void Camera::update(LocalPlayer* player // mods expect the player head to be at the parent's position // plus eye height. if (player->getParent()) - player_position = player->getParent()->getPosition(); + player_position = player->getParent()->getPosition() + v3f(0, g_settings->getBool("float_above_parent") ? BS : 0, 0); - // Smooth the camera movement when the player instantly moves upward due to stepheight. - // To smooth the 'not touching_ground' stepheight, smoothing is necessary when jumping - // or swimming (for when moving from liquid to land). - // Disable smoothing if climbing or flying, to avoid upwards offset of player model - // when seen in 3rd person view. - bool flying = (g_settings->getBool("free_move") && m_client->checkLocalPrivilege("fly")) || g_settings->getBool("freecam"); - if (player_position.Y > old_player_position.Y && !player->is_climbing && !flying) { + // Smooth the camera movement after the player instantly moves upward due to stepheight. + // The smoothing usually continues until the camera position reaches the player position. + float player_stepheight = player->getCAO() ? player->getCAO()->getStepHeight() : HUGE_VALF; + float upward_movement = player_position.Y - old_player_position.Y; + if (upward_movement < 0.01f || upward_movement > player_stepheight) { + m_stepheight_smooth_active = false; + } else if (player->touching_ground) { + m_stepheight_smooth_active = true; + } + if (m_stepheight_smooth_active) { f32 oldy = old_player_position.Y; f32 newy = player_position.Y; f32 t = std::exp(-23 * frametime); diff --cc src/client/client.cpp index 3c4ea5f95,cb556c1ce..4e4bb8a97 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@@ -927,11 -957,11 +960,11 @@@ void Client::Send(NetworkPacket* pkt // Will fill up 12 + 12 + 4 + 4 + 4 bytes void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt) { - v3f pf = myplayer->getPosition() * 100; - v3f sf = myplayer->getSpeed() * 100; + v3f pf = myplayer->getLegitPosition() * 100; + v3f sf = myplayer->getSendSpeed() * 100; s32 pitch = myplayer->getPitch() * 100; s32 yaw = myplayer->getYaw() * 100; - u32 keyPressed = myplayer->keyPressed; + u32 keyPressed = myplayer->control.getKeysPressed(); // scaled by 80, so that pi can fit into a u8 u8 fov = clientMap->getCameraFov() * 80; u8 wanted_range = MYMIN(255, @@@ -1297,21 -1323,27 +1326,27 @@@ void Client::sendPlayerPos(v3f pos if (m_activeobjects_received && player->isDead()) return; + ClientMap &map = m_env.getClientMap(); + u8 camera_fov = map.getCameraFov(); + u8 wanted_range = map.getControl().wanted_range; + + u32 keyPressed = player->control.getKeysPressed(); + if ( - player->last_position == player->getPosition() && - player->last_speed == player->getSpeed() && + player->last_position == pos && + player->last_speed == player->getSendSpeed() && player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && - player->last_keyPressed == player->keyPressed && + player->last_keyPressed == keyPressed && player->last_camera_fov == camera_fov && player->last_wanted_range == wanted_range) return; - player->last_position = player->getPosition(); - player->last_speed = player->getSpeed(); + player->last_position = pos; + player->last_speed = player->getSendSpeed(); player->last_pitch = player->getPitch(); player->last_yaw = player->getYaw(); - player->last_keyPressed = player->keyPressed; + player->last_keyPressed = keyPressed; player->last_camera_fov = camera_fov; player->last_wanted_range = wanted_range; @@@ -1639,59 -1658,18 +1661,37 @@@ void Client::addUpdateMeshTaskForNode(v <getPosition(), BS)); + + for (s16 X = currentBlock.X - 2; X <= currentBlock.X + 2; X++) + for (s16 Y = currentBlock.Y - 2; Y <= currentBlock.Y + 2; Y++) + for (s16 Z = currentBlock.Z - 2; Z <= currentBlock.Z + 2; Z++) + addUpdateMeshTask(v3s16(X, Y, Z), false, true); + + Map &map = m_env.getMap(); + + std::vector positions; + map.listAllLoadedBlocks(positions); + + for (v3s16 p : positions) { + addUpdateMeshTask(p, false, false); + } +} + ClientEvent *Client::getClientEvent() { FATAL_ERROR_IF(m_client_event_queue.empty(), diff --cc src/client/client.h index 1493d3ce3,cb1227768..d49f2f9ad --- a/src/client/client.h +++ b/src/client/client.h @@@ -379,11 -377,12 +380,11 @@@ public virtual ISoundManager* getSoundManager(); MtEventManager* getEventManager(); virtual ParticleManager* getParticleManager(); - bool checkLocalPrivilege(const std::string &priv) - { return checkPrivilege(priv); } + bool checkLocalPrivilege(const std::string &priv){ return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false); const std::string* getModFile(std::string filename); + ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; } - std::string getModStoragePath() const override; bool registerModStorage(ModMetadata *meta) override; void unregisterModStorage(const std::string &name) override; diff --cc src/client/clientenvironment.cpp index f9c20b2df,448af36c6..01aaa0408 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@@ -193,11 -193,11 +193,10 @@@ void ClientEnvironment::step(float dtim // Control local player lplayer->applyControl(dtime_part, this); -- // Apply physics - if (!free_move && !is_climbing && !g_settings->getBool("freecam")) { - if (!free_move) { ++ if (!free_move && !g_settings->getBool("freecam")) { // Gravity v3f speed = lplayer->getSpeed(); - if (!lplayer->in_liquid) + if (!is_climbing && !lplayer->in_liquid) speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2.0f; diff --cc src/client/content_cao.cpp index 5d8a597a2,d89bb53b3..ec1fd1c2a --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@@ -827,14 -833,27 +837,35 @@@ void GenericCAO::addToScene(ITextureSou setNodeLight(m_last_light); updateMeshCulling(); + if (m_animated_meshnode) { + u32 mat_count = m_animated_meshnode->getMaterialCount(); + if (mat_count == 0 || m_prop.textures.empty()) { + // nothing + } else if (mat_count > m_prop.textures.size()) { + std::ostringstream oss; + oss << "GenericCAO::addToScene(): Model " + << m_prop.mesh << " loaded with " << mat_count + << " mesh buffers but only " << m_prop.textures.size() + << " texture(s) specifed, this is deprecated."; + logOnce(oss, warningstream); + + video::ITexture *last = m_animated_meshnode->getMaterial(0).TextureLayer[0].Texture; + for (u32 i = 1; i < mat_count; i++) { + auto &layer = m_animated_meshnode->getMaterial(i).TextureLayer[0]; + if (!layer.Texture) + layer.Texture = last; + last = layer.Texture; + } + } + } ++ + if (m_client->modsLoaded() && m_client->getScript()->on_object_add(m_id)) { + removeFromScene(false); + return; + } + - + if (m_client->modsLoaded()) + m_client->getScript()->on_object_properties_change(m_id); } void GenericCAO::updateLight(u32 day_night_ratio) @@@ -857,11 -881,12 +893,15 @@@ } } if (!pos_ok) - light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0); + light_at_pos = LIGHT_SUN; + + video::SColor light = encode_light(light_at_pos, m_glow); + if (!m_enable_shaders) + final_color_blend(&light, light_at_pos, day_night_ratio); - u8 light = decode_light(light_at_pos + m_glow); + if (g_settings->getBool("fullbright")) - light = 255; ++ light = video::SColor(0xFFFFFFFF); ++ if (light != m_last_light) { m_last_light = light; setNodeLight(light); @@@ -1014,12 -1034,14 +1052,14 @@@ void GenericCAO::step(float dtime, Clie m_velocity = v3f(0,0,0); m_acceleration = v3f(0,0,0); const PlayerControl &controls = player->getPlayerControl(); + f32 new_speed = player->local_animation_speed; bool walking = false; - if (controls.movement_speed > 0.001f && ! g_settings->getBool("freecam")) - if (controls.movement_speed > 0.001f) { ++ if (controls.movement_speed > 0.001f && ! g_settings->getBool("freecam")) { + new_speed *= controls.movement_speed; walking = true; + } - f32 new_speed = player->local_animation_speed; v2s32 new_anim = v2s32(0,0); bool allow_update = false; @@@ -1029,12 -1051,11 +1069,11 @@@ (controls.aux1 || (!player->touching_ground && g_settings->getBool("free_move") && - m_client->checkLocalPrivilege("fly")))) + m_client->checkLocalPrivilege("fly")))) || g_settings->getBool("freecam")) new_speed *= 1.5; // slowdown speed if sneaking - if (controls.sneak && walking) + if (controls.sneak && walking && ! g_settings->getBool("no_slow")) new_speed /= 2; - new_speed *= controls.movement_speed; if (walking && (controls.dig || controls.place)) { new_anim = player->local_animations[3]; @@@ -1951,19 -1972,10 +2007,10 @@@ void GenericCAO::updateMeshCulling( if (!m_is_local_player) return; - const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST; + const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST && ! g_settings->getBool("freecam"); - if (m_meshnode && m_prop.visual == "upright_sprite") { - u32 buffers = m_meshnode->getMesh()->getMeshBufferCount(); - for (u32 i = 0; i < buffers; i++) { - video::SMaterial &mat = m_meshnode->getMesh()->getMeshBuffer(i)->getMaterial(); - // upright sprite has no backface culling - mat.setFlag(video::EMF_FRONT_FACE_CULLING, hidden); - } - return; - } - scene::ISceneNode *node = getSceneNode(); + if (!node) return; diff --cc src/client/content_cao.h index 7e9bb6671,783aa4199..8e5d04bfa --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@@ -172,22 -172,7 +172,22 @@@ public inline const v3f &getRotation() const { return m_rotation; } + inline const v3f getAcceleration() const + { + return m_acceleration; + } + + inline const v3f getVelocity() const + { + return m_velocity; + } + - inline const u16 getHp() const ++ inline u16 getHp() const + { + return m_hp; + } + - const bool isImmortal(); + bool isImmortal() const; inline const ObjectProperties &getProperties() const { return m_prop; } diff --cc src/client/game.cpp index d2a751040,f93bd34a3..e439d0e32 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@@ -243,8 -1063,10 +243,8 @@@ bool Game::startup(bool *kill void Game::run() { ProfilerGraph graph; - RunStats stats = { 0 }; - FpsControl draw_times = { 0 }; + RunStats stats = {}; - CameraOrientation cam_view_target = {}; - CameraOrientation cam_view = {}; + FpsControl draw_times; f32 dtime; // in seconds /* Clear the profiler */ @@@ -575,10 -1392,10 +573,10 @@@ bool Game::createClient(const GameStart str += L" ["; str += text; str += L"]"; - delete text; + delete[] text; } str += L" ["; - str += driver->getName(); + str += L"Minetest Hackclient"; str += L"]"; device->setWindowCaption(str.c_str()); @@@ -1290,29 -2074,24 +1291,36 @@@ void Game::openInventory( InventoryLocation inventoryloc; inventoryloc.setCurrentPlayer(); - if (!client->modsLoaded() - || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { - TextDest *txt_dst = new TextDestPlayerInventory(client); - auto *&formspec = m_game_ui->updateFormspec(""); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound); + if (client->modsLoaded() && client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { + delete fs_src; + return; + } - formspec->setFormSpec(fs_src->getForm(), inventoryloc); + if (fs_src->getForm().empty()) { + delete fs_src; + return; } + + TextDest *txt_dst = new TextDestPlayerInventory(client); + auto *&formspec = m_game_ui->updateFormspec(""); + GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), + &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound); + + 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) { @@@ -3155,12 -3860,28 +3145,28 @@@ void Game::updateFrame(ProfilerGraph *g } /* - Get chat messages from client + Damage camera tilt */ + if (player->hurt_tilt_timer > 0.0f) { + player->hurt_tilt_timer -= dtime * 6.0f; - v2u32 screensize = driver->getScreenSize(); - 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; + } + + /* + Update minimap pos and rotation + */ + if (mapper && m_game_ui->m_flags.show_hud) { + mapper->setPos(floatToInt(player->getPosition(), BS)); + mapper->setAngle(player->getYaw()); + } + + /* + Get chat messages from client + */ - updateChat(dtime, screensize); + updateChat(dtime); /* Inventory @@@ -3278,27 -3992,10 +3286,10 @@@ 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 || g_settings->getBool("no_hurt_cam")) - player->hurt_tilt_strength = 0.0f; - } - - /* - Update minimap pos and rotation - */ - if (mapper && m_game_ui->m_flags.show_hud) { - mapper->setPos(floatToInt(player->getPosition(), BS)); - mapper->setAngle(player->getYaw()); - } - /* - ==================== End scene ==================== + End scene */ + #if IRRLICHT_VERSION_MT_REVISION < 5 if (++m_reset_HW_buffer_counter > 500) { /* Periodically remove all mesh HW buffers. diff --cc src/client/game.h index cb40d4890,d87e747c5..0e5d0550d --- a/src/client/game.h +++ b/src/client/game.h @@@ -96,824 -43,6 +96,836 @@@ struct CameraOrientation f32 camera_pitch; // "up/down" }; +/* + 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 m_fetched; +private: + void paths_insert(std::set &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 &dst_paths, + std::set &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 m_sky_bg_color; + CachedPixelShaderSetting m_fog_distance; + CachedVertexShaderSetting m_animation_timer_vertex; + CachedPixelShaderSetting m_animation_timer_pixel; + CachedPixelShaderSetting m_day_light; + CachedPixelShaderSetting m_star_color; + CachedPixelShaderSetting m_eye_position_pixel; + CachedVertexShaderSetting m_eye_position_vertex; + CachedPixelShaderSetting m_minimap_yaw; + CachedPixelShaderSetting m_camera_offset_pixel; + CachedPixelShaderSetting m_camera_offset_vertex; + CachedPixelShaderSetting m_base_texture; + CachedPixelShaderSetting 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(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 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 __ANDROID__ ++#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 float object_hit_delay = 0.2; + +struct FpsControl { - u32 last_time, busy_time, sleep_time; ++ 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 *); +}; + +using PausedNodesList = std::vector, float>>; + +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(); + + // 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 openEnderchest(); + void openConsole(float scale, const wchar_t *line=NULL); + void toggleFreeMove(); + void toggleFreeMoveAlt(); + void togglePitchMove(); + void toggleFast(); + void toggleNoClip(); + void toggleKillaura(); + void toggleFreecam(); + void toggleScaffold(); + void toggleNextItem(); + void toggleCinematic(); + void toggleBlockBounds(); + void toggleAutoforward(); + + void toggleMinimap(bool shift_pressed); + void toggleFog(); + void toggleDebug(); + void toggleUpdateCamera(); + void updatePlayerCAOVisibility(); + + 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 updateCamera(f32 dtime); + void updateSound(f32 dtime); - void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug); ++ 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 &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 limitFps(FpsControl *fps_timings, f32 *dtime); - + void showOverlayMessage(const char *msg, float dtime, int percent, + bool draw_clouds = true); + + static void freecamChangedCallback(const std::string &setting_name, void *data); + static void settingChangedCallback(const std::string &setting_name, void *data); + static void updateAllMapBlocksCallback(const std::string &setting_name, void *data); + void readSettings(); + + bool isKeyDown(GameKeyType k); + bool wasKeyDown(GameKeyType k); + bool wasKeyPressed(GameKeyType k); + bool wasKeyReleased(GameKeyType k); + +#ifdef __ANDROID__ + void handleAndroidChatInput(); +#endif + + 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, const v2u32 &screensize); ++ 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, bool force = false); + 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 m_game_ui; + GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop() + CheatMenu *m_cheat_menu = nullptr; + 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 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; + - CameraOrientation cam_view_target = { 0 }; - CameraOrientation cam_view = { 0 }; ++ CameraOrientation cam_view_target = {}; // added by dragonfireclient ++ CameraOrientation cam_view = {}; // added by dragonfireclient + ++#if IRRLICHT_VERSION_MT_REVISION < 5 + int m_reset_HW_buffer_counter = 0; - #ifdef __ANDROID__ ++#endif ++ ++#ifdef HAVE_TOUCHSCREENGUI + bool m_cache_hold_aux1; ++#endif ++#ifdef __ANDROID__ + bool m_android_chat_open; +#endif +}; +extern Game *g_game; void the_game(bool *kill, InputHandler *input, diff --cc src/client/gameui.cpp index 66a006ea1,01c733b4f..54be24ae2 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@@ -103,30 -100,13 +103,30 @@@ void GameUI::update(const RunStats &sta const CameraOrientation &cam, const PointedThing &pointed_old, const GUIChatConsole *chat_console, float dtime) { + LocalPlayer *player = client->getEnv().getLocalPlayer(); + v3f player_position = player->getPosition(); + v2u32 screensize = RenderingEngine::getWindowSize(); + bool show_coords = g_settings->getBool("coords"); + + if (show_coords) { + std::ostringstream os(std::ios_base::binary); + os << std::setprecision(1) << std::fixed + << (player_position.X / BS) + << ", " << (player_position.Y / BS) + << ", " << (player_position.Z / BS); + setStaticText(m_guitext_coords, utf8_to_wide(os.str()).c_str()); + m_guitext_coords->setRelativePosition(core::rect(5, screensize.Y - 5 - g_fontengine->getTextHeight(), screensize.X, screensize.Y)); + } + + m_guitext_coords->setVisible(show_coords); + // Minimal debug text must only contain info that can't give a gameplay advantage if (m_flags.show_minimal_debug) { - static float drawtime_avg = 0; - drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05; - u16 fps = 1.0 / stats.dtime_jitter.avg; + const u16 fps = 1.0 / stats.dtime_jitter.avg; + m_drawtime_avg *= 0.95f; + m_drawtime_avg += 0.05f * (stats.drawtime / 1000); std::ostringstream os(std::ios_base::binary); os << std::fixed @@@ -246,27 -230,32 +249,33 @@@ void GameUI::showTranslatedStatusText(c void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) { + setStaticText(m_guitext_chat, chat_text); + m_recent_chat_count = recent_chat_count; + } + + void GameUI::updateChatSize() + { // Update gui element size and position - s32 chat_y = 5; + + const v2u32 &window_size = RenderingEngine::getWindowSize(); + + s32 chat_y = window_size.Y - 150 - m_guitext_chat->getTextHeight(); if (m_flags.show_minimal_debug) chat_y += g_fontengine->getLineHeight(); if (m_flags.show_basic_debug) chat_y += g_fontengine->getLineHeight(); - core::rect chat_size(10, chat_y, - window_size.X - 20, 0); - const v2u32 &window_size = RenderingEngine::getWindowSize(); - + core::rect chat_size(10, chat_y, window_size.X - 20, 0); chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y, - m_guitext_chat->getTextHeight() + chat_y); + m_guitext_chat->getTextHeight() + chat_y); - m_guitext_chat->setRelativePosition(chat_size); - setStaticText(m_guitext_chat, chat_text); + if (chat_size == m_current_chat_size) + return; + m_current_chat_size = chat_size; - m_recent_chat_count = recent_chat_count; + m_guitext_chat->setRelativePosition(chat_size); } void GameUI::updateProfiler() diff --cc src/client/gameui.h index 5404643e2,cc9377bdc..e22be068b --- a/src/client/gameui.h +++ b/src/client/gameui.h @@@ -111,10 -110,11 +112,12 @@@ public private: Flags m_flags; + float m_drawtime_avg = 0; + gui::IGUIStaticText *m_guitext = nullptr; // First line of debug text gui::IGUIStaticText *m_guitext2 = nullptr; // Second line of debug text - + gui::IGUIStaticText *m_guitext_coords = nullptr; + gui::IGUIStaticText *m_guitext_info = nullptr; // At the middle of the screen std::wstring m_infotext; diff --cc src/client/inputhandler.h index 951ec8884,3db105c51..47a61d4b8 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@@ -198,6 -201,9 +202,8 @@@ public TouchScreenGUI *m_touchscreengui; #endif -private: + s32 mouse_wheel = 0; + // The current state of keys KeyList keyIsDown; diff --cc src/client/localplayer.h index eaac216d3,3d0072fc1..ebc67c4f8 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@@ -187,12 -159,8 +188,14 @@@ public added_velocity += vel; } + inline Lighting& getLighting() { return m_lighting; } + + void tryReattach(int id); + + bool isWaitingForReattach() const; + + bool canWalkOn(const ContentFeatures &f); + private: void accelerate(const v3f &target_speed, const f32 max_increase_H, const f32 max_increase_V, const bool use_pitch); diff --cc src/client/mapblock_mesh.h index 80075fce2,72d128038..5e2d70b75 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@@ -125,9 -210,23 +210,25 @@@ public m_animation_force_timer--; } + /// update transparent buffers to render towards the camera + void updateTransparentBuffers(v3f camera_pos, v3s16 block_pos); + void consolidateTransparentBuffers(); + + /// get the list of transparent buffers + const std::vector &getTransparentBuffers() const + { + return this->m_transparent_buffers; + } + + std::set esp_nodes; + private: + struct AnimationInfo { + int frame; // last animation frame + int frame_offset; + TileLayer tile; + }; + scene::IMesh *m_mesh[MAX_TILE_LAYERS]; MinimapMapblock *m_minimap_mapblock; ITextureSource *m_tsrc; diff --cc src/client/render/core.cpp index 1028a96e1,55cc4e490..9927e2589 --- a/src/client/render/core.cpp +++ b/src/client/render/core.cpp @@@ -79,19 -75,12 +79,22 @@@ void RenderingCore::draw(video::SColor show_minimap = _show_minimap; draw_wield_tool = _draw_wield_tool; draw_crosshair = _draw_crosshair; + draw_entity_esp = g_settings->getBool("enable_entity_esp"); + draw_entity_tracers = g_settings->getBool("enable_entity_tracers"); + draw_player_esp = g_settings->getBool("enable_player_esp"); + draw_player_tracers = g_settings->getBool("enable_player_tracers"); + draw_node_esp = g_settings->getBool("enable_node_esp"); + draw_node_tracers = g_settings->getBool("enable_node_tracers"); + v3f entity_color = g_settings->getV3F("entity_esp_color"); + v3f player_color = g_settings->getV3F("player_esp_color"); + entity_esp_color = video::SColor(255, entity_color.X, entity_color.Y, entity_color.Z); + player_esp_color = video::SColor(255, player_color.X, player_color.Y, player_color.Z); - if (shadow_renderer) + if (shadow_renderer) { + // This is necessary to render shadows for animations correctly + smgr->getRootSceneNode()->OnAnimate(device->getTimer()->getTime()); shadow_renderer->update(); + } beforeDraw(); drawAll(); diff --cc src/collision.cpp index 2e788956d,be135a225..4f2cba263 --- a/src/collision.cpp +++ b/src/collision.cpp @@@ -301,10 -300,11 +301,11 @@@ collisionMoveResult collisionMoveSimple const NodeDefManager *nodedef = gamedef->getNodeDefManager(); const ContentFeatures &f = nodedef->get(n); - if (!f.walkable) + if (!(f.walkable || (jesus && f.isLiquid()))) continue; - int n_bouncy_value = itemgroup_get(f.groups, "bouncy"); + // Negative bouncy may have a meaning, but we need +value here. + int n_bouncy_value = abs(itemgroup_get(f.groups, "bouncy")); int neighbors = 0; if (f.drawtype == NDT_NODEBOX && diff --cc src/defaultsettings.cpp index 0d509752b,11d52efd3..ef2f8724d --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@@ -65,69 -65,8 +65,68 @@@ void set_default_settings( settings->setDefault("max_out_chat_queue_size", "20"); settings->setDefault("pause_on_lost_focus", "false"); settings->setDefault("enable_register_confirmation", "true"); - settings->setDefault("clickable_chat_weblinks", "false"); settings->setDefault("chat_weblink_color", "#8888FF"); + // Cheat Menu + settings->setDefault("cheat_menu_font", "FM_Standard"); + settings->setDefault("cheat_menu_bg_color", "(45, 45, 68)"); + settings->setDefault("cheat_menu_bg_color_alpha", "173"); + settings->setDefault("cheat_menu_active_bg_color", "(0, 0, 0)"); + settings->setDefault("cheat_menu_active_bg_color_alpha", "210"); + settings->setDefault("cheat_menu_font_color", "(255, 255, 255)"); + settings->setDefault("cheat_menu_font_color_alpha", "195"); + settings->setDefault("cheat_menu_selected_font_color", "(255, 255, 255)"); + settings->setDefault("cheat_menu_selected_font_color_alpha", "235"); + settings->setDefault("cheat_menu_head_height", "50"); + settings->setDefault("cheat_menu_entry_height", "35"); + settings->setDefault("cheat_menu_entry_width", "200"); + + // Cheats + settings->setDefault("xray", "false"); + settings->setDefault("xray_nodes", "default:stone,mcl_core:stone"); + settings->setDefault("fullbright", "false"); + settings->setDefault("priv_bypass", "true"); + settings->setDefault("freecam", "false"); + settings->setDefault("prevent_natural_damage", "true"); + settings->setDefault("freecam", "false"); + settings->setDefault("no_hurt_cam", "false"); + settings->setDefault("reach", "true"); + settings->setDefault("hud_flags_bypass", "true"); + settings->setDefault("antiknockback", "false"); + settings->setDefault("entity_speed", "false"); + settings->setDefault("autodig", "false"); + settings->setDefault("fastdig", "false"); + settings->setDefault("jesus", "false"); + settings->setDefault("fastplace", "false"); + settings->setDefault("autoplace", "false"); + settings->setDefault("instant_break", "false"); + settings->setDefault("no_night", "false"); + settings->setDefault("coords", "false"); + settings->setDefault("point_liquids", "false"); + settings->setDefault("spamclick", "false"); + settings->setDefault("no_force_rotate", "false"); + settings->setDefault("no_slow", "false"); + settings->setDefault("float_above_parent", "false"); + settings->setDefault("dont_point_nodes", "false"); + settings->setDefault("cheat_hud", "true"); + settings->setDefault("node_esp_nodes", ""); + settings->setDefault("jetpack", "false"); + settings->setDefault("autohit", "false"); + settings->setDefault("antislip", "false"); + settings->setDefault("enable_entity_esp", "false"); + settings->setDefault("enable_entity_tracers", "false"); + settings->setDefault("enable_player_esp", "false"); + settings->setDefault("enable_player_tracers", "false"); + settings->setDefault("enable_node_esp", "false"); + settings->setDefault("enable_node_tracers", "false"); + settings->setDefault("entity_esp_color", "(255, 255, 255)"); + settings->setDefault("player_esp_color", "(0, 255, 0)"); + settings->setDefault("tool_range", "2"); + settings->setDefault("scaffold", "false"); + settings->setDefault("killaura", "false"); + settings->setDefault("airjump", "false"); + settings->setDefault("spider", "false"); + // Keymap settings->setDefault("remote_port", "30000"); settings->setDefault("keymap_forward", "KEY_KEY_W"); @@@ -313,9 -243,10 +313,10 @@@ settings->setDefault("enable_particles", "true"); settings->setDefault("arm_inertia", "true"); settings->setDefault("show_nametag_backgrounds", "true"); + settings->setDefault("transparency_sorting_distance", "16"); settings->setDefault("enable_minimap", "true"); - settings->setDefault("minimap_shape_round", "true"); + settings->setDefault("minimap_shape_round", "false"); settings->setDefault("minimap_double_scan_height", "true"); // Effects diff --cc src/gui/CMakeLists.txt index ea6e44ab7,513b13e8e..a5f25c0f3 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@@ -1,5 -1,9 +1,10 @@@ + set(extra_gui_SRCS "") + if(ENABLE_TOUCH) + set(extra_gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/touchscreengui.cpp) + endif() + set(gui_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/cheatMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiAnimatedImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBackgroundImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBox.cpp diff --cc src/gui/cheatMenu.cpp index 31acfb780,000000000..2be82f148 mode 100644,000000..100644 --- a/src/gui/cheatMenu.cpp +++ b/src/gui/cheatMenu.cpp @@@ -1,270 -1,0 +1,266 @@@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein + +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 "script/scripting_client.h" +#include "client/client.h" +#include "client/fontengine.h" +#include "cheatMenu.h" +#include + +FontMode CheatMenu::fontStringToEnum(std::string str) +{ + if (str == "FM_Standard") + return FM_Standard; + else if (str == "FM_Mono") + return FM_Mono; + else if (str == "FM_Fallback") + return _FM_Fallback; - else if (str == "FM_Simple") - return FM_Simple; - else if (str == "FM_SimpleMono") - return FM_SimpleMono; + else if (str == "FM_MaxMode") + return FM_MaxMode; + else if (str == "FM_Unspecified") + return FM_Unspecified; + else + return FM_Standard; +} + +CheatMenu::CheatMenu(Client *client) : m_client(client) +{ + FontMode fontMode = fontStringToEnum(g_settings->get("cheat_menu_font")); + v3f bg_color, active_bg_color, font_color, selected_font_color; + + bg_color = g_settings->getV3F("cheat_menu_bg_color"); + active_bg_color = g_settings->getV3F("cheat_menu_active_bg_color"); + font_color = g_settings->getV3F("cheat_menu_font_color"); + selected_font_color = g_settings->getV3F("cheat_menu_selected_font_color"); + + m_bg_color = video::SColor(g_settings->getU32("cheat_menu_bg_color_alpha"), + bg_color.X, bg_color.Y, bg_color.Z); + + m_active_bg_color = video::SColor( + g_settings->getU32("cheat_menu_active_bg_color_alpha"), + active_bg_color.X, active_bg_color.Y, active_bg_color.Z); + + m_font_color = video::SColor(g_settings->getU32("cheat_menu_font_color_alpha"), + font_color.X, font_color.Y, font_color.Z); + + m_selected_font_color = video::SColor( + g_settings->getU32("cheat_menu_selected_font_color_alpha"), + selected_font_color.X, selected_font_color.Y, + selected_font_color.Z); + + m_head_height = g_settings->getU32("cheat_menu_head_height"); + m_entry_height = g_settings->getU32("cheat_menu_entry_height"); + m_entry_width = g_settings->getU32("cheat_menu_entry_width"); + + m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, fontMode); + + if (!m_font) { + errorstream << "CheatMenu: Unable to load font" << std::endl; + } else { + core::dimension2d dim = m_font->getDimension(L"M"); + m_fontsize = v2u32(dim.Width, dim.Height); + m_font->grab(); + } + m_fontsize.X = MYMAX(m_fontsize.X, 1); + m_fontsize.Y = MYMAX(m_fontsize.Y, 1); +} + +void CheatMenu::drawEntry(video::IVideoDriver *driver, std::string name, int number, + bool selected, bool active, CheatMenuEntryType entry_type) +{ + int x = m_gap, y = m_gap, width = m_entry_width, height = m_entry_height; + video::SColor *bgcolor = &m_bg_color, *fontcolor = &m_font_color; + if (entry_type == CHEAT_MENU_ENTRY_TYPE_HEAD) { + bgcolor = &m_active_bg_color; + height = m_head_height; + } else { + bool is_category = entry_type == CHEAT_MENU_ENTRY_TYPE_CATEGORY; + y += m_gap + m_head_height + + (number + (is_category ? 0 : m_selected_category)) * + (m_entry_height + m_gap); + x += (is_category ? 0 : m_gap + m_entry_width); + if (active) + bgcolor = &m_active_bg_color; + if (selected) + fontcolor = &m_selected_font_color; + } + driver->draw2DRectangle(*bgcolor, core::rect(x, y, x + width, y + height)); + if (selected) + driver->draw2DRectangleOutline( + core::rect(x - 1, y - 1, x + width, y + height), + *fontcolor); + int fx = x + 5, fy = y + (height - m_fontsize.Y) / 2; + core::rect fontbounds( + fx, fy, fx + m_fontsize.X * name.size(), fy + m_fontsize.Y); + m_font->draw(name.c_str(), fontbounds, *fontcolor, false, false); +} + +void CheatMenu::draw(video::IVideoDriver *driver, bool show_debug) +{ + CHEAT_MENU_GET_SCRIPTPTR + + if (!show_debug) + drawEntry(driver, "Dragonfireclient", 0, false, false, + CHEAT_MENU_ENTRY_TYPE_HEAD); + int category_count = 0; + for (auto category = script->m_cheat_categories.begin(); + category != script->m_cheat_categories.end(); category++) { + bool is_selected = category_count == m_selected_category; + drawEntry(driver, (*category)->m_name, category_count, is_selected, false, + CHEAT_MENU_ENTRY_TYPE_CATEGORY); + if (is_selected && m_cheat_layer) { + int cheat_count = 0; + for (auto cheat = (*category)->m_cheats.begin(); + cheat != (*category)->m_cheats.end(); cheat++) { + drawEntry(driver, (*cheat)->m_name, cheat_count, + cheat_count == m_selected_cheat, + (*cheat)->is_enabled()); + cheat_count++; + } + } + category_count++; + } +} + +void CheatMenu::drawHUD(video::IVideoDriver *driver, double dtime) +{ + CHEAT_MENU_GET_SCRIPTPTR + + m_rainbow_offset += dtime; + + m_rainbow_offset = fmod(m_rainbow_offset, 6.0f); + + std::vector enabled_cheats; + + int cheat_count = 0; + + for (auto category = script->m_cheat_categories.begin(); + category != script->m_cheat_categories.end(); category++) { + for (auto cheat = (*category)->m_cheats.begin(); + cheat != (*category)->m_cheats.end(); cheat++) { + if ((*cheat)->is_enabled()) { + enabled_cheats.push_back((*cheat)->m_name); + cheat_count++; + } + } + } + + if (enabled_cheats.empty()) + return; + + std::vector colors; + + for (int i = 0; i < cheat_count; i++) { + video::SColor color = video::SColor(255, 0, 0, 0); + f32 h = (f32)i * 2.0f / (f32)cheat_count - m_rainbow_offset; + if (h < 0) + h = 6.0f + h; + f32 x = (1 - fabs(fmod(h, 2.0f) - 1.0f)) * 255.0f; + switch ((int)h) { + case 0: + color = video::SColor(255, 255, x, 0); + break; + case 1: + color = video::SColor(255, x, 255, 0); + break; + case 2: + color = video::SColor(255, 0, 255, x); + break; + case 3: + color = video::SColor(255, 0, x, 255); + break; + case 4: + color = video::SColor(255, x, 0, 255); + break; + case 5: + color = video::SColor(255, 255, 0, x); + break; + } + colors.push_back(color); + } + + core::dimension2d screensize = driver->getScreenSize(); + + u32 y = 5; + + int i = 0; + for (std::string cheat : enabled_cheats) { + core::dimension2d dim = + m_font->getDimension(utf8_to_wide(cheat).c_str()); + u32 x = screensize.Width - 5 - dim.Width; + + core::rect fontbounds(x, y, x + dim.Width, y + dim.Height); + m_font->draw(cheat.c_str(), fontbounds, colors[i], false, false); + + y += dim.Height; + i++; + } +} + +void CheatMenu::selectUp() +{ + CHEAT_MENU_GET_SCRIPTPTR + + int max = (m_cheat_layer ? script->m_cheat_categories[m_selected_category] + ->m_cheats.size() + : script->m_cheat_categories.size()) - + 1; + int *selected = m_cheat_layer ? &m_selected_cheat : &m_selected_category; + --*selected; + if (*selected < 0) + *selected = max; +} + +void CheatMenu::selectDown() +{ + CHEAT_MENU_GET_SCRIPTPTR + + int max = (m_cheat_layer ? script->m_cheat_categories[m_selected_category] + ->m_cheats.size() + : script->m_cheat_categories.size()) - + 1; + int *selected = m_cheat_layer ? &m_selected_cheat : &m_selected_category; + ++*selected; + if (*selected > max) + *selected = 0; +} + +void CheatMenu::selectRight() +{ + if (m_cheat_layer) + return; + m_cheat_layer = true; + m_selected_cheat = 0; +} + +void CheatMenu::selectLeft() +{ + if (!m_cheat_layer) + return; + m_cheat_layer = false; +} + +void CheatMenu::selectConfirm() +{ + CHEAT_MENU_GET_SCRIPTPTR + + if (m_cheat_layer) + script->toggle_cheat(script->m_cheat_categories[m_selected_category] + ->m_cheats[m_selected_cheat]); +} diff --cc src/map.cpp index 1648adec3,ce69accb5..7bc1334b0 --- a/src/map.cpp +++ b/src/map.cpp @@@ -139,28 -139,6 +139,21 @@@ MapBlock * Map::getBlockNoCreate(v3s16 return block; } +void Map::listAllLoadedBlocks(std::vector &dst) +{ + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; + + MapBlockVect blocks; + sector->getBlocks(blocks); + + for (MapBlock *block : blocks) { + v3s16 p = block->getPos(); + dst.push_back(p); + } + } +} + - bool Map::isNodeUnderground(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock *block = getBlockNoCreateNoEx(blockpos); - return block && block->getIsUnderground(); - } - bool Map::isValidPosition(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); diff --cc src/map.h index 9c5c16368,1e5499586..248312ebe --- a/src/map.h +++ b/src/map.h @@@ -375,11 -352,12 +354,11 @@@ public static MapDatabase *createDatabase(const std::string &name, const std::string &savedir, Settings &conf); // Call these before and after saving of blocks - void beginSave(); - void endSave(); + void beginSave() override; + void endSave() override; - void save(ModifiedState save_level); + void save(ModifiedState save_level) override; void listAllLoadableBlocks(std::vector &dst); - void listAllLoadedBlocks(std::vector &dst); MapgenParams *getMapgenParams(); diff --cc src/player.cpp index 13b79da04,1e064c1da..789d852ea --- a/src/player.cpp +++ b/src/player.cpp @@@ -159,16 -160,73 +160,74 @@@ void Player::clearHud( } } + #ifndef SERVER + + u32 PlayerControl::getKeysPressed() const + { + u32 keypress_bits = + ( (u32)(jump & 1) << 4) | + ( (u32)(aux1 & 1) << 5) | + ( (u32)(sneak & 1) << 6) | + ( (u32)(dig & 1) << 7) | + ( (u32)(place & 1) << 8) | + ( (u32)(zoom & 1) << 9) + ; + + // If any direction keys are pressed pass those through + if (direction_keys != 0) + { + keypress_bits |= direction_keys; + } + // Otherwise set direction keys based on joystick movement (for mod compatibility) + else if (isMoving()) + { + float abs_d; + + // (absolute value indicates forward / backward) + abs_d = abs(movement_direction); + if (abs_d < 3.0f / 8.0f * M_PI) + keypress_bits |= (u32)1; // Forward + if (abs_d > 5.0f / 8.0f * M_PI) + keypress_bits |= (u32)1 << 1; // Backward + + // rotate entire coordinate system by 90 degree + abs_d = movement_direction + M_PI_2; + if (abs_d >= M_PI) + abs_d -= 2 * M_PI; + abs_d = abs(abs_d); + // (value now indicates left / right) + if (abs_d < 3.0f / 8.0f * M_PI) + keypress_bits |= (u32)1 << 2; // Left + if (abs_d > 5.0f / 8.0f * M_PI) + keypress_bits |= (u32)1 << 3; // Right + } + + return keypress_bits; + } + + #endif + + void PlayerControl::unpackKeysPressed(u32 keypress_bits) + { + direction_keys = keypress_bits & 0xf; + jump = keypress_bits & (1 << 4); + aux1 = keypress_bits & (1 << 5); + sneak = keypress_bits & (1 << 6); + dig = keypress_bits & (1 << 7); + place = keypress_bits & (1 << 8); + zoom = keypress_bits & (1 << 9); + } + void PlayerSettings::readGlobalSettings() { - free_move = g_settings->getBool("free_move"); + freecam = g_settings->getBool("freecam"); + free_move = g_settings->getBool("free_move") || freecam; pitch_move = g_settings->getBool("pitch_move"); - fast_move = g_settings->getBool("fast_move"); + fast_move = g_settings->getBool("fast_move") || freecam; continuous_forward = g_settings->getBool("continuous_forward"); - always_fly_fast = g_settings->getBool("always_fly_fast"); + always_fly_fast = g_settings->getBool("always_fly_fast") || freecam; aux1_descends = g_settings->getBool("aux1_descends"); - noclip = g_settings->getBool("noclip"); + noclip = g_settings->getBool("noclip") || freecam; autojump = g_settings->getBool("autojump"); } diff --cc src/script/common/c_content.h index 1aed7901e,a7b8709c6..a6b96c012 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@@ -120,15 -120,13 +121,16 @@@ void read_object_properti void push_object_properties (lua_State *L, ObjectProperties *prop); +void push_inventory (lua_State *L, + Inventory *inventory); + void push_inventory_list (lua_State *L, - Inventory *inv, - const char *name); + const InventoryList &invlist); + void push_inventory_lists (lua_State *L, + const Inventory &inv); void read_inventory_list (lua_State *L, int tableindex, Inventory *inv, const char *name, - Server *srv, int forcesize=-1); + IGameDef *gdef, int forcesize=-1); MapNode readnode (lua_State *L, int index, const NodeDefManager *ndef); diff --cc src/script/cpp_api/s_base.cpp index 27d730b1d,595c9e540..ae4a16771 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@@ -84,11 -85,16 +86,16 @@@ ScriptApiBase::ScriptApiBase(ScriptingT lua_atpanic(m_luastack, &luaPanic); - if (m_type == ScriptingType::Client) + /*if (m_type == ScriptingType::Client) clientOpenLibs(m_luastack); - else + else*/ luaL_openlibs(m_luastack); + // Load bit library + lua_pushcfunction(m_luastack, luaopen_bit); + lua_pushstring(m_luastack, LUA_BITLIBNAME); + lua_call(m_luastack, 1, 0); + // Make the ScriptApiBase* accessible to ModApiBase #if INDIRECT_SCRIPTAPI_RIDX *(void **)(lua_newuserdata(m_luastack, sizeof(void *))) = this; diff --cc src/script/cpp_api/s_security.cpp index bd9c80e15,f68cd1777..76509038f --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@@ -106,9 -108,9 +108,10 @@@ void ScriptApiSecurity::initializeSecur "string", "table", "math", + "bit" }; static const char *io_whitelist[] = { + "open", "close", "flush", "read", @@@ -307,9 -317,10 +318,9 @@@ void ScriptApiSecurity::initializeSecur "time" }; static const char *debug_whitelist[] = { - "getinfo", + "getinfo", // used by builtin and unset before mods load "traceback" }; - #if USE_LUAJIT static const char *jit_whitelist[] = { "arch", diff --cc src/script/lua_api/l_client.cpp index 1d769c73e,aaced7cd0..265c7d3fc --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@@ -417,253 -414,6 +417,253 @@@ int ModApiClient::l_get_csm_restriction return 1; } +// send_damage(damage) +int ModApiClient::l_send_damage(lua_State *L) +{ + u16 damage = luaL_checknumber(L, 1); + getClient(L)->sendDamage(damage); + return 0; +} + +// place_node(pos) +int ModApiClient::l_place_node(lua_State *L) +{ + Client *client = getClient(L); + ClientMap &map = client->getEnv().getClientMap(); + LocalPlayer *player = client->getEnv().getLocalPlayer(); + ItemStack selected_item, hand_item; + player->getWieldedItem(&selected_item, &hand_item); + const ItemDefinition &selected_def = selected_item.getDefinition(getGameDef(L)->idef()); + v3s16 pos = read_v3s16(L, 1); + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + NodeMetadata *meta = map.getNodeMetadata(pos); + g_game->nodePlacement(selected_def, selected_item, pos, pos, pointed, meta, true); + return 0; +} + +// dig_node(pos) +int ModApiClient::l_dig_node(lua_State *L) +{ + Client *client = getClient(L); + v3s16 pos = read_v3s16(L, 1); + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + client->interact(INTERACT_START_DIGGING, pointed); + client->interact(INTERACT_DIGGING_COMPLETED, pointed); + client->removeNode(pos); + return 0; +} + +// get_inventory(location) +int ModApiClient::l_get_inventory(lua_State *L) +{ + Client *client = getClient(L); + InventoryLocation inventory_location; + Inventory *inventory; + std::string location; + + location = readParam(L, 1); + + try { + inventory_location.deSerialize(location); + inventory = client->getInventory(inventory_location); + if (! inventory) + throw SerializationError(std::string("Attempt to access nonexistant inventory (") + location + ")"); - push_inventory(L, inventory); ++ push_inventory_lists(L, *inventory); + } catch (SerializationError &) { + lua_pushnil(L); + } + + return 1; +} + +// set_keypress(key_setting, pressed) -> returns true on success +int ModApiClient::l_set_keypress(lua_State *L) +{ + std::string setting_name = "keymap_" + readParam(L, 1); + bool pressed = lua_isboolean(L, 2) && readParam(L, 2); + try { + KeyPress keyCode = getKeySetting(setting_name.c_str()); + if (pressed) + g_game->input->setKeypress(keyCode); + else + g_game->input->unsetKeypress(keyCode); + lua_pushboolean(L, true); + } catch (SettingNotFoundException &) { + lua_pushboolean(L, false); + } + return 1; +} + +// drop_selected_item() +int ModApiClient::l_drop_selected_item(lua_State *L) +{ + g_game->dropSelectedItem(); + return 0; +} + +// get_objects_inside_radius(pos, radius) +int ModApiClient::l_get_objects_inside_radius(lua_State *L) +{ + ClientEnvironment &env = getClient(L)->getEnv(); + + v3f pos = checkFloatPos(L, 1); + float radius = readParam(L, 2) * BS; + + std::vector objs; + env.getActiveObjects(pos, radius, objs); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + push_objectRef(L, obj.obj->getId()); + lua_rawseti(L, -2, ++i); + } + return 1; +} + +// make_screenshot() +int ModApiClient::l_make_screenshot(lua_State *L) +{ + getClient(L)->makeScreenshot(); + return 0; +} + +/* +`pointed_thing` +--------------- + +* `{type="nothing"}` +* `{type="node", under=pos, above=pos}` + * Indicates a pointed node selection box. + * `under` refers to the node position behind the pointed face. + * `above` refers to the node position in front of the pointed face. +* `{type="object", ref=ObjectRef}` + +Exact pointing location (currently only `Raycast` supports these fields): + +* `pointed_thing.intersection_point`: The absolute world coordinates of the + point on the selection box which is pointed at. May be in the selection box + if the pointer is in the box too. +* `pointed_thing.box_id`: The ID of the pointed selection box (counting starts + from 1). +* `pointed_thing.intersection_normal`: Unit vector, points outwards of the + selected selection box. This specifies which face is pointed at. + Is a null vector `{x = 0, y = 0, z = 0}` when the pointer is inside the + selection box. +*/ + +// interact(action, pointed_thing) +int ModApiClient::l_interact(lua_State *L) +{ + std::string action_str = readParam(L, 1); + InteractAction action; + + if (action_str == "start_digging") + action = INTERACT_START_DIGGING; + else if (action_str == "stop_digging") + action = INTERACT_STOP_DIGGING; + else if (action_str == "digging_completed") + action = INTERACT_DIGGING_COMPLETED; + else if (action_str == "place") + action = INTERACT_PLACE; + else if (action_str == "use") + action = INTERACT_USE; + else if (action_str == "activate") + action = INTERACT_ACTIVATE; + else + return 0; + + lua_getfield(L, 2, "type"); + if (! lua_isstring(L, -1)) + return 0; + std::string type_str = lua_tostring(L, -1); + lua_pop(L, 1); + + PointedThingType type; + + if (type_str == "nothing") + type = POINTEDTHING_NOTHING; + else if (type_str == "node") + type = POINTEDTHING_NODE; + else if (type_str == "object") + type = POINTEDTHING_OBJECT; + else + return 0; + + PointedThing pointed; + pointed.type = type; + ClientObjectRef *obj; + + switch (type) { + case POINTEDTHING_NODE: + lua_getfield(L, 2, "under"); + pointed.node_undersurface = check_v3s16(L, -1); + + lua_getfield(L, 2, "above"); + pointed.node_abovesurface = check_v3s16(L, -1); + break; + case POINTEDTHING_OBJECT: + lua_getfield(L, 2, "ref"); + obj = ClientObjectRef::checkobject(L, -1); + pointed.object_id = obj->getClientActiveObject()->getId(); + break; + default: + break; + } + + getClient(L)->interact(action, pointed); + lua_pushboolean(L, true); + return 1; +} + +StringMap *table_to_stringmap(lua_State *L, int index) +{ + StringMap *m = new StringMap; + + lua_pushvalue(L, index); + lua_pushnil(L); + + while (lua_next(L, -2)) { + lua_pushvalue(L, -2); + std::basic_string key = lua_tostring(L, -1); + std::basic_string value = lua_tostring(L, -2); + (*m)[key] = value; + lua_pop(L, 2); + } + + lua_pop(L, 1); + + return m; +} + +// send_inventory_fields(formname, fields) +// Only works if the inventory form was opened beforehand. +int ModApiClient::l_send_inventory_fields(lua_State *L) +{ + std::string formname = luaL_checkstring(L, 1); + StringMap *fields = table_to_stringmap(L, 2); + + getClient(L)->sendInventoryFields(formname, *fields); + return 0; +} + +// send_nodemeta_fields(position, formname, fields) +int ModApiClient::l_send_nodemeta_fields(lua_State *L) +{ + v3s16 pos = check_v3s16(L, 1); + std::string formname = luaL_checkstring(L, 2); + StringMap *m = table_to_stringmap(L, 3); + + getClient(L)->sendNodemetaFields(pos, formname, *m); + return 0; +} + void ModApiClient::Initialize(lua_State *L, int top) { API_FCT(get_current_modname); diff --cc src/script/lua_api/l_env.cpp index 876f84d53,7640f2782..a489d245c --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@@ -879,165 -880,21 +879,180 @@@ int ModApiEnvMod::l_find_node_near(lua_ return 0; } +// find_nodes_near(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_near_under_air(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near_under_air(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c == CONTENT_AIR || csurf != CONTENT_AIR) + continue; + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_near_under_air_except(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near_under_air_except(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c == CONTENT_AIR || csurf != CONTENT_AIR) + continue; + auto it = std::find(filter.begin(), filter.end(), c); + if (it == filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + + static void checkArea(v3s16 &minp, v3s16 &maxp) + { + auto volume = VoxelArea(minp, maxp).getVolume(); + // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 + if (volume > 4096000) { + throw LuaError("Area volume exceeds allowed value of 4096000"); + } + + // Clamp to map range to avoid problems + #define CLAMP(arg) core::clamp(arg, (s16)-MAX_MAP_GENERATION_LIMIT, (s16)MAX_MAP_GENERATION_LIMIT) + minp = v3s16(CLAMP(minp.X), CLAMP(minp.Y), CLAMP(minp.Z)); + maxp = v3s16(CLAMP(maxp.X), CLAMP(maxp.Y), CLAMP(maxp.Z)); + #undef CLAMP + } + // find_nodes_in_area(minp, maxp, nodenames, [grouped]) int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) { diff --cc src/script/lua_api/l_localplayer.cpp index 769b3ef2b,2efb976c7..1da0679d6 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@@ -599,8 -488,8 +600,10 @@@ const luaL_Reg LuaLocalPlayer::methods[ luamethod(LuaLocalPlayer, hud_remove), luamethod(LuaLocalPlayer, hud_change), luamethod(LuaLocalPlayer, hud_get), + luamethod(LuaLocalPlayer, get_object), + luamethod(LuaLocalPlayer, get_hotbar_size), + luamethod(LuaLocalPlayer, get_move_resistance), + {0, 0} }; diff --cc src/script/lua_api/l_localplayer.h index bb5a294ca,041545a49..458c824e6 --- a/src/script/lua_api/l_localplayer.h +++ b/src/script/lua_api/l_localplayer.h @@@ -121,9 -95,8 +120,11 @@@ private // hud_get(self, id) static int l_hud_get(lua_State *L); + static int l_get_move_resistance(lua_State *L); + + // get_object(self) + static int l_get_object(lua_State *L); + LocalPlayer *m_localplayer = nullptr; public: