diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
new file mode 100644
index 00000000..68f6acc2
--- /dev/null
+++ b/.github/workflows/macos.yml
@@ -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
\ No newline at end of file
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
new file mode 100644
index 00000000..46fe7122
--- /dev/null
+++ b/.github/workflows/windows.yml
@@ -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/*'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b4e4bfd..2ec2faab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/doc/ru/8.Скриптинг.md b/doc/ru/8.Скриптинг.md
index e68ea1a4..f5ba82c2 100644
--- a/doc/ru/8.Скриптинг.md
+++ b/doc/ru/8.Скриптинг.md
@@ -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
```
+
+Возвращает дельту времени (время прошедшее с предыдущего кадра)
diff --git a/doc/ru/Консоль.md b/doc/ru/Консоль.md
new file mode 100644
index 00000000..a279be61
--- /dev/null
+++ b/doc/ru/Консоль.md
@@ -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 передается таблица значений именованных аргументов.
+
+Проверку и приведение типов интерпретатор команд производит автоматически.
diff --git a/doc/ru/Пользовательский-ввод.md b/doc/ru/Пользовательский-ввод.md
new file mode 100644
index 00000000..e113b3b1
--- /dev/null
+++ b/doc/ru/Пользовательский-ввод.md
@@ -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() -> массив строк
+```
+
+Возвращает названия всех доступных привязок.
diff --git a/doc/ru/События-движка.md b/doc/ru/События-движка.md
new file mode 100644
index 00000000..ecfd2db9
--- /dev/null
+++ b/doc/ru/События-движка.md
@@ -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)
+```
+
+Вызывается при выходе из мира, перед его сохранением.
diff --git a/doc/ru/Файловая-система-и-сериализация.md b/doc/ru/Файловая-система-и-сериализация.md
new file mode 100644
index 00000000..ed350048
--- /dev/null
+++ b/doc/ru/Файловая-система-и-сериализация.md
@@ -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}/...` возможна потеря данных при перезаписи мира.
diff --git a/res/bindings.toml b/res/config/bindings.toml
similarity index 94%
rename from res/bindings.toml
rename to res/config/bindings.toml
index d0a7e1e9..e13be068 100644
--- a/res/bindings.toml
+++ b/res/config/bindings.toml
@@ -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"
diff --git a/res/config/builtins.list b/res/config/builtins.list
new file mode 100644
index 00000000..df967b96
--- /dev/null
+++ b/res/config/builtins.list
@@ -0,0 +1 @@
+base
diff --git a/res/content/base/scripts/grass_block.lua b/res/content/base/scripts/grass_block.lua
index d92813c4..e58597cc 100644
--- a/res/content/base/scripts/grass_block.lua
+++ b/res/content/base/scripts/grass_block.lua
@@ -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
diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua
index 7ed29c1f..31314ca5 100644
--- a/res/layouts/console.xml.lua
+++ b/res/layouts/console.xml.lua
@@ -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
diff --git a/res/layouts/pages/languages.xml.lua b/res/layouts/pages/languages.xml.lua
index a064c68c..2e3710e9 100644
--- a/res/layouts/pages/languages.xml.lua
+++ b/res/layouts/pages/languages.xml.lua
@@ -9,10 +9,9 @@ function on_open()
table.sort(names)
local panel = document.root
- for _,k in ipairs(names) do
- panel:add(string.format(
- "",
- 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("")
diff --git a/res/layouts/pages/settings_controls.xml.lua b/res/layouts/pages/settings_controls.xml.lua
index 879f21e5..c7c8632c 100644
--- a/res/layouts/pages/settings_controls.xml.lua
+++ b/res/layouts/pages/settings_controls.xml.lua
@@ -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", {
diff --git a/res/layouts/pages/settings_graphics.xml.lua b/res/layouts/pages/settings_graphics.xml.lua
index 835b3746..179d8df8 100644
--- a/res/layouts/pages/settings_graphics.xml.lua
+++ b/res/layouts/pages/settings_graphics.xml.lua
@@ -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(
- "%s",
- id, core.str_setting(id), gui.str(name, "settings")
+ "%s",
+ 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
diff --git a/res/layouts/templates/language.xml b/res/layouts/templates/language.xml
new file mode 100644
index 00000000..fea34b31
--- /dev/null
+++ b/res/layouts/templates/language.xml
@@ -0,0 +1,4 @@
+
diff --git a/res/layouts/templates/track_setting.xml b/res/layouts/templates/track_setting.xml
index 994e5bb3..82ee81c1 100644
--- a/res/layouts/templates/track_setting.xml
+++ b/res/layouts/templates/track_setting.xml
@@ -1,6 +1,6 @@
diff --git a/res/modules/toml.lua b/res/modules/toml.lua
index 33c8faf1..3b66790c 100644
--- a/res/modules/toml.lua
+++ b/res/modules/toml.lua
@@ -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
diff --git a/res/texts/be_BY.txt b/res/texts/be_BY.txt
index c208eeaf..318a95ff 100644
--- a/res/texts/be_BY.txt
+++ b/res/texts/be_BY.txt
@@ -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=Улева
diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt
index 979c7330..4b8d5e5e 100644
--- a/res/texts/en_US.txt
+++ b/res/texts/en_US.txt
@@ -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
diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt
index ec2946ef..f4cdfbc0 100644
--- a/res/texts/ru_RU.txt
+++ b/res/texts/ru_RU.txt
@@ -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=Назад
diff --git a/src/coders/toml.hpp b/src/coders/toml.hpp
index d658eb20..e695a298 100644
--- a/src/coders/toml.hpp
+++ b/src/coders/toml.hpp
@@ -2,8 +2,6 @@
#define CODERS_TOML_HPP_
#include "../data/dynamic.hpp"
-#include "commons.hpp"
-
#include
class SettingsHandler;
diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp
index f3c47b6b..5b7e30b3 100644
--- a/src/content/ContentLoader.cpp
+++ b/src/content/ContentLoader.cpp
@@ -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;
diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp
index e58fbd84..7ff4b972 100644
--- a/src/content/ContentPack.hpp
+++ b/src/content/ContentPack.hpp
@@ -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;
diff --git a/src/core_defs.cpp b/src/core_defs.cpp
index 39c70991..57c85bbc 100644
--- a/src/core_defs.cpp
+++ b/src/core_defs.cpp
@@ -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)
);
}
diff --git a/src/core_defs.hpp b/src/core_defs.hpp
index 884cda26..d57435ad 100644
--- a/src/core_defs.hpp
+++ b/src/core_defs.hpp
@@ -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";
diff --git a/src/data/dynamic.cpp b/src/data/dynamic.cpp
index 4d60d2a5..6b87f97c 100644
--- a/src/data/dynamic.cpp
+++ b/src/data/dynamic.cpp
@@ -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;
}
diff --git a/src/debug/Logger.cpp b/src/debug/Logger.cpp
index 75582642..b14d441b 100644
--- a/src/debug/Logger.cpp
+++ b/src/debug/Logger.cpp
@@ -44,13 +44,14 @@ void Logger::log(LogLevel level, const std::string& name, std::string message) {
auto ms = duration_cast(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 lock(mutex);
auto string = ss.str();
if (file.good()) {
file << string << '\n';
+ file.flush();
}
std::cout << string << std::endl;
}
diff --git a/src/engine.cpp b/src/engine.cpp
index fa34c3c7..4720f909 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -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(resdir, resRoots);
diff --git a/src/engine.hpp b/src/engine.hpp
index 878b1e54..e9ebeb8a 100644
--- a/src/engine.hpp
+++ b/src/engine.hpp
@@ -57,7 +57,7 @@ class Engine : public util::ObjectsKeeper {
std::recursive_mutex postRunnablesMutex;
std::unique_ptr controller;
std::unique_ptr interpreter;
- std::vector basePacks {"base"};
+ std::vector basePacks;
uint64_t frame = 0;
double lastTime = 0.0;
@@ -65,6 +65,7 @@ class Engine : public util::ObjectsKeeper {
std::unique_ptr gui;
+ void loadControls();
void loadSettings();
void saveSettings();
void updateTimers();
diff --git a/src/files/WorldRegions.cpp b/src/files/WorldRegions.cpp
index ffc51e8c..166865b1 100644
--- a/src/files/WorldRegions.cpp
+++ b/src/files/WorldRegions.cpp
@@ -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);
}
diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp
index 509c4055..15e80325 100644
--- a/src/files/engine_paths.cpp
+++ b/src/files/engine_paths.cpp
@@ -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 roots)
diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp
index ac1ed5a8..92353e65 100644
--- a/src/files/engine_paths.hpp
+++ b/src/files/engine_paths.hpp
@@ -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 scanForWorlds();
- fs::path resolve(std::string path);
+ fs::path resolve(std::string path, bool throwErr=true);
};
struct PathsRoot {
diff --git a/src/files/files.hpp b/src/files/files.hpp
index 560d5e1f..142deb25 100644
--- a/src/files/files.hpp
+++ b/src/files/files.hpp
@@ -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);
diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp
index 014af2f0..4362b189 100644
--- a/src/frontend/debug_panel.cpp
+++ b/src/frontend/debug_panel.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
using namespace gui;
@@ -72,15 +73,24 @@ std::shared_ptr 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 create_debug_panel(
}
{
auto bar = std::make_shared(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);
}
{
diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp
index 95aa69f7..42be78f9 100644
--- a/src/frontend/hud.cpp
+++ b/src/frontend/hud.cpp
@@ -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;
diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp
index 5814f3bf..d740ce19 100644
--- a/src/frontend/screens/LevelScreen.cpp
+++ b/src/frontend/screens/LevelScreen.cpp
@@ -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)
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();
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) {
diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp
index c07c6632..6e80af51 100644
--- a/src/graphics/core/Batch2D.cpp
+++ b/src/graphics/core/Batch2D.cpp
@@ -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(buffer, 0, attrs);
+ buffer = std::make_unique(capacity * B2D_VERTEX_SIZE);
+ mesh = std::make_unique(buffer.get(), 0, attrs);
index = 0;
ubyte pixels[] = {
0xFF, 0xFF, 0xFF, 0xFF
};
blank = std::make_unique(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;
}
diff --git a/src/graphics/core/Batch2D.hpp b/src/graphics/core/Batch2D.hpp
index cbd56908..18095cff 100644
--- a/src/graphics/core/Batch2D.hpp
+++ b/src/graphics/core/Batch2D.hpp
@@ -12,13 +12,13 @@ class Texture;
struct UVRegion;
class Batch2D {
- float* buffer;
+ std::unique_ptr buffer;
size_t capacity;
std::unique_ptr mesh;
std::unique_ptr blank;
size_t index;
glm::vec4 color;
- Texture* _texture;
+ Texture* currentTexture;
DrawPrimitive primitive = DrawPrimitive::triangle;
void setPrimitive(DrawPrimitive primitive);
diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp
index 105c8623..7481ec6f 100644
--- a/src/graphics/core/Batch3D.cpp
+++ b/src/graphics/core/Batch3D.cpp
@@ -14,28 +14,29 @@ Batch3D::Batch3D(size_t capacity)
{3}, {2}, {4}, {0}
};
- buffer = new float[capacity * B3D_VERTEX_SIZE];
- mesh = std::make_unique(buffer, 0, attrs);
+ buffer = std::make_unique(capacity * B3D_VERTEX_SIZE);
+ mesh = std::make_unique(buffer.get(), 0, attrs);
index = 0;
ubyte pixels[] = {
255, 255, 255, 255,
};
blank = std::make_unique(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;
}
diff --git a/src/graphics/core/Batch3D.hpp b/src/graphics/core/Batch3D.hpp
index d6cda86b..6ff42caa 100644
--- a/src/graphics/core/Batch3D.hpp
+++ b/src/graphics/core/Batch3D.hpp
@@ -12,28 +12,35 @@ class Mesh;
class Texture;
class Batch3D {
- float* buffer;
+ std::unique_ptr buffer;
size_t capacity;
std::unique_ptr mesh;
std::unique_ptr 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);
diff --git a/src/graphics/core/Texture.hpp b/src/graphics/core/Texture.hpp
index b5d28617..c95bab27 100644
--- a/src/graphics/core/Texture.hpp
+++ b/src/graphics/core/Texture.hpp
@@ -4,7 +4,6 @@
#include "../../typedefs.hpp"
#include "ImageData.hpp"
-#include
#include
class Texture {
diff --git a/src/graphics/render/BlocksPreview.cpp b/src/graphics/render/BlocksPreview.cpp
index 96a8a718..eb7e72cf 100644
--- a/src/graphics/render/BlocksPreview.cpp
+++ b/src/graphics/render/BlocksPreview.cpp
@@ -44,13 +44,18 @@ std::unique_ptr 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 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)));
diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp
index 4b1779d4..9420d070 100644
--- a/src/graphics/render/BlocksRenderer.cpp
+++ b/src/graphics/render/BlocksRenderer.cpp
@@ -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:
diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp
index 23b6d625..244d4674 100644
--- a/src/graphics/render/BlocksRenderer.hpp
+++ b/src/graphics/render/BlocksRenderer.hpp
@@ -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;
diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp
index 7f3f3959..d1011a10 100644
--- a/src/graphics/render/ChunksRenderer.cpp
+++ b/src/graphics/render/ChunksRenderer.cpp
@@ -56,19 +56,16 @@ ChunksRenderer::~ChunksRenderer() {
}
std::shared_ptr ChunksRenderer::render(std::shared_ptr 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 ChunksRenderer::getOrRender(std::shared_ptr chunk,
if (found == meshes.end()) {
return render(chunk, important);
}
- if (chunk->isModified()) {
+ if (chunk->flags.modified) {
render(chunk, important);
}
return found->second;
diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp
index 6e5888ce..8d02a750 100644
--- a/src/graphics/render/WorldRenderer.cpp
+++ b/src/graphics/render/WorldRenderer.cpp
@@ -37,6 +37,9 @@
#include
#include
+#include
+#include
+
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& 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(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;
diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp
index b99be9ae..27d1a569 100644
--- a/src/graphics/render/WorldRenderer.hpp
+++ b/src/graphics/render/WorldRenderer.hpp
@@ -8,8 +8,6 @@
#include
#include
-#include
-#include
class Level;
class Player;
@@ -76,8 +74,6 @@ public:
Camera* camera,
const EngineSettings& settings
);
-
- static float fog;
};
diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp
index 63576877..c7ef8a1f 100644
--- a/src/graphics/ui/GUI.cpp
+++ b/src/graphics/ui/GUI.cpp
@@ -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(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(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
diff --git a/src/graphics/ui/elements/CheckBox.hpp b/src/graphics/ui/elements/CheckBox.hpp
index e366768e..cc0593c8 100644
--- a/src/graphics/ui/elements/CheckBox.hpp
+++ b/src/graphics/ui/elements/CheckBox.hpp
@@ -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);
+ }
};
}
diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp
index fe8d77ba..e31cde11 100644
--- a/src/graphics/ui/elements/Container.cpp
+++ b/src/graphics/ui/elements/Container.cpp
@@ -127,6 +127,14 @@ void Container::remove(std::shared_ptr 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);
diff --git a/src/graphics/ui/elements/Container.hpp b/src/graphics/ui/elements/Container.hpp
index 8aa38b2f..be385f35 100644
--- a/src/graphics/ui/elements/Container.hpp
+++ b/src/graphics/ui/elements/Container.hpp
@@ -26,6 +26,7 @@ namespace gui {
virtual void add(std::shared_ptr node, glm::vec2 pos);
virtual void clear();
virtual void remove(std::shared_ptr 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);
diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp
index 43b6304e..ac454c42 100644
--- a/src/graphics/ui/elements/TextBox.cpp
+++ b/src/graphics/ui/elements/TextBox.cpp
@@ -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;
diff --git a/src/lighting/LightSolver.cpp b/src/lighting/LightSolver.cpp
index 0656ee53..f9865956 100644
--- a/src/lighting/LightSolver.cpp
+++ b/src/lighting/LightSolver.cpp
@@ -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)];
diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp
index 6cbb4aba..9ddf12b8 100644
--- a/src/logic/BlocksController.cpp
+++ b/src/logic/BlocksController.cpp
@@ -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);
}
diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp
index 1c039a14..56eab88c 100644
--- a/src/logic/ChunksController.cpp
+++ b/src/logic/ChunksController.cpp
@@ -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) {
}
}
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;
}
diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp
index 44e73512..beb452f6 100644
--- a/src/logic/PlayerController.cpp
+++ b/src/logic/PlayerController.cpp
@@ -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);
+ }
+ }
}
}
diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp
index d48a3a51..46e5cbca 100644
--- a/src/logic/PlayerController.hpp
+++ b/src/logic/PlayerController.hpp
@@ -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,
diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libblock.cpp
index 695d8e36..22e76b84 100644
--- a/src/logic/scripting/lua/libblock.cpp
+++ b/src/logic/scripting/lua/libblock.cpp
@@ -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(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(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(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(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},
{"name", lua_wrap_errors},
{"material", lua_wrap_errors},
+ {"caption", lua_wrap_errors},
{"defs_count", lua_wrap_errors},
{"is_solid_at", lua_wrap_errors},
{"is_replaceable_at", lua_wrap_errors},
diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp
index 86b4e9f6..b9df74f2 100644
--- a/src/logic/scripting/lua/libcore.cpp
+++ b/src/logic/scripting/lua/libcore.cpp
@@ -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},
{"delete_world", lua_wrap_errors},
{"reconfig_packs", lua_wrap_errors},
- {"get_bindings", lua_wrap_errors},
{"get_setting", lua_wrap_errors},
{"set_setting", lua_wrap_errors},
{"str_setting", lua_wrap_errors},
diff --git a/src/logic/scripting/lua/libfile.cpp b/src/logic/scripting/lua/libfile.cpp
index b14a15b4..6b97da3c 100644
--- a/src/logic/scripting/lua/libfile.cpp
+++ b/src/logic/scripting/lua/libfile.cpp
@@ -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(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 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");
}
diff --git a/src/logic/scripting/lua/libgui.cpp b/src/logic/scripting/lua/libgui.cpp
index b4f4d9c3..3c738dde 100644
--- a/src/logic/scripting/lua/libgui.cpp
+++ b/src/logic/scripting/lua/libgui.cpp
@@ -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(docnode.node);
+ engine->getGUI()->postRunnable([node]() {
+ auto parent = node->getParent();
+ if (auto container = dynamic_cast(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(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(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(node)) {
- return state->pushcfunction(l_container_add);
+ return state->pushcfunction(lua_wrap_errors);
}
return 0;
}
+static int p_get_destruct(UINode*) {
+ return state->pushcfunction(lua_wrap_errors);
+}
+
static int p_get_clear(UINode* node) {
if (dynamic_cast(node)) {
- return state->pushcfunction(l_container_clear);
+ return state->pushcfunction(lua_wrap_errors);
+ }
+ return 0;
+}
+
+static int p_set_interval(UINode* node) {
+ if (dynamic_cast(node)) {
+ return state->pushcfunction(lua_wrap_errors);
}
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},
diff --git a/src/logic/scripting/lua/libinput.cpp b/src/logic/scripting/lua/libinput.cpp
index beb2c5de..309cc26b 100644
--- a/src/logic/scripting/lua/libinput.cpp
+++ b/src/logic/scripting/lua/libinput.cpp
@@ -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(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},
+ {"mousecode", lua_wrap_errors},
{"add_callback", lua_wrap_errors},
{"get_mouse_pos", lua_wrap_errors},
+ {"get_bindings", lua_wrap_errors},
{NULL, NULL}
};
diff --git a/src/logic/scripting/lua/libjson.cpp b/src/logic/scripting/lua/libjson.cpp
index d868d2da..7d3be119 100644
--- a/src/logic/scripting/lua/libjson.cpp
+++ b/src/logic/scripting/lua/libjson.cpp
@@ -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);
scripting::state->pushvalue(element);
return 1;
}
const luaL_Reg jsonlib [] = {
- {"stringify", lua_wrap_errors},
+ {"tostring", lua_wrap_errors},
{"parse", lua_wrap_errors},
{NULL, NULL}
};
diff --git a/src/logic/scripting/lua/libplayer.cpp b/src/logic/scripting/lua/libplayer.cpp
index 3ce71759..045aae37 100644
--- a/src/logic/scripting/lua/libplayer.cpp
+++ b/src/logic/scripting/lua/libplayer.cpp
@@ -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},
{"set_pos", lua_wrap_errors},
@@ -144,5 +158,6 @@ const luaL_Reg playerlib [] = {
{"set_flight", lua_wrap_errors},
{"is_noclip", lua_wrap_errors},
{"set_noclip", lua_wrap_errors},
+ {"get_selected_block", lua_wrap_errors},
{NULL, NULL}
};
diff --git a/src/logic/scripting/lua/libtoml.cpp b/src/logic/scripting/lua/libtoml.cpp
index 4bb4a123..86411d42 100644
--- a/src/logic/scripting/lua/libtoml.cpp
+++ b/src/logic/scripting/lua/libtoml.cpp
@@ -31,7 +31,7 @@ static int l_toml_parse(lua_State*) {
}
const luaL_Reg tomllib [] = {
- {"serialize", lua_wrap_errors},
- {"deserialize", lua_wrap_errors},
+ {"tostring", lua_wrap_errors},
+ {"parse", lua_wrap_errors},
{NULL, NULL}
};
diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp
index 3dd9e4be..0afeda5c 100644
--- a/src/objects/Player.hpp
+++ b/src/objects/Player.hpp
@@ -44,8 +44,9 @@ public:
std::shared_ptr currentCamera;
std::unique_ptr 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 inv);
~Player();
diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp
index 6760aa89..4370be30 100644
--- a/src/physics/PhysicsSolver.cpp
+++ b/src/physics/PhysicsSolver.cpp
@@ -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;
diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp
index e35f364c..6e940f99 100644
--- a/src/physics/PhysicsSolver.hpp
+++ b/src/physics/PhysicsSolver.hpp
@@ -2,6 +2,7 @@
#define PHYSICS_PHYSICSSOLVER_HPP_
#include "../typedefs.hpp"
+#include "../voxels/voxel.hpp"
#include
@@ -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_
diff --git a/src/settings.hpp b/src/settings.hpp
index 165dcbec..250d0e4b 100644
--- a/src/settings.hpp
+++ b/src/settings.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
diff --git a/src/voxel_engine.cpp b/src/voxel_engine.cpp
index 33d18c0e..c6a2ec7e 100644
--- a/src/voxel_engine.cpp
+++ b/src/voxel_engine.cpp
@@ -5,7 +5,6 @@
#include "util/platform.hpp"
#include "util/command_line.hpp"
#include "debug/Logger.hpp"
-#include "objects/Player.hpp"
#include
@@ -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))
diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp
index 0c4b3b68..4628dd0e 100644
--- a/src/voxels/Block.hpp
+++ b/src/voxels/Block.hpp
@@ -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 modelTextures = {};
- std::vector modelBoxes = {};
- std::vector modelExtraPoints = {}; //initially made for tetragons
- std::vector modelUVs = {}; // boxes' tex-UVs also there
+ std::string textureFaces[6]; // -x,x, -y,y, -z,z
+
+ std::vector modelTextures = {};
+ std::vector modelBoxes = {};
+ std::vector modelExtraPoints = {}; //initially made for tetragons
+ std::vector 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 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 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_ */
diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp
index bfd79cda..18dabec2 100644
--- a/src/voxels/Chunk.cpp
+++ b/src/voxels/Chunk.cpp
@@ -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,
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 Chunk::getBlockInventory(uint x, uint y, uint z) const {
@@ -70,12 +66,12 @@ std::shared_ptr Chunk::getBlockInventory(uint x, uint y, uint z) cons
}
std::unique_ptr Chunk::clone() const {
- auto other = std::make_unique(x,z);
- for (uint i = 0; i < CHUNK_VOL; i++) {
- other->voxels[i] = voxels[i];
+ auto other = std::make_unique(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::clone() const {
Total size: (CHUNK_VOL * 4) bytes
*/
std::unique_ptr Chunk::encode() const {
- auto buffer = std::make_unique(CHUNK_DATA_LEN);
- for (uint i = 0; i < CHUNK_VOL; i++) {
- buffer[i] = voxels[i].id >> 8;
+ auto buffer = std::make_unique(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) {
diff --git a/src/voxels/Chunk.hpp b/src/voxels/Chunk.hpp
index 019b4f09..535b6125 100644
--- a/src/voxels/Chunk.hpp
+++ b/src/voxels/Chunk.hpp
@@ -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 clone() const;
-
- // flags getters/setters below
- inline void setFlags(int mask, bool value){
- if (value)
- flags |= mask;
- else
- flags &= ~(mask);
- }
+ std::unique_ptr clone() const;
/* Creates new block inventory given size
@return inventory id or 0 if block does not exists */
void addBlockInventory(std::shared_ptr 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 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 encode() const;
+ std::unique_ptr 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);
};
diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp
index 63919499..b9a63e9e 100644
--- a/src/voxels/Chunks.cpp
+++ b/src/voxels/Chunks.cpp
@@ -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& 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& 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);
}
diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp
index d2eb8cea..7dd4312d 100644
--- a/src/voxels/Chunks.hpp
+++ b/src/voxels/Chunks.hpp
@@ -5,6 +5,8 @@
#include
#include
#include
+
+#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,
diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp
index fa28f4f6..8a9c97b4 100644
--- a/src/voxels/ChunksStorage.cpp
+++ b/src/voxels/ChunksStorage.cpp
@@ -61,7 +61,7 @@ std::shared_ptr 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 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;
}
diff --git a/src/voxels/DefaultWorldGenerator.cpp b/src/voxels/DefaultWorldGenerator.cpp
index b0c8561b..49747519 100644
--- a/src/voxels/DefaultWorldGenerator.cpp
+++ b/src/voxels/DefaultWorldGenerator.cpp
@@ -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;
}
}
}
diff --git a/src/voxels/FlatWorldGenerator.cpp b/src/voxels/FlatWorldGenerator.cpp
index 8e5c440e..f729d32b 100644
--- a/src/voxels/FlatWorldGenerator.cpp
+++ b/src/voxels/FlatWorldGenerator.cpp
@@ -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;
}
}
}
diff --git a/src/voxels/voxel.hpp b/src/voxels/voxel.hpp
index db2c8138..9cfaf698 100644
--- a/src/voxels/voxel.hpp
+++ b/src/voxels/voxel.hpp
@@ -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(b.rotation) |
+ static_cast(b.segment) << 3 |
+ static_cast(b.reserved) << 5 |
+ static_cast(b.userbits) << 8;
+}
+
+inline constexpr blockstate int2blockstate(blockstate_t i) {
+ return {
+ static_cast(i & 0b111),
+ static_cast((i >> 3) & 0b11),
+ static_cast((i >> 5) & 0b111),
+ static_cast((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_
diff --git a/src/window/Events.cpp b/src/window/Events.cpp
index 76158230..1aab7881 100644
--- a/src/window/Events.cpp
+++ b/src/window/Events.cpp
@@ -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(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(binding.code));
+ break;
+ case inputtype::mouse:
+ value = "mouse:"+input_util::get_name(static_cast(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(&entry.second)) {
diff --git a/src/window/Events.hpp b/src/window/Events.hpp
index c8d354d3..97405d2c 100644
--- a/src/window/Events.hpp
+++ b/src/window/Events.hpp
@@ -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_
diff --git a/src/window/input.cpp b/src/window/input.cpp
index 33621f70..cfb06e84 100644
--- a/src/window/input.cpp
+++ b/src/window/input.cpp
@@ -40,6 +40,25 @@ static std::unordered_map mousecodes {
{"middle", GLFW_MOUSE_BUTTON_3},
};
+static std::unordered_map 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(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) {
diff --git a/src/window/input.hpp b/src/window/input.hpp
index 21ca63c1..50e26446 100644
--- a/src/window/input.hpp
+++ b/src/window/input.hpp
@@ -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 {
diff --git a/src/window/input.md b/src/window/input.md
deleted file mode 100644
index aa1829bd..00000000
--- a/src/window/input.md
+++ /dev/null
@@ -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
diff --git a/src/world/Level.hpp b/src/world/Level.hpp
index e64871ab..7b18f06f 100644
--- a/src/world/Level.hpp
+++ b/src/world/Level.hpp
@@ -58,8 +58,8 @@ public:
std::shared_ptr tObj = std::make_shared(args...);
std::shared_ptr