# 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)
--- /dev/null
-dofile(gamepath .. "voxelarea.lua")
+ 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(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
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 <item> | add <item> | 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 <command>")
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
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")
file:close()
end
end
- get_mods(core.get_clientmodpath(), clientmods)
+
+ -- Parse clientmods
+ local clientmods_category_initialized = false
+ local 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
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
-
--------------------------------------------------------------------------------
- if modpath ~= nil and
- modpath ~= "" then
- get_mods(modpath,clientmods)
+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", 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 = {}
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
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
# 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 (MenuFont) enum FM_Mono FM_Standard,FM_Mono,FM_Fallback,FM_Simple,FM_SimpleMono,FM_MaxMode,FM_Unspecified
+
+[Cheat Menu]
+
+# Font to use for cheat menu
++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
#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
// 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);
// 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,
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;
<<std::endl;
}
- v3s16 blockpos = getNodeBlockPos(nodepos);
+ v3s16 blockpos = getNodeBlockPos(nodepos);
v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
-
- try{
- addUpdateMeshTask(blockpos, ack_to_server, urgent);
- }
- catch(InvalidPositionException &e) {}
-
+ m_mesh_update_thread.updateBlock(&m_env.getMap(), blockpos, ack_to_server, urgent, false);
// Leading edge
- if(nodepos.X == blockpos_relative.X){
- try{
- v3s16 p = blockpos + v3s16(-1,0,0);
- addUpdateMeshTask(p, false, urgent);
- }
- catch(InvalidPositionException &e){}
- }
-
- if(nodepos.Y == blockpos_relative.Y){
- try{
- v3s16 p = blockpos + v3s16(0,-1,0);
- addUpdateMeshTask(p, false, urgent);
- }
- catch(InvalidPositionException &e){}
- }
-
- if(nodepos.Z == blockpos_relative.Z){
- try{
- v3s16 p = blockpos + v3s16(0,0,-1);
- addUpdateMeshTask(p, false, urgent);
- }
- catch(InvalidPositionException &e){}
- }
+ if (nodepos.X == blockpos_relative.X)
+ addUpdateMeshTask(blockpos + v3s16(-1, 0, 0), false, urgent);
+ if (nodepos.Y == blockpos_relative.Y)
+ addUpdateMeshTask(blockpos + v3s16(0, -1, 0), false, urgent);
+ if (nodepos.Z == blockpos_relative.Z)
+ addUpdateMeshTask(blockpos + v3s16(0, 0, -1), false, urgent);
}
+void Client::updateAllMapBlocks()
+{
+ v3s16 currentBlock = getNodeBlockPos(floatToInt(m_env.getLocalPlayer()->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<v3s16> positions;
+ map.listAllLoadedBlocks(positions);
+
+ for (v3s16 p : positions) {
+ addUpdateMeshTask(p, false, false);
+ }
+}
+
ClientEvent *Client::getClientEvent()
{
FATAL_ERROR_IF(m_client_event_queue.empty(),
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;
// 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;
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)
}
}
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);
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;
(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];
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;
inline const v3f &getRotation() const { return m_rotation; }
- inline const u16 getHp() const
+ inline const v3f getAcceleration() const
+ {
+ return m_acceleration;
+ }
+
+ inline const v3f getVelocity() const
+ {
+ return m_velocity;
+ }
+
- const bool isImmortal();
++ inline u16 getHp() const
+ {
+ return m_hp;
+ }
+
+ bool isImmortal() const;
inline const ObjectProperties &getProperties() const { return m_prop; }
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 */
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());
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)
{
}
/*
- 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
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.
f32 camera_pitch; // "up/down"
};
- #ifdef __ANDROID__
+/*
+ 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;
+ }
+};
+
- u32 last_time, busy_time, sleep_time;
++#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 {
- void updateCamera(u32 busy_time, f32 dtime);
++ 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<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, 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 processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug);
++ void updateCamera(f32 dtime);
+ void updateSound(f32 dtime);
- void limitFps(FpsControl *fps_timings, 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 updateChat(f32 dtime, const v2u32 &screensize);
+ 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);
+
- CameraOrientation cam_view_target = { 0 };
- CameraOrientation cam_view = { 0 };
++ 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<GameUI> 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<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;
+
- #ifdef __ANDROID__
++ 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;
++#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,
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<s32>(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
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<s32> chat_size(10, chat_y,
- window_size.X - 20, 0);
- const v2u32 &window_size = RenderingEngine::getWindowSize();
-
+ core::rect<s32> 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()
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;
TouchScreenGUI *m_touchscreengui;
#endif
-private:
+ s32 mouse_wheel = 0;
+
// The current state of keys
KeyList keyIsDown;
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);
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<PartialMeshBuffer> &getTransparentBuffers() const
+ {
+ return this->m_transparent_buffers;
+ }
+
+ std::set<v3s16> 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;
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();
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 &&
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");
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
+ 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
--- /dev/null
- else if (str == "FM_Simple")
- return FM_Simple;
- else if (str == "FM_SimpleMono")
- return FM_SimpleMono;
+/*
+Dragonfire
+Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de>
+
+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 <cstddef>
+
+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_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<u32> 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<s32>(x, y, x + width, y + height));
+ if (selected)
+ driver->draw2DRectangleOutline(
+ core::rect<s32>(x - 1, y - 1, x + width, y + height),
+ *fontcolor);
+ int fx = x + 5, fy = y + (height - m_fontsize.Y) / 2;
+ core::rect<s32> 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<std::string> 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<video::SColor> 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<u32> screensize = driver->getScreenSize();
+
+ u32 y = 5;
+
+ int i = 0;
+ for (std::string cheat : enabled_cheats) {
+ core::dimension2d<u32> dim =
+ m_font->getDimension(utf8_to_wide(cheat).c_str());
+ u32 x = screensize.Width - 5 - dim.Width;
+
+ core::rect<s32> 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]);
+}
return block;
}
- bool Map::isNodeUnderground(v3s16 p)
- {
- v3s16 blockpos = getNodeBlockPos(p);
- MapBlock *block = getBlockNoCreateNoEx(blockpos);
- return block && block->getIsUnderground();
- }
-
+void Map::listAllLoadedBlocks(std::vector<v3s16> &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::isValidPosition(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
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<v3s16> &dst);
- void listAllLoadedBlocks(std::vector<v3s16> &dst);
MapgenParams *getMapgenParams();
}
}
+ #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");
}
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);
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;
"string",
"table",
"math",
+ "bit"
};
static const char *io_whitelist[] = {
+ "open",
"close",
"flush",
"read",
"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",
return 1;
}
- push_inventory(L, inventory);
+// 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<std::string>(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_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<std::string>(L, 1);
+ bool pressed = lua_isboolean(L, 2) && readParam<bool>(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<float>(L, 2) * BS;
+
+ std::vector<DistanceSortedActiveObject> 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<std::string>(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<char> key = lua_tostring(L, -1);
+ std::basic_string<char> 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);
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<content_t> filter;
+ collectNodeIds(L, 3, ndef, filter);
+
+ int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1;
+
+#ifndef SERVER
+ // Client API limitations
+ if (Client *client = getClient(L))
+ radius = client->CSMClampRadius(pos, radius);
+#endif
+
+ std::vector<u32> individual_count;
+ individual_count.resize(filter.size());
+
+ lua_newtable(L);
+ u32 i = 0;
+
+ for (int d = start_radius; d <= radius; d++) {
+ const std::vector<v3s16> &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<content_t> filter;
+ collectNodeIds(L, 3, ndef, filter);
+ int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1;
+
+#ifndef SERVER
+ // Client API limitations
+ if (Client *client = getClient(L))
+ radius = client->CSMClampRadius(pos, radius);
+#endif
+
+ std::vector<u32> individual_count;
+ individual_count.resize(filter.size());
+
+ lua_newtable(L);
+ u32 i = 0;
+
+ for (int d = start_radius; d <= radius; d++) {
+ const std::vector<v3s16> &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<content_t> filter;
+ collectNodeIds(L, 3, ndef, filter);
+ int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1;
+
+#ifndef SERVER
+ // Client API limitations
+ if (Client *client = getClient(L))
+ radius = client->CSMClampRadius(pos, radius);
+#endif
+
+ std::vector<u32> individual_count;
+ individual_count.resize(filter.size());
+
+ lua_newtable(L);
+ u32 i = 0;
+
+ for (int d = start_radius; d <= radius; d++) {
+ const std::vector<v3s16> &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)
{
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}
};
// 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: