Merge branch 'headless-mode' into Fix-usage-vcpkg-for-windows-and-add-cmake-preset-for-vscode
This commit is contained in:
commit
f73bc1b06b
50
dev/tests/filesystem.lua
Normal file
50
dev/tests/filesystem.lua
Normal file
@ -0,0 +1,50 @@
|
||||
debug.log("check initial state")
|
||||
assert(file.exists("config:"))
|
||||
|
||||
debug.log("write text file")
|
||||
assert(file.write("config:text.txt", "example, пример"))
|
||||
assert(file.exists("config:text.txt"))
|
||||
|
||||
debug.log("read text file")
|
||||
assert(file.read("config:text.txt") == "example, пример")
|
||||
|
||||
debug.log("delete file")
|
||||
file.remove("config:text.txt")
|
||||
assert(not file.exists("config:text.txt"))
|
||||
|
||||
debug.log("create directory")
|
||||
file.mkdir("config:dir")
|
||||
assert(file.isdir("config:dir"))
|
||||
|
||||
debug.log("remove directory")
|
||||
file.remove("config:dir")
|
||||
|
||||
debug.log("create directories")
|
||||
file.mkdirs("config:dir/subdir/other")
|
||||
assert(file.isdir("config:dir/subdir/other"))
|
||||
|
||||
debug.log("remove tree")
|
||||
file.remove_tree("config:dir")
|
||||
assert(not file.isdir("config:dir"))
|
||||
|
||||
debug.log("write binary file")
|
||||
local bytes = {0xDE, 0xAD, 0xC0, 0xDE}
|
||||
file.write_bytes("config:binary", bytes)
|
||||
assert(file.exists("config:binary"))
|
||||
|
||||
debug.log("read binary file")
|
||||
local rbytes = file.read_bytes("config:binary")
|
||||
assert(#rbytes == #bytes)
|
||||
for i, b in ipairs(bytes) do
|
||||
assert(rbytes[i] == b)
|
||||
end
|
||||
|
||||
debug.log("delete file")
|
||||
file.remove("config:binary")
|
||||
assert(not file.exists("config:binary"))
|
||||
|
||||
debug.log("checking entry points for writeability")
|
||||
assert(file.is_writeable("config:"))
|
||||
assert(file.is_writeable("export:"))
|
||||
assert(not file.is_writeable("user:"))
|
||||
assert(not file.is_writeable("res:"))
|
||||
@ -81,6 +81,13 @@ Turns off block model shading
|
||||
|
||||
Determines the presence of the vertex AO effect. Turned-on by default.
|
||||
|
||||
### *culling*
|
||||
|
||||
Face culling mode:
|
||||
- **default** - normal face culling
|
||||
- **optional** - face culling among blocks of the same rendering group can be disabled via the `graphics.dense-render` setting.
|
||||
- **disabled** - face culling among blocks of the same rendering group disabled.
|
||||
|
||||
## Physics
|
||||
|
||||
### *obstacle*
|
||||
|
||||
@ -16,6 +16,10 @@ Particles are a table, all fields of which are optional.
|
||||
| acceleration | Particles acceleration. | {0, -16, 0} |
|
||||
| explosion | Force of particles explosion on spawn. | {2, 2, 2} |
|
||||
| size | Size of particles. | {0.1, 0.1, 0.1} |
|
||||
| size_spread | Maximum particle size spread over time. | 0.2 |
|
||||
| angle_spread | Maximum initial rotation angle spread (0 to 1) | 0.0 |
|
||||
| min_angular_vel | Minimum angular velocity (radians per sec). Non-negative. | 0.0 |
|
||||
| max_angular_vel | Maximum angular velocity (radians per sec). Non-negative. | 0.0 |
|
||||
| spawn_shape | Shape of particle spawn area. (ball/sphere/box) | ball |
|
||||
| spawn_spread | Size of particle spawn area. | {0, 0, 0} |
|
||||
| random_sub_uv | Size of random texture subregion (1 - entire texture will be used). | 1.0 |
|
||||
|
||||
@ -25,6 +25,12 @@ file.read_bytes(path: str) -> array of integers
|
||||
|
||||
Read file into bytes array.
|
||||
|
||||
```lua
|
||||
file.is_writeable(path: str) -> bool
|
||||
```
|
||||
|
||||
Checks if the specified path is writable.
|
||||
|
||||
```python
|
||||
file.write(path: str, text: str) -> nil
|
||||
```
|
||||
|
||||
@ -32,6 +32,21 @@ inventory.size(invid: int) -> int
|
||||
-- Returns remaining count if could not to add fully.
|
||||
inventory.add(invid: int, itemid: int, count: int) -> int
|
||||
|
||||
-- Returns the index of the first matching slot in the given range.
|
||||
-- If no matching slot was found, returns nil
|
||||
inventory.find_by_item(
|
||||
-- inventory id
|
||||
invid: int,
|
||||
-- item id
|
||||
itemid: int,
|
||||
-- [optional] index of the slot range start (from 0)
|
||||
range_begin: int,
|
||||
-- [optional] index of the slot range end (from 0)
|
||||
range_end: int,
|
||||
-- [optional] minimum item count in the slot
|
||||
min_count: int = 1
|
||||
) -> int
|
||||
|
||||
-- Returns block inventory ID or 0.
|
||||
inventory.get_block(x: int, y: int, z: int) -> int
|
||||
|
||||
|
||||
@ -70,6 +70,13 @@ player.set_instant_destruction(playerid: int, bool)
|
||||
|
||||
Getter and setter for instant destruction of blocks when the `player.destroy` binding is activated.
|
||||
|
||||
```lua
|
||||
player.is_loading_chunks(playerid: int) -> bool
|
||||
player.set_loading_chunks(playerid: int, bool)
|
||||
```
|
||||
|
||||
Getter and setter of the property that determines whether the player is loading chunks.
|
||||
|
||||
``` lua
|
||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||
@ -84,6 +91,12 @@ player.get_name(playerid: int) -> str
|
||||
|
||||
Player name setter and getter
|
||||
|
||||
```lua
|
||||
player.set_selected_slot(playerid: int, slotid: int)
|
||||
```
|
||||
|
||||
Sets the selected slot index
|
||||
|
||||
```lua
|
||||
player.get_selected_block(playerid: int) -> x,y,z
|
||||
```
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
* [Small structures placement](#small-structures-placement)
|
||||
* [Wide structures placement](#wide-structures-placement)
|
||||
- [Structural air](#structural-air)
|
||||
- [Generator 'Demo' (base:demo)](#generator-demo-basedemo)
|
||||
|
||||
## Basic concepts
|
||||
|
||||
@ -473,3 +474,24 @@ function place_structures_wide(
|
||||
`core:struct_air` - a block that should be used in chunks to mark empty space that should not be filled with blocks when generated in the world.
|
||||
|
||||
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
|
||||
|
||||
# Generator 'Demo' (base:demo)
|
||||
|
||||
## Adding new ore
|
||||
|
||||
To add a new ore in your pack:
|
||||
1. In the `generators` folder, create a `demo.files` folder (you don't need to create demo.toml).
|
||||
|
||||
2. In the created folder, create a fragments folder and place the ore fragment file in it.
|
||||
3. In `demo.files`, create a structures.toml file:
|
||||
```toml
|
||||
fragment_name = {}
|
||||
```
|
||||
4. Also in `demo.files`, create an ores.json file:
|
||||
```json
|
||||
[
|
||||
{"struct": "fragment_name", "rarity": rarity}
|
||||
]
|
||||
```
|
||||
The higher the rarity value, the less ore generation chance.
|
||||
You can rely on the rarity of coal ore: 4400.
|
||||
|
||||
@ -82,7 +82,6 @@
|
||||
|
||||
При значении `true` блок не препятствует прохождению вертикального луча солнечного света.
|
||||
|
||||
|
||||
### Без освещения - *shadeless*
|
||||
|
||||
Выключает освещение на модели блока.
|
||||
@ -91,6 +90,13 @@
|
||||
|
||||
Определяет наличие эффекта вершинного AO. Включен по-умолчанию.
|
||||
|
||||
### Отсечение - *culling*
|
||||
|
||||
Режим отсечения граней:
|
||||
- **default** - обычное отсечение граней
|
||||
- **optional** - отсечение граней среди блоков одной группы отрисовки можно отключить через настройку `graphics.dense-render` (Плотный рендер блоков).
|
||||
- **disabled** - отсечение граней среди блоков одной группы отрисовки отключено.
|
||||
|
||||
## Физика
|
||||
|
||||
### Препятствие - *obstacle*
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} |
|
||||
| size | Размер частиц. | {0.1, 0.1, 0.1} |
|
||||
| size_spread | Максимальное отклонение времени размера частиц. | 0.2 |
|
||||
| angle_spread | Максимальное отклонение начального угла поворота (от 0 до 1) | 0.0 |
|
||||
| min_angular_vel | Минимальная угловая скорость (радианы в сек.). Неотрицательное. | 0.0 |
|
||||
| max_angular_vel | Максимальная угловая скорость (радианы в сек.). Неотрицательное. | 0.0 |
|
||||
| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball |
|
||||
| spawn_spread | Размер области спавна частиц. | {0, 0, 0} |
|
||||
| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 |
|
||||
|
||||
@ -25,6 +25,12 @@ file.read_bytes(путь: str) -> array of integers
|
||||
|
||||
Читает файл в массив байт.
|
||||
|
||||
```lua
|
||||
file.is_writeable(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, доступно ли право записи по указанному пути.
|
||||
|
||||
```python
|
||||
file.write(путь: str, текст: str) -> nil
|
||||
```
|
||||
|
||||
@ -38,6 +38,21 @@ inventory.add(
|
||||
count: int
|
||||
) -> int
|
||||
|
||||
-- Возвращает индекс первого подходящего под критерии слота в заданном диапазоне.
|
||||
-- Если подходящий слот не был найден, возвращает nil
|
||||
inventory.find_by_item(
|
||||
-- id инвентаря
|
||||
invid: int,
|
||||
-- id предмета
|
||||
itemid: int,
|
||||
-- [опционально] индекс начала диапазона слотов (c 0)
|
||||
range_begin: int,
|
||||
-- [опционально] индекс конца диапазона слотов (c 0)
|
||||
range_end: int,
|
||||
-- [опционально] минимальное количество предмета в слоте
|
||||
min_count: int = 1
|
||||
) -> int
|
||||
|
||||
-- Функция возвращает id инвентаря блока.
|
||||
-- Если блок не может иметь инвентарь - возвращает 0.
|
||||
inventory.get_block(x: int, y: int, z: int) -> int
|
||||
|
||||
@ -70,6 +70,13 @@ player.set_instant_destruction(playerid: int, bool)
|
||||
|
||||
Геттер и сеттер мнгновенного разрушения блоков при активации привязки `player.destroy`.
|
||||
|
||||
```lua
|
||||
player.is_loading_chunks(playerid: int) -> bool
|
||||
player.set_loading_chunks(playerid: int, bool)
|
||||
```
|
||||
|
||||
Геттер и сеттер свойства, определяющего, прогружает ли игрок чанки вокруг.
|
||||
|
||||
```lua
|
||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||
@ -84,6 +91,12 @@ player.get_name(playerid: int) -> str
|
||||
|
||||
Сеттер и геттер имени игрока
|
||||
|
||||
```lua
|
||||
player.set_selected_slot(playerid: int, slotid: int)
|
||||
```
|
||||
|
||||
Устанавливает индекс выбранного слота
|
||||
|
||||
```lua
|
||||
player.get_selected_block(playerid: int) -> x,y,z
|
||||
```
|
||||
|
||||
@ -46,6 +46,13 @@ table.remove_value(t: table, x: object)
|
||||
|
||||
Удаляет элемент **x** из **t**.
|
||||
|
||||
```lua
|
||||
table.shuffle(t: table) -> table
|
||||
```
|
||||
|
||||
Перемешивает значения в таблице.
|
||||
|
||||
|
||||
```lua
|
||||
table.tostring(t: table) -> string
|
||||
```
|
||||
@ -146,6 +153,18 @@ math.rand(low, high)
|
||||
|
||||
Возвращает случайное дробное число в диапазоне от **low** до **high**.
|
||||
|
||||
```lua
|
||||
math.normalize(num: number, [опционально] conf: num) -> number
|
||||
```
|
||||
|
||||
Возвращает нормализованное значение num относительно conf.
|
||||
|
||||
```lua
|
||||
math.round(num: number, [опционально] places: num) -> number
|
||||
```
|
||||
|
||||
Возвращает округлённое значение num до указанного количества знаков после запятой places.
|
||||
|
||||
## Дополнительные глобальные функции
|
||||
|
||||
В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
* [Расстановка малых структур](#расстановка-малых-структур)
|
||||
* [Расстановка 'широких' структур](#расстановка-широких-структур)
|
||||
- [Структурный воздух](#структурный-воздух)
|
||||
- [Генератор 'Demo' (base:demo)](#генератор-demo-basedemo)
|
||||
|
||||
## Основные понятия
|
||||
|
||||
@ -478,3 +479,23 @@ function place_structures_wide(
|
||||
`core:struct_air` - блок, которые следует использовать в фрагментах для обозначения пустого пространства, которое не должно заполняться блоками при генерации в мире.
|
||||
|
||||
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
|
||||
|
||||
# Генератор 'Demo' (base:demo)
|
||||
|
||||
## Добавление новой руды
|
||||
|
||||
Чтобы добавить новую руду из своего пака:
|
||||
1. В папке `generators` создайте папку `demo.files` (demo.toml создавать не нужно).
|
||||
2. В созданной папке создайте папку fragments и поместите в неё файл фрагмента руды.
|
||||
3. В `demo.files` создайте файл structures.toml:
|
||||
```toml
|
||||
имя_фрагмента = {}
|
||||
```
|
||||
4. Также в `demo.files` создайте файл ores.json:
|
||||
```json
|
||||
[
|
||||
{"struct": "имя_фрагмента", "rarity": редкость}
|
||||
]
|
||||
```
|
||||
Чем выше значение редкости, тем меньше вероятность генерации руды.
|
||||
Опираться можно на редкость угольной руды: 4400.
|
||||
|
||||
@ -1,5 +1,26 @@
|
||||
{
|
||||
"texture": "leaves",
|
||||
"material": "base:grass",
|
||||
"draw-group": 5,
|
||||
"culling": "optional",
|
||||
"particles": {
|
||||
"lifetime": 4.0,
|
||||
"spawn_interval": 1000.0,
|
||||
"acceleration": [0, -0.1, 0],
|
||||
"velocity": [0.2, -2.5, 0.3],
|
||||
"explosion": [0, 0, 0],
|
||||
"collision": false,
|
||||
"size": [0.3, 0.3, 0.3],
|
||||
"size_spread": 0.2,
|
||||
"spawn_shape": "box",
|
||||
"spawn_spread": [0.2, 0.2, 0.2],
|
||||
"angle_spread": 1.0,
|
||||
"min_angular_vel": 0.5,
|
||||
"max_angular_vel": 2.0,
|
||||
"lighting": true,
|
||||
"frames": [
|
||||
"particles:leaf_0"
|
||||
]
|
||||
},
|
||||
"base:durability": 0.7
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 899 B |
BIN
res/content/base/textures/blocks/leaves_opaque.png
Normal file
BIN
res/content/base/textures/blocks/leaves_opaque.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
res/content/base/textures/particles/leaf_0.png
Normal file
BIN
res/content/base/textures/particles/leaf_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
@ -41,4 +41,5 @@ function on_open()
|
||||
create_setting("graphics.fog-curve", "Fog Curve", 0.1)
|
||||
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
|
||||
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
|
||||
create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip")
|
||||
end
|
||||
|
||||
@ -72,9 +72,14 @@ function data_buffer:put_byte(byte)
|
||||
end
|
||||
|
||||
function data_buffer:put_bytes(bytes)
|
||||
if type(self.bytes) == 'table' then
|
||||
for i = 1, #bytes do
|
||||
self:put_byte(bytes[i])
|
||||
end
|
||||
else
|
||||
self.bytes:insert(self.pos, bytes)
|
||||
self.pos = self.pos + #bytes
|
||||
end
|
||||
end
|
||||
|
||||
function data_buffer:put_single(single)
|
||||
|
||||
58
res/scripts/hud.lua
Normal file
58
res/scripts/hud.lua
Normal file
@ -0,0 +1,58 @@
|
||||
function on_hud_open()
|
||||
input.add_callback("player.pick", function ()
|
||||
if hud.is_paused() or hud.is_inventory_open() then
|
||||
return
|
||||
end
|
||||
local pid = hud.get_player()
|
||||
local x, y, z = player.get_selected_block(pid)
|
||||
if x == nil then
|
||||
return
|
||||
end
|
||||
local id = block.get_picking_item(block.get(x, y, z))
|
||||
local inv, cur_slot = player.get_inventory(pid)
|
||||
local slot = inventory.find_by_item(inv, id, 0, 9)
|
||||
if slot then
|
||||
player.set_selected_slot(pid, slot)
|
||||
return
|
||||
end
|
||||
if not rules.get("allow-content-access") then
|
||||
return
|
||||
end
|
||||
slot = inventory.find_by_item(inv, 0, 0, 9)
|
||||
if slot then
|
||||
cur_slot = slot
|
||||
end
|
||||
player.set_selected_slot(pid, cur_slot)
|
||||
inventory.set(inv, cur_slot, id, 1)
|
||||
end)
|
||||
|
||||
input.add_callback("player.noclip", function ()
|
||||
if hud.is_paused() or hud.is_inventory_open() then
|
||||
return
|
||||
end
|
||||
local pid = hud.get_player()
|
||||
if player.is_noclip(pid) then
|
||||
player.set_flight(pid, false)
|
||||
player.set_noclip(pid, false)
|
||||
else
|
||||
player.set_flight(pid, true)
|
||||
player.set_noclip(pid, true)
|
||||
end
|
||||
end)
|
||||
|
||||
input.add_callback("player.flight", function ()
|
||||
if hud.is_paused() or hud.is_inventory_open() then
|
||||
return
|
||||
end
|
||||
local pid = hud.get_player()
|
||||
if player.is_noclip(pid) then
|
||||
return
|
||||
end
|
||||
if player.is_flight(pid) then
|
||||
player.set_flight(pid, false)
|
||||
else
|
||||
player.set_flight(pid, true)
|
||||
player.set_vel(pid, 0, 1, 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@ -320,7 +320,6 @@ function __vc_on_hud_open()
|
||||
|
||||
_rules.create("allow-content-access", hud._is_content_access(), function(value)
|
||||
hud._set_content_access(value)
|
||||
input.set_enabled("player.pick", value)
|
||||
end)
|
||||
_rules.create("allow-flight", true, function(value)
|
||||
input.set_enabled("player.flight", value)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- Check if given table is an array
|
||||
function is_array(x)
|
||||
if #t > 0 then
|
||||
if #x > 0 then
|
||||
return true
|
||||
end
|
||||
for k, v in pairs(x) do
|
||||
@ -51,6 +51,19 @@ function math.rand(low, high)
|
||||
return low + (high - low) * math.random()
|
||||
end
|
||||
|
||||
function math.normalize(num, conf)
|
||||
conf = conf or 1
|
||||
|
||||
return (num / conf) % 1
|
||||
end
|
||||
|
||||
function math.round(num, places)
|
||||
places = places or 0
|
||||
|
||||
local mult = 10 ^ places
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
function table.copy(t)
|
||||
@ -91,6 +104,15 @@ function table.random(t)
|
||||
return t[math.random(1, #t)]
|
||||
end
|
||||
|
||||
function table.shuffle(t)
|
||||
for i = #t, 2, -1 do
|
||||
local j = math.random(i)
|
||||
t[i], t[j] = t[j], t[i]
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
local pattern_escape_replacements = {
|
||||
|
||||
@ -16,6 +16,7 @@ devtools.traceback=Traceback (most recent call first)
|
||||
# Tooltips
|
||||
graphics.gamma.tooltip=Lighting brightness curve
|
||||
graphics.backlight.tooltip=Backlight to prevent total darkness
|
||||
graphics.dense-render.tooltip=Enables transparency in blocks like leaves
|
||||
|
||||
# settings
|
||||
settings.Controls Search Mode=Search by attached button name
|
||||
|
||||
@ -28,6 +28,7 @@ pack.remove-confirm=Удалить весь поставляемый паком/
|
||||
# Подсказки
|
||||
graphics.gamma.tooltip=Кривая яркости освещения
|
||||
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
|
||||
graphics.dense-render.tooltip=Включает прозрачность блоков, таких как листья.
|
||||
|
||||
# Меню
|
||||
menu.Apply=Применить
|
||||
@ -67,6 +68,7 @@ world.delete-confirm=Удалить мир безвозвратно?
|
||||
# Настройки
|
||||
settings.Ambient=Фон
|
||||
settings.Backlight=Подсветка
|
||||
settings.Dense blocks render=Плотный рендер блоков
|
||||
settings.Camera Shaking=Тряска Камеры
|
||||
settings.Camera Inertia=Инерция Камеры
|
||||
settings.Camera FOV Effects=Эффекты поля зрения
|
||||
|
||||
@ -239,10 +239,18 @@ void ContentLoader::loadBlock(
|
||||
}
|
||||
def.model = *model;
|
||||
} else if (!modelTypeName.empty()) {
|
||||
logger.error() << "unknown model " << modelTypeName;
|
||||
logger.error() << "unknown model: " << modelTypeName;
|
||||
def.model = BlockModel::none;
|
||||
}
|
||||
|
||||
std::string cullingModeName = to_string(def.culling);
|
||||
root.at("culling").get(cullingModeName);
|
||||
if (auto mode = CullingMode_from(cullingModeName)) {
|
||||
def.culling = *mode;
|
||||
} else {
|
||||
logger.error() << "unknown culling mode: " << cullingModeName;
|
||||
}
|
||||
|
||||
root.at("material").get(def.material);
|
||||
|
||||
// rotation profile
|
||||
|
||||
@ -95,12 +95,13 @@ struct ContentPackStats {
|
||||
}
|
||||
};
|
||||
|
||||
struct world_funcs_set {
|
||||
bool onblockplaced : 1;
|
||||
bool onblockreplaced : 1;
|
||||
bool onblockbroken : 1;
|
||||
bool onblockinteract : 1;
|
||||
bool onplayertick : 1;
|
||||
struct WorldFuncsSet {
|
||||
bool onblockplaced;
|
||||
bool onblockreplaced;
|
||||
bool onblockbreaking;
|
||||
bool onblockbroken;
|
||||
bool onblockinteract;
|
||||
bool onplayertick;
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
@ -108,7 +109,7 @@ class ContentPackRuntime {
|
||||
ContentPackStats stats {};
|
||||
scriptenv env;
|
||||
public:
|
||||
world_funcs_set worldfuncsset {};
|
||||
WorldFuncsSet worldfuncsset {};
|
||||
|
||||
ContentPackRuntime(ContentPack info, scriptenv env);
|
||||
~ContentPackRuntime();
|
||||
|
||||
@ -21,12 +21,9 @@ inline const std::string BIND_MOVE_CROUCH = "movement.crouch";
|
||||
inline const std::string BIND_MOVE_CHEAT = "movement.cheat";
|
||||
inline const std::string BIND_CAM_ZOOM = "camera.zoom";
|
||||
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 =
|
||||
"player.fast_interaction";
|
||||
inline const std::string BIND_HUD_INVENTORY = "hud.inventory";
|
||||
|
||||
@ -83,6 +83,9 @@ Engine::Engine(CoreParameters coreParameters)
|
||||
paths.setResourcesFolder(params.resFolder);
|
||||
paths.setUserFilesFolder(params.userFolder);
|
||||
paths.prepare();
|
||||
if (!params.scriptFile.empty()) {
|
||||
paths.setScriptFolder(params.scriptFile.parent_path());
|
||||
}
|
||||
loadSettings();
|
||||
|
||||
auto resdir = paths.getResourcesFolder();
|
||||
|
||||
@ -169,6 +169,10 @@ void EnginePaths::setResourcesFolder(std::filesystem::path folder) {
|
||||
this->resourcesFolder = std::move(folder);
|
||||
}
|
||||
|
||||
void EnginePaths::setScriptFolder(std::filesystem::path folder) {
|
||||
this->scriptFolder = std::move(folder);
|
||||
}
|
||||
|
||||
void EnginePaths::setCurrentWorldFolder(std::filesystem::path folder) {
|
||||
this->currentWorldFolder = std::move(folder);
|
||||
}
|
||||
@ -211,7 +215,9 @@ std::filesystem::path EnginePaths::resolve(
|
||||
if (prefix == "export") {
|
||||
return userFilesFolder / EXPORT_FOLDER / fs::u8path(filename);
|
||||
}
|
||||
|
||||
if (prefix == "script" && scriptFolder) {
|
||||
return scriptFolder.value() / fs::u8path(filename);
|
||||
}
|
||||
if (contentPacks) {
|
||||
for (auto& pack : *contentPacks) {
|
||||
if (pack.id == prefix) {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
@ -26,6 +27,8 @@ public:
|
||||
void setResourcesFolder(std::filesystem::path folder);
|
||||
std::filesystem::path getResourcesFolder() const;
|
||||
|
||||
void setScriptFolder(std::filesystem::path folder);
|
||||
|
||||
std::filesystem::path getWorldFolderByName(const std::string& name);
|
||||
std::filesystem::path getWorldsFolder() const;
|
||||
std::filesystem::path getConfigFolder() const;
|
||||
@ -51,6 +54,7 @@ private:
|
||||
std::filesystem::path userFilesFolder {"."};
|
||||
std::filesystem::path resourcesFolder {"res"};
|
||||
std::filesystem::path currentWorldFolder;
|
||||
std::optional<std::filesystem::path> scriptFolder;
|
||||
std::vector<ContentPack>* contentPacks = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@ -68,10 +68,12 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
|
||||
builder.section("graphics");
|
||||
builder.add("fog-curve", &settings.graphics.fogCurve);
|
||||
builder.add("backlight", &settings.graphics.backlight);
|
||||
builder.add("dense-render", &settings.graphics.denseRender);
|
||||
builder.add("gamma", &settings.graphics.gamma);
|
||||
builder.add("frustum-culling", &settings.graphics.frustumCulling);
|
||||
builder.add("skybox-resolution", &settings.graphics.skyboxResolution);
|
||||
builder.add("chunk-max-vertices", &settings.graphics.chunkMaxVertices);
|
||||
builder.add("chunk-max-vertices-dense", &settings.graphics.chunkMaxVerticesDense);
|
||||
builder.add("chunk-max-renderers", &settings.graphics.chunkMaxRenderers);
|
||||
|
||||
builder.section("ui");
|
||||
|
||||
@ -6,32 +6,39 @@
|
||||
#include "assets/Assets.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "content/ContentPack.hpp"
|
||||
#include "core_defs.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "maths/UVRegion.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "core_defs.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
ContentGfxCache::ContentGfxCache(const Content* content, const Assets& assets)
|
||||
: content(content) {
|
||||
auto indices = content->getIndices();
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->blocks.count() * 6);
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
const auto& blocks = indices->blocks.getIterable();
|
||||
for (blockid_t i = 0; i < blocks.size(); i++) {
|
||||
auto def = blocks[i];
|
||||
ContentGfxCache::ContentGfxCache(
|
||||
const Content& content,
|
||||
const Assets& assets,
|
||||
const GraphicsSettings& settings
|
||||
)
|
||||
: content(content), assets(assets), settings(settings) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void ContentGfxCache::refresh(const Block& def, const Atlas& atlas) {
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
const std::string& tex = def->textureFaces[side];
|
||||
std::string tex = def.textureFaces[side];
|
||||
if (def.culling == CullingMode::OPTIONAL &&
|
||||
!settings.denseRender.get() && atlas.has(tex + "_opaque")) {
|
||||
tex = tex + "_opaque";
|
||||
}
|
||||
if (atlas.has(tex)) {
|
||||
sideregions[i * 6 + side] = atlas.get(tex);
|
||||
sideregions[def.rt.id * 6 + side] = atlas.get(tex);
|
||||
} else if (atlas.has(TEXTURE_NOTFOUND)) {
|
||||
sideregions[i * 6 + side] = atlas.get(TEXTURE_NOTFOUND);
|
||||
sideregions[def.rt.id * 6 + side] = atlas.get(TEXTURE_NOTFOUND);
|
||||
}
|
||||
}
|
||||
if (def->model == BlockModel::custom) {
|
||||
auto model = assets.require<model::Model>(def->modelName);
|
||||
if (def.model == BlockModel::custom) {
|
||||
auto model = assets.require<model::Model>(def.modelName);
|
||||
// temporary dirty fix tbh
|
||||
if (def->modelName.find(':') == std::string::npos) {
|
||||
if (def.modelName.find(':') == std::string::npos) {
|
||||
for (auto& mesh : model.meshes) {
|
||||
size_t pos = mesh.texture.find(':');
|
||||
if (pos == std::string::npos) {
|
||||
@ -44,15 +51,26 @@ ContentGfxCache::ContentGfxCache(const Content* content, const Assets& assets)
|
||||
}
|
||||
}
|
||||
}
|
||||
models[def->rt.id] = std::move(model);
|
||||
models[def.rt.id] = std::move(model);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentGfxCache::refresh() {
|
||||
auto indices = content.getIndices();
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->blocks.count() * 6);
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
const auto& blocks = indices->blocks.getIterable();
|
||||
for (blockid_t i = 0; i < blocks.size(); i++) {
|
||||
auto def = blocks[i];
|
||||
refresh(*def, atlas);
|
||||
}
|
||||
}
|
||||
|
||||
ContentGfxCache::~ContentGfxCache() = default;
|
||||
|
||||
const Content* ContentGfxCache::getContent() const {
|
||||
return content;
|
||||
return &content;
|
||||
}
|
||||
|
||||
const model::Model& ContentGfxCache::getModel(blockid_t id) const {
|
||||
|
||||
@ -10,19 +10,29 @@
|
||||
|
||||
class Content;
|
||||
class Assets;
|
||||
class Atlas;
|
||||
class Block;
|
||||
struct UVRegion;
|
||||
struct GraphicsSettings;
|
||||
|
||||
namespace model {
|
||||
struct Model;
|
||||
}
|
||||
|
||||
class ContentGfxCache {
|
||||
const Content* content;
|
||||
const Content& content;
|
||||
const Assets& assets;
|
||||
const GraphicsSettings& settings;
|
||||
|
||||
// array of block sides uv regions (6 per block)
|
||||
std::unique_ptr<UVRegion[]> sideregions;
|
||||
std::unordered_map<blockid_t, model::Model> models;
|
||||
public:
|
||||
ContentGfxCache(const Content* content, const Assets& assets);
|
||||
ContentGfxCache(
|
||||
const Content& content,
|
||||
const Assets& assets,
|
||||
const GraphicsSettings& settings
|
||||
);
|
||||
~ContentGfxCache();
|
||||
|
||||
inline const UVRegion& getRegion(blockid_t id, int side) const {
|
||||
@ -32,4 +42,8 @@ public:
|
||||
const model::Model& getModel(blockid_t id) const;
|
||||
|
||||
const Content* getContent() const;
|
||||
|
||||
void refresh(const Block& block, const Atlas& atlas);
|
||||
|
||||
void refresh();
|
||||
};
|
||||
|
||||
@ -14,12 +14,17 @@
|
||||
#include "world/Level.hpp"
|
||||
|
||||
LevelFrontend::LevelFrontend(
|
||||
Player* currentPlayer, LevelController* controller, Assets& assets
|
||||
) : level(*controller->getLevel()),
|
||||
Player* currentPlayer,
|
||||
LevelController* controller,
|
||||
Assets& assets,
|
||||
const EngineSettings& settings
|
||||
)
|
||||
: level(*controller->getLevel()),
|
||||
controller(controller),
|
||||
assets(assets),
|
||||
contentCache(std::make_unique<ContentGfxCache>(level.content, assets))
|
||||
{
|
||||
contentCache(std::make_unique<ContentGfxCache>(
|
||||
*level.content, assets, settings.graphics
|
||||
)) {
|
||||
assets.store(
|
||||
BlocksPreview::build(
|
||||
*contentCache, assets, *level.content->getIndices()
|
||||
@ -98,6 +103,10 @@ const Assets& LevelFrontend::getAssets() const {
|
||||
return assets;
|
||||
}
|
||||
|
||||
ContentGfxCache& LevelFrontend::getContentGfxCache() {
|
||||
return *contentCache;
|
||||
}
|
||||
|
||||
const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
|
||||
return *contentCache;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ class Assets;
|
||||
class Player;
|
||||
class ContentGfxCache;
|
||||
class LevelController;
|
||||
struct EngineSettings;
|
||||
|
||||
class LevelFrontend {
|
||||
Level& level;
|
||||
@ -14,12 +15,18 @@ class LevelFrontend {
|
||||
const Assets& assets;
|
||||
std::unique_ptr<ContentGfxCache> contentCache;
|
||||
public:
|
||||
LevelFrontend(Player* currentPlayer, LevelController* controller, Assets& assets);
|
||||
LevelFrontend(
|
||||
Player* currentPlayer,
|
||||
LevelController* controller,
|
||||
Assets& assets,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
~LevelFrontend();
|
||||
|
||||
Level& getLevel();
|
||||
const Level& getLevel() const;
|
||||
const Assets& getAssets() const;
|
||||
const ContentGfxCache& getContentGfxCache() const;
|
||||
ContentGfxCache& getContentGfxCache();
|
||||
LevelController* getController() const;
|
||||
};
|
||||
|
||||
@ -154,7 +154,7 @@ static constexpr uint WORLDGEN_IMG_SIZE = 128U;
|
||||
Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
||||
: engine(engine),
|
||||
assets(*engine.getAssets()),
|
||||
gui(engine.getGUI()),
|
||||
gui(*engine.getGUI()),
|
||||
frontend(frontend),
|
||||
player(player),
|
||||
debugImgWorldGen(std::make_unique<ImageData>(
|
||||
@ -183,11 +183,11 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
||||
engine, frontend.getLevel(), player, allowDebugCheats
|
||||
);
|
||||
debugPanel->setZIndex(2);
|
||||
gui->add(debugPanel);
|
||||
gui.add(debugPanel);
|
||||
|
||||
gui->add(darkOverlay);
|
||||
gui->add(hotbarView);
|
||||
gui->add(contentAccessPanel);
|
||||
gui.add(darkOverlay);
|
||||
gui.add(hotbarView);
|
||||
gui.add(contentAccessPanel);
|
||||
|
||||
auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16);
|
||||
dplotter->setGravity(Gravity::bottom_right);
|
||||
@ -208,10 +208,10 @@ Hud::~Hud() {
|
||||
for (auto& element : elements) {
|
||||
onRemove(element);
|
||||
}
|
||||
gui->remove(hotbarView);
|
||||
gui->remove(darkOverlay);
|
||||
gui->remove(contentAccessPanel);
|
||||
gui->remove(debugPanel);
|
||||
gui.remove(hotbarView);
|
||||
gui.remove(darkOverlay);
|
||||
gui.remove(contentAccessPanel);
|
||||
gui.remove(debugPanel);
|
||||
}
|
||||
|
||||
/// @brief Remove all elements marked as removed
|
||||
@ -322,9 +322,9 @@ void Hud::updateWorldGenDebugVisualization() {
|
||||
void Hud::update(bool visible) {
|
||||
const auto& level = frontend.getLevel();
|
||||
const auto& chunks = *player.chunks;
|
||||
auto menu = gui->getMenu();
|
||||
const auto& menu = gui.getMenu();
|
||||
|
||||
debugPanel->setVisible(player.debug && visible);
|
||||
debugPanel->setVisible(debug && visible);
|
||||
|
||||
if (!visible && inventoryOpen) {
|
||||
closeInventory();
|
||||
@ -333,7 +333,7 @@ void Hud::update(bool visible) {
|
||||
setPause(false);
|
||||
}
|
||||
|
||||
if (!gui->isFocusCaught()) {
|
||||
if (!gui.isFocusCaught()) {
|
||||
processInput(visible);
|
||||
}
|
||||
if ((pause || inventoryOpen) == Events::_cursor_locked) {
|
||||
@ -359,7 +359,7 @@ void Hud::update(bool visible) {
|
||||
|
||||
if (visible) {
|
||||
for (auto& element : elements) {
|
||||
element.update(pause, inventoryOpen, player.debug);
|
||||
element.update(pause, inventoryOpen, debug);
|
||||
if (element.isRemoved()) {
|
||||
onRemove(element);
|
||||
}
|
||||
@ -367,8 +367,8 @@ void Hud::update(bool visible) {
|
||||
}
|
||||
cleanup();
|
||||
|
||||
debugMinimap->setVisible(player.debug && showGeneratorMinimap);
|
||||
if (player.debug && showGeneratorMinimap) {
|
||||
debugMinimap->setVisible(debug && showGeneratorMinimap);
|
||||
if (debug && showGeneratorMinimap) {
|
||||
updateWorldGenDebugVisualization();
|
||||
}
|
||||
}
|
||||
@ -460,7 +460,7 @@ void Hud::showExchangeSlot() {
|
||||
exchangeSlot->setColor(glm::vec4());
|
||||
exchangeSlot->setInteractive(false);
|
||||
exchangeSlot->setZIndex(1);
|
||||
gui->store(SlotView::EXCHANGE_SLOT_NAME, exchangeSlot);
|
||||
gui.store(SlotView::EXCHANGE_SLOT_NAME, exchangeSlot);
|
||||
|
||||
}
|
||||
|
||||
@ -494,7 +494,7 @@ void Hud::openPermanent(UiDocument* doc) {
|
||||
|
||||
void Hud::dropExchangeSlot() {
|
||||
auto slotView = std::dynamic_pointer_cast<SlotView>(
|
||||
gui->get(SlotView::EXCHANGE_SLOT_NAME)
|
||||
gui.get(SlotView::EXCHANGE_SLOT_NAME)
|
||||
);
|
||||
if (slotView == nullptr) {
|
||||
return;
|
||||
@ -518,7 +518,7 @@ void Hud::dropExchangeSlot() {
|
||||
|
||||
void Hud::closeInventory() {
|
||||
dropExchangeSlot();
|
||||
gui->remove(SlotView::EXCHANGE_SLOT_NAME);
|
||||
gui.remove(SlotView::EXCHANGE_SLOT_NAME);
|
||||
exchangeSlot = nullptr;
|
||||
exchangeSlotInv = nullptr;
|
||||
inventoryOpen = false;
|
||||
@ -536,7 +536,7 @@ void Hud::closeInventory() {
|
||||
}
|
||||
|
||||
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
||||
gui->add(element.getNode());
|
||||
gui.add(element.getNode());
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||
@ -572,7 +572,7 @@ void Hud::onRemove(const HudElement& element) {
|
||||
invview->unbind();
|
||||
}
|
||||
}
|
||||
gui->remove(element.getNode());
|
||||
gui.remove(element.getNode());
|
||||
}
|
||||
|
||||
void Hud::remove(const std::shared_ptr<UINode>& node) {
|
||||
@ -585,6 +585,10 @@ void Hud::remove(const std::shared_ptr<UINode>& node) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::setDebug(bool flag) {
|
||||
debug = flag;
|
||||
}
|
||||
|
||||
void Hud::draw(const DrawContext& ctx){
|
||||
const Viewport& viewport = ctx.getViewport();
|
||||
const uint width = viewport.getWidth();
|
||||
@ -602,7 +606,7 @@ void Hud::draw(const DrawContext& ctx){
|
||||
uishader.uniformMatrix("u_projview", uicamera->getProjView());
|
||||
|
||||
// Crosshair
|
||||
if (!pause && !inventoryOpen && !player.debug) {
|
||||
if (!pause && !inventoryOpen && !debug) {
|
||||
DrawContext chctx = ctx.sub(batch);
|
||||
chctx.setBlendMode(BlendMode::inversion);
|
||||
auto texture = assets.get<Texture>("gui/crosshair");
|
||||
@ -681,7 +685,7 @@ void Hud::setPause(bool pause) {
|
||||
closeInventory();
|
||||
}
|
||||
|
||||
auto menu = gui->getMenu();
|
||||
const auto& menu = gui.getMenu();
|
||||
if (pause) {
|
||||
menu->setPage("pause");
|
||||
} else {
|
||||
@ -713,10 +717,10 @@ void Hud::setContentAccess(bool flag) {
|
||||
void Hud::setDebugCheats(bool flag) {
|
||||
allowDebugCheats = flag;
|
||||
|
||||
gui->remove(debugPanel);
|
||||
gui.remove(debugPanel);
|
||||
debugPanel = create_debug_panel(
|
||||
engine, frontend.getLevel(), player, allowDebugCheats
|
||||
);
|
||||
debugPanel->setZIndex(2);
|
||||
gui->add(debugPanel);
|
||||
gui.add(debugPanel);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ class Hud : public util::ObjectsKeeper {
|
||||
Engine& engine;
|
||||
Assets& assets;
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
gui::GUI* gui;
|
||||
gui::GUI& gui;
|
||||
LevelFrontend& frontend;
|
||||
Player& player;
|
||||
|
||||
@ -113,6 +113,7 @@ class Hud : public util::ObjectsKeeper {
|
||||
bool showContentPanel = true;
|
||||
/// @brief Provide cheat controllers to the debug panel
|
||||
bool allowDebugCheats = true;
|
||||
bool debug = false;
|
||||
/// @brief UI element will be dynamicly positioned near to inventory or in screen center
|
||||
std::shared_ptr<gui::UINode> secondUI;
|
||||
|
||||
@ -193,6 +194,8 @@ public:
|
||||
void onRemove(const HudElement& element);
|
||||
void remove(const std::shared_ptr<gui::UINode>& node);
|
||||
|
||||
void setDebug(bool flag);
|
||||
|
||||
Player* getPlayer() const;
|
||||
|
||||
std::shared_ptr<Inventory> getBlockInventory();
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
#include "graphics/render/WorldRenderer.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "graphics/ui/elements/Menu.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "frontend/ContentGfxCache.hpp"
|
||||
#include "logic/LevelController.hpp"
|
||||
#include "logic/PlayerController.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
@ -55,7 +57,7 @@ LevelScreen::LevelScreen(Engine& engine, std::unique_ptr<Level> levelPtr)
|
||||
);
|
||||
|
||||
frontend = std::make_unique<LevelFrontend>(
|
||||
player, controller.get(), assets
|
||||
player, controller.get(), assets, settings
|
||||
);
|
||||
worldRenderer = std::make_unique<WorldRenderer>(
|
||||
engine, *frontend, *player
|
||||
@ -70,6 +72,11 @@ LevelScreen::LevelScreen(Engine& engine, std::unique_ptr<Level> levelPtr)
|
||||
player->chunks->saveAndClear();
|
||||
worldRenderer->clear();
|
||||
}));
|
||||
keepAlive(settings.graphics.denseRender.observe([=](bool) {
|
||||
player->chunks->saveAndClear();
|
||||
worldRenderer->clear();
|
||||
frontend->getContentGfxCache().refresh();
|
||||
}));
|
||||
keepAlive(settings.camera.fov.observe([=](double value) {
|
||||
player->fpCamera->setFov(glm::radians(value));
|
||||
}));
|
||||
@ -150,7 +157,9 @@ void LevelScreen::updateHotkeys() {
|
||||
hudVisible = !hudVisible;
|
||||
}
|
||||
if (Events::jpressed(keycode::F3)) {
|
||||
player->debug = !player->debug;
|
||||
debug = !debug;
|
||||
hud->setDebug(debug);
|
||||
worldRenderer->setDebug(debug);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ class LevelScreen : public Screen {
|
||||
void saveWorldPreview();
|
||||
|
||||
bool hudVisible = true;
|
||||
bool debug = false;
|
||||
void updateHotkeys();
|
||||
void initializeContent();
|
||||
void initializePack(ContentPackRuntime* pack);
|
||||
|
||||
@ -25,7 +25,7 @@ GLTexture::GLTexture(const ubyte* data, uint width, uint height, ImageFormat ima
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -8,9 +8,6 @@
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "lighting/Lightmap.hpp"
|
||||
#include "frontend/ContentGfxCache.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
||||
|
||||
@ -342,41 +339,41 @@ void BlocksRenderer::blockCube(
|
||||
}
|
||||
|
||||
if (ao) {
|
||||
if (isOpen(coord + Z, group)) {
|
||||
if (isOpen(coord + Z, block)) {
|
||||
faceAO(coord, X, Y, Z, texfaces[5], lights);
|
||||
}
|
||||
if (isOpen(coord - Z, group)) {
|
||||
if (isOpen(coord - Z, block)) {
|
||||
faceAO(coord, -X, Y, -Z, texfaces[4], lights);
|
||||
}
|
||||
if (isOpen(coord + Y, group)) {
|
||||
if (isOpen(coord + Y, block)) {
|
||||
faceAO(coord, X, -Z, Y, texfaces[3], lights);
|
||||
}
|
||||
if (isOpen(coord - Y, group)) {
|
||||
if (isOpen(coord - Y, block)) {
|
||||
faceAO(coord, X, Z, -Y, texfaces[2], lights);
|
||||
}
|
||||
if (isOpen(coord + X, group)) {
|
||||
if (isOpen(coord + X, block)) {
|
||||
faceAO(coord, -Z, Y, X, texfaces[1], lights);
|
||||
}
|
||||
if (isOpen(coord - X, group)) {
|
||||
if (isOpen(coord - X, block)) {
|
||||
faceAO(coord, Z, Y, -X, texfaces[0], lights);
|
||||
}
|
||||
} else {
|
||||
if (isOpen(coord + Z, group)) {
|
||||
if (isOpen(coord + Z, block)) {
|
||||
face(coord, X, Y, Z, texfaces[5], pickLight(coord + Z), lights);
|
||||
}
|
||||
if (isOpen(coord - Z, group)) {
|
||||
if (isOpen(coord - Z, block)) {
|
||||
face(coord, -X, Y, -Z, texfaces[4], pickLight(coord - Z), lights);
|
||||
}
|
||||
if (isOpen(coord + Y, group)) {
|
||||
if (isOpen(coord + Y, block)) {
|
||||
face(coord, X, -Z, Y, texfaces[3], pickLight(coord + Y), lights);
|
||||
}
|
||||
if (isOpen(coord - Y, group)) {
|
||||
if (isOpen(coord - Y, block)) {
|
||||
face(coord, X, Z, -Y, texfaces[2], pickLight(coord - Y), lights);
|
||||
}
|
||||
if (isOpen(coord + X, group)) {
|
||||
if (isOpen(coord + X, block)) {
|
||||
face(coord, -Z, Y, X, texfaces[1], pickLight(coord + X), lights);
|
||||
}
|
||||
if (isOpen(coord - X, group)) {
|
||||
if (isOpen(coord - X, block)) {
|
||||
face(coord, Z, Y, -X, texfaces[0], pickLight(coord - X), lights);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "commons.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
class Content;
|
||||
class Mesh;
|
||||
@ -22,7 +23,6 @@ class Chunks;
|
||||
class VoxelsVolume;
|
||||
class Chunks;
|
||||
class ContentGfxCache;
|
||||
struct EngineSettings;
|
||||
struct UVRegion;
|
||||
|
||||
class BlocksRenderer {
|
||||
@ -118,7 +118,7 @@ class BlocksRenderer {
|
||||
bool isOpenForLight(int x, int y, int z) const;
|
||||
|
||||
// Does block allow to see other blocks sides (is it transparent)
|
||||
inline bool isOpen(const glm::ivec3& pos, ubyte group) const {
|
||||
inline bool isOpen(const glm::ivec3& pos, const Block& def) const {
|
||||
auto id = voxelsBuffer->pickBlockId(
|
||||
chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z
|
||||
);
|
||||
@ -126,7 +126,13 @@ class BlocksRenderer {
|
||||
return false;
|
||||
}
|
||||
const auto& block = *blockDefsCache[id];
|
||||
if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) {
|
||||
if (((block.drawGroup != def.drawGroup) && block.drawGroup) || !block.rt.solid) {
|
||||
return true;
|
||||
}
|
||||
if ((def.culling == CullingMode::DISABLED ||
|
||||
(def.culling == CullingMode::OPTIONAL &&
|
||||
settings.graphics.denseRender.get())) &&
|
||||
id == def.rt.id) {
|
||||
return true;
|
||||
}
|
||||
return !id;
|
||||
|
||||
@ -28,11 +28,18 @@ public:
|
||||
const Chunks& chunks,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
) : level(level),
|
||||
)
|
||||
: level(level),
|
||||
chunks(chunks),
|
||||
renderer(settings.graphics.chunkMaxVertices.get(),
|
||||
*level.content, cache, settings)
|
||||
{}
|
||||
renderer(
|
||||
settings.graphics.denseRender.get()
|
||||
? settings.graphics.chunkMaxVerticesDense.get()
|
||||
: settings.graphics.chunkMaxVertices.get(),
|
||||
*level.content,
|
||||
cache,
|
||||
settings
|
||||
) {
|
||||
}
|
||||
|
||||
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
|
||||
renderer.build(chunk.get(), &chunks);
|
||||
|
||||
@ -104,9 +104,8 @@ void Decorator::update(
|
||||
|
||||
void Decorator::update(float delta, const Camera& camera) {
|
||||
glm::ivec3 pos = camera.position;
|
||||
pos -= glm::ivec3(UPDATE_AREA_DIAMETER / 2);
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
update(delta, pos, camera.position);
|
||||
update(delta, pos - glm::ivec3(UPDATE_AREA_DIAMETER / 2), pos);
|
||||
}
|
||||
const auto& chunks = *player.chunks;
|
||||
const auto& indices = *level.content->getIndices();
|
||||
|
||||
@ -14,7 +14,7 @@ class Chunks;
|
||||
class Camera;
|
||||
class Assets;
|
||||
class Player;
|
||||
struct Block;
|
||||
class Block;
|
||||
class Engine;
|
||||
class LevelController;
|
||||
class WorldRenderer;
|
||||
|
||||
@ -19,12 +19,13 @@ Emitter::Emitter(
|
||||
)
|
||||
: level(level),
|
||||
origin(std::move(origin)),
|
||||
prototype({this, 0, glm::vec3(), preset.velocity, preset.lifetime, region}),
|
||||
prototype({this, 0, {}, preset.velocity, preset.lifetime, region}),
|
||||
texture(texture),
|
||||
count(count),
|
||||
preset(std::move(preset)) {
|
||||
random.setSeed(reinterpret_cast<ptrdiff_t>(this));
|
||||
this->prototype.emitter = this;
|
||||
timer = preset.spawnInterval;
|
||||
timer = preset.spawnInterval * random.randFloat();
|
||||
}
|
||||
|
||||
const Texture* Emitter::getTexture() const {
|
||||
@ -76,6 +77,10 @@ void Emitter::update(
|
||||
count = std::max(0, count - skipped);
|
||||
timer -= skipped * spawnInterval;
|
||||
}
|
||||
if (count < 0) {
|
||||
int skipped = timer / spawnInterval;
|
||||
timer -= skipped * spawnInterval;
|
||||
}
|
||||
return;
|
||||
}
|
||||
while (count && timer > spawnInterval) {
|
||||
@ -83,6 +88,15 @@ void Emitter::update(
|
||||
Particle particle = prototype;
|
||||
particle.emitter = this;
|
||||
particle.random = random.rand32();
|
||||
if (glm::abs(preset.angleSpread) >= 0.005f) {
|
||||
particle.angle =
|
||||
random.randFloat() * preset.angleSpread * glm::pi<float>() * 2;
|
||||
}
|
||||
particle.angularVelocity =
|
||||
(preset.minAngularVelocity +
|
||||
random.randFloat() *
|
||||
(preset.maxAngularVelocity - preset.minAngularVelocity)) *
|
||||
((random.rand() % 2) * 2 - 1);
|
||||
|
||||
glm::vec3 spawnOffset = generate_coord(preset.spawnShape);
|
||||
spawnOffset *= preset.spawnSpread;
|
||||
@ -103,6 +117,7 @@ void Emitter::update(
|
||||
if (count > 0) {
|
||||
count--;
|
||||
}
|
||||
refCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,6 +129,10 @@ bool Emitter::isDead() const {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
bool Emitter::isReferred() const {
|
||||
return refCount > 0;
|
||||
}
|
||||
|
||||
const EmitterOrigin& Emitter::getOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
@ -27,6 +27,10 @@ struct Particle {
|
||||
float lifetime;
|
||||
/// @brief UV region
|
||||
UVRegion region;
|
||||
/// @brief Current rotation angle
|
||||
float angle;
|
||||
/// @brief Angular velocity
|
||||
float angularVelocity;
|
||||
};
|
||||
|
||||
class Texture;
|
||||
@ -39,7 +43,7 @@ class Emitter {
|
||||
EmitterOrigin origin;
|
||||
/// @brief Particle prototype
|
||||
Particle prototype;
|
||||
/// @brief Particle
|
||||
/// @brief Particle texture
|
||||
const Texture* texture;
|
||||
/// @brief Number of particles should be spawned before emitter deactivation.
|
||||
/// -1 is infinite.
|
||||
@ -50,6 +54,9 @@ class Emitter {
|
||||
|
||||
util::PseudoRandom random;
|
||||
public:
|
||||
/// @brief Number of references (alive particles)
|
||||
int refCount = 0;
|
||||
/// @brief Particle settings
|
||||
ParticlesPreset preset;
|
||||
|
||||
Emitter(
|
||||
@ -82,6 +89,9 @@ public:
|
||||
/// @return true if the emitter has spawned all particles
|
||||
bool isDead() const;
|
||||
|
||||
/// @return true if there is at least one alive referring particle left
|
||||
bool isReferred() const;
|
||||
|
||||
const EmitterOrigin& getOrigin() const;
|
||||
|
||||
void setOrigin(const EmitterOrigin& origin);
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
struct ItemDef;
|
||||
class Assets;
|
||||
class Content;
|
||||
struct Block;
|
||||
class Block;
|
||||
|
||||
class ModelsGenerator {
|
||||
public:
|
||||
|
||||
@ -36,12 +36,14 @@ static inline void update_particle(
|
||||
const auto& preset = particle.emitter->preset;
|
||||
auto& pos = particle.position;
|
||||
auto& vel = particle.velocity;
|
||||
auto& angle = particle.angle;
|
||||
|
||||
vel += delta * preset.acceleration;
|
||||
if (preset.collision && chunks.isObstacleAt(pos + vel * delta)) {
|
||||
vel *= 0.0f;
|
||||
}
|
||||
pos += vel * delta;
|
||||
angle += particle.angularVelocity * delta;
|
||||
particle.lifetime -= delta;
|
||||
}
|
||||
|
||||
@ -65,7 +67,8 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
auto iter = vec.begin();
|
||||
while (iter != vec.end()) {
|
||||
auto& particle = *iter;
|
||||
auto& preset = particle.emitter->preset;
|
||||
auto& emitter = *particle.emitter;
|
||||
auto& preset = emitter.preset;
|
||||
|
||||
if (!preset.frames.empty()) {
|
||||
float time = preset.lifetime - particle.lifetime;
|
||||
@ -86,19 +89,52 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
}
|
||||
update_particle(particle, delta, chunks);
|
||||
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
|
||||
glm::vec4 light(1, 1, 1, 0);
|
||||
if (preset.lighting) {
|
||||
light = MainBatch::sampleLight(
|
||||
particle.position, chunks, backlight
|
||||
particle.position,
|
||||
chunks,
|
||||
backlight
|
||||
);
|
||||
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
light = glm::max(
|
||||
light,
|
||||
MainBatch::sampleLight(
|
||||
particle.position -
|
||||
size * glm::vec3(x, y, z),
|
||||
chunks,
|
||||
backlight
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||
}
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
|
||||
|
||||
glm::vec3 localRight = right;
|
||||
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
|
||||
float angle = particle.angle;
|
||||
if (glm::abs(angle) >= 0.005f) {
|
||||
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
|
||||
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
|
||||
|
||||
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
|
||||
camera.front * rotatedRight.z;
|
||||
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
|
||||
camera.front * rotatedUp.z;
|
||||
}
|
||||
batch->quad(
|
||||
particle.position,
|
||||
right,
|
||||
preset.globalUpVector ? glm::vec3(0, 1, 0) : up,
|
||||
localRight,
|
||||
localUp,
|
||||
preset.size * scale,
|
||||
light,
|
||||
glm::vec3(1.0f),
|
||||
@ -106,6 +142,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
);
|
||||
if (particle.lifetime <= 0.0f) {
|
||||
iter = vec.erase(iter);
|
||||
emitter.refCount--;
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
@ -128,19 +165,15 @@ void ParticlesRenderer::render(const Camera& camera, float delta) {
|
||||
auto iter = emitters.begin();
|
||||
while (iter != emitters.end()) {
|
||||
auto& emitter = *iter->second;
|
||||
auto texture = emitter.getTexture();
|
||||
const auto& found = particles.find(texture);
|
||||
std::vector<Particle>* vec;
|
||||
if (found == particles.end()) {
|
||||
if (emitter.isDead()) {
|
||||
if (emitter.isDead() && !emitter.isReferred()) {
|
||||
// destruct Emitter only when there is no particles spawned by it
|
||||
iter = emitters.erase(iter);
|
||||
continue;
|
||||
}
|
||||
auto texture = emitter.getTexture();
|
||||
const auto& found = particles.find(texture);
|
||||
std::vector<Particle>* vec;
|
||||
vec = &particles[texture];
|
||||
} else {
|
||||
vec = &found->second;
|
||||
}
|
||||
emitter.update(delta, camera.position, *vec);
|
||||
iter++;
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ void WorldRenderer::renderBlockSelection() {
|
||||
lineBatch->box(
|
||||
center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 0.5f)
|
||||
);
|
||||
if (player.debug) {
|
||||
if (debug) {
|
||||
lineBatch->line(
|
||||
point, point + norm * 0.5f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f)
|
||||
);
|
||||
@ -228,7 +228,7 @@ void WorldRenderer::renderLines(
|
||||
if (player.selection.vox.id != BLOCK_VOID) {
|
||||
renderBlockSelection();
|
||||
}
|
||||
if (player.debug && showEntitiesDebug) {
|
||||
if (debug && showEntitiesDebug) {
|
||||
auto ctx = pctx.sub(lineBatch.get());
|
||||
bool culling = engine.getSettings().graphics.frustumCulling.get();
|
||||
level.entities->renderDebug(
|
||||
@ -337,7 +337,7 @@ void WorldRenderer::draw(
|
||||
renderLevel(ctx, camera, settings, delta, pause, hudVisible);
|
||||
// Debug lines
|
||||
if (hudVisible) {
|
||||
if (player.debug) {
|
||||
if (debug) {
|
||||
guides->renderDebugLines(
|
||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||
);
|
||||
@ -410,3 +410,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||
void WorldRenderer::clear() {
|
||||
chunks->clear();
|
||||
}
|
||||
|
||||
void WorldRenderer::setDebug(bool flag) {
|
||||
debug = flag;
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ class WorldRenderer {
|
||||
std::unique_ptr<ModelBatch> modelBatch;
|
||||
|
||||
float timer = 0.0f;
|
||||
bool debug = false;
|
||||
|
||||
/// @brief Render block selection lines
|
||||
void renderBlockSelection();
|
||||
@ -100,4 +101,6 @@ public:
|
||||
);
|
||||
|
||||
void clear();
|
||||
|
||||
void setDebug(bool flag);
|
||||
};
|
||||
|
||||
@ -23,10 +23,13 @@ size_t Inventory::findEmptySlot(size_t begin, size_t end) const {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t Inventory::findSlotByItem(itemid_t id, size_t begin, size_t end) {
|
||||
size_t Inventory::findSlotByItem(
|
||||
itemid_t id, size_t begin, size_t end, size_t minCount
|
||||
) {
|
||||
end = std::min(slots.size(), end);
|
||||
for (size_t i = begin; i < end; i++) {
|
||||
if (slots[i].getItemId() == id) {
|
||||
const auto& stack = slots[i];
|
||||
if (stack.getItemId() == id && stack.getCount() >= minCount) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@ public:
|
||||
|
||||
ItemStack& getSlot(size_t index);
|
||||
size_t findEmptySlot(size_t begin = 0, size_t end = -1) const;
|
||||
size_t findSlotByItem(itemid_t id, size_t begin = 0, size_t end = -1);
|
||||
size_t findSlotByItem(
|
||||
itemid_t id, size_t begin = 0, size_t end = -1, size_t minCount = 1
|
||||
);
|
||||
|
||||
inline size_t size() const {
|
||||
return slots.size();
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include "data/dv.hpp"
|
||||
#include "typedefs.hpp"
|
||||
|
||||
struct item_funcs_set {
|
||||
struct ItemFuncsSet {
|
||||
bool init : 1;
|
||||
bool on_use : 1;
|
||||
bool on_use_on_block : 1;
|
||||
@ -43,7 +43,7 @@ struct ItemDef {
|
||||
struct {
|
||||
itemid_t id;
|
||||
blockid_t placingBlock;
|
||||
item_funcs_set funcsset {};
|
||||
ItemFuncsSet funcsset {};
|
||||
bool emissive = false;
|
||||
} rt {};
|
||||
|
||||
|
||||
@ -38,7 +38,10 @@ void ChunksController::update(
|
||||
int centerX = floordiv<CHUNK_W>(position.x);
|
||||
int centerY = floordiv<CHUNK_D>(position.z);
|
||||
|
||||
if (player.isLoadingChunks()) {
|
||||
/// FIXME: one generator for multiple players
|
||||
generator->update(centerX, centerY, loadDistance);
|
||||
}
|
||||
|
||||
int64_t mcstotal = 0;
|
||||
|
||||
@ -121,6 +124,12 @@ bool ChunksController::buildLights(const Player& player, const std::shared_ptr<C
|
||||
}
|
||||
|
||||
void ChunksController::createChunk(const Player& player, int x, int z) const {
|
||||
if (!player.isLoadingChunks()) {
|
||||
if (auto chunk = level.chunks->fetch(x, z)) {
|
||||
player.chunks->putChunk(chunk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto chunk = level.chunks->create(x, z);
|
||||
player.chunks->putChunk(chunk);
|
||||
auto& chunkFlags = chunk->flags;
|
||||
|
||||
@ -47,9 +47,7 @@ LevelController::LevelController(
|
||||
);
|
||||
chunks->update(16, 1, 0, *player);
|
||||
if (player->chunks->get(
|
||||
std::floor(position.x),
|
||||
std::floor(position.y),
|
||||
std::floor(position.z)
|
||||
std::floor(position.x), 0, std::floor(position.z)
|
||||
)) {
|
||||
confirmed++;
|
||||
}
|
||||
@ -83,7 +81,7 @@ void LevelController::update(float delta, bool pause) {
|
||||
playerTickClock.getPart()) {
|
||||
|
||||
const auto& position = player->getPosition();
|
||||
if (!player->chunks->get(
|
||||
if (player->chunks->get(
|
||||
std::floor(position.x),
|
||||
std::floor(position.y),
|
||||
std::floor(position.z)
|
||||
|
||||
@ -288,8 +288,6 @@ void PlayerController::updateKeyboard() {
|
||||
input.jump = Events::active(BIND_MOVE_JUMP);
|
||||
input.zoom = Events::active(BIND_CAM_ZOOM);
|
||||
input.cameraMode = Events::jactive(BIND_CAM_MODE);
|
||||
input.noclip = Events::jactive(BIND_PLAYER_NOCLIP);
|
||||
input.flight = Events::jactive(BIND_PLAYER_FLIGHT);
|
||||
}
|
||||
|
||||
void PlayerController::resetKeyboard() {
|
||||
@ -341,28 +339,6 @@ static int determine_rotation(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pick_block(
|
||||
ContentIndices* indices,
|
||||
const Block& block,
|
||||
Player& player,
|
||||
int x,
|
||||
int y,
|
||||
int z
|
||||
) {
|
||||
itemid_t id = block.rt.pickingItem;
|
||||
auto inventory = player.getInventory();
|
||||
size_t slotid = inventory->findSlotByItem(id, 0, 10);
|
||||
if (slotid == Inventory::npos) {
|
||||
slotid = player.getChosenSlot();
|
||||
} else {
|
||||
player.setChosenSlot(slotid);
|
||||
}
|
||||
ItemStack& stack = inventory->getSlot(slotid);
|
||||
if (stack.getItemId() != id) {
|
||||
stack.set(ItemStack(id, 1));
|
||||
}
|
||||
}
|
||||
|
||||
voxel* PlayerController::updateSelection(float maxDistance) {
|
||||
auto indices = level.content->getIndices();
|
||||
auto& chunks = *player.chunks;
|
||||
@ -544,6 +520,7 @@ void PlayerController::updateInteraction(float delta) {
|
||||
}
|
||||
auto& target = indices->blocks.require(vox->id);
|
||||
if (lclick) {
|
||||
scripting::on_block_breaking(&player, target, iend);
|
||||
if (player.isInstantDestruction() && target.breakable) {
|
||||
blocksController.breakBlock(
|
||||
&player, target, iend.x, iend.y, iend.z
|
||||
@ -567,10 +544,6 @@ void PlayerController::updateInteraction(float delta) {
|
||||
if (def && rclick) {
|
||||
processRightClick(*def, target);
|
||||
}
|
||||
if (Events::jactive(BIND_PLAYER_PICK)) {
|
||||
auto coord = selection.actualPosition;
|
||||
pick_block(indices, target, player, coord.x, coord.y, coord.z);
|
||||
}
|
||||
}
|
||||
|
||||
Player* PlayerController::getPlayer() {
|
||||
|
||||
@ -57,12 +57,7 @@ static fs::path get_writeable_path(lua::State* L) {
|
||||
fs::path path = resolve_path(rawpath);
|
||||
auto entryPoint = rawpath.substr(0, rawpath.find(':'));
|
||||
if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) {
|
||||
if (lua::getglobal(L, "__vc_warning")) {
|
||||
lua::pushstring(L, "writing to read-only entry point");
|
||||
lua::pushstring(L, entryPoint);
|
||||
lua::pushinteger(L, 1);
|
||||
lua::call_nothrow(L, 3);
|
||||
}
|
||||
throw std::runtime_error("access denied");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@ -149,27 +144,6 @@ static int l_read_bytes(lua::State* L) {
|
||||
);
|
||||
}
|
||||
|
||||
static void read_bytes_from_table(
|
||||
lua::State* L, int tableIndex, std::vector<ubyte>& bytes
|
||||
) {
|
||||
if (!lua::istable(L, tableIndex)) {
|
||||
throw std::runtime_error("table expected");
|
||||
} else {
|
||||
size_t size = lua::objlen(L, tableIndex);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
lua::rawgeti(L, i + 1, tableIndex);
|
||||
const int byte = lua::tointeger(L, -1);
|
||||
lua::pop(L);
|
||||
if (byte < 0 || byte > 255) {
|
||||
throw std::runtime_error(
|
||||
"invalid byte '" + std::to_string(byte) + "'"
|
||||
);
|
||||
}
|
||||
bytes.push_back(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int l_write_bytes(lua::State* L) {
|
||||
fs::path path = get_writeable_path(L);
|
||||
|
||||
@ -181,7 +155,7 @@ static int l_write_bytes(lua::State* L) {
|
||||
}
|
||||
|
||||
std::vector<ubyte> bytes;
|
||||
read_bytes_from_table(L, 2, bytes);
|
||||
lua::read_bytes_from_table(L, 2, bytes);
|
||||
return lua::pushboolean(
|
||||
L, files::write_bytes(path, bytes.data(), bytes.size())
|
||||
);
|
||||
@ -223,7 +197,7 @@ static int l_list(lua::State* L) {
|
||||
static int l_gzip_compress(lua::State* L) {
|
||||
std::vector<ubyte> bytes;
|
||||
|
||||
read_bytes_from_table(L, 1, bytes);
|
||||
lua::read_bytes_from_table(L, 1, bytes);
|
||||
auto compressed_bytes = gzip::compress(bytes.data(), bytes.size());
|
||||
int newTable = lua::gettop(L);
|
||||
|
||||
@ -237,7 +211,7 @@ static int l_gzip_compress(lua::State* L) {
|
||||
static int l_gzip_decompress(lua::State* L) {
|
||||
std::vector<ubyte> bytes;
|
||||
|
||||
read_bytes_from_table(L, 1, bytes);
|
||||
lua::read_bytes_from_table(L, 1, bytes);
|
||||
auto decompressed_bytes = gzip::decompress(bytes.data(), bytes.size());
|
||||
int newTable = lua::gettop(L);
|
||||
|
||||
@ -264,6 +238,16 @@ static int l_read_combined_object(lua::State* L) {
|
||||
return lua::pushvalue(L, engine->getResPaths()->readCombinedObject(path));
|
||||
}
|
||||
|
||||
static int l_is_writeable(lua::State* L) {
|
||||
std::string rawpath = lua::require_string(L, 1);
|
||||
fs::path path = resolve_path(rawpath);
|
||||
auto entryPoint = rawpath.substr(0, rawpath.find(':'));
|
||||
if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) {
|
||||
return lua::pushboolean(L, false);
|
||||
}
|
||||
return lua::pushboolean(L, true);
|
||||
}
|
||||
|
||||
const luaL_Reg filelib[] = {
|
||||
{"exists", lua::wrap<l_exists>},
|
||||
{"find", lua::wrap<l_find>},
|
||||
@ -284,4 +268,5 @@ const luaL_Reg filelib[] = {
|
||||
{"gzip_decompress", lua::wrap<l_gzip_decompress>},
|
||||
{"read_combined_list", lua::wrap<l_read_combined_list>},
|
||||
{"read_combined_object", lua::wrap<l_read_combined_object>},
|
||||
{"is_writeable", lua::wrap<l_is_writeable>},
|
||||
{NULL, NULL}};
|
||||
|
||||
@ -13,15 +13,15 @@ static void validate_itemid(itemid_t id) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::shared_ptr<Inventory> get_inventory(int64_t id) {
|
||||
static Inventory& get_inventory(int64_t id) {
|
||||
auto inv = level->inventories->get(id);
|
||||
if (inv == nullptr) {
|
||||
throw std::runtime_error("inventory not found: " + std::to_string(id));
|
||||
}
|
||||
return inv;
|
||||
return *inv;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Inventory> get_inventory(int64_t id, int arg) {
|
||||
static Inventory& get_inventory(int64_t id, int arg) {
|
||||
auto inv = level->inventories->get(id);
|
||||
if (inv == nullptr) {
|
||||
throw std::runtime_error(
|
||||
@ -29,62 +29,62 @@ static std::shared_ptr<Inventory> get_inventory(int64_t id, int arg) {
|
||||
std::to_string(arg)
|
||||
);
|
||||
}
|
||||
return inv;
|
||||
return *inv;
|
||||
}
|
||||
|
||||
static void validate_slotid(int slotid, Inventory* inv) {
|
||||
if (static_cast<size_t>(slotid) >= inv->size()) {
|
||||
static void validate_slotid(int slotid, const Inventory& inv) {
|
||||
if (static_cast<size_t>(slotid) >= inv.size()) {
|
||||
throw std::runtime_error(
|
||||
"slot index is out of range [0..inventory.size(invid)]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_inventory_get(lua::State* L) {
|
||||
static int l_get(lua::State* L) {
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
auto slotid = lua::tointeger(L, 2);
|
||||
auto inv = get_inventory(invid);
|
||||
validate_slotid(slotid, inv.get());
|
||||
const ItemStack& item = inv->getSlot(slotid);
|
||||
validate_slotid(slotid, inv);
|
||||
const ItemStack& item = inv.getSlot(slotid);
|
||||
lua::pushinteger(L, item.getItemId());
|
||||
lua::pushinteger(L, item.getCount());
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int l_inventory_set(lua::State* L) {
|
||||
static int l_set(lua::State* L) {
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
auto slotid = lua::tointeger(L, 2);
|
||||
auto itemid = lua::tointeger(L, 3);
|
||||
auto count = lua::tointeger(L, 4);
|
||||
validate_itemid(itemid);
|
||||
|
||||
auto inv = get_inventory(invid);
|
||||
auto& inv = get_inventory(invid);
|
||||
|
||||
validate_slotid(slotid, inv.get());
|
||||
ItemStack& item = inv->getSlot(slotid);
|
||||
validate_slotid(slotid, inv);
|
||||
ItemStack& item = inv.getSlot(slotid);
|
||||
item.set(ItemStack(itemid, count));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_inventory_size(lua::State* L) {
|
||||
static int l_size(lua::State* L) {
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
auto inv = get_inventory(invid);
|
||||
return lua::pushinteger(L, inv->size());
|
||||
auto& inv = get_inventory(invid);
|
||||
return lua::pushinteger(L, inv.size());
|
||||
}
|
||||
|
||||
static int l_inventory_add(lua::State* L) {
|
||||
static int l_add(lua::State* L) {
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
auto itemid = lua::tointeger(L, 2);
|
||||
auto count = lua::tointeger(L, 3);
|
||||
validate_itemid(itemid);
|
||||
|
||||
auto inv = get_inventory(invid);
|
||||
auto& inv = get_inventory(invid);
|
||||
ItemStack item(itemid, count);
|
||||
inv->move(item, indices);
|
||||
inv.move(item, indices);
|
||||
return lua::pushinteger(L, item.getCount());
|
||||
}
|
||||
|
||||
static int l_inventory_get_block(lua::State* L) {
|
||||
static int l_get_block(lua::State* L) {
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
@ -92,7 +92,7 @@ static int l_inventory_get_block(lua::State* L) {
|
||||
return lua::pushinteger(L, id);
|
||||
}
|
||||
|
||||
static int l_inventory_bind_block(lua::State* L) {
|
||||
static int l_bind_block(lua::State* L) {
|
||||
auto id = lua::tointeger(L, 1);
|
||||
auto x = lua::tointeger(L, 2);
|
||||
auto y = lua::tointeger(L, 3);
|
||||
@ -101,7 +101,7 @@ static int l_inventory_bind_block(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_inventory_unbind_block(lua::State* L) {
|
||||
static int l_unbind_block(lua::State* L) {
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
@ -109,7 +109,7 @@ static int l_inventory_unbind_block(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_inventory_create(lua::State* L) {
|
||||
static int l_create(lua::State* L) {
|
||||
auto invsize = lua::tointeger(L, 1);
|
||||
auto inv = level->inventories->create(invsize);
|
||||
if (inv == nullptr) {
|
||||
@ -118,18 +118,13 @@ static int l_inventory_create(lua::State* L) {
|
||||
return lua::pushinteger(L, inv->getId());
|
||||
}
|
||||
|
||||
static int l_inventory_remove(lua::State* L) {
|
||||
static int l_remove(lua::State* L) {
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
auto inv = get_inventory(invid);
|
||||
if (inv == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
level->inventories->remove(invid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_inventory_clone(lua::State* L) {
|
||||
static int l_clone(lua::State* L) {
|
||||
auto id = lua::tointeger(L, 1);
|
||||
auto clone = level->inventories->clone(id);
|
||||
if (clone == nullptr) {
|
||||
@ -138,54 +133,69 @@ static int l_inventory_clone(lua::State* L) {
|
||||
return lua::pushinteger(L, clone->getId());
|
||||
}
|
||||
|
||||
static int l_inventory_move(lua::State* L) {
|
||||
static int l_move(lua::State* L) {
|
||||
auto invAid = lua::tointeger(L, 1);
|
||||
auto slotAid = lua::tointeger(L, 2);
|
||||
auto invA = get_inventory(invAid, 1);
|
||||
validate_slotid(slotAid, invA.get());
|
||||
auto& invA = get_inventory(invAid, 1);
|
||||
validate_slotid(slotAid, invA);
|
||||
|
||||
auto invBid = lua::tointeger(L, 3);
|
||||
auto slotBid = lua::isnil(L, 4) ? -1 : lua::tointeger(L, 4);
|
||||
auto invB = get_inventory(invBid, 3);
|
||||
auto& slot = invA->getSlot(slotAid);
|
||||
auto& invB = get_inventory(invBid, 3);
|
||||
auto& slot = invA.getSlot(slotAid);
|
||||
if (slotBid == -1) {
|
||||
invB->move(slot, content->getIndices());
|
||||
invB.move(slot, content->getIndices());
|
||||
} else {
|
||||
invB->move(slot, content->getIndices(), slotBid, slotBid + 1);
|
||||
invB.move(slot, content->getIndices(), slotBid, slotBid + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_inventory_move_range(lua::State* L) {
|
||||
static int l_move_range(lua::State* L) {
|
||||
auto invAid = lua::tointeger(L, 1);
|
||||
auto slotAid = lua::tointeger(L, 2);
|
||||
auto invA = get_inventory(invAid, 1);
|
||||
validate_slotid(slotAid, invA.get());
|
||||
auto& invA = get_inventory(invAid, 1);
|
||||
validate_slotid(slotAid, invA);
|
||||
|
||||
auto invBid = lua::tointeger(L, 3);
|
||||
auto slotBegin = lua::isnoneornil(L, 4) ? -1 : lua::tointeger(L, 4);
|
||||
auto slotEnd = lua::isnoneornil(L, 5) ? -1 : lua::tointeger(L, 5) + 1;
|
||||
auto invB = get_inventory(invBid, 3);
|
||||
auto& slot = invA->getSlot(slotAid);
|
||||
auto& slot = invA.getSlot(slotAid);
|
||||
if (slotBegin == -1) {
|
||||
invB->move(slot, content->getIndices());
|
||||
invB.move(slot, content->getIndices());
|
||||
} else {
|
||||
invB->move(slot, content->getIndices(), slotBegin, slotEnd);
|
||||
invB.move(slot, content->getIndices(), slotBegin, slotEnd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_find_by_item(lua::State* L) {
|
||||
auto invId = lua::tointeger(L, 1);
|
||||
auto& inv = get_inventory(invId, 1);
|
||||
integer_t blockid = lua::tointeger(L, 2);
|
||||
integer_t begin = lua::isnumber(L, 3) ? lua::tointeger(L, 3) : 0;
|
||||
integer_t end = lua::isnumber(L, 4) ? lua::tointeger(L, 4) : -1;
|
||||
integer_t minCount = lua::isnumber(L, 5) ? lua::tointeger(L, 5) : blockid != 0;
|
||||
size_t index = inv.findSlotByItem(blockid, begin, end, minCount);
|
||||
if (index == Inventory::npos) {
|
||||
return 0;
|
||||
}
|
||||
return lua::pushinteger(L, index);
|
||||
}
|
||||
|
||||
const luaL_Reg inventorylib[] = {
|
||||
{"get", lua::wrap<l_inventory_get>},
|
||||
{"set", lua::wrap<l_inventory_set>},
|
||||
{"size", lua::wrap<l_inventory_size>},
|
||||
{"add", lua::wrap<l_inventory_add>},
|
||||
{"move", lua::wrap<l_inventory_move>},
|
||||
{"move_range", lua::wrap<l_inventory_move_range>},
|
||||
{"get_block", lua::wrap<l_inventory_get_block>},
|
||||
{"bind_block", lua::wrap<l_inventory_bind_block>},
|
||||
{"unbind_block", lua::wrap<l_inventory_unbind_block>},
|
||||
{"create", lua::wrap<l_inventory_create>},
|
||||
{"remove", lua::wrap<l_inventory_remove>},
|
||||
{"clone", lua::wrap<l_inventory_clone>},
|
||||
{"get", lua::wrap<l_get>},
|
||||
{"set", lua::wrap<l_set>},
|
||||
{"size", lua::wrap<l_size>},
|
||||
{"add", lua::wrap<l_add>},
|
||||
{"move", lua::wrap<l_move>},
|
||||
{"move_range", lua::wrap<l_move_range>},
|
||||
{"find_by_item", lua::wrap<l_find_by_item>},
|
||||
{"get_block", lua::wrap<l_get_block>},
|
||||
{"bind_block", lua::wrap<l_bind_block>},
|
||||
{"unbind_block", lua::wrap<l_unbind_block>},
|
||||
{"create", lua::wrap<l_create>},
|
||||
{"remove", lua::wrap<l_remove>},
|
||||
{"clone", lua::wrap<l_clone>},
|
||||
{NULL, NULL}};
|
||||
|
||||
@ -101,6 +101,13 @@ static int l_get_inv(lua::State* L) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int l_set_selected_slot(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
player->setChosenSlot(lua::tointeger(L, 2) % 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_flight(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
return lua::pushboolean(L, player->isFlight());
|
||||
@ -157,6 +164,20 @@ static int l_set_instant_destruction(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_loading_chunks(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
return lua::pushboolean(L, player->isLoadingChunks());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_loading_chunks(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
player->setLoadingChunks(lua::toboolean(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_selected_block(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
if (player->selection.vox.id == BLOCK_VOID) {
|
||||
@ -275,6 +296,9 @@ const luaL_Reg playerlib[] = {
|
||||
{"set_infinite_items", lua::wrap<l_set_infinite_items>},
|
||||
{"is_instant_destruction", lua::wrap<l_is_instant_destruction>},
|
||||
{"set_instant_destruction", lua::wrap<l_set_instant_destruction>},
|
||||
{"is_loading_chunks", lua::wrap<l_is_loading_chunks>},
|
||||
{"set_loading_chunks", lua::wrap<l_set_loading_chunks>},
|
||||
{"set_selected_slot", lua::wrap<l_set_selected_slot>},
|
||||
{"get_selected_block", lua::wrap<l_get_selected_block>},
|
||||
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
|
||||
{"set_spawnpoint", lua::wrap<l_set_spawnpoint>},
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "data/dv.hpp"
|
||||
@ -698,4 +699,25 @@ namespace lua {
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
inline void read_bytes_from_table(
|
||||
lua::State* L, int tableIndex, std::vector<ubyte>& bytes
|
||||
) {
|
||||
if (!lua::istable(L, tableIndex)) {
|
||||
throw std::runtime_error("table expected");
|
||||
} else {
|
||||
size_t size = lua::objlen(L, tableIndex);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
lua::rawgeti(L, i + 1, tableIndex);
|
||||
const int byte = lua::tointeger(L, -1);
|
||||
lua::pop(L);
|
||||
if (byte < 0 || byte > 255) {
|
||||
throw std::runtime_error(
|
||||
"invalid byte '" + std::to_string(byte) + "'"
|
||||
);
|
||||
}
|
||||
bytes.push_back(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "util/listutil.hpp"
|
||||
#include "../lua_util.hpp"
|
||||
|
||||
using namespace lua;
|
||||
@ -18,8 +19,16 @@ LuaBytearray::~LuaBytearray() {
|
||||
|
||||
static int l_append(lua::State* L) {
|
||||
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
|
||||
if (lua::isnumber(L, 2)) {
|
||||
auto value = tointeger(L, 2);
|
||||
buffer->data().push_back(static_cast<ubyte>(value));
|
||||
} else if (lua::istable(L, 2)) {
|
||||
lua::read_bytes_from_table(L, 2, buffer->data());
|
||||
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 2)) {
|
||||
util::concat(buffer->data(), extension->data());
|
||||
} else {
|
||||
throw std::runtime_error("integer/table/Bytearray expected");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -34,8 +43,19 @@ static int l_insert(lua::State* L) {
|
||||
if (static_cast<size_t>(index) > data.size()) {
|
||||
return 0;
|
||||
}
|
||||
if (lua::isnumber(L, 3)) {
|
||||
auto value = tointeger(L, 3);
|
||||
data.insert(data.begin() + index, static_cast<ubyte>(value));
|
||||
} else if (lua::istable(L, 3)) {
|
||||
std::vector<ubyte> temp;
|
||||
lua::read_bytes_from_table(L, 3, temp);
|
||||
data.insert(data.begin() + index, temp.begin(), temp.end());
|
||||
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 3)) {
|
||||
const std::vector<ubyte>& src = extension->data();
|
||||
data.insert(data.begin() + index, src.begin(), src.end());
|
||||
} else {
|
||||
throw std::runtime_error("integer/table/Bytearray expected");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -336,14 +336,22 @@ void scripting::random_update_block(const Block& block, const glm::ivec3& pos) {
|
||||
});
|
||||
}
|
||||
|
||||
void scripting::on_block_placed(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
/// TODO: replace template with index
|
||||
template<bool WorldFuncsSet::*worldfunc>
|
||||
static bool on_block_common(
|
||||
const std::string& suffix,
|
||||
bool blockfunc,
|
||||
Player* player,
|
||||
const Block& block,
|
||||
const glm::ivec3& pos
|
||||
) {
|
||||
if (block.rt.funcsset.onplaced) {
|
||||
std::string name = block.name + ".placed";
|
||||
bool result = false;
|
||||
if (blockfunc) {
|
||||
std::string name = block.name + "." + suffix;
|
||||
result =
|
||||
lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) {
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
lua::pushinteger(L, player->getId());
|
||||
return 4;
|
||||
});
|
||||
}
|
||||
@ -354,93 +362,53 @@ void scripting::on_block_placed(
|
||||
return 5;
|
||||
};
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
if (pack->worldfuncsset.onblockplaced) {
|
||||
if (pack->worldfuncsset.*worldfunc) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(), packid + ":.blockplaced", args
|
||||
lua::get_main_state(), packid + ":.block" + suffix, args
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void scripting::on_block_placed(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
) {
|
||||
on_block_common<&WorldFuncsSet::onblockplaced>(
|
||||
"placed", block.rt.funcsset.onplaced, player, block, pos
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_block_replaced(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
) {
|
||||
if (block.rt.funcsset.onreplaced) {
|
||||
std::string name = block.name + ".replaced";
|
||||
lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) {
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
return 4;
|
||||
});
|
||||
}
|
||||
auto args = [&](lua::State* L) {
|
||||
lua::pushinteger(L, block.rt.id);
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
return 5;
|
||||
};
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
if (pack->worldfuncsset.onblockreplaced) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(), packid + ":.blockreplaced", args
|
||||
on_block_common<&WorldFuncsSet::onblockreplaced>(
|
||||
"replaced", block.rt.funcsset.onreplaced, player, block, pos
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_block_breaking(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
) {
|
||||
on_block_common<&WorldFuncsSet::onblockbreaking>(
|
||||
"breaking", block.rt.funcsset.onbreaking, player, block, pos
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_block_broken(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
) {
|
||||
if (block.rt.funcsset.onbroken) {
|
||||
std::string name = block.name + ".broken";
|
||||
lua::emit_event(
|
||||
lua::get_main_state(),
|
||||
name,
|
||||
[pos, player](auto L) {
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
return 4;
|
||||
}
|
||||
on_block_common<&WorldFuncsSet::onblockbroken>(
|
||||
"broken", block.rt.funcsset.onbroken, player, block, pos
|
||||
);
|
||||
}
|
||||
auto args = [&](lua::State* L) {
|
||||
lua::pushinteger(L, block.rt.id);
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
return 5;
|
||||
};
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
if (pack->worldfuncsset.onblockbroken) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(), packid + ":.blockbroken", args
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool scripting::on_block_interact(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
) {
|
||||
std::string name = block.name + ".interact";
|
||||
auto result = lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) {
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player->getId());
|
||||
return 4;
|
||||
});
|
||||
auto args = [&](lua::State* L) {
|
||||
lua::pushinteger(L, block.rt.id);
|
||||
lua::pushivec_stack(L, pos);
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
return 5;
|
||||
};
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
if (pack->worldfuncsset.onblockinteract) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(), packid + ":.blockinteract", args
|
||||
return on_block_common<&WorldFuncsSet::onblockinteract>(
|
||||
"interact", block.rt.funcsset.oninteract, player, block, pos
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void scripting::on_player_tick(Player* player, int tps) {
|
||||
@ -599,7 +567,7 @@ static void process_entity_callback(
|
||||
static void process_entity_callback(
|
||||
const Entity& entity,
|
||||
const std::string& name,
|
||||
bool entity_funcs_set::*flag,
|
||||
bool EntityFuncsSet::*flag,
|
||||
std::function<int(lua::State*)> args
|
||||
) {
|
||||
const auto& script = entity.getScripting();
|
||||
@ -612,7 +580,7 @@ static void process_entity_callback(
|
||||
|
||||
void scripting::on_entity_despawn(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_despawn", &entity_funcs_set::on_despawn, nullptr
|
||||
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
|
||||
);
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, "stdcomp", "remove_Entity", true);
|
||||
@ -624,20 +592,20 @@ void scripting::on_entity_grounded(const Entity& entity, float force) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_grounded",
|
||||
&entity_funcs_set::on_grounded,
|
||||
&EntityFuncsSet::on_grounded,
|
||||
[force](auto L) { return lua::pushnumber(L, force); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_fall(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_fall", &entity_funcs_set::on_fall, nullptr
|
||||
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_save(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_save", &entity_funcs_set::on_save, nullptr
|
||||
entity, "on_save", &EntityFuncsSet::on_save, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
@ -647,7 +615,7 @@ void scripting::on_sensor_enter(
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_sensor_enter",
|
||||
&entity_funcs_set::on_sensor_enter,
|
||||
&EntityFuncsSet::on_sensor_enter,
|
||||
[index, oid](auto L) {
|
||||
lua::pushinteger(L, index);
|
||||
lua::pushinteger(L, oid);
|
||||
@ -662,7 +630,7 @@ void scripting::on_sensor_exit(
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_sensor_exit",
|
||||
&entity_funcs_set::on_sensor_exit,
|
||||
&EntityFuncsSet::on_sensor_exit,
|
||||
[index, oid](auto L) {
|
||||
lua::pushinteger(L, index);
|
||||
lua::pushinteger(L, oid);
|
||||
@ -675,7 +643,7 @@ void scripting::on_aim_on(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_aim_on",
|
||||
&entity_funcs_set::on_aim_on,
|
||||
&EntityFuncsSet::on_aim_on,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
@ -684,7 +652,7 @@ void scripting::on_aim_off(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_aim_off",
|
||||
&entity_funcs_set::on_aim_off,
|
||||
&EntityFuncsSet::on_aim_off,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
@ -695,7 +663,7 @@ void scripting::on_attacked(
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_attacked",
|
||||
&entity_funcs_set::on_attacked,
|
||||
&EntityFuncsSet::on_attacked,
|
||||
[player, attacker](auto L) {
|
||||
lua::pushinteger(L, attacker);
|
||||
lua::pushinteger(L, player->getId());
|
||||
@ -708,7 +676,7 @@ void scripting::on_entity_used(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_used",
|
||||
&entity_funcs_set::on_used,
|
||||
&EntityFuncsSet::on_used,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
@ -796,7 +764,7 @@ void scripting::load_content_script(
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
BlockFuncsSet& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "block", file, fileName));
|
||||
@ -804,6 +772,8 @@ void scripting::load_content_script(
|
||||
funcsset.update = register_event(env, "on_update", prefix + ".update");
|
||||
funcsset.randupdate =
|
||||
register_event(env, "on_random_update", prefix + ".randupdate");
|
||||
funcsset.onbreaking =
|
||||
register_event(env, "on_breaking", prefix + ".breaking");
|
||||
funcsset.onbroken = register_event(env, "on_broken", prefix + ".broken");
|
||||
funcsset.onplaced = register_event(env, "on_placed", prefix + ".placed");
|
||||
funcsset.onreplaced =
|
||||
@ -819,7 +789,7 @@ void scripting::load_content_script(
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
ItemFuncsSet& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "item", file, fileName));
|
||||
@ -846,7 +816,7 @@ void scripting::load_world_script(
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
WorldFuncsSet& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "world", file, fileName));
|
||||
@ -857,6 +827,8 @@ void scripting::load_world_script(
|
||||
register_event(env, "on_world_quit", prefix + ":.worldquit");
|
||||
funcsset.onblockplaced =
|
||||
register_event(env, "on_block_placed", prefix + ":.blockplaced");
|
||||
funcsset.onblockbreaking =
|
||||
register_event(env, "on_block_breaking", prefix + ":.blockbreaking");
|
||||
funcsset.onblockbroken =
|
||||
register_event(env, "on_block_broken", prefix + ":.blockbroken");
|
||||
funcsset.onblockreplaced =
|
||||
|
||||
@ -21,9 +21,9 @@ class Player;
|
||||
struct ItemDef;
|
||||
class Inventory;
|
||||
class UiDocument;
|
||||
struct block_funcs_set;
|
||||
struct item_funcs_set;
|
||||
struct world_funcs_set;
|
||||
struct BlockFuncsSet;
|
||||
struct ItemFuncsSet;
|
||||
struct WorldFuncsSet;
|
||||
struct UserComponent;
|
||||
struct uidocscript;
|
||||
class BlocksController;
|
||||
@ -77,6 +77,9 @@ namespace scripting {
|
||||
void on_block_replaced(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
);
|
||||
void on_block_breaking(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
);
|
||||
void on_block_broken(
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
);
|
||||
@ -141,7 +144,7 @@ namespace scripting {
|
||||
const std::string& prefix,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
BlockFuncsSet& funcsset
|
||||
);
|
||||
|
||||
/// @brief Load script associated with an Item
|
||||
@ -155,7 +158,7 @@ namespace scripting {
|
||||
const std::string& prefix,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
ItemFuncsSet& funcsset
|
||||
);
|
||||
|
||||
/// @brief Load component script
|
||||
@ -184,7 +187,7 @@ namespace scripting {
|
||||
const std::string& packid,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
WorldFuncsSet& funcsset
|
||||
);
|
||||
|
||||
/// @brief Load script associated with an UiDocument
|
||||
|
||||
30
src/maths/UVFace.hpp
Normal file
30
src/maths/UVFace.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "UVRegion.hpp"
|
||||
|
||||
struct UVFace {
|
||||
std::array<glm::vec2, 4> points;
|
||||
|
||||
UVFace(const UVRegion& region) {
|
||||
points[0] = {region.u1, region.v1};
|
||||
points[1] = {region.u2, region.v1};
|
||||
points[2] = {region.u2, region.v2};
|
||||
points[3] = {region.u1, region.v2};
|
||||
}
|
||||
|
||||
template<int n>
|
||||
inline void rotate(int times) {
|
||||
int times = n % 4;
|
||||
if (times < 0) {
|
||||
times += 4;
|
||||
}
|
||||
std::array<glm::vec2, 4> temp = points;
|
||||
points[0] = temp[times];
|
||||
points[1] = temp[(times + 1) % 4];
|
||||
points[2] = temp[(times + 2) % 4];
|
||||
points[3] = temp[(times + 3) % 4];
|
||||
}
|
||||
};
|
||||
@ -162,7 +162,7 @@ entityid_t Entities::spawn(
|
||||
|
||||
for (auto& componentName : def.components) {
|
||||
auto component = std::make_unique<UserComponent>(
|
||||
componentName, entity_funcs_set {}, nullptr
|
||||
componentName, EntityFuncsSet {}, nullptr
|
||||
);
|
||||
scripting.components.emplace_back(std::move(component));
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
struct entity_funcs_set {
|
||||
struct EntityFuncsSet {
|
||||
bool init;
|
||||
bool on_despawn;
|
||||
bool on_grounded;
|
||||
@ -77,11 +77,11 @@ struct Rigidbody {
|
||||
|
||||
struct UserComponent {
|
||||
std::string name;
|
||||
entity_funcs_set funcsset;
|
||||
EntityFuncsSet funcsset;
|
||||
scriptenv env;
|
||||
|
||||
UserComponent(
|
||||
const std::string& name, entity_funcs_set funcsset, scriptenv env
|
||||
const std::string& name, EntityFuncsSet funcsset, scriptenv env
|
||||
)
|
||||
: name(name), funcsset(funcsset), env(env) {
|
||||
}
|
||||
|
||||
@ -135,18 +135,7 @@ void Player::updateInput(PlayerInput& input, float delta) {
|
||||
hitbox->velocity.y = JUMP_FORCE;
|
||||
}
|
||||
|
||||
if ((input.flight && !noclip) || (input.noclip && flight == noclip)) {
|
||||
flight = !flight;
|
||||
if (flight) {
|
||||
hitbox->velocity.y += 1.0f;
|
||||
}
|
||||
}
|
||||
hitbox->type = noclip ? BodyType::KINEMATIC : BodyType::DYNAMIC;
|
||||
if (input.noclip) {
|
||||
noclip = !noclip;
|
||||
}
|
||||
input.noclip = false;
|
||||
input.flight = false;
|
||||
}
|
||||
|
||||
void Player::updateSelectedEntity() {
|
||||
@ -262,6 +251,14 @@ void Player::setInstantDestruction(bool flag) {
|
||||
instantDestruction = flag;
|
||||
}
|
||||
|
||||
bool Player::isLoadingChunks() const {
|
||||
return loadingChunks;
|
||||
}
|
||||
|
||||
void Player::setLoadingChunks(bool flag) {
|
||||
loadingChunks = flag;
|
||||
}
|
||||
|
||||
entityid_t Player::getEntity() const {
|
||||
return eid;
|
||||
}
|
||||
@ -308,6 +305,7 @@ dv::value Player::serialize() const {
|
||||
root["noclip"] = noclip;
|
||||
root["infinite-items"] = infiniteItems;
|
||||
root["instant-destruction"] = instantDestruction;
|
||||
root["loading-chunks"] = loadingChunks;
|
||||
root["chosen-slot"] = chosenSlot;
|
||||
root["entity"] = eid;
|
||||
root["inventory"] = inventory->serialize();
|
||||
@ -340,6 +338,7 @@ void Player::deserialize(const dv::value& src) {
|
||||
noclip = src["noclip"].asBoolean();
|
||||
src.at("infinite-items").get(infiniteItems);
|
||||
src.at("instant-destruction").get(instantDestruction);
|
||||
src.at("loading-chunks").get(loadingChunks);
|
||||
|
||||
setChosenSlot(src["chosen-slot"].asInteger());
|
||||
eid = src["entity"].asNumber();
|
||||
|
||||
@ -27,8 +27,6 @@ struct PlayerInput {
|
||||
bool shift : 1;
|
||||
bool cheat : 1;
|
||||
bool jump : 1;
|
||||
bool noclip : 1;
|
||||
bool flight : 1;
|
||||
};
|
||||
|
||||
struct CursorSelection {
|
||||
@ -53,13 +51,13 @@ class Player : public Serializable {
|
||||
bool noclip = false;
|
||||
bool infiniteItems = true;
|
||||
bool instantDestruction = true;
|
||||
bool loadingChunks = true;
|
||||
entityid_t eid;
|
||||
entityid_t selectedEid = 0;
|
||||
public:
|
||||
std::unique_ptr<Chunks> chunks; // not in use yet
|
||||
std::unique_ptr<Chunks> chunks;
|
||||
std::shared_ptr<Camera> fpCamera, spCamera, tpCamera;
|
||||
std::shared_ptr<Camera> currentCamera;
|
||||
bool debug = false;
|
||||
std::shared_ptr<Camera> currentCamera;;
|
||||
glm::vec3 cam {};
|
||||
CursorSelection selection {};
|
||||
|
||||
@ -99,6 +97,9 @@ public:
|
||||
bool isInstantDestruction() const;
|
||||
void setInstantDestruction(bool flag);
|
||||
|
||||
bool isLoadingChunks() const;
|
||||
void setLoadingChunks(bool flag);
|
||||
|
||||
entityid_t getEntity() const;
|
||||
void setEntity(entityid_t eid);
|
||||
|
||||
|
||||
@ -43,6 +43,9 @@ dv::value ParticlesPreset::serialize() const {
|
||||
root["explosion"] = dv::to_value(explosion);
|
||||
root["size"] = dv::to_value(size);
|
||||
root["size_spread"] = sizeSpread;
|
||||
root["angle_spread"] = angleSpread;
|
||||
root["min_angular_vel"] = minAngularVelocity;
|
||||
root["max_angular_vel"] = maxAngularVelocity;
|
||||
root["spawn_spread"] = dv::to_value(size);
|
||||
root["spawn_shape"] = to_string(spawnShape);
|
||||
root["random_sub_uv"] = randomSubUV;
|
||||
@ -58,6 +61,9 @@ void ParticlesPreset::deserialize(const dv::value& src) {
|
||||
src.at("spawn_interval").get(spawnInterval);
|
||||
src.at("lifetime").get(lifetime);
|
||||
src.at("lifetime_spread").get(lifetimeSpread);
|
||||
src.at("angle_spread").get(angleSpread);
|
||||
src.at("min_angular_vel").get(minAngularVelocity);
|
||||
src.at("max_angular_vel").get(maxAngularVelocity);
|
||||
src.at("random_sub_uv").get(randomSubUV);
|
||||
if (src.has("velocity")) {
|
||||
dv::get_vec(src["velocity"], velocity);
|
||||
|
||||
@ -27,7 +27,7 @@ struct ParticlesPreset : public Serializable {
|
||||
/// @brief Use global up vector instead of camera-dependent one
|
||||
bool globalUpVector = false;
|
||||
/// @brief Max distance of actually spawning particles.
|
||||
float maxDistance = 16.0f;
|
||||
float maxDistance = 32.0f;
|
||||
/// @brief Particles spawn interval
|
||||
float spawnInterval = 0.1f;
|
||||
/// @brief Particle life time
|
||||
@ -44,6 +44,12 @@ struct ParticlesPreset : public Serializable {
|
||||
glm::vec3 size {0.1f};
|
||||
/// @brief Particles size spread
|
||||
float sizeSpread = 0.2f;
|
||||
/// @brief Random initial angle spread
|
||||
float angleSpread = 0.0f;
|
||||
/// @brief Minimum angular velocity
|
||||
float minAngularVelocity = 0.0f;
|
||||
/// @brief Maximum angular velocity
|
||||
float maxAngularVelocity = 0.0f;
|
||||
/// @brief Spawn spread shape
|
||||
ParticleSpawnShape spawnShape = BALL;
|
||||
/// @brief Spawn spread
|
||||
|
||||
@ -63,16 +63,24 @@ struct GraphicsSettings {
|
||||
NumberSetting gamma {1.0f, 0.4f, 1.0f};
|
||||
/// @brief Enable blocks backlight to prevent complete darkness
|
||||
FlagSetting backlight {true};
|
||||
/// @brief Disable culling with 'optional' mode
|
||||
FlagSetting denseRender {true};
|
||||
/// @brief Enable chunks frustum culling
|
||||
FlagSetting frustumCulling {true};
|
||||
/// @brief Skybox texture face resolution
|
||||
IntegerSetting skyboxResolution {64 + 32, 64, 128};
|
||||
/// @brief Chunk renderer vertices buffer capacity
|
||||
IntegerSetting chunkMaxVertices {200'000, 0, 4'000'000};
|
||||
/// @brief Chunk renderer vertices buffer capacity in dense render mode
|
||||
IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000};
|
||||
/// @brief Limit of chunk renderers count
|
||||
IntegerSetting chunkMaxRenderers {6, -4, 32};
|
||||
};
|
||||
|
||||
struct DebugSettings {
|
||||
/// @brief Turns off chunks saving/loading
|
||||
FlagSetting generatorTestMode {false};
|
||||
/// @brief Write lights cache
|
||||
FlagSetting doWriteLights {true};
|
||||
};
|
||||
|
||||
|
||||
@ -49,6 +49,30 @@ std::optional<BlockModel> BlockModel_from(std::string_view str) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string to_string(CullingMode mode) {
|
||||
switch (mode) {
|
||||
case CullingMode::DEFAULT:
|
||||
return "default";
|
||||
case CullingMode::OPTIONAL:
|
||||
return "optional";
|
||||
case CullingMode::DISABLED:
|
||||
return "disabled";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<CullingMode> CullingMode_from(std::string_view str) {
|
||||
if (str == "default") {
|
||||
return CullingMode::DEFAULT;
|
||||
} else if (str == "optional") {
|
||||
return CullingMode::OPTIONAL;
|
||||
} else if (str == "disabled") {
|
||||
return CullingMode::DISABLED;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
|
||||
: axisX(axisX), axisY(axisY), axisZ(axisZ) {
|
||||
fix = glm::ivec3(0);
|
||||
|
||||
@ -34,10 +34,11 @@ inline constexpr size_t MAX_USER_BLOCK_FIELDS_SIZE = 240;
|
||||
|
||||
inline std::string DEFAULT_MATERIAL = "base:stone";
|
||||
|
||||
struct block_funcs_set {
|
||||
struct BlockFuncsSet {
|
||||
bool init : 1;
|
||||
bool update : 1;
|
||||
bool onplaced : 1;
|
||||
bool onbreaking : 1;
|
||||
bool onbroken : 1;
|
||||
bool onreplaced : 1;
|
||||
bool oninteract : 1;
|
||||
@ -97,6 +98,15 @@ enum class BlockModel {
|
||||
std::string to_string(BlockModel model);
|
||||
std::optional<BlockModel> BlockModel_from(std::string_view str);
|
||||
|
||||
enum class CullingMode {
|
||||
DEFAULT,
|
||||
OPTIONAL,
|
||||
DISABLED,
|
||||
};
|
||||
|
||||
std::string to_string(CullingMode mode);
|
||||
std::optional<CullingMode> CullingMode_from(std::string_view str);
|
||||
|
||||
using BoxModel = AABB;
|
||||
|
||||
/// @brief Common kit of block properties applied to groups of blocks
|
||||
@ -142,6 +152,9 @@ public:
|
||||
|
||||
std::string modelName = "";
|
||||
|
||||
/// @brief Culling mode
|
||||
CullingMode culling = CullingMode::DEFAULT;
|
||||
|
||||
/// @brief Does the block passing lights into itself
|
||||
bool lightPassing = false;
|
||||
|
||||
@ -229,7 +242,7 @@ public:
|
||||
std::vector<AABB> hitboxes[BlockRotProfile::MAX_COUNT];
|
||||
|
||||
/// @brief set of block callbacks flags
|
||||
block_funcs_set funcsset {};
|
||||
BlockFuncsSet funcsset {};
|
||||
|
||||
/// @brief picking item integer id
|
||||
itemid_t pickingItem = 0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user