forked from Mirrorlandia_minetest/minetest
update
This commit is contained in:
commit
c990427d07
39
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
39
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -15,11 +15,10 @@ body:
|
||||
label: Minetest version
|
||||
description: |
|
||||
Paste the Minetest version below.
|
||||
If you are on a devel version, please add a git commit hash.
|
||||
You can use `minetest --version` to find it.
|
||||
You can also refer to the "About" tab of the menu.
|
||||
If you are on a dev version, please also indicate the git commit hash.
|
||||
Refer to the "About" tab of the menu or run `minetest --version` on the command line.
|
||||
placeholder: |
|
||||
Example:
|
||||
Example:
|
||||
Minetest 5.7.0-dev-ca13c51 (Linux)
|
||||
Using Irrlicht 1.9.0mt9
|
||||
Using LuaJIT 2.1.0-beta3
|
||||
@ -33,59 +32,53 @@ body:
|
||||
render: "true"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Active renderer
|
||||
description: For graphical and input-related issues. You can find these in the About tab in the mainmenu.
|
||||
placeholder: "Example: OpenGL 4.6.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Irrlicht device
|
||||
description:
|
||||
description:
|
||||
placeholder: "Example: X11"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating system and version
|
||||
description: It is recommended to upgrade your operating system to see if the problem still exists.
|
||||
description: It is recommended to upgrade your operating system to see if the problem persists.
|
||||
placeholder: "Example: Ubuntu 22.04"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: CPU model
|
||||
description: Usually found in system settings.
|
||||
placeholder: "Example: Intel i5-2410M (4) @ 2.900GHz"
|
||||
description: Usually found in OS/system settings.
|
||||
placeholder: "Example: Intel Core i5-2410M"
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: The GPU model and OpenGL version can be omitted if the bug is not a graphical issue.
|
||||
value: The GPU model and renderer can be omitted if the bug is not a graphical issue.
|
||||
- type: input
|
||||
attributes:
|
||||
label: GPU model
|
||||
description: Usually found in system settings.
|
||||
placeholder: "Example: NVIDA GeForce RTX 4090"
|
||||
description: Usually found in OS/system settings.
|
||||
placeholder: "Example: NVIDIA GeForce GTX 1660"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: OpenGL version
|
||||
placeholder: "Example: 4.6"
|
||||
label: Active renderer
|
||||
description: You can find this in the "About" tab in the main menu.
|
||||
placeholder: "Example: OpenGL 4.6.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe your problem here.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible.
|
||||
description: Explain how the problem has happened, providing a minimal test (e.g. a minimized code snippet) where possible.
|
||||
validations:
|
||||
required: true
|
||||
|
10
.github/workflows/android.yml
vendored
10
.github/workflows/android.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@ -35,22 +35,22 @@ jobs:
|
||||
- name: Build with Gradle
|
||||
run: cd android; ./gradlew assemblerelease
|
||||
- name: Save armeabi artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Minetest-armeabi-v7a.apk
|
||||
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||
- name: Save arm64 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Minetest-arm64-v8a.apk
|
||||
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||
- name: Save x86 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Minetest-x86.apk
|
||||
path: android/app/build/outputs/apk/release/app-x86-release-unsigned.apk
|
||||
- name: Save x86_64 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Minetest-x86_64.apk
|
||||
path: android/app/build/outputs/apk/release/app-x86_64-release-unsigned.apk
|
||||
|
2
.github/workflows/cpp_lint.yml
vendored
2
.github/workflows/cpp_lint.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
clang_tidy:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
|
19
.github/workflows/linux.yml
vendored
19
.github/workflows/linux.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
gcc_7:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
@ -58,7 +58,7 @@ jobs:
|
||||
gcc_12:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
@ -82,11 +82,11 @@ jobs:
|
||||
clang_7:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-7 valgrind
|
||||
install_linux_deps clang-7 llvm
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@ -94,20 +94,17 @@ jobs:
|
||||
env:
|
||||
CC: clang-7
|
||||
CXX: clang++-7
|
||||
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
|
||||
|
||||
- name: Unittest
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
- name: Valgrind
|
||||
run: |
|
||||
valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests
|
||||
|
||||
# Current clang version
|
||||
clang_14:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
@ -133,7 +130,7 @@ jobs:
|
||||
name: "clang_9 (PROMETHEUS=1)"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
@ -159,7 +156,7 @@ jobs:
|
||||
name: "Docker image"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build docker image
|
||||
run: |
|
||||
docker build . -t minetest:latest
|
||||
|
8
.github/workflows/lua.yml
vendored
8
.github/workflows/lua.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
name: "Compile and run multiplayer tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
@ -43,11 +43,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: leafo/gh-actions-lua@v9
|
||||
- uses: actions/checkout@v4
|
||||
- uses: leafo/gh-actions-lua@v10
|
||||
with:
|
||||
luaVersion: "5.1.5"
|
||||
- uses: leafo/gh-actions-luarocks@v4
|
||||
- uses: leafo/gh-actions-luarocks@v4.3.0
|
||||
|
||||
- name: Install LuaJIT
|
||||
run: |
|
||||
|
10
.github/workflows/lua_api_deploy.yml
vendored
10
.github/workflows/lua_api_deploy.yml
vendored
@ -19,10 +19,10 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
@ -36,13 +36,13 @@ jobs:
|
||||
./build.sh
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: 'public/'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
uses: actions/deploy-pages@v4
|
||||
|
4
.github/workflows/macos.yml
vendored
4
.github/workflows/macos.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
@ -56,7 +56,7 @@ jobs:
|
||||
cd build
|
||||
cpack -G ZIP -B macos
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: minetest-macos
|
||||
path: ./build/macos/*.zip
|
||||
|
18
.github/workflows/windows.yml
vendored
18
.github/workflows/windows.yml
vendored
@ -32,17 +32,17 @@ jobs:
|
||||
name: "MinGW cross-compiler (32-bit)"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y gettext
|
||||
sudo ./util/buildbot/download_toolchain.sh i686 /usr
|
||||
sudo ./util/buildbot/download_toolchain.sh /usr
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh B
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mingw32
|
||||
path: B/build/*.zip
|
||||
@ -52,17 +52,17 @@ jobs:
|
||||
name: "MinGW cross-compiler (64-bit)"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y gettext
|
||||
sudo ./util/buildbot/download_toolchain.sh x86_64 /usr
|
||||
sudo ./util/buildbot/download_toolchain.sh /usr
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh B
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mingw64
|
||||
path: B/build/*.zip
|
||||
@ -74,7 +74,7 @@ jobs:
|
||||
env:
|
||||
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
|
||||
# 2023.10.19
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry sdl2
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -95,7 +95,7 @@ jobs:
|
||||
# Enable it, when working on the installer.
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout IrrlichtMt
|
||||
run: |
|
||||
@ -145,7 +145,7 @@ jobs:
|
||||
env:
|
||||
TYPE: ${{matrix.type}}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
path: .\Package\
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -92,11 +92,8 @@ cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
CPackConfig.cmake
|
||||
CPackSourceConfig.cmake
|
||||
src/test_config.h
|
||||
src/cmake_config.h
|
||||
src/cmake_config_githash.h
|
||||
src/unittest/test_world/world.mt
|
||||
games/devtest/mods/testnodes/textures/testnodes_generated_*.png
|
||||
/locale/
|
||||
.directory
|
||||
*.cbp
|
||||
|
@ -17,6 +17,7 @@ read_globals = {
|
||||
"VoxelArea",
|
||||
"profiler",
|
||||
"Settings",
|
||||
"PerlinNoise", "PerlinNoiseMap",
|
||||
|
||||
string = {fields = {"split", "trim"}},
|
||||
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
|
||||
@ -70,7 +71,6 @@ files["builtin/mainmenu"] = {
|
||||
|
||||
read_globals = {
|
||||
"PLATFORM",
|
||||
"TOUCHSCREEN_GUI",
|
||||
},
|
||||
}
|
||||
|
||||
@ -81,9 +81,3 @@ files["builtin/common/tests"] = {
|
||||
"assert",
|
||||
},
|
||||
}
|
||||
|
||||
files["builtin/fstk"] = {
|
||||
read_globals = {
|
||||
"TOUCHSCREEN_GUI",
|
||||
},
|
||||
}
|
||||
|
@ -1,11 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Set policies up to 3.9 since we want to enable the IPO option
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.9)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.9)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(minetest)
|
||||
@ -44,6 +37,25 @@ set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||
set(BUILD_DOCUMENTATION TRUE CACHE BOOL "Build documentation")
|
||||
|
||||
set(DEFAULT_ENABLE_LTO TRUE)
|
||||
# by default don't enable on Debug builds to get faster builds
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(DEFAULT_ENABLE_LTO FALSE)
|
||||
endif()
|
||||
#### LTO testing list ####
|
||||
# - Linux: seems to work always
|
||||
# - win32/msvc: works
|
||||
# - win32/gcc: fails to link
|
||||
# - win32/clang: works
|
||||
# - macOS on x86: seems to be fine
|
||||
# - macOS on ARM: crashes, see <https://github.com/minetest/minetest/issues/14397>
|
||||
# Note: since CMake has no easy architecture detection disabling for Mac entirely
|
||||
#### ####
|
||||
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
|
||||
set(DEFAULT_ENABLE_LTO FALSE)
|
||||
endif()
|
||||
set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization")
|
||||
|
||||
set(DEFAULT_RUN_IN_PLACE FALSE)
|
||||
if(WIN32)
|
||||
set(DEFAULT_RUN_IN_PLACE TRUE)
|
||||
@ -140,7 +152,7 @@ elseif(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
||||
endif()
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
|
||||
set(TARGET_VER_S 1.9.0mt14)
|
||||
set(TARGET_VER_S 1.9.0mt15)
|
||||
string(REPLACE "mt" "." TARGET_VER ${TARGET_VER_S})
|
||||
if(IrrlichtMt_VERSION VERSION_LESS ${TARGET_VER})
|
||||
message(FATAL_ERROR "At least IrrlichtMt ${TARGET_VER_S} is required to build")
|
||||
@ -149,6 +161,20 @@ elseif(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_LTO OR CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||
if(lto_supported)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
message(STATUS "LTO/IPO is enabled")
|
||||
else()
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
message(STATUS "LTO/IPO was requested but is not supported by the compiler: ${lto_output}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "LTO/IPO is not enabled")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GCC_MINIMUM_VERSION}")
|
||||
message(FATAL_ERROR "Insufficient gcc version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
||||
|
13
Dockerfile
13
Dockerfile
@ -1,8 +1,8 @@
|
||||
ARG DOCKER_IMAGE=alpine:3.16
|
||||
ARG DOCKER_IMAGE=alpine:3.19
|
||||
FROM $DOCKER_IMAGE AS dev
|
||||
|
||||
ENV IRRLICHT_VERSION master
|
||||
ENV SPATIALINDEX_VERSION 1.9.3
|
||||
ENV SPATIALINDEX_VERSION master
|
||||
ENV LUAJIT_VERSION v2.1
|
||||
|
||||
RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \
|
||||
@ -10,7 +10,7 @@ RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \
|
||||
gmp-dev jsoncpp-dev ninja ca-certificates
|
||||
|
||||
WORKDIR /usr/src/
|
||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp && \
|
||||
cd prometheus-cpp && \
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
@ -29,9 +29,9 @@ RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||
cd /usr/src/ && \
|
||||
git clone --recursive https://luajit.org/git/luajit.git -b ${LUAJIT_VERSION} && \
|
||||
cd luajit && \
|
||||
make && make install && \
|
||||
make amalg && make install && \
|
||||
cd /usr/src/ && \
|
||||
git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
||||
git clone --depth=1 https://github.com/minetest/irrlicht -b ${IRRLICHT_VERSION} && \
|
||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
||||
|
||||
FROM dev as builder
|
||||
@ -56,13 +56,12 @@ RUN cmake -B build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SERVER=TRUE \
|
||||
-DENABLE_PROMETHEUS=TRUE \
|
||||
-DBUILD_UNITTESTS=FALSE \
|
||||
-DBUILD_UNITTESTS=FALSE -DBUILD_BENCHMARKS=FALSE \
|
||||
-DBUILD_CLIENT=FALSE \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
ARG DOCKER_IMAGE=alpine:3.16
|
||||
FROM $DOCKER_IMAGE AS runtime
|
||||
|
||||
RUN apk add --no-cache curl gmp libstdc++ libgcc libpq jsoncpp zstd-libs \
|
||||
|
@ -7,6 +7,7 @@ dofile(clientpath .. "register.lua")
|
||||
dofile(commonpath .. "after.lua")
|
||||
dofile(commonpath .. "mod_storage.lua")
|
||||
dofile(commonpath .. "chatcommands.lua")
|
||||
dofile(commonpath .. "information_formspecs.lua")
|
||||
dofile(clientpath .. "chatcommands.lua")
|
||||
dofile(clientpath .. "death_formspec.lua")
|
||||
dofile(clientpath .. "misc.lua")
|
||||
|
@ -89,7 +89,7 @@ local function do_help_cmd(name, param)
|
||||
if #args > 1 then
|
||||
return false, S("Too many arguments, try using just /help <command>")
|
||||
end
|
||||
local use_gui = INIT ~= "client" and core.get_player_by_name(name)
|
||||
local use_gui = INIT == "client" or core.get_player_by_name(name)
|
||||
use_gui = use_gui and not opts:find("t")
|
||||
|
||||
if #args == 0 and not use_gui then
|
||||
@ -163,8 +163,8 @@ end
|
||||
|
||||
if INIT == "client" then
|
||||
core.register_chatcommand("help", {
|
||||
params = core.gettext("[all | <cmd>]"),
|
||||
description = core.gettext("Get help for commands"),
|
||||
params = core.gettext("[all | <cmd>] [-t]"),
|
||||
description = core.gettext("Get help for commands (-t: output in chat)"),
|
||||
func = function(param)
|
||||
return do_help_cmd(nil, param)
|
||||
end,
|
||||
|
@ -61,15 +61,20 @@ local function build_chatcommands_formspec(name, sel, copy)
|
||||
for i, data in ipairs(mod_cmds) do
|
||||
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
|
||||
for j, cmds in ipairs(data[2]) do
|
||||
local has_priv = check_player_privs(name, cmds[2].privs)
|
||||
local has_priv = INIT == "client" or check_player_privs(name, cmds[2].privs)
|
||||
rows[#rows + 1] = ("%s,1,%s,%s"):format(
|
||||
has_priv and COLOR_GREEN or COLOR_GRAY,
|
||||
cmds[1], F(cmds[2].params))
|
||||
if sel == #rows then
|
||||
description = cmds[2].description
|
||||
if copy then
|
||||
core.chat_send_player(name, S("Command: @1 @2",
|
||||
core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params))
|
||||
local msg = S("Command: @1 @2",
|
||||
core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params)
|
||||
if INIT == "client" then
|
||||
core.display_chat_message(msg)
|
||||
else
|
||||
core.chat_send_player(name, msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -111,26 +116,46 @@ end
|
||||
|
||||
|
||||
-- DETAILED CHAT COMMAND INFORMATION
|
||||
if INIT == "client" then
|
||||
core.register_on_formspec_input(function(formname, fields)
|
||||
if formname ~= "__builtin:help_cmds" or fields.quit then
|
||||
return
|
||||
end
|
||||
|
||||
core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "__builtin:help_cmds" or fields.quit then
|
||||
return
|
||||
end
|
||||
local event = core.explode_table_event(fields.list)
|
||||
if event.type ~= "INV" then
|
||||
core.show_formspec("__builtin:help_cmds",
|
||||
build_chatcommands_formspec(nil, event.row, event.type == "DCL"))
|
||||
end
|
||||
end)
|
||||
else
|
||||
core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "__builtin:help_cmds" or fields.quit then
|
||||
return
|
||||
end
|
||||
|
||||
local event = core.explode_table_event(fields.list)
|
||||
if event.type ~= "INV" then
|
||||
local name = player:get_player_name()
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name, event.row, event.type == "DCL"))
|
||||
end
|
||||
end)
|
||||
local event = core.explode_table_event(fields.list)
|
||||
if event.type ~= "INV" then
|
||||
local name = player:get_player_name()
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name, event.row, event.type == "DCL"))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function core.show_general_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
if INIT == "client" then
|
||||
core.show_formspec("__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
else
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
end
|
||||
end
|
||||
|
||||
function core.show_privs_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_privs",
|
||||
build_privs_formspec(name))
|
||||
if INIT ~= "client" then
|
||||
function core.show_privs_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_privs",
|
||||
build_privs_formspec(name))
|
||||
end
|
||||
end
|
||||
|
61
builtin/emerge/env.lua
Normal file
61
builtin/emerge/env.lua
Normal file
@ -0,0 +1,61 @@
|
||||
-- Reimplementations of some environment function on vmanips, since this is
|
||||
-- what the emerge environment operates on
|
||||
|
||||
-- core.vmanip = <VoxelManip> -- set by C++
|
||||
|
||||
function core.set_node(pos, node)
|
||||
return core.vmanip:set_node_at(pos, node)
|
||||
end
|
||||
|
||||
function core.bulk_set_node(pos_list, node)
|
||||
local vm = core.vmanip
|
||||
local set_node_at = vm.set_node_at
|
||||
for _, pos in ipairs(pos_list) do
|
||||
if not set_node_at(vm, pos, node) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
core.add_node = core.set_node
|
||||
|
||||
-- we don't deal with metadata currently
|
||||
core.swap_node = core.set_node
|
||||
|
||||
function core.remove_node(pos)
|
||||
return core.vmanip:set_node_at(pos, {name="air"})
|
||||
end
|
||||
|
||||
function core.get_node(pos)
|
||||
return core.vmanip:get_node_at(pos)
|
||||
end
|
||||
|
||||
function core.get_node_or_nil(pos)
|
||||
local node = core.vmanip:get_node_at(pos)
|
||||
return node.name ~= "ignore" and node
|
||||
end
|
||||
|
||||
function core.get_perlin(seed, octaves, persist, spread)
|
||||
local params
|
||||
if type(seed) == "table" then
|
||||
params = table.copy(seed)
|
||||
else
|
||||
assert(type(seed) == "number")
|
||||
params = {
|
||||
seed = seed,
|
||||
octaves = octaves,
|
||||
persist = persist,
|
||||
spread = {x=spread, y=spread, z=spread},
|
||||
}
|
||||
end
|
||||
params.seed = core.get_seed(params.seed) -- add mapgen seed
|
||||
return PerlinNoise(params)
|
||||
end
|
||||
|
||||
|
||||
function core.get_perlin_map(params, size)
|
||||
local params2 = table.copy(params)
|
||||
params2.seed = core.get_seed(params.seed) -- add mapgen seed
|
||||
return PerlinNoiseMap(params2, size)
|
||||
end
|
21
builtin/emerge/init.lua
Normal file
21
builtin/emerge/init.lua
Normal file
@ -0,0 +1,21 @@
|
||||
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
|
||||
local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM
|
||||
local epath = core.get_builtin_path() .. "emerge" .. DIR_DELIM
|
||||
|
||||
local builtin_shared = {}
|
||||
|
||||
-- Import parts shared with "game" environment
|
||||
dofile(gamepath .. "constants.lua")
|
||||
assert(loadfile(commonpath .. "item_s.lua"))(builtin_shared)
|
||||
dofile(gamepath .. "misc_s.lua")
|
||||
dofile(gamepath .. "features.lua")
|
||||
dofile(gamepath .. "voxelarea.lua")
|
||||
|
||||
-- Now for our own stuff
|
||||
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
|
||||
assert(loadfile(epath .. "register.lua"))(builtin_shared)
|
||||
dofile(epath .. "env.lua")
|
||||
|
||||
builtin_shared.cache_content_ids()
|
||||
|
||||
core.log("info", "Initialized emerge Lua environment")
|
54
builtin/emerge/register.lua
Normal file
54
builtin/emerge/register.lua
Normal file
@ -0,0 +1,54 @@
|
||||
local builtin_shared = ...
|
||||
|
||||
-- Copy all the registration tables over
|
||||
do
|
||||
local all = assert(core.transferred_globals)
|
||||
core.transferred_globals = nil
|
||||
|
||||
all.registered_nodes = {}
|
||||
all.registered_craftitems = {}
|
||||
all.registered_tools = {}
|
||||
for k, v in pairs(all.registered_items) do
|
||||
-- Disable further modification
|
||||
setmetatable(v, {__newindex = {}})
|
||||
-- Reassemble the other tables
|
||||
if v.type == "node" then
|
||||
getmetatable(v).__index = all.nodedef_default
|
||||
all.registered_nodes[k] = v
|
||||
elseif v.type == "craft" then
|
||||
getmetatable(v).__index = all.craftitemdef_default
|
||||
all.registered_craftitems[k] = v
|
||||
elseif v.type == "tool" then
|
||||
getmetatable(v).__index = all.tooldef_default
|
||||
all.registered_tools[k] = v
|
||||
else
|
||||
getmetatable(v).__index = all.noneitemdef_default
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(all) do
|
||||
core[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- For tables that are indexed by item name:
|
||||
-- If table[X] does not exist, default to table[core.registered_aliases[X]]
|
||||
local alias_metatable = {
|
||||
__index = function(t, name)
|
||||
return rawget(t, core.registered_aliases[name])
|
||||
end
|
||||
}
|
||||
setmetatable(core.registered_items, alias_metatable)
|
||||
setmetatable(core.registered_nodes, alias_metatable)
|
||||
setmetatable(core.registered_craftitems, alias_metatable)
|
||||
setmetatable(core.registered_tools, alias_metatable)
|
||||
|
||||
--
|
||||
-- Callbacks
|
||||
--
|
||||
|
||||
local make_registration = builtin_shared.make_registration
|
||||
|
||||
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
|
||||
core.registered_on_generateds, core.register_on_generated = make_registration()
|
||||
core.registered_on_shutdown, core.register_on_shutdown = make_registration()
|
@ -18,7 +18,9 @@
|
||||
|
||||
|
||||
local BASE_SPACING = 0.1
|
||||
local SCROLL_BTN_WIDTH = TOUCHSCREEN_GUI and 0.8 or 0.5
|
||||
local function get_scroll_btn_width()
|
||||
return core.settings:get_bool("enable_touch") and 0.8 or 0.5
|
||||
end
|
||||
|
||||
local function buttonbar_formspec(self)
|
||||
if self.hidden then
|
||||
@ -39,7 +41,7 @@ local function buttonbar_formspec(self)
|
||||
|
||||
-- The number of buttons per page is always calculated as if the scroll
|
||||
-- buttons were visible.
|
||||
local avail_space = self.size.x - 2*BASE_SPACING - 2*SCROLL_BTN_WIDTH
|
||||
local avail_space = self.size.x - 2*BASE_SPACING - 2*get_scroll_btn_width()
|
||||
local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
|
||||
|
||||
self.num_pages = math.ceil(#self.buttons / btns_per_page)
|
||||
@ -55,7 +57,7 @@ local function buttonbar_formspec(self)
|
||||
|
||||
local btn_start_x = self.pos.x + btn_spacing
|
||||
if show_scroll_btns then
|
||||
btn_start_x = btn_start_x + BASE_SPACING + SCROLL_BTN_WIDTH
|
||||
btn_start_x = btn_start_x + BASE_SPACING + get_scroll_btn_width()
|
||||
end
|
||||
|
||||
for i = first_btn, first_btn + btns_per_page - 1 do
|
||||
@ -80,7 +82,7 @@ local function buttonbar_formspec(self)
|
||||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
local btn_next_pos = {
|
||||
x = self.pos.x + self.size.x - BASE_SPACING - SCROLL_BTN_WIDTH,
|
||||
x = self.pos.x + self.size.x - BASE_SPACING - get_scroll_btn_width(),
|
||||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
|
||||
@ -88,11 +90,11 @@ local function buttonbar_formspec(self)
|
||||
self.btn_prev_name, self.btn_next_name))
|
||||
|
||||
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]",
|
||||
btn_prev_pos.x, btn_prev_pos.y, SCROLL_BTN_WIDTH, btn_size,
|
||||
btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size,
|
||||
self.btn_prev_name))
|
||||
|
||||
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;>]",
|
||||
btn_next_pos.x, btn_next_pos.y, SCROLL_BTN_WIDTH, btn_size,
|
||||
btn_next_pos.x, btn_next_pos.y, get_scroll_btn_width(), btn_size,
|
||||
self.btn_next_name))
|
||||
end
|
||||
|
||||
|
@ -79,6 +79,9 @@ core.register_entity(":__builtin:falling_node", {
|
||||
-- Cache whether we're supposed to float on water
|
||||
self.floats = core.get_item_group(node.name, "float") ~= 0
|
||||
|
||||
-- Save liquidtype for falling water
|
||||
self.liquidtype = def.liquidtype
|
||||
|
||||
-- Set entity visuals
|
||||
if def.drawtype == "torchlike" or def.drawtype == "signlike" then
|
||||
local textures
|
||||
@ -294,9 +297,17 @@ core.register_entity(":__builtin:falling_node", {
|
||||
end
|
||||
|
||||
-- Decide if we're replacing the node or placing on top
|
||||
-- This condition is very similar to the check in core.check_single_for_falling(p)
|
||||
local np = vector.copy(bcp)
|
||||
if bcd and bcd.buildable_to and
|
||||
(not self.floats or bcd.liquidtype == "none") then
|
||||
if bcd and bcd.buildable_to
|
||||
and -- Take "float" group into consideration:
|
||||
(
|
||||
-- Fall through non-liquids
|
||||
not self.floats or bcd.liquidtype == "none" or
|
||||
-- Only let sources fall through flowing liquids
|
||||
(self.floats and self.liquidtype ~= "none" and bcd.liquidtype ~= "source")
|
||||
) then
|
||||
|
||||
core.remove_node(bcp)
|
||||
else
|
||||
np.y = np.y + 1
|
||||
@ -307,7 +318,7 @@ core.register_entity(":__builtin:falling_node", {
|
||||
local nd = core.registered_nodes[n2.name]
|
||||
-- If it's not air or liquid, remove node and replace it with
|
||||
-- it's drops
|
||||
if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then
|
||||
if n2.name ~= "air" and (not nd or nd.liquidtype ~= "source") then
|
||||
if nd and nd.buildable_to == false then
|
||||
nd.on_dig(np, n2, nil)
|
||||
-- If it's still there, it might be protected
|
||||
@ -555,11 +566,19 @@ function core.check_single_for_falling(p)
|
||||
local success, _ = convert_to_falling_node(p, n)
|
||||
return success
|
||||
end
|
||||
local d_falling = core.registered_nodes[n.name]
|
||||
local do_float = core.get_item_group(n.name, "float") > 0
|
||||
-- Otherwise only if the bottom node is considered "fall through"
|
||||
if not same and
|
||||
(not d_bottom.walkable or d_bottom.buildable_to) and
|
||||
(core.get_item_group(n.name, "float") == 0 or
|
||||
d_bottom.liquidtype == "none") then
|
||||
(not d_bottom.walkable or d_bottom.buildable_to)
|
||||
and -- Take "float" group into consideration:
|
||||
(
|
||||
-- Fall through non-liquids
|
||||
not do_float or d_bottom.liquidtype == "none" or
|
||||
-- Only let sources fall through flowing liquids
|
||||
(do_float and d_falling.liquidtype == "source" and d_bottom.liquidtype ~= "source")
|
||||
) then
|
||||
|
||||
local success, _ = convert_to_falling_node(p, n)
|
||||
return success
|
||||
end
|
||||
|
@ -35,6 +35,8 @@ core.features = {
|
||||
wallmounted_rotate = true,
|
||||
item_specific_pointabilities = true,
|
||||
blocking_pointability_type = true,
|
||||
dynamic_add_media_startup = true,
|
||||
dynamic_add_media_filepath = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
@ -237,8 +237,8 @@ end
|
||||
core.dynamic_media_callbacks = {}
|
||||
|
||||
|
||||
-- Transfer of certain globals into async environment
|
||||
-- see builtin/async/game.lua for the other side
|
||||
-- Transfer of certain globals into seconday Lua environments
|
||||
-- see builtin/async/game.lua or builtin/emerge/register.lua for the unpacking
|
||||
|
||||
local function copy_filtering(t, seen)
|
||||
if type(t) == "userdata" or type(t) == "function" then
|
||||
@ -261,6 +261,9 @@ function core.get_globals_to_transfer()
|
||||
local all = {
|
||||
registered_items = copy_filtering(core.registered_items),
|
||||
registered_aliases = core.registered_aliases,
|
||||
registered_biomes = core.registered_biomes,
|
||||
registered_ores = core.registered_ores,
|
||||
registered_decorations = core.registered_decorations,
|
||||
|
||||
nodedef_default = copy_filtering(core.nodedef_default),
|
||||
craftitemdef_default = copy_filtering(core.craftitemdef_default),
|
||||
|
@ -24,6 +24,13 @@ local bar_definitions = {
|
||||
size = {x = 24, y = 24},
|
||||
offset = {x = 25, y= -(48 + 24 + 16)},
|
||||
},
|
||||
minimap = {
|
||||
type = "minimap",
|
||||
position = {x = 1, y = 0},
|
||||
alignment = {x = -1, y = 1},
|
||||
offset = {x = -10, y = 10},
|
||||
size = {x = 256 , y = 256},
|
||||
},
|
||||
}
|
||||
|
||||
local hud_ids = {}
|
||||
@ -92,6 +99,16 @@ local function update_builtin_statbars(player)
|
||||
end, name, hud.id_breathbar)
|
||||
hud.id_breathbar = nil
|
||||
end
|
||||
|
||||
-- Don't add a minimap for clients which already have it hardcoded in C++.
|
||||
local show_minimap = flags.minimap and
|
||||
minetest.get_player_information(name).protocol_version >= 44
|
||||
if show_minimap and not hud.id_minimap then
|
||||
hud.id_minimap = player:hud_add(bar_definitions.minimap)
|
||||
elseif not show_minimap and hud.id_minimap then
|
||||
player:hud_remove(hud.id_minimap)
|
||||
hud.id_minimap = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function cleanup_builtin_statbars(player)
|
||||
@ -138,8 +155,7 @@ local function player_event_handler(player,eventname)
|
||||
end
|
||||
|
||||
function core.hud_replace_builtin(hud_name, definition)
|
||||
if type(definition) ~= "table" or
|
||||
(definition.type or definition.hud_elem_type) ~= "statbar" then
|
||||
if type(definition) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
@ -175,6 +191,20 @@ function core.hud_replace_builtin(hud_name, definition)
|
||||
return true
|
||||
end
|
||||
|
||||
if hud_name == "minimap" then
|
||||
bar_definitions.minimap = definition
|
||||
|
||||
for name, ids in pairs(hud_ids) do
|
||||
local player = core.get_player_by_name(name)
|
||||
if player and ids.id_minimap then
|
||||
player:hud_remove(ids.id_minimap)
|
||||
ids.id_minimap = nil
|
||||
update_builtin_statbars(player)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -31,8 +31,6 @@ minetest = core
|
||||
|
||||
-- Load other files
|
||||
local scriptdir = core.get_builtin_path()
|
||||
local gamepath = scriptdir .. "game" .. DIR_DELIM
|
||||
local clientpath = scriptdir .. "client" .. DIR_DELIM
|
||||
local commonpath = scriptdir .. "common" .. DIR_DELIM
|
||||
local asyncpath = scriptdir .. "async" .. DIR_DELIM
|
||||
|
||||
@ -42,7 +40,7 @@ dofile(commonpath .. "serialize.lua")
|
||||
dofile(commonpath .. "misc_helpers.lua")
|
||||
|
||||
if INIT == "game" then
|
||||
dofile(gamepath .. "init.lua")
|
||||
dofile(scriptdir .. "game" .. DIR_DELIM .. "init.lua")
|
||||
assert(not core.get_http_api)
|
||||
elseif INIT == "mainmenu" then
|
||||
local mm_script = core.settings:get("main_menu_script")
|
||||
@ -67,7 +65,9 @@ elseif INIT == "async" then
|
||||
elseif INIT == "async_game" then
|
||||
dofile(asyncpath .. "game.lua")
|
||||
elseif INIT == "client" then
|
||||
dofile(clientpath .. "init.lua")
|
||||
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
|
||||
elseif INIT == "emerge" then
|
||||
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
|
||||
else
|
||||
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
|
||||
end
|
||||
|
@ -154,7 +154,9 @@ local function start_install(package, reason)
|
||||
|
||||
if conf_path then
|
||||
local conf = Settings(conf_path)
|
||||
conf:set("title", package.title)
|
||||
if not conf:get("title") then
|
||||
conf:set("title", package.title)
|
||||
end
|
||||
if not name_is_title then
|
||||
conf:set("name", package.name)
|
||||
end
|
||||
@ -642,8 +644,21 @@ local function fetch_pkgs()
|
||||
end
|
||||
end
|
||||
|
||||
local languages
|
||||
local current_language = core.get_language()
|
||||
if current_language ~= "" then
|
||||
languages = { current_language, "en;q=0.8" }
|
||||
else
|
||||
languages = { "en" }
|
||||
end
|
||||
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({ url = url })
|
||||
local response = http.fetch_sync({
|
||||
url = url,
|
||||
extra_headers = {
|
||||
"Accept-Language: " .. table.concat(languages, ", ")
|
||||
},
|
||||
})
|
||||
if not response.succeeded then
|
||||
return
|
||||
end
|
||||
@ -898,7 +913,7 @@ local function get_info_formspec(text)
|
||||
return table.concat({
|
||||
"formspec_version[6]",
|
||||
"size[15.75,9.5]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
|
||||
"label[4,4.35;", text, "]",
|
||||
"container[0,", H - 0.8 - 0.375, "]",
|
||||
@ -928,7 +943,7 @@ function store.get_formspec(dlgdata)
|
||||
local formspec = {
|
||||
"formspec_version[6]",
|
||||
"size[15.75,9.5]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
|
||||
"style[status,downloading,queued;border=false]",
|
||||
|
||||
@ -1175,8 +1190,8 @@ end
|
||||
|
||||
function store.handle_events(event)
|
||||
if event == "DialogShow" then
|
||||
-- On mobile, don't show the "MINETEST" header behind the dialog.
|
||||
mm_game_theme.set_engine(TOUCHSCREEN_GUI)
|
||||
-- On touchscreen, don't show the "MINETEST" header behind the dialog.
|
||||
mm_game_theme.set_engine(core.settings:get_bool("enable_touch"))
|
||||
|
||||
-- If the store is already loaded, auto-install packages here.
|
||||
do_auto_install()
|
||||
|
@ -150,6 +150,8 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
|
||||
toadd.virtual_path = mod_virtual_path
|
||||
toadd.type = "mod"
|
||||
|
||||
pkgmgr.update_translations({ toadd })
|
||||
|
||||
-- Check modpack.txt
|
||||
-- Note: modpack.conf is already checked above
|
||||
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
|
||||
@ -189,6 +191,8 @@ function pkgmgr.get_texture_packs()
|
||||
load_texture_packs(txtpath_system, retval)
|
||||
end
|
||||
|
||||
pkgmgr.update_translations(retval)
|
||||
|
||||
table.sort(retval, function(a, b)
|
||||
return a.title:lower() < b.title:lower()
|
||||
end)
|
||||
@ -775,6 +779,29 @@ function pkgmgr.update_gamelist()
|
||||
table.sort(pkgmgr.games, function(a, b)
|
||||
return a.title:lower() < b.title:lower()
|
||||
end)
|
||||
pkgmgr.update_translations(pkgmgr.games)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.update_translations(list)
|
||||
for _, item in ipairs(list) do
|
||||
local info = core.get_content_info(item.path)
|
||||
assert(info.path)
|
||||
assert(info.textdomain)
|
||||
|
||||
assert(not item.is_translated)
|
||||
item.is_translated = true
|
||||
|
||||
if info.title and info.title ~= "" then
|
||||
item.title = core.get_content_translation(info.path, info.textdomain,
|
||||
core.translate(info.textdomain, info.title))
|
||||
end
|
||||
|
||||
if info.description and info.description ~= "" then
|
||||
item.description = core.get_content_translation(info.path, info.textdomain,
|
||||
core.translate(info.textdomain, info.description))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -70,6 +70,8 @@ local flag_checkboxes = {
|
||||
{ "trees", fgettext("Trees and jungle grass") },
|
||||
{ "flat", fgettext("Flat terrain") },
|
||||
{ "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") },
|
||||
{ "temples", fgettext("Desert temples"),
|
||||
fgettext("Different dungeon variant generated in desert biomes (only if dungeons enabled)") },
|
||||
-- Biome settings are in mgv6_biomes below
|
||||
},
|
||||
}
|
||||
@ -279,7 +281,7 @@ local function create_world_formspec(dialogdata)
|
||||
end
|
||||
|
||||
local retval =
|
||||
"size[12.25,7,true]" ..
|
||||
"size[12.25,7.4,true]" ..
|
||||
|
||||
-- Left side
|
||||
"container[0,0]"..
|
||||
@ -321,8 +323,10 @@ local function create_world_formspec(dialogdata)
|
||||
"container_end[]"..
|
||||
|
||||
-- Menu buttons
|
||||
"button[3.25,6.5;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
|
||||
"button[6.25,6.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
|
||||
"container[0,6.9]"..
|
||||
"button[3.25,0;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
|
||||
"button[6.25,0;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" ..
|
||||
"container_end[]"
|
||||
|
||||
return retval
|
||||
|
||||
|
@ -316,8 +316,8 @@ local function check_requirements(name, requires)
|
||||
local special = {
|
||||
android = PLATFORM == "Android",
|
||||
desktop = PLATFORM ~= "Android",
|
||||
touchscreen_gui = TOUCHSCREEN_GUI,
|
||||
keyboard_mouse = not TOUCHSCREEN_GUI,
|
||||
touchscreen_gui = core.settings:get_bool("enable_touch"),
|
||||
keyboard_mouse = not core.settings:get_bool("enable_touch"),
|
||||
shaders_support = shaders_support,
|
||||
shaders = core.settings:get_bool("enable_shaders") and shaders_support,
|
||||
opengl = video_driver == "opengl",
|
||||
@ -449,13 +449,14 @@ local function get_formspec(dialogdata)
|
||||
|
||||
local extra_h = 1 -- not included in tabsize.height
|
||||
local tabsize = {
|
||||
width = TOUCHSCREEN_GUI and 16.5 or 15.5,
|
||||
height = TOUCHSCREEN_GUI and (10 - extra_h) or 12,
|
||||
width = core.settings:get_bool("enable_touch") and 16.5 or 15.5,
|
||||
height = core.settings:get_bool("enable_touch") and (10 - extra_h) or 12,
|
||||
}
|
||||
|
||||
local scrollbar_w = TOUCHSCREEN_GUI and 0.6 or 0.4
|
||||
local scrollbar_w = core.settings:get_bool("enable_touch") and 0.6 or 0.4
|
||||
|
||||
local left_pane_width = TOUCHSCREEN_GUI and 4.5 or 4.25
|
||||
local left_pane_width = core.settings:get_bool("enable_touch") and 4.5 or 4.25
|
||||
local left_pane_padding = 0.25
|
||||
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
|
||||
|
||||
local back_w = 3
|
||||
@ -468,7 +469,7 @@ local function get_formspec(dialogdata)
|
||||
local fs = {
|
||||
"formspec_version[6]",
|
||||
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "",
|
||||
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "",
|
||||
"bgcolor[#0000]",
|
||||
|
||||
-- HACK: this is needed to allow resubmitting the same formspec
|
||||
@ -516,9 +517,9 @@ local function get_formspec(dialogdata)
|
||||
y = y + 0.82
|
||||
end
|
||||
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
|
||||
y, left_pane_width, other_page.id == page_id and "#467832FF" or "#3339")
|
||||
y, left_pane_width-left_pane_padding, other_page.id == page_id and "#467832FF" or "#3339")
|
||||
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
|
||||
:format(y, left_pane_width, other_page.id, fgettext(other_page.title))
|
||||
:format(y, left_pane_width-left_pane_padding, other_page.id, fgettext(other_page.title))
|
||||
y = y + 0.82
|
||||
end
|
||||
|
||||
@ -641,11 +642,22 @@ local function buttonhandler(this, fields)
|
||||
local value = core.is_yes(fields.show_advanced)
|
||||
core.settings:set_bool("show_advanced", value)
|
||||
write_settings_early()
|
||||
end
|
||||
|
||||
-- enable_touch is a checkbox in a setting component. We handle this
|
||||
-- setting differently so we can hide/show pages using the next if-statement
|
||||
if fields.enable_touch ~= nil then
|
||||
local value = core.is_yes(fields.enable_touch)
|
||||
core.settings:set_bool("enable_touch", value)
|
||||
write_settings_early()
|
||||
end
|
||||
|
||||
if fields.show_advanced ~= nil or fields.enable_touch ~= nil then
|
||||
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
dialogdata.components = nil
|
||||
|
||||
if not filtered_page_by_id[dialogdata.page_id] then
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
|
@ -114,12 +114,13 @@ local function get_formspec(tabview, name, tabdata)
|
||||
modscreenshot = defaulttexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
local desc = fgettext("No package description available")
|
||||
if info.description and info.description:trim() ~= "" then
|
||||
desc = core.formspec_escape(info.description)
|
||||
if selected_pkg.description and selected_pkg.description:trim() ~= "" then
|
||||
desc = core.formspec_escape(selected_pkg.description)
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
|
||||
local title_and_name
|
||||
if selected_pkg.type == "game" then
|
||||
title_and_name = selected_pkg.name
|
||||
|
@ -94,7 +94,7 @@ function singleplayer_refresh_gamebar()
|
||||
|
||||
local btnbar = buttonbar_create(
|
||||
"game_button_bar",
|
||||
TOUCHSCREEN_GUI and {x = 0, y = 7.25} or {x = 0, y = 7.475},
|
||||
core.settings:get_bool("enable_touch") and {x = 0, y = 7.25} or {x = 0, y = 7.475},
|
||||
{x = 15.5, y = 1.25},
|
||||
"#000000",
|
||||
game_buttonbar_button_handler)
|
||||
|
@ -168,6 +168,11 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
|
||||
|
||||
[*Touchscreen]
|
||||
|
||||
# Enables touchscreen mode, allowing you to play the game with a touchscreen.
|
||||
#
|
||||
# Requires: !android
|
||||
enable_touch (Enable touchscreen) bool true
|
||||
|
||||
# The length in pixels it takes for touchscreen interaction to start.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
@ -422,7 +427,8 @@ anisotropic_filter (Anisotropic filtering) bool false
|
||||
#
|
||||
# * None - No antialiasing (default)
|
||||
#
|
||||
# * FSAA - Hardware-provided full-screen antialiasing (incompatible with shaders)
|
||||
# * FSAA - Hardware-provided full-screen antialiasing
|
||||
# (incompatible with Post Processing and Undersampling)
|
||||
# A.K.A multi-sample antialiasing (MSAA)
|
||||
# Smoothens out block edges but does not affect the insides of textures.
|
||||
# A restart is required to change this option.
|
||||
@ -577,12 +583,17 @@ shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 -60.0 60.0
|
||||
|
||||
[**Post Processing]
|
||||
|
||||
# Enables the post processing pipeline.
|
||||
#
|
||||
# Requires: shaders
|
||||
enable_post_processing (Enable Post Processing) bool true
|
||||
|
||||
# Enables Hable's 'Uncharted 2' filmic tone mapping.
|
||||
# Simulates the tone curve of photographic film and how this approximates the
|
||||
# appearance of high dynamic range images. Mid-range contrast is slightly
|
||||
# enhanced, highlights and shadows are gradually compressed.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
tone_mapping (Filmic tone mapping) bool false
|
||||
|
||||
# Enable automatic exposure correction
|
||||
@ -590,14 +601,14 @@ tone_mapping (Filmic tone mapping) bool false
|
||||
# automatically adjust to the brightness of the scene,
|
||||
# simulating the behavior of human eye.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
enable_auto_exposure (Enable Automatic Exposure) bool false
|
||||
|
||||
# Set the exposure compensation in EV units.
|
||||
# Value of 0.0 (default) means no exposure compensation.
|
||||
# Range: from -1 to 1.0
|
||||
#
|
||||
# Requires: shaders, enable_auto_exposure
|
||||
# Requires: shaders, enable_post_processing, enable_auto_exposure
|
||||
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||
|
||||
# Apply dithering to reduce color banding artifacts.
|
||||
@ -608,7 +619,7 @@ exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||
# With OpenGL ES, dithering only works if the shader supports high
|
||||
# floating-point precision and it may have a higher performance impact.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
debanding (Enable Debanding) bool true
|
||||
|
||||
[**Bloom]
|
||||
@ -616,7 +627,7 @@ debanding (Enable Debanding) bool true
|
||||
# Set to true to enable bloom effect.
|
||||
# Bright colors will bleed over the neighboring objects.
|
||||
#
|
||||
# Requires: shaders
|
||||
# Requires: shaders, enable_post_processing
|
||||
enable_bloom (Enable Bloom) bool false
|
||||
|
||||
# Set to true to render debugging breakdown of the bloom effect.
|
||||
@ -624,32 +635,32 @@ enable_bloom (Enable Bloom) bool false
|
||||
# top-left - processed base image, top-right - final image
|
||||
# bottom-left - raw base image, bottom-right - bloom texture.
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
enable_bloom_debug (Enable Bloom Debug) bool false
|
||||
|
||||
# Defines how much bloom is applied to the rendered image
|
||||
# Smaller values make bloom more subtle
|
||||
# Range: from 0.01 to 1.0, default: 0.05
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_intensity (Bloom Intensity) float 0.05 0.01 1.0
|
||||
|
||||
# Defines the magnitude of bloom overexposure.
|
||||
# Range: from 0.1 to 10.0, default: 1.0
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0
|
||||
|
||||
# Logical value that controls how far the bloom effect spreads
|
||||
# from the bright objects.
|
||||
# Range: from 0.1 to 8, default: 1
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
bloom_radius (Bloom Radius) float 1 0.1 8
|
||||
|
||||
# Set to true to enable volumetric lighting effect (a.k.a. "Godrays").
|
||||
#
|
||||
# Requires: shaders, enable_bloom
|
||||
# Requires: shaders, enable_post_processing, enable_bloom
|
||||
enable_volumetric_lighting (Volumetric lighting) bool false
|
||||
|
||||
[*Audio]
|
||||
@ -1089,7 +1100,8 @@ mgv5_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2
|
||||
# The 'snowbiomes' flag enables the new 5 biome system.
|
||||
# When the 'snowbiomes' flag is enabled jungles are automatically enabled and
|
||||
# the 'jungles' flag is ignored.
|
||||
mgv6_spflags (Mapgen V6 specific flags) flags jungles,biomeblend,mudflow,snowbiomes,noflat,trees jungles,biomeblend,mudflow,snowbiomes,flat,trees,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees
|
||||
# The 'temples' flag disables generation of desert temples. Normal dungeons will appear instead.
|
||||
mgv6_spflags (Mapgen V6 specific flags) flags jungles,biomeblend,mudflow,snowbiomes,noflat,trees,temples jungles,biomeblend,mudflow,snowbiomes,flat,trees,temples,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees,notemples
|
||||
|
||||
# Deserts occur when np_biome exceeds this value.
|
||||
# When the 'snowbiomes' flag is enabled, this is ignored.
|
||||
@ -2028,9 +2040,10 @@ ask_reconnect_on_crash (Ask to reconnect after crash) bool false
|
||||
|
||||
[**Server/Env Performance]
|
||||
|
||||
# Length of a server tick and the interval at which objects are generally updated over
|
||||
# network, stated in seconds.
|
||||
dedicated_server_step (Dedicated server step) float 0.09 0.0
|
||||
# Length of a server tick (the interval at which everything is generally updated),
|
||||
# stated in seconds.
|
||||
# Does not apply to sessions hosted from the client menu.
|
||||
dedicated_server_step (Dedicated server step) float 0.09 0.0 1.0
|
||||
|
||||
# Whether players are shown to clients without any range limit.
|
||||
# Deprecated, use the setting player_transfer_distance instead.
|
||||
@ -2100,8 +2113,7 @@ liquid_update (Liquid update tick) float 1.0 0.001
|
||||
# At this distance the server will aggressively optimize which blocks are sent to
|
||||
# clients.
|
||||
# Small values potentially improve performance a lot, at the expense of visible
|
||||
# rendering glitches (some blocks will not be rendered under water and in caves,
|
||||
# as well as sometimes on land).
|
||||
# rendering glitches (some blocks might not be rendered correctly in caves).
|
||||
# Setting this to a value greater than max_block_send_distance disables this
|
||||
# optimization.
|
||||
# Stated in MapBlocks (16 nodes).
|
||||
|
@ -28,6 +28,7 @@ General options and their default values:
|
||||
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
||||
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
|
||||
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
|
||||
ENABLE_LTO=<varies> - Build with IPO/LTO optimizations (smaller and more efficient than regular build)
|
||||
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
|
||||
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
|
||||
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||
@ -37,7 +38,7 @@ General options and their default values:
|
||||
INSTALL_DEVTEST=FALSE - Whether the Development Test game should be installed alongside Minetest
|
||||
USE_GPROF=FALSE - Enable profiling using GProf
|
||||
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
||||
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
|
||||
ENABLE_TOUCH=FALSE - Enable touchscreen support by default (requires support by IrrlichtMt)
|
||||
|
||||
Library specific options:
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
Install dependencies with homebrew:
|
||||
|
||||
```
|
||||
brew install cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd gettext
|
||||
brew install cmake freetype gettext gmp hiredis jpeg-turbo jsoncpp leveldb libogg libpng libvorbis luajit zstd gettext
|
||||
```
|
||||
|
||||
## Download
|
||||
|
@ -14,7 +14,7 @@ It is highly recommended to use vcpkg as package manager.
|
||||
|
||||
After you successfully built vcpkg you can easily install the required libraries:
|
||||
```powershell
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry gettext sdl2 --triplet x64-windows
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp gettext sdl2 --triplet x64-windows
|
||||
```
|
||||
|
||||
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt`:
|
||||
|
309
doc/lua_api.md
309
doc/lua_api.md
@ -61,7 +61,8 @@ The game directory can contain the following files:
|
||||
* `game.conf`, with the following keys:
|
||||
* `title`: Required, a human-readable title to address the game, e.g. `title = Minetest Game`.
|
||||
* `name`: (Deprecated) same as title.
|
||||
* `description`: Short description to be shown in the content tab
|
||||
* `description`: Short description to be shown in the content tab.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `allowed_mapgens = <comma-separated mapgens>`
|
||||
e.g. `allowed_mapgens = v5,v6,flat`
|
||||
Mapgens not in this list are removed from the list of mapgens for the
|
||||
@ -87,10 +88,11 @@ The game directory can contain the following files:
|
||||
`enable_damage`, `creative_mode`, `enable_server`.
|
||||
* `map_persistent`: Specifies whether newly created worlds should use
|
||||
a persistent map backend. Defaults to `true` (= "sqlite3")
|
||||
* `author`: The author of the game. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is
|
||||
an internal ID used to track versions.
|
||||
* `textdomain`: Textdomain used to translate description. Defaults to game id.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `minetest.conf`:
|
||||
Used to set default settings when running this game.
|
||||
* `settingtypes.txt`:
|
||||
@ -156,13 +158,14 @@ The file is a key-value store of modpack details.
|
||||
|
||||
* `name`: The modpack name. Allows Minetest to determine the modpack name even
|
||||
if the folder is wrongly named.
|
||||
* `title`: A human-readable title to address the modpack. See [Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main
|
||||
menu.
|
||||
* `author`: The author of the modpack. It only appears when downloaded from
|
||||
ContentDB.
|
||||
menu. See [Translating content meta](#translating-content-meta).
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
internal ID used to track versions.
|
||||
* `title`: A human-readable title to address the modpack.
|
||||
* `textdomain`: Textdomain used to translate title and description. Defaults to modpack name.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
|
||||
Note: to support 0.4.x, please also create an empty modpack.txt file.
|
||||
|
||||
@ -201,17 +204,18 @@ A `Settings` file that provides meta information about the mod.
|
||||
|
||||
* `name`: The mod name. Allows Minetest to determine the mod name even if the
|
||||
folder is wrongly named.
|
||||
* `title`: A human-readable title to address the mod. See [Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main
|
||||
menu.
|
||||
menu. See [Translating content meta](#translating-content-meta).
|
||||
* `depends`: A comma separated list of dependencies. These are mods that must be
|
||||
loaded before this mod.
|
||||
* `optional_depends`: A comma separated list of optional dependencies.
|
||||
Like a dependency, but no error if the mod doesn't exist.
|
||||
* `author`: The author of the mod. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
internal ID used to track versions.
|
||||
* `title`: A human-readable title to address the mod.
|
||||
* `textdomain`: Textdomain used to translate title and description. Defaults to modname.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
|
||||
### `screenshot.png`
|
||||
|
||||
@ -506,8 +510,8 @@ Example:
|
||||
|
||||
* `<w>`: width
|
||||
* `<h>`: height
|
||||
* `<x>`: x position
|
||||
* `<y>`: y position
|
||||
* `<x>`: x position, negative numbers allowed
|
||||
* `<y>`: y position, negative numbers allowed
|
||||
* `<file>`: texture to combine
|
||||
|
||||
Creates a texture of size `<w>` times `<h>` and blits the listed files to their
|
||||
@ -613,13 +617,13 @@ Creates an inventorycube with `grass.png`, `dirt.png^grass_side.png` and
|
||||
* `<y>`: y position
|
||||
* `<color>`: a `ColorString`.
|
||||
|
||||
Creates a texture of the given size and color, optionally with an <x>,<y>
|
||||
Creates a texture of the given size and color, optionally with an `<x>,<y>`
|
||||
position. An alpha value may be specified in the `Colorstring`.
|
||||
|
||||
The optional <x>,<y> position is only used if the [fill is being overlaid
|
||||
The optional `<x>,<y>` position is only used if the `[fill` is being overlaid
|
||||
onto another texture with '^'.
|
||||
|
||||
When [fill is overlaid onto another texture it will not upscale or change
|
||||
When `[fill` is overlaid onto another texture it will not upscale or change
|
||||
the resolution of the texture, the base texture will determine the output
|
||||
resolution.
|
||||
|
||||
@ -2167,6 +2171,8 @@ to games.
|
||||
Negative damage values are discarded as no damage.
|
||||
* `falling_node`: if there is no walkable block under the node it will fall
|
||||
* `float`: the node will not fall through liquids (`liquidtype ~= "none"`)
|
||||
* A liquid source with `groups = {falling_node = 1, float = 1}`
|
||||
will fall through flowing liquids.
|
||||
* `level`: Can be used to give an additional sense of progression in the game.
|
||||
* A larger level will cause e.g. a weapon of a lower level make much less
|
||||
damage, and get worn out much faster, or not be able to get drops
|
||||
@ -4133,6 +4139,46 @@ the table returned by `minetest.get_player_information(name)`.
|
||||
IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes.
|
||||
You do not need to use this to get translated strings to show up on the client.
|
||||
|
||||
Translating content meta
|
||||
------------------------
|
||||
|
||||
You can translate content meta, such as `title` and `description`, by placing
|
||||
translations in a `locale/DOMAIN.LANG.tr` file. The textdomain defaults to the
|
||||
content name, but can be customised using `textdomain` in the content's .conf.
|
||||
|
||||
### Mods and Texture Packs
|
||||
|
||||
Say you have a mod called `mymod` with a short description in mod.conf:
|
||||
|
||||
```
|
||||
description = This is the short description
|
||||
```
|
||||
|
||||
Minetest will look for translations in the `mymod` textdomain as there's no
|
||||
textdomain specified in mod.conf. For example, `mymod/locale/mymod.fr.tr`:
|
||||
|
||||
```
|
||||
# textdomain:mymod
|
||||
This is the short description=Voici la description succincte
|
||||
```
|
||||
|
||||
### Games and Modpacks
|
||||
|
||||
For games and modpacks, Minetest will look for the textdomain in all mods.
|
||||
|
||||
Say you have a game called `mygame` with the following game.conf:
|
||||
|
||||
```
|
||||
description = This is the game's short description
|
||||
textdomain = mygame
|
||||
```
|
||||
|
||||
Minetest will then look for the textdomain `mygame` in all mods, for example,
|
||||
`mygame/mods/anymod/locale/mygame.fr.tr`. Note that it is still recommended that your
|
||||
textdomain match the mod name, but this isn't required.
|
||||
|
||||
|
||||
|
||||
Perlin noise
|
||||
============
|
||||
|
||||
@ -4677,6 +4723,7 @@ differences:
|
||||
into it; it's not necessary to call `VoxelManip:read_from_map()`.
|
||||
Note that the region of map it has loaded is NOT THE SAME as the `minp`, `maxp`
|
||||
parameters of `on_generated()`. Refer to `minetest.get_mapgen_object` docs.
|
||||
Once you're done you still need to call `VoxelManip:write_to_map()`
|
||||
|
||||
* The `on_generated()` callbacks of some mods may place individual nodes in the
|
||||
generated area using non-VoxelManip map modification methods. Because the
|
||||
@ -4873,10 +4920,10 @@ Mapgen objects
|
||||
==============
|
||||
|
||||
A mapgen object is a construct used in map generation. Mapgen objects can be
|
||||
used by an `on_generate` callback to speed up operations by avoiding
|
||||
used by an `on_generated` callback to speed up operations by avoiding
|
||||
unnecessary recalculations, these can be retrieved using the
|
||||
`minetest.get_mapgen_object()` function. If the requested Mapgen object is
|
||||
unavailable, or `get_mapgen_object()` was called outside of an `on_generate()`
|
||||
unavailable, or `get_mapgen_object()` was called outside of an `on_generated`
|
||||
callback, `nil` is returned.
|
||||
|
||||
The following Mapgen objects are currently available:
|
||||
@ -4908,12 +4955,14 @@ generated chunk by the current mapgen.
|
||||
|
||||
### `gennotify`
|
||||
|
||||
Returns a table mapping requested generation notification types to arrays of
|
||||
positions at which the corresponding generated structures are located within
|
||||
the current chunk. To enable the capture of positions of interest to be recorded
|
||||
call `minetest.set_gen_notify()` first.
|
||||
Returns a table. You need to announce your interest in a specific
|
||||
field by calling `minetest.set_gen_notify()` *before* map generation happens.
|
||||
|
||||
Possible fields of the returned table are:
|
||||
* key = string: generation notification type
|
||||
* value = list of positions (usually)
|
||||
* Exceptions are denoted in the listing below.
|
||||
|
||||
Available generation notification types:
|
||||
|
||||
* `dungeon`: bottom center position of dungeon rooms
|
||||
* `temple`: as above but for desert temples (mgv6 only)
|
||||
@ -4921,7 +4970,12 @@ Possible fields of the returned table are:
|
||||
* `cave_end`
|
||||
* `large_cave_begin`
|
||||
* `large_cave_end`
|
||||
* `decoration#id` (see below)
|
||||
* `custom`: data originating from [Mapgen environment] (Lua API)
|
||||
* This is a table.
|
||||
* key = user-defined ID (string)
|
||||
* value = arbitrary Lua value
|
||||
* `decoration#id`: decorations
|
||||
* (see below)
|
||||
|
||||
Decorations have a key in the format of `"decoration#id"`, where `id` is the
|
||||
numeric unique decoration ID as returned by `minetest.get_decoration_id()`.
|
||||
@ -5302,6 +5356,10 @@ Utilities
|
||||
item_specific_pointabilities = true,
|
||||
-- Nodes `pointable` property can be `"blocking"` (5.9.0)
|
||||
blocking_pointability_type = true,
|
||||
-- dynamic_add_media can be called at startup when leaving callback as `nil` (5.9.0)
|
||||
dynamic_add_media_startup = true,
|
||||
-- dynamic_add_media supports `filename` and `filedata` parameters (5.9.0)
|
||||
dynamic_add_media_filepath = true,
|
||||
}
|
||||
```
|
||||
|
||||
@ -5426,6 +5484,9 @@ Utilities
|
||||
* `minetest.sha1(data, [raw])`: returns the sha1 hash of data
|
||||
* `data`: string of data to hash
|
||||
* `raw`: return raw bytes instead of hex digits, default: false
|
||||
* `minetest.sha256(data, [raw])`: returns the sha256 hash of data
|
||||
* `data`: string of data to hash
|
||||
* `raw`: return raw bytes instead of hex digits, default: false
|
||||
* `minetest.colorspec_to_colorstring(colorspec)`: Converts a ColorSpec to a
|
||||
ColorString. If the ColorSpec is invalid, returns `nil`.
|
||||
* `colorspec`: The ColorSpec to convert
|
||||
@ -5445,7 +5506,7 @@ Utilities
|
||||
You can use `colorspec_to_bytes` to generate raw RGBA values.
|
||||
Palettes are not supported at the moment.
|
||||
You may use this to procedurally generate textures during server init.
|
||||
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||
* `minetest.urlencode(str)`: Encodes reserved URI characters by a
|
||||
percent sign followed by two hex digits. See
|
||||
[RFC 3986, section 2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3).
|
||||
|
||||
@ -5581,8 +5642,10 @@ Call these functions only at load time!
|
||||
* `minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing))`
|
||||
* Called when a node is punched
|
||||
* `minetest.register_on_generated(function(minp, maxp, blockseed))`
|
||||
* Called after generating a piece of world. Modifying nodes inside the area
|
||||
is a bit faster than usual.
|
||||
* Called after generating a piece of world between `minp` and `maxp`.
|
||||
* **Avoid using this** whenever possible. As with other callbacks this blocks
|
||||
the main thread and introduces noticable latency.
|
||||
Consider [Mapgen environment] for an alternative.
|
||||
* `minetest.register_on_newplayer(function(ObjectRef))`
|
||||
* Called when a new player enters the world for the first time
|
||||
* `minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage))`
|
||||
@ -5948,8 +6011,11 @@ Environment access
|
||||
* `minetest.add_entity(pos, name, [staticdata])`: Spawn Lua-defined entity at
|
||||
position.
|
||||
* Returns `ObjectRef`, or `nil` if failed
|
||||
* Entities with `static_save = true` can be added also
|
||||
to unloaded and non-generated blocks.
|
||||
* `minetest.add_item(pos, item)`: Spawn item
|
||||
* Returns `ObjectRef`, or `nil` if failed
|
||||
* Items can be added also to unloaded and non-generated blocks.
|
||||
* `minetest.get_player_by_name(name)`: Get an `ObjectRef` to a player
|
||||
* `minetest.get_objects_inside_radius(pos, radius)`: returns a list of
|
||||
ObjectRefs.
|
||||
@ -5995,20 +6061,18 @@ Environment access
|
||||
* `minetest.get_voxel_manip([pos1, pos2])`
|
||||
* Return voxel manipulator object.
|
||||
* Loads the manipulator from the map if positions are passed.
|
||||
* `minetest.set_gen_notify(flags, {deco_ids})`
|
||||
* `minetest.set_gen_notify(flags, [deco_ids], [custom_ids])`
|
||||
* Set the types of on-generate notifications that should be collected.
|
||||
* `flags` is a flag field with the available flags:
|
||||
* dungeon
|
||||
* temple
|
||||
* cave_begin
|
||||
* cave_end
|
||||
* large_cave_begin
|
||||
* large_cave_end
|
||||
* decoration
|
||||
* The second parameter is a list of IDs of decorations which notification
|
||||
* `flags`: flag field, see [`gennotify`] for available generation notification types.
|
||||
* The following parameters are optional:
|
||||
* `deco_ids` is a list of IDs of decorations which notification
|
||||
is requested for.
|
||||
* `custom_ids` is a list of user-defined IDs (strings) which are
|
||||
requested. By convention these should be the mod name with an optional
|
||||
colon and specifier added, e.g. `"default"` or `"default:dungeon_loot"`
|
||||
* `minetest.get_gen_notify()`
|
||||
* Returns a flagstring and a table with the `deco_id`s.
|
||||
* Returns a flagstring, a table with the `deco_id`s and a table with
|
||||
user-defined IDs.
|
||||
* `minetest.get_decoration_id(decoration_name)`
|
||||
* Returns the decoration ID number for the provided decoration name string,
|
||||
or `nil` on failure.
|
||||
@ -6180,6 +6244,17 @@ Environment access
|
||||
* increase level of leveled node by level, default `level` equals `1`
|
||||
* if `totallevel > maxlevel`, returns rest (`total-max`)
|
||||
* `level` must be between -127 and 127
|
||||
* `minetest.get_node_boxes(box_type, pos, [node])`
|
||||
* `box_type` must be `"node_box"`, `"collision_box"` or `"selection_box"`.
|
||||
* `pos` must be a node position.
|
||||
* `node` can be a table in the form `{name=string, param1=number, param2=number}`.
|
||||
If `node` is `nil`, the actual node at `pos` is used instead.
|
||||
* Resolves any facedir-rotated boxes, connected boxes and the like into
|
||||
actual boxes.
|
||||
* Returns a list of boxes in the form
|
||||
`{{x1, y1, z1, x2, y2, z2}, {x1, y1, z1, x2, y2, z2}, ...}`. Coordinates
|
||||
are relative to `pos`.
|
||||
* See also: [Node boxes](#node-boxes)
|
||||
* `minetest.fix_light(pos1, pos2)`: returns `true`/`false`
|
||||
* resets the light in a cuboid-shaped part of
|
||||
the map and removes lighting bugs.
|
||||
@ -6544,7 +6619,6 @@ Class instances that can be transferred between environments:
|
||||
Functions:
|
||||
* Standalone helpers such as logging, filesystem, encoding,
|
||||
hashing or compression APIs
|
||||
* `minetest.request_insecure_environment` (same restrictions apply)
|
||||
|
||||
Variables:
|
||||
* `minetest.settings`
|
||||
@ -6553,6 +6627,85 @@ Variables:
|
||||
* with all functions and userdata values replaced by `true`, calling any
|
||||
callbacks here is obviously not possible
|
||||
|
||||
Mapgen environment
|
||||
------------------
|
||||
|
||||
The engine runs the map generator on separate threads, each of these also has
|
||||
a Lua environment. Its primary purpose is to allow mods to operate on newly
|
||||
generated parts of the map to e.g. generate custom structures.
|
||||
Internally it is referred to as "emerge environment".
|
||||
|
||||
Refer to [Async environment] for the usual disclaimer on what environment isolation entails.
|
||||
|
||||
The map generator threads, which also contain the above mentioned Lua environment,
|
||||
are initialized after all mods have been loaded by the server. After that the
|
||||
registered scripts (not all mods!) - see below - are run during initialization of
|
||||
the mapgen environment. After that only callbacks happen. The mapgen env
|
||||
does not have a global step or timer.
|
||||
|
||||
* `minetest.register_mapgen_script(path)`:
|
||||
* Register a path to a Lua file to be imported when a mapgen environment
|
||||
is initialized. Run in order of registration.
|
||||
|
||||
### List of APIs exclusive to the mapgen env
|
||||
|
||||
* `minetest.register_on_generated(function(vmanip, minp, maxp, blockseed))`
|
||||
* Called after the engine mapgen finishes a chunk but before it is written to
|
||||
the map.
|
||||
* Chunk data resides in `vmanip`. Other parts of the map are not accessible.
|
||||
The area of the chunk if comprised of `minp` and `maxp`, note that is smaller
|
||||
than the emerged area of the VoxelManip.
|
||||
Note: calling `read_from_map()` or `write_to_map()` on the VoxelManipulator object
|
||||
is not necessary and is disallowed.
|
||||
* `blockseed`: 64-bit seed number used for this chunk
|
||||
* `minetest.save_gen_notify(id, data)`
|
||||
* Saves data for retrieval using the gennotify mechanism (see [Mapgen objects]).
|
||||
* Data is bound to the chunk that is currently being processed, so this function
|
||||
only makes sense inside the `on_generated` callback.
|
||||
* `id`: user-defined ID (a string)
|
||||
By convention these should be the mod name with an optional
|
||||
colon and specifier added, e.g. `"default"` or `"default:dungeon_loot"`
|
||||
* `data`: any Lua object (will be serialized, no userdata allowed)
|
||||
* returns `true` if the data was remembered. That is if `minetest.set_gen_notify`
|
||||
was called with the same user-defined ID before.
|
||||
|
||||
### List of APIs available in the mapgen env
|
||||
|
||||
Classes:
|
||||
* `AreaStore`
|
||||
* `ItemStack`
|
||||
* `PerlinNoise`
|
||||
* `PerlinNoiseMap`
|
||||
* `PseudoRandom`
|
||||
* `PcgRandom`
|
||||
* `SecureRandom`
|
||||
* `VoxelArea`
|
||||
* `VoxelManip`
|
||||
* only given by callbacks; cannot access rest of map
|
||||
* `Settings`
|
||||
|
||||
Functions:
|
||||
* Standalone helpers such as logging, filesystem, encoding,
|
||||
hashing or compression APIs
|
||||
* `minetest.get_biome_id`, `get_biome_name`, `get_heat`, `get_humidity`,
|
||||
`get_biome_data`, `get_mapgen_object`, `get_mapgen_params`, `get_mapgen_edges`,
|
||||
`get_mapgen_setting`, `get_noiseparams`, `get_decoration_id` and more
|
||||
* `minetest.get_node`, `set_node`, `find_node_near`, `find_nodes_in_area`,
|
||||
`spawn_tree` and similar
|
||||
* these only operate on the current chunk (if inside a callback)
|
||||
|
||||
Variables:
|
||||
* `minetest.settings`
|
||||
* `minetest.registered_items`, `registered_nodes`, `registered_tools`,
|
||||
`registered_craftitems` and `registered_aliases`
|
||||
* with all functions and userdata values replaced by `true`, calling any
|
||||
callbacks here is obviously not possible
|
||||
* `minetest.registered_biomes`, `registered_ores`, `registered_decorations`
|
||||
|
||||
Note that node metadata does not exist in the mapgen env, we suggest deferring
|
||||
setting any metadata you need to the `on_generated` callback in the regular env.
|
||||
You can use the gennotify mechanism to transfer this information.
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
@ -6583,11 +6736,15 @@ Server
|
||||
* Returns boolean indicating success (false if player nonexistent)
|
||||
* `minetest.dynamic_add_media(options, callback)`
|
||||
* `options`: table containing the following parameters
|
||||
* `filepath`: path to a media file on the filesystem
|
||||
* `filename`: name the media file will be usable as
|
||||
(optional if `filepath` present)
|
||||
* `filepath`: path to the file on the filesystem [*]
|
||||
* `filedata`: the data of the file to be sent [*]
|
||||
* `to_player`: name of the player the media should be sent to instead of
|
||||
all players (optional)
|
||||
* `ephemeral`: boolean that marks the media as ephemeral,
|
||||
it will not be cached on the client (optional, default false)
|
||||
* Exactly one of the paramters marked [*] must be specified.
|
||||
* `callback`: function with arguments `name`, which is a player name
|
||||
* Pushes the specified media file to client(s). (details below)
|
||||
The file must be a supported image, sound or model format.
|
||||
@ -6605,6 +6762,9 @@ Server
|
||||
name twice is not possible/guaranteed to work. An exception to this is the
|
||||
use of `to_player` to send the same, already existent file to multiple
|
||||
chosen players.
|
||||
* You can also call this at startup time. In that case `callback` MUST
|
||||
be `nil` and you cannot use `ephemeral` or `to_player`, as these logically
|
||||
do not make sense.
|
||||
* Clients will attempt to fetch files added this way via remote media,
|
||||
this can make transfer of bigger files painless (if set up). Nevertheless
|
||||
it is advised not to use dynamic media for big media files.
|
||||
@ -6786,7 +6946,7 @@ Misc.
|
||||
(regardless of online status)
|
||||
* `minetest.hud_replace_builtin(name, hud_definition)`
|
||||
* Replaces definition of a builtin hud element
|
||||
* `name`: `"breath"` or `"health"`
|
||||
* `name`: `"breath"`, `"health"` or `"minimap"`
|
||||
* `hud_definition`: definition to replace builtin definition
|
||||
* `minetest.parse_relative_number(arg, relative_to)`: returns number or nil
|
||||
* Helper function for chat commands.
|
||||
@ -7054,10 +7214,6 @@ Global tables
|
||||
* Map of registered decoration definitions, indexed by the `name` field.
|
||||
* If `name` is nil, the key is the object handle returned by
|
||||
`minetest.register_decoration`.
|
||||
* `minetest.registered_schematics`
|
||||
* Map of registered schematic definitions, indexed by the `name` field.
|
||||
* If `name` is nil, the key is the object handle returned by
|
||||
`minetest.register_schematic`.
|
||||
* `minetest.registered_chatcommands`
|
||||
* Map of registered chat command definitions, indexed by name
|
||||
* `minetest.registered_privileges`
|
||||
@ -7273,6 +7429,8 @@ an itemstring, a table or `nil`.
|
||||
the item breaks after `max_uses` times
|
||||
* Valid `max_uses` range is [0,65536]
|
||||
* Does nothing if item is not a tool or if `max_uses` is 0
|
||||
* `get_wear_bar_params()`: returns the wear bar parameters of the item,
|
||||
or nil if none are defined for this item type or in the stack's meta
|
||||
* `add_item(item)`: returns leftover `ItemStack`
|
||||
* Put some item or stack onto this stack
|
||||
* `item_fits(item)`: returns `true` if item or stack can be fully added to
|
||||
@ -7314,6 +7472,10 @@ Can be obtained via `item:get_meta()`.
|
||||
* Overrides the item's tool capabilities
|
||||
* A nil value will clear the override data and restore the original
|
||||
behavior.
|
||||
* `set_wear_bar_params([wear_bar_params])`
|
||||
* Overrides the item's wear bar parameters (see "Wear Bar Color" section)
|
||||
* A nil value will clear the override data and restore the original
|
||||
behavior.
|
||||
|
||||
`MetaDataRef`
|
||||
-------------
|
||||
@ -8397,11 +8559,14 @@ Player properties need to be saved manually.
|
||||
-- If `rotate = false`, the selection box will not rotate with the object itself, remaining fixed to the axes.
|
||||
-- If `rotate = true`, it will match the object's rotation and any attachment rotations.
|
||||
-- Raycasts use the selection box and object's rotation, but do *not* obey attachment rotations.
|
||||
-- For server-side raycasts to work correctly,
|
||||
-- the selection box should extend at most 5 units in each direction.
|
||||
|
||||
|
||||
pointable = true,
|
||||
-- Can be `true` if it is pointable, `false` if it can be pointed through,
|
||||
-- or `"blocking"` if it is pointable but not selectable.
|
||||
-- Clients older than 5.9.0 interpret `pointable = "blocking"` as `pointable = true`.
|
||||
-- Can be overridden by the `pointabilities` of the held item.
|
||||
|
||||
visual = "cube" / "sprite" / "upright_sprite" / "mesh" / "wielditem" / "item",
|
||||
@ -8814,6 +8979,19 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
|
||||
-- fallback behavior.
|
||||
},
|
||||
|
||||
-- Set wear bar color of the tool by setting color stops and blend mode
|
||||
-- See "Wear Bar Color" section for further explanation including an example
|
||||
wear_color = {
|
||||
-- interpolation mode: 'constant' or 'linear'
|
||||
-- (nil defaults to 'constant')
|
||||
blend = "linear",
|
||||
color_stops = {
|
||||
[0.0] = "#ff0000",
|
||||
[0.5] = "#ffff00",
|
||||
[1.0] = "#00ff00",
|
||||
}
|
||||
},
|
||||
|
||||
node_placement_prediction = nil,
|
||||
-- If nil and item is node, prediction is made automatically.
|
||||
-- If nil and item is not a node, no prediction is made.
|
||||
@ -9015,6 +9193,7 @@ Used by `minetest.register_node`.
|
||||
pointable = true,
|
||||
-- Can be `true` if it is pointable, `false` if it can be pointed through,
|
||||
-- or `"blocking"` if it is pointable but not selectable.
|
||||
-- Clients older than 5.9.0 interpret `pointable = "blocking"` as `pointable = true`.
|
||||
-- Can be overridden by the `pointabilities` of the held item.
|
||||
-- A client may be able to point non-pointable nodes, since it isn't checked server-side.
|
||||
|
||||
@ -9380,6 +9559,46 @@ Used by `minetest.register_node`.
|
||||
}
|
||||
```
|
||||
|
||||
Wear Bar Color
|
||||
--------------
|
||||
|
||||
'Wear Bar' is a property of items that defines the coloring
|
||||
of the bar that appears under damaged tools.
|
||||
If it is absent, the default behavior of green-yellow-red is
|
||||
used.
|
||||
|
||||
### Wear bar colors definition
|
||||
|
||||
#### Syntax
|
||||
|
||||
```lua
|
||||
{
|
||||
-- 'constant' or 'linear'
|
||||
-- (nil defaults to 'constant')
|
||||
blend = "linear",
|
||||
color_stops = {
|
||||
[0.0] = "#ff0000",
|
||||
[0.5] = "slateblue",
|
||||
[1.0] = {r=0, g=255, b=0, a=150},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Blend mode `blend`
|
||||
|
||||
* `linear`: blends smoothly between each defined color point.
|
||||
* `constant`: each color starts at its defined point, and continues up to the next point
|
||||
|
||||
#### Color stops `color_stops`
|
||||
|
||||
Specified as `ColorSpec` color values assigned to `float` durability keys.
|
||||
|
||||
"Durability" is defined as `1 - (wear / 65535)`.
|
||||
|
||||
#### Shortcut usage
|
||||
|
||||
Wear bar color can also be specified as a single `ColorSpec` instead of a table.
|
||||
|
||||
Crafting recipes
|
||||
----------------
|
||||
|
||||
|
@ -323,6 +323,7 @@ Package - content which is downloadable from the content db, may or may not be i
|
||||
description = "description",
|
||||
author = "author",
|
||||
path = "path/to/content",
|
||||
textdomain = "textdomain", -- textdomain to translate title / description with
|
||||
depends = {"mod", "names"}, -- mods only
|
||||
optional_depends = {"mod", "names"}, -- mods only
|
||||
}
|
||||
@ -340,6 +341,13 @@ Package - content which is downloadable from the content db, may or may not be i
|
||||
error_message = "", -- message or nil
|
||||
}
|
||||
```
|
||||
* `core.get_content_translation(path, domain, string)`
|
||||
* Translates `string` using `domain` in content directory at `path`.
|
||||
* Textdomains will be found by looking through all locale folders.
|
||||
* String should contain translation markup from `core.translate(textdomain, ...)`.
|
||||
* Ex: `core.get_content_translation("mods/mymod", "mymod", core.translate("mymod", "Hello World"))`
|
||||
will translate "Hello World" into the current user's language
|
||||
using `mods/mymod/locale/mymod.fr.tr`.
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
@ -119,7 +119,7 @@ Display an interactive terminal over ncurses during execution.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B MINETEST_SUBGAME_PATH
|
||||
.B MINETEST_GAME_PATH
|
||||
Colon delimited list of directories to search for games.
|
||||
.TP
|
||||
.B MINETEST_MOD_PATH
|
||||
|
@ -25,8 +25,14 @@ texture pack. The name must not be “base”.
|
||||
### `texture_pack.conf`
|
||||
A key-value config file with the following keys:
|
||||
|
||||
* `title` - human readable title
|
||||
* `name`: The texture pack name. Allows Minetest to determine the texture pack name even if
|
||||
the folder is wrongly named.
|
||||
* `title` - human-readable title
|
||||
* `description` - short description, shown in the content tab
|
||||
* `author`: The author's ContentDB username.
|
||||
* `textdomain`: Textdomain used to translate title and description.
|
||||
Defaults to the texture pack name.
|
||||
See [Translating content meta](lua_api.md#translating-content-meta).
|
||||
|
||||
### `description.txt`
|
||||
**Deprecated**, you should use texture_pack.conf instead.
|
||||
@ -205,7 +211,8 @@ Here are targets you can choose from:
|
||||
Nodes support all targets, but other items only support 'inventory'
|
||||
and 'wield'.
|
||||
|
||||
¹ : `N` is an integer [0,255]. Sets align_style = "world" and scale = N on the tile, refer to lua_api.md for details.
|
||||
¹ : `N` is an integer [0,255]. Sets align_style = "world" and scale = N on the tile,
|
||||
refer to lua_api.md for details.
|
||||
|
||||
### Using the special targets
|
||||
|
||||
|
@ -420,36 +420,141 @@ minetest.register_tool("basetools:dagger_steel", {
|
||||
}
|
||||
})
|
||||
|
||||
-- Test tool uses and punch_attack_uses
|
||||
local uses = { 1, 2, 3, 5, 10, 50, 100, 1000, 10000, 65535 }
|
||||
for i=1, #uses do
|
||||
local u = uses[i]
|
||||
local ustring
|
||||
if i == 1 then
|
||||
ustring = u.."-Use"
|
||||
else
|
||||
ustring = u.."-Uses"
|
||||
end
|
||||
local color = string.format("#FF00%02X", math.floor(((i-1)/#uses) * 255))
|
||||
minetest.register_tool("basetools:pick_uses_"..string.format("%05d", u), {
|
||||
-- Test tool uses, punch_attack_uses, and wear bar coloring
|
||||
local tool_params = {
|
||||
{uses = 1},
|
||||
{uses = 2},
|
||||
{uses = 3},
|
||||
{
|
||||
uses = 5,
|
||||
wear_color = "#5865f2",
|
||||
wear_description = "Solid color: #5865f2",
|
||||
},
|
||||
{
|
||||
uses = 10,
|
||||
wear_color = "slateblue",
|
||||
wear_description = "Solid color: slateblue",
|
||||
},
|
||||
{
|
||||
uses = 50,
|
||||
wear_color = {
|
||||
color_stops = {
|
||||
[0] = "red",
|
||||
[0.5] = "yellow",
|
||||
[1.0] = "blue"
|
||||
},
|
||||
blend = "linear"
|
||||
},
|
||||
wear_description = "Ranges from blue to yellow to red",
|
||||
},
|
||||
{
|
||||
uses = 100,
|
||||
wear_color = {
|
||||
color_stops = {
|
||||
[0] = "#ffff00",
|
||||
[0.2] = "#ff00ff",
|
||||
[0.3] = "#ffff00",
|
||||
[0.45] = "#c0ffee",
|
||||
[0.6] = {r=255, g=255, b=0, a=100}, -- continues until the end
|
||||
},
|
||||
blend = "constant"
|
||||
},
|
||||
wear_description = "Misc. colors, constant interpolation",
|
||||
},
|
||||
{uses = 1e3},
|
||||
{uses = 1e4},
|
||||
{uses = 65535},
|
||||
}
|
||||
|
||||
for i, params in ipairs(tool_params) do
|
||||
local uses = params.uses
|
||||
local ustring = uses.."-Use"..(uses == 1 and "" or "s")
|
||||
local color = string.format("#FF00%02X", math.floor(((i-1)/#tool_params) * 255))
|
||||
minetest.register_tool("basetools:pick_uses_"..string.format("%05d", uses), {
|
||||
description = ustring.." Pickaxe".."\n"..
|
||||
"Digs cracky=3",
|
||||
"Digs cracky=3"..
|
||||
(params.wear_description and "\n".."Wear bar: " .. params.wear_description or ""),
|
||||
inventory_image = "basetools_usespick.png^[colorize:"..color..":127",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=u, maxlevel=0}
|
||||
cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=uses, maxlevel=0}
|
||||
},
|
||||
},
|
||||
wear_color = params.wear_color
|
||||
})
|
||||
|
||||
minetest.register_tool("basetools:sword_uses_"..string.format("%05d", u), {
|
||||
minetest.register_tool("basetools:sword_uses_"..string.format("%05d", uses), {
|
||||
description = ustring.." Sword".."\n"..
|
||||
"Damage: fleshy=1",
|
||||
inventory_image = "basetools_usessword.png^[colorize:"..color..":127",
|
||||
tool_capabilities = {
|
||||
damage_groups = {fleshy=1},
|
||||
punch_attack_uses = u,
|
||||
punch_attack_uses = uses,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("wear_color", {
|
||||
params = "[idx]",
|
||||
description = "Set wear bar color override",
|
||||
func = function(player_name, param)
|
||||
local player = minetest.get_player_by_name(player_name)
|
||||
if not player then return end
|
||||
|
||||
local wear_color = nil
|
||||
local wear_desc = "Reset override"
|
||||
|
||||
if param ~= "" then
|
||||
local params = tool_params[tonumber(param)]
|
||||
if not params then
|
||||
return false, "idx out of bounds"
|
||||
end
|
||||
wear_color = params.wear_color
|
||||
wear_desc = "Set override: "..(params.wear_description or "Default behavior")
|
||||
end
|
||||
local tool = player:get_wielded_item()
|
||||
if tool:get_count() == 0 then
|
||||
return false, "Tool not found"
|
||||
end
|
||||
tool:get_meta():set_wear_bar_params(wear_color)
|
||||
player:set_wielded_item(tool)
|
||||
return true, wear_desc
|
||||
end
|
||||
})
|
||||
|
||||
-- Punch handler to set random color & wear
|
||||
local wear_on_use = function(itemstack, user, pointed_thing)
|
||||
local meta = itemstack:get_meta()
|
||||
local color = math.random(0, 0xFFFFFF)
|
||||
local colorstr = string.format("#%06x", color)
|
||||
meta:set_wear_bar_params(colorstr)
|
||||
minetest.log("action", "[basetool] Wear bar color of "..itemstack:get_name().." changed to "..colorstr)
|
||||
itemstack:set_wear(math.random(0, 65535))
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Place handler to clear item metadata color
|
||||
local wear_on_place = function(itemstack, user, pointed_thing)
|
||||
local meta = itemstack:get_meta()
|
||||
meta:set_wear_bar_params(nil)
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_tool("basetools:random_wear_bar", {
|
||||
description = "Wear Bar Color Test\n" ..
|
||||
"Punch: Set random color & wear\n" ..
|
||||
"Place: Clear color",
|
||||
-- Base texture: A grayscale square (can be colorized)
|
||||
inventory_image = "basetools_usespick.png^[colorize:#FFFFFF:127",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=1000, maxlevel=0}
|
||||
},
|
||||
},
|
||||
|
||||
on_use = wear_on_use,
|
||||
on_place = wear_on_place,
|
||||
on_secondary_use = wear_on_place,
|
||||
})
|
||||
|
@ -9,7 +9,7 @@ for d=0, 8 do
|
||||
end
|
||||
minetest.register_node("testnodes:rliquid_"..d, {
|
||||
description = "Test Liquid Source, Range "..d..
|
||||
tt_normal,
|
||||
tt_normal .. "\n" .. "(falling & floating node)",
|
||||
drawtype = "liquid",
|
||||
tiles = {"testnodes_liquidsource_r"..d..".png"},
|
||||
special_tiles = {
|
||||
@ -25,6 +25,8 @@ for d=0, 8 do
|
||||
liquid_alternative_flowing = "testnodes:rliquid_flowing_"..d,
|
||||
liquid_alternative_source = "testnodes:rliquid_"..d,
|
||||
liquid_range = d,
|
||||
-- Also use these nodes to test falling, floating liquid source nodes
|
||||
groups = {float = 1, falling_node = 1},
|
||||
})
|
||||
|
||||
minetest.register_node("testnodes:rliquid_flowing_"..d, {
|
||||
|
@ -149,15 +149,28 @@ fractal = nil
|
||||
frac_emb = nil
|
||||
checker = nil
|
||||
|
||||
local textures_path = minetest.get_modpath( minetest.get_current_modname() ) .. "/textures/"
|
||||
minetest.safe_file_write(
|
||||
textures_path .. "testnodes_generated_mb.png",
|
||||
do
|
||||
-- we used to write the textures to our mod folder. in order to avoid
|
||||
-- duplication errors delete them if they still exist.
|
||||
local path = core.get_modpath(core.get_current_modname()) .. "/textures/"
|
||||
os.remove(path .. "testnodes_generated_mb.png")
|
||||
os.remove(path .. "testnodes_generated_ck.png")
|
||||
end
|
||||
|
||||
local textures_path = core.get_worldpath() .. "/"
|
||||
core.safe_file_write(
|
||||
textures_path .. "testnodes1.png",
|
||||
encode_and_check(512, 512, "rgb", data_mb)
|
||||
)
|
||||
minetest.safe_file_write(
|
||||
textures_path .. "testnodes_generated_ck.png",
|
||||
encode_and_check(512, 512, "gray", data_ck)
|
||||
)
|
||||
local png_ck = encode_and_check(512, 512, "gray", data_ck)
|
||||
core.dynamic_add_media({
|
||||
filename = "testnodes_generated_mb.png",
|
||||
filepath = textures_path .. "testnodes1.png"
|
||||
})
|
||||
core.dynamic_add_media({
|
||||
filename = "testnodes_generated_ck.png",
|
||||
filedata = png_ck,
|
||||
})
|
||||
|
||||
minetest.register_node("testnodes:generated_png_mb", {
|
||||
description = S("Generated Mandelbrot PNG Test Node"),
|
||||
@ -200,6 +213,8 @@ minetest.register_node("testnodes:generated_png_dst_emb", {
|
||||
groups = { dig_immediate = 2 },
|
||||
})
|
||||
|
||||
png_ck = nil
|
||||
png_emb = nil
|
||||
data_emb = nil
|
||||
data_mb = nil
|
||||
data_ck = nil
|
||||
|
@ -6,6 +6,7 @@ testtools = {}
|
||||
dofile(minetest.get_modpath("testtools") .. "/light.lua")
|
||||
dofile(minetest.get_modpath("testtools") .. "/privatizer.lua")
|
||||
dofile(minetest.get_modpath("testtools") .. "/particles.lua")
|
||||
dofile(minetest.get_modpath("testtools") .. "/node_box_visualizer.lua")
|
||||
|
||||
local pointabilities_nodes = {
|
||||
nodes = {
|
||||
|
79
games/devtest/mods/testtools/node_box_visualizer.lua
Normal file
79
games/devtest/mods/testtools/node_box_visualizer.lua
Normal file
@ -0,0 +1,79 @@
|
||||
local S = minetest.get_translator("testtools")
|
||||
|
||||
minetest.register_entity("testtools:visual_box", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"blank.png", "blank.png", "blank.png",
|
||||
"blank.png", "blank.png", "blank.png",
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
physical = false,
|
||||
pointable = false,
|
||||
static_save = false,
|
||||
},
|
||||
|
||||
on_activate = function(self)
|
||||
self.timestamp = minetest.get_us_time() + 5000000
|
||||
end,
|
||||
|
||||
on_step = function(self)
|
||||
if minetest.get_us_time() >= self.timestamp then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local BOX_TYPES = {"node_box", "collision_box", "selection_box"}
|
||||
local DEFAULT_BOX_TYPE = "selection_box"
|
||||
|
||||
local function visualizer_on_use(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = itemstack:get_meta()
|
||||
local box_type = meta:get("box_type") or DEFAULT_BOX_TYPE
|
||||
|
||||
local result = minetest.get_node_boxes(box_type, pointed_thing.under)
|
||||
local t = "testtools_visual_" .. box_type .. ".png"
|
||||
|
||||
for _, box in ipairs(result) do
|
||||
local box_min = pointed_thing.under + vector.new(box[1], box[2], box[3])
|
||||
local box_max = pointed_thing.under + vector.new(box[4], box[5], box[6])
|
||||
local box_center = (box_min + box_max) / 2
|
||||
local obj = minetest.add_entity(box_center, "testtools:visual_box")
|
||||
if not obj then
|
||||
break
|
||||
end
|
||||
obj:set_properties({
|
||||
textures = {t, t, t, t, t, t},
|
||||
-- Add a small offset to avoid Z-fighting.
|
||||
visual_size = vector.add(box_max - box_min, 0.01),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function visualizer_on_place(itemstack, placer, pointed_thing)
|
||||
local meta = itemstack:get_meta()
|
||||
local prev_value = meta:get("box_type") or DEFAULT_BOX_TYPE
|
||||
local prev_index = table.indexof(BOX_TYPES, prev_value)
|
||||
assert(prev_index ~= -1)
|
||||
|
||||
local new_value = BOX_TYPES[(prev_index % #BOX_TYPES) + 1]
|
||||
meta:set_string("box_type", new_value)
|
||||
minetest.chat_send_player(placer:get_player_name(), S("[Node Box Visualizer] box_type = @1", new_value))
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:node_box_visualizer", {
|
||||
description = S("Node Box Visualizer") .. "\n" ..
|
||||
S("Punch: Show node/collision/selection boxes of the pointed node") .. "\n" ..
|
||||
S("Place: Change selected box type (default: selection box)"),
|
||||
inventory_image = "testtools_node_box_visualizer.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = visualizer_on_use,
|
||||
on_place = visualizer_on_place,
|
||||
on_secondary_use = visualizer_on_place,
|
||||
})
|
Binary file not shown.
After Width: | Height: | Size: 108 B |
Binary file not shown.
After Width: | Height: | Size: 119 B |
Binary file not shown.
After Width: | Height: | Size: 124 B |
Binary file not shown.
After Width: | Height: | Size: 121 B |
32
games/devtest/mods/unittests/inside_mapgen_env.lua
Normal file
32
games/devtest/mods/unittests/inside_mapgen_env.lua
Normal file
@ -0,0 +1,32 @@
|
||||
core.log("info", "Hello World")
|
||||
|
||||
local function do_tests()
|
||||
assert(core == minetest)
|
||||
-- stuff that should not be here
|
||||
assert(not core.get_player_by_name)
|
||||
assert(not core.object_refs)
|
||||
-- stuff that should be here
|
||||
assert(core.register_on_generated)
|
||||
assert(core.get_node)
|
||||
assert(core.spawn_tree)
|
||||
assert(ItemStack)
|
||||
local meta = ItemStack():get_meta()
|
||||
assert(type(meta) == "userdata")
|
||||
assert(type(meta.set_tool_capabilities) == "function")
|
||||
assert(core.registered_items[""])
|
||||
assert(core.save_gen_notify)
|
||||
-- alias handling
|
||||
assert(core.registered_items["unittests:steel_ingot_alias"].name ==
|
||||
"unittests:steel_ingot")
|
||||
-- fallback to item defaults
|
||||
assert(core.registered_items["unittests:description_test"].on_place == true)
|
||||
end
|
||||
|
||||
-- there's no (usable) communcation path between mapgen and the regular env
|
||||
-- so we just run the test unconditionally
|
||||
do_tests()
|
||||
|
||||
core.register_on_generated(function(vm, pos1, pos2, blockseed)
|
||||
local n = tonumber(core.get_mapgen_setting("chunksize")) * 16 - 1
|
||||
assert(pos2:subtract(pos1) == vector.new(n, n, n))
|
||||
end)
|
@ -77,6 +77,29 @@ local function test_metadata(meta)
|
||||
assert(not meta:equals(compare_meta))
|
||||
end
|
||||
|
||||
local function test_metadata_compat(meta)
|
||||
-- key/value removal using set_string (undocumented, deprecated way)
|
||||
meta:set_string("key", "value")
|
||||
assert(meta:get_string("key") == "value")
|
||||
meta:set_string("key", nil) -- ignore warning
|
||||
assert(meta:to_table().fields["key"] == nil)
|
||||
|
||||
-- undocumented but supported consequence of Lua's
|
||||
-- automatic string <--> number cast
|
||||
meta:set_string("key", 2)
|
||||
assert(meta:get_string("key") == "2")
|
||||
|
||||
-- from_table with non-string keys (supported)
|
||||
local values = meta:to_table()
|
||||
values.fields["new"] = 420
|
||||
meta:from_table(values)
|
||||
assert(meta:get_int("new") == 420)
|
||||
values.fields["new"] = nil
|
||||
meta:from_table(values)
|
||||
assert(meta:get("new") == nil)
|
||||
end
|
||||
|
||||
|
||||
local storage_a = core.get_mod_storage()
|
||||
local storage_b = core.get_mod_storage()
|
||||
local function test_mod_storage()
|
||||
@ -86,7 +109,9 @@ end
|
||||
unittests.register("test_mod_storage", test_mod_storage)
|
||||
|
||||
local function test_item_metadata()
|
||||
test_metadata(ItemStack("unittest:coal_lump"):get_meta())
|
||||
local meta = ItemStack("unittest:coal_lump"):get_meta()
|
||||
test_metadata(meta)
|
||||
test_metadata_compat(meta)
|
||||
end
|
||||
unittests.register("test_item_metadata", test_item_metadata)
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
core.register_mapgen_script(core.get_modpath(core.get_current_modname()) ..
|
||||
DIR_DELIM .. "inside_mapgen_env.lua")
|
||||
|
||||
local function test_pseudo_random()
|
||||
-- We have comprehensive unit tests in C++, this is just to make sure the API code isn't messing up
|
||||
local gen1 = PseudoRandom(13)
|
||||
@ -108,6 +111,13 @@ unittests.register("test_punch_node", function(_, pos)
|
||||
-- currently failing: assert(on_punch_called)
|
||||
end, {map=true})
|
||||
|
||||
local function test_hashing()
|
||||
local input = "hello\000world"
|
||||
assert(core.sha1(input) == "f85b420f1e43ebf88649dfcab302b898d889606c")
|
||||
assert(core.sha256(input) == "b206899bc103669c8e7b36de29d73f95b46795b508aa87d612b2ce84bfb29df2")
|
||||
end
|
||||
unittests.register("test_hashing", test_hashing)
|
||||
|
||||
local function test_compress()
|
||||
-- This text should be compressible, to make sure the results are... normal
|
||||
local text = "The\000 icey canoe couldn't move very well on the\128 lake. The\000 ice was too stiff and the icey canoe's paddles simply wouldn't punch through."
|
||||
@ -130,6 +140,12 @@ local function test_compress()
|
||||
end
|
||||
unittests.register("test_compress", test_compress)
|
||||
|
||||
local function test_urlencode()
|
||||
-- checks that API code handles null bytes
|
||||
assert(core.urlencode("foo\000bar!") == "foo%00bar%21")
|
||||
end
|
||||
unittests.register("test_urlencode", test_urlencode)
|
||||
|
||||
local function test_game_info()
|
||||
local info = minetest.get_game_info()
|
||||
local game_conf = Settings(info.path .. "/game.conf")
|
||||
@ -204,3 +220,30 @@ local function test_on_mapblocks_changed(cb, player, pos)
|
||||
end
|
||||
end
|
||||
unittests.register("test_on_mapblocks_changed", test_on_mapblocks_changed, {map=true, async=true})
|
||||
|
||||
local function test_gennotify_api()
|
||||
local DECO_ID = 123
|
||||
local UD_ID = "unittests:dummy"
|
||||
|
||||
-- the engine doesn't check if the id is actually valid, maybe it should
|
||||
core.set_gen_notify({decoration=true}, {DECO_ID})
|
||||
|
||||
core.set_gen_notify({custom=true}, nil, {UD_ID})
|
||||
|
||||
local flags, deco, custom = core.get_gen_notify()
|
||||
local function ff(flag)
|
||||
return (" " .. flags .. " "):match("[ ,]" .. flag .. "[ ,]") ~= nil
|
||||
end
|
||||
assert(ff("decoration"), "'decoration' flag missing")
|
||||
assert(ff("custom"), "'custom' flag missing")
|
||||
assert(table.indexof(deco, DECO_ID) > 0)
|
||||
assert(table.indexof(custom, UD_ID) > 0)
|
||||
|
||||
core.set_gen_notify({decoration=false, custom=false})
|
||||
|
||||
flags, deco, custom = core.get_gen_notify()
|
||||
assert(not ff("decoration") and not ff("custom"))
|
||||
assert(#deco == 0, "deco ids not empty")
|
||||
assert(#custom == 0, "custom ids not empty")
|
||||
end
|
||||
unittests.register("test_gennotify_api", test_gennotify_api)
|
||||
|
@ -210,6 +210,29 @@ minetest.register_chatcommand("dump_item", {
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("dump_itemdef", {
|
||||
params = "",
|
||||
description = "Prints a dump of the wielded item's definition in table form",
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local str = dump(player:get_wielded_item():get_definition())
|
||||
print(str)
|
||||
return true, str
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("dump_wear_bar", {
|
||||
params = "",
|
||||
description = "Prints a dump of the wielded item's wear bar parameters in table form",
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local item = player:get_wielded_item()
|
||||
local str = dump(item:get_wear_bar_params())
|
||||
print(str)
|
||||
return true, str
|
||||
end,
|
||||
})
|
||||
|
||||
core.register_chatcommand("set_saturation", {
|
||||
params = "<saturation>",
|
||||
description = "Set the saturation for current player.",
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 85081d6fe0c422cf47f74714ee25562715528aa2
|
||||
Subproject commit 154064522ef548c019ce66131ea654642e663a42
|
@ -1 +1 @@
|
||||
1.9.0mt14
|
||||
1.9.0mt15
|
||||
|
@ -109,9 +109,10 @@ if(BUILD_CLIENT AND ENABLE_SOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(ENABLE_TOUCH "Enable Touchscreen support" FALSE)
|
||||
option(ENABLE_TOUCH "Enable touchscreen by default" FALSE)
|
||||
if(ENABLE_TOUCH)
|
||||
add_definitions(-DHAVE_TOUCHSCREENGUI)
|
||||
message(STATUS "Touchscreen support enabled by default.")
|
||||
add_definitions(-DENABLE_TOUCH)
|
||||
endif()
|
||||
|
||||
if(BUILD_CLIENT)
|
||||
|
@ -46,7 +46,7 @@ enum ActiveObjectType {
|
||||
|
||||
struct ActiveObjectMessage
|
||||
{
|
||||
ActiveObjectMessage(u16 id_, bool reliable_=true, const std::string &data_ = "") :
|
||||
ActiveObjectMessage(u16 id_, bool reliable_=true, std::string_view data_ = "") :
|
||||
id(id_),
|
||||
reliable(reliable_),
|
||||
datastring(data_)
|
||||
|
@ -577,6 +577,7 @@ void ChatPrompt::historyNext()
|
||||
|
||||
void ChatPrompt::nickCompletion(const std::set<std::string> &names, bool backwards)
|
||||
{
|
||||
const std::wstring_view line(getLineRef());
|
||||
// Two cases:
|
||||
// (a) m_nick_completion_start == m_nick_completion_end == 0
|
||||
// Then no previous nick completion is active.
|
||||
@ -586,7 +587,6 @@ void ChatPrompt::nickCompletion(const std::set<std::string> &names, bool backwar
|
||||
// m_nick_completion_start..m_nick_completion_end are the
|
||||
// interval where the originally used prefix was. Cycle
|
||||
// through the list of completions of that prefix.
|
||||
const std::wstring &line = getLineRef();
|
||||
u32 prefix_start = m_nick_completion_start;
|
||||
u32 prefix_end = m_nick_completion_end;
|
||||
bool initial = (prefix_end == 0);
|
||||
@ -601,7 +601,7 @@ void ChatPrompt::nickCompletion(const std::set<std::string> &names, bool backwar
|
||||
if (prefix_start == prefix_end)
|
||||
return;
|
||||
}
|
||||
std::wstring prefix = line.substr(prefix_start, prefix_end - prefix_start);
|
||||
auto prefix = line.substr(prefix_start, prefix_end - prefix_start);
|
||||
|
||||
// find all names that start with the selected prefix
|
||||
std::vector<std::wstring> completions;
|
||||
@ -624,7 +624,7 @@ void ChatPrompt::nickCompletion(const std::set<std::string> &names, bool backwar
|
||||
{
|
||||
while (word_end < line.size() && !iswspace(line[word_end]))
|
||||
++word_end;
|
||||
std::wstring word = line.substr(prefix_start, word_end - prefix_start);
|
||||
auto word = line.substr(prefix_start, word_end - prefix_start);
|
||||
|
||||
// cycle through completions
|
||||
for (u32 i = 0; i < completions.size(); ++i)
|
||||
@ -640,7 +640,7 @@ void ChatPrompt::nickCompletion(const std::set<std::string> &names, bool backwar
|
||||
}
|
||||
}
|
||||
}
|
||||
std::wstring replacement = completions[replacement_index];
|
||||
const auto &replacement = completions[replacement_index];
|
||||
if (word_end < line.size() && iswspace(line[word_end]))
|
||||
++word_end;
|
||||
|
||||
|
@ -138,8 +138,8 @@ void Camera::notifyFovChange()
|
||||
// Returns the fractional part of x
|
||||
inline f32 my_modf(f32 x)
|
||||
{
|
||||
double dummy;
|
||||
return modf(x, &dummy);
|
||||
float dummy;
|
||||
return std::modf(x, &dummy);
|
||||
}
|
||||
|
||||
void Camera::step(f32 dtime)
|
||||
@ -407,10 +407,10 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
||||
f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0;
|
||||
|
||||
f32 bobknob = 1.2;
|
||||
f32 bobtmp = sin(pow(bobfrac, bobknob) * M_PI);
|
||||
f32 bobtmp = std::sin(std::pow(bobfrac, bobknob) * M_PI);
|
||||
|
||||
v3f bobvec = v3f(
|
||||
0.3 * bobdir * sin(bobfrac * M_PI),
|
||||
0.3 * bobdir * std::sin(bobfrac * M_PI),
|
||||
-0.28 * bobtmp * bobtmp,
|
||||
0.);
|
||||
|
||||
@ -531,11 +531,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
||||
addArmInertia(yaw);
|
||||
|
||||
// Position the wielded item
|
||||
//v3f wield_position = v3f(45, -35, 65);
|
||||
v3f wield_position = v3f(m_wieldmesh_offset.X, m_wieldmesh_offset.Y, 65);
|
||||
//v3f wield_rotation = v3f(-100, 120, -100);
|
||||
v3f wield_rotation = v3f(-100, 120, -100);
|
||||
wield_position.Y += fabs(m_wield_change_timer)*320 - 40;
|
||||
wield_position.Y += std::abs(m_wield_change_timer)*320 - 40;
|
||||
if(m_digging_anim < 0.05 || m_digging_anim > 0.5)
|
||||
{
|
||||
f32 frac = 1.0;
|
||||
@ -543,33 +541,29 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
||||
frac = 2.0 * (m_digging_anim - 0.5);
|
||||
// This value starts from 1 and settles to 0
|
||||
f32 ratiothing = std::pow((1.0f - tool_reload_ratio), 0.5f);
|
||||
//f32 ratiothing2 = pow(ratiothing, 0.5f);
|
||||
f32 ratiothing2 = (easeCurve(ratiothing*0.5))*2.0;
|
||||
wield_position.Y -= frac * 25.0 * pow(ratiothing2, 1.7f);
|
||||
//wield_position.Z += frac * 5.0 * ratiothing2;
|
||||
wield_position.X -= frac * 35.0 * pow(ratiothing2, 1.1f);
|
||||
wield_rotation.Y += frac * 70.0 * pow(ratiothing2, 1.4f);
|
||||
//wield_rotation.X -= frac * 15.0 * pow(ratiothing2, 1.4f);
|
||||
//wield_rotation.Z += frac * 15.0 * pow(ratiothing2, 1.0f);
|
||||
wield_position.Y -= frac * 25.0f * std::pow(ratiothing2, 1.7f);
|
||||
wield_position.X -= frac * 35.0f * std::pow(ratiothing2, 1.1f);
|
||||
wield_rotation.Y += frac * 70.0f * std::pow(ratiothing2, 1.4f);
|
||||
}
|
||||
if (m_digging_button != -1)
|
||||
{
|
||||
f32 digfrac = m_digging_anim;
|
||||
wield_position.X -= 50 * sin(pow(digfrac, 0.8f) * M_PI);
|
||||
wield_position.Y += 24 * sin(digfrac * 1.8 * M_PI);
|
||||
wield_position.X -= 50 * std::sin(std::pow(digfrac, 0.8f) * M_PI);
|
||||
wield_position.Y += 24 * std::sin(digfrac * 1.8 * M_PI);
|
||||
wield_position.Z += 25 * 0.5;
|
||||
|
||||
// Euler angles are PURE EVIL, so why not use quaternions?
|
||||
core::quaternion quat_begin(wield_rotation * core::DEGTORAD);
|
||||
core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD);
|
||||
core::quaternion quat_slerp;
|
||||
quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI));
|
||||
quat_slerp.slerp(quat_begin, quat_end, std::sin(digfrac * M_PI));
|
||||
quat_slerp.toEuler(wield_rotation);
|
||||
wield_rotation *= core::RADTODEG;
|
||||
} else {
|
||||
f32 bobfrac = my_modf(m_view_bobbing_anim);
|
||||
wield_position.X -= sin(bobfrac*M_PI*2.0) * 3.0;
|
||||
wield_position.Y += sin(my_modf(bobfrac*2.0)*M_PI) * 3.0;
|
||||
wield_position.X -= std::sin(bobfrac*M_PI*2.0) * 3.0;
|
||||
wield_position.Y += std::sin(my_modf(bobfrac*2.0)*M_PI) * 3.0;
|
||||
}
|
||||
m_wieldnode->setPosition(wield_position);
|
||||
m_wieldnode->setRotation(wield_rotation);
|
||||
@ -584,8 +578,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
||||
// view bobbing is enabled and free_move is off,
|
||||
// start (or continue) the view bobbing animation.
|
||||
const v3f &speed = player->getSpeed();
|
||||
const bool movement_XZ = hypot(speed.X, speed.Z) > BS;
|
||||
const bool movement_Y = fabs(speed.Y) > BS;
|
||||
const bool movement_XZ = std::hypot(speed.X, speed.Z) > BS;
|
||||
const bool movement_Y = std::abs(speed.Y) > BS;
|
||||
|
||||
const bool walking = movement_XZ && player->touching_ground;
|
||||
const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid;
|
||||
|
@ -1806,7 +1806,7 @@ struct TextureUpdateArgs {
|
||||
void Client::showUpdateProgressTexture(void *args, u32 progress, u32 max_progress)
|
||||
{
|
||||
TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
|
||||
u16 cur_percent = ceil(progress / (double) max_progress * 100.);
|
||||
u16 cur_percent = std::ceil(progress / max_progress * 100.f);
|
||||
|
||||
// update the loading menu -- if necessary
|
||||
bool do_draw = false;
|
||||
@ -1971,21 +1971,11 @@ void Client::makeScreenshot()
|
||||
raw_image->drop();
|
||||
}
|
||||
|
||||
bool Client::shouldShowMinimap() const
|
||||
{
|
||||
return !m_minimap_disabled_by_server;
|
||||
}
|
||||
|
||||
void Client::pushToEventQueue(ClientEvent *event)
|
||||
{
|
||||
m_client_event_queue.push(event);
|
||||
}
|
||||
|
||||
void Client::showMinimap(const bool show)
|
||||
{
|
||||
m_game_ui->showMinimap(show);
|
||||
}
|
||||
|
||||
// IGameDef interface
|
||||
// Under envlock
|
||||
IItemDefManager* Client::getItemDefManager()
|
||||
@ -2133,3 +2123,11 @@ const std::string &Client::getFormspecPrepend() const
|
||||
{
|
||||
return m_env.getLocalPlayer()->formspec_prepend;
|
||||
}
|
||||
|
||||
void Client::removeActiveObjectSounds(u16 id)
|
||||
{
|
||||
for (auto it : m_sounds_to_objects) {
|
||||
if (it.second == id)
|
||||
m_sound->stopSound(it.first);
|
||||
}
|
||||
}
|
||||
|
@ -369,8 +369,6 @@ public:
|
||||
Camera* getCamera () { return m_camera; }
|
||||
scene::ISceneManager *getSceneManager();
|
||||
|
||||
bool shouldShowMinimap() const;
|
||||
|
||||
// IGameDef interface
|
||||
IItemDefManager* getItemDefManager() override;
|
||||
const NodeDefManager* getNodeDefManager() override;
|
||||
@ -412,8 +410,6 @@ public:
|
||||
|
||||
void pushToEventQueue(ClientEvent *event);
|
||||
|
||||
void showMinimap(bool show = true);
|
||||
|
||||
// IP and port we're connected to
|
||||
const Address getServerAddress();
|
||||
|
||||
@ -475,6 +471,9 @@ private:
|
||||
|
||||
bool canSendChatMessage() const;
|
||||
|
||||
// remove sounds attached to object
|
||||
void removeActiveObjectSounds(u16 id);
|
||||
|
||||
float m_packetcounter_timer = 0.0f;
|
||||
float m_connection_reinit_timer = 0.1f;
|
||||
float m_avg_rtt_timer = 0.0f;
|
||||
@ -498,7 +497,6 @@ private:
|
||||
ELoginRegister m_allow_login_or_register = ELoginRegister::Any;
|
||||
Camera *m_camera = nullptr;
|
||||
Minimap *m_minimap = nullptr;
|
||||
bool m_minimap_disabled_by_server = false;
|
||||
|
||||
// Server serialization version
|
||||
u8 m_server_ser_ver;
|
||||
|
@ -182,7 +182,7 @@ void ClientEnvironment::step(float dtime)
|
||||
Stuff that has a maximum time increment
|
||||
*/
|
||||
|
||||
u32 steps = ceil(dtime / dtime_max_increment);
|
||||
u32 steps = std::ceil(dtime / dtime_max_increment);
|
||||
f32 dtime_part = dtime / steps;
|
||||
for (; steps > 0; --steps) {
|
||||
/*
|
||||
|
@ -114,12 +114,6 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
|
||||
m_rendering_engine->setupTopLevelWindow();
|
||||
|
||||
/*
|
||||
This changes the minimum allowed number of vertices in a VBO.
|
||||
Default is 500.
|
||||
*/
|
||||
//driver->setMinHardwareBufferVertexCount(50);
|
||||
|
||||
// Create game callback for menus
|
||||
g_gamecallback = new MainGameCallback();
|
||||
|
||||
@ -204,11 +198,18 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
while (m_rendering_engine->run() && !*kill &&
|
||||
!g_gamecallback->shutdown_requested) {
|
||||
// Set the window caption
|
||||
#if IRRLICHT_VERSION_MT_REVISION >= 15
|
||||
auto driver_name = m_rendering_engine->getVideoDriver()->getName();
|
||||
#else
|
||||
auto driver_name = wide_to_utf8(m_rendering_engine->getVideoDriver()->getName());
|
||||
#endif
|
||||
std::string caption = std::string(PROJECT_NAME_C) +
|
||||
" " + g_version_hash +
|
||||
" [" + gettext("Main Menu") + "]" +
|
||||
" [" + driver_name + "]";
|
||||
|
||||
m_rendering_engine->get_raw_device()->
|
||||
setWindowCaption((utf8_to_wide(PROJECT_NAME_C) +
|
||||
L" " + utf8_to_wide(g_version_hash) +
|
||||
L" [" + wstrgettext("Main Menu") + L"]" +
|
||||
L" [" + m_rendering_engine->getVideoDriver()->getName() + L"]" ).c_str());
|
||||
setWindowCaption(utf8_to_wide(caption).c_str());
|
||||
|
||||
try { // This is used for catching disconnects
|
||||
|
||||
@ -248,10 +249,10 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
m_rendering_engine->get_video_driver()->setTextureCreationFlag(
|
||||
video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
receiver->m_touchscreengui = new TouchScreenGUI(m_rendering_engine->get_raw_device(), receiver);
|
||||
g_touchscreengui = receiver->m_touchscreengui;
|
||||
#endif
|
||||
if (g_settings->getBool("enable_touch")) {
|
||||
receiver->m_touchscreengui = new TouchScreenGUI(m_rendering_engine->get_raw_device(), receiver);
|
||||
g_touchscreengui = receiver->m_touchscreengui;
|
||||
}
|
||||
|
||||
the_game(
|
||||
kill,
|
||||
@ -282,11 +283,11 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
|
||||
m_rendering_engine->get_scene_manager()->clear();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
delete g_touchscreengui;
|
||||
g_touchscreengui = NULL;
|
||||
receiver->m_touchscreengui = NULL;
|
||||
#endif
|
||||
if (g_touchscreengui) {
|
||||
delete g_touchscreengui;
|
||||
g_touchscreengui = NULL;
|
||||
receiver->m_touchscreengui = NULL;
|
||||
}
|
||||
|
||||
/* Save the settings when leaving the game.
|
||||
* This makes sure that setting changes made in-game are persisted even
|
||||
|
@ -1305,7 +1305,7 @@ void ClientMap::updateTransparentMeshBuffers()
|
||||
ScopeProfiler sp(g_profiler, "CM::updateTransparentMeshBuffers", SPT_AVG);
|
||||
u32 sorted_blocks = 0;
|
||||
u32 unsorted_blocks = 0;
|
||||
f32 sorting_distance_sq = pow(m_cache_transparency_sorting_distance * BS, 2.0f);
|
||||
f32 sorting_distance_sq = std::pow(m_cache_transparency_sorting_distance * BS, 2.0f);
|
||||
|
||||
|
||||
// Update the order of transparent mesh buffers in each mesh
|
||||
|
@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "util/serialize.h"
|
||||
#include "util/sha1.h"
|
||||
#include "util/string.h"
|
||||
#include <sstream>
|
||||
|
||||
static std::string getMediaCacheDir()
|
||||
{
|
||||
@ -41,7 +42,16 @@ bool clientMediaUpdateCache(const std::string &raw_hash, const std::string &file
|
||||
std::string sha1_hex = hex_encode(raw_hash);
|
||||
if (!media_cache.exists(sha1_hex))
|
||||
return media_cache.update(sha1_hex, filedata);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool clientMediaUpdateCacheCopy(const std::string &raw_hash, const std::string &path)
|
||||
{
|
||||
FileCache media_cache(getMediaCacheDir());
|
||||
std::string sha1_hex = hex_encode(raw_hash);
|
||||
if (!media_cache.exists(sha1_hex))
|
||||
return media_cache.updateCopyFile(sha1_hex, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -189,10 +199,6 @@ void ClientMediaDownloader::initialStep(Client *client)
|
||||
|
||||
assert(m_uncached_received_count == 0);
|
||||
|
||||
// Create the media cache dir if we are likely to write to it
|
||||
if (m_uncached_count != 0)
|
||||
createCacheDirs();
|
||||
|
||||
// If we found all files in the cache, report this fact to the server.
|
||||
// If the server reported no remote servers, immediately start
|
||||
// conventional transfers. Note: if cURL support is not compiled in,
|
||||
@ -511,18 +517,6 @@ IClientMediaDownloader::IClientMediaDownloader():
|
||||
{
|
||||
}
|
||||
|
||||
void IClientMediaDownloader::createCacheDirs()
|
||||
{
|
||||
if (!m_write_to_cache)
|
||||
return;
|
||||
|
||||
std::string path = getMediaCacheDir();
|
||||
if (!fs::CreateAllDirs(path)) {
|
||||
errorstream << "Client: Could not create media cache directory: "
|
||||
<< path << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool IClientMediaDownloader::tryLoadFromCache(const std::string &name,
|
||||
const std::string &sha1, Client *client)
|
||||
{
|
||||
@ -547,11 +541,9 @@ bool IClientMediaDownloader::checkAndLoad(
|
||||
// Compute actual checksum of data
|
||||
std::string data_sha1;
|
||||
{
|
||||
SHA1 data_sha1_calculator;
|
||||
data_sha1_calculator.addBytes(data.c_str(), data.size());
|
||||
unsigned char *data_tmpdigest = data_sha1_calculator.getDigest();
|
||||
data_sha1.assign((char*) data_tmpdigest, 20);
|
||||
free(data_tmpdigest);
|
||||
SHA1 ctx;
|
||||
ctx.addBytes(data);
|
||||
data_sha1 = ctx.getDigest();
|
||||
}
|
||||
|
||||
// Check that received file matches announced checksum
|
||||
@ -726,8 +718,6 @@ void SingleMediaDownloader::initialStep(Client *client)
|
||||
if (isDone())
|
||||
return;
|
||||
|
||||
createCacheDirs();
|
||||
|
||||
// If the server reported no remote servers, immediately fall back to
|
||||
// conventional transfer.
|
||||
if (!USE_CURL || m_remotes.empty()) {
|
||||
|
@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irrlichttypes.h"
|
||||
#include "filecache.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
@ -35,10 +34,15 @@ struct HTTPFetchResult;
|
||||
#define MTHASHSET_FILE_NAME "index.mth"
|
||||
|
||||
// Store file into media cache (unless it exists already)
|
||||
// Validating the hash is responsibility of the caller
|
||||
// Caller should check the hash.
|
||||
// return true if something was updated
|
||||
bool clientMediaUpdateCache(const std::string &raw_hash,
|
||||
const std::string &filedata);
|
||||
|
||||
// Copy file on disk(!) into media cache (unless it exists already)
|
||||
bool clientMediaUpdateCacheCopy(const std::string &raw_hash,
|
||||
const std::string &path);
|
||||
|
||||
// more of a base class than an interface but this name was most convenient...
|
||||
class IClientMediaDownloader
|
||||
{
|
||||
@ -81,8 +85,6 @@ protected:
|
||||
virtual bool loadMedia(Client *client, const std::string &data,
|
||||
const std::string &name) = 0;
|
||||
|
||||
void createCacheDirs();
|
||||
|
||||
bool tryLoadFromCache(const std::string &name, const std::string &sha1,
|
||||
Client *client);
|
||||
|
||||
|
@ -1216,7 +1216,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
|
||||
}
|
||||
}
|
||||
|
||||
if (node && fabs(m_prop.automatic_rotate) > 0.001f) {
|
||||
if (node && std::abs(m_prop.automatic_rotate) > 0.001f) {
|
||||
// This is the child node's rotation. It is only used for automatic_rotate.
|
||||
v3f local_rot = node->getRotation();
|
||||
local_rot.Y = modulo360f(local_rot.Y - dtime * core::RADTODEG *
|
||||
|
@ -77,7 +77,7 @@ MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector
|
||||
scene::IMeshManipulator *mm):
|
||||
data(input),
|
||||
collector(output),
|
||||
nodedef(data->m_client->ndef()),
|
||||
nodedef(data->nodedef),
|
||||
meshmanip(mm),
|
||||
blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE),
|
||||
enable_mesh_cache(g_settings->getBool("enable_mesh_cache") &&
|
||||
@ -617,14 +617,14 @@ void MapblockMeshGenerator::calculateCornerLevels()
|
||||
cur_liquid.corner_levels[k][i] = getCornerLevel(i, k);
|
||||
}
|
||||
|
||||
f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
|
||||
f32 MapblockMeshGenerator::getCornerLevel(int i, int k) const
|
||||
{
|
||||
float sum = 0;
|
||||
int count = 0;
|
||||
int air_count = 0;
|
||||
for (int dk = 0; dk < 2; dk++)
|
||||
for (int di = 0; di < 2; di++) {
|
||||
LiquidData::NeighborData &neighbor_data = cur_liquid.neighbors[k + dk][i + di];
|
||||
const LiquidData::NeighborData &neighbor_data = cur_liquid.neighbors[k + dk][i + di];
|
||||
content_t content = neighbor_data.content;
|
||||
|
||||
// If top is liquid, draw starting from top of node
|
||||
|
@ -138,7 +138,7 @@ private:
|
||||
void prepareLiquidNodeDrawing();
|
||||
void getLiquidNeighborhood();
|
||||
void calculateCornerLevels();
|
||||
f32 getCornerLevel(int i, int k);
|
||||
f32 getCornerLevel(int i, int k) const;
|
||||
void drawLiquidSides();
|
||||
void drawLiquidTop();
|
||||
void drawLiquidBottom();
|
||||
|
@ -70,14 +70,13 @@ public:
|
||||
}
|
||||
void dereg(MtEvent::Type type, event_receive_func f, void *data) override
|
||||
{
|
||||
std::map<MtEvent::Type, Dest>::iterator i = m_dest.find(type);
|
||||
auto i = m_dest.find(type);
|
||||
if (i != m_dest.end()) {
|
||||
std::list<FuncSpec> &funcs = i->second.funcs;
|
||||
auto j = funcs.begin();
|
||||
while (j != funcs.end()) {
|
||||
for (auto j = funcs.begin(); j != funcs.end(); ) {
|
||||
bool remove = (j->f == f && (!data || j->d == data));
|
||||
if (remove)
|
||||
funcs.erase(j++);
|
||||
j = funcs.erase(j);
|
||||
else
|
||||
++j;
|
||||
}
|
||||
|
@ -28,6 +28,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
void FileCache::createDir()
|
||||
{
|
||||
if (!fs::CreateAllDirs(m_dir)) {
|
||||
errorstream << "Could not create cache directory: "
|
||||
<< m_dir << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
{
|
||||
std::ifstream fis(path.c_str(), std::ios_base::binary);
|
||||
@ -40,8 +48,8 @@ bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
|
||||
bool bad = false;
|
||||
for(;;){
|
||||
char buf[1024];
|
||||
fis.read(buf, 1024);
|
||||
char buf[4096];
|
||||
fis.read(buf, sizeof(buf));
|
||||
std::streamsize len = fis.gcount();
|
||||
os.write(buf, len);
|
||||
if(fis.eof())
|
||||
@ -59,8 +67,9 @@ bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
return !bad;
|
||||
}
|
||||
|
||||
bool FileCache::updateByPath(const std::string &path, const std::string &data)
|
||||
bool FileCache::updateByPath(const std::string &path, std::string_view data)
|
||||
{
|
||||
createDir();
|
||||
std::ofstream file(path.c_str(), std::ios_base::binary |
|
||||
std::ios_base::trunc);
|
||||
|
||||
@ -71,13 +80,13 @@ bool FileCache::updateByPath(const std::string &path, const std::string &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(data.c_str(), data.length());
|
||||
file << data;
|
||||
file.close();
|
||||
|
||||
return !file.fail();
|
||||
}
|
||||
|
||||
bool FileCache::update(const std::string &name, const std::string &data)
|
||||
bool FileCache::update(const std::string &name, std::string_view data)
|
||||
{
|
||||
std::string path = m_dir + DIR_DELIM + name;
|
||||
return updateByPath(path, data);
|
||||
@ -95,3 +104,11 @@ bool FileCache::exists(const std::string &name)
|
||||
std::ifstream fis(path.c_str(), std::ios_base::binary);
|
||||
return fis.good();
|
||||
}
|
||||
|
||||
bool FileCache::updateCopyFile(const std::string &name, const std::string &src_path)
|
||||
{
|
||||
std::string path = m_dir + DIR_DELIM + name;
|
||||
|
||||
createDir();
|
||||
return fs::CopyFileContents(src_path, path);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class FileCache
|
||||
{
|
||||
@ -31,13 +32,17 @@ public:
|
||||
*/
|
||||
FileCache(const std::string &dir) : m_dir(dir) {}
|
||||
|
||||
bool update(const std::string &name, const std::string &data);
|
||||
bool update(const std::string &name, std::string_view data);
|
||||
bool load(const std::string &name, std::ostream &os);
|
||||
bool exists(const std::string &name);
|
||||
|
||||
// Copy another file on disk into the cache
|
||||
bool updateCopyFile(const std::string &name, const std::string &src_path);
|
||||
|
||||
private:
|
||||
std::string m_dir;
|
||||
|
||||
void createDir();
|
||||
bool loadByPath(const std::string &path, std::ostream &os);
|
||||
bool updateByPath(const std::string &path, const std::string &data);
|
||||
bool updateByPath(const std::string &path, std::string_view data);
|
||||
};
|
||||
|
@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "client/mapblock_mesh.h"
|
||||
#include "client/sound.h"
|
||||
#include "clientmap.h"
|
||||
#include "clientmedia.h" // For clientMediaUpdateCacheCopy
|
||||
#include "clouds.h"
|
||||
#include "config.h"
|
||||
#include "content_cao.h"
|
||||
@ -663,11 +664,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#define SIZE_TAG "size[11,5.5]"
|
||||
#else
|
||||
#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
|
||||
#endif
|
||||
#define SIZE_TAG "size[11,5.5,true]" // Fixed size (ignored in touchscreen mode)
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************/
|
||||
@ -769,6 +766,7 @@ protected:
|
||||
bool initSound();
|
||||
bool createSingleplayerServer(const std::string &map_dir,
|
||||
const SubgameSpec &gamespec, u16 port);
|
||||
void copyServerClientCache();
|
||||
|
||||
// Client creation
|
||||
bool createClient(const GameStartData &start_data);
|
||||
@ -1019,13 +1017,11 @@ private:
|
||||
// this happens in pause menu in singleplayer
|
||||
bool m_is_paused = false;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
bool m_cache_hold_aux1;
|
||||
bool m_touch_simulate_aux1 = false;
|
||||
bool m_touch_use_crosshair;
|
||||
inline bool isNoCrosshairAllowed() {
|
||||
inline bool isTouchCrosshairDisabled() {
|
||||
return !m_touch_use_crosshair && camera->getCameraMode() == CAMERA_MODE_FIRST;
|
||||
}
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
bool m_android_chat_open;
|
||||
#endif
|
||||
@ -1073,11 +1069,6 @@ Game::Game() :
|
||||
&settingChangedCallback, this);
|
||||
|
||||
readSettings();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_cache_hold_aux1 = false; // This is initialised properly later
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1180,9 +1171,7 @@ bool Game::startup(bool *kill,
|
||||
|
||||
m_first_loop_after_window_activation = true;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_touch_use_crosshair = g_settings->getBool("touch_use_crosshair");
|
||||
#endif
|
||||
|
||||
g_client_translations->clear();
|
||||
|
||||
@ -1217,10 +1206,8 @@ void Game::run()
|
||||
|
||||
set_light_table(g_settings->getFloat("display_gamma"));
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_cache_hold_aux1 = g_settings->getBool("fast_move")
|
||||
m_touch_simulate_aux1 = g_settings->getBool("fast_move")
|
||||
&& client->checkPrivilege("fast");
|
||||
#endif
|
||||
|
||||
const irr::core::dimension2du initial_screen_size(
|
||||
g_settings->getU16("screen_w"),
|
||||
@ -1288,9 +1275,6 @@ void Game::run()
|
||||
updateFrame(&graph, &stats, dtime, cam_view);
|
||||
updateProfilerGraphs(&graph);
|
||||
|
||||
// Update if minimap has been disabled by the server
|
||||
m_game_ui->m_flags.show_minimap &= client->shouldShowMinimap();
|
||||
|
||||
if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) {
|
||||
showPauseMenu();
|
||||
}
|
||||
@ -1309,9 +1293,8 @@ void Game::shutdown()
|
||||
// Clear text when exiting.
|
||||
m_game_ui->clearText();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
g_touchscreengui->hide();
|
||||
#endif
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->hide();
|
||||
|
||||
showOverlayMessage(N_("Shutting down..."), 0, 0, false);
|
||||
|
||||
@ -1453,9 +1436,31 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
|
||||
false, nullptr, error_message);
|
||||
server->start();
|
||||
|
||||
copyServerClientCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Game::copyServerClientCache()
|
||||
{
|
||||
// It would be possible to let the client directly read the media files
|
||||
// from where the server knows they are. But aside from being more complicated
|
||||
// it would also *not* fill the media cache and cause slower joining of
|
||||
// remote servers.
|
||||
// (Imagine that you launch a game once locally and then connect to a server.)
|
||||
|
||||
assert(server);
|
||||
auto map = server->getMediaList();
|
||||
u32 n = 0;
|
||||
for (auto &it : map) {
|
||||
assert(it.first.size() == 20); // SHA1
|
||||
if (clientMediaUpdateCacheCopy(it.first, it.second))
|
||||
n++;
|
||||
}
|
||||
infostream << "Copied " << n << " files directly from server to client cache"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
bool Game::createClient(const GameStartData &start_data)
|
||||
{
|
||||
showOverlayMessage(N_("Creating client..."), 0, 10);
|
||||
@ -1499,11 +1504,10 @@ bool Game::createClient(const GameStartData &start_data)
|
||||
if (client->modsLoaded())
|
||||
client->getScript()->on_camera_ready(camera);
|
||||
client->setCamera(camera);
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
if (g_touchscreengui) {
|
||||
g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
|
||||
g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clouds
|
||||
*/
|
||||
@ -1530,18 +1534,20 @@ bool Game::createClient(const GameStartData &start_data)
|
||||
|
||||
/* Set window caption
|
||||
*/
|
||||
std::wstring str = utf8_to_wide(PROJECT_NAME_C);
|
||||
str += L" ";
|
||||
str += utf8_to_wide(g_version_hash);
|
||||
str += L" [";
|
||||
str += simple_singleplayer_mode ? wstrgettext("Singleplayer")
|
||||
: wstrgettext("Multiplayer");
|
||||
str += L"]";
|
||||
str += L" [";
|
||||
str += driver->getName();
|
||||
str += L"]";
|
||||
#if IRRLICHT_VERSION_MT_REVISION >= 15
|
||||
auto driver_name = driver->getName();
|
||||
#else
|
||||
auto driver_name = wide_to_utf8(driver->getName());
|
||||
#endif
|
||||
std::string str = std::string(PROJECT_NAME_C) +
|
||||
" " + g_version_hash + " [";
|
||||
str += simple_singleplayer_mode ? gettext("Singleplayer")
|
||||
: gettext("Multiplayer");
|
||||
str += "] [";
|
||||
str += driver_name;
|
||||
str += "]";
|
||||
|
||||
device->setWindowCaption(str.c_str());
|
||||
device->setWindowCaption(utf8_to_wide(str).c_str());
|
||||
|
||||
LocalPlayer *player = client->getEnv().getLocalPlayer();
|
||||
player->hurt_tilt_timer = 0;
|
||||
@ -1571,10 +1577,8 @@ bool Game::initGui()
|
||||
gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
|
||||
-1, chat_backend, client, &g_menumgr);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->init(texture_src);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2008,18 +2012,17 @@ void Game::processUserInput(f32 dtime)
|
||||
} else {
|
||||
input->clear();
|
||||
}
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
g_touchscreengui->hide();
|
||||
#endif
|
||||
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->hide();
|
||||
|
||||
} else {
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
/* on touchscreengui step may generate own input events which ain't
|
||||
* what we want in case we just did clear them */
|
||||
g_touchscreengui->show();
|
||||
g_touchscreengui->step(dtime);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_game_focused = true;
|
||||
}
|
||||
@ -2211,13 +2214,11 @@ void Game::processItemSelection(u16 *new_playeritem)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
std::optional<u16> selection = g_touchscreengui->getHotbarSelection();
|
||||
if (selection)
|
||||
*new_playeritem = *selection;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clamp selection again in case it wasn't changed but max_item was
|
||||
*new_playeritem = MYMIN(*new_playeritem, max_item);
|
||||
@ -2368,9 +2369,7 @@ void Game::toggleFast()
|
||||
m_game_ui->showTranslatedStatusText("Fast mode disabled");
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_cache_hold_aux1 = fast_move && has_fast_privs;
|
||||
#endif
|
||||
m_touch_simulate_aux1 = fast_move && has_fast_privs;
|
||||
}
|
||||
|
||||
|
||||
@ -2455,26 +2454,14 @@ void Game::toggleMinimap(bool shift_pressed)
|
||||
// -->
|
||||
u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags;
|
||||
|
||||
if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) {
|
||||
m_game_ui->m_flags.show_minimap = false;
|
||||
} else {
|
||||
|
||||
// If radar is disabled, try to find a non radar mode or fall back to 0
|
||||
if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE))
|
||||
while (mapper->getModeIndex() &&
|
||||
mapper->getModeDef().type == MINIMAP_TYPE_RADAR)
|
||||
mapper->nextMode();
|
||||
|
||||
m_game_ui->m_flags.show_minimap = mapper->getModeDef().type != MINIMAP_TYPE_OFF;
|
||||
// <--
|
||||
// End of 'not so satifying code'
|
||||
if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) ||
|
||||
(hud && hud->hasElementOfType(HUD_ELEM_MINIMAP)))
|
||||
m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label));
|
||||
else
|
||||
m_game_ui->showTranslatedStatusText(
|
||||
"Minimap currently disabled by game or mod");
|
||||
}
|
||||
if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) {
|
||||
// If radar is disabled, try to find a non radar mode or fall back to 0
|
||||
if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE))
|
||||
while (mapper->getModeIndex() &&
|
||||
mapper->getModeDef().type == MINIMAP_TYPE_RADAR)
|
||||
mapper->nextMode();
|
||||
}
|
||||
m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label));
|
||||
}
|
||||
|
||||
void Game::toggleFog()
|
||||
@ -2627,10 +2614,8 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
|
||||
Since Minetest has its own code to synthesize mouse events from touch events,
|
||||
this results in duplicated input. To avoid that, we don't enable relative
|
||||
mouse mode if we're in touchscreen mode. */
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
if (cur_control)
|
||||
cur_control->setRelativeMode(!isMenuActive());
|
||||
#endif
|
||||
cur_control->setRelativeMode(!g_touchscreengui && !isMenuActive());
|
||||
|
||||
if ((device->isWindowActive() && device->isWindowFocused()
|
||||
&& !isMenuActive()) || input->isRandom()) {
|
||||
@ -2668,17 +2653,15 @@ f32 Game::getSensitivityScaleFactor() const
|
||||
// Multiply by a constant such that it becomes 1.0 at 72 degree FOV and
|
||||
// 16:9 aspect ratio to minimize disruption of existing sensitivity
|
||||
// settings.
|
||||
return tan(fov_y / 2.0f) * 1.3763818698f;
|
||||
return std::tan(fov_y / 2.0f) * 1.3763819f;
|
||||
}
|
||||
|
||||
void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
cam->camera_yaw += g_touchscreengui->getYawChange();
|
||||
cam->camera_pitch += g_touchscreengui->getPitchChange();
|
||||
} else {
|
||||
#endif
|
||||
v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
|
||||
v2s32 dist = input->getMousePos() - center;
|
||||
|
||||
@ -2692,9 +2675,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
||||
|
||||
if (dist.X != 0 || dist.Y != 0)
|
||||
input->setMousePos(center.X, center.Y);
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_cache_enable_joysticks) {
|
||||
f32 sens_scale = getSensitivityScaleFactor();
|
||||
@ -2735,20 +2716,18 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
|
||||
client->activeObjectsReceived() && !player->isDead()) {
|
||||
control.movement_speed = 1.0f;
|
||||
// sideways movement only
|
||||
float dx = sin(control.movement_direction);
|
||||
control.movement_direction = atan2(dx, 1.0f);
|
||||
float dx = std::sin(control.movement_direction);
|
||||
control.movement_direction = std::atan2(dx, 1.0f);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
/* For touch, simulate holding down AUX1 (fast move) if the user has
|
||||
* the fast_move setting toggled on. If there is an aux1 key defined for
|
||||
* touch then its meaning is inverted (i.e. holding aux1 means walk and
|
||||
* not fast)
|
||||
*/
|
||||
if (m_cache_hold_aux1) {
|
||||
if (g_touchscreengui && m_touch_simulate_aux1) {
|
||||
control.aux1 = control.aux1 ^ true;
|
||||
}
|
||||
#endif
|
||||
|
||||
client->setPlayerControl(control);
|
||||
|
||||
@ -2777,10 +2756,16 @@ inline void Game::step(f32 dtime)
|
||||
g_settings->getFloat("fps_max_unfocused") :
|
||||
g_settings->getFloat("fps_max");
|
||||
fps_max = std::max(fps_max, 1.0f);
|
||||
float steplen = 1.0f / fps_max;
|
||||
/*
|
||||
* Unless you have a barebones game, running the server at more than 60Hz
|
||||
* is hardly realistic and you're at the point of diminishing returns.
|
||||
* fps_max is also not necessarily anywhere near the FPS actually achieved
|
||||
* (also due to vsync).
|
||||
*/
|
||||
fps_max = std::min(fps_max, 60.0f);
|
||||
|
||||
server->setStepSettings(Server::StepSettings{
|
||||
steplen,
|
||||
1.0f / fps_max,
|
||||
m_is_paused
|
||||
});
|
||||
|
||||
@ -3229,10 +3214,8 @@ void Game::updateCamera(f32 dtime)
|
||||
|
||||
camera->toggleCameraMode();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
|
||||
#endif
|
||||
g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled());
|
||||
|
||||
// Make the player visible depending on camera mode.
|
||||
playercao->updateMeshCulling();
|
||||
@ -3333,8 +3316,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
||||
}
|
||||
shootline.end = shootline.start + camera_direction * BS * d;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui && isNoCrosshairAllowed()) {
|
||||
if (g_touchscreengui && isTouchCrosshairDisabled()) {
|
||||
shootline = g_touchscreengui->getShootline();
|
||||
// Scale shootline to the acual distance the player can reach
|
||||
shootline.end = shootline.start +
|
||||
@ -3342,7 +3324,6 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
||||
shootline.start += intToFloat(camera_offset, BS);
|
||||
shootline.end += intToFloat(camera_offset, BS);
|
||||
}
|
||||
#endif
|
||||
|
||||
PointedThing pointed = updatePointedThing(shootline,
|
||||
selected_def.liquids_pointable,
|
||||
@ -3353,10 +3334,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
||||
if (pointed != runData.pointed_old)
|
||||
infostream << "Pointing at " << pointed.dump() << std::endl;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->applyContextControls(selected_def.touch_interaction.getMode(pointed));
|
||||
#endif
|
||||
|
||||
// Note that updating the selection mesh every frame is not particularly efficient,
|
||||
// but the halo rendering code is already inefficient so there's no point in optimizing it here
|
||||
@ -3734,11 +3713,11 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
|
||||
break;
|
||||
};
|
||||
case NDT_SIGNLIKE: {
|
||||
rotate90 = abs(pdir.X) < abs(pdir.Z);
|
||||
rotate90 = std::abs(pdir.X) < std::abs(pdir.Z);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rotate90 = abs(pdir.X) > abs(pdir.Z);
|
||||
rotate90 = std::abs(pdir.X) > std::abs(pdir.Z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4276,14 +4255,14 @@ void Game::updateShadows()
|
||||
if (!shadow)
|
||||
return;
|
||||
|
||||
float in_timeofday = fmod(runData.time_of_day_smooth, 1.0f);
|
||||
float in_timeofday = std::fmod(runData.time_of_day_smooth, 1.0f);
|
||||
|
||||
float timeoftheday = getWickedTimeOfDay(in_timeofday);
|
||||
bool is_day = timeoftheday > 0.25 && timeoftheday < 0.75;
|
||||
bool is_shadow_visible = is_day ? sky->getSunVisible() : sky->getMoonVisible();
|
||||
shadow->setShadowIntensity(is_shadow_visible ? client->getEnv().getLocalPlayer()->getLighting().shadow_intensity : 0.0f);
|
||||
|
||||
timeoftheday = fmod(timeoftheday + 0.75f, 0.5f) + 0.25f;
|
||||
timeoftheday = std::fmod(timeoftheday + 0.75f, 0.5f) + 0.25f;
|
||||
const float offset_constant = 10000.0f;
|
||||
|
||||
v3f light = is_day ? sky->getSunDirection() : sky->getMoonDirection();
|
||||
@ -4339,12 +4318,12 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
||||
bool draw_crosshair = (
|
||||
(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
|
||||
(this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (this->isNoCrosshairAllowed())
|
||||
|
||||
if (g_touchscreengui && isTouchCrosshairDisabled())
|
||||
draw_crosshair = false;
|
||||
#endif
|
||||
|
||||
this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud,
|
||||
this->m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);
|
||||
draw_wield_tool, draw_crosshair);
|
||||
|
||||
/*
|
||||
Profiler graph
|
||||
@ -4490,21 +4469,23 @@ void Game::showDeathFormspec()
|
||||
#define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
|
||||
void Game::showPauseMenu()
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
static const std::string control_text = strgettext("Controls:\n"
|
||||
"No menu open:\n"
|
||||
"- slide finger: look around\n"
|
||||
"- tap: place/punch/use (default)\n"
|
||||
"- long tap: dig/use (default)\n"
|
||||
"Menu/inventory open:\n"
|
||||
"- double tap (outside):\n"
|
||||
" --> close\n"
|
||||
"- touch stack, touch slot:\n"
|
||||
" --> move stack\n"
|
||||
"- touch&drag, tap 2nd finger\n"
|
||||
" --> place single item to slot\n"
|
||||
);
|
||||
#endif
|
||||
std::string control_text;
|
||||
|
||||
if (g_touchscreengui) {
|
||||
control_text = strgettext("Controls:\n"
|
||||
"No menu open:\n"
|
||||
"- slide finger: look around\n"
|
||||
"- tap: place/punch/use (default)\n"
|
||||
"- long tap: dig/use (default)\n"
|
||||
"Menu/inventory open:\n"
|
||||
"- double tap (outside):\n"
|
||||
" --> close\n"
|
||||
"- touch stack, touch slot:\n"
|
||||
" --> move stack\n"
|
||||
"- touch&drag, tap 2nd finger\n"
|
||||
" --> place single item to slot\n"
|
||||
);
|
||||
}
|
||||
|
||||
float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
|
||||
std::ostringstream os;
|
||||
@ -4534,9 +4515,9 @@ void Game::showPauseMenu()
|
||||
<< strgettext("Exit to Menu") << "]";
|
||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
|
||||
<< strgettext("Exit to OS") << "]";
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (!control_text.empty()) {
|
||||
os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]";
|
||||
#endif
|
||||
}
|
||||
os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
|
||||
<< "\n"
|
||||
<< strgettext("Game info:") << "\n";
|
||||
|
@ -219,11 +219,6 @@ void GameUI::initFlags()
|
||||
m_flags.show_minimal_debug = g_settings->getBool("show_debug");
|
||||
}
|
||||
|
||||
void GameUI::showMinimap(bool show)
|
||||
{
|
||||
m_flags.show_minimap = show;
|
||||
}
|
||||
|
||||
void GameUI::showTranslatedStatusText(const char *str)
|
||||
{
|
||||
showStatusText(wstrgettext(str));
|
||||
|
@ -57,7 +57,6 @@ public:
|
||||
{
|
||||
bool show_chat = true;
|
||||
bool show_hud = true;
|
||||
bool show_minimap = false;
|
||||
bool show_minimal_debug = false;
|
||||
bool show_basic_debug = false;
|
||||
bool show_profiler_graph = false;
|
||||
@ -71,8 +70,6 @@ public:
|
||||
void initFlags();
|
||||
const Flags &getFlags() const { return m_flags; }
|
||||
|
||||
void showMinimap(bool show);
|
||||
|
||||
inline void setInfoText(const std::wstring &str) { m_infotext = str; }
|
||||
inline void clearInfoText() { m_infotext.clear(); }
|
||||
|
||||
|
@ -39,10 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "wieldmesh.h"
|
||||
#include "client/renderingengine.h"
|
||||
#include "client/minimap.h"
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "gui/touchscreengui.h"
|
||||
#endif
|
||||
|
||||
#define OBJECT_CROSSHAIR_LINE_SIZE 8
|
||||
#define CROSSHAIR_LINE_SIZE 10
|
||||
@ -292,10 +289,8 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
|
||||
|
||||
drawItem(mainlist->getItem(i), item_rect, (i + 1) == selectitem);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (is_hotbar && g_touchscreengui)
|
||||
g_touchscreengui->registerHotbarRect(i, item_rect);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,6 +335,14 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
|
||||
std::vector<HudElement*> elems;
|
||||
elems.reserve(player->maxHudId());
|
||||
|
||||
// Add builtin minimap if the server doesn't send it.
|
||||
HudElement minimap;
|
||||
if (client->getProtoVersion() < 44 && (player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) {
|
||||
minimap = {HUD_ELEM_MINIMAP, v2f(1, 0), "", v2f(), "", 0 , 0, 0, v2f(-1, 1),
|
||||
v2f(-10, 10), v3f(), v2s32(256, 256), 0, "", 0};
|
||||
elems.push_back(&minimap);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != player->maxHudId(); i++) {
|
||||
HudElement *e = player->getHud(i);
|
||||
if (!e)
|
||||
@ -741,10 +744,8 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
|
||||
|
||||
void Hud::drawHotbar(u16 playeritem)
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->resetHotbarRects();
|
||||
#endif
|
||||
|
||||
InventoryList *mainlist = inventory->getList("main");
|
||||
if (mainlist == NULL) {
|
||||
@ -1179,17 +1180,26 @@ void drawItemStack(
|
||||
(1 - wear) * progressrect.LowerRightCorner.X;
|
||||
|
||||
// Compute progressbar color
|
||||
// default scheme:
|
||||
// wear = 0.0: green
|
||||
// wear = 0.5: yellow
|
||||
// wear = 1.0: red
|
||||
video::SColor color(255, 255, 255, 255);
|
||||
int wear_i = MYMIN(std::floor(wear * 600), 511);
|
||||
wear_i = MYMIN(wear_i + 10, 511);
|
||||
|
||||
if (wear_i <= 255)
|
||||
color.set(255, wear_i, 255, 0);
|
||||
else
|
||||
color.set(255, 255, 511 - wear_i, 0);
|
||||
video::SColor color;
|
||||
auto barParams = item.getWearBarParams(client->idef());
|
||||
if (barParams.has_value()) {
|
||||
f32 durabilityPercent = 1.0 - wear;
|
||||
color = barParams->getWearBarColor(durabilityPercent);
|
||||
} else {
|
||||
color = video::SColor(255, 255, 255, 255);
|
||||
int wear_i = MYMIN(std::floor(wear * 600), 511);
|
||||
wear_i = MYMIN(wear_i + 10, 511);
|
||||
|
||||
if (wear_i <= 255)
|
||||
color.set(255, wear_i, 255, 0);
|
||||
else
|
||||
color.set(255, 255, 511 - wear_i, 0);
|
||||
}
|
||||
|
||||
core::rect<s32> progressrect2 = progressrect;
|
||||
progressrect2.LowerRightCorner.X = progressmid;
|
||||
|
@ -65,20 +65,27 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/* Fill in RGB values for transparent pixels, to correct for odd colors
|
||||
* appearing at borders when blending. This is because many PNG optimizers
|
||||
* like to discard RGB values of transparent pixels, but when blending then
|
||||
* with non-transparent neighbors, their RGB values will show up nonetheless.
|
||||
*
|
||||
* This function modifies the original image in-place.
|
||||
*
|
||||
* Parameter "threshold" is the alpha level below which pixels are considered
|
||||
* transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
|
||||
* 0 when alpha blending is used.
|
||||
*/
|
||||
void imageCleanTransparent(video::IImage *src, u32 threshold)
|
||||
template <bool IS_A8R8G8B8>
|
||||
static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold)
|
||||
{
|
||||
core::dimension2d<u32> dim = src->getDimension();
|
||||
void *const src_data = src->getData();
|
||||
const core::dimension2d<u32> dim = src->getDimension();
|
||||
|
||||
auto get_pixel = [=](u32 x, u32 y) -> video::SColor {
|
||||
if constexpr (IS_A8R8G8B8) {
|
||||
return reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
|
||||
} else {
|
||||
return src->getPixel(x, y);
|
||||
}
|
||||
};
|
||||
auto set_pixel = [=](u32 x, u32 y, video::SColor color) {
|
||||
if constexpr (IS_A8R8G8B8) {
|
||||
u32 *dest = &reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
|
||||
*dest = color.color;
|
||||
} else {
|
||||
src->setPixel(x, y, color);
|
||||
}
|
||||
};
|
||||
|
||||
Bitmap bitmap(dim.Width, dim.Height);
|
||||
|
||||
@ -86,7 +93,7 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
|
||||
// Note: loop y around x for better cache locality.
|
||||
for (u32 ctry = 0; ctry < dim.Height; ctry++)
|
||||
for (u32 ctrx = 0; ctrx < dim.Width; ctrx++) {
|
||||
if (src->getPixel(ctrx, ctry).getAlpha() > threshold)
|
||||
if (get_pixel(ctrx, ctry).getAlpha() > threshold)
|
||||
bitmap.set(ctrx, ctry);
|
||||
}
|
||||
|
||||
@ -125,7 +132,7 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
|
||||
|
||||
// Add RGB values weighted by alpha IF the pixel is opaque, otherwise
|
||||
// use full weight since we want to propagate colors.
|
||||
video::SColor d = src->getPixel(sx, sy);
|
||||
video::SColor d = get_pixel(sx, sy);
|
||||
u32 a = d.getAlpha() <= threshold ? 255 : d.getAlpha();
|
||||
ss += a;
|
||||
sr += a * d.getRed();
|
||||
@ -135,11 +142,11 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
|
||||
|
||||
// Set pixel to average weighted by alpha
|
||||
if (ss > 0) {
|
||||
video::SColor c = src->getPixel(ctrx, ctry);
|
||||
video::SColor c = get_pixel(ctrx, ctry);
|
||||
c.setRed(sr / ss);
|
||||
c.setGreen(sg / ss);
|
||||
c.setBlue(sb / ss);
|
||||
src->setPixel(ctrx, ctry, c);
|
||||
set_pixel(ctrx, ctry, c);
|
||||
newmap.set(ctrx, ctry);
|
||||
}
|
||||
}
|
||||
@ -154,6 +161,25 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in RGB values for transparent pixels, to correct for odd colors
|
||||
* appearing at borders when blending. This is because many PNG optimizers
|
||||
* like to discard RGB values of transparent pixels, but when blending then
|
||||
* with non-transparent neighbors, their RGB values will show up nonetheless.
|
||||
*
|
||||
* This function modifies the original image in-place.
|
||||
*
|
||||
* Parameter "threshold" is the alpha level below which pixels are considered
|
||||
* transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
|
||||
* 0 when alpha blending is used.
|
||||
*/
|
||||
void imageCleanTransparent(video::IImage *src, u32 threshold)
|
||||
{
|
||||
if (src->getColorFormat() == video::ECF_A8R8G8B8)
|
||||
imageCleanTransparentWithInlining<true>(src, threshold);
|
||||
else
|
||||
imageCleanTransparentWithInlining<false>(src, threshold);
|
||||
}
|
||||
|
||||
/* Scale a region of an image into another image, using nearest-neighbor with
|
||||
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
|
||||
* to prevent non-integer scaling ratio artifacts. Note that this may cause
|
||||
|
@ -102,11 +102,9 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
||||
React to nothing here if a menu is active
|
||||
*/
|
||||
if (isMenuActive()) {
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (m_touchscreengui) {
|
||||
m_touchscreengui->setVisible(false);
|
||||
}
|
||||
#endif
|
||||
return g_menumgr.preprocessEvent(event);
|
||||
}
|
||||
|
||||
@ -130,12 +128,10 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
} else if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) {
|
||||
// In case of touchscreengui, we have to handle different events
|
||||
m_touchscreengui->translateEvent(event);
|
||||
return true;
|
||||
#endif
|
||||
|
||||
} else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
|
||||
// joystick may be nullptr if game is launched with '--random-input' parameter
|
||||
|
@ -24,10 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <list>
|
||||
#include "keycode.h"
|
||||
#include "renderingengine.h"
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "gui/touchscreengui.h"
|
||||
#endif
|
||||
|
||||
class InputHandler;
|
||||
|
||||
@ -203,16 +200,12 @@ public:
|
||||
|
||||
MyEventReceiver()
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_touchscreengui = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
JoystickController *joystick = nullptr;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
TouchScreenGUI *m_touchscreengui;
|
||||
#endif
|
||||
|
||||
private:
|
||||
s32 mouse_wheel = 0;
|
||||
@ -332,11 +325,9 @@ public:
|
||||
return 0.0f;
|
||||
return 1.0f; // If there is a keyboard event, assume maximum speed
|
||||
}
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
return m_receiver->m_touchscreengui->getMovementSpeed();
|
||||
#else
|
||||
if (m_receiver->m_touchscreengui && m_receiver->m_touchscreengui->getMovementSpeed())
|
||||
return m_receiver->m_touchscreengui->getMovementSpeed();
|
||||
return joystick.getMovementSpeed();
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual float getMovementDirection()
|
||||
@ -355,12 +346,9 @@ public:
|
||||
|
||||
if (x != 0 || z != 0) /* If there is a keyboard event, it takes priority */
|
||||
return atan2(x, z);
|
||||
else
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
else if (m_receiver->m_touchscreengui && m_receiver->m_touchscreengui->getMovementDirection())
|
||||
return m_receiver->m_touchscreengui->getMovementDirection();
|
||||
#else
|
||||
return joystick.getMovementDirection();
|
||||
#endif
|
||||
return joystick.getMovementDirection();
|
||||
}
|
||||
|
||||
virtual bool cancelPressed()
|
||||
|
@ -318,12 +318,14 @@ float JoystickController::getAxisWithoutDead(JoystickAxis axis)
|
||||
|
||||
float JoystickController::getMovementDirection()
|
||||
{
|
||||
return atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE), -getAxisWithoutDead(JA_FORWARD_MOVE));
|
||||
return std::atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE),
|
||||
-getAxisWithoutDead(JA_FORWARD_MOVE));
|
||||
}
|
||||
|
||||
float JoystickController::getMovementSpeed()
|
||||
{
|
||||
float speed = sqrt(pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) + pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
|
||||
float speed = std::sqrt(std::pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) +
|
||||
std::pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
|
||||
if (speed > 1.0f)
|
||||
speed = 1.0f;
|
||||
return speed;
|
||||
|
@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "keycode.h"
|
||||
#include "exceptions.h"
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#include "debug.h"
|
||||
@ -26,13 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "util/string.h"
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
class UnknownKeycode : public BaseException
|
||||
{
|
||||
public:
|
||||
UnknownKeycode(const char *s) :
|
||||
BaseException(s) {};
|
||||
};
|
||||
|
||||
struct table_key {
|
||||
const char *Name;
|
||||
irr::EKEY_CODE Key;
|
||||
|
@ -19,11 +19,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "exceptions.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include "Keycodes.h"
|
||||
#include <IEventReceiver.h>
|
||||
#include <string>
|
||||
|
||||
class UnknownKeycode : public BaseException
|
||||
{
|
||||
public:
|
||||
UnknownKeycode(const char *s) :
|
||||
BaseException(s) {};
|
||||
};
|
||||
|
||||
/* A key press, consisting of either an Irrlicht keycode
|
||||
or an actual char */
|
||||
|
||||
|
@ -600,7 +600,8 @@ void LocalPlayer::applyControl(float dtime, Environment *env)
|
||||
}
|
||||
}
|
||||
|
||||
speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
|
||||
speedH = v3f(std::sin(control.movement_direction), 0.0f,
|
||||
std::cos(control.movement_direction));
|
||||
|
||||
if (m_autojump) {
|
||||
// release autojump after a given time
|
||||
|
@ -38,10 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
MeshMakeData
|
||||
*/
|
||||
|
||||
MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
|
||||
m_mesh_grid(client->getMeshGrid()),
|
||||
side_length(MAP_BLOCKSIZE * m_mesh_grid.cell_size),
|
||||
m_client(client),
|
||||
MeshMakeData::MeshMakeData(const NodeDefManager *ndef, u16 side_length, bool use_shaders):
|
||||
side_length(side_length),
|
||||
nodedef(ndef),
|
||||
m_use_shaders(use_shaders)
|
||||
{}
|
||||
|
||||
@ -147,7 +146,7 @@ u16 getFaceLight(MapNode n, MapNode n2, const NodeDefManager *ndef)
|
||||
static u16 getSmoothLightCombined(const v3s16 &p,
|
||||
const std::array<v3s16,8> &dirs, MeshMakeData *data)
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
u16 ambient_occlusion = 0;
|
||||
u16 light_count = 0;
|
||||
@ -360,7 +359,7 @@ static const v3s16 vertex_dirs_table[] = {
|
||||
*/
|
||||
void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
const ContentFeatures &f = ndef->get(mn);
|
||||
tile = f.tiles[tileindex];
|
||||
bool has_crack = p == data->m_crack_pos_relative;
|
||||
@ -380,7 +379,7 @@ void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data,
|
||||
*/
|
||||
void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
// Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
|
||||
// (0,0,1), (0,0,-1) or (0,0,0)
|
||||
@ -635,9 +634,9 @@ void PartialMeshBuffer::afterDraw() const
|
||||
MapBlockMesh
|
||||
*/
|
||||
|
||||
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
m_tsrc(data->m_client->getTextureSource()),
|
||||
m_shdrsrc(data->m_client->getShaderSource()),
|
||||
MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offset):
|
||||
m_tsrc(client->getTextureSource()),
|
||||
m_shdrsrc(client->getShaderSource()),
|
||||
m_bounding_sphere_center((data->side_length * 0.5f - 0.5f) * BS),
|
||||
m_animation_force_timer(0), // force initial animation
|
||||
m_last_crack(-1),
|
||||
@ -648,26 +647,27 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
m_enable_shaders = data->m_use_shaders;
|
||||
m_enable_vbo = g_settings->getBool("enable_vbo");
|
||||
|
||||
auto mesh_grid = client->getMeshGrid();
|
||||
v3s16 bp = data->m_blockpos;
|
||||
// Only generate minimap mapblocks at even coordinates.
|
||||
if (data->m_mesh_grid.isMeshPos(bp) && data->m_client->getMinimap()) {
|
||||
m_minimap_mapblocks.resize(data->m_mesh_grid.getCellVolume(), nullptr);
|
||||
if (mesh_grid.isMeshPos(bp) && client->getMinimap()) {
|
||||
m_minimap_mapblocks.resize(mesh_grid.getCellVolume(), nullptr);
|
||||
v3s16 ofs;
|
||||
|
||||
// See also client.cpp for the code that reads the array of minimap blocks.
|
||||
for (ofs.Z = 0; ofs.Z < data->m_mesh_grid.cell_size; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y < data->m_mesh_grid.cell_size; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X < data->m_mesh_grid.cell_size; ofs.X++) {
|
||||
for (ofs.Z = 0; ofs.Z < mesh_grid.cell_size; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y < mesh_grid.cell_size; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X < mesh_grid.cell_size; ofs.X++) {
|
||||
v3s16 p = (bp + ofs) * MAP_BLOCKSIZE;
|
||||
if (data->m_vmanip.getNodeNoEx(p).getContent() != CONTENT_IGNORE) {
|
||||
MinimapMapblock *block = new MinimapMapblock;
|
||||
m_minimap_mapblocks[data->m_mesh_grid.getOffsetIndex(ofs)] = block;
|
||||
m_minimap_mapblocks[mesh_grid.getOffsetIndex(ofs)] = block;
|
||||
block->getMinimapNodes(&data->m_vmanip, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v3f offset = intToFloat((data->m_blockpos - data->m_mesh_grid.getMeshPos(data->m_blockpos)) * MAP_BLOCKSIZE, BS);
|
||||
v3f offset = intToFloat((data->m_blockpos - mesh_grid.getMeshPos(data->m_blockpos)) * MAP_BLOCKSIZE, BS);
|
||||
MeshCollector collector(m_bounding_sphere_center, offset);
|
||||
/*
|
||||
Add special graphics:
|
||||
@ -679,7 +679,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
|
||||
{
|
||||
MapblockMeshGenerator(data, &collector,
|
||||
data->m_client->getSceneManager()->getMeshManipulator()).generate();
|
||||
client->getSceneManager()->getMeshManipulator()).generate();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1011,7 +1011,7 @@ u8 get_solid_sides(MeshMakeData *data)
|
||||
std::unordered_map<v3s16, u8> results;
|
||||
v3s16 ofs;
|
||||
v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
u8 result = 0x3F; // all sides solid;
|
||||
|
||||
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <unordered_map>
|
||||
|
||||
class Client;
|
||||
class NodeDefManager;
|
||||
class IShaderSource;
|
||||
|
||||
/*
|
||||
@ -43,13 +44,12 @@ struct MeshMakeData
|
||||
v3s16 m_blockpos = v3s16(-1337,-1337,-1337);
|
||||
v3s16 m_crack_pos_relative = v3s16(-1337,-1337,-1337);
|
||||
bool m_smooth_lighting = false;
|
||||
MeshGrid m_mesh_grid;
|
||||
u16 side_length;
|
||||
|
||||
Client *m_client;
|
||||
const NodeDefManager *nodedef;
|
||||
bool m_use_shaders;
|
||||
|
||||
MeshMakeData(Client *client, bool use_shaders);
|
||||
MeshMakeData(const NodeDefManager *ndef, u16 side_length, bool use_shaders);
|
||||
|
||||
/*
|
||||
Copy block data manually (to allow optimizations by the caller)
|
||||
@ -179,7 +179,7 @@ class MapBlockMesh
|
||||
{
|
||||
public:
|
||||
// Builds the mesh given
|
||||
MapBlockMesh(MeshMakeData *data, v3s16 camera_offset);
|
||||
MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offset);
|
||||
~MapBlockMesh();
|
||||
|
||||
// Main animation function, parameters:
|
||||
|
@ -189,16 +189,17 @@ void MeshUpdateQueue::done(v3s16 pos)
|
||||
|
||||
void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
|
||||
{
|
||||
MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
|
||||
auto mesh_grid = m_client->getMeshGrid();
|
||||
MeshMakeData *data = new MeshMakeData(m_client->ndef(), MAP_BLOCKSIZE * mesh_grid.cell_size, m_cache_enable_shaders);
|
||||
q->data = data;
|
||||
|
||||
data->fillBlockDataBegin(q->p);
|
||||
|
||||
v3s16 pos;
|
||||
int i = 0;
|
||||
for (pos.X = q->p.X - 1; pos.X <= q->p.X + data->m_mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + data->m_mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + data->m_mesh_grid.cell_size; pos.Y++) {
|
||||
for (pos.X = q->p.X - 1; pos.X <= q->p.X + mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + mesh_grid.cell_size; pos.Y++) {
|
||||
MapBlock *block = q->map_blocks[i++];
|
||||
data->fillBlockData(pos, block ? block->getData() : block_placeholder.data);
|
||||
}
|
||||
@ -211,8 +212,8 @@ void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
|
||||
MeshUpdateWorkerThread
|
||||
*/
|
||||
|
||||
MeshUpdateWorkerThread::MeshUpdateWorkerThread(MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset) :
|
||||
UpdateThread("Mesh"), m_queue_in(queue_in), m_manager(manager), m_camera_offset(camera_offset)
|
||||
MeshUpdateWorkerThread::MeshUpdateWorkerThread(Client *client, MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset) :
|
||||
UpdateThread("Mesh"), m_client(client), m_queue_in(queue_in), m_manager(manager), m_camera_offset(camera_offset)
|
||||
{
|
||||
m_generation_interval = g_settings->getU16("mesh_generation_interval");
|
||||
m_generation_interval = rangelim(m_generation_interval, 0, 50);
|
||||
@ -226,7 +227,7 @@ void MeshUpdateWorkerThread::doUpdate()
|
||||
sleep_ms(m_generation_interval);
|
||||
ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)");
|
||||
|
||||
MapBlockMesh *mesh_new = new MapBlockMesh(q->data, *m_camera_offset);
|
||||
MapBlockMesh *mesh_new = new MapBlockMesh(m_client, q->data, *m_camera_offset);
|
||||
|
||||
|
||||
|
||||
@ -262,7 +263,7 @@ MeshUpdateManager::MeshUpdateManager(Client *client):
|
||||
infostream << "MeshUpdateManager: using " << number_of_threads << " threads" << std::endl;
|
||||
|
||||
for (int i = 0; i < number_of_threads; i++)
|
||||
m_workers.push_back(std::make_unique<MeshUpdateWorkerThread>(&m_queue_in, this, &m_camera_offset));
|
||||
m_workers.push_back(std::make_unique<MeshUpdateWorkerThread>(client, &m_queue_in, this, &m_camera_offset));
|
||||
}
|
||||
|
||||
void MeshUpdateManager::updateBlock(Map *map, v3s16 p, bool ack_block_to_server,
|
||||
|
@ -109,12 +109,13 @@ class MeshUpdateManager;
|
||||
class MeshUpdateWorkerThread : public UpdateThread
|
||||
{
|
||||
public:
|
||||
MeshUpdateWorkerThread(MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset);
|
||||
MeshUpdateWorkerThread(Client *client, MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset);
|
||||
|
||||
protected:
|
||||
virtual void doUpdate();
|
||||
|
||||
private:
|
||||
Client *m_client;
|
||||
MeshUpdateQueue *m_queue_in;
|
||||
MeshUpdateManager *m_manager;
|
||||
v3s16 *m_camera_offset;
|
||||
|
@ -206,25 +206,6 @@ Minimap::Minimap(Client *client)
|
||||
|
||||
data->minimap_shape_round = g_settings->getBool("minimap_shape_round");
|
||||
|
||||
// Get round minimap textures
|
||||
data->minimap_mask_round = driver->createImage(
|
||||
m_tsrc->getTexture("minimap_mask_round.png"),
|
||||
core::position2d<s32>(0, 0),
|
||||
core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
|
||||
data->minimap_overlay_round = m_tsrc->getTexture("minimap_overlay_round.png");
|
||||
|
||||
// Get square minimap textures
|
||||
data->minimap_mask_square = driver->createImage(
|
||||
m_tsrc->getTexture("minimap_mask_square.png"),
|
||||
core::position2d<s32>(0, 0),
|
||||
core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
|
||||
data->minimap_overlay_square = m_tsrc->getTexture("minimap_overlay_square.png");
|
||||
|
||||
// Create player marker texture
|
||||
data->player_marker = m_tsrc->getTexture("player_marker.png");
|
||||
// Create object marker texture
|
||||
data->object_marker_red = m_tsrc->getTexture("object_marker_red.png");
|
||||
|
||||
setModeIndex(0);
|
||||
|
||||
// Create mesh buffer for minimap
|
||||
@ -243,8 +224,10 @@ Minimap::~Minimap()
|
||||
|
||||
m_meshbuffer->drop();
|
||||
|
||||
data->minimap_mask_round->drop();
|
||||
data->minimap_mask_square->drop();
|
||||
if (data->minimap_mask_round)
|
||||
data->minimap_mask_round->drop();
|
||||
if (data->minimap_mask_square)
|
||||
data->minimap_mask_square->drop();
|
||||
|
||||
driver->removeTexture(data->texture);
|
||||
driver->removeTexture(data->heightmap_texture);
|
||||
@ -461,6 +444,29 @@ void Minimap::blitMinimapPixelsToImageSurface(
|
||||
}
|
||||
}
|
||||
|
||||
video::IImage *Minimap::getMinimapMask()
|
||||
{
|
||||
if (data->minimap_shape_round) {
|
||||
if (!data->minimap_mask_round) {
|
||||
// Get round minimap textures
|
||||
data->minimap_mask_round = driver->createImage(
|
||||
m_tsrc->getTexture("minimap_mask_round.png"),
|
||||
core::position2d<s32>(0, 0),
|
||||
core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
|
||||
}
|
||||
return data->minimap_mask_round;
|
||||
}
|
||||
|
||||
if (!data->minimap_mask_square) {
|
||||
// Get square minimap textures
|
||||
data->minimap_mask_square = driver->createImage(
|
||||
m_tsrc->getTexture("minimap_mask_square.png"),
|
||||
core::position2d<s32>(0, 0),
|
||||
core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
|
||||
}
|
||||
return data->minimap_mask_square;
|
||||
}
|
||||
|
||||
video::ITexture *Minimap::getMinimapTexture()
|
||||
{
|
||||
// update minimap textures when new scan is ready
|
||||
@ -509,16 +515,13 @@ video::ITexture *Minimap::getMinimapTexture()
|
||||
map_image->copyToScaling(minimap_image);
|
||||
map_image->drop();
|
||||
|
||||
video::IImage *minimap_mask = data->minimap_shape_round ?
|
||||
data->minimap_mask_round : data->minimap_mask_square;
|
||||
video::IImage *minimap_mask = getMinimapMask();
|
||||
|
||||
if (minimap_mask) {
|
||||
for (s16 y = 0; y < MINIMAP_MAX_SY; y++)
|
||||
for (s16 x = 0; x < MINIMAP_MAX_SX; x++) {
|
||||
const video::SColor &mask_col = minimap_mask->getPixel(x, y);
|
||||
if (!mask_col.getAlpha())
|
||||
minimap_image->setPixel(x, y, video::SColor(0,0,0,0));
|
||||
}
|
||||
for (s16 y = 0; y < MINIMAP_MAX_SY; y++)
|
||||
for (s16 x = 0; x < MINIMAP_MAX_SX; x++) {
|
||||
const video::SColor &mask_col = minimap_mask->getPixel(x, y);
|
||||
if (!mask_col.getAlpha())
|
||||
minimap_image->setPixel(x, y, video::SColor(0,0,0,0));
|
||||
}
|
||||
|
||||
if (data->texture)
|
||||
@ -571,25 +574,22 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer()
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Minimap::drawMinimap()
|
||||
void Minimap::drawMinimap(core::rect<s32> rect)
|
||||
{
|
||||
// Non hud managed minimap drawing (legacy minimap)
|
||||
v2u32 screensize = RenderingEngine::getWindowSize();
|
||||
const u32 size = 0.25 * screensize.Y;
|
||||
|
||||
drawMinimap(core::rect<s32>(
|
||||
screensize.X - size - 10, 10,
|
||||
screensize.X - 10, size + 10));
|
||||
}
|
||||
|
||||
void Minimap::drawMinimap(core::rect<s32> rect) {
|
||||
if (data->mode.type == MINIMAP_TYPE_OFF)
|
||||
return;
|
||||
|
||||
// Get textures
|
||||
video::ITexture *minimap_texture = getMinimapTexture();
|
||||
if (!minimap_texture)
|
||||
return;
|
||||
|
||||
if (data->mode.type == MINIMAP_TYPE_OFF)
|
||||
return;
|
||||
if (!data->textures_initialised) {
|
||||
data->minimap_overlay_round = m_tsrc->getTexture("minimap_overlay_round.png");
|
||||
data->minimap_overlay_square = m_tsrc->getTexture("minimap_overlay_square.png");
|
||||
data->player_marker = m_tsrc->getTexture("player_marker.png");
|
||||
data->object_marker_red = m_tsrc->getTexture("object_marker_red.png");
|
||||
data->textures_initialised = true;
|
||||
}
|
||||
|
||||
updateActiveMarkers();
|
||||
|
||||
@ -700,8 +700,7 @@ void Minimap::removeMarker(MinimapMarker **m)
|
||||
|
||||
void Minimap::updateActiveMarkers()
|
||||
{
|
||||
video::IImage *minimap_mask = data->minimap_shape_round ?
|
||||
data->minimap_mask_round : data->minimap_mask_square;
|
||||
video::IImage *minimap_mask = getMinimapMask();
|
||||
|
||||
m_active_markers.clear();
|
||||
v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS);
|
||||
|
@ -79,6 +79,7 @@ struct MinimapData {
|
||||
video::IImage *minimap_mask_square = nullptr;
|
||||
video::ITexture *texture = nullptr;
|
||||
video::ITexture *heightmap_texture = nullptr;
|
||||
bool textures_initialised = false; // True if the following textures are not nullptrs.
|
||||
video::ITexture *minimap_overlay_round = nullptr;
|
||||
video::ITexture *minimap_overlay_square = nullptr;
|
||||
video::ITexture *player_marker = nullptr;
|
||||
@ -140,6 +141,7 @@ public:
|
||||
|
||||
MinimapModeDef getModeDef() const { return data->mode; }
|
||||
|
||||
video::IImage *getMinimapMask();
|
||||
video::ITexture *getMinimapTexture();
|
||||
|
||||
void blitMinimapPixelsToImageRadar(video::IImage *map_image);
|
||||
@ -152,7 +154,6 @@ public:
|
||||
void removeMarker(MinimapMarker **marker);
|
||||
|
||||
void updateActiveMarkers();
|
||||
void drawMinimap();
|
||||
void drawMinimap(core::rect<s32> rect);
|
||||
|
||||
video::IVideoDriver *driver;
|
||||
|
@ -213,10 +213,10 @@ void Particle::step(float dtime)
|
||||
|
||||
if (m_p.animation.type != TAT_NONE) {
|
||||
m_animation_time += dtime;
|
||||
int frame_length_i, frame_count;
|
||||
int frame_length_i = 0;
|
||||
m_p.animation.determineParams(
|
||||
m_material.getTexture(0)->getSize(),
|
||||
&frame_count, &frame_length_i, NULL);
|
||||
NULL, &frame_length_i, NULL);
|
||||
float frame_length = frame_length_i / 1000.0;
|
||||
while (m_animation_time > frame_length) {
|
||||
m_animation_frame++;
|
||||
|
@ -36,7 +36,7 @@ RenderingCore::~RenderingCore()
|
||||
delete shadow_renderer;
|
||||
}
|
||||
|
||||
void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_minimap,
|
||||
void RenderingCore::draw(video::SColor _skycolor, bool _show_hud,
|
||||
bool _draw_wield_tool, bool _draw_crosshair)
|
||||
{
|
||||
v2u32 screensize = device->getVideoDriver()->getScreenSize();
|
||||
@ -46,7 +46,6 @@ void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_min
|
||||
context.draw_crosshair = _draw_crosshair;
|
||||
context.draw_wield_tool = _draw_wield_tool;
|
||||
context.show_hud = _show_hud;
|
||||
context.show_minimap = _show_minimap;
|
||||
|
||||
pipeline->reset(context);
|
||||
pipeline->run(context);
|
||||
@ -55,4 +54,4 @@ void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_min
|
||||
v2u32 RenderingCore::getVirtualSize() const
|
||||
{
|
||||
return virtual_size;
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public:
|
||||
RenderingCore &operator=(const RenderingCore &) = delete;
|
||||
RenderingCore &operator=(RenderingCore &&) = delete;
|
||||
|
||||
void draw(video::SColor _skycolor, bool _show_hud, bool _show_minimap,
|
||||
void draw(video::SColor _skycolor, bool _show_hud,
|
||||
bool _draw_wield_tool, bool _draw_crosshair);
|
||||
|
||||
v2u32 getVirtualSize() const;
|
||||
|
@ -46,7 +46,6 @@ struct PipelineContext
|
||||
v2u32 target_size;
|
||||
|
||||
bool show_hud {true};
|
||||
bool show_minimap {true};
|
||||
bool draw_wield_tool {true};
|
||||
bool draw_crosshair {true};
|
||||
};
|
||||
|
@ -64,9 +64,6 @@ void DrawHUD::run(PipelineContext &context)
|
||||
context.hud->drawHotbar(context.client->getEnv().getLocalPlayer()->getWieldIndex());
|
||||
context.hud->drawLuaElements(context.client->getCamera()->getOffset());
|
||||
context.client->getCamera()->drawNametags();
|
||||
auto mapper = context.client->getMinimap();
|
||||
if (mapper && context.show_minimap)
|
||||
mapper->drawMinimap();
|
||||
}
|
||||
context.device->getGUIEnvironment()->drawAll();
|
||||
}
|
||||
@ -106,7 +103,7 @@ void UpscaleStep::run(PipelineContext &context)
|
||||
std::unique_ptr<RenderStep> create3DStage(Client *client, v2f scale)
|
||||
{
|
||||
RenderStep *step = new Draw3D();
|
||||
if (g_settings->getBool("enable_shaders")) {
|
||||
if (g_settings->getBool("enable_shaders") && g_settings->getBool("enable_post_processing")) {
|
||||
RenderPipeline *pipeline = new RenderPipeline();
|
||||
pipeline->addStep(pipeline->own(std::unique_ptr<RenderStep>(step)));
|
||||
|
||||
@ -131,7 +128,7 @@ RenderStep* addUpscaling(RenderPipeline *pipeline, RenderStep *previousStep, v2f
|
||||
return previousStep;
|
||||
|
||||
// When shaders are enabled, post-processing pipeline takes care of rescaling
|
||||
if (g_settings->getBool("enable_shaders"))
|
||||
if (g_settings->getBool("enable_shaders") && g_settings->getBool("enable_post_processing"))
|
||||
return previousStep;
|
||||
|
||||
|
||||
|
@ -152,6 +152,9 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver)
|
||||
driver = m_device->getVideoDriver();
|
||||
infostream << "Using the " << RenderingEngine::getVideoDriverInfo(driver->getDriverType()).friendly_name << " video driver" << std::endl;
|
||||
|
||||
// This changes the minimum allowed number of vertices in a VBO. Default is 500.
|
||||
driver->setMinHardwareBufferVertexCount(4);
|
||||
|
||||
s_singleton = this;
|
||||
|
||||
auto skin = createSkin(m_device->getGUIEnvironment(),
|
||||
@ -319,9 +322,9 @@ void RenderingEngine::finalize()
|
||||
}
|
||||
|
||||
void RenderingEngine::draw_scene(video::SColor skycolor, bool show_hud,
|
||||
bool show_minimap, bool draw_wield_tool, bool draw_crosshair)
|
||||
bool draw_wield_tool, bool draw_crosshair)
|
||||
{
|
||||
core->draw(skycolor, show_hud, show_minimap, draw_wield_tool, draw_crosshair);
|
||||
core->draw(skycolor, show_hud, draw_wield_tool, draw_crosshair);
|
||||
}
|
||||
|
||||
const VideoDriverInfo &RenderingEngine::getVideoDriverInfo(irr::video::E_DRIVER_TYPE type)
|
||||
|
@ -118,7 +118,7 @@ public:
|
||||
float dtime = 0, int percent = 0, bool sky = true);
|
||||
|
||||
void draw_scene(video::SColor skycolor, bool show_hud,
|
||||
bool show_minimap, bool draw_wield_tool, bool draw_crosshair);
|
||||
bool draw_wield_tool, bool draw_crosshair);
|
||||
|
||||
void initialize(Client *client, Hud *hud);
|
||||
void finalize();
|
||||
|
@ -804,7 +804,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
|
||||
}
|
||||
|
||||
void dumpShaderProgram(std::ostream &output_stream,
|
||||
const std::string &program_type, const std::string &program)
|
||||
const std::string &program_type, std::string_view program)
|
||||
{
|
||||
output_stream << program_type << " shader program:" << std::endl <<
|
||||
"----------------------------------" << std::endl;
|
||||
|
@ -192,4 +192,4 @@ public:
|
||||
IWritableShaderSource *createShaderSource();
|
||||
|
||||
void dumpShaderProgram(std::ostream &output_stream,
|
||||
const std::string &program_type, const std::string &program);
|
||||
const std::string &program_type, std::string_view program);
|
||||
|
@ -171,8 +171,8 @@ f32 ShadowRenderer::getMaxShadowFar() const
|
||||
|
||||
void ShadowRenderer::setShadowIntensity(float shadow_intensity)
|
||||
{
|
||||
m_shadow_strength = pow(shadow_intensity, 1.0f / m_shadow_strength_gamma);
|
||||
if (m_shadow_strength > 1E-2)
|
||||
m_shadow_strength = std::pow(shadow_intensity, 1.0f / m_shadow_strength_gamma);
|
||||
if (m_shadow_strength > 1e-2f)
|
||||
enable();
|
||||
else
|
||||
disable();
|
||||
@ -183,8 +183,12 @@ void ShadowRenderer::addNodeToShadowList(
|
||||
{
|
||||
m_shadow_node_array.emplace_back(node, shadowMode);
|
||||
// node should never be ClientMap
|
||||
#if IRRLICHT_VERSION_MT_REVISION >= 15
|
||||
assert(!node->getName().has_value() || *node->getName() != "ClientMap");
|
||||
#else
|
||||
// TODO: Remove this as soon as we require 1.9.0mt15
|
||||
assert(strcmp(node->getName(), "ClientMap") != 0);
|
||||
|
||||
#endif
|
||||
node->forEachMaterial([this] (auto &mat) {
|
||||
mat.setTexture(TEXTURE_LAYER_SHADOW, shadowMapTextureFinal);
|
||||
});
|
||||
|
@ -680,7 +680,7 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day)
|
||||
|
||||
float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day);
|
||||
float day_opacity = clamp(m_star_params.day_opacity, 0.0f, 1.0f);
|
||||
float starbrightness = (0.25f - fabs(tod)) * 20.0f;
|
||||
float starbrightness = (0.25f - std::abs(tod)) * 20.0f;
|
||||
float alpha = clamp(starbrightness, day_opacity, 1.0f);
|
||||
|
||||
m_star_color = m_star_params.starcolor;
|
||||
|
@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "guiscalingfilter.h"
|
||||
#include "renderingengine.h"
|
||||
#include "util/base64.h"
|
||||
#include "irrlicht_changes/printing.h"
|
||||
|
||||
/*
|
||||
A cache from texture name to texture path
|
||||
@ -403,7 +404,7 @@ private:
|
||||
// Generate image based on a string like "stone.png" or "[crack:1:0".
|
||||
// if baseimg is NULL, it is created. Otherwise stuff is made on it.
|
||||
// source_image_names is important to determine when to flush the image from a cache (dynamic media)
|
||||
bool generateImagePart(std::string part_of_name, video::IImage *& baseimg, std::set<std::string> &source_image_names);
|
||||
bool generateImagePart(std::string_view part_of_name, video::IImage *& baseimg, std::set<std::string> &source_image_names);
|
||||
|
||||
/*! Generates an image from a full string like
|
||||
* "stone.png^mineral_coal.png^[crack:1:0".
|
||||
@ -411,7 +412,7 @@ private:
|
||||
* The returned Image should be dropped.
|
||||
* source_image_names is important to determine when to flush the image from a cache (dynamic media)
|
||||
*/
|
||||
video::IImage* generateImage(const std::string &name, std::set<std::string> &source_image_names);
|
||||
video::IImage* generateImage(std::string_view name, std::set<std::string> &source_image_names);
|
||||
|
||||
// Thread-safe cache of what source images are known (true = known)
|
||||
MutexedMap<std::string, bool> m_source_image_existence;
|
||||
@ -538,14 +539,11 @@ u32 TextureSource::getTextureId(const std::string &name)
|
||||
|
||||
// Draw an image on top of another one, using the alpha channel of the
|
||||
// source image
|
||||
// overlay: only modify destination pixels that are fully opaque.
|
||||
template<bool overlay = false>
|
||||
static void blit_with_alpha(video::IImage *src, video::IImage *dst,
|
||||
v2s32 src_pos, v2s32 dst_pos, v2u32 size);
|
||||
|
||||
// Like blit_with_alpha, but only modifies destination pixels that
|
||||
// are fully opaque
|
||||
static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
|
||||
v2s32 src_pos, v2s32 dst_pos, v2u32 size);
|
||||
|
||||
// Apply a color to an image. Uses an int (0-255) to calculate the ratio.
|
||||
// If the ratio is 255 or -1 and keep_alpha is true, then it multiples the
|
||||
// color alpha with the destination alpha.
|
||||
@ -595,7 +593,7 @@ static void draw_crack(video::IImage *crack, video::IImage *dst,
|
||||
// Brighten image
|
||||
void brighten(video::IImage *image);
|
||||
// Parse a transform name
|
||||
u32 parseImageTransform(const std::string& s);
|
||||
u32 parseImageTransform(std::string_view s);
|
||||
// Apply transform to image dimension
|
||||
core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
|
||||
// Apply transform to image data
|
||||
@ -965,7 +963,8 @@ static video::IImage *createInventoryCubeImage(
|
||||
return result;
|
||||
}
|
||||
|
||||
video::IImage* TextureSource::generateImage(const std::string &name, std::set<std::string> &source_image_names)
|
||||
video::IImage* TextureSource::generateImage(std::string_view name,
|
||||
std::set<std::string> &source_image_names)
|
||||
{
|
||||
// Get the base image
|
||||
|
||||
@ -1026,15 +1025,15 @@ video::IImage* TextureSource::generateImage(const std::string &name, std::set<st
|
||||
according to it
|
||||
*/
|
||||
|
||||
std::string last_part_of_name = name.substr(last_separator_pos + 1);
|
||||
auto last_part_of_name = name.substr(last_separator_pos + 1);
|
||||
|
||||
/*
|
||||
If this name is enclosed in parentheses, generate it
|
||||
and blit it onto the base image
|
||||
*/
|
||||
if (last_part_of_name[0] == paren_open
|
||||
&& last_part_of_name[last_part_of_name.size() - 1] == paren_close) {
|
||||
std::string name2 = last_part_of_name.substr(1,
|
||||
&& last_part_of_name.back() == paren_close) {
|
||||
auto name2 = last_part_of_name.substr(1,
|
||||
last_part_of_name.size() - 2);
|
||||
video::IImage *tmp = generateImage(name2, source_image_names);
|
||||
if (!tmp) {
|
||||
@ -1201,7 +1200,7 @@ void blitBaseImage(video::IImage* &src, video::IImage* &dst)
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
bool TextureSource::generateImagePart(std::string_view part_of_name,
|
||||
video::IImage *& baseimg, std::set<std::string> &source_image_names)
|
||||
{
|
||||
const char escape = '\\'; // same as in generateImage()
|
||||
@ -1218,8 +1217,9 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
|
||||
// Stuff starting with [ are special commands
|
||||
if (part_of_name.empty() || part_of_name[0] != '[') {
|
||||
source_image_names.insert(part_of_name);
|
||||
video::IImage *image = m_sourcecache.getOrLoad(part_of_name);
|
||||
std::string part_s(part_of_name);
|
||||
source_image_names.insert(part_s);
|
||||
video::IImage *image = m_sourcecache.getOrLoad(part_s);
|
||||
if (!image) {
|
||||
// Do not create the dummy texture
|
||||
if (part_of_name.empty())
|
||||
@ -1323,37 +1323,45 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
sf.next(":");
|
||||
u32 w0 = stoi(sf.next("x"));
|
||||
u32 h0 = stoi(sf.next(":"));
|
||||
CHECK_DIM(w0, h0);
|
||||
core::dimension2d<u32> dim(w0,h0);
|
||||
if (baseimg == NULL) {
|
||||
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
|
||||
if (!baseimg) {
|
||||
CHECK_DIM(w0, h0);
|
||||
baseimg = driver->createImage(video::ECF_A8R8G8B8, {w0, h0});
|
||||
baseimg->fill(video::SColor(0,0,0,0));
|
||||
}
|
||||
|
||||
while (!sf.at_end()) {
|
||||
u32 x = stoi(sf.next(","));
|
||||
u32 y = stoi(sf.next("="));
|
||||
v2s32 pos_base;
|
||||
pos_base.X = stoi(sf.next(","));
|
||||
pos_base.Y = stoi(sf.next("="));
|
||||
std::string filename = unescape_string(sf.next_esc(":", escape), escape);
|
||||
|
||||
if (x >= w0 || y >= h0)
|
||||
COMPLAIN_INVALID("X or Y offset");
|
||||
infostream<<"Adding \""<<filename
|
||||
<<"\" to combined ("<<x<<","<<y<<")"
|
||||
<<std::endl;
|
||||
auto basedim = baseimg->getDimension();
|
||||
if (pos_base.X > (s32)basedim.Width || pos_base.Y > (s32)basedim.Height) {
|
||||
warningstream << "generateImagePart(): Skipping \""
|
||||
<< filename << "\" as it's out-of-bounds " << pos_base
|
||||
<< " for [combine" << std::endl;
|
||||
continue;
|
||||
}
|
||||
infostream << "Adding \"" << filename<< "\" to combined "
|
||||
<< pos_base << std::endl;
|
||||
|
||||
video::IImage *img = generateImage(filename, source_image_names);
|
||||
if (img) {
|
||||
core::dimension2d<u32> dim = img->getDimension();
|
||||
core::position2d<s32> pos_base(x, y);
|
||||
video::IImage *img2 =
|
||||
driver->createImage(video::ECF_A8R8G8B8, dim);
|
||||
img->copyTo(img2);
|
||||
img->drop();
|
||||
blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
|
||||
img2->drop();
|
||||
} else {
|
||||
if (!img) {
|
||||
errorstream << "generateImagePart(): Failed to load image \""
|
||||
<< filename << "\" for [combine" << std::endl;
|
||||
continue;
|
||||
}
|
||||
const auto dim = img->getDimension();
|
||||
if (pos_base.X + dim.Width <= 0 || pos_base.Y + dim.Height <= 0) {
|
||||
warningstream << "generateImagePart(): Skipping \""
|
||||
<< filename << "\" as it's out-of-bounds " << pos_base
|
||||
<< " for [combine" << std::endl;
|
||||
img->drop();
|
||||
continue;
|
||||
}
|
||||
|
||||
blit_with_alpha(img, baseimg, v2s32(0,0), pos_base, dim);
|
||||
img->drop();
|
||||
}
|
||||
}
|
||||
/*
|
||||
@ -1510,8 +1518,10 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
return false;
|
||||
}
|
||||
|
||||
str_replace(part_of_name, '&', '^');
|
||||
Strfnd sf(part_of_name);
|
||||
std::string part_s(part_of_name);
|
||||
str_replace(part_s, '&', '^');
|
||||
|
||||
Strfnd sf(part_s);
|
||||
sf.next("{");
|
||||
std::string imagename_top = sf.next("{");
|
||||
std::string imagename_left = sf.next("{");
|
||||
@ -1867,7 +1877,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
else if (str_starts_with(part_of_name, "[png:")) {
|
||||
std::string png;
|
||||
{
|
||||
std::string blob = part_of_name.substr(5);
|
||||
auto blob = part_of_name.substr(5);
|
||||
if (!base64_is_valid(blob)) {
|
||||
errorstream << "generateImagePart(): "
|
||||
<< "malformed base64 in [png" << std::endl;
|
||||
@ -2033,32 +2043,21 @@ static inline video::SColor blitPixel(const video::SColor src_c, const video::SC
|
||||
This exists because IImage::copyToWithAlpha() doesn't seem to always
|
||||
work.
|
||||
*/
|
||||
template<bool overlay>
|
||||
static void blit_with_alpha(video::IImage *src, video::IImage *dst,
|
||||
v2s32 src_pos, v2s32 dst_pos, v2u32 size)
|
||||
{
|
||||
for (u32 y0=0; y0<size.Y; y0++)
|
||||
for (u32 x0=0; x0<size.X; x0++)
|
||||
{
|
||||
s32 src_x = src_pos.X + x0;
|
||||
s32 src_y = src_pos.Y + y0;
|
||||
s32 dst_x = dst_pos.X + x0;
|
||||
s32 dst_y = dst_pos.Y + y0;
|
||||
video::SColor src_c = src->getPixel(src_x, src_y);
|
||||
video::SColor dst_c = dst->getPixel(dst_x, dst_y);
|
||||
dst_c = blitPixel(src_c, dst_c, src_c.getAlpha());
|
||||
dst->setPixel(dst_x, dst_y, dst_c);
|
||||
}
|
||||
}
|
||||
auto src_dim = src->getDimension();
|
||||
auto dst_dim = dst->getDimension();
|
||||
|
||||
/*
|
||||
Draw an image on top of another one, using the alpha channel of the
|
||||
source image; only modify fully opaque pixels in destinaion
|
||||
*/
|
||||
static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
|
||||
v2s32 src_pos, v2s32 dst_pos, v2u32 size)
|
||||
{
|
||||
for (u32 y0=0; y0<size.Y; y0++)
|
||||
for (u32 x0=0; x0<size.X; x0++)
|
||||
// Limit y and x to the overlapping ranges
|
||||
// s.t. the positions are all in bounds after offsetting.
|
||||
for (u32 y0 = std::max(0, -dst_pos.Y);
|
||||
y0 < std::min<s64>({size.Y, src_dim.Height, dst_dim.Height - (s64) dst_pos.Y});
|
||||
++y0)
|
||||
for (u32 x0 = std::max(0, -dst_pos.X);
|
||||
x0 < std::min<s64>({size.X, src_dim.Width, dst_dim.Width - (s64) dst_pos.X});
|
||||
++x0)
|
||||
{
|
||||
s32 src_x = src_pos.X + x0;
|
||||
s32 src_y = src_pos.Y + y0;
|
||||
@ -2066,45 +2065,13 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
|
||||
s32 dst_y = dst_pos.Y + y0;
|
||||
video::SColor src_c = src->getPixel(src_x, src_y);
|
||||
video::SColor dst_c = dst->getPixel(dst_x, dst_y);
|
||||
if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
|
||||
{
|
||||
if (!overlay || (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)) {
|
||||
dst_c = blitPixel(src_c, dst_c, src_c.getAlpha());
|
||||
dst->setPixel(dst_x, dst_y, dst_c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function has been disabled because it is currently unused.
|
||||
// Feel free to re-enable if you find it handy.
|
||||
#if 0
|
||||
/*
|
||||
Draw an image on top of another one, using the specified ratio
|
||||
modify all partially-opaque pixels in the destination.
|
||||
*/
|
||||
static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
|
||||
v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio)
|
||||
{
|
||||
for (u32 y0 = 0; y0 < size.Y; y0++)
|
||||
for (u32 x0 = 0; x0 < size.X; x0++)
|
||||
{
|
||||
s32 src_x = src_pos.X + x0;
|
||||
s32 src_y = src_pos.Y + y0;
|
||||
s32 dst_x = dst_pos.X + x0;
|
||||
s32 dst_y = dst_pos.Y + y0;
|
||||
video::SColor src_c = src->getPixel(src_x, src_y);
|
||||
video::SColor dst_c = dst->getPixel(dst_x, dst_y);
|
||||
if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0)
|
||||
{
|
||||
if (ratio == -1)
|
||||
dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
|
||||
else
|
||||
dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f);
|
||||
dst->setPixel(dst_x, dst_y, dst_c);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Apply color to destination, using a weighted interpolation blend
|
||||
*/
|
||||
@ -2443,7 +2410,7 @@ static void draw_crack(video::IImage *crack, video::IImage *dst,
|
||||
if (!crack_scaled)
|
||||
return;
|
||||
|
||||
auto blit = use_overlay ? blit_with_alpha_overlay : blit_with_alpha;
|
||||
auto blit = use_overlay ? blit_with_alpha<true> : blit_with_alpha<false>;
|
||||
for (s32 i = 0; i < frame_count; ++i) {
|
||||
v2s32 dst_pos(0, frame_size.Height * i);
|
||||
blit(crack_scaled, dst, v2s32(0,0), dst_pos, frame_size);
|
||||
@ -2470,7 +2437,7 @@ void brighten(video::IImage *image)
|
||||
}
|
||||
}
|
||||
|
||||
u32 parseImageTransform(const std::string& s)
|
||||
u32 parseImageTransform(std::string_view s)
|
||||
{
|
||||
int total_transform = 0;
|
||||
|
||||
|
@ -315,7 +315,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
|
||||
static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n,
|
||||
std::vector<ItemPartColor> *colors, const ContentFeatures &f)
|
||||
{
|
||||
MeshMakeData mesh_make_data(client, false);
|
||||
MeshMakeData mesh_make_data(client->ndef(), 1, false);
|
||||
MeshCollector collector(v3f(0.0f * BS), v3f());
|
||||
mesh_make_data.setSmoothLighting(false);
|
||||
MapblockMeshGenerator gen(&mesh_make_data, &collector,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user