diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md index 8b2055e4..62ef8ec4 100644 --- a/doc/ru/block-properties.md +++ b/doc/ru/block-properties.md @@ -48,6 +48,13 @@ - "pipe" - профиль "труба". Примеры блоков: бревно, труба, лампочка - "pane" - профиль "панель". Примеры блоков: панель, дверь, табличка +### Испускаемые частицы - *particles* + +Частицы указываются в виде JSON объекта. Имена свойств можно найти [в разделе, посвященном частицам](particles.md). + +При приближении к блоку движок создаст эмиттер, который будет работать +до разрушения блока или отдаления камеры на некоторое расстояние. + ## Освещение ### Излучение - *emission*: diff --git a/doc/ru/main-page.md b/doc/ru/main-page.md index 0297cd46..310c80c4 100644 --- a/doc/ru/main-page.md +++ b/doc/ru/main-page.md @@ -19,3 +19,4 @@ - [Свойства блоков](block-properties.md) - [Свойства предметов](item-properties.md) - [Скриптинг](scripting.md) +- [Частицы](particles.md) diff --git a/doc/ru/particles.md b/doc/ru/particles.md new file mode 100644 index 00000000..bc20a4a3 --- /dev/null +++ b/doc/ru/particles.md @@ -0,0 +1,66 @@ +# Частицы + +Частицы представляют собой таблицу, все поля которой опциональны. + + +| Поле | Описание | По-умолчанию | +| --------------- | ---------------------------------------------------------------------------- | --------------- | +| texture | Текстура частицы. | "" | +| frames | Кадры анимации (массив имен текстур). Должны находиться в одном атласе. | {} | +| lighting | Освещение. | true | +| collision | Обнаружение столкновений. | true | +| max_distance | Максимальная дистанция от камеры, при которой происходит спавн частиц. | 16.0 | +| spawn_interval | Интервал спавна частиц в секундах. | 1.0 | +| lifetime | Среднее время жизни частиц в секундах. | 5.0 | +| lifetime_spread | Максимальное отклонение времени жизни частицы (от 0.0 до 1.0). | 0.2 | +| velocity | Начальная линейная скорость частиц. | {0, 0, 0} | +| acceleration | Ускорение частиц. | {0, -16, 0} | +| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} | +| size | Размер частиц. | {0.1, 0.1, 0.1} | +| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball | +| spawn_spread | Размер области спавна частиц. | {0, 0, 0} | +| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 | + +## Библиотека *gfx.particles* + +```lua +gfx.particles.emit( + -- позиция эмиттера: статические координаты или uid сущности + origin: vec3 | int, + -- количество частиц (-1 - бесконечно) + count: int, + -- таблица настроек частиц + preset: table, + -- дополнительная таблица настроек частиц + [опционально] extension: table +) -> int +``` + +Создаёт эмиттер частиц, возвращая его id. + +```lua +gfx.particles.stop(id: int) +``` + +Останавливает эмиттер без возможности возобновления. Эмиттер будет удален +позже автоматически. + +```lua +gfx.particles.is_alive(id: int) -> bool +``` + +Проверяет, работает ли эмиттер. Возвращает false если работа прекращена или +эмиттер не существует. + +```lua +gfx.particles.get_origin(id: int) -> vec3 | int +``` + +Возвращает статическую позицию или uid сущности, к которой привязан эмиттер. +Если эмиттера не существует, возвращает nil. + +```lua +gfx.particles.set_origin(id: int, origin: vec3 | int) +``` + +Устанавливает статическую позицию или uid сущности, к которой будет привязан эмиттер. diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index 12878e88..95d3d944 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -21,6 +21,7 @@ - [pack](scripting/builtins/libpack.md) - [player](scripting/builtins/libplayer.md) - [quat](scripting/builtins/libquat.md) + - [rules](scripting/builtins/librules.md) - [time](scripting/builtins/libtime.md) - [utf8](scripting/builtins/libutf8.md) - [vec2, vec3, vec4](scripting/builtins/libvecn.md) diff --git a/doc/ru/scripting/builtins/librules.md b/doc/ru/scripting/builtins/librules.md new file mode 100644 index 00000000..54f9e70a --- /dev/null +++ b/doc/ru/scripting/builtins/librules.md @@ -0,0 +1,72 @@ +# Библиотека *rules* + +```lua +rules.create( + -- имя правила + name: str, + -- значение по-умолчанию + default: bool, + -- функция-обработчик изменения значения + [опционально] handler: function +) -> int +``` + +Создаёт правило. Если указан обработчик, возвращает id для возможности удаления. + +> [!NOTE] +> Созданием правила считается вызов rules.create с назначением значения по-умолчанию. +> Не созданные правила могут быть использованы, но сброс через rules.reset приведёт +> к установке значения nil. + +```lua +rules.listen( + -- имя правила + name: str, + -- функция-обработчик изменения значения + handler: function +) -> int +``` + +Добавляет обработчик изменения значения правила. +Возвращает id для возможности удаления. +Также позволяет подписаться на правило до его создания. + +```lua +rules.unlisten(name: str, id: int) +``` + +Удаляет обработчик правила по id, если он существует. + +```lua +rules.get(name: str) -> bool | nil +``` + +Возвращает значение правила или nil, если оно ещё не было создано. + +```lua +rules.set(name: str, value: bool) +``` + +Устанавливает значение правила, вызывая обработчики. Может использоваться и +до создания правила. + +```lua +rules.reset(name: str) +``` + +Сбрасывает значение правила к значению по-умолчанию. + + +## Стандартные правила + + +| Имя | Описание | По-умолчанию | +| -------------------- | --------------------------------------------------------------- | ------------ | +| cheat-commands | Разрешить команды, имена которых есть в массиве console.chears. | true | +| show-content-access | Разрешить панель доступа к контенту. | true | +| allow-flight | Разрешить полёт | true | +| allow-noclip | Разрешить включение noclip. | true | +| allow-attack | Разрешить атаковать сущности. | true | +| allow-destroy | Разрешить разрушение блоков. | true | +| allow-cheat-movement | Разрешить специальные клавиши быстрого перемещения. | true | +| allow-debug-cheats | Разрешить нечестные элементы управления на дебаг-панели. | true | diff --git a/res/config/bindings.toml b/res/config/bindings.toml index a0ab278f..426a580f 100644 --- a/res/config/bindings.toml +++ b/res/config/bindings.toml @@ -13,6 +13,7 @@ camera.mode="key:f4" player.noclip="key:n" player.flight="key:f" player.attack="mouse:left" +player.destroy="mouse:left" player.build="mouse:right" player.pick="mouse:middle" player.drop="key:q" diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua index 5719c4fb..62d111ae 100644 --- a/res/content/base/scripts/world.lua +++ b/res/content/base/scripts/world.lua @@ -1,5 +1,5 @@ function on_block_broken(id, x, y, z, playerid) - particles.emit({x+0.5, y+0.5, z+0.5}, 64, { + gfx.particles.emit({x+0.5, y+0.5, z+0.5}, 64, { lifetime=1.0, spawn_interval=0.0001, explosion={4, 4, 4}, diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua index 711f0435..7f5ccd0e 100644 --- a/res/layouts/console.xml.lua +++ b/res/layouts/console.xml.lua @@ -61,7 +61,7 @@ function submit(text) if name == nil then name = text end - if not rules.get("cheat-commands") and table.has(console.cheats, name) then + if not rules.get("allow-cheats") and table.has(console.cheats, name) then console.log("cheat commands are disabled") document.prompt.text = "" document.prompt.focused = true diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 1df86dd6..98609688 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -33,7 +33,7 @@ console.add_command( local str = "Available commands:" for i,k in ipairs(commands) do - if rules.get("cheat-commands") or not table.has(console.cheats, k) then + if rules.get("allow-cheats") or not table.has(console.cheats, k) then str = str .. "\n " .. build_scheme(console.get_command_info(k)) end end @@ -113,7 +113,7 @@ console.add_command( console.add_command( "time.daycycle operation:[stop|reset]", - "Control time.daycycle", + "Control time.daycycle. Operations: stop, reset", function(args, kwargs) local operation = args[1] if operation == "stop" then @@ -198,7 +198,7 @@ console.add_command( ) console.add_command( - "fragment.crop filename:str", + "fragment.crop file:str", "Crop fragment", function(args, kwargs) local filename = args[1] diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index c9cef6dd..16e04b6e 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -229,7 +229,10 @@ function _rules.create(name, value, handler) end function _rules.unlisten(name, id) - local rule = _rules.get_rule(name) + local rule = _rules.rules[name] + if rule == nil then + return + end rule.listeners[utf8.encode(id)] = nil end @@ -237,7 +240,7 @@ function _rules.clear() _rules.rules = {} _rules.nextid = 1 - _rules.create("cheat-commands", true) + _rules.create("allow-cheats", true) end function __vc_create_hud_rules() @@ -250,9 +253,12 @@ function __vc_create_hud_rules() _rules.create("allow-noclip", true, function(value) input.set_enabled("player.noclip", value) end) - _rules.create("allow-destruct", true, function(value) + _rules.create("allow-attack", true, function(value) input.set_enabled("player.attack", value) end) + _rules.create("allow-destroy", true, function(value) + input.set_enabled("player.destroy", value) + end) _rules.create("allow-cheat-movement", true, function(value) input.set_enabled("movement.cheat", value) end) diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt index 40c684a4..0d15d5b0 100644 --- a/res/texts/en_US.txt +++ b/res/texts/en_US.txt @@ -29,7 +29,8 @@ movement.crouch=Crouch movement.cheat=Cheat hud.inventory=Inventory player.pick=Pick Block -player.attack=Attack / Break +player.attack=Attack +player.destroy=Destroy player.build=Place Block player.fast_interaction=Accelerated interaction player.flight=Flight diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index 490ef4d7..a7d5ce43 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -91,7 +91,8 @@ movement.crouch=Красться movement.cheat=Чит hud.inventory=Инвентарь player.pick=Подобрать Блок -player.attack=Атаковать / Сломать +player.attack=Атаковать +player.destroy=Сломать player.build=Поставить Блок player.fast_interaction=Ускоренное взаимодействие player.flight=Полёт diff --git a/src/core_defs.hpp b/src/core_defs.hpp index c2b5dd39..4a02cac9 100644 --- a/src/core_defs.hpp +++ b/src/core_defs.hpp @@ -25,6 +25,7 @@ inline const std::string BIND_CAM_MODE = "camera.mode"; inline const std::string BIND_PLAYER_NOCLIP = "player.noclip"; inline const std::string BIND_PLAYER_FLIGHT = "player.flight"; inline const std::string BIND_PLAYER_ATTACK = "player.attack"; +inline const std::string BIND_PLAYER_DESTROY = "player.destroy"; inline const std::string BIND_PLAYER_BUILD = "player.build"; inline const std::string BIND_PLAYER_PICK = "player.pick"; inline const std::string BIND_PLAYER_FAST_INTERACTOIN = diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index ed916aac..57af3c13 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -494,7 +494,9 @@ void PlayerController::updateInteraction(float delta) { bool xkey = Events::active(BIND_PLAYER_FAST_INTERACTOIN); float maxDistance = xkey ? 200.0f : 10.0f; bool longInteraction = interactionTimer <= 0 || xkey; - bool lclick = Events::jactive(BIND_PLAYER_ATTACK) || + bool lclick = Events::jactive(BIND_PLAYER_DESTROY) || + (longInteraction && Events::active(BIND_PLAYER_DESTROY)); + bool lattack = Events::jactive(BIND_PLAYER_ATTACK) || (longInteraction && Events::active(BIND_PLAYER_ATTACK)); bool rclick = Events::jactive(BIND_PLAYER_BUILD) || (longInteraction && Events::active(BIND_PLAYER_BUILD)); @@ -512,7 +514,7 @@ void PlayerController::updateInteraction(float delta) { scripting::on_item_use(player.get(), item); } if (selection.entity) { - updateEntityInteraction(selection.entity, lclick, rclick); + updateEntityInteraction(selection.entity, lattack, rclick); } return; } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 138aa73c..f2b51c53 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -599,6 +599,23 @@ namespace lua { setglobal(L, name); } + inline void openlib( + lua::State* L, + const std::string& package, + const std::string& name, + const luaL_Reg* libfuncs + ) { + if (!hasglobal(L, package)) { + createtable(L, 0, 1); + setglobal(L, package); + } + getglobal(L, package); + createtable(L, 0, 0); + luaL_setfuncs(L, libfuncs, 0); + setfield(L, name); + pop(L); + } + inline const char* require_string_field( lua::State* L, const std::string& name, int idx=-1 ) { diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index 0d970e0d..87548c2e 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -24,7 +24,7 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { auto L = lua::get_main_state(); lua::openlib(L, "hud", hudlib); - lua::openlib(L, "particles", particleslib); + lua::openlib(L, "gfx", "particles", particleslib); if (lua::getglobal(L, "__vc_create_hud_rules")) { lua::call_nothrow(L, 0, 0); diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 774ab553..31b0cff2 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -26,7 +26,7 @@ dv::value ParticlesPreset::serialize() const { if (frames.empty()) { root["texture"] = texture; } else { - auto& arr = root.list("animation"); + auto& arr = root.list("frames"); for (const auto& frame : frames) { arr.add(frame); } diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index 262b2c07..483ce1b4 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -43,7 +43,7 @@ struct ParticlesPreset : public Serializable { /// @brief Particle size glm::vec3 size {0.1f}; /// @brief Spawn spread shape - ParticleSpawnShape spawnShape; + ParticleSpawnShape spawnShape = BALL; /// @brief Spawn spread glm::vec3 spawnSpread {}; /// @brief Texture name