Merge branch 'main' into blocks_meta
This commit is contained in:
commit
86bbece599
44
.github/workflows/macos.yml
vendored
Normal file
44
.github/workflows/macos.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
name: Macos Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build-dmg:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Install dependencies from brew
|
||||
run: |
|
||||
brew install glfw3 glew libpng openal-soft luajit libvorbis
|
||||
|
||||
- name: Install specific version of GLM
|
||||
run: |
|
||||
curl -O https://raw.githubusercontent.com/Homebrew/homebrew-core/5c7655a866646aa4b857c002b8ae5465b9d26f65/Formula/g/glm.rb
|
||||
brew install --formula glm.rb
|
||||
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build -t install
|
||||
|
||||
- name: Create DMG
|
||||
run: |
|
||||
mkdir VoxelEngineDmgContent
|
||||
cp -r build/res VoxelEngineDmgContent/
|
||||
cp -r build/VoxelEngine VoxelEngineDmgContent/
|
||||
hdiutil create VoxelEngineMacApp.dmg -volname "VoxelEngine" -srcfolder VoxelEngineDmgContent -ov -format UDZO
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: VoxelEngineMacOs
|
||||
path: VoxelEngineMacApp.dmg
|
||||
48
.github/workflows/windows.yml
vendored
Normal file
48
.github/workflows/windows.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: Windows Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Set up vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
.\bootstrap-vcpkg.bat
|
||||
.\vcpkg integrate install
|
||||
cd ..
|
||||
- name: Configure and build project with CMake and vcpkg
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON ..
|
||||
Remove-Item -Path CMakeFiles -Recurse -Force
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON ..
|
||||
cmake --build . --config Release
|
||||
- name: Package for Windows
|
||||
run: |
|
||||
mkdir packaged
|
||||
cp -r build/* packaged/
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Windows-Build
|
||||
path: 'packaged/Release/*'
|
||||
@ -50,6 +50,9 @@ else()
|
||||
-Wformat-nonliteral -Wcast-align
|
||||
-Wpointer-arith -Wundef
|
||||
-Wwrite-strings -Wno-unused-parameter)
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Og)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32)
|
||||
@ -98,6 +101,18 @@ if (WIN32)
|
||||
set(VORBISLIB vorbis vorbisfile) # not tested
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libs/glfw)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(LUAJIT REQUIRED luajit)
|
||||
pkg_check_modules(VORBIS REQUIRED vorbis vorbisfile)
|
||||
set(LUA_INCLUDE_DIR "/opt/homebrew/include/luajit-2.1")
|
||||
set(LUA_LIBRARIES "/opt/homebrew/lib/libluajit-5.1.a")
|
||||
message(STATUS "LUA Libraries: ${LUA_LIBRARIES}")
|
||||
message(STATUS "LUA Include Dir: ${LUA_INCLUDE_DIR}")
|
||||
find_package(PNG REQUIRED)
|
||||
set(PNGLIB PNG::PNG)
|
||||
set(VORBISLIB ${VORBIS_LDFLAGS})
|
||||
message(STATUS "Vorbis Lib: ${VORBIS_LDFLAGS}")
|
||||
else()
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(LUAJIT REQUIRED luajit)
|
||||
|
||||
@ -2,11 +2,7 @@
|
||||
|
||||
В качестве языка сценариев используется LuaJIT
|
||||
|
||||
## Функции, доступные в скриптах
|
||||
|
||||
```lua
|
||||
load_script("контентпак:scripts/имя_скрипта.lua") -- загружает скрипт, если ещё не загружен
|
||||
load_script("контентпак:scripts/имя_скрипта.lua", true) -- перезагружает скрипт
|
||||
require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается)
|
||||
```
|
||||
|
||||
@ -77,8 +73,23 @@ player.set_noclip(bool)
|
||||
|
||||
Геттер и сеттер noclip режима (выключенная коллизия игрока)
|
||||
|
||||
```python
|
||||
player.get_selected_block(playerid: int) -> x,y,z
|
||||
```
|
||||
|
||||
Возвращает координаты выделенного блока, либо nil
|
||||
|
||||
## Библиотека world
|
||||
|
||||
```python
|
||||
world.get_list() -> массив таблиц {
|
||||
name: str,
|
||||
icon: str
|
||||
}
|
||||
```
|
||||
|
||||
Возвращает информацию о мирах: название и предпросмотр (автоматически загружаемая текстура).
|
||||
|
||||
```python
|
||||
world.get_day_time() -> number
|
||||
```
|
||||
@ -103,6 +114,12 @@ world.get_seed() -> int
|
||||
|
||||
Возвращает зерно мира.
|
||||
|
||||
```python
|
||||
world.exists() -> bool
|
||||
```
|
||||
|
||||
Проверяет существование мира по имени.
|
||||
|
||||
## Библиотека pack
|
||||
|
||||
```python
|
||||
@ -123,6 +140,38 @@ pack.get_installed() -> массив строк
|
||||
|
||||
Возращает id всех установленных в мире контент-паков
|
||||
|
||||
```python
|
||||
pack.get_available() -> массив строк
|
||||
```
|
||||
|
||||
Возвращает id всех доступных, но не установленных в мире контент-паков
|
||||
|
||||
```python
|
||||
pack.get_base_packs() -> массив строк
|
||||
```
|
||||
|
||||
Возвращает id всех базовых паков (неудаляемых)
|
||||
|
||||
```python
|
||||
pack.get_info(packid: str) -> {
|
||||
id: str,
|
||||
title: str,
|
||||
creator: str,
|
||||
description: str,
|
||||
version: str,
|
||||
icon: str,
|
||||
dependencies: опциональный массив строк
|
||||
}
|
||||
```
|
||||
|
||||
Возвращает информацию о паке (не обязательно установленном).
|
||||
- icon - название текстуры предпросмотра (загружается автоматически)
|
||||
- dependencies - строки в формате `{lvl}{id}`, где lvl:
|
||||
- `!` - required
|
||||
- `?` - optional
|
||||
- `~` - weak
|
||||
например `!teal`
|
||||
|
||||
## Библиотека gui
|
||||
|
||||
Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex:
|
||||
@ -134,6 +183,33 @@ indentory_doc.some_button.text = "new text"
|
||||
|
||||
В скрипте макета `layouts/файл_макета.xml` - `layouts/файл_макета.xml.lua` уже доступна переменная **document** содержащая объект класса Document
|
||||
|
||||
```python
|
||||
gui.str(text: str, context: str) -> str
|
||||
```
|
||||
|
||||
Возращает переведенный текст.
|
||||
|
||||
```python
|
||||
gui.get_viewport() -> {int, int}
|
||||
```
|
||||
|
||||
Возвращает размер главного контейнера (окна).
|
||||
|
||||
```python
|
||||
gui.get_env(document: str) -> table
|
||||
```
|
||||
|
||||
Возвращает окружение (таблица глобальных переменных) указанного документа.
|
||||
|
||||
```python
|
||||
get_locales_info() -> таблица таблиц где
|
||||
ключ - id локали в формате isolangcode_ISOCOUNTRYCODE
|
||||
значение - таблица {
|
||||
name: str # название локали на её языке
|
||||
}
|
||||
```
|
||||
|
||||
Возвращает информацию о всех загруженных локалях (res/texts/\*).
|
||||
## Библиотека inventory
|
||||
|
||||
Библиотека функций для работы с инвентарем.
|
||||
@ -211,6 +287,18 @@ block.index(name: str) -> int
|
||||
|
||||
Возвращает числовой id блока, принимая в качестве агрумента строковый
|
||||
|
||||
```python
|
||||
block.material(blockid: int) -> str
|
||||
```
|
||||
|
||||
Возвращает id материала блока.
|
||||
|
||||
```python
|
||||
block.caption(blockid: int) -> str
|
||||
```
|
||||
|
||||
Возвращает название блока, отображаемое в интерфейсе.
|
||||
|
||||
```python
|
||||
block.get(x: int, y: int, z: int) -> int
|
||||
```
|
||||
@ -373,206 +461,27 @@ hud.close(layoutid: str)
|
||||
hud.get_block_inventory() -> int
|
||||
```
|
||||
|
||||
Получить ID инвентаря открытого блока или 0
|
||||
|
||||
## События блоков
|
||||
|
||||
```lua
|
||||
function on_placed(x, y, z, playerid)
|
||||
```
|
||||
|
||||
Вызывается после установки блока игроком
|
||||
|
||||
```lua
|
||||
function on_broken(x, y, z, playerid)
|
||||
```
|
||||
|
||||
Вызывается после разрушения блока игроком
|
||||
|
||||
```lua
|
||||
function on_interact(x, y, z, playerid) -> bool
|
||||
```
|
||||
|
||||
Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true`
|
||||
|
||||
```lua
|
||||
function on_update(x, y, z)
|
||||
```
|
||||
|
||||
Вызывается при обновлении блока (если изменился соседний блок)
|
||||
|
||||
```lua
|
||||
function on_random_update(x, y, z)
|
||||
```
|
||||
|
||||
Вызывается в случайные моменты времени (рост травы на блоках земли)
|
||||
|
||||
```lua
|
||||
function on_blocks_tick(tps: int)
|
||||
```
|
||||
|
||||
Вызывается tps (20) раз в секунду
|
||||
|
||||
## События предметов
|
||||
|
||||
```lua
|
||||
function on_use(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при нажатии ПКМ не на блок.
|
||||
|
||||
```lua
|
||||
function on_use_on_block(x: int, y: int, z: int, playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при нажатии ПКМ на блок. Предотвращает установку блока, прописанного в `placing-block` если возвращает `true`
|
||||
|
||||
```lua
|
||||
function on_block_break_by(x: int, y: int, z: int, playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при нажатии ЛКМ на блок (в т.ч неразрушимый). Предотвращает разрушение блока, если возвращает `true`
|
||||
|
||||
## События мира
|
||||
|
||||
События мира для контент-пака прописываются в `scripts/world.lua`
|
||||
|
||||
```lua
|
||||
function on_world_open()
|
||||
```
|
||||
|
||||
Вызывается при загрузке мира
|
||||
|
||||
```lua
|
||||
function on_world_save()
|
||||
```
|
||||
|
||||
Вызывается перед сохранением мира
|
||||
|
||||
```lua
|
||||
function on_world_tick()
|
||||
```
|
||||
|
||||
Вызывается 20 раз в секунду
|
||||
|
||||
```lua
|
||||
function on_world_quit()
|
||||
```
|
||||
|
||||
Вызывается при выходе из мира (после сохранения)
|
||||
|
||||
## События макета
|
||||
|
||||
События прописываются в файле `layouts/имя_макета.xml.lua`.
|
||||
|
||||
```lua
|
||||
function on_open(invid: int, x: int, y: int, z: int)
|
||||
```
|
||||
|
||||
Вызывается при добавлении элемента на экран.
|
||||
При отсутствии привязки к инвентарю invid будет равен 0.
|
||||
При отсутствии привязки к блоку x, y, z так же будут равны 0.
|
||||
|
||||
```lua
|
||||
function on_close(invid: int)
|
||||
```
|
||||
|
||||
Вызывается при удалении элемента с экрана.
|
||||
|
||||
## События HUD
|
||||
|
||||
События связанные с игровым интерфейсом прописываются в файле `scripts/hud.lua`
|
||||
|
||||
```lua
|
||||
function on_hud_open(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается после входа в мир, когда становится доступна библиотека hud. Здесь на экран добавляются постоянные элементы.
|
||||
|
||||
```lua
|
||||
function on_hud_close(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при выходе из мира, перед его сохранением.
|
||||
|
||||
## Библиотеки движка
|
||||
|
||||
### file
|
||||
|
||||
Библиотека функций для работы с файлами
|
||||
Дает ID инвентаря открытого блока или 0
|
||||
|
||||
```python
|
||||
file.resolve(путь: str) -> str
|
||||
hud.get_player() -> int
|
||||
```
|
||||
|
||||
Функция приводит запись `точка_входа:путь` (например `user:worlds/house1`) к обычному пути. (например `C://Users/user/.voxeng/worlds/house1`)
|
||||
|
||||
> [!NOTE]
|
||||
> Функцию не нужно использовать в сочетании с другими функциями из библиотеки, так как они делают это автоматически
|
||||
|
||||
Возвращаемый путь не является каноническим и может быть как абсолютным, так и относительным.
|
||||
Дает ID игрока, к которому привязан пользовательский интерфейс
|
||||
|
||||
```python
|
||||
file.read(путь: str) -> str
|
||||
hud.pause()
|
||||
```
|
||||
|
||||
Читает весь текстовый файл и возвращает в виде строки
|
||||
Открывает меню паузы
|
||||
|
||||
```python
|
||||
file.read_bytes(путь: str) -> array of integers
|
||||
hud.resume()
|
||||
```
|
||||
|
||||
Читает файл в массив байт.
|
||||
Закрывает меню паузы.
|
||||
|
||||
```python
|
||||
file.write(путь: str, текст: str) -> nil
|
||||
```
|
||||
|
||||
Записывает текст в файл (с перезаписью)
|
||||
|
||||
```python
|
||||
file.write_bytes(путь: str, data: array of integers)
|
||||
```
|
||||
|
||||
Записывает массив байт в файл (с перезаписью)
|
||||
|
||||
```python
|
||||
file.length(путь: str) -> int
|
||||
```
|
||||
|
||||
Возвращает размер файла в байтах, либо -1, если файл не найден
|
||||
|
||||
```python
|
||||
file.exists(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, существует ли по данному пути файл или директория
|
||||
|
||||
```python
|
||||
file.isfile(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, существует ли по данному пути файл
|
||||
|
||||
```python
|
||||
file.isdir(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, существует ли по данному пути директория
|
||||
|
||||
```python
|
||||
file.mkdir(путь: str) -> bool
|
||||
```
|
||||
|
||||
Создает директорию. Возвращает true если была создана новая директория
|
||||
|
||||
```python
|
||||
file.mkdirs(путь: str) -> bool
|
||||
```
|
||||
|
||||
Создает всю цепочку директорий. Возвращает true если были созданы директории.
|
||||
|
||||
### time
|
||||
## Библиотека time
|
||||
|
||||
```python
|
||||
time.uptime() -> float
|
||||
@ -580,24 +489,8 @@ time.uptime() -> float
|
||||
|
||||
Возвращает время с момента запуска движка в секундах
|
||||
|
||||
## Доступные модули
|
||||
|
||||
### TOML сериализация/десериализация
|
||||
|
||||
```lua
|
||||
local toml = require "core:toml"
|
||||
|
||||
local t = {a=53, b=42, s="test", sub={x=1, y=6}}
|
||||
local s = toml.serialize(t)
|
||||
print(s)
|
||||
local t2 = toml.deserialize(s)
|
||||
```
|
||||
вывод:
|
||||
```toml
|
||||
b = 42
|
||||
s = "test"
|
||||
a = 53
|
||||
[sub]
|
||||
y = 6
|
||||
x = 1
|
||||
```python
|
||||
time.delta() -> float
|
||||
```
|
||||
|
||||
Возвращает дельту времени (время прошедшее с предыдущего кадра)
|
||||
|
||||
109
doc/ru/Консоль.md
Normal file
109
doc/ru/Консоль.md
Normal file
@ -0,0 +1,109 @@
|
||||
# Консоль
|
||||
|
||||
Для работы с командным интерпретатором предоставляется библиотека **console**
|
||||
|
||||
## Создание команд
|
||||
|
||||
Для создания команды консоли используется следующая функция:
|
||||
|
||||
```python
|
||||
console.add_command(схема: str, исполнитель: function)
|
||||
```
|
||||
|
||||
Схема имеет следующий синтаксис:
|
||||
|
||||
```
|
||||
название позиционные параметры {именованные параметры}
|
||||
```
|
||||
|
||||
Название может содержать:
|
||||
- латинницу
|
||||
- цифры (кроме первого символа)
|
||||
- `.`, `_`, `-`
|
||||
|
||||
Позиционные параметры разделяются пробелами и имеют следующий синтаксис:
|
||||
|
||||
```
|
||||
название:тип (вариант 1)
|
||||
название:тип=по-умолчанию (вариант 2)
|
||||
название:тип~центральное-значение (вариант 3)
|
||||
название:тип=по-умолчанию~центральное-значение (вариант 4)
|
||||
```
|
||||
|
||||
Доступные типы:
|
||||
- **int** - целое число
|
||||
- **num** - дробное число
|
||||
- **str** - строка
|
||||
- **sel** - селектор (id объекта представленный целым числом)
|
||||
- **enum** - перечисление
|
||||
|
||||
На вариантах 3 и 4 показан оператор `~` позволяющий использовать относительные значения. *Центральное значение* - значение, относительно которого будет указываться пользовательское. Например позиция игрока.
|
||||
|
||||
Относительный оператор работает только с числами (num или int)
|
||||
|
||||
В качестве центральных значений могут указываться переменные, назначаемые через **console.set**.
|
||||
|
||||
Пример:
|
||||
|
||||
```python
|
||||
x:num~pos.x
|
||||
```
|
||||
|
||||
Переменные можно указывать и в качестве значений по-умолчанию, при использовании префикса `$`:
|
||||
|
||||
```python
|
||||
t:int=$time
|
||||
```
|
||||
|
||||
Перечисления указывазываются в формате:
|
||||
|
||||
```python
|
||||
mode:[replace|destruct|none]
|
||||
```
|
||||
|
||||
Либо через переменную:
|
||||
|
||||
```python
|
||||
mode:enum $modes
|
||||
```
|
||||
|
||||
Селекторы указываются с префиксом `@`. На данный момент являются заглушкой, по причине отсутствия объектной модели. Следует делать опциональными и использовать переменные:
|
||||
|
||||
```python
|
||||
obj:sel=$obj.id # obj.id - id игрока
|
||||
```
|
||||
|
||||
Именованные аргументы указываются в специальном блоке, ограниченном фигурными скобками `{ }` по той же схеме.
|
||||
|
||||
Пример:
|
||||
|
||||
```python
|
||||
eval name:str="World" {greeting:str='Hello'}
|
||||
```
|
||||
|
||||
## Примеры схем команд
|
||||
|
||||
Схемы существующих команд можно найти в файле `res/script/stdcmd.lua`.
|
||||
|
||||
Пример - команда **tp**:
|
||||
|
||||
```python
|
||||
tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z
|
||||
```
|
||||
|
||||
Полный lua код создания команды:
|
||||
|
||||
```lua
|
||||
console.add_command(
|
||||
"tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z",
|
||||
"Teleport object",
|
||||
function (args, kwargs)
|
||||
player.set_pos(unpack(args))
|
||||
end
|
||||
)
|
||||
```
|
||||
|
||||
- В args передаются готовые значения позиционных аргументов.
|
||||
- В kwargs передается таблица значений именованных аргументов.
|
||||
|
||||
Проверку и приведение типов интерпретатор команд производит автоматически.
|
||||
59
doc/ru/Пользовательский-ввод.md
Normal file
59
doc/ru/Пользовательский-ввод.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Пользовательский ввод
|
||||
|
||||
Обработка нажатий клавиш и кнопок мыши обрабатываются через привязки (bindings), которые назначаются в паке, в файле `config/bindings.toml` в формате:
|
||||
|
||||
```toml
|
||||
packid.binding.name="inputtype:codename"
|
||||
```
|
||||
|
||||
- packid - опционально, но желательно
|
||||
- inputtype - key или mouse
|
||||
- codename - имя клавиши или кнопки мыши (left/right/middle)
|
||||
|
||||
## Имена клавиш
|
||||
|
||||
- space, backspace, tab, enter, caps-lock, escape
|
||||
- left-ctrl, left-shift, left-alt, left-super
|
||||
- right-ctrl, right-shift, right-alt, right-super
|
||||
- delete, home, end, insert, page-up, page-down
|
||||
- left, right, down, up
|
||||
- a..z
|
||||
- 0..9
|
||||
- f1..f25
|
||||
|
||||
## Библиотека input
|
||||
|
||||
```python
|
||||
input.keycode(keyname: str) -> int
|
||||
```
|
||||
|
||||
Возвращает код клавиши по имени, либо -1
|
||||
|
||||
```python
|
||||
input.mousecode(mousename: str) -> int
|
||||
```
|
||||
|
||||
Возвращает код кнопки мыши по имени, либо -1
|
||||
|
||||
```python
|
||||
input.add_callback(bindname: str, callback: function)
|
||||
```
|
||||
|
||||
Назначает функцию, которая будет вызываться при активации привязки. Пример:
|
||||
```lua
|
||||
input.add_callback("hud.inventory", function ()
|
||||
print("Inventory open key pressed")
|
||||
end)
|
||||
```
|
||||
|
||||
```python
|
||||
input.get_mouse_pos() -> {int, int}
|
||||
```
|
||||
|
||||
Возвращает позицию курсора на экране.
|
||||
|
||||
```python
|
||||
input.get_bindings() -> массив строк
|
||||
```
|
||||
|
||||
Возвращает названия всех доступных привязок.
|
||||
126
doc/ru/События-движка.md
Normal file
126
doc/ru/События-движка.md
Normal file
@ -0,0 +1,126 @@
|
||||
# События движка
|
||||
|
||||
## События блоков
|
||||
|
||||
Функции для обработки событий, прописываемые в скрипте блока.
|
||||
|
||||
```lua
|
||||
function on_placed(x, y, z, playerid)
|
||||
```
|
||||
|
||||
Вызывается после установки блока игроком
|
||||
|
||||
```lua
|
||||
function on_broken(x, y, z, playerid)
|
||||
```
|
||||
|
||||
Вызывается после разрушения блока игроком
|
||||
|
||||
```lua
|
||||
function on_interact(x, y, z, playerid) -> bool
|
||||
```
|
||||
|
||||
Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true`
|
||||
|
||||
```lua
|
||||
function on_update(x, y, z)
|
||||
```
|
||||
|
||||
Вызывается при обновлении блока (если изменился соседний блок)
|
||||
|
||||
```lua
|
||||
function on_random_update(x, y, z)
|
||||
```
|
||||
|
||||
Вызывается в случайные моменты времени (рост травы на блоках земли)
|
||||
|
||||
```lua
|
||||
function on_blocks_tick(tps: int)
|
||||
```
|
||||
|
||||
Вызывается tps (20) раз в секунду
|
||||
|
||||
## События предметов
|
||||
|
||||
Функции для обработки событий, прописываемые в скрипте предмета.
|
||||
|
||||
```lua
|
||||
function on_use(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при нажатии ПКМ не на блок.
|
||||
|
||||
```lua
|
||||
|
||||
function on_use_on_block(x: int, y: int, z: int, playerid: int)
|
||||
|
||||
```
|
||||
|
||||
Вызывается при нажатии ПКМ на блок. Предотвращает установку блока, прописанного в `placing-block` если возвращает `true`
|
||||
|
||||
```lua
|
||||
function on_block_break_by(x: int, y: int, z: int, playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при нажатии ЛКМ на блок (в т.ч неразрушимый). Предотвращает разрушение блока, если возвращает `true`
|
||||
|
||||
## События мира
|
||||
|
||||
События мира для контент-пака прописываются в `scripts/world.lua`
|
||||
|
||||
```lua
|
||||
function on_world_open()
|
||||
```
|
||||
|
||||
Вызывается при загрузке мира
|
||||
|
||||
```lua
|
||||
function on_world_save()
|
||||
```
|
||||
|
||||
Вызывается перед сохранением мира
|
||||
|
||||
```lua
|
||||
function on_world_tick()
|
||||
```
|
||||
|
||||
Вызывается 20 раз в секунду
|
||||
|
||||
```lua
|
||||
function on_world_quit()
|
||||
```
|
||||
|
||||
Вызывается при выходе из мира (после сохранения)
|
||||
## События макета
|
||||
|
||||
События прописываются в файле `layouts/имя_макета.xml.lua`.
|
||||
|
||||
```lua
|
||||
function on_open(invid: int, x: int, y: int, z: int)
|
||||
```
|
||||
|
||||
Вызывается при добавлении элемента на экран.
|
||||
- При отсутствии привязки к инвентарю invid будет равен 0.
|
||||
- При отсутствии привязки к блоку x, y, z так же будут равны 0.
|
||||
|
||||
```lua
|
||||
function on_close(invid: int)
|
||||
```
|
||||
|
||||
Вызывается при удалении элемента с экрана.
|
||||
|
||||
## События HUD
|
||||
|
||||
События связанные с игровым интерфейсом прописываются в файле `scripts/hud.lua`
|
||||
|
||||
```lua
|
||||
function on_hud_open(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается после входа в мир, когда становится доступна библиотека hud. Здесь на экран добавляются постоянные элементы.
|
||||
|
||||
```lua
|
||||
function on_hud_close(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при выходе из мира, перед его сохранением.
|
||||
137
doc/ru/Файловая-система-и-сериализация.md
Normal file
137
doc/ru/Файловая-система-и-сериализация.md
Normal file
@ -0,0 +1,137 @@
|
||||
## Библиотека *file*
|
||||
|
||||
Библиотека функций для работы с файлами
|
||||
|
||||
```python
|
||||
file.resolve(путь: str) -> str
|
||||
```
|
||||
|
||||
Функция приводит запись `точка_входа:путь` (например `user:worlds/house1`) к обычному пути. (например `C://Users/user/.voxeng/worlds/house1`)
|
||||
|
||||
> [!NOTE]
|
||||
> Функцию не нужно использовать в сочетании с другими функциями из библиотеки, так как они делают это автоматически
|
||||
|
||||
Возвращаемый путь не является каноническим и может быть как абсолютным, так и относительным.
|
||||
|
||||
```python
|
||||
file.read(путь: str) -> str
|
||||
```
|
||||
|
||||
Читает весь текстовый файл и возвращает в виде строки
|
||||
|
||||
```python
|
||||
file.read_bytes(путь: str) -> array of integers
|
||||
```
|
||||
|
||||
Читает файл в массив байт.
|
||||
|
||||
```python
|
||||
file.write(путь: str, текст: str) -> nil
|
||||
```
|
||||
|
||||
Записывает текст в файл (с перезаписью)
|
||||
|
||||
```python
|
||||
file.write_bytes(путь: str, data: array of integers)
|
||||
```
|
||||
|
||||
Записывает массив байт в файл (с перезаписью)
|
||||
|
||||
```python
|
||||
file.length(путь: str) -> int
|
||||
```
|
||||
|
||||
Возвращает размер файла в байтах, либо -1, если файл не найден
|
||||
|
||||
```python
|
||||
file.exists(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, существует ли по данному пути файл или директория
|
||||
|
||||
```python
|
||||
file.isfile(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, существует ли по данному пути файл
|
||||
|
||||
```python
|
||||
file.isdir(путь: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет, существует ли по данному пути директория
|
||||
|
||||
```python
|
||||
file.mkdir(путь: str) -> bool
|
||||
```
|
||||
|
||||
Создает директорию. Возвращает true если была создана новая директория
|
||||
|
||||
```python
|
||||
file.mkdirs(путь: str) -> bool
|
||||
```
|
||||
|
||||
Создает всю цепочку директорий. Возвращает true если были созданы директории.
|
||||
|
||||
```python
|
||||
file.find(путь: str) -> str
|
||||
```
|
||||
|
||||
Ищет файл от последнего пака до res. Путь указывается без префикса. Возвращает путь с нужным префиксом. Если файл не найден, возвращает nil.
|
||||
|
||||
```python
|
||||
file.remove(путь: str) -> bool
|
||||
```
|
||||
|
||||
Удаляет файл. Возращает **true** если файл существовал. Бросает исключение при нарушении доступа.
|
||||
|
||||
```python
|
||||
file.remove_tree(путь: str) -> int
|
||||
```
|
||||
|
||||
Рекурсивно удаляет файлы. Возвращает число удаленных файлов.
|
||||
|
||||
## Библиотека json
|
||||
|
||||
Библиотека содержит функции для сериализации и десериализации таблиц:
|
||||
|
||||
```python
|
||||
json.tostring(object: table, human_readable: bool=false) -> str
|
||||
```
|
||||
|
||||
Сериализует объект в JSON строку. При значении второго параметра **true** будет использовано многострочное форматирование, удобное для чтения человеком, а не компактное, использующееся по-умолчанию.
|
||||
|
||||
```python
|
||||
json.parse(code: str) -> table
|
||||
```
|
||||
|
||||
Парсит JSON строку в таблицу.
|
||||
|
||||
## Библиотека toml
|
||||
|
||||
Библиотека содержит функции для сериализации и десериализации таблиц:
|
||||
|
||||
```python
|
||||
toml.tostring(object: table) -> str
|
||||
```
|
||||
|
||||
Сериализует объект в TOML строку.
|
||||
|
||||
```python
|
||||
toml.parse(code: str) -> table
|
||||
```
|
||||
|
||||
Парсит TOML строку в таблицу.
|
||||
|
||||
## Сохранение данных в мире
|
||||
|
||||
При сохранении данных пака в мире следует использовать функцию
|
||||
```python
|
||||
pack.data_file(packid: str, filename: str) -> str
|
||||
```
|
||||
|
||||
Функция возвращает путь к файлу данных по типу: `world:data/packid/filename`
|
||||
|
||||
и создает недостающие директории в пути.
|
||||
|
||||
При использовании путей не соответствующим `data/{packid}/...` возможна потеря данных при перезаписи мира.
|
||||
@ -1,4 +1,5 @@
|
||||
devtools.console="key:grave-accent"
|
||||
chunks.reload="key:f5"
|
||||
movement.forward="key:w"
|
||||
movement.back="key:s"
|
||||
movement.left="key:a"
|
||||
1
res/config/builtins.list
Normal file
1
res/config/builtins.list
Normal file
@ -0,0 +1 @@
|
||||
base
|
||||
@ -1,15 +1,15 @@
|
||||
function on_random_update(x, y, z)
|
||||
local dirtid = block_index('base:dirt');
|
||||
if is_solid_at(x, y+1, z) then
|
||||
set_block(x, y, z, dirtid, 0)
|
||||
local dirtid = block.index('base:dirt');
|
||||
if block.is_solid_at(x, y+1, z) then
|
||||
block.set(x, y, z, dirtid, 0)
|
||||
else
|
||||
local grassblockid = block_index('base:grass_block')
|
||||
local grassblockid = block.index('base:grass_block')
|
||||
for lx=-1,1 do
|
||||
for ly=-1,1 do
|
||||
for lz=-1,1 do
|
||||
if get_block(x + lx, y + ly, z + lz) == dirtid then
|
||||
if not is_solid_at(x + lx, y + ly + 1, z + lz) then
|
||||
set_block(x + lx, y + ly, z + lz, grassblockid, 0)
|
||||
if block.get(x + lx, y + ly, z + lz) == dirtid then
|
||||
if not block.is_solid_at(x + lx, y + ly + 1, z + lz) then
|
||||
block.set(x + lx, y + ly, z + lz, grassblockid, 0)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
@ -35,6 +35,7 @@ function submit(text)
|
||||
add_to_history(text)
|
||||
setup_variables()
|
||||
|
||||
document.log.caret = -1
|
||||
local status, result = pcall(function() return console.execute(text) end)
|
||||
if result ~= nil then
|
||||
local prevtext = document.log.text
|
||||
|
||||
@ -9,10 +9,9 @@ function on_open()
|
||||
table.sort(names)
|
||||
|
||||
local panel = document.root
|
||||
for _,k in ipairs(names) do
|
||||
panel:add(string.format(
|
||||
"<button onclick=%q>%s</button>",
|
||||
string.format("core.set_setting('ui.language', %q) menu:back()", invlocales[k]), k
|
||||
for _,name in ipairs(names) do
|
||||
panel:add(gui.template(
|
||||
"language", {id=invlocales[name], name=name}
|
||||
))
|
||||
end
|
||||
panel:add("<button onclick='menu:back()'>@Back</button>")
|
||||
|
||||
@ -16,7 +16,7 @@ function on_open()
|
||||
refresh_sensitivity()
|
||||
|
||||
local panel = document.bindings_panel
|
||||
local bindings = core.get_bindings()
|
||||
local bindings = input.get_bindings()
|
||||
table.sort(bindings, function(a, b) return a > b end)
|
||||
for i,name in ipairs(bindings) do
|
||||
panel:add(gui.template("binding", {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
function create_setting(id, name, step, postfix)
|
||||
function create_setting(id, name, step, postfix, tooltip)
|
||||
local info = core.get_setting_info(id)
|
||||
postfix = postfix or ""
|
||||
tooltip = tooltip or ""
|
||||
document.root:add(gui.template("track_setting", {
|
||||
id=id,
|
||||
name=gui.str(name, "settings"),
|
||||
@ -8,7 +9,8 @@ function create_setting(id, name, step, postfix)
|
||||
min=info.min,
|
||||
max=info.max,
|
||||
step=step,
|
||||
postfix=postfix
|
||||
postfix=postfix,
|
||||
tooltip=tooltip
|
||||
}))
|
||||
update_setting(core.get_setting(id), id, name, postfix)
|
||||
end
|
||||
@ -24,10 +26,11 @@ function update_setting(x, id, name, postfix)
|
||||
)
|
||||
end
|
||||
|
||||
function create_checkbox(id, name)
|
||||
function create_checkbox(id, name, tooltip)
|
||||
tooltip = tooltip or ''
|
||||
document.root:add(string.format(
|
||||
"<checkbox consumer='function(x) core.set_setting(\"%s\", x) end' checked='%s'>%s</checkbox>",
|
||||
id, core.str_setting(id), gui.str(name, "settings")
|
||||
"<checkbox consumer='function(x) core.set_setting(\"%s\", x) end' checked='%s' tooltip='%s'>%s</checkbox>",
|
||||
id, core.str_setting(id), gui.str(tooltip, "settings"), gui.str(name, "settings")
|
||||
))
|
||||
end
|
||||
|
||||
@ -35,10 +38,10 @@ function on_open()
|
||||
create_setting("chunks.load-distance", "Load Distance", 1)
|
||||
create_setting("chunks.load-speed", "Load Speed", 1)
|
||||
create_setting("graphics.fog-curve", "Fog Curve", 0.1)
|
||||
create_setting("graphics.gamma", "Gamma", 0.05)
|
||||
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
|
||||
create_setting("camera.fov", "FOV", 1, "°")
|
||||
create_checkbox("display.fullscreen", "Fullscreen")
|
||||
create_checkbox("display.vsync", "V-Sync")
|
||||
create_checkbox("graphics.backlight", "Backlight")
|
||||
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
|
||||
create_checkbox("camera.shaking", "Camera Shaking")
|
||||
end
|
||||
|
||||
4
res/layouts/templates/language.xml
Normal file
4
res/layouts/templates/language.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<button
|
||||
onclick='core.set_setting("ui.language", "%{id}") menu:back()'>
|
||||
%{name}
|
||||
</button>
|
||||
@ -1,6 +1,6 @@
|
||||
<panel color='0' context='settings'>
|
||||
<label id='%{id}.L' margin='0,3,0,0'>%{name}: %{value}%{postfix}</label>
|
||||
<trackbar
|
||||
value='%{value}' min='%{min}' max='%{max}' step='%{step}'
|
||||
value='%{value}' min='%{min}' max='%{max}' step='%{step}' tooltip='%{tooltip}'
|
||||
consumer='function(x) update_setting(x, "%{id}", "%{name}", "%{postfix}") end'/>
|
||||
</panel>
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
print("WARNING: toml is replaced with built-in library, just remove 'require \"core:toml\"'")
|
||||
toml.serialize = toml.tostring
|
||||
toml.deserialize = toml.parse
|
||||
return toml
|
||||
|
||||
@ -12,47 +12,55 @@ error.pack-not-found=Не ўдалося знайсці пакет
|
||||
error.dependency-not-found=Выкарыстоўваная залежнасць не знойдзена
|
||||
pack.remove-confirm=Выдаліць увесь кантэнт які пастаўляецца пакам са свету (беззваротна)?
|
||||
|
||||
# Подсказки
|
||||
graphics.gamma.tooltip=Крывая яркасці асвятлення
|
||||
graphics.backlight.tooltip=Падсветка, якая прадухіляе поўную цемру
|
||||
|
||||
# Меню
|
||||
menu.New World=Новы Свет
|
||||
menu.Quit=Выхад
|
||||
menu.Continue=Працягнуть
|
||||
menu.Save and Quit to Menu=Захаваць і Выйсці ў Меню
|
||||
menu.missing-content=Адсутнічае Кантэнт!
|
||||
menu.Content Error=Памылка Кантэнту
|
||||
menu.Controls=Кіраванне
|
||||
menu.Back to Main Menu=Вярнуцца ў Меню
|
||||
menu.Settings=Налады
|
||||
menu.Content=Кантэнт
|
||||
menu.Apply=Ужыць
|
||||
menu.Audio=Гук
|
||||
menu.Back to Main Menu=Вярнуцца ў Меню
|
||||
menu.Content Error=Памылка Кантэнту
|
||||
menu.Content=Кантэнт
|
||||
menu.Continue=Працягнуть
|
||||
menu.Controls=Кіраванне
|
||||
menu.Graphics=Графіка
|
||||
menu.missing-content=Адсутнічае Кантэнт!
|
||||
menu.New World=Новы Свет
|
||||
menu.Page not found=Старонка не знойдзена
|
||||
menu.Quit=Выхад
|
||||
menu.Save and Quit to Menu=Захаваць і Выйсці ў Меню
|
||||
menu.Settings=Налады
|
||||
|
||||
world.Seed=Зерне
|
||||
world.Name=Назва
|
||||
|
||||
world.World generator=Генератар свету
|
||||
world.generators.default=Звычайны
|
||||
world.generators.flat=Плоскі
|
||||
menu.Create World=Стварыць Свет
|
||||
|
||||
world.Create World=Стварыць Свет
|
||||
world.convert-request=Ёсць змены ў індэксах! Канвертаваць свет?
|
||||
world.delete-confirm=Выдаліць свет незваротна?
|
||||
|
||||
# Настройки
|
||||
settings.Ambient=Фон
|
||||
settings.Backlight=Падсветка
|
||||
settings.Camera Shaking=Труска Камеры
|
||||
settings.Fog Curve=Крывая Туману
|
||||
settings.FOV=Поле Зроку
|
||||
settings.Fullscreen=Поўны экран
|
||||
settings.Gamma=Гама
|
||||
settings.Language=Мова
|
||||
settings.Load Distance=Дыстанцыя Загрузкі
|
||||
settings.Load Speed=Хуткасць Загрузкі
|
||||
settings.Fog Curve=Крывая Туману
|
||||
settings.Backlight=Падсветка
|
||||
settings.V-Sync=Вертыкальная Сінхранізацыя
|
||||
settings.Camera Shaking=Труска Камеры
|
||||
settings.Master Volume=Агульная Гучнасць
|
||||
settings.Mouse Sensitivity=Адчувальнасць Мышы
|
||||
settings.Music=Музыка
|
||||
settings.Regular Sounds=Звычайныя Гукі
|
||||
settings.UI Sounds=Гукі Інтэрфейсу
|
||||
settings.Ambient=Фон
|
||||
settings.Music=Музыка
|
||||
|
||||
settings.FOV=Поле Зроку
|
||||
settings.Mouse Sensitivity=Адчувальнасць Мышы
|
||||
settings.Language=Мова
|
||||
settings.V-Sync=Вертыкальная Сінхранізацыя
|
||||
|
||||
# Управление
|
||||
devtools.console=Кансоль
|
||||
movement.forward=Уперад
|
||||
movement.back=Назад
|
||||
movement.left=Улева
|
||||
|
||||
@ -8,7 +8,12 @@ world.delete-confirm=Do you want to delete world forever?
|
||||
world.generators.default=Default
|
||||
world.generators.flat=Flat
|
||||
|
||||
# Tooltips
|
||||
graphics.gamma.tooltip=Lighting brightness curve
|
||||
graphics.backlight.tooltip=Backlight to prevent total darkness
|
||||
|
||||
# Bindings
|
||||
chunks.reload=Reload Chunks
|
||||
devtools.console=Console
|
||||
movement.forward=Forward
|
||||
movement.back=Back
|
||||
|
||||
@ -12,6 +12,10 @@ error.pack-not-found=Не удалось найти пакет
|
||||
error.dependency-not-found=Используемая зависимость не найдена
|
||||
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
|
||||
|
||||
# Подсказки
|
||||
graphics.gamma.tooltip=Кривая яркости освещения
|
||||
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
|
||||
|
||||
# Меню
|
||||
menu.Apply=Применить
|
||||
menu.Audio=Звук
|
||||
@ -56,6 +60,7 @@ settings.UI Sounds=Звуки Интерфейса
|
||||
settings.V-Sync=Вертикальная Синхронизация
|
||||
|
||||
# Управление
|
||||
chunks.reload=Перезагрузить Чанки
|
||||
devtools.console=Консоль
|
||||
movement.forward=Вперёд
|
||||
movement.back=Назад
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
#define CODERS_TOML_HPP_
|
||||
|
||||
#include "../data/dynamic.hpp"
|
||||
#include "commons.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class SettingsHandler;
|
||||
|
||||
@ -212,6 +212,10 @@ void ContentLoader::loadBlock(Block& def, std::string name, fs::path file) {
|
||||
root->str("script-name", def.scriptName);
|
||||
root->str("ui-layout", def.uiLayout);
|
||||
root->num("inventory-size", def.inventorySize);
|
||||
root->num("tick-interval", def.tickInterval);
|
||||
if (def.tickInterval == 0) {
|
||||
def.tickInterval = 1;
|
||||
}
|
||||
|
||||
if (def.hidden && def.pickingItem == def.name+BLOCK_ITEM_SUFFIX) {
|
||||
def.pickingItem = CORE_EMPTY;
|
||||
|
||||
@ -25,11 +25,11 @@ public:
|
||||
enum class DependencyLevel {
|
||||
required, // dependency must be installed
|
||||
optional, // dependency will be installed if found
|
||||
weak, // dependency will not be installed automatically
|
||||
weak, // only affects packs order
|
||||
};
|
||||
|
||||
|
||||
/// @brief Content-pack that should be installed before the dependent
|
||||
/// @brief Content-pack that should be installed earlier the dependent
|
||||
struct DependencyPack {
|
||||
DependencyLevel level;
|
||||
std::string id;
|
||||
|
||||
@ -27,7 +27,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
|
||||
|
||||
auto bindsFile = paths->getResources()/fs::path("bindings.toml");
|
||||
if (fs::is_regular_file(bindsFile)) {
|
||||
Events::loadBindingsToml(
|
||||
Events::loadBindings(
|
||||
bindsFile.u8string(), files::read_string(bindsFile)
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ inline const std::string TEXTURE_NOTFOUND = "notfound";
|
||||
|
||||
// built-in bindings
|
||||
inline const std::string BIND_DEVTOOLS_CONSOLE = "devtools.console";
|
||||
inline const std::string BIND_CHUNKS_RELOAD = "chunks.reload";
|
||||
inline const std::string BIND_MOVE_FORWARD = "movement.forward";
|
||||
inline const std::string BIND_MOVE_BACK = "movement.back";
|
||||
inline const std::string BIND_MOVE_LEFT = "movement.left";
|
||||
|
||||
@ -214,7 +214,7 @@ void Map::flag(const std::string& key, bool& dst) const {
|
||||
}
|
||||
|
||||
Map& Map::put(std::string key, const Value& value) {
|
||||
values.emplace(key, value);
|
||||
values[key] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@ -44,13 +44,14 @@ void Logger::log(LogLevel level, const std::string& name, std::string message) {
|
||||
auto ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000;
|
||||
ss << " " << std::put_time(std::localtime(&tm), "%Y/%m/%d %T");
|
||||
ss << '.' << std::setfill('0') << std::setw(3) << ms.count();
|
||||
ss << utcOffset << " (" << std::setfill(' ') << std::setw(moduleLen) << name << ") ";
|
||||
ss << utcOffset << " [" << std::setfill(' ') << std::setw(moduleLen) << name << "] ";
|
||||
ss << message;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
auto string = ss.str();
|
||||
if (file.good()) {
|
||||
file << string << '\n';
|
||||
file.flush();
|
||||
}
|
||||
std::cout << string << std::endl;
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ inline void create_channel(Engine* engine, std::string name, NumberSetting& sett
|
||||
}
|
||||
engine->keepAlive(setting.observe([=](auto value) {
|
||||
audio::get_channel(name)->setVolume(value*value);
|
||||
}));
|
||||
}, true));
|
||||
}
|
||||
|
||||
Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths)
|
||||
@ -71,6 +71,7 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin
|
||||
if (Window::initialize(&this->settings.display)){
|
||||
throw initialize_error("could not initialize window");
|
||||
}
|
||||
loadControls();
|
||||
audio::initialize(settings.audio.enabled.get());
|
||||
create_channel(this, "master", settings.audio.volumeMaster);
|
||||
create_channel(this, "regular", settings.audio.volumeRegular);
|
||||
@ -94,6 +95,9 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin
|
||||
addWorldGenerators();
|
||||
|
||||
scripting::initialize(this);
|
||||
|
||||
auto resdir = paths->getResources();
|
||||
basePacks = files::read_list(resdir/fs::path("config/builtins.list"));
|
||||
}
|
||||
|
||||
void Engine::loadSettings() {
|
||||
@ -103,11 +107,22 @@ void Engine::loadSettings() {
|
||||
std::string text = files::read_string(settings_file);
|
||||
toml::parse(settingsHandler, settings_file.string(), text);
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::loadControls() {
|
||||
fs::path controls_file = paths->getControlsFile();
|
||||
if (fs::is_regular_file(controls_file)) {
|
||||
logger.info() << "loading controls";
|
||||
std::string text = files::read_string(controls_file);
|
||||
Events::loadBindings(controls_file.u8string(), text);
|
||||
} else {
|
||||
controls_file = paths->getControlsFileOld();
|
||||
if (fs::is_regular_file(controls_file)) {
|
||||
logger.info() << "loading controls (old)";
|
||||
std::string text = files::read_string(controls_file);
|
||||
Events::loadBindingsOld(controls_file.u8string(), text);
|
||||
fs::remove(controls_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,6 +267,16 @@ void Engine::loadAssets() {
|
||||
assets.reset(new_assets.release());
|
||||
}
|
||||
|
||||
static void load_configs(const fs::path& root) {
|
||||
auto configFolder = root/fs::path("config");
|
||||
auto bindsFile = configFolder/fs::path("bindings.toml");
|
||||
if (fs::is_regular_file(bindsFile)) {
|
||||
Events::loadBindings(
|
||||
bindsFile.u8string(), files::read_string(bindsFile)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::loadContent() {
|
||||
auto resdir = paths->getResources();
|
||||
ContentBuilder contentBuilder;
|
||||
@ -274,14 +299,10 @@ void Engine::loadContent() {
|
||||
ContentLoader loader(&pack);
|
||||
loader.load(contentBuilder);
|
||||
|
||||
auto configFolder = pack.folder/fs::path("config");
|
||||
auto bindsFile = configFolder/fs::path("bindings.toml");
|
||||
if (fs::is_regular_file(bindsFile)) {
|
||||
Events::loadBindingsToml(
|
||||
bindsFile.u8string(), files::read_string(bindsFile)
|
||||
);
|
||||
}
|
||||
}
|
||||
load_configs(pack.folder);
|
||||
}
|
||||
load_configs(paths->getResources());
|
||||
|
||||
content = contentBuilder.build();
|
||||
resPaths = std::make_unique<ResPaths>(resdir, resRoots);
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ class Engine : public util::ObjectsKeeper {
|
||||
std::recursive_mutex postRunnablesMutex;
|
||||
std::unique_ptr<EngineController> controller;
|
||||
std::unique_ptr<cmd::CommandsInterpreter> interpreter;
|
||||
std::vector<std::string> basePacks {"base"};
|
||||
std::vector<std::string> basePacks;
|
||||
|
||||
uint64_t frame = 0;
|
||||
double lastTime = 0.0;
|
||||
@ -65,6 +65,7 @@ class Engine : public util::ObjectsKeeper {
|
||||
|
||||
std::unique_ptr<gui::GUI> gui;
|
||||
|
||||
void loadControls();
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void updateTimers();
|
||||
|
||||
@ -364,7 +364,7 @@ void WorldRegions::put(Chunk* chunk){
|
||||
chunk->encode(), CHUNK_DATA_LEN, true);
|
||||
|
||||
// Writing lights cache
|
||||
if (doWriteLights && chunk->isLighted()) {
|
||||
if (doWriteLights && chunk->flags.lighted) {
|
||||
put(chunk->x, chunk->z, REGION_LAYER_LIGHTS,
|
||||
chunk->lightmap.encode(), LIGHTMAP_DATA_LEN, true);
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
#include "WorldFiles.hpp"
|
||||
|
||||
const fs::path SCREENSHOTS_FOLDER {"screenshots"};
|
||||
const fs::path CONTROLS_FILE {"controls.json"};
|
||||
const fs::path CONTROLS_FILE {"controls.toml"};
|
||||
const fs::path SETTINGS_FILE {"settings.toml"};
|
||||
|
||||
fs::path EnginePaths::getUserfiles() const {
|
||||
@ -60,6 +60,10 @@ fs::path EnginePaths::getControlsFile() {
|
||||
return userfiles/fs::path(CONTROLS_FILE);
|
||||
}
|
||||
|
||||
fs::path EnginePaths::getControlsFileOld() {
|
||||
return userfiles/fs::path("controls.json");
|
||||
}
|
||||
|
||||
fs::path EnginePaths::getSettingsFile() {
|
||||
return userfiles/fs::path(SETTINGS_FILE);
|
||||
}
|
||||
@ -135,7 +139,7 @@ static fs::path toCanonic(fs::path path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
fs::path EnginePaths::resolve(std::string path) {
|
||||
fs::path EnginePaths::resolve(std::string path, bool throwErr) {
|
||||
size_t separator = path.find(':');
|
||||
if (separator == std::string::npos) {
|
||||
throw files_access_error("no entry point specified");
|
||||
@ -161,7 +165,10 @@ fs::path EnginePaths::resolve(std::string path) {
|
||||
}
|
||||
}
|
||||
}
|
||||
throw files_access_error("unknown entry point '"+prefix+"'");
|
||||
if (throwErr) {
|
||||
throw files_access_error("unknown entry point '"+prefix+"'");
|
||||
}
|
||||
return fs::path(filename);
|
||||
}
|
||||
|
||||
ResPaths::ResPaths(fs::path mainRoot, std::vector<PathsRoot> roots)
|
||||
|
||||
@ -29,6 +29,7 @@ public:
|
||||
fs::path getWorldFolder();
|
||||
fs::path getWorldFolder(const std::string& name);
|
||||
fs::path getControlsFile();
|
||||
fs::path getControlsFileOld(); // TODO: remove in 0.22
|
||||
fs::path getSettingsFile();
|
||||
bool isWorldNameUsed(std::string name);
|
||||
|
||||
@ -39,7 +40,7 @@ public:
|
||||
|
||||
std::vector<fs::path> scanForWorlds();
|
||||
|
||||
fs::path resolve(std::string path);
|
||||
fs::path resolve(std::string path, bool throwErr=true);
|
||||
};
|
||||
|
||||
struct PathsRoot {
|
||||
|
||||
@ -20,7 +20,7 @@ namespace files {
|
||||
std::ifstream file;
|
||||
size_t filelength;
|
||||
public:
|
||||
rafile(std::filesystem::path filename);
|
||||
rafile(fs::path filename);
|
||||
|
||||
void seekg(std::streampos pos);
|
||||
void read(char* buffer, std::streamsize size);
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <bitset>
|
||||
|
||||
using namespace gui;
|
||||
|
||||
@ -72,15 +73,24 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
L" visible: "+std::to_wstring(level->chunks->visible);
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
auto* indices = level->content->getIndices();
|
||||
auto def = indices->getBlockDef(player->selectedVoxel.id);
|
||||
std::wstringstream stream;
|
||||
stream << std::hex << player->selectedVoxel.states;
|
||||
if (def) {
|
||||
stream << L" (" << util::str2wstr_utf8(def->name) << L")";
|
||||
stream << "r:" << player->selectedVoxel.state.rotation << " s:"
|
||||
<< player->selectedVoxel.state.segment << " u:"
|
||||
<< std::bitset<8>(player->selectedVoxel.state.userbits);
|
||||
if (player->selectedVoxel.id == BLOCK_VOID) {
|
||||
return std::wstring {L"block: -"};
|
||||
} else {
|
||||
return L"block: "+std::to_wstring(player->selectedVoxel.id)+
|
||||
L" "+stream.str();
|
||||
}
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
auto* indices = level->content->getIndices();
|
||||
if (auto def = indices->getBlockDef(player->selectedVoxel.id)) {
|
||||
return L"name: " + util::str2wstr_utf8(def->name);
|
||||
} else {
|
||||
return std::wstring {L"name: void"};
|
||||
}
|
||||
return L"block: "+std::to_wstring(player->selectedVoxel.id)+
|
||||
L" "+stream.str();
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
return L"seed: "+std::to_wstring(level->getWorld()->getSeed());
|
||||
@ -139,8 +149,8 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
}
|
||||
{
|
||||
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
|
||||
bar->setSupplier([=]() {return WorldRenderer::fog;});
|
||||
bar->setConsumer([=](double val) {WorldRenderer::fog = val;});
|
||||
bar->setSupplier([=]() {return level->getWorld()->fog;});
|
||||
bar->setConsumer([=](double val) {level->getWorld()->fog = val;});
|
||||
panel->add(bar);
|
||||
}
|
||||
{
|
||||
|
||||
@ -333,7 +333,7 @@ void Hud::openInventory(
|
||||
if (blockinv == nullptr) {
|
||||
blockinv = level->inventories->createVirtual(blockUI->getSlotsCount());
|
||||
}
|
||||
level->chunks->getChunkByVoxel(block.x, block.y, block.z)->setUnsaved(true);
|
||||
level->chunks->getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true;
|
||||
blockUI->bind(blockinv, content);
|
||||
blockPos = block;
|
||||
currentblockid = level->chunks->get(block.x, block.y, block.z)->id;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "LevelScreen.hpp"
|
||||
|
||||
#include "../../core_defs.hpp"
|
||||
#include "../hud.hpp"
|
||||
#include "../LevelFrontend.hpp"
|
||||
#include "../../audio/audio.hpp"
|
||||
@ -47,6 +48,9 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> level)
|
||||
keepAlive(settings.camera.fov.observe([=](double value) {
|
||||
controller->getPlayer()->camera->setFov(glm::radians(value));
|
||||
}));
|
||||
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
|
||||
controller->getLevel()->chunks->saveAndClear();
|
||||
}));
|
||||
|
||||
animator = std::make_unique<TextureAnimator>();
|
||||
animator->addAnimations(assets->getAnimations());
|
||||
@ -114,9 +118,6 @@ void LevelScreen::updateHotkeys() {
|
||||
if (Events::jpressed(keycode::F3)) {
|
||||
controller->getPlayer()->debug = !controller->getPlayer()->debug;
|
||||
}
|
||||
if (Events::jpressed(keycode::F5)) {
|
||||
controller->getLevel()->chunks->saveAndClear();
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::update(float delta) {
|
||||
|
||||
@ -13,19 +13,18 @@ Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
||||
{2}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
buffer = new float[capacity * B2D_VERTEX_SIZE];
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
buffer = std::make_unique<float[]>(capacity * B2D_VERTEX_SIZE);
|
||||
mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
|
||||
index = 0;
|
||||
|
||||
ubyte pixels[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888);
|
||||
_texture = nullptr;
|
||||
currentTexture = nullptr;
|
||||
}
|
||||
|
||||
Batch2D::~Batch2D(){
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void Batch2D::setPrimitive(DrawPrimitive primitive) {
|
||||
@ -37,7 +36,7 @@ void Batch2D::setPrimitive(DrawPrimitive primitive) {
|
||||
}
|
||||
|
||||
void Batch2D::begin(){
|
||||
_texture = nullptr;
|
||||
currentTexture = nullptr;
|
||||
blank->bind();
|
||||
color = glm::vec4(1.0f);
|
||||
primitive = DrawPrimitive::triangle;
|
||||
@ -73,14 +72,16 @@ void Batch2D::vertex(
|
||||
}
|
||||
|
||||
void Batch2D::texture(Texture* new_texture){
|
||||
if (_texture == new_texture)
|
||||
if (currentTexture == new_texture) {
|
||||
return;
|
||||
}
|
||||
flush();
|
||||
_texture = new_texture;
|
||||
if (new_texture == nullptr)
|
||||
currentTexture = new_texture;
|
||||
if (new_texture == nullptr) {
|
||||
blank->bind();
|
||||
else
|
||||
} else {
|
||||
new_texture->bind();
|
||||
}
|
||||
}
|
||||
|
||||
void Batch2D::untexture() {
|
||||
@ -327,7 +328,7 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index
|
||||
void Batch2D::flush() {
|
||||
if (index == 0)
|
||||
return;
|
||||
mesh->reload(buffer, index / B2D_VERTEX_SIZE);
|
||||
mesh->reload(buffer.get(), index / B2D_VERTEX_SIZE);
|
||||
mesh->draw(gl::to_glenum(primitive));
|
||||
index = 0;
|
||||
}
|
||||
|
||||
@ -12,13 +12,13 @@ class Texture;
|
||||
struct UVRegion;
|
||||
|
||||
class Batch2D {
|
||||
float* buffer;
|
||||
std::unique_ptr<float[]> buffer;
|
||||
size_t capacity;
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
std::unique_ptr<Texture> blank;
|
||||
size_t index;
|
||||
glm::vec4 color;
|
||||
Texture* _texture;
|
||||
Texture* currentTexture;
|
||||
DrawPrimitive primitive = DrawPrimitive::triangle;
|
||||
|
||||
void setPrimitive(DrawPrimitive primitive);
|
||||
|
||||
@ -14,28 +14,29 @@ Batch3D::Batch3D(size_t capacity)
|
||||
{3}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
buffer = new float[capacity * B3D_VERTEX_SIZE];
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
buffer = std::make_unique<float[]>(capacity * B3D_VERTEX_SIZE);
|
||||
mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
|
||||
index = 0;
|
||||
|
||||
ubyte pixels[] = {
|
||||
255, 255, 255, 255,
|
||||
};
|
||||
blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888);
|
||||
_texture = nullptr;
|
||||
currentTexture = nullptr;
|
||||
}
|
||||
|
||||
Batch3D::~Batch3D(){
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void Batch3D::begin(){
|
||||
_texture = nullptr;
|
||||
currentTexture = nullptr;
|
||||
blank->bind();
|
||||
}
|
||||
|
||||
void Batch3D::vertex(float x, float y, float z, float u, float v,
|
||||
float r, float g, float b, float a) {
|
||||
void Batch3D::vertex(
|
||||
float x, float y, float z, float u, float v,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
buffer[index++] = x;
|
||||
buffer[index++] = y;
|
||||
buffer[index++] = z;
|
||||
@ -46,8 +47,10 @@ void Batch3D::vertex(float x, float y, float z, float u, float v,
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
void Batch3D::vertex(glm::vec3 coord, float u, float v,
|
||||
float r, float g, float b, float a) {
|
||||
void Batch3D::vertex(
|
||||
glm::vec3 coord, float u, float v,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
buffer[index++] = coord.x;
|
||||
buffer[index++] = coord.y;
|
||||
buffer[index++] = coord.z;
|
||||
@ -58,9 +61,11 @@ void Batch3D::vertex(glm::vec3 coord, float u, float v,
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
void Batch3D::vertex(glm::vec3 point,
|
||||
glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a) {
|
||||
void Batch3D::vertex(
|
||||
glm::vec3 point,
|
||||
glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
buffer[index++] = point.x;
|
||||
buffer[index++] = point.y;
|
||||
buffer[index++] = point.z;
|
||||
@ -99,10 +104,10 @@ void Batch3D::face(
|
||||
}
|
||||
|
||||
void Batch3D::texture(Texture* new_texture){
|
||||
if (_texture == new_texture)
|
||||
if (currentTexture == new_texture)
|
||||
return;
|
||||
flush();
|
||||
_texture = new_texture;
|
||||
currentTexture = new_texture;
|
||||
if (new_texture == nullptr)
|
||||
blank->bind();
|
||||
else
|
||||
@ -166,7 +171,9 @@ inline glm::vec4 do_tint(float value) {
|
||||
return glm::vec4(value, value, value, 1.0f);
|
||||
}
|
||||
|
||||
void Batch3D::xSprite(float w, float h, const UVRegion& uv, const glm::vec4 tint, bool shading) {
|
||||
void Batch3D::xSprite(
|
||||
float w, float h, const UVRegion& uv, const glm::vec4 tint, bool shading
|
||||
) {
|
||||
face(
|
||||
glm::vec3(-w * 0.25f, 0.0f, -w * 0.25f),
|
||||
w, h,
|
||||
@ -244,13 +251,13 @@ void Batch3D::point(glm::vec3 coord, glm::vec4 tint) {
|
||||
}
|
||||
|
||||
void Batch3D::flush() {
|
||||
mesh->reload(buffer, index / B3D_VERTEX_SIZE);
|
||||
mesh->reload(buffer.get(), index / B3D_VERTEX_SIZE);
|
||||
mesh->draw();
|
||||
index = 0;
|
||||
}
|
||||
|
||||
void Batch3D::flushPoints() {
|
||||
mesh->reload(buffer, index / B3D_VERTEX_SIZE);
|
||||
mesh->reload(buffer.get(), index / B3D_VERTEX_SIZE);
|
||||
mesh->draw(GL_POINTS);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
@ -12,28 +12,35 @@ class Mesh;
|
||||
class Texture;
|
||||
|
||||
class Batch3D {
|
||||
float* buffer;
|
||||
std::unique_ptr<float[]> buffer;
|
||||
size_t capacity;
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
std::unique_ptr<Texture> blank;
|
||||
size_t index;
|
||||
|
||||
Texture* _texture;
|
||||
Texture* currentTexture;
|
||||
|
||||
void vertex(float x, float y, float z,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a);
|
||||
void vertex(glm::vec3 coord,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a);
|
||||
void vertex(glm::vec3 point, glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a);
|
||||
|
||||
void face(const glm::vec3& coord, float w, float h,
|
||||
void vertex(
|
||||
float x, float y, float z,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
void vertex(
|
||||
glm::vec3 coord,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
void vertex(
|
||||
glm::vec3 point, glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
void face(
|
||||
const glm::vec3& coord, float w, float h,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const UVRegion& region,
|
||||
const glm::vec4& tint);
|
||||
const glm::vec4& tint
|
||||
);
|
||||
|
||||
public:
|
||||
Batch3D(size_t capacity);
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#include "../../typedefs.hpp"
|
||||
#include "ImageData.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class Texture {
|
||||
|
||||
@ -44,13 +44,18 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
|
||||
break;
|
||||
case BlockModel::aabb:
|
||||
{
|
||||
glm::vec3 hitbox = glm::vec3();
|
||||
for (const auto& box : def->hitboxes)
|
||||
glm::vec3 hitbox {};
|
||||
for (const auto& box : def->hitboxes) {
|
||||
hitbox = glm::max(hitbox, box.size());
|
||||
offset.y += (1.0f - hitbox).y * 0.5f;
|
||||
}
|
||||
offset = glm::vec3(1, 1, 0.0f);
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
batch->blockCube(hitbox * glm::vec3(size * 0.63f),
|
||||
texfaces, glm::vec4(1.0f), !def->rt.emissive);
|
||||
batch->cube(
|
||||
-hitbox * glm::vec3(size * 0.63f)*0.5f * glm::vec3(1,1,-1),
|
||||
hitbox * glm::vec3(size * 0.63f),
|
||||
texfaces, glm::vec4(1.0f),
|
||||
!def->rt.emissive
|
||||
);
|
||||
}
|
||||
batch->flush();
|
||||
break;
|
||||
@ -138,7 +143,7 @@ std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
shader->uniformMatrix("u_projview",
|
||||
glm::ortho(0.0f, float(iconSize), 0.0f, float(iconSize),
|
||||
-100.0f, 100.0f) *
|
||||
glm::lookAt(glm::vec3(2, 2, 2),
|
||||
glm::lookAt(glm::vec3(0.57735f),
|
||||
glm::vec3(0.0f),
|
||||
glm::vec3(0, 1, 0)));
|
||||
|
||||
|
||||
@ -219,10 +219,13 @@ void BlocksRenderer::blockXSprite(int x, int y, int z,
|
||||
// HINT: texture faces order: {east, west, bottom, top, south, north}
|
||||
|
||||
/* AABB blocks render method */
|
||||
void BlocksRenderer::blockAABB(const ivec3& icoord,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const Block* block, ubyte rotation,
|
||||
bool lights) {
|
||||
void BlocksRenderer::blockAABB(
|
||||
const ivec3& icoord,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const Block* block,
|
||||
ubyte rotation,
|
||||
bool lights
|
||||
) {
|
||||
if (block->hitboxes.empty()) {
|
||||
return;
|
||||
}
|
||||
@ -301,11 +304,13 @@ void BlocksRenderer::blockCustomModel(const ivec3& icoord,
|
||||
}
|
||||
|
||||
/* Fastest solid shaded blocks render method */
|
||||
void BlocksRenderer::blockCube(int x, int y, int z,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const Block* block,
|
||||
ubyte states,
|
||||
bool lights) {
|
||||
void BlocksRenderer::blockCube(
|
||||
int x, int y, int z,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const Block* block,
|
||||
blockstate states,
|
||||
bool lights
|
||||
) {
|
||||
ubyte group = block->drawGroup;
|
||||
|
||||
vec3 X(1, 0, 0);
|
||||
@ -314,7 +319,7 @@ void BlocksRenderer::blockCube(int x, int y, int z,
|
||||
vec3 coord(x, y, z);
|
||||
if (block->rotatable) {
|
||||
auto& rotations = block->rotations;
|
||||
auto& orient = rotations.variants[states & BLOCK_ROT_MASK];
|
||||
auto& orient = rotations.variants[states.rotation];
|
||||
X = orient.axisX;
|
||||
Y = orient.axisY;
|
||||
Z = orient.axisZ;
|
||||
@ -423,7 +428,7 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
int z = (i / CHUNK_D) % CHUNK_W;
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
blockCube(x, y, z, texfaces, &def, vox.states, !def.rt.emissive);
|
||||
blockCube(x, y, z, texfaces, &def, vox.state, !def.rt.emissive);
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
blockXSprite(x, y, z, vec3(1.0f),
|
||||
@ -431,11 +436,11 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
break;
|
||||
}
|
||||
case BlockModel::aabb: {
|
||||
blockAABB(ivec3(x,y,z), texfaces, &def, vox.rotation(), !def.rt.emissive);
|
||||
blockAABB(ivec3(x,y,z), texfaces, &def, vox.state.rotation, !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
case BlockModel::custom: {
|
||||
blockCustomModel(ivec3(x, y, z), &def, vox.rotation(), !def.rt.emissive);
|
||||
blockCustomModel(ivec3(x, y, z), &def, vox.state.rotation, !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@ -70,16 +70,33 @@ class BlocksRenderer {
|
||||
const UVRegion& texreg,
|
||||
bool lights);
|
||||
|
||||
void blockCube(int x, int y, int z, const UVRegion(&faces)[6], const Block* block, ubyte states, bool lights);
|
||||
void blockAABB(const glm::ivec3& coord,
|
||||
const UVRegion(&faces)[6],
|
||||
const Block* block,
|
||||
ubyte rotation,
|
||||
bool lights);
|
||||
void blockXSprite(int x, int y, int z, const glm::vec3& size, const UVRegion& face1, const UVRegion& face2, float spread);
|
||||
void blockCustomModel(const glm::ivec3& icoord,
|
||||
const Block* block, ubyte rotation,
|
||||
bool lights);
|
||||
void blockCube(
|
||||
int x, int y, int z,
|
||||
const UVRegion(&faces)[6],
|
||||
const Block* block,
|
||||
blockstate states,
|
||||
bool lights
|
||||
);
|
||||
void blockAABB(
|
||||
const glm::ivec3& coord,
|
||||
const UVRegion(&faces)[6],
|
||||
const Block* block,
|
||||
ubyte rotation,
|
||||
bool lights
|
||||
);
|
||||
void blockXSprite(
|
||||
int x, int y, int z,
|
||||
const glm::vec3& size,
|
||||
const UVRegion& face1,
|
||||
const UVRegion& face2,
|
||||
float spread
|
||||
);
|
||||
void blockCustomModel(
|
||||
const glm::ivec3& icoord,
|
||||
const Block* block,
|
||||
ubyte rotation,
|
||||
bool lights
|
||||
);
|
||||
|
||||
bool isOpenForLight(int x, int y, int z) const;
|
||||
bool isOpen(int x, int y, int z, ubyte group) const;
|
||||
|
||||
@ -56,19 +56,16 @@ ChunksRenderer::~ChunksRenderer() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::render(std::shared_ptr<Chunk> chunk, bool important) {
|
||||
chunk->setModified(false);
|
||||
|
||||
chunk->flags.modified = false;
|
||||
if (important) {
|
||||
auto mesh = renderer->render(chunk.get(), level->chunksStorage.get());
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
glm::ivec2 key(chunk->x, chunk->z);
|
||||
if (inwork.find(key) != inwork.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inwork[key] = true;
|
||||
threadPool.enqueueJob(chunk);
|
||||
return nullptr;
|
||||
@ -86,7 +83,7 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(std::shared_ptr<Chunk> chunk,
|
||||
if (found == meshes.end()) {
|
||||
return render(chunk, important);
|
||||
}
|
||||
if (chunk->isModified()) {
|
||||
if (chunk->flags.modified) {
|
||||
render(chunk, important);
|
||||
}
|
||||
return found->second;
|
||||
|
||||
@ -37,6 +37,9 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
bool WorldRenderer::showChunkBorders = false;
|
||||
|
||||
WorldRenderer::WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player)
|
||||
@ -76,7 +79,7 @@ bool WorldRenderer::drawChunk(
|
||||
bool culling
|
||||
){
|
||||
auto chunk = level->chunks->chunks[index];
|
||||
if (!chunk->isLighted()) {
|
||||
if (!chunk->flags.lighted) {
|
||||
return false;
|
||||
}
|
||||
float distance = glm::distance(
|
||||
@ -160,6 +163,7 @@ void WorldRenderer::renderLevel(
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_proj", camera->getProjection());
|
||||
shader->uniformMatrix("u_view", camera->getView());
|
||||
shader->uniform1f("u_timer", Window::time());
|
||||
shader->uniform1f("u_gamma", settings.graphics.gamma.get());
|
||||
shader->uniform1f("u_fogFactor", fogFactor);
|
||||
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||
@ -194,19 +198,19 @@ void WorldRenderer::renderBlockSelection(Camera* camera, Shader* linesShader) {
|
||||
auto indices = level->content->getIndices();
|
||||
blockid_t id = PlayerController::selectedBlockId;
|
||||
auto block = indices->getBlockDef(id);
|
||||
const glm::vec3 pos = PlayerController::selectedBlockPosition;
|
||||
const glm::ivec3 pos = player->selectedBlockPosition;
|
||||
const glm::vec3 point = PlayerController::selectedPointPosition;
|
||||
const glm::vec3 norm = PlayerController::selectedBlockNormal;
|
||||
|
||||
const std::vector<AABB>& hitboxes = block->rotatable
|
||||
? block->rt.hitboxes[PlayerController::selectedBlockStates]
|
||||
? block->rt.hitboxes[PlayerController::selectedBlockRotation]
|
||||
: block->hitboxes;
|
||||
|
||||
linesShader->use();
|
||||
linesShader->uniformMatrix("u_projview", camera->getProjView());
|
||||
lineBatch->lineWidth(2.0f);
|
||||
for (auto& hitbox: hitboxes) {
|
||||
const glm::vec3 center = pos + hitbox.center();
|
||||
const glm::vec3 center = glm::vec3(pos) + hitbox.center();
|
||||
const glm::vec3 size = hitbox.size();
|
||||
lineBatch->box(center, size + glm::vec3(0.02), glm::vec4(0.f, 0.f, 0.f, 0.5f));
|
||||
if (player->debug) {
|
||||
@ -274,11 +278,12 @@ void WorldRenderer::draw(
|
||||
bool hudVisible,
|
||||
PostProcessing* postProcessing
|
||||
){
|
||||
auto world = level->getWorld();
|
||||
const Viewport& vp = pctx.getViewport();
|
||||
camera->aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
|
||||
|
||||
const EngineSettings& settings = engine->getSettings();
|
||||
skybox->refresh(pctx, level->getWorld()->daytime, 1.0f+fog*2.0f, 4);
|
||||
skybox->refresh(pctx, world->daytime, 1.0f+world->fog*2.0f, 4);
|
||||
|
||||
Assets* assets = engine->getAssets();
|
||||
Shader* linesShader = assets->getShader("lines");
|
||||
@ -291,7 +296,7 @@ void WorldRenderer::draw(
|
||||
Window::clearDepth();
|
||||
|
||||
// Drawing background sky plane
|
||||
skybox->draw(pctx, camera, assets, level->getWorld()->daytime, fog);
|
||||
skybox->draw(pctx, camera, assets, world->daytime, world->fog);
|
||||
|
||||
// Actually world render with depth buffer on
|
||||
{
|
||||
@ -355,5 +360,3 @@ void WorldRenderer::drawBorders(int sx, int sy, int sz, int ex, int ey, int ez)
|
||||
}
|
||||
lineBatch->render();
|
||||
}
|
||||
|
||||
float WorldRenderer::fog = 0.0f;
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
class Level;
|
||||
class Player;
|
||||
@ -76,8 +74,6 @@ public:
|
||||
Camera* camera,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
|
||||
static float fog;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -68,27 +68,25 @@ void GUI::updateTooltip(float delta) {
|
||||
if (hover == nullptr || !hover->isInside(Events::cursor)) {
|
||||
return resetTooltip();
|
||||
}
|
||||
float mouseDelta = glm::length(Events::delta);
|
||||
if (mouseDelta < 1.0f || tooltipTimer >= hover->getTooltipDelay()) {
|
||||
if (tooltipTimer + delta >= hover->getTooltipDelay()) {
|
||||
auto label = std::dynamic_pointer_cast<gui::Label>(get("tooltip.label"));
|
||||
const auto& text = hover->getTooltip();
|
||||
if (label && !text.empty()) {
|
||||
tooltip->setVisible(true);
|
||||
label->setText(langs::get(text));
|
||||
auto size = label->getSize()+glm::vec2(4.0f);
|
||||
auto pos = Events::cursor+glm::vec2(10.0f);
|
||||
auto rootSize = container->getSize();
|
||||
pos.x = glm::min(pos.x, rootSize.x-size.x);
|
||||
pos.y = glm::min(pos.y, rootSize.y-size.y);
|
||||
tooltip->setSize(size);
|
||||
tooltip->setPos(pos);
|
||||
}
|
||||
if (tooltipTimer + delta >= hover->getTooltipDelay()) {
|
||||
auto label = std::dynamic_pointer_cast<gui::Label>(get("tooltip.label"));
|
||||
const auto& text = hover->getTooltip();
|
||||
if (text.empty() && tooltip->isVisible()) {
|
||||
return resetTooltip();
|
||||
}
|
||||
if (label && !text.empty()) {
|
||||
tooltip->setVisible(true);
|
||||
label->setText(langs::get(text));
|
||||
auto size = label->getSize()+glm::vec2(4.0f);
|
||||
auto pos = Events::cursor+glm::vec2(10.0f);
|
||||
auto rootSize = container->getSize();
|
||||
pos.x = glm::min(pos.x, rootSize.x-size.x);
|
||||
pos.y = glm::min(pos.y, rootSize.y-size.y);
|
||||
tooltip->setSize(size);
|
||||
tooltip->setPos(pos);
|
||||
}
|
||||
tooltipTimer += delta;
|
||||
} else {
|
||||
resetTooltip();
|
||||
}
|
||||
tooltipTimer += delta;
|
||||
}
|
||||
|
||||
/// @brief Mouse related input and logic handling
|
||||
|
||||
@ -50,6 +50,11 @@ namespace gui {
|
||||
virtual bool isChecked() const {
|
||||
return checkbox->isChecked();
|
||||
}
|
||||
|
||||
virtual void setTooltip(const std::wstring& text) override {
|
||||
Panel::setTooltip(text);
|
||||
checkbox->setTooltip(text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -127,6 +127,14 @@ void Container::remove(std::shared_ptr<UINode> selected) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::remove(const std::string& id) {
|
||||
for (auto& node : nodes) {
|
||||
if (node->getId() == id) {
|
||||
return remove(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Container::clear() {
|
||||
for (auto node : nodes) {
|
||||
node->setParent(nullptr);
|
||||
|
||||
@ -26,6 +26,7 @@ namespace gui {
|
||||
virtual void add(std::shared_ptr<UINode> node, glm::vec2 pos);
|
||||
virtual void clear();
|
||||
virtual void remove(std::shared_ptr<UINode> node);
|
||||
virtual void remove(const std::string& id);
|
||||
virtual void scrolled(int value) override;
|
||||
virtual void setScrollable(bool flag);
|
||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||
|
||||
@ -337,6 +337,9 @@ inline std::wstring get_alphabet(wchar_t c) {
|
||||
}
|
||||
|
||||
void TextBox::tokenSelectAt(int index) {
|
||||
if (input.empty()) {
|
||||
return;
|
||||
}
|
||||
int left = index;
|
||||
int right = index;
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ void LightSolver::add(int x, int y, int z, int emission) {
|
||||
addqueue.push(lightentry {x, y, z, ubyte(emission)});
|
||||
|
||||
Chunk* chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
chunk->lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, emission);
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ void LightSolver::solve(){
|
||||
if (chunk) {
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
|
||||
ubyte light = chunk->lightmap.get(lx,y,lz, channel);
|
||||
if (light != 0 && light == entry.light-1){
|
||||
@ -96,7 +96,7 @@ void LightSolver::solve(){
|
||||
if (chunk) {
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
|
||||
ubyte light = chunk->lightmap.get(lx, y, lz, channel);
|
||||
voxel& v = chunk->voxels[vox_index(lx, y, lz)];
|
||||
|
||||
@ -53,8 +53,8 @@ int Clock::getTickId() const {
|
||||
|
||||
BlocksController::BlocksController(Level* level, uint padding)
|
||||
: level(level),
|
||||
chunks(level->chunks.get()),
|
||||
lighting(level->lighting.get()),
|
||||
chunks(level->chunks.get()),
|
||||
lighting(level->lighting.get()),
|
||||
randTickClock(20, 3),
|
||||
blocksTickClock(20, 1),
|
||||
worldTickClock(20, 1),
|
||||
@ -71,7 +71,7 @@ void BlocksController::updateSides(int x, int y, int z) {
|
||||
}
|
||||
|
||||
void BlocksController::breakBlock(Player* player, const Block* def, int x, int y, int z) {
|
||||
chunks->set(x,y,z, 0, 0);
|
||||
chunks->set(x,y,z, 0, {});
|
||||
lighting->onBlockSet(x,y,z, 0);
|
||||
if (def->rt.funcsset.onbroken) {
|
||||
scripting::on_block_broken(player, def, x, y, z);
|
||||
@ -113,8 +113,9 @@ void BlocksController::onBlocksTick(int tickid, int parts) {
|
||||
if ((id + tickid) % parts != 0)
|
||||
continue;
|
||||
auto def = indices->getBlockDef(id);
|
||||
if (def->rt.funcsset.onblockstick) {
|
||||
scripting::on_blocks_tick(def, tickRate);
|
||||
auto interval = def->tickInterval;
|
||||
if (def->rt.funcsset.onblockstick && tickid / parts % interval == 0) {
|
||||
scripting::on_blocks_tick(def, tickRate / interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,7 +133,7 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
if ((index + tickid) % parts != 0)
|
||||
continue;
|
||||
auto& chunk = chunks->chunks[index];
|
||||
if (chunk == nullptr || !chunk->isLighted())
|
||||
if (chunk == nullptr || !chunk->flags.lighted)
|
||||
continue;
|
||||
for (int s = 0; s < segments; s++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@ -150,49 +151,49 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
auto inv = chunk->getBlockInventory(lx, y, lz);
|
||||
if (inv == nullptr) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
auto inv = chunk->getBlockInventory(lx, y, lz);
|
||||
if (inv == nullptr) {
|
||||
auto indices = level->content->getIndices();
|
||||
auto def = indices->getBlockDef(chunk->voxels[vox_index(lx, y, lz)].id);
|
||||
int invsize = def->inventorySize;
|
||||
if (invsize == 0) {
|
||||
return 0;
|
||||
}
|
||||
inv = level->inventories->create(invsize);
|
||||
inv = level->inventories->create(invsize);
|
||||
chunk->addBlockInventory(inv, lx, y, lz);
|
||||
}
|
||||
}
|
||||
return inv->getId();
|
||||
}
|
||||
|
||||
void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
if (invid <= 0) {
|
||||
throw std::runtime_error("unable to bind virtual inventory");
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
chunk->addBlockInventory(level->inventories->get(invid), lx, y, lz);
|
||||
}
|
||||
|
||||
void BlocksController::unbindInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
chunk->removeBlockInventory(lx, y, lz);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ bool ChunksController::loadVisible(){
|
||||
int index = z * w + x;
|
||||
auto& chunk = chunks->chunks[index];
|
||||
if (chunk != nullptr){
|
||||
if (chunk->isLoaded() && !chunk->isLighted()) {
|
||||
if (chunk->flags.loaded && !chunk->flags.lighted) {
|
||||
if (buildLights(chunk)) {
|
||||
return true;
|
||||
}
|
||||
@ -99,12 +99,12 @@ bool ChunksController::buildLights(std::shared_ptr<Chunk> chunk) {
|
||||
}
|
||||
}
|
||||
if (surrounding == MIN_SURROUNDING) {
|
||||
bool lightsCache = chunk->isLoadedLights();
|
||||
bool lightsCache = chunk->flags.loadedLights;
|
||||
if (!lightsCache) {
|
||||
lighting->buildSkyLight(chunk->x, chunk->z);
|
||||
}
|
||||
lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
||||
chunk->setLighted(true);
|
||||
chunk->flags.lighted = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -114,20 +114,20 @@ void ChunksController::createChunk(int x, int z) {
|
||||
auto chunk = level->chunksStorage->create(x, z);
|
||||
chunks->putChunk(chunk);
|
||||
|
||||
if (!chunk->isLoaded()) {
|
||||
if (!chunk->flags.loaded) {
|
||||
generator->generate(
|
||||
chunk->voxels, x, z,
|
||||
level->getWorld()->getSeed()
|
||||
);
|
||||
chunk->setUnsaved(true);
|
||||
chunk->flags.unsaved = true;
|
||||
}
|
||||
chunk->updateHeights();
|
||||
|
||||
if (!chunk->isLoadedLights()) {
|
||||
if (!chunk->flags.loadedLights) {
|
||||
Lighting::prebuildSkyLight(
|
||||
chunk.get(), level->content->getIndices()
|
||||
);
|
||||
}
|
||||
chunk->setLoaded(true);
|
||||
chunk->setReady(true);
|
||||
chunk->flags.loaded = true;
|
||||
chunk->flags.ready = true;
|
||||
}
|
||||
|
||||
@ -166,11 +166,10 @@ void CameraControl::update(const PlayerInput& input, float delta, Chunks* chunks
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 PlayerController::selectedBlockPosition;
|
||||
glm::vec3 PlayerController::selectedPointPosition;
|
||||
glm::ivec3 PlayerController::selectedBlockNormal;
|
||||
int PlayerController::selectedBlockId = -1;
|
||||
int PlayerController::selectedBlockStates = 0;
|
||||
int PlayerController::selectedBlockRotation = 0;
|
||||
|
||||
PlayerController::PlayerController(
|
||||
Level* level,
|
||||
@ -250,7 +249,7 @@ void PlayerController::update(float delta, bool input, bool pause) {
|
||||
updateInteraction();
|
||||
} else {
|
||||
selectedBlockId = -1;
|
||||
selectedBlockStates = 0;
|
||||
selectedBlockRotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,11 +361,11 @@ void PlayerController::updateInteraction(){
|
||||
maxDistance,
|
||||
end, norm, iend
|
||||
);
|
||||
if (vox != nullptr){
|
||||
if (vox != nullptr) {
|
||||
player->selectedVoxel = *vox;
|
||||
selectedBlockId = vox->id;
|
||||
selectedBlockStates = vox->states;
|
||||
selectedBlockPosition = iend;
|
||||
selectedBlockRotation = vox->state.rotation;
|
||||
player->selectedBlockPosition = iend;
|
||||
selectedPointPosition = end;
|
||||
selectedBlockNormal = norm;
|
||||
int x = iend.x;
|
||||
@ -374,7 +373,8 @@ void PlayerController::updateInteraction(){
|
||||
int z = iend.z;
|
||||
|
||||
Block* def = indices->getBlockDef(item->rt.placingBlock);
|
||||
uint8_t states = determine_rotation(def, norm, camera->dir);
|
||||
blockstate state {};
|
||||
state.rotation = determine_rotation(def, norm, camera->dir);
|
||||
|
||||
if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) {
|
||||
if (scripting::on_item_break_block(player.get(), item, x, y, z))
|
||||
@ -411,13 +411,13 @@ void PlayerController::updateInteraction(){
|
||||
z = (iend.z)+(norm.z);
|
||||
} else {
|
||||
if (def->rotations.name == "pipe") {
|
||||
states = BLOCK_DIR_UP;
|
||||
state.rotation = BLOCK_DIR_UP;
|
||||
}
|
||||
}
|
||||
vox = chunks->get(x, y, z);
|
||||
blockid_t chosenBlock = def->rt.id;
|
||||
if (vox && (target = indices->getBlockDef(vox->id))->replaceable) {
|
||||
if (!level->physics->isBlockInside(x,y,z,def,states, player->hitbox.get())
|
||||
if (!level->physics->isBlockInside(x,y,z,def,state, player->hitbox.get())
|
||||
|| !def->obstacle){
|
||||
if (def->grounded && !chunks->isSolidBlock(x, y-1, z)) {
|
||||
chosenBlock = 0;
|
||||
@ -427,7 +427,7 @@ void PlayerController::updateInteraction(){
|
||||
glm::ivec3(x, y, z), def,
|
||||
BlockInteraction::placing
|
||||
);
|
||||
chunks->set(x, y, z, chosenBlock, states);
|
||||
chunks->set(x, y, z, chosenBlock, state);
|
||||
lighting->onBlockSet(x,y,z, chosenBlock);
|
||||
if (def->rt.funcsset.onplaced) {
|
||||
scripting::on_block_placed(player.get(), def, x, y, z);
|
||||
@ -442,12 +442,13 @@ void PlayerController::updateInteraction(){
|
||||
}
|
||||
} else {
|
||||
selectedBlockId = -1;
|
||||
selectedBlockStates = 0;
|
||||
}
|
||||
if (rclick) {
|
||||
if (item->rt.funcsset.on_use) {
|
||||
scripting::on_item_use(player.get(), item);
|
||||
}
|
||||
selectedBlockRotation = 0;
|
||||
player->selectedVoxel.id = BLOCK_VOID;
|
||||
if (rclick) {
|
||||
if (item->rt.funcsset.on_use) {
|
||||
scripting::on_item_use(player.get(), item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -77,11 +77,10 @@ class PlayerController {
|
||||
void onFootstep();
|
||||
void updateFootsteps(float delta);
|
||||
public:
|
||||
static glm::vec3 selectedBlockPosition;
|
||||
static glm::ivec3 selectedBlockNormal;
|
||||
static glm::vec3 selectedPointPosition;
|
||||
static int selectedBlockId;
|
||||
static int selectedBlockStates;
|
||||
static int selectedBlockRotation;
|
||||
|
||||
PlayerController(
|
||||
Level* level,
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
int l_block_name(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua_Integer id = lua_tointeger(L, 1);
|
||||
if (id < 0 || size_t(id) >= indices->countBlockDefs()) {
|
||||
if (static_cast<size_t>(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
@ -27,7 +27,7 @@ int l_block_name(lua_State* L) {
|
||||
int l_block_material(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua_Integer id = lua_tointeger(L, 1);
|
||||
if (id < 0 || size_t(id) >= indices->countBlockDefs()) {
|
||||
if (static_cast<size_t>(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
@ -60,18 +60,19 @@ int l_set_block(lua_State* L) {
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
lua_Integer id = lua_tointeger(L, 4);
|
||||
lua_Integer states = lua_tointeger(L, 5);
|
||||
lua_Integer state = lua_tointeger(L, 5);
|
||||
bool noupdate = lua_toboolean(L, 6);
|
||||
if (id < 0 || size_t(id) >= scripting::indices->countBlockDefs()) {
|
||||
if (static_cast<size_t>(id) >= scripting::indices->countBlockDefs()) {
|
||||
return 0;
|
||||
}
|
||||
if (!scripting::level->chunks->get(x, y, z)) {
|
||||
return 0;
|
||||
}
|
||||
scripting::level->chunks->set(x, y, z, id, states);
|
||||
scripting::level->chunks->set(x, y, z, id, int2blockstate(state));
|
||||
scripting::level->lighting->onBlockSet(x,y,z, id);
|
||||
if (!noupdate)
|
||||
if (!noupdate) {
|
||||
scripting::blocks->updateSides(x, y, z);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -97,7 +98,7 @@ int l_get_block_x(lua_State* L) {
|
||||
if (!def->rotatable) {
|
||||
return lua::pushivec3(L, 1, 0, 0);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
const CoordSystem& rot = def->rotations.variants[vox->state.rotation];
|
||||
return lua::pushivec3(L, rot.axisX.x, rot.axisX.y, rot.axisX.z);
|
||||
}
|
||||
}
|
||||
@ -114,7 +115,7 @@ int l_get_block_y(lua_State* L) {
|
||||
if (!def->rotatable) {
|
||||
return lua::pushivec3(L, 0, 1, 0);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
const CoordSystem& rot = def->rotations.variants[vox->state.rotation];
|
||||
return lua::pushivec3(L, rot.axisY.x, rot.axisY.y, rot.axisY.z);
|
||||
}
|
||||
}
|
||||
@ -131,7 +132,7 @@ int l_get_block_z(lua_State* L) {
|
||||
if (!def->rotatable) {
|
||||
return lua::pushivec3(L, 0, 0, 1);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
const CoordSystem& rot = def->rotations.variants[vox->state.rotation];
|
||||
return lua::pushivec3(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z);
|
||||
}
|
||||
}
|
||||
@ -141,7 +142,7 @@ int l_get_block_rotation(lua_State* L) {
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
int rotation = vox == nullptr ? 0 : vox->rotation();
|
||||
int rotation = vox == nullptr ? 0 : vox->state.rotation;
|
||||
lua_pushinteger(L, rotation);
|
||||
return 1;
|
||||
}
|
||||
@ -155,8 +156,8 @@ int l_set_block_rotation(lua_State* L) {
|
||||
if (vox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
vox->setRotation(value);
|
||||
scripting::level->chunks->getChunkByVoxel(x, y, z)->setModified(true);
|
||||
vox->state.rotation = value;
|
||||
scripting::level->chunks->getChunkByVoxel(x, y, z)->setModifiedAndUnsaved();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -165,7 +166,7 @@ int l_get_block_states(lua_State* L) {
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
int states = vox == nullptr ? 0 : vox->states;
|
||||
int states = vox == nullptr ? 0 : blockstate2int(vox->state);
|
||||
lua_pushinteger(L, states);
|
||||
return 1;
|
||||
}
|
||||
@ -181,8 +182,8 @@ int l_set_block_states(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
vox->states = states;
|
||||
chunk->setModified(true);
|
||||
vox->state = int2blockstate(states);
|
||||
chunk->setModifiedAndUnsaved();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -199,7 +200,7 @@ int l_get_block_user_bits(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
uint mask = ((1 << bits) - 1) << offset;
|
||||
uint data = (vox->states & mask) >> offset;
|
||||
uint data = (blockstate2int(vox->state) & mask) >> offset;
|
||||
lua_pushinteger(L, data);
|
||||
return 1;
|
||||
}
|
||||
@ -208,18 +209,23 @@ int l_set_block_user_bits(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
lua_Integer offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||
lua_Integer offset = lua_tointeger(L, 4);
|
||||
lua_Integer bits = lua_tointeger(L, 5);
|
||||
|
||||
size_t mask = ((1 << bits) - 1) << offset;
|
||||
lua_Integer value = (lua_tointeger(L, 6) << offset) & mask;
|
||||
|
||||
Chunk* chunk = scripting::level->chunks->getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
vox->states = (vox->states & (~mask)) | value;
|
||||
return 0;
|
||||
vox->state.userbits = (vox->state.userbits & (~mask)) | value;
|
||||
chunk->setModifiedAndUnsaved();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l_is_replaceable_at(lua_State* L) {
|
||||
@ -231,10 +237,22 @@ int l_is_replaceable_at(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_block_caption(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua_Integer id = lua_tointeger(L, 1);
|
||||
if (static_cast<size_t>(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
lua_pushstring(L, def->caption.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg blocklib [] = {
|
||||
{"index", lua_wrap_errors<l_block_index>},
|
||||
{"name", lua_wrap_errors<l_block_name>},
|
||||
{"material", lua_wrap_errors<l_block_material>},
|
||||
{"caption", lua_wrap_errors<l_block_caption>},
|
||||
{"defs_count", lua_wrap_errors<l_blocks_count>},
|
||||
{"is_solid_at", lua_wrap_errors<l_is_solid_at>},
|
||||
{"is_replaceable_at", lua_wrap_errors<l_is_replaceable_at>},
|
||||
|
||||
@ -100,19 +100,6 @@ static int l_reconfig_packs(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_bindings(lua_State* L) {
|
||||
auto& bindings = Events::bindings;
|
||||
lua_createtable(L, bindings.size(), 0);
|
||||
|
||||
int i = 0;
|
||||
for (auto& entry : bindings) {
|
||||
lua_pushstring(L, entry.first.c_str());
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
i++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_get_setting(lua_State* L) {
|
||||
auto name = lua_tostring(L, 1);
|
||||
const auto value = scripting::engine->getSettingsHandler().getValue(name);
|
||||
@ -186,7 +173,6 @@ const luaL_Reg corelib [] = {
|
||||
{"close_world", lua_wrap_errors<l_close_world>},
|
||||
{"delete_world", lua_wrap_errors<l_delete_world>},
|
||||
{"reconfig_packs", lua_wrap_errors<l_reconfig_packs>},
|
||||
{"get_bindings", lua_wrap_errors<l_get_bindings>},
|
||||
{"get_setting", lua_wrap_errors<l_get_setting>},
|
||||
{"set_setting", lua_wrap_errors<l_set_setting>},
|
||||
{"str_setting", lua_wrap_errors<l_str_setting>},
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "lua_commons.hpp"
|
||||
#include "api_lua.hpp"
|
||||
#include "LuaState.hpp"
|
||||
#include "../scripting.hpp"
|
||||
#include "../../../engine.hpp"
|
||||
#include "../../../files/files.hpp"
|
||||
@ -11,24 +12,41 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static fs::path resolve_path(lua_State*, const std::string& path) {
|
||||
return scripting::engine->getPaths()->resolve(path);
|
||||
namespace scripting {
|
||||
extern lua::LuaState* state;
|
||||
}
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static fs::path resolve_path(const std::string& path) {
|
||||
return engine->getPaths()->resolve(path);
|
||||
}
|
||||
|
||||
static fs::path resolve_path_soft(const std::string& path) {
|
||||
if (path.find(':') == std::string::npos) {
|
||||
return path;
|
||||
}
|
||||
return engine->getPaths()->resolve(path, false);
|
||||
}
|
||||
|
||||
static int l_file_find(lua_State* L) {
|
||||
std::string path = lua_tostring(L, 1);
|
||||
lua_pushstring(L, scripting::engine->getResPaths()->findRaw(path).c_str());
|
||||
return 1;
|
||||
std::string path = state->requireString(1);
|
||||
try {
|
||||
lua_pushstring(L, engine->getResPaths()->findRaw(path).c_str());
|
||||
return 1;
|
||||
} catch (const std::runtime_error& err) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int l_file_resolve(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
lua_pushstring(L, path.u8string().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_read(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
if (fs::is_regular_file(path)) {
|
||||
lua_pushstring(L, files::read_string(path).c_str());
|
||||
return 1;
|
||||
@ -36,55 +54,50 @@ static int l_file_read(lua_State* L) {
|
||||
throw std::runtime_error("file does not exists "+util::quote(path.u8string()));
|
||||
}
|
||||
|
||||
static int l_file_write(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
const char* text = lua_tostring(L, 2);
|
||||
static int l_file_write(lua_State*) {
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
auto text = state->requireString(2);
|
||||
files::write_string(path, text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_remove(lua_State* L) {
|
||||
std::string rawpath = lua_tostring(L, 1);
|
||||
fs::path path = resolve_path(L, rawpath);
|
||||
static int l_file_remove(lua_State*) {
|
||||
std::string rawpath = state->requireString(1);
|
||||
fs::path path = resolve_path(rawpath);
|
||||
auto entryPoint = rawpath.substr(0, rawpath.find(':'));
|
||||
if (entryPoint != "world") {
|
||||
throw std::runtime_error("access denied");
|
||||
}
|
||||
lua_pushboolean(L, fs::remove(path));
|
||||
return 1;
|
||||
return state->pushboolean(fs::remove(path));
|
||||
}
|
||||
|
||||
static int l_file_remove_tree(lua_State* L) {
|
||||
std::string rawpath = lua_tostring(L, 1);
|
||||
fs::path path = resolve_path(L, rawpath);
|
||||
static int l_file_remove_tree(lua_State*) {
|
||||
std::string rawpath = state->requireString(1);
|
||||
fs::path path = resolve_path(rawpath);
|
||||
auto entryPoint = rawpath.substr(0, rawpath.find(':'));
|
||||
if (entryPoint != "world") {
|
||||
throw std::runtime_error("access denied");
|
||||
}
|
||||
lua_pushinteger(L, fs::remove_all(path));
|
||||
return 1;
|
||||
return state->pushinteger(fs::remove_all(path));
|
||||
}
|
||||
|
||||
static int l_file_exists(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::exists(path));
|
||||
return 1;
|
||||
static int l_file_exists(lua_State*) {
|
||||
fs::path path = resolve_path_soft(state->requireString(1));
|
||||
return state->pushboolean(fs::exists(path));
|
||||
}
|
||||
|
||||
static int l_file_isfile(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::is_regular_file(path));
|
||||
return 1;
|
||||
static int l_file_isfile(lua_State*) {
|
||||
fs::path path = resolve_path_soft(state->requireString(1));
|
||||
return state->pushboolean(fs::is_regular_file(path));
|
||||
}
|
||||
|
||||
static int l_file_isdir(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::is_directory(path));
|
||||
return 1;
|
||||
static int l_file_isdir(lua_State*) {
|
||||
fs::path path = resolve_path_soft(state->requireString(1));
|
||||
return state->pushboolean(fs::is_directory(path));
|
||||
}
|
||||
|
||||
static int l_file_length(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
if (fs::exists(path)){
|
||||
lua_pushinteger(L, fs::file_size(path));
|
||||
} else {
|
||||
@ -94,19 +107,19 @@ static int l_file_length(lua_State* L) {
|
||||
}
|
||||
|
||||
static int l_file_mkdir(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
lua_pushboolean(L, fs::create_directory(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_mkdirs(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
lua_pushboolean(L, fs::create_directories(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_read_bytes(lua_State* L) {
|
||||
fs::path path = resolve_path(L, lua_tostring(L, 1));
|
||||
fs::path path = resolve_path(state->requireString(1));
|
||||
if (fs::is_regular_file(path)) {
|
||||
size_t length = static_cast<size_t>(fs::file_size(path));
|
||||
|
||||
@ -148,7 +161,7 @@ static int l_file_write_bytes(lua_State* L) {
|
||||
throw std::runtime_error("string expected");
|
||||
}
|
||||
|
||||
fs::path path = resolve_path(L, lua_tostring(L, pathIndex));
|
||||
fs::path path = resolve_path(state->requireString(pathIndex));
|
||||
|
||||
std::vector<ubyte> bytes;
|
||||
|
||||
@ -163,7 +176,7 @@ static int l_file_write_bytes(lua_State* L) {
|
||||
}
|
||||
|
||||
static int l_file_list_all_res(lua_State* L, const std::string& path) {
|
||||
auto files = scripting::engine->getResPaths()->listdirRaw(path);
|
||||
auto files = engine->getResPaths()->listdirRaw(path);
|
||||
lua_createtable(L, files.size(), 0);
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
lua_pushstring(L, files[i].c_str());
|
||||
@ -173,11 +186,11 @@ static int l_file_list_all_res(lua_State* L, const std::string& path) {
|
||||
}
|
||||
|
||||
static int l_file_list(lua_State* L) {
|
||||
std::string dirname = lua_tostring(L, 1);
|
||||
std::string dirname = state->requireString(1);
|
||||
if (dirname.find(':') == std::string::npos) {
|
||||
return l_file_list_all_res(L, dirname);
|
||||
}
|
||||
fs::path path = resolve_path(L, dirname);
|
||||
fs::path path = resolve_path(dirname);
|
||||
if (!fs::is_directory(path)) {
|
||||
throw std::runtime_error(util::quote(path.u8string())+" is not a directory");
|
||||
}
|
||||
|
||||
@ -93,6 +93,18 @@ static int l_container_add(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_node_destruct(lua_State* L) {
|
||||
auto docnode = getDocumentNode(L);
|
||||
auto node = std::dynamic_pointer_cast<Container>(docnode.node);
|
||||
engine->getGUI()->postRunnable([node]() {
|
||||
auto parent = node->getParent();
|
||||
if (auto container = dynamic_cast<Container*>(parent)) {
|
||||
container->remove(node);
|
||||
}
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_container_clear(lua_State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
|
||||
@ -101,6 +113,17 @@ static int l_container_clear(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_container_set_interval(lua_State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
auto interval = state->tointeger(2) / 1000.0f;
|
||||
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
|
||||
state->pushvalue(3);
|
||||
auto runnable = state->createRunnable();
|
||||
container->listenInterval(interval, runnable);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_move_into(lua_State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
auto dest = getDocumentNode(L, 2);
|
||||
@ -243,14 +266,25 @@ static int p_get_src(UINode* node) {
|
||||
|
||||
static int p_get_add(UINode* node) {
|
||||
if (dynamic_cast<Container*>(node)) {
|
||||
return state->pushcfunction(l_container_add);
|
||||
return state->pushcfunction(lua_wrap_errors<l_container_add>);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_destruct(UINode*) {
|
||||
return state->pushcfunction(lua_wrap_errors<l_node_destruct>);
|
||||
}
|
||||
|
||||
static int p_get_clear(UINode* node) {
|
||||
if (dynamic_cast<Container*>(node)) {
|
||||
return state->pushcfunction(l_container_clear);
|
||||
return state->pushcfunction(lua_wrap_errors<l_container_clear>);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_set_interval(UINode* node) {
|
||||
if (dynamic_cast<Container*>(node)) {
|
||||
return state->pushcfunction(lua_wrap_errors<l_container_set_interval>);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -313,10 +347,12 @@ static int l_gui_getattr(lua_State* L) {
|
||||
{"size", p_get_size},
|
||||
{"interactive", p_is_interactive},
|
||||
{"visible", p_is_visible},
|
||||
{"enabled", p_is_enabled},
|
||||
{"enabled", p_is_enabled},
|
||||
{"move_into", p_move_into},
|
||||
{"add", p_get_add},
|
||||
{"destruct", p_get_destruct},
|
||||
{"clear", p_get_clear},
|
||||
{"setInterval", p_set_interval},
|
||||
{"placeholder", p_get_placeholder},
|
||||
{"valid", p_is_valid},
|
||||
{"caret", p_get_caret},
|
||||
|
||||
@ -25,16 +25,23 @@ static int l_keycode(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_add_callback(lua_State* L) {
|
||||
static int l_mousecode(lua_State* L) {
|
||||
const char* name = state->requireString(1);
|
||||
lua_pushinteger(L, static_cast<int>(input_util::mousecode_from(name)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_add_callback(lua_State*) {
|
||||
auto bindname = state->requireString(1);
|
||||
const auto& bind = Events::bindings.find(bindname);
|
||||
if (bind == Events::bindings.end()) {
|
||||
throw std::runtime_error("unknown binding "+util::quote(bindname));
|
||||
}
|
||||
state->pushvalue(2);
|
||||
runnable actual_callback = state->createRunnable();
|
||||
runnable callback = [=]() {
|
||||
if (!scripting::engine->getGUI()->isFocusCaught()) {
|
||||
state->createRunnable();
|
||||
actual_callback();
|
||||
}
|
||||
};
|
||||
if (hud) {
|
||||
@ -49,10 +56,25 @@ static int l_get_mouse_pos(lua_State* L) {
|
||||
return lua::pushvec2_arr(L, Events::cursor);
|
||||
}
|
||||
|
||||
static int l_get_bindings(lua_State* L) {
|
||||
auto& bindings = Events::bindings;
|
||||
lua_createtable(L, bindings.size(), 0);
|
||||
|
||||
int i = 0;
|
||||
for (auto& entry : bindings) {
|
||||
lua_pushstring(L, entry.first.c_str());
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
i++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg inputlib [] = {
|
||||
{"keycode", lua_wrap_errors<l_keycode>},
|
||||
{"mousecode", lua_wrap_errors<l_mousecode>},
|
||||
{"add_callback", lua_wrap_errors<l_add_callback>},
|
||||
{"get_mouse_pos", lua_wrap_errors<l_get_mouse_pos>},
|
||||
{"get_bindings", lua_wrap_errors<l_get_bindings>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
@ -23,14 +23,14 @@ static int l_json_stringify(lua_State* L) {
|
||||
}
|
||||
|
||||
static int l_json_parse(lua_State* L) {
|
||||
auto string = lua_tostring(L, 1);
|
||||
auto string = scripting::state->requireString(1);
|
||||
auto element = json::parse("<string>", string);
|
||||
scripting::state->pushvalue(element);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg jsonlib [] = {
|
||||
{"stringify", lua_wrap_errors<l_json_stringify>},
|
||||
{"tostring", lua_wrap_errors<l_json_stringify>},
|
||||
{"parse", lua_wrap_errors<l_json_parse>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -132,6 +132,20 @@ static int l_player_set_noclip(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_player_get_selected_block(lua_State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
if (player->selectedVoxel.id == BLOCK_VOID) {
|
||||
return 0;
|
||||
}
|
||||
const glm::ivec3 pos = player->selectedBlockPosition;
|
||||
lua_pushinteger(L, pos.x);
|
||||
lua_pushinteger(L, pos.y);
|
||||
lua_pushinteger(L, pos.z);
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg playerlib [] = {
|
||||
{"get_pos", lua_wrap_errors<l_player_get_pos>},
|
||||
{"set_pos", lua_wrap_errors<l_player_set_pos>},
|
||||
@ -144,5 +158,6 @@ const luaL_Reg playerlib [] = {
|
||||
{"set_flight", lua_wrap_errors<l_player_set_flight>},
|
||||
{"is_noclip", lua_wrap_errors<l_player_is_noclip>},
|
||||
{"set_noclip", lua_wrap_errors<l_player_set_noclip>},
|
||||
{"get_selected_block", lua_wrap_errors<l_player_get_selected_block>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -31,7 +31,7 @@ static int l_toml_parse(lua_State*) {
|
||||
}
|
||||
|
||||
const luaL_Reg tomllib [] = {
|
||||
{"serialize", lua_wrap_errors<l_toml_stringify>},
|
||||
{"deserialize", lua_wrap_errors<l_toml_parse>},
|
||||
{"tostring", lua_wrap_errors<l_toml_stringify>},
|
||||
{"parse", lua_wrap_errors<l_toml_parse>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -44,8 +44,9 @@ public:
|
||||
std::shared_ptr<Camera> currentCamera;
|
||||
std::unique_ptr<Hitbox> hitbox;
|
||||
bool debug = false;
|
||||
voxel selectedVoxel {0, 0};
|
||||
voxel selectedVoxel {0, {}};
|
||||
glm::vec3 cam {};
|
||||
glm::ivec3 selectedBlockPosition {};
|
||||
|
||||
Player(glm::vec3 position, float speed, std::shared_ptr<Inventory> inv);
|
||||
~Player();
|
||||
|
||||
@ -33,6 +33,7 @@ void PhysicsSolver::step(
|
||||
hitbox->grounded = false;
|
||||
for (uint i = 0; i < substeps; i++) {
|
||||
float px = pos.x;
|
||||
float py = pos.y;
|
||||
float pz = pos.z;
|
||||
|
||||
vel += gravity * dt * gravityScale;
|
||||
@ -44,6 +45,9 @@ void PhysicsSolver::step(
|
||||
vel.z *= glm::max(0.0f, 1.0f - dt * linear_damping);
|
||||
|
||||
pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f;
|
||||
if (hitbox->grounded) {
|
||||
pos.y = py;
|
||||
}
|
||||
|
||||
if (shifting && hitbox->grounded){
|
||||
float y = (pos.y-half.y-E);
|
||||
@ -219,21 +223,19 @@ bool PhysicsSolver::isBlockInside(int x, int y, int z, Hitbox* hitbox) {
|
||||
y >= floor(pos.y-half.y) && y <= floor(pos.y+half.y);
|
||||
}
|
||||
|
||||
bool PhysicsSolver::isBlockInside(int x, int y, int z, Block* def, blockstate_t states, Hitbox* hitbox) {
|
||||
bool PhysicsSolver::isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox) {
|
||||
const float E = 0.001f; // inaccuracy
|
||||
const glm::vec3& pos = hitbox->position;
|
||||
const glm::vec3& half = hitbox->halfsize;
|
||||
voxel v {};
|
||||
v.states = states;
|
||||
const auto& boxes = def->rotatable
|
||||
? def->rt.hitboxes[v.rotation()]
|
||||
? def->rt.hitboxes[state.rotation]
|
||||
: def->hitboxes;
|
||||
for (const auto& block_hitbox : boxes) {
|
||||
glm::vec3 min = block_hitbox.min();
|
||||
glm::vec3 max = block_hitbox.max();
|
||||
// 0.00001 - inaccuracy
|
||||
if (min.x < pos.x+half.x-x-0.00001f && max.x > pos.x-half.x-x+0.00001f &&
|
||||
min.z < pos.z+half.z-z-0.00001f && max.z > pos.z-half.z-z+0.00001f &&
|
||||
min.y < pos.y+half.y-y-0.00001f && max.y > pos.y-half.y-y+0.00001f)
|
||||
if (min.x < pos.x+half.x-x-E && max.x > pos.x-half.x-x+E &&
|
||||
min.z < pos.z+half.z-z-E && max.z > pos.z-half.z-z+E &&
|
||||
min.y < pos.y+half.y-y-E && max.y > pos.y-half.y-y+E)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define PHYSICS_PHYSICSSOLVER_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "../voxels/voxel.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
@ -31,7 +32,7 @@ public:
|
||||
float stepHeight
|
||||
);
|
||||
bool isBlockInside(int x, int y, int z, Hitbox* hitbox);
|
||||
bool isBlockInside(int x, int y, int z, Block* def, blockstate_t states, Hitbox* hitbox);
|
||||
bool isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox);
|
||||
};
|
||||
|
||||
#endif // PHYSICS_PHYSICSSOLVER_HPP_
|
||||
|
||||
@ -57,7 +57,7 @@ struct GraphicsSettings {
|
||||
/// 1.0 is linear, 2.0 is quadratic
|
||||
NumberSetting fogCurve {1.6f, 1.0f, 6.0f};
|
||||
/// @brief Lighting gamma
|
||||
NumberSetting gamma {1.0f, 0.5f, 2.0f};
|
||||
NumberSetting gamma {1.0f, 0.4f, 1.0f};
|
||||
/// @brief Enable blocks backlight to prevent complete darkness
|
||||
FlagSetting backlight {true};
|
||||
/// @brief Enable chunks frustum culling
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include "util/platform.hpp"
|
||||
#include "util/command_line.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@ -13,7 +12,6 @@ static debug::Logger logger("main");
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
debug::Logger::init("latest.log");
|
||||
std::cout << sizeof(PlayerInput) << std::endl;
|
||||
|
||||
EnginePaths paths;
|
||||
if (!parse_cmdline(argc, argv, paths))
|
||||
|
||||
@ -25,8 +25,8 @@ inline constexpr uint BLOCK_AABB_GRID = 16;
|
||||
inline std::string DEFAULT_MATERIAL = "base:stone";
|
||||
|
||||
struct block_funcs_set {
|
||||
bool init: 1;
|
||||
bool update: 1;
|
||||
bool init: 1;
|
||||
bool update: 1;
|
||||
bool onplaced: 1;
|
||||
bool onbroken: 1;
|
||||
bool oninteract: 1;
|
||||
@ -35,46 +35,46 @@ struct block_funcs_set {
|
||||
};
|
||||
|
||||
struct CoordSystem {
|
||||
glm::ivec3 axisX;
|
||||
glm::ivec3 axisY;
|
||||
glm::ivec3 axisZ;
|
||||
glm::ivec3 axisX;
|
||||
glm::ivec3 axisY;
|
||||
glm::ivec3 axisZ;
|
||||
|
||||
/// @brief Grid 3d position fix offset (for negative vectors)
|
||||
glm::ivec3 fix;
|
||||
/// @brief Grid 3d position fix offset (for negative vectors)
|
||||
glm::ivec3 fix;
|
||||
|
||||
CoordSystem() = default;
|
||||
CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ);
|
||||
CoordSystem() = default;
|
||||
CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ);
|
||||
|
||||
void transform(AABB& aabb) const;
|
||||
void transform(AABB& aabb) const;
|
||||
|
||||
inline bool isVectorHasNegatives(glm::ivec3 vec) {
|
||||
return (vec.x < 0 || vec.y < 0 || vec.z < 0);
|
||||
}
|
||||
inline bool isVectorHasNegatives(glm::ivec3 vec) {
|
||||
return (vec.x < 0 || vec.y < 0 || vec.z < 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockRotProfile {
|
||||
static const int MAX_COUNT = 8;
|
||||
std::string name;
|
||||
CoordSystem variants[MAX_COUNT];
|
||||
static const int MAX_COUNT = 8;
|
||||
std::string name;
|
||||
CoordSystem variants[MAX_COUNT];
|
||||
|
||||
/// @brief Wood logs, pillars, pipes
|
||||
static const BlockRotProfile PIPE;
|
||||
/// @brief Wood logs, pillars, pipes
|
||||
static const BlockRotProfile PIPE;
|
||||
|
||||
/// @brief Doors, signs and other panes
|
||||
static const BlockRotProfile PANE;
|
||||
/// @brief Doors, signs and other panes
|
||||
static const BlockRotProfile PANE;
|
||||
};
|
||||
|
||||
enum class BlockModel {
|
||||
/// @brief invisible
|
||||
none,
|
||||
/// @brief default cube shape
|
||||
block,
|
||||
/// @brief X-shape (grass)
|
||||
xsprite,
|
||||
/// @brief box shape sized as block hitbox
|
||||
aabb,
|
||||
/// @brief custom model defined in json
|
||||
custom
|
||||
/// @brief invisible
|
||||
none,
|
||||
/// @brief default cube shape
|
||||
block,
|
||||
/// @brief X-shape (grass)
|
||||
xsprite,
|
||||
/// @brief box shape sized as block hitbox
|
||||
aabb,
|
||||
/// @brief custom model defined in json
|
||||
custom
|
||||
};
|
||||
|
||||
using BoxModel = AABB;
|
||||
@ -82,61 +82,61 @@ using BoxModel = AABB;
|
||||
|
||||
/// @brief Common kit of block properties applied to groups of blocks
|
||||
struct BlockMaterial {
|
||||
std::string name;
|
||||
std::string stepsSound {""};
|
||||
std::string placeSound {""};
|
||||
std::string breakSound {""};
|
||||
std::string name;
|
||||
std::string stepsSound {""};
|
||||
std::string placeSound {""};
|
||||
std::string breakSound {""};
|
||||
};
|
||||
|
||||
/// @brief Block properties definition
|
||||
class Block {
|
||||
public:
|
||||
/// @brief Block string id (with prefix included)
|
||||
std::string const name;
|
||||
/// @brief Block string id (with prefix included)
|
||||
std::string const name;
|
||||
|
||||
std::string caption;
|
||||
|
||||
std::string caption;
|
||||
|
||||
/// @brief Textures set applied to block sides
|
||||
std::string textureFaces[6]; // -x,x, -y,y, -z,z
|
||||
|
||||
std::vector<std::string> modelTextures = {};
|
||||
std::vector<BoxModel> modelBoxes = {};
|
||||
std::vector<glm::vec3> modelExtraPoints = {}; //initially made for tetragons
|
||||
std::vector<UVRegion> modelUVs = {}; // boxes' tex-UVs also there
|
||||
std::string textureFaces[6]; // -x,x, -y,y, -z,z
|
||||
|
||||
std::vector<std::string> modelTextures = {};
|
||||
std::vector<BoxModel> modelBoxes = {};
|
||||
std::vector<glm::vec3> modelExtraPoints = {}; //initially made for tetragons
|
||||
std::vector<UVRegion> modelUVs = {}; // boxes' tex-UVs also there
|
||||
|
||||
/// @brief id of used BlockMaterial, may specify non-existing material
|
||||
std::string material = DEFAULT_MATERIAL;
|
||||
|
||||
/// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive clouds)
|
||||
uint8_t emission[4] {0, 0, 0, 0};
|
||||
/// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive clouds)
|
||||
uint8_t emission[4] {0, 0, 0, 0};
|
||||
|
||||
/// @brief Influences visible block sides for transparent blocks
|
||||
uint8_t drawGroup = 0;
|
||||
|
||||
/// @brief Influences visible block sides for transparent blocks
|
||||
uint8_t drawGroup = 0;
|
||||
|
||||
/// @brief Block model type
|
||||
BlockModel model = BlockModel::block;
|
||||
|
||||
BlockModel model = BlockModel::block;
|
||||
|
||||
/// @brief Does the block passing lights into itself
|
||||
bool lightPassing = false;
|
||||
|
||||
bool lightPassing = false;
|
||||
|
||||
/// @brief Does the block passing top-down sky lights into itself
|
||||
bool skyLightPassing = false;
|
||||
|
||||
bool skyLightPassing = false;
|
||||
|
||||
/// @brief Is the block a physical obstacle
|
||||
bool obstacle = true;
|
||||
|
||||
bool obstacle = true;
|
||||
|
||||
/// @brief Can the block be selected
|
||||
bool selectable = true;
|
||||
|
||||
bool selectable = true;
|
||||
|
||||
/// @brief Can the block be replaced with other.
|
||||
/// Examples of replaceable blocks: air, flower, water
|
||||
bool replaceable = false;
|
||||
|
||||
/// Examples of replaceable blocks: air, flower, water
|
||||
bool replaceable = false;
|
||||
|
||||
/// @brief Can player destroy the block
|
||||
bool breakable = true;
|
||||
|
||||
bool breakable = true;
|
||||
|
||||
/// @brief Can the block be oriented different ways
|
||||
bool rotatable = false;
|
||||
bool rotatable = false;
|
||||
|
||||
/// @brief Can the block exist without physical support be a solid block below
|
||||
bool grounded = false;
|
||||
@ -146,46 +146,49 @@ public:
|
||||
|
||||
/// @brief Set of block physical hitboxes
|
||||
std::vector<AABB> hitboxes;
|
||||
|
||||
|
||||
/// @brief Set of available block rotations (coord-systems)
|
||||
BlockRotProfile rotations;
|
||||
BlockRotProfile rotations;
|
||||
|
||||
/// @brief Item will be picked on MMB click on the block
|
||||
std::string pickingItem = name+BLOCK_ITEM_SUFFIX;
|
||||
|
||||
/// @brief Block script name in blocks/ without extension
|
||||
std::string scriptName = name.substr(name.find(':')+1);
|
||||
|
||||
|
||||
/// @brief Default block layout will be used by hud.open_block(...)
|
||||
std::string uiLayout = name;
|
||||
std::string uiLayout = name;
|
||||
|
||||
/// @brief Block inventory size. 0 - no inventory
|
||||
uint inventorySize = 0;
|
||||
|
||||
/// @brief Runtime indices (content indexing results)
|
||||
struct {
|
||||
/// @brief block runtime integer id
|
||||
blockid_t id;
|
||||
|
||||
// @brief Block tick interval (1 - 20tps, 2 - 10tps)
|
||||
uint tickInterval = 1;
|
||||
|
||||
/// @brief Runtime indices (content indexing results)
|
||||
struct {
|
||||
/// @brief block runtime integer id
|
||||
blockid_t id;
|
||||
|
||||
/// @brief is the block completely opaque for render and raycast
|
||||
bool solid = true;
|
||||
|
||||
bool solid = true;
|
||||
|
||||
/// @brief does the block emit any lights
|
||||
bool emissive = false;
|
||||
bool emissive = false;
|
||||
|
||||
/// @brief set of hitboxes sets with all coord-systems precalculated
|
||||
std::vector<AABB> hitboxes[BlockRotProfile::MAX_COUNT];
|
||||
|
||||
|
||||
/// @brief set of block callbacks flags
|
||||
block_funcs_set funcsset {};
|
||||
block_funcs_set funcsset {};
|
||||
|
||||
/// @brief picking item integer id
|
||||
itemid_t pickingItem = 0;
|
||||
} rt;
|
||||
} rt;
|
||||
|
||||
Block(std::string name);
|
||||
Block(std::string name, std::string texture);
|
||||
Block(const Block&) = delete;
|
||||
Block(std::string name);
|
||||
Block(std::string name, std::string texture);
|
||||
Block(const Block&) = delete;
|
||||
};
|
||||
|
||||
#endif /* VOXELS_BLOCK_HPP_ */
|
||||
|
||||
@ -7,56 +7,52 @@
|
||||
#include "../lighting/Lightmap.hpp"
|
||||
|
||||
Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos){
|
||||
bottom = 0;
|
||||
top = CHUNK_H;
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
voxels[i].id = 2;
|
||||
voxels[i].states = 0;
|
||||
}
|
||||
bottom = 0;
|
||||
top = CHUNK_H;
|
||||
}
|
||||
|
||||
bool Chunk::isEmpty(){
|
||||
int id = -1;
|
||||
for (uint i = 0; i < CHUNK_VOL; i++){
|
||||
if (voxels[i].id != id){
|
||||
if (id != -1)
|
||||
return false;
|
||||
else
|
||||
id = voxels[i].id;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
int id = -1;
|
||||
for (uint i = 0; i < CHUNK_VOL; i++){
|
||||
if (voxels[i].id != id){
|
||||
if (id != -1)
|
||||
return false;
|
||||
else
|
||||
id = voxels[i].id;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Chunk::updateHeights() {
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
if (voxels[i].id != 0) {
|
||||
bottom = i / (CHUNK_D * CHUNK_W);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = CHUNK_VOL - 1; i >= 0; i--) {
|
||||
if (voxels[i].id != 0) {
|
||||
top = i / (CHUNK_D * CHUNK_W) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
if (voxels[i].id != 0) {
|
||||
bottom = i / (CHUNK_D * CHUNK_W);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = CHUNK_VOL - 1; i >= 0; i--) {
|
||||
if (voxels[i].id != 0) {
|
||||
top = i / (CHUNK_D * CHUNK_W) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::addBlockInventory(std::shared_ptr<Inventory> inventory,
|
||||
uint x, uint y, uint z) {
|
||||
inventories[vox_index(x, y, z)] = inventory;
|
||||
setUnsaved(true);
|
||||
flags.unsaved = true;
|
||||
}
|
||||
|
||||
void Chunk::removeBlockInventory(uint x, uint y, uint z) {
|
||||
if (inventories.erase(vox_index(x, y, z))) {
|
||||
setUnsaved(true);
|
||||
}
|
||||
if (inventories.erase(vox_index(x, y, z))) {
|
||||
flags.unsaved = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::setBlockInventories(chunk_inventories_map map) {
|
||||
inventories = map;
|
||||
inventories = map;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Chunk::getBlockInventory(uint x, uint y, uint z) const {
|
||||
@ -70,12 +66,12 @@ std::shared_ptr<Inventory> Chunk::getBlockInventory(uint x, uint y, uint z) cons
|
||||
}
|
||||
|
||||
std::unique_ptr<Chunk> Chunk::clone() const {
|
||||
auto other = std::make_unique<Chunk>(x,z);
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
other->voxels[i] = voxels[i];
|
||||
auto other = std::make_unique<Chunk>(x,z);
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
other->voxels[i] = voxels[i];
|
||||
}
|
||||
other->lightmap.set(&lightmap);
|
||||
return other;
|
||||
other->lightmap.set(&lightmap);
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,19 +89,21 @@ std::unique_ptr<Chunk> Chunk::clone() const {
|
||||
Total size: (CHUNK_VOL * 4) bytes
|
||||
*/
|
||||
std::unique_ptr<ubyte[]> Chunk::encode() const {
|
||||
auto buffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
buffer[i] = voxels[i].id >> 8;
|
||||
auto buffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
buffer[i] = voxels[i].id >> 8;
|
||||
buffer[CHUNK_VOL+i] = voxels[i].id & 0xFF;
|
||||
buffer[CHUNK_VOL*2 + i] = voxels[i].states >> 8;
|
||||
buffer[CHUNK_VOL*3 + i] = voxels[i].states & 0xFF;
|
||||
}
|
||||
return buffer;
|
||||
|
||||
blockstate_t state = blockstate2int(voxels[i].state);
|
||||
buffer[CHUNK_VOL*2 + i] = state >> 8;
|
||||
buffer[CHUNK_VOL*3 + i] = state & 0xFF;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool Chunk::decode(const ubyte* data) {
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
voxel& vox = voxels[i];
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
voxel& vox = voxels[i];
|
||||
|
||||
ubyte bid1 = data[i];
|
||||
ubyte bid2 = data[CHUNK_VOL + i];
|
||||
@ -113,10 +111,10 @@ bool Chunk::decode(const ubyte* data) {
|
||||
ubyte bst1 = data[CHUNK_VOL*2 + i];
|
||||
ubyte bst2 = data[CHUNK_VOL*3 + i];
|
||||
|
||||
vox.id = (blockid_t(bid1) << 8) | (blockid_t(bid2));
|
||||
vox.states = (blockstate_t(bst1) << 8) | (blockstate_t(bst2));
|
||||
}
|
||||
return true;
|
||||
vox.id = (blockid_t(bid1) << 8) | (blockid_t(bid2));
|
||||
vox.state = int2blockstate((blockstate_t(bst1) << 8) | (blockstate_t(bst2)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Chunk::convert(ubyte* data, const ContentLUT* lut) {
|
||||
|
||||
@ -9,14 +9,6 @@
|
||||
#include "voxel.hpp"
|
||||
#include "../lighting/Lightmap.hpp"
|
||||
|
||||
struct ChunkFlag {
|
||||
static const int MODIFIED = 0x1;
|
||||
static const int READY = 0x2;
|
||||
static const int LOADED = 0x4;
|
||||
static const int LIGHTED = 0x8;
|
||||
static const int UNSAVED = 0x10;
|
||||
static const int LOADED_LIGHTS = 0x20;
|
||||
};
|
||||
inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL*4;
|
||||
|
||||
class Lightmap;
|
||||
@ -27,72 +19,52 @@ using chunk_inventories_map = std::unordered_map<uint, std::shared_ptr<Inventory
|
||||
|
||||
class Chunk {
|
||||
public:
|
||||
int x, z;
|
||||
int bottom, top;
|
||||
voxel voxels[CHUNK_VOL];
|
||||
Lightmap lightmap;
|
||||
int flags = 0;
|
||||
int x, z;
|
||||
int bottom, top;
|
||||
voxel voxels[CHUNK_VOL] {};
|
||||
Lightmap lightmap;
|
||||
struct {
|
||||
bool modified: 1;
|
||||
bool ready: 1;
|
||||
bool loaded: 1;
|
||||
bool lighted: 1;
|
||||
bool unsaved: 1;
|
||||
bool loadedLights: 1;
|
||||
} flags {};
|
||||
|
||||
/* Block inventories map where key is index of block in voxels array */
|
||||
chunk_inventories_map inventories;
|
||||
|
||||
Chunk(int x, int z);
|
||||
Chunk(int x, int z);
|
||||
|
||||
bool isEmpty();
|
||||
bool isEmpty();
|
||||
|
||||
void updateHeights();
|
||||
void updateHeights();
|
||||
|
||||
// unused
|
||||
std::unique_ptr<Chunk> clone() const;
|
||||
|
||||
// flags getters/setters below
|
||||
inline void setFlags(int mask, bool value){
|
||||
if (value)
|
||||
flags |= mask;
|
||||
else
|
||||
flags &= ~(mask);
|
||||
}
|
||||
std::unique_ptr<Chunk> clone() const;
|
||||
|
||||
/* Creates new block inventory given size
|
||||
@return inventory id or 0 if block does not exists */
|
||||
void addBlockInventory(std::shared_ptr<Inventory> inventory,
|
||||
uint x, uint y, uint z);
|
||||
void removeBlockInventory(uint x, uint y, uint z);
|
||||
void setBlockInventories(chunk_inventories_map map);
|
||||
void removeBlockInventory(uint x, uint y, uint z);
|
||||
void setBlockInventories(chunk_inventories_map map);
|
||||
|
||||
/* @return inventory bound to the given block or nullptr */
|
||||
std::shared_ptr<Inventory> getBlockInventory(uint x, uint y, uint z) const;
|
||||
|
||||
inline bool isUnsaved() const {return flags & ChunkFlag::UNSAVED;}
|
||||
inline void setModifiedAndUnsaved() {
|
||||
flags.modified = true;
|
||||
flags.unsaved = true;
|
||||
}
|
||||
|
||||
inline bool isModified() const {return flags & ChunkFlag::MODIFIED;}
|
||||
|
||||
inline bool isLighted() const {return flags & ChunkFlag::LIGHTED;}
|
||||
|
||||
inline bool isLoaded() const {return flags & ChunkFlag::LOADED;}
|
||||
|
||||
inline bool isLoadedLights() const {return flags & ChunkFlag::LOADED_LIGHTS;}
|
||||
|
||||
inline bool isReady() const {return flags & ChunkFlag::READY;}
|
||||
|
||||
inline void setUnsaved(bool newState) {setFlags(ChunkFlag::UNSAVED, newState);}
|
||||
|
||||
inline void setModified(bool newState) {setFlags(ChunkFlag::MODIFIED, newState);}
|
||||
|
||||
inline void setLoaded(bool newState) {setFlags(ChunkFlag::LOADED, newState);}
|
||||
|
||||
inline void setLoadedLights(bool newState) {setFlags(ChunkFlag::LOADED_LIGHTS, newState);}
|
||||
|
||||
inline void setLighted(bool newState) {setFlags(ChunkFlag::LIGHTED, newState);}
|
||||
|
||||
inline void setReady(bool newState) {setFlags(ChunkFlag::READY, newState);}
|
||||
|
||||
std::unique_ptr<ubyte[]> encode() const;
|
||||
std::unique_ptr<ubyte[]> encode() const;
|
||||
|
||||
/**
|
||||
* @return true if all is fine
|
||||
**/
|
||||
bool decode(const ubyte* data);
|
||||
bool decode(const ubyte* data);
|
||||
|
||||
static void convert(ubyte* data, const ContentLUT* lut);
|
||||
};
|
||||
|
||||
@ -68,7 +68,7 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z){
|
||||
const Block* def = contentIds->getBlockDef(v->id);
|
||||
if (def->obstacle) {
|
||||
const auto& boxes = def->rotatable
|
||||
? def->rt.hitboxes[v->rotation()]
|
||||
? def->rt.hitboxes[v->state.rotation]
|
||||
: def->hitboxes;
|
||||
for (const auto& hitbox : boxes) {
|
||||
if (hitbox.contains({x - ix, y - iy, z - iz})) {
|
||||
@ -158,7 +158,7 @@ Chunk* Chunks::getChunk(int x, int z){
|
||||
return chunks[z * w + x].get();
|
||||
}
|
||||
|
||||
void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, uint8_t states) {
|
||||
void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state) {
|
||||
if (y < 0 || y >= CHUNK_H)
|
||||
return;
|
||||
x -= ox * CHUNK_W;
|
||||
@ -178,24 +178,22 @@ void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, uint8_t states) {
|
||||
if (def->inventorySize == 0)
|
||||
chunk->removeBlockInventory(lx, y, lz);
|
||||
vox.id = id;
|
||||
vox.states = states;
|
||||
|
||||
chunk->setUnsaved(true);
|
||||
chunk->setModified(true);
|
||||
vox.state = state;
|
||||
chunk->setModifiedAndUnsaved();
|
||||
|
||||
if (y < chunk->bottom) chunk->bottom = y;
|
||||
else if (y + 1 > chunk->top) chunk->top = y + 1;
|
||||
else if (id == 0) chunk->updateHeights();
|
||||
|
||||
if (lx == 0 && (chunk = getChunk(cx+ox-1, cz+oz)))
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
if (lz == 0 && (chunk = getChunk(cx+ox, cz+oz-1)))
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
|
||||
if (lx == CHUNK_W-1 && (chunk = getChunk(cx+ox+1, cz+oz)))
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
if (lz == CHUNK_D-1 && (chunk = getChunk(cx+ox, cz+oz+1)))
|
||||
chunk->setModified(true);
|
||||
chunk->flags.modified = true;
|
||||
}
|
||||
|
||||
voxel* Chunks::rayCast(
|
||||
@ -255,7 +253,7 @@ voxel* Chunks::rayCast(
|
||||
|
||||
if (!def->rt.solid) {
|
||||
const std::vector<AABB>& hitboxes = def->rotatable
|
||||
? def->rt.hitboxes[voxel->rotation()]
|
||||
? def->rt.hitboxes[voxel->state.rotation]
|
||||
: def->hitboxes;
|
||||
|
||||
scalar_t distance = maxDist;
|
||||
@ -365,7 +363,7 @@ glm::vec3 Chunks::rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDis
|
||||
if (def->obstacle) {
|
||||
if (!def->rt.solid) {
|
||||
const std::vector<AABB>& hitboxes = def->rotatable
|
||||
? def->rt.hitboxes[voxel->rotation()]
|
||||
? def->rt.hitboxes[voxel->state.rotation]
|
||||
: def->modelBoxes;
|
||||
|
||||
scalar_t distance;
|
||||
@ -499,12 +497,12 @@ void Chunks::saveAndClear(){
|
||||
for (size_t i = 0; i < volume; i++){
|
||||
Chunk* chunk = chunks[i].get();
|
||||
chunks[i] = nullptr;
|
||||
if (chunk == nullptr || !chunk->isLighted())
|
||||
if (chunk == nullptr || !chunk->flags.lighted)
|
||||
continue;
|
||||
|
||||
bool lightsUnsaved = !chunk->isLoadedLights() &&
|
||||
bool lightsUnsaved = !chunk->flags.loadedLights &&
|
||||
worldFiles->doesWriteLights();
|
||||
if (!chunk->isUnsaved() && !lightsUnsaved)
|
||||
if (!chunk->flags.unsaved && !lightsUnsaved)
|
||||
continue;
|
||||
regions.put(chunk);
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "voxel.hpp"
|
||||
#include "../typedefs.hpp"
|
||||
|
||||
class VoxelRenderer;
|
||||
@ -13,7 +15,6 @@ struct AABB;
|
||||
class Content;
|
||||
class ContentIndices;
|
||||
class Chunk;
|
||||
struct voxel;
|
||||
class WorldFiles;
|
||||
class LevelEvents;
|
||||
|
||||
@ -42,7 +43,7 @@ public:
|
||||
voxel* get(int32_t x, int32_t y, int32_t z);
|
||||
light_t getLight(int32_t x, int32_t y, int32_t z);
|
||||
ubyte getLight(int32_t x, int32_t y, int32_t z, int channel);
|
||||
void set(int32_t x, int32_t y, int32_t z, uint32_t id, uint8_t states);
|
||||
void set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state);
|
||||
|
||||
voxel* rayCast(
|
||||
glm::vec3 start,
|
||||
|
||||
@ -61,7 +61,7 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
|
||||
chunk->decode(data.get());
|
||||
auto invs = regions.fetchInventories(chunk->x, chunk->z);
|
||||
chunk->setBlockInventories(std::move(invs));
|
||||
chunk->setLoaded(true);
|
||||
chunk->flags.loaded = true;
|
||||
for(auto& entry : chunk->inventories) {
|
||||
level->inventories->store(entry.second);
|
||||
}
|
||||
@ -71,7 +71,7 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
|
||||
auto lights = regions.getLights(chunk->x, chunk->z);
|
||||
if (lights) {
|
||||
chunk->lightmap.set(lights.get());
|
||||
chunk->setLoadedLights(true);
|
||||
chunk->flags.loadedLights = true;
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ void DefaultWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed){
|
||||
for (int cur_y = 0; cur_y < CHUNK_H; cur_y++){
|
||||
// int cur_y = y;
|
||||
int id = cur_y < SEA_LEVEL ? idWater : BLOCK_AIR;
|
||||
int states = 0;
|
||||
blockstate state {};
|
||||
if ((cur_y == (int)height) && (SEA_LEVEL-2 < cur_y)) {
|
||||
id = idGrassBlock;
|
||||
} else if (cur_y < (height - 6)){
|
||||
@ -177,7 +177,7 @@ void DefaultWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed){
|
||||
treesTile, idWood, idLeaves);
|
||||
if (tree) {
|
||||
id = tree;
|
||||
states = BLOCK_DIR_UP;
|
||||
state.rotation = BLOCK_DIR_UP;
|
||||
}
|
||||
}
|
||||
float sand = fmax(heights.get(MAPS::SAND, cur_x, cur_z), heights.get(MAPS::CLIFF, cur_x, cur_z));
|
||||
@ -198,10 +198,10 @@ void DefaultWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed){
|
||||
}
|
||||
if ((height > SEA_LEVEL+1) && ((int)(height + 1) == cur_y) && ((unsigned short)randomgrass.rand() > 65533)){
|
||||
id = idWood;
|
||||
states = BLOCK_DIR_UP;
|
||||
state.rotation = BLOCK_DIR_UP;
|
||||
}
|
||||
voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].id = id;
|
||||
voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].states = states;
|
||||
voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].state = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ void FlatWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed) {
|
||||
for (int x = 0; x < CHUNK_W; x++) {
|
||||
for (int cur_y = 0; cur_y < CHUNK_H; cur_y++){
|
||||
int id = BLOCK_AIR;
|
||||
int states = 0;
|
||||
blockstate state {};
|
||||
|
||||
if(cur_y == 2) {
|
||||
id = idBazalt;
|
||||
@ -21,7 +21,7 @@ void FlatWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed) {
|
||||
}
|
||||
|
||||
voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].id = id;
|
||||
voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].states = states;
|
||||
voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].state = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,22 +10,34 @@ inline constexpr int BLOCK_DIR_EAST = 0x3;
|
||||
inline constexpr int BLOCK_DIR_UP = 0x4;
|
||||
inline constexpr int BLOCK_DIR_DOWN = 0x5;
|
||||
|
||||
// limited to 8 block orientations
|
||||
inline constexpr int BLOCK_ROT_MASK = 0b0000'0111;
|
||||
// reserved bits
|
||||
inline constexpr int BLOCK_RESERVED_MASK = 0b1111'1000;
|
||||
struct blockstate {
|
||||
uint8_t rotation : 3;
|
||||
uint8_t segment : 2; // planned to 0.22
|
||||
uint8_t reserved : 3;
|
||||
uint8_t userbits : 8;
|
||||
};
|
||||
static_assert (sizeof(blockstate) == 2);
|
||||
|
||||
inline constexpr blockstate_t blockstate2int(blockstate b) {
|
||||
return static_cast<blockstate_t>(b.rotation) |
|
||||
static_cast<blockstate_t>(b.segment) << 3 |
|
||||
static_cast<blockstate_t>(b.reserved) << 5 |
|
||||
static_cast<blockstate_t>(b.userbits) << 8;
|
||||
}
|
||||
|
||||
inline constexpr blockstate int2blockstate(blockstate_t i) {
|
||||
return {
|
||||
static_cast<uint8_t>(i & 0b111),
|
||||
static_cast<uint8_t>((i >> 3) & 0b11),
|
||||
static_cast<uint8_t>((i >> 5) & 0b111),
|
||||
static_cast<uint8_t>((i >> 8) & 0xFF)
|
||||
};
|
||||
}
|
||||
|
||||
struct voxel {
|
||||
blockid_t id;
|
||||
blockstate_t states;
|
||||
|
||||
inline uint8_t rotation() const {
|
||||
return states & BLOCK_ROT_MASK;
|
||||
}
|
||||
|
||||
inline void setRotation(uint8_t rotation) {
|
||||
states = (states & (~BLOCK_ROT_MASK)) | (rotation & BLOCK_ROT_MASK);
|
||||
}
|
||||
blockstate state;
|
||||
};
|
||||
static_assert(sizeof(voxel) == 4);
|
||||
|
||||
#endif // VOXELS_VOXEL_HPP_
|
||||
|
||||
@ -98,6 +98,14 @@ void Events::pollEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
Binding& Events::getBinding(const std::string& name) {
|
||||
auto found = bindings.find(name);
|
||||
if (found == bindings.end()) {
|
||||
throw std::runtime_error("binding '"+name+"' does not exists");
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
void Events::bind(const std::string& name, inputtype type, keycode code) {
|
||||
bind(name, type, static_cast<int>(code));
|
||||
}
|
||||
@ -110,6 +118,10 @@ void Events::bind(const std::string& name, inputtype type, int code) {
|
||||
bindings.emplace(name, Binding(type, code));
|
||||
}
|
||||
|
||||
void Events::rebind(const std::string& name, inputtype type, int code) {
|
||||
bindings[name] = Binding(type, code);
|
||||
}
|
||||
|
||||
bool Events::active(const std::string& name) {
|
||||
const auto& found = bindings.find(name);
|
||||
if (found == bindings.end()) {
|
||||
@ -155,19 +167,22 @@ std::string Events::writeBindings() {
|
||||
dynamic::Map obj;
|
||||
for (auto& entry : Events::bindings) {
|
||||
const auto& binding = entry.second;
|
||||
|
||||
auto& jentry = obj.putMap(entry.first);
|
||||
std::string value;
|
||||
switch (binding.type) {
|
||||
case inputtype::keyboard: jentry.put("type", "keyboard"); break;
|
||||
case inputtype::mouse: jentry.put("type", "mouse"); break;
|
||||
case inputtype::keyboard:
|
||||
value = "key:"+input_util::get_name(static_cast<keycode>(binding.code));
|
||||
break;
|
||||
case inputtype::mouse:
|
||||
value = "mouse:"+input_util::get_name(static_cast<mousecode>(binding.code));
|
||||
break;
|
||||
default: throw std::runtime_error("unsupported control type");
|
||||
}
|
||||
jentry.put("code", binding.code);
|
||||
obj.put(entry.first, value);
|
||||
}
|
||||
return json::stringify(&obj, true, " ");
|
||||
return toml::stringify(obj);
|
||||
}
|
||||
|
||||
void Events::loadBindings(const std::string& filename, const std::string& source) {
|
||||
void Events::loadBindingsOld(const std::string& filename, const std::string& source) {
|
||||
auto obj = json::parse(filename, source);
|
||||
for (auto& entry : Events::bindings) {
|
||||
auto& binding = entry.second;
|
||||
@ -192,7 +207,7 @@ void Events::loadBindings(const std::string& filename, const std::string& source
|
||||
}
|
||||
}
|
||||
|
||||
void Events::loadBindingsToml(const std::string& filename, const std::string& source) {
|
||||
void Events::loadBindings(const std::string& filename, const std::string& source) {
|
||||
auto map = toml::parse(filename, source);
|
||||
for (auto& entry : map->values) {
|
||||
if (auto value = std::get_if<std::string>(&entry.second)) {
|
||||
|
||||
@ -38,9 +38,11 @@ public:
|
||||
|
||||
static void toggleCursor();
|
||||
|
||||
static Binding& getBinding(const std::string& name);
|
||||
static void bind(const std::string& name, inputtype type, keycode code);
|
||||
static void bind(const std::string& name, inputtype type, mousecode code);
|
||||
static void bind(const std::string& name, inputtype type, int code);
|
||||
static void rebind(const std::string& name, inputtype type, int code);
|
||||
static bool active(const std::string& name);
|
||||
static bool jactive(const std::string& name);
|
||||
|
||||
@ -51,7 +53,7 @@ public:
|
||||
|
||||
static std::string writeBindings();
|
||||
static void loadBindings(const std::string& filename, const std::string& source);
|
||||
static void loadBindingsToml(const std::string& filename, const std::string& source);
|
||||
static void loadBindingsOld(const std::string& filename, const std::string& source); // TODO: remove in 0.22
|
||||
};
|
||||
|
||||
#endif // WINDOW_EVENTS_HPP_
|
||||
|
||||
@ -40,6 +40,25 @@ static std::unordered_map<std::string, int> mousecodes {
|
||||
{"middle", GLFW_MOUSE_BUTTON_3},
|
||||
};
|
||||
|
||||
static std::unordered_map<int, std::string> keynames {};
|
||||
|
||||
std::string input_util::get_name(mousecode code) {
|
||||
switch (code) {
|
||||
case mousecode::BUTTON_1: return "left";
|
||||
case mousecode::BUTTON_2: return "right";
|
||||
case mousecode::BUTTON_3: return "middle";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string input_util::get_name(keycode code) {
|
||||
auto found = keynames.find(static_cast<int>(code));
|
||||
if (found == keynames.end()) {
|
||||
return "unknown";
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
void Binding::reset(inputtype type, int code) {
|
||||
this->type = type;
|
||||
this->code = code;
|
||||
@ -58,11 +77,14 @@ void input_util::initialize() {
|
||||
keycodes[std::to_string(i)] = GLFW_KEY_0+i;
|
||||
}
|
||||
for (int i = 0; i < 25; i++) {
|
||||
keycodes["f"+std::to_string(i)] = GLFW_KEY_F1+i;
|
||||
keycodes["f"+std::to_string(i+1)] = GLFW_KEY_F1+i;
|
||||
}
|
||||
for (char i = 'a'; i <= 'z'; i++) {
|
||||
keycodes[std::string({i})] = GLFW_KEY_A-'a'+i;
|
||||
}
|
||||
for (const auto& entry : keycodes) {
|
||||
keynames[entry.second] = entry.first;
|
||||
}
|
||||
}
|
||||
|
||||
keycode input_util::keycode_from(const std::string& name) {
|
||||
|
||||
@ -122,6 +122,11 @@ namespace input_util {
|
||||
std::string to_string(keycode code);
|
||||
/// @return Mouse button label by keycode
|
||||
std::string to_string(mousecode code);
|
||||
|
||||
/// @return Key name by keycode
|
||||
std::string get_name(keycode code);
|
||||
/// @return Mouse button name by keycode
|
||||
std::string get_name(mousecode code);
|
||||
}
|
||||
|
||||
enum class inputtype {
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
## Key Names
|
||||
- space, backspace, tab, enter, caps-lock, escape
|
||||
- left-ctrl, left-shift, left-alt, left-super
|
||||
- right-ctrl, right-shift, right-alt, right-alt
|
||||
- delete, home, end, insert, page-up, page-down
|
||||
- left, right, down, up
|
||||
- a..z
|
||||
- 0..9
|
||||
- f1..f25
|
||||
@ -58,8 +58,8 @@ public:
|
||||
std::shared_ptr<T> tObj = std::make_shared<T>(args...);
|
||||
|
||||
std::shared_ptr<Object> obj = std::dynamic_pointer_cast<Object, T>(tObj);
|
||||
objects.push_back(obj);
|
||||
obj->objectUID = objects.size();
|
||||
objects.push_back(obj);
|
||||
obj->spawned();
|
||||
return tObj;
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include "../voxels/Chunk.hpp"
|
||||
#include "../voxels/Chunks.hpp"
|
||||
#include "../voxels/ChunksStorage.hpp"
|
||||
#include "../window/Camera.hpp"
|
||||
#include "../world/WorldGenerators.hpp"
|
||||
|
||||
#include <memory>
|
||||
@ -58,11 +57,11 @@ void World::write(Level* level) {
|
||||
|
||||
for (size_t i = 0; i < chunks->volume; i++) {
|
||||
auto chunk = chunks->chunks[i];
|
||||
if (chunk == nullptr || !chunk->isLighted())
|
||||
if (chunk == nullptr || !chunk->flags.lighted)
|
||||
continue;
|
||||
bool lightsUnsaved = !chunk->isLoadedLights() &&
|
||||
bool lightsUnsaved = !chunk->flags.loadedLights &&
|
||||
settings.debug.doWriteLights.get();
|
||||
if (!chunk->isUnsaved() && !lightsUnsaved)
|
||||
if (!chunk->flags.unsaved && !lightsUnsaved)
|
||||
continue;
|
||||
regions.put(chunk.get());
|
||||
}
|
||||
@ -135,7 +134,6 @@ std::unique_ptr<Level> World::load(
|
||||
}
|
||||
}
|
||||
}
|
||||
(void)world.release();
|
||||
return level;
|
||||
}
|
||||
|
||||
@ -194,19 +192,20 @@ void World::deserialize(dynamic::Map* root) {
|
||||
if (generator == "") {
|
||||
generator = WorldGenerators::getDefaultGeneratorID();
|
||||
}
|
||||
auto verobj = root->map("version");
|
||||
if (verobj) {
|
||||
if (auto verobj = root->map("version")) {
|
||||
int major=0, minor=-1;
|
||||
verobj->num("major", major);
|
||||
verobj->num("minor", minor);
|
||||
std::cout << "world version: " << major << "." << minor << std::endl;
|
||||
logger.info() << "world version: " << major << "." << minor;
|
||||
}
|
||||
auto timeobj = root->map("time");
|
||||
if (timeobj) {
|
||||
if (auto timeobj = root->map("time")) {
|
||||
timeobj->num("day-time", daytime);
|
||||
timeobj->num("day-time-speed", daytimeSpeed);
|
||||
timeobj->num("total-time", totalTime);
|
||||
}
|
||||
if (auto weatherobj = root->map("weather")) {
|
||||
weatherobj->num("fog", fog);
|
||||
}
|
||||
nextInventoryId = root->get("next-inventory-id", 2);
|
||||
}
|
||||
|
||||
@ -226,6 +225,9 @@ std::unique_ptr<dynamic::Map> World::serialize() const {
|
||||
timeobj.put("day-time-speed", daytimeSpeed);
|
||||
timeobj.put("total-time", totalTime);
|
||||
|
||||
auto& weatherobj = root->putMap("weather");
|
||||
weatherobj.put("fog", fog);
|
||||
|
||||
root->put("next-inventory-id", nextInventoryId);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -49,6 +49,9 @@ public:
|
||||
/// @brief total time passed in the world (not depending on daytimeSpeed)
|
||||
double totalTime = 0.0;
|
||||
|
||||
/// @brief will be replaced with weather in future
|
||||
float fog = 0.0f;
|
||||
|
||||
World(
|
||||
std::string name,
|
||||
std::string generator,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user