diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 00000000..371e1ac1 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,51 @@ +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: Make fix_dylibs.sh executable + run: chmod +x fix_dylibs.sh + + - name: Fix dylibs + run: ./fix_dylibs.sh VoxelEngine Release build + + - name: Create DMG + run: | + mkdir VoxelEngineDmgContent + cp -r build/res VoxelEngineDmgContent/ + cp -r build/VoxelEngine VoxelEngineDmgContent/ + cp -r build/libs VoxelEngineDmgContent/libs + 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/en/8.Scripting.md b/doc/en/8.Scripting.md index e18ab0ad..5aa1f8cb 100644 --- a/doc/en/8.Scripting.md +++ b/doc/en/8.Scripting.md @@ -52,10 +52,10 @@ player.set_pos(playerid: int, x: number, y: number, z: number) Set player position ```python -player.get_rot(playerid: int) -> number, number +player.get_rot(playerid: int) -> number, number, number ``` -Returns x, y of camera rotation (radians) +Returns x, y, z of camera rotation (radians) ```python player.set_rot(playerid: int, x: number, y: number, z: number) @@ -69,6 +69,20 @@ player.get_inventory(playerid: int) -> int, int Returns player inventory ID and selected slot index (0-9) +```python +player.is_flight() -> bool +player.set_flight(bool) +``` + +Getter and setter for player flight mode + +```python +player.is_noclip() -> bool +player.set_noclip(bool) +``` + +Getter and setter for player noclip mode (collisions disabled) + ## *world* library ```python diff --git a/doc/ru/5.XML-разметка-интерфейса.md b/doc/ru/5.XML-разметка-интерфейса.md index 17f4e701..93c23c93 100644 --- a/doc/ru/5.XML-разметка-интерфейса.md +++ b/doc/ru/5.XML-разметка-интерфейса.md @@ -26,14 +26,31 @@ # Общие атрибуты элементов +- `enabled` - при значении false блокирует элемент, в отличие от interactive, обозначая это состояние визуально. - `id` - идентификатор элемента. Тип: строка. - `pos` - позиция элемента. Тип: 2D вектор. - `size` - размер элемента. Тип: 2D вектор. +- `context` - указывает контекст перевода для `@`-строк. - `color` - цвет элемента. Тип: RGBA цвет. +- `hover-color` - цвет элемента при наведении курсора. Тип: RGBA цвет. +- `pressed-color` - цвет элемента при нажатии на элемент. Тип: RGBA цвет. - `margin` - внешний отступ элемента. Тип: 4D вектор. Порядок: `"left,top,right,bottom"` - `visible` - видимость элемента. Тип: логический ("true"/"false"). - `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open. +- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open. +- `onclick` - lua функция вызываемая при нажатии на элемент. +- `ondoubleclick` - lua функция вызываемая при двойном нажатии на элемент. +- `tooltip` - текст всплывающей подсказки +- `tooltip-delay` - задержка появления всплывающей подсказки +- `gravity` - автоматическое позиционирование элемента в контейнере. (Не работает в автоматических контейнерах, как panel). Значения: *top-left, top-center, top-right, center-left, center-center, center-right, bottom-left, bottom-center, bottom-right*. +- `z-index` - определяет порядок элементов, при большем значении будет перекрывать элементы с меньшим. +- `interactive` - при значении false наведение на элемент и все под-элементы будет игнорироваться. + +# Атрибуты шаблонов + +- `if` при значениях ('', 'false', 'nil') элемент будет проигнорирован, включая под-элементы. +- `ifnot` то же, что и `if`, но с обратным условием. # Общие атрибуты контейнеров @@ -46,47 +63,69 @@ В число панелей также входят кнопки. - `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число +- `orientation` - ориентация панели: horizontal/vertical. # Основные элементы -## Кнопка `button` +## Кнопка - *button* Внутренний текст - текст кнопки. - `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка. -- `onclick` - lua функция вызываемая при нажатии на кнопку. -## Изображение `image` +## Флажок - *checkbox* + +- `checked` - определяет состояние отметки. +- `supplier` - поставщик состояния отметки (вызывается каждый кадр) +- `consumer` - lua функция-приемник состояния отметки. Вызывается только при завершении ввода +## Метка - *label* + +- `valign` - вертикальное выравнивание текста: top/center/bottom +- `supplier` - поставщик текста (вызывается каждый кадр) +- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. +- `multiline` - разрешает отображение многострочного текста. +- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") + +## Изображение - *image* - `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error` -# Текстовое поле `textbox` +## Текстовое поле - *textbox* Внутренний текст - изначально введенный текст - `placeholder` - текст подстановки (используется текстовое поле пусто) +- `supplier` - поставщик текста (вызывается каждый кадр) - `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода - -## Ползунок `trackbar` +- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. +- `multiline` - разрешает отображение многострочного текста. +- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") +- `editable`- определяет возможность редактирования текста. +- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет. +- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен. +- `onup` - lua функция вызываемая при нажатии стрелки вверх. +- `ondown` - lua функция вызываемая при нажатии стрелки вниз. +## Ползунок - *trackbar* - `min` - минимальное значение. Тип: число. По-умолчанию: 0 - `max` - максимальное значение. Тип: число. По-умолчанию: 1 - `value` - изначальное значение. Тип: число. По-умолчанию: 0 - `step` - размер деления ползунка. Тип: число. По-умолчанию: 1 -- `track-width` - ширина указателя (в делениях). Тип: число. По-умолчанию: 1 +- `track-width` - ширина указателя (в пикселях). Тип: число. По-умолчанию: 12 +- `track-color` - цвет указателя при наведении курсора. Тип: RGBA цвет. - `consumer` - lua функция-приемник установленного значения - `supplier` - lua функция-поставщик значения # Элементы инвентаря -## Инвентарь `inventory` +## Инвентарь - *inventory* Элемент является контейнером. На данный момент не имеет специфических атрибутов. > [!WARNING] > Расположение инвентарей управляется движком и не может быть изменено свойствами pos, margin и т.д. -## Одиночный слот `slot` +## Одиночный слот - *slot* Элемент должен находиться внутри `inventory` элемента, без посредников. - `index` - индекс слота инвентаря. (Нумерация с 0) @@ -95,7 +134,7 @@ - `updatefunc` - lua событие вызываемое при изменении содержимого слота - `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота -## Решетка слотов `slots-grid` +## Сетка слотов - *slots-grid* Элемент должен находиться внутри `inventory` элемента, без посредников. - `start-index` - индекс первого слота diff --git a/doc/ru/8.Скриптинг.md b/doc/ru/8.Скриптинг.md index 82f27159..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 (расширение не указывается) ``` @@ -46,10 +42,10 @@ player.set_pos(playerid: int, x: number, y: number, z: number) Устанавливает x, y, z координаты игрока ```python -player.get_rot(playerid: int) -> number, number +player.get_rot(playerid: int) -> number, number, number ``` -Возвращает x, y вращения камеры (в радианах) +Возвращает x, y, z вращения камеры (в радианах) ```python player.set_rot(playerid: int, x: number, y: number, z: number) @@ -63,8 +59,37 @@ player.get_inventory(playerid: int) -> int, int Возвращает id инвентаря игрока и индекс выбранного слота (от 0 до 9) +```python +player.is_flight() -> bool +player.set_flight(bool) +``` + +Геттер и сеттер режима полета + +```python +player.is_noclip() -> bool +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 ``` @@ -89,6 +114,12 @@ world.get_seed() -> int Возвращает зерно мира. +```python +world.exists() -> bool +``` + +Проверяет существование мира по имени. + ## Библиотека pack ```python @@ -109,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: @@ -120,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 Библиотека функций для работы с инвентарем. @@ -197,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 ``` @@ -359,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 @@ -566,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/fix_dylibs.sh b/fix_dylibs.sh new file mode 100755 index 00000000..779336b3 --- /dev/null +++ b/fix_dylibs.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +PROJECT_NAME=$1 +CONFIG=$2 +OUTPUT_DIR=$3 +LIBS_DIR="$OUTPUT_DIR/libs" + +mkdir -p "$LIBS_DIR" + +TMP_BINARY="$OUTPUT_DIR/tmp_$PROJECT_NAME" +BINARY="$OUTPUT_DIR/$PROJECT_NAME" + +cp "$BINARY" "$TMP_BINARY" + +otool -L "$TMP_BINARY" | grep -o '/.*dylib' | while read -r dylib; do + if [[ "$dylib" == /usr/lib/* || "$dylib" == /System/Library/* ]]; then + continue + fi + + cp "$dylib" "$LIBS_DIR" + install_name_tool -change "$dylib" "@executable_path/libs/$(basename "$dylib")" "$TMP_BINARY" +done + +mv "$TMP_BINARY" "$BINARY" diff --git a/res/config/bindings.toml b/res/config/bindings.toml new file mode 100644 index 00000000..e13be068 --- /dev/null +++ b/res/config/bindings.toml @@ -0,0 +1,18 @@ +devtools.console="key:grave-accent" +chunks.reload="key:f5" +movement.forward="key:w" +movement.back="key:s" +movement.left="key:a" +movement.right="key:d" +movement.jump="key:space" +movement.sprint="key:left-ctrl" +movement.crouch="key:left-shift" +movement.cheat="key:r" +camera.zoom="key:c" +camera.mode="key:f4" +player.noclip="key:n" +player.flight="key:f" +player.attack="mouse:left" +player.build="mouse:right" +player.pick="mouse:middle" +hud.inventory="key:tab" 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 a66e17e3..c7c8632c 100644 --- a/res/layouts/pages/settings_controls.xml.lua +++ b/res/layouts/pages/settings_controls.xml.lua @@ -16,7 +16,8 @@ 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", { id=name, name=gui.str(name) 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 fb082e75..3b66790c 100644 --- a/res/modules/toml.lua +++ b/res/modules/toml.lua @@ -1,65 +1,4 @@ --- TOML serialization module -local toml = {} - --- Convert table to TOML -function toml.serialize(tb, isinner) - local text = "" - for k, v in pairs(tb) do - local tp = type(v) - if tp ~= "table" then - text = text..k.." = " - if tp == "string" then - text = text..string.format("%q", v) - else - text = text..tostring(v) - end - text = text.."\n" - end - end - for k, v in pairs(tb) do - local tp = type(v) - if tp == "table" then - if isinner then - error("only one level of subtables supported") - end - text = text.."["..k.."]\n"..toml.serialize(v).."\n" - end - end - return text -end - --- Parse TOML to new table -function toml.deserialize(s) - local output = {} - local current = output - local lines = {} - for line in string.gmatch(s, "[^\r\n]+") do - line = string.gsub(line, "%s+", "") - table.insert(lines, line) - end - for i = 1,#lines do - local s = lines[i] - if string.sub(s, 1, 1) == "[" then - local section = s.sub(s, 2, #s-1) - current = {} - output[section] = current - else - for k, v in string.gmatch(s, "(%w+)=(.+)" ) do - v = string.gsub(v, "%s+", "") - if v.sub(v, 1, 1) == "\"" then - current[k] = v.sub(v, 2, #v-1) - elseif v == "true" or v == "false" then - current[k] = v == "true" - end - - local num = tonumber(v) - if num ~= nil then - current[k] = num - end - end - end - end - return output -end - +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/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 48741102..0db4d208 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -51,7 +51,7 @@ console.add_command( ) console.add_command( - "obj.tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z", + "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)) 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/assets/Assets.cpp b/src/assets/Assets.cpp index d1c93360..12a3db65 100644 --- a/src/assets/Assets.cpp +++ b/src/assets/Assets.cpp @@ -18,8 +18,8 @@ Texture* Assets::getTexture(std::string name) const { return found->second.get(); } -void Assets::store(Texture* texture, std::string name){ - textures.emplace(name, texture); +void Assets::store(std::unique_ptr texture, std::string name){ + textures.emplace(name, std::move(texture)); } @@ -30,8 +30,8 @@ Shader* Assets::getShader(std::string name) const{ return found->second.get(); } -void Assets::store(Shader* shader, std::string name){ - shaders.emplace(name, shader); +void Assets::store(std::unique_ptr shader, std::string name){ + shaders.emplace(name, std::move(shader)); } Font* Assets::getFont(std::string name) const { @@ -41,8 +41,8 @@ Font* Assets::getFont(std::string name) const { return found->second.get(); } -void Assets::store(Font* font, std::string name){ - fonts.emplace(name, font); +void Assets::store(std::unique_ptr font, std::string name){ + fonts.emplace(name, std::move(font)); } Atlas* Assets::getAtlas(std::string name) const { @@ -52,8 +52,8 @@ Atlas* Assets::getAtlas(std::string name) const { return found->second.get(); } -void Assets::store(Atlas* atlas, std::string name){ - atlases.emplace(name, atlas); +void Assets::store(std::unique_ptr atlas, std::string name){ + atlases.emplace(name, std::move(atlas)); } audio::Sound* Assets::getSound(std::string name) const { @@ -63,8 +63,8 @@ audio::Sound* Assets::getSound(std::string name) const { return found->second.get(); } -void Assets::store(audio::Sound* sound, std::string name) { - sounds.emplace(name, sound); +void Assets::store(std::unique_ptr sound, std::string name) { + sounds.emplace(name, std::move(sound)); } const std::vector& Assets::getAnimations() { @@ -82,6 +82,6 @@ UiDocument* Assets::getLayout(std::string name) const { return found->second.get(); } -void Assets::store(UiDocument* layout, std::string name) { - layouts[name] = std::shared_ptr(layout); +void Assets::store(std::unique_ptr layout, std::string name) { + layouts[name] = std::shared_ptr(std::move(layout)); } diff --git a/src/assets/Assets.hpp b/src/assets/Assets.hpp index a9d4b927..c06e764f 100644 --- a/src/assets/Assets.hpp +++ b/src/assets/Assets.hpp @@ -39,25 +39,25 @@ public: ~Assets(); Texture* getTexture(std::string name) const; - void store(Texture* texture, std::string name); + void store(std::unique_ptr texture, std::string name); Shader* getShader(std::string name) const; - void store(Shader* shader, std::string name); + void store(std::unique_ptr shader, std::string name); Font* getFont(std::string name) const; - void store(Font* font, std::string name); + void store(std::unique_ptr font, std::string name); Atlas* getAtlas(std::string name) const; - void store(Atlas* atlas, std::string name); + void store(std::unique_ptr atlas, std::string name); audio::Sound* getSound(std::string name) const; - void store(audio::Sound* sound, std::string name); + void store(std::unique_ptr sound, std::string name); const std::vector& getAnimations(); void store(const TextureAnimation& animation); UiDocument* getLayout(std::string name) const; - void store(UiDocument* layout, std::string name); + void store(std::unique_ptr layout, std::string name); }; #endif // ASSETS_ASSETS_HPP_ diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 8d9119ee..1f194c21 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -11,6 +11,7 @@ #include "../files/engine_paths.hpp" #include "../content/Content.hpp" #include "../content/ContentPack.hpp" +#include "../voxels/Block.hpp" #include "../graphics/core/Texture.hpp" #include "../logic/scripting/scripting.hpp" @@ -187,7 +188,7 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { loader.processPreloadConfigs(content); for (auto& entry : content->getBlockMaterials()) { - auto& material = entry.second; + auto& material = *entry.second; loader.tryAddSound(material.stepsSound); loader.tryAddSound(material.placeSound); loader.tryAddSound(material.breakSound); @@ -217,7 +218,7 @@ bool AssetsLoader::loadExternalTexture( if (fs::exists(path)) { try { auto image = imageio::read(path.string()); - assets->store(Texture::from(image.get()).release(), name); + assets->store(Texture::from(image.get()), name); return true; } catch (const std::exception& err) { logger.error() << "error while loading external " diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index ea4560b5..bfe799bb 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -6,6 +6,7 @@ #include "../audio/audio.hpp" #include "../files/files.hpp" #include "../files/engine_paths.hpp" +#include "../coders/commons.hpp" #include "../coders/imageio.hpp" #include "../coders/json.hpp" #include "../coders/GLSLExtension.hpp" @@ -43,7 +44,7 @@ assetload::postfunc assetload::texture( imageio::read(paths->find(filename+".png").u8string()).release() ); return [name, image](auto assets) { - assets->store(Texture::from(image.get()).release(), name); + assets->store(Texture::from(image.get()), name); }; } @@ -102,7 +103,7 @@ assetload::postfunc assetload::atlas( Atlas* atlas = builder.build(2, false).release(); return [=](auto assets) { atlas->prepare(); - assets->store(atlas, name); + assets->store(std::unique_ptr(atlas), name); for (const auto& file : names) { animation(assets, paths, name, directory, file, atlas); } @@ -128,7 +129,7 @@ assetload::postfunc assetload::font( for (auto& page : *pages) { textures.emplace_back(Texture::from(page.get())); } - assets->store(new Font(std::move(textures), res, 4), name); + assets->store(std::make_unique(std::move(textures), res, 4), name); }; } @@ -142,8 +143,7 @@ assetload::postfunc assetload::layout( return [=](auto assets) { try { auto cfg = std::dynamic_pointer_cast(config); - auto document = UiDocument::read(cfg->env, name, file); - assets->store(document.release(), name); + assets->store(UiDocument::read(cfg->env, name, file), name); } catch (const parsing_error& err) { throw std::runtime_error( "failed to parse layout XML '"+file+"':\n"+err.errorLog() @@ -189,7 +189,7 @@ assetload::postfunc assetload::sound( } auto sound = baseSound.release(); return [=](auto assets) { - assets->store(sound, name); + assets->store(std::unique_ptr(sound), name); }; } @@ -304,7 +304,7 @@ static bool animation( auto animation = create_animation( srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList ); - assets->store(srcAtlas.release(), atlasName + "/" + name + "_animation"); + assets->store(std::move(srcAtlas), atlasName + "/" + name + "_animation"); assets->store(animation); return true; } diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index bf5f7f13..939aa4ad 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -389,7 +389,7 @@ std::unique_ptr ALAudio::openStream(std::shared_ptr stream, b return std::make_unique(this, stream, keepSource); } -ALAudio* ALAudio::create() { +std::unique_ptr ALAudio::create() { ALCdevice* device = alcOpenDevice(nullptr); if (device == nullptr) return nullptr; @@ -400,7 +400,7 @@ ALAudio* ALAudio::create() { } AL_CHECK(); logger.info() << "initialized"; - return new ALAudio(device, context); + return std::make_unique(device, context); } uint ALAudio::getFreeSource(){ diff --git a/src/audio/AL/ALAudio.hpp b/src/audio/AL/ALAudio.hpp index 294b0224..f0a22dde 100644 --- a/src/audio/AL/ALAudio.hpp +++ b/src/audio/AL/ALAudio.hpp @@ -136,9 +136,8 @@ namespace audio { std::vector freebuffers; uint maxSources = 256; - - ALAudio(ALCdevice* device, ALCcontext* context); public: + ALAudio(ALCdevice* device, ALCcontext* context); ~ALAudio(); uint getFreeSource(); @@ -164,7 +163,7 @@ namespace audio { return false; } - static ALAudio* create(); + static std::unique_ptr create(); }; } diff --git a/src/audio/NoAudio.cpp b/src/audio/NoAudio.cpp index 3000b891..04dd2b77 100644 --- a/src/audio/NoAudio.cpp +++ b/src/audio/NoAudio.cpp @@ -17,6 +17,6 @@ std::unique_ptr NoAudio::openStream(std::shared_ptr stream, b return std::make_unique(stream, keepSource); } -NoAudio* NoAudio::create() { - return new NoAudio(); +std::unique_ptr NoAudio::create() { + return std::make_unique(); } diff --git a/src/audio/NoAudio.hpp b/src/audio/NoAudio.hpp index e24fb5c6..bc105139 100644 --- a/src/audio/NoAudio.hpp +++ b/src/audio/NoAudio.hpp @@ -81,7 +81,7 @@ namespace audio { return true; } - static NoAudio* create(); + static std::unique_ptr create(); }; } diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 3829c16a..9fe62de0 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -147,11 +147,11 @@ public: void audio::initialize(bool enabled) { if (enabled) { - backend = ALAudio::create(); + backend = ALAudio::create().release(); } if (backend == nullptr) { std::cerr << "could not to initialize audio" << std::endl; - backend = NoAudio::create(); + backend = NoAudio::create().release(); } create_channel("master"); } @@ -333,7 +333,7 @@ int audio::create_channel(const std::string& name) { if (index != -1) { return index; } - channels.emplace_back(new Channel(name)); + channels.emplace_back(std::make_unique(name)); return channels.size()-1; } diff --git a/src/coders/binary_json.hpp b/src/coders/binary_json.hpp index ba7531bc..c79a7d4d 100644 --- a/src/coders/binary_json.hpp +++ b/src/coders/binary_json.hpp @@ -11,20 +11,20 @@ namespace dynamic { } namespace json { - const int BJSON_END = 0x0; - const int BJSON_TYPE_DOCUMENT = 0x1; - const int BJSON_TYPE_LIST = 0x2; - const int BJSON_TYPE_BYTE = 0x3; - const int BJSON_TYPE_INT16 = 0x4; - const int BJSON_TYPE_INT32 = 0x5; - const int BJSON_TYPE_INT64 = 0x6; - const int BJSON_TYPE_NUMBER = 0x7; - const int BJSON_TYPE_STRING = 0x8; - const int BJSON_TYPE_BYTES = 0x9; - const int BJSON_TYPE_FALSE = 0xA; - const int BJSON_TYPE_TRUE = 0xB; - const int BJSON_TYPE_NULL = 0xC; - const int BJSON_TYPE_CDOCUMENT = 0x1F; + inline constexpr int BJSON_END = 0x0; + inline constexpr int BJSON_TYPE_DOCUMENT = 0x1; + inline constexpr int BJSON_TYPE_LIST = 0x2; + inline constexpr int BJSON_TYPE_BYTE = 0x3; + inline constexpr int BJSON_TYPE_INT16 = 0x4; + inline constexpr int BJSON_TYPE_INT32 = 0x5; + inline constexpr int BJSON_TYPE_INT64 = 0x6; + inline constexpr int BJSON_TYPE_NUMBER = 0x7; + inline constexpr int BJSON_TYPE_STRING = 0x8; + inline constexpr int BJSON_TYPE_BYTES = 0x9; + inline constexpr int BJSON_TYPE_FALSE = 0xA; + inline constexpr int BJSON_TYPE_TRUE = 0xB; + inline constexpr int BJSON_TYPE_NULL = 0xC; + inline constexpr int BJSON_TYPE_CDOCUMENT = 0x1F; extern std::vector to_binary(const dynamic::Map* obj, bool compress=false); extern std::shared_ptr from_binary(const ubyte* src, size_t size); diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 89f06354..19b59726 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -1,5 +1,7 @@ #include "json.hpp" +#include "commons.hpp" + #include "../data/dynamic.hpp" #include "../util/stringutil.hpp" @@ -240,11 +242,11 @@ Value Parser::parseValue() { throw error("unexpected character '"+std::string({next})+"'"); } -std::unique_ptr json::parse(const std::string& filename, const std::string& source) { +dynamic::Map_sptr json::parse(const std::string& filename, const std::string& source) { Parser parser(filename, source); return parser.parse(); } -std::unique_ptr json::parse(const std::string& source) { +dynamic::Map_sptr json::parse(const std::string& source) { return parse("", source); } diff --git a/src/coders/json.hpp b/src/coders/json.hpp index 692117d5..f4379fdc 100644 --- a/src/coders/json.hpp +++ b/src/coders/json.hpp @@ -1,25 +1,20 @@ #ifndef CODERS_JSON_HPP_ #define CODERS_JSON_HPP_ -#include "commons.hpp" #include "binary_json.hpp" #include "../data/dynamic.hpp" #include "../typedefs.hpp" -#include #include -#include -#include -#include namespace json { - std::unique_ptr parse(const std::string& filename, const std::string& source); - std::unique_ptr parse(const std::string& source); + dynamic::Map_sptr parse(const std::string& filename, const std::string& source); + dynamic::Map_sptr parse(const std::string& source); std::string stringify( - const dynamic::Map* obj, - bool nice, + const dynamic::Map* obj, + bool nice, const std::string& indent ); diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index dfa0e467..e20a0875 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -7,15 +7,14 @@ #include "../files/settings_io.hpp" #include -#include #include #include #include using namespace toml; -class Reader : BasicParser { - SettingsHandler& handler; +class TomlReader : BasicParser { + dynamic::Map_sptr root; void skipWhitespace() override { BasicParser::skipWhitespace(); @@ -26,7 +25,34 @@ class Reader : BasicParser { } } } - void readSection(const std::string& section) { + + dynamic::Map& getSection(const std::string& section) { + if (section.empty()) { + return *root; + } + size_t offset = 0; + auto& rootMap = *root; + do { + size_t index = section.find('.', offset); + if (index == std::string::npos) { + auto map = rootMap.map(section); + if (map == nullptr) { + return rootMap.putMap(section); + } + return *map; + } + auto subsection = section.substr(offset, index); + auto map = rootMap.map(subsection); + if (map == nullptr) { + rootMap = rootMap.putMap(subsection); + } else { + rootMap = *map; + } + offset = index+1; + } while (true); + } + + void readSection(const std::string& section, dynamic::Map& map) { while (hasNext()) { skipWhitespace(); if (!hasNext()) { @@ -36,43 +62,31 @@ class Reader : BasicParser { if (c == '[') { std::string name = parseName(); pos++; - readSection(name); + readSection(name, getSection(name)); return; } pos--; - std::string name = section+"."+parseName(); + std::string name = parseName(); expect('='); c = peek(); if (is_digit(c)) { - auto num = parseNumber(1); - if (handler.has(name)) { - handler.setValue(name, num); - } + map.put(name, parseNumber(1)); } else if (c == '-' || c == '+') { int sign = c == '-' ? -1 : 1; pos++; - auto num = parseNumber(sign); - if (handler.has(name)) { - handler.setValue(name, num); - } + map.put(name, parseNumber(sign)); } else if (is_identifier_start(c)) { std::string identifier = parseName(); - if (handler.has(name)) { - if (identifier == "true" || identifier == "false") { - bool flag = identifier == "true"; - handler.setValue(name, flag); - } else if (identifier == "inf") { - handler.setValue(name, INFINITY); - } else if (identifier == "nan") { - handler.setValue(name, NAN); - } + if (identifier == "true" || identifier == "false") { + map.put(name, identifier == "true"); + } else if (identifier == "inf") { + map.put(name, INFINITY); + } else if (identifier == "nan") { + map.put(name, NAN); } } else if (c == '"' || c == '\'') { pos++; - std::string str = parseString(c); - if (handler.has(name)) { - handler.setValue(name, str); - } + map.put(name, parseString(c)); } else { throw error("feature is not supported"); } @@ -81,29 +95,67 @@ class Reader : BasicParser { } public: - Reader( - SettingsHandler& handler, + TomlReader( std::string_view file, std::string_view source) - : BasicParser(file, source), handler(handler) { + : BasicParser(file, source) { + root = dynamic::create_map(); } - void read() { + dynamic::Map_sptr read() { skipWhitespace(); if (!hasNext()) { - return; + return root; } - readSection(""); + readSection("", *root); + return root; } }; +dynamic::Map_sptr toml::parse(std::string_view file, std::string_view source) { + return TomlReader(file, source).read(); +} + void toml::parse( - SettingsHandler& handler, - const std::string& file, - const std::string& source + SettingsHandler& handler, std::string_view file, std::string_view source ) { - Reader reader(handler, file, source); - reader.read(); + auto map = parse(file, source); + for (auto& entry : map->values) { + const auto& sectionName = entry.first; + auto sectionMap = std::get_if(&entry.second); + if (sectionMap == nullptr) { + continue; + } + for (auto& sectionEntry : (*sectionMap)->values) { + const auto& name = sectionEntry.first; + auto& value = sectionEntry.second; + auto fullname = sectionName+"."+name; + if (handler.has(fullname)) { + handler.setValue(fullname, value); + } + } + } +} + +std::string toml::stringify(dynamic::Map& root, const std::string& name) { + std::stringstream ss; + if (!name.empty()) { + ss << "[" << name << "]\n"; + } + for (auto& entry : root.values) { + if (!std::holds_alternative(entry.second)) { + ss << entry.first << " = "; + ss << entry.second << "\n"; + } + } + for (auto& entry : root.values) { + if (auto submap = std::get_if(&entry.second)) { + ss << "\n" << toml::stringify( + **submap, name.empty() ? entry.first : name+"."+entry.first + ); + } + } + return ss.str(); } std::string toml::stringify(SettingsHandler& handler) { diff --git a/src/coders/toml.hpp b/src/coders/toml.hpp index 4574e468..e695a298 100644 --- a/src/coders/toml.hpp +++ b/src/coders/toml.hpp @@ -1,19 +1,20 @@ #ifndef CODERS_TOML_HPP_ #define CODERS_TOML_HPP_ -#include "commons.hpp" - +#include "../data/dynamic.hpp" #include class SettingsHandler; namespace toml { std::string stringify(SettingsHandler& handler); + std::string stringify(dynamic::Map& root, const std::string& name=""); + dynamic::Map_sptr parse(std::string_view file, std::string_view source); void parse( SettingsHandler& handler, - const std::string& file, - const std::string& source + std::string_view file, + std::string_view source ); } diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 4ba19fe3..4de7367c 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -10,127 +10,6 @@ #include "ContentPack.hpp" #include "../logic/scripting/scripting.hpp" -ContentBuilder::~ContentBuilder() {} - -void ContentBuilder::add(Block* def) { - checkIdentifier(def->name); - blockDefs[def->name] = def; - blockIds.push_back(def->name); -} - -void ContentBuilder::add(ItemDef* def) { - checkIdentifier(def->name); - itemDefs[def->name] = def; - itemIds.push_back(def->name); -} - -void ContentBuilder::add(ContentPackRuntime* pack) { - packs.emplace(pack->getId(), pack); -} - -void ContentBuilder::add(BlockMaterial material) { - blockMaterials.emplace(material.name, material); -} - -Block& ContentBuilder::createBlock(std::string id) { - auto found = blockDefs.find(id); - if (found != blockDefs.end()) { - return *found->second; - // throw namereuse_error("name "+id+" is already used", contenttype::item); - } - Block* block = new Block(id); - add(block); - return *block; -} - -ItemDef& ContentBuilder::createItem(std::string id) { - auto found = itemDefs.find(id); - if (found != itemDefs.end()) { - // if (found->second->generated) { - return *found->second; - // } - // throw namereuse_error("name "+id+" is already used", contenttype::item); - } - ItemDef* item = new ItemDef(id); - add(item); - return *item; -} - -void ContentBuilder::checkIdentifier(std::string id) { - contenttype result; - if (((result = checkContentType(id)) != contenttype::none)) { - throw namereuse_error("name "+id+" is already used", result); - } -} - -contenttype ContentBuilder::checkContentType(std::string id) { - if (blockDefs.find(id) != blockDefs.end()) { - return contenttype::block; - } - if (itemDefs.find(id) != itemDefs.end()) { - return contenttype::item; - } - return contenttype::none; -} - -Content* ContentBuilder::build() { - std::vector blockDefsIndices; - auto groups = std::make_unique(); - for (const std::string& name : blockIds) { - Block* def = blockDefs[name]; - - // Generating runtime info - def->rt.id = blockDefsIndices.size(); - def->rt.emissive = *reinterpret_cast(def->emission); - def->rt.solid = def->model == BlockModel::block; - - if (def->rotatable) { - for (uint i = 0; i < BlockRotProfile::MAX_COUNT; i++) { - def->rt.hitboxes[i].reserve(def->hitboxes.size()); - for (AABB aabb : def->hitboxes) { - def->rotations.variants[i].transform(aabb); - def->rt.hitboxes[i].push_back(aabb); - } - } - } - - blockDefsIndices.push_back(def); - groups->insert(def->drawGroup); - } - - std::vector itemDefsIndices; - for (const std::string& name : itemIds) { - ItemDef* def = itemDefs[name]; - - // Generating runtime info - def->rt.id = itemDefsIndices.size(); - def->rt.emissive = *reinterpret_cast(def->emission); - itemDefsIndices.push_back(def); - } - - auto indices = new ContentIndices(blockDefsIndices, itemDefsIndices); - - auto content = std::make_unique( - indices, - std::move(groups), - blockDefs, - itemDefs, - std::move(packs), - std::move(blockMaterials) - ); - - // Now, it's time to resolve foreign keys - for (Block* def : blockDefsIndices) { - def->rt.pickingItem = content->requireItem(def->pickingItem).rt.id; - } - - for (ItemDef* def : itemDefsIndices) { - def->rt.placingBlock = content->requireBlock(def->placingBlock).rt.id; - } - - return content.release(); -} - ContentIndices::ContentIndices( std::vector blockDefs, std::vector itemDefs @@ -139,27 +18,21 @@ ContentIndices::ContentIndices( {} Content::Content( - ContentIndices* indices, + std::unique_ptr indices, std::unique_ptr drawGroups, - std::unordered_map blockDefs, - std::unordered_map itemDefs, + std::unordered_map> blockDefs, + std::unordered_map> itemDefs, std::unordered_map> packs, - std::unordered_map blockMaterials -) : blockDefs(blockDefs), - itemDefs(itemDefs), - indices(indices), + std::unordered_map> blockMaterials +) : blockDefs(std::move(blockDefs)), + itemDefs(std::move(itemDefs)), + indices(std::move(indices)), packs(std::move(packs)), blockMaterials(std::move(blockMaterials)), drawGroups(std::move(drawGroups)) {} Content::~Content() { - for (auto& entry : blockDefs) { - delete entry.second; - } - for (auto& entry : itemDefs) { - delete entry.second; - } } Block* Content::findBlock(std::string id) const { @@ -167,7 +40,7 @@ Block* Content::findBlock(std::string id) const { if (found == blockDefs.end()) { return nullptr; } - return found->second; + return found->second.get(); } Block& Content::requireBlock(std::string id) const { @@ -183,7 +56,7 @@ ItemDef* Content::findItem(std::string id) const { if (found == itemDefs.end()) { return nullptr; } - return found->second; + return found->second.get(); } ItemDef& Content::requireItem(std::string id) const { @@ -199,7 +72,7 @@ const BlockMaterial* Content::findBlockMaterial(std::string id) const { if (found == blockMaterials.end()) { return nullptr; } - return &found->second; + return found->second.get(); } const ContentPackRuntime* Content::getPackRuntime(std::string id) const { @@ -210,7 +83,7 @@ const ContentPackRuntime* Content::getPackRuntime(std::string id) const { return found->second.get(); } -const std::unordered_map& Content::getBlockMaterials() const { +const std::unordered_map>& Content::getBlockMaterials() const { return blockMaterials; } diff --git a/src/content/Content.hpp b/src/content/Content.hpp index dcf3c56f..c9df8417 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -1,17 +1,19 @@ #ifndef CONTENT_CONTENT_HPP_ #define CONTENT_CONTENT_HPP_ +#include "../typedefs.hpp" + #include #include #include #include #include #include -#include "../typedefs.hpp" -#include "../voxels/Block.hpp" using DrawGroups = std::set; +class Block; +struct BlockMaterial; class ItemDef; class Content; class ContentPackRuntime; @@ -41,33 +43,6 @@ public: } }; -class ContentBuilder { - std::unordered_map blockDefs; - std::vector blockIds; - - std::unordered_map itemDefs; - std::vector itemIds; - - std::unordered_map blockMaterials; - - std::unordered_map> packs; -public: - ~ContentBuilder(); - - void add(Block* def); - void add(ItemDef* def); - void add(ContentPackRuntime* pack); - void add(BlockMaterial material); - - Block& createBlock(std::string id); - ItemDef& createItem(std::string id); - - void checkIdentifier(std::string id); - contenttype checkContentType(std::string id); - - Content* build(); -}; - /// @brief Runtime defs cache: indices class ContentIndices { std::vector blockDefs; @@ -110,21 +85,21 @@ public: /* Content is a definitions repository */ class Content { - std::unordered_map blockDefs; - std::unordered_map itemDefs; + std::unordered_map> blockDefs; + std::unordered_map> itemDefs; std::unique_ptr indices; std::unordered_map> packs; - std::unordered_map blockMaterials; + std::unordered_map> blockMaterials; public: std::unique_ptr const drawGroups; Content( - ContentIndices* indices, + std::unique_ptr indices, std::unique_ptr drawGroups, - std::unordered_map blockDefs, - std::unordered_map itemDefs, + std::unordered_map> blockDefs, + std::unordered_map> itemDefs, std::unordered_map> packs, - std::unordered_map blockMaterials + std::unordered_map> blockMaterials ); ~Content(); @@ -142,7 +117,7 @@ public: const ContentPackRuntime* getPackRuntime(std::string id) const; - const std::unordered_map& getBlockMaterials() const; + const std::unordered_map>& getBlockMaterials() const; const std::unordered_map>& getPacks() const; }; diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp new file mode 100644 index 00000000..c95f5e5a --- /dev/null +++ b/src/content/ContentBuilder.cpp @@ -0,0 +1,109 @@ +#include "ContentBuilder.hpp" + +ContentBuilder::~ContentBuilder() {} + +void ContentBuilder::add(std::unique_ptr pack) { + packs[pack->getId()] = std::move(pack); +} + +Block& ContentBuilder::createBlock(std::string id) { + auto found = blockDefs.find(id); + if (found != blockDefs.end()) { + return *found->second; + } + checkIdentifier(id); + blockIds.push_back(id); + blockDefs[id] = std::make_unique(id); + return *blockDefs[id]; +} + +ItemDef& ContentBuilder::createItem(std::string id) { + auto found = itemDefs.find(id); + if (found != itemDefs.end()) { + return *found->second; + } + checkIdentifier(id); + itemIds.push_back(id); + itemDefs[id] = std::make_unique(id); + return *itemDefs[id]; +} + +BlockMaterial& ContentBuilder::createBlockMaterial(std::string id) { + blockMaterials[id] = std::make_unique(); + auto& material = *blockMaterials[id]; + material.name = id; + return material; +} + +void ContentBuilder::checkIdentifier(std::string id) { + contenttype result; + if (((result = checkContentType(id)) != contenttype::none)) { + throw namereuse_error("name "+id+" is already used", result); + } +} + +contenttype ContentBuilder::checkContentType(std::string id) { + if (blockDefs.find(id) != blockDefs.end()) { + return contenttype::block; + } + if (itemDefs.find(id) != itemDefs.end()) { + return contenttype::item; + } + return contenttype::none; +} + +std::unique_ptr ContentBuilder::build() { + std::vector blockDefsIndices; + auto groups = std::make_unique(); + for (const std::string& name : blockIds) { + Block& def = *blockDefs[name]; + + // Generating runtime info + def.rt.id = blockDefsIndices.size(); + def.rt.emissive = *reinterpret_cast(def.emission); + def.rt.solid = def.model == BlockModel::block; + + if (def.rotatable) { + for (uint i = 0; i < BlockRotProfile::MAX_COUNT; i++) { + def.rt.hitboxes[i].reserve(def.hitboxes.size()); + for (AABB aabb : def.hitboxes) { + def.rotations.variants[i].transform(aabb); + def.rt.hitboxes[i].push_back(aabb); + } + } + } + + blockDefsIndices.push_back(&def); + groups->insert(def.drawGroup); + } + + std::vector itemDefsIndices; + for (const std::string& name : itemIds) { + ItemDef& def = *itemDefs[name]; + + // Generating runtime info + def.rt.id = itemDefsIndices.size(); + def.rt.emissive = *reinterpret_cast(def.emission); + itemDefsIndices.push_back(&def); + } + + auto content = std::make_unique( + std::make_unique(blockDefsIndices, itemDefsIndices), + std::move(groups), + std::move(blockDefs), + std::move(itemDefs), + std::move(packs), + std::move(blockMaterials) + ); + + // Now, it's time to resolve foreign keys + for (Block* def : blockDefsIndices) { + def->rt.pickingItem = content->requireItem(def->pickingItem).rt.id; + } + + for (ItemDef* def : itemDefsIndices) { + def->rt.placingBlock = content->requireBlock(def->placingBlock).rt.id; + } + + return content; +} diff --git a/src/content/ContentBuilder.hpp b/src/content/ContentBuilder.hpp new file mode 100644 index 00000000..f6bcbd5e --- /dev/null +++ b/src/content/ContentBuilder.hpp @@ -0,0 +1,37 @@ +#ifndef CONTENT_CONTENT_BUILDER_HPP_ +#define CONTENT_CONTENT_BUILDER_HPP_ + +#include "../items/ItemDef.hpp" +#include "../voxels/Block.hpp" +#include "../content/Content.hpp" +#include "../content/ContentPack.hpp" + +#include +#include +#include + +class ContentBuilder { + std::unordered_map> blockDefs; + std::vector blockIds; + + std::unordered_map> itemDefs; + std::vector itemIds; + + std::unordered_map> blockMaterials; + std::unordered_map> packs; +public: + ~ContentBuilder(); + + void add(std::unique_ptr pack); + + Block& createBlock(std::string id); + ItemDef& createItem(std::string id); + BlockMaterial& createBlockMaterial(std::string id); + + void checkIdentifier(std::string id); + contenttype checkContentType(std::string id); + + std::unique_ptr build(); +}; + +#endif // CONTENT_CONTENT_BUILDER_HPP_ diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 9878d9ef..5b7e30b3 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -2,6 +2,7 @@ #include "Content.hpp" #include "ContentPack.hpp" +#include "ContentBuilder.hpp" #include "../coders/json.hpp" #include "../core_defs.hpp" #include "../data/dynamic.hpp" @@ -26,9 +27,11 @@ static debug::Logger logger("content-loader"); ContentLoader::ContentLoader(ContentPack* pack) : pack(pack) { } -bool ContentLoader::fixPackIndices(fs::path folder, - dynamic::Map* indicesRoot, - std::string contentSection) { +bool ContentLoader::fixPackIndices( + fs::path folder, + dynamic::Map* indicesRoot, + std::string contentSection +) { std::vector detected; std::vector indexed; if (fs::is_directory(folder)) { @@ -209,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; @@ -312,22 +319,22 @@ void ContentLoader::loadItem(ItemDef& def, std::string full, std::string name) { } } -BlockMaterial ContentLoader::loadBlockMaterial(fs::path file, std::string full) { +void ContentLoader::loadBlockMaterial(BlockMaterial& def, fs::path file) { auto root = files::read_json(file); - BlockMaterial material {full}; - root->str("steps-sound", material.stepsSound); - root->str("place-sound", material.placeSound); - root->str("break-sound", material.breakSound); - return material; + root->str("steps-sound", def.stepsSound); + root->str("place-sound", def.placeSound); + root->str("break-sound", def.breakSound); } void ContentLoader::load(ContentBuilder& builder) { logger.info() << "loading pack [" << pack->id << "]"; - auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack)); - builder.add(runtime); + auto runtime = std::make_unique( + *pack, scripting::create_pack_environment(*pack) + ); env = runtime->getEnvironment(); ContentPackStats& stats = runtime->getStatsWriteable(); + builder.add(std::move(runtime)); fixPackIndices(); @@ -350,7 +357,9 @@ void ContentLoader::load(ContentBuilder& builder) { std::string full = colon == std::string::npos ? pack->id + ":" + name : name; if (colon != std::string::npos) name[colon] = '/'; auto& def = builder.createBlock(full); - if (colon != std::string::npos) def.scriptName = name.substr(0, colon) + '/' + def.scriptName; + if (colon != std::string::npos) { + def.scriptName = name.substr(0, colon) + '/' + def.scriptName; + } loadBlock(def, full, name); stats.totalBlocks++; if (!def.hidden) { @@ -388,7 +397,7 @@ void ContentLoader::load(ContentBuilder& builder) { for (auto entry : fs::directory_iterator(materialsDir)) { fs::path file = entry.path(); std::string name = pack->id+":"+file.stem().u8string(); - builder.add(loadBlockMaterial(file, name)); + loadBlockMaterial(builder.createBlockMaterial(name), file); } } } diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index d709f0d0..515d742a 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -1,13 +1,15 @@ #ifndef CONTENT_CONTENT_LOADER_HPP_ #define CONTENT_CONTENT_LOADER_HPP_ -#include "../voxels/Block.hpp" +#include "../typedefs.hpp" #include #include namespace fs = std::filesystem; +class Block; +struct BlockMaterial; class ItemDef; struct ContentPack; class ContentBuilder; @@ -23,7 +25,7 @@ class ContentLoader { void loadBlock(Block& def, std::string full, std::string name); void loadCustomBlockModel(Block& def, dynamic::Map* primitives); void loadItem(ItemDef& def, std::string full, std::string name); - BlockMaterial loadBlockMaterial(fs::path file, std::string full); + void loadBlockMaterial(BlockMaterial& def, fs::path file); public: ContentLoader(ContentPack* pack); diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index d8790b8b..7ff4b972 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -12,17 +12,11 @@ class EnginePaths; namespace fs = std::filesystem; -namespace scripting { - class Environment; -} - class contentpack_error : public std::runtime_error { std::string packId; fs::path folder; public: - contentpack_error(std::string packId, - fs::path folder, - std::string message); + contentpack_error(std::string packId, fs::path folder, std::string message); std::string getPackId() const; fs::path getFolder() const; @@ -31,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 12e840b4..57c85bbc 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -2,13 +2,16 @@ #include "items/ItemDef.hpp" #include "content/Content.hpp" +#include "content/ContentBuilder.hpp" +#include "files/files.hpp" +#include "files/engine_paths.hpp" #include "window/Window.hpp" #include "window/Events.hpp" #include "window/input.hpp" #include "voxels/Block.hpp" // All in-game definitions (blocks, items, etc..) -void corecontent::setup(ContentBuilder* builder) { +void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { Block& block = builder->createBlock("core:air"); block.replaceable = true; block.drawGroup = 1; @@ -21,24 +24,11 @@ void corecontent::setup(ContentBuilder* builder) { ItemDef& item = builder->createItem("core:empty"); item.iconType = item_icon_type::none; -} -void corecontent::setup_bindings() { - Events::bind(BIND_DEVTOOLS_CONSOLE, inputtype::keyboard, keycode::GRAVE_ACCENT); - Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W); - Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S); - Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D); - Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A); - Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE); - Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL); - Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT); - Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R); - Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C); - Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4); - Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N); - Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F); - Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1); - Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2); - Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3); - Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB); + auto bindsFile = paths->getResources()/fs::path("bindings.toml"); + if (fs::is_regular_file(bindsFile)) { + Events::loadBindings( + bindsFile.u8string(), files::read_string(bindsFile) + ); + } } diff --git a/src/core_defs.hpp b/src/core_defs.hpp index 1a2b0a9f..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"; @@ -27,11 +28,11 @@ inline const std::string BIND_PLAYER_BUILD = "player.build"; inline const std::string BIND_PLAYER_PICK = "player.pick"; inline const std::string BIND_HUD_INVENTORY = "hud.inventory"; +class EnginePaths; class ContentBuilder; namespace corecontent { - void setup_bindings(); - void setup(ContentBuilder* builder); + void setup(EnginePaths* paths, ContentBuilder* builder); } #endif // CORE_DEFS_HPP_ 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/data/dynamic.hpp b/src/data/dynamic.hpp index a9dc81c1..4c5b602c 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,9 @@ namespace dynamic { Map& put(std::string key, bool value) { return put(key, Value(static_cast(value))); } + Map& put(std::string key, const char* value) { + return put(key, Value(value)); + } Map& put(std::string key, const Value& value); void remove(const std::string& key); 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 a903453e..4720f909 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -9,6 +9,7 @@ #include "coders/imageio.hpp" #include "coders/json.hpp" #include "coders/toml.hpp" +#include "content/ContentBuilder.hpp" #include "content/ContentLoader.hpp" #include "core_defs.hpp" #include "files/files.hpp" @@ -34,6 +35,7 @@ #include "window/input.hpp" #include "window/Window.hpp" #include "world/WorldGenerators.hpp" +#include "settings.hpp" #include #include @@ -56,20 +58,20 @@ 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) : settings(settings), settingsHandler(settingsHandler), paths(paths), interpreter(std::make_unique()) { - corecontent::setup_bindings(); loadSettings(); controller = std::make_unique(this); 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); @@ -93,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() { @@ -102,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); + } } } @@ -251,10 +267,20 @@ 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; - corecontent::setup(&contentBuilder); + corecontent::setup(paths, &contentBuilder); paths->setContentPacks(&contentPacks); std::vector names; @@ -272,8 +298,12 @@ void Engine::loadContent() { ContentLoader loader(&pack); loader.load(contentBuilder); - } - content.reset(contentBuilder.build()); + + load_configs(pack.folder); + } + load_configs(paths->getResources()); + + content = contentBuilder.build(); resPaths = std::make_unique(resdir, resRoots); langs::setup(resdir, langs::current->getId(), contentPacks); diff --git a/src/engine.hpp b/src/engine.hpp index 61066143..e9ebeb8a 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -2,7 +2,6 @@ #define ENGINE_HPP_ #include "delegates.hpp" -#include "settings.hpp" #include "typedefs.hpp" #include "assets/Assets.hpp" @@ -27,6 +26,7 @@ class ResPaths; class Batch2D; class EngineController; class SettingsHandler; +struct EngineSettings; namespace fs = std::filesystem; @@ -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/WorldFiles.cpp b/src/files/WorldFiles.cpp index ff9b2663..b5d68c85 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -13,6 +13,7 @@ #include "../objects/Player.hpp" #include "../physics/Hitbox.hpp" #include "../typedefs.hpp" +#include "../settings.hpp" #include "../util/data_io.hpp" #include "../voxels/Block.hpp" #include "../voxels/Chunk.hpp" diff --git a/src/files/WorldFiles.hpp b/src/files/WorldFiles.hpp index 7247b246..f2938416 100644 --- a/src/files/WorldFiles.hpp +++ b/src/files/WorldFiles.hpp @@ -5,7 +5,6 @@ #include "files.hpp" #include "../typedefs.hpp" -#include "../settings.hpp" #include "../content/ContentPack.hpp" #include "../voxels/Chunk.hpp" @@ -24,6 +23,7 @@ class Player; class Content; class ContentIndices; class World; +struct DebugSettings; namespace fs = std::filesystem; diff --git a/src/files/WorldRegions.cpp b/src/files/WorldRegions.cpp index 55f3b3d2..af3aadd4 100644 --- a/src/files/WorldRegions.cpp +++ b/src/files/WorldRegions.cpp @@ -374,7 +374,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.cpp b/src/files/files.cpp index d1ec0ffb..bd9e1716 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -1,6 +1,8 @@ #include "files.hpp" +#include "../coders/commons.hpp" #include "../coders/json.hpp" +#include "../coders/toml.hpp" #include "../coders/gzip.hpp" #include "../util/stringutil.hpp" #include "../data/dynamic.hpp" @@ -107,8 +109,7 @@ bool files::write_binary_json(fs::path filename, const dynamic::Map* obj, bool c std::shared_ptr files::read_json(fs::path filename) { std::string text = files::read_string(filename); try { - auto obj = json::parse(filename.string(), text); - return obj; + return json::parse(filename.string(), text);; } catch (const parsing_error& error) { std::cerr << error.errorLog() << std::endl; throw std::runtime_error("could not to parse "+filename.string()); @@ -121,6 +122,10 @@ std::shared_ptr files::read_binary_json(fs::path file) { return json::from_binary(bytes.get(), size); } +std::shared_ptr files::read_toml(fs::path file) { + return toml::parse(file.u8string(), files::read_string(file)); +} + std::vector files::read_list(fs::path filename) { std::ifstream file(filename); if (!file) { diff --git a/src/files/files.hpp b/src/files/files.hpp index 1270d6eb..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); @@ -44,10 +44,7 @@ namespace files { /// @brief Write dynamic data to the JSON file /// @param nice if true, human readable format will be used, otherwise minimal - bool write_json( - fs::path filename, - const dynamic::Map* obj, - bool nice=true); + bool write_json(fs::path filename, const dynamic::Map* obj, bool nice=true); /// @brief Write dynamic data to the binary JSON file /// (see src/coders/binary_json_spec.md) @@ -66,6 +63,7 @@ namespace files { /// @param file *.json or *.bjson file std::shared_ptr read_json(fs::path file); std::shared_ptr read_binary_json(fs::path file); + std::shared_ptr read_toml(fs::path file); std::vector read_list(fs::path file); } diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index d44df1d1..7a62ca50 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -19,7 +19,7 @@ LevelFrontend::LevelFrontend(LevelController* controller, Assets* assets) contentCache(std::make_unique(level->content, assets)) { assets->store( - BlocksPreview::build(contentCache.get(), assets, level->content).release(), + BlocksPreview::build(contentCache.get(), assets, level->content), "block-previews" ); controller->getPlayerController()->listenBlockInteraction( diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index d591d70a..529d5acf 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -1,6 +1,7 @@ #include "../audio/audio.hpp" #include "../delegates.hpp" #include "../engine.hpp" +#include "../settings.hpp" #include "../graphics/core/Mesh.hpp" #include "../graphics/ui/elements/CheckBox.hpp" #include "../graphics/ui/elements/TextBox.hpp" @@ -19,6 +20,7 @@ #include #include #include +#include using namespace gui; @@ -71,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()); @@ -137,8 +148,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/menu.cpp b/src/frontend/menu.cpp index 4c89aeaa..2abc23ac 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -62,9 +62,11 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) { auto file = engine->getResPaths()->find("layouts/pages/"+name+".xml"); auto fullname = "core:pages/"+name; - auto document = UiDocument::read(scripting::get_root_environment(), fullname, file).release(); - engine->getAssets()->store(document, fullname); - + auto document_ptr = UiDocument::read( + scripting::get_root_environment(), fullname, file + ); + auto document = document_ptr.get(); + engine->getAssets()->store(std::move(document_ptr), fullname); scripting::on_ui_open(document, std::move(args)); return document->getRoot(); }; @@ -75,8 +77,11 @@ UiDocument* menus::show(Engine* engine, const std::string& name, std::vectorgetResPaths()->find("layouts/"+name+".xml"); auto fullname = "core:layouts/"+name; - auto document = UiDocument::read(scripting::get_root_environment(), fullname, file).release(); - engine->getAssets()->store(document, fullname); + auto document_ptr = UiDocument::read( + scripting::get_root_environment(), fullname, file + ); + auto document = document_ptr.get(); + engine->getAssets()->store(std::move(document_ptr), fullname); scripting::on_ui_open(document, std::move(args)); menu->addPage(name, document->getRoot()); menu->setPage(name); diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 7f805e27..d740ce19 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -1,27 +1,30 @@ #include "LevelScreen.hpp" +#include "../../core_defs.hpp" #include "../hud.hpp" #include "../LevelFrontend.hpp" -#include "../../debug/Logger.hpp" #include "../../audio/audio.hpp" #include "../../coders/imageio.hpp" -#include "../../graphics/core/PostProcessing.hpp" +#include "../../debug/Logger.hpp" +#include "../../engine.hpp" +#include "../../files/files.hpp" #include "../../graphics/core/DrawContext.hpp" -#include "../../graphics/core/Viewport.hpp" #include "../../graphics/core/ImageData.hpp" -#include "../../graphics/ui/GUI.hpp" -#include "../../graphics/ui/elements/Menu.hpp" +#include "../../graphics/core/PostProcessing.hpp" +#include "../../graphics/core/Viewport.hpp" #include "../../graphics/render/WorldRenderer.hpp" +#include "../../graphics/ui/elements/Menu.hpp" +#include "../../graphics/ui/GUI.hpp" #include "../../logic/LevelController.hpp" #include "../../logic/scripting/scripting_hud.hpp" +#include "../../util/stringutil.hpp" #include "../../physics/Hitbox.hpp" #include "../../voxels/Chunks.hpp" -#include "../../world/Level.hpp" -#include "../../world/World.hpp" #include "../../window/Camera.hpp" #include "../../window/Events.hpp" #include "../../window/Window.hpp" -#include "../../engine.hpp" +#include "../../world/Level.hpp" +#include "../../world/World.hpp" static debug::Logger logger("level-screen"); @@ -45,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()); @@ -55,16 +61,19 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr level) void LevelScreen::initializeContent() { auto content = controller->getLevel()->content; for (auto& entry : content->getPacks()) { - auto pack = entry.second.get(); - const ContentPack& info = pack->getInfo(); - fs::path scriptFile = info.folder/fs::path("scripts/hud.lua"); - if (fs::is_regular_file(scriptFile)) { - scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile); - } + initializePack(entry.second.get()); } scripting::on_frontend_init(hud.get()); } +void LevelScreen::initializePack(ContentPackRuntime* pack) { + const ContentPack& info = pack->getInfo(); + fs::path scriptFile = info.folder/fs::path("scripts/hud.lua"); + if (fs::is_regular_file(scriptFile)) { + scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile); + } +} + LevelScreen::~LevelScreen() { saveWorldPreview(); scripting::on_frontend_close(); @@ -83,8 +92,11 @@ void LevelScreen::saveWorldPreview() { // camera special copy for world preview Camera camera = *player->camera; camera.setFov(glm::radians(70.0f)); + + DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get()); + Viewport viewport(previewSize * 1.5, previewSize); - DrawContext ctx(nullptr, viewport, batch.get()); + DrawContext ctx(&pctx, viewport, batch.get()); worldRenderer->draw(ctx, &camera, false, postProcessing.get()); auto image = postProcessing->toImage(); @@ -106,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) { @@ -138,7 +147,7 @@ void LevelScreen::update(float delta) { controller->getLevel()->getWorld()->updateTimers(delta); animator->update(delta); } - controller->update(delta, !inputLocked, hud->isPause()); + controller->update(glm::min(delta, 0.2f), !inputLocked, hud->isPause()); hud->update(hudVisible); } diff --git a/src/frontend/screens/LevelScreen.hpp b/src/frontend/screens/LevelScreen.hpp index 8548c06c..2da4cbec 100644 --- a/src/frontend/screens/LevelScreen.hpp +++ b/src/frontend/screens/LevelScreen.hpp @@ -12,21 +12,23 @@ class LevelController; class WorldRenderer; class TextureAnimator; class PostProcessing; +class ContentPackRuntime; class Level; class LevelScreen : public Screen { std::unique_ptr frontend; - std::unique_ptr hud; std::unique_ptr controller; std::unique_ptr worldRenderer; std::unique_ptr animator; std::unique_ptr postProcessing; + std::unique_ptr hud; void saveWorldPreview(); bool hudVisible = true; void updateHotkeys(); void initializeContent(); + void initializePack(ContentPackRuntime* pack); public: LevelScreen(Engine* engine, std::unique_ptr level); ~LevelScreen(); diff --git a/src/frontend/screens/MenuScreen.cpp b/src/frontend/screens/MenuScreen.cpp index 08d4f8a4..96b7656c 100644 --- a/src/frontend/screens/MenuScreen.cpp +++ b/src/frontend/screens/MenuScreen.cpp @@ -5,6 +5,7 @@ #include "../../graphics/core/Batch2D.hpp" #include "../../graphics/core/Shader.hpp" #include "../../graphics/core/Texture.hpp" +#include "../../maths/UVRegion.hpp" #include "../../window/Window.hpp" #include "../../window/Camera.hpp" #include "../../engine.hpp" 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/Shader.cpp b/src/graphics/core/Shader.cpp index b6bb6802..62d82ee3 100644 --- a/src/graphics/core/Shader.cpp +++ b/src/graphics/core/Shader.cpp @@ -98,7 +98,7 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi return glshader(new GLuint(shader), shader_deleter); } -Shader* Shader::create( +std::unique_ptr Shader::create( const std::string& vertexFile, const std::string& fragmentFile, const std::string& vertexCode, @@ -125,5 +125,5 @@ Shader* Shader::create( "shader program linking failed:\n"+std::string(infoLog) ); } - return new Shader(id); + return std::make_unique(id); } diff --git a/src/graphics/core/Shader.hpp b/src/graphics/core/Shader.hpp index 64b4cc83..25d182a5 100644 --- a/src/graphics/core/Shader.hpp +++ b/src/graphics/core/Shader.hpp @@ -4,6 +4,7 @@ #include "../../typedefs.hpp" #include +#include #include #include @@ -36,7 +37,7 @@ public: /// @param vertexSource vertex shader source code /// @param fragmentSource fragment shader source code /// @return linked shader program containing vertex and fragment shaders - static Shader* create( + static std::unique_ptr create( const std::string& vertexFile, const std::string& fragmentFile, const std::string& vertexSource, 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 cdff6fc5..c7ef8a1f 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -1,9 +1,14 @@ #include "GUI.hpp" + +#include "gui_util.hpp" + #include "elements/UINode.hpp" +#include "elements/Label.hpp" #include "elements/Menu.hpp" #include "../../assets/Assets.hpp" #include "../../frontend/UiDocument.hpp" +#include "../../frontend/locale.hpp" #include "../../graphics/core/Batch2D.hpp" #include "../../graphics/core/Shader.hpp" #include "../../graphics/core/DrawContext.hpp" @@ -27,6 +32,15 @@ GUI::GUI() { menu->setId("menu"); container->add(menu); container->setScrollable(false); + + tooltip = guiutil::create( + "" + "" + "" + ); + store("tooltip", tooltip); + store("tooltip.label", UINode::find(tooltip, "tooltip.label")); + container->add(tooltip); } GUI::~GUI() { @@ -37,7 +51,7 @@ std::shared_ptr GUI::getMenu() { } void GUI::onAssetsLoad(Assets* assets) { - assets->store(new UiDocument( + assets->store(std::make_unique( "core:root", uidocscript {}, std::dynamic_pointer_cast(container), @@ -45,10 +59,41 @@ void GUI::onAssetsLoad(Assets* assets) { ), "core:root"); } +void GUI::resetTooltip() { + tooltipTimer = 0.0f; + tooltip->setVisible(false); +} + +void GUI::updateTooltip(float delta) { + if (hover == nullptr || !hover->isInside(Events::cursor)) { + return resetTooltip(); + } + 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; +} + /// @brief Mouse related input and logic handling void GUI::actMouse(float delta) { + float mouseDelta = glm::length(Events::delta); doubleClicked = false; - doubleClickTimer += delta + glm::length(Events::delta) * 0.1f; + doubleClickTimer += delta + mouseDelta * 0.1f; auto hover = container->getAt(Events::cursor, nullptr); if (this->hover && this->hover != hover) { @@ -134,8 +179,14 @@ void GUI::act(float delta, const Viewport& vp) { container->act(delta); auto prevfocus = focus; + updateTooltip(delta); if (!Events::_cursor_locked) { actMouse(delta); + } else { + if (hover) { + hover->setHover(false); + hover = nullptr; + } } if (focus) { diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index e88a3ea1..c28e5d58 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -54,21 +54,25 @@ namespace gui { /// @brief The main UI controller class GUI { std::shared_ptr container; - std::shared_ptr hover = nullptr; - std::shared_ptr pressed = nullptr; - std::shared_ptr focus = nullptr; + std::shared_ptr hover; + std::shared_ptr pressed; + std::shared_ptr focus; + std::shared_ptr tooltip; std::unordered_map> storage; std::unique_ptr uicamera; std::shared_ptr menu; std::queue postRunnables; + float tooltipTimer = 0.0f; float doubleClickTimer = 0.0f; float doubleClickDelay = 0.5f; bool doubleClicked = false; void actMouse(float delta); void actFocused(); + void updateTooltip(float delta); + void resetTooltip(); public: GUI(); ~GUI(); diff --git a/src/graphics/ui/elements/CheckBox.cpp b/src/graphics/ui/elements/CheckBox.cpp index 16a2d9e6..eb9aa233 100644 --- a/src/graphics/ui/elements/CheckBox.cpp +++ b/src/graphics/ui/elements/CheckBox.cpp @@ -7,7 +7,8 @@ using namespace gui; CheckBox::CheckBox(bool checked) : UINode(glm::vec2(32.0f)), checked(checked) { - setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f)); + setColor({0.0f, 0.0f, 0.0f, 0.5f}); + setHoverColor({0.05f, 0.1f, 0.2f, 0.75f}); } void CheckBox::draw(const DrawContext* pctx, Assets*) { diff --git a/src/graphics/ui/elements/CheckBox.hpp b/src/graphics/ui/elements/CheckBox.hpp index 41e9c61f..cc0593c8 100644 --- a/src/graphics/ui/elements/CheckBox.hpp +++ b/src/graphics/ui/elements/CheckBox.hpp @@ -6,7 +6,6 @@ namespace gui { class CheckBox : public UINode { protected: - glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f}; glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f}; boolsupplier supplier = nullptr; boolconsumer consumer = nullptr; @@ -51,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 51341aa3..e31cde11 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -13,7 +13,7 @@ Container::Container(glm::vec2 size) : UINode(size) { } std::shared_ptr Container::getAt(glm::vec2 pos, std::shared_ptr self) { - if (!interactive || !isEnabled()) { + if (!isInteractive() || !isEnabled()) { return nullptr; } if (!isInside(pos)) return nullptr; @@ -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/Image.cpp b/src/graphics/ui/elements/Image.cpp index f350ca0d..ffc00491 100644 --- a/src/graphics/ui/elements/Image.cpp +++ b/src/graphics/ui/elements/Image.cpp @@ -33,3 +33,11 @@ void Image::setAutoResize(bool flag) { bool Image::isAutoResize() const { return autoresize; } + +const std::string& Image::getTexture() const { + return texture; +} + +void Image::setTexture(const std::string& name) { + texture = name; +} diff --git a/src/graphics/ui/elements/Image.hpp b/src/graphics/ui/elements/Image.hpp index 66781ef0..06bab69d 100644 --- a/src/graphics/ui/elements/Image.hpp +++ b/src/graphics/ui/elements/Image.hpp @@ -15,6 +15,8 @@ namespace gui { virtual void setAutoResize(bool flag); virtual bool isAutoResize() const; + virtual const std::string& getTexture() const; + virtual void setTexture(const std::string& name); }; } diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index 1ca82a72..a50f45df 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -1,7 +1,9 @@ #include "InventoryView.hpp" + #include "../../../assets/Assets.hpp" #include "../../../content/Content.hpp" #include "../../../frontend/LevelFrontend.hpp" +#include "../../../frontend/locale.hpp" #include "../../../items/Inventories.hpp" #include "../../../items/Inventory.hpp" #include "../../../items/ItemDef.hpp" @@ -22,7 +24,6 @@ #include "../../render/BlocksPreview.hpp" #include "../GUI.hpp" -#include #include using namespace gui; @@ -108,6 +109,7 @@ SlotView::SlotView( layout(layout) { setColor(glm::vec4(0, 0, 0, 0.2f)); + setTooltipDelay(0.05f); } void SlotView::draw(const DrawContext* pctx, Assets* assets) { @@ -251,11 +253,12 @@ void SlotView::clicked(gui::GUI* gui, mousecode button) { stack.setCount(halfremain); } } else { + auto stackDef = content->getIndices()->getItemDef(stack.getItemId()); if (stack.isEmpty()) { stack.set(grabbed); stack.setCount(1); grabbed.setCount(grabbed.getCount()-1); - } else if (stack.accepts(grabbed)){ + } else if (stack.accepts(grabbed) && stack.getCount() < stackDef->stackSize){ stack.setCount(stack.getCount()+1); grabbed.setCount(grabbed.getCount()-1); } @@ -270,6 +273,17 @@ void SlotView::onFocus(gui::GUI* gui) { clicked(gui, mousecode::BUTTON_1); } +const std::wstring SlotView::getTooltip() const { + const auto str = UINode::getTooltip(); + if (!str.empty() || bound->isEmpty()) { + return str; + } + auto def = content->getIndices()->getItemDef(bound->getItemId()); + return util::pascal_case( + langs::get(util::str2wstr_utf8(def->caption)) + ); // TODO: cache +} + void SlotView::bind( int64_t inventoryid, ItemStack& stack, diff --git a/src/graphics/ui/elements/InventoryView.hpp b/src/graphics/ui/elements/InventoryView.hpp index 8e3006d7..5bb5629c 100644 --- a/src/graphics/ui/elements/InventoryView.hpp +++ b/src/graphics/ui/elements/InventoryView.hpp @@ -63,6 +63,7 @@ namespace gui { virtual void clicked(gui::GUI*, mousecode) override; virtual void onFocus(gui::GUI*) override; + virtual const std::wstring getTooltip() const override; void bind( int64_t inventoryid, diff --git a/src/graphics/ui/elements/Label.cpp b/src/graphics/ui/elements/Label.cpp index a7bb198d..9e355e86 100644 --- a/src/graphics/ui/elements/Label.cpp +++ b/src/graphics/ui/elements/Label.cpp @@ -64,12 +64,28 @@ Label::Label(std::wstring text, std::string fontName) cache.update(this->text, multiline, textWrap); } +glm::vec2 Label::calcSize() { + auto font = cache.font; + uint lineHeight = font->getLineHeight(); + if (cache.lines.size() > 1) { + lineHeight *= lineInterval; + } + return glm::vec2 ( + cache.font->calcWidth(text), + lineHeight * cache.lines.size() + font->getYOffset() + ); +} + void Label::setText(std::wstring text) { if (text == this->text && !cache.resetFlag) { return; } this->text = text; cache.update(this->text, multiline, textWrap); + + if (cache.font && autoresize) { + setSize(calcSize()); + } } const std::wstring& Label::getText() const { @@ -156,10 +172,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) { lineHeight *= lineInterval; } glm::vec2 size = getSize(); - glm::vec2 newsize ( - font->calcWidth(text), - lineHeight * cache.lines.size() + font->getYOffset() - ); + glm::vec2 newsize = calcSize(); + if (autoresize) { + setSize(newsize); + } glm::vec2 pos = calcPos(); switch (align) { @@ -194,6 +210,13 @@ void Label::textSupplier(wstringsupplier supplier) { this->supplier = supplier; } +void Label::setAutoResize(bool flag) { + this->autoresize = flag; +} + +bool Label::isAutoResize() const { + return autoresize; +} void Label::setMultiline(bool multiline) { if (multiline != this->multiline) { diff --git a/src/graphics/ui/elements/Label.hpp b/src/graphics/ui/elements/Label.hpp index b7615ad5..87e82e72 100644 --- a/src/graphics/ui/elements/Label.hpp +++ b/src/graphics/ui/elements/Label.hpp @@ -24,6 +24,8 @@ namespace gui { class Label : public UINode { LabelCache cache; + + glm::vec2 calcSize(); protected: std::wstring text; std::string fontName; @@ -47,6 +49,9 @@ namespace gui { /// @brief Text line height multiplied by line interval int totalLineHeight = 1; + + /// @brief Auto resize label to fit text + bool autoresize = false; public: Label(std::string text, std::string fontName="normal"); Label(std::wstring text, std::string fontName="normal"); @@ -95,6 +100,9 @@ namespace gui { virtual void textSupplier(wstringsupplier supplier); + virtual void setAutoResize(bool flag); + virtual bool isAutoResize() const; + virtual void setMultiline(bool multiline); virtual bool isMultiline() const; diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 2240acee..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; @@ -369,7 +372,7 @@ void TextBox::click(GUI*, int x, int y) { } void TextBox::mouseMove(GUI*, int x, int y) { - ssize_t index = calcIndexAt(x, y); + ptrdiff_t index = calcIndexAt(x, y); setCaret(index); extendSelection(index); resetMaxLocalCaret(); @@ -452,7 +455,7 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) { uint offset = std::min(size_t(maxLocalCaret), getLineLength(caretLine-1)-1); setCaret(label->getTextLineOffset(caretLine-1) + offset); } else { - setCaret(0UL); + setCaret(static_cast(0)); } if (shiftPressed) { if (selectionStart == selectionEnd) { @@ -652,11 +655,11 @@ void TextBox::setCaret(size_t position) { if (realoffset-width > 0) { setTextOffset(textOffset + realoffset-width); } else if (realoffset < 0) { - setTextOffset(std::max(textOffset + realoffset, 0LU)); + setTextOffset(std::max(textOffset + realoffset, static_cast(0))); } } -void TextBox::setCaret(ssize_t position) { +void TextBox::setCaret(ptrdiff_t position) { if (position < 0) { setCaret(static_cast(input.length() + position + 1)); } else { diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index 183a5ec7..ee59b778 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -120,7 +120,7 @@ namespace gui { /// @brief Set caret position in the text /// @param position integer in range [-text.length(), text.length()] - virtual void setCaret(ssize_t position); + virtual void setCaret(ptrdiff_t position); /// @brief Select part of the text /// @param start index of the first selected character diff --git a/src/graphics/ui/elements/UINode.cpp b/src/graphics/ui/elements/UINode.cpp index de1ced1c..76297154 100644 --- a/src/graphics/ui/elements/UINode.cpp +++ b/src/graphics/ui/elements/UINode.cpp @@ -13,6 +13,9 @@ UINode::~UINode() { } bool UINode::isVisible() const { + if (visible && parent) { + return parent->isVisible(); + } return visible; } @@ -107,7 +110,7 @@ bool UINode::isInside(glm::vec2 point) { } std::shared_ptr UINode::getAt(glm::vec2 point, std::shared_ptr self) { - if (!interactive || !enabled) { + if (!isInteractive() || !enabled) { return nullptr; } return isInside(point) ? self : nullptr; @@ -129,6 +132,22 @@ bool UINode::isResizing() const { return resizing; } +void UINode::setTooltip(const std::wstring& text) { + this->tooltip = text; +} + +const std::wstring UINode::getTooltip() const { + return tooltip; +} + +void UINode::setTooltipDelay(float delay) { + tooltipDelay = delay; +} + +float UINode::getTooltipDelay() const { + return tooltipDelay; +} + glm::vec2 UINode::calcPos() const { if (parent) { return pos + parent->calcPos() + parent->contentOffset(); @@ -315,8 +334,18 @@ void UINode::setGravity(Gravity gravity) { } } +bool UINode::isSubnodeOf(const UINode* node) { + if (parent == nullptr) { + return false; + } + if (parent == node) { + return true; + } + return parent->isSubnodeOf(node); +} + void UINode::getIndices( - std::shared_ptr node, + const std::shared_ptr node, std::unordered_map>& map ) { const std::string& id = node->getId(); @@ -330,3 +359,19 @@ void UINode::getIndices( } } } +std::shared_ptr UINode::find( + const std::shared_ptr node, + const std::string& id +) { + if (node->getId() == id) { + return node; + } + if (auto container = std::dynamic_pointer_cast(node)) { + for (auto subnode : container->getNodes()) { + if (auto found = UINode::find(subnode, id)) { + return found; + } + } + } + return nullptr; +} diff --git a/src/graphics/ui/elements/UINode.hpp b/src/graphics/ui/elements/UINode.hpp index b4667acd..74977b57 100644 --- a/src/graphics/ui/elements/UINode.hpp +++ b/src/graphics/ui/elements/UINode.hpp @@ -109,6 +109,10 @@ namespace gui { ActionsSet actions; /// @brief 'ondoubleclick' callbacks ActionsSet doubleClickCallbacks; + /// @brief element tooltip text + std::wstring tooltip; + /// @brief element tooltip delay + float tooltipDelay = 0.5f; UINode(glm::vec2 size); public: @@ -197,6 +201,12 @@ namespace gui { virtual void setResizing(bool flag); virtual bool isResizing() const; + virtual void setTooltip(const std::wstring& text); + virtual const std::wstring getTooltip() const; + + virtual void setTooltipDelay(float delay); + virtual float getTooltipDelay() const; + virtual glm::vec4 calcColor() const; /// @brief Get inner content offset. Used for scroll @@ -235,11 +245,18 @@ namespace gui { virtual void setGravity(Gravity gravity); + bool isSubnodeOf(const UINode* node); + /// @brief collect all nodes having id static void getIndices( - std::shared_ptr node, + const std::shared_ptr node, std::unordered_map>& map ); + + static std::shared_ptr find( + const std::shared_ptr node, + const std::string& id + ); }; } diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 50ba1bb2..3ef8a09d 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -143,6 +143,13 @@ static void _readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& no )); } + if (element->has("tooltip")) { + node.setTooltip(util::str2wstr_utf8(element->attr("tooltip").getText())); + } + if (element->has("tooltip-delay")) { + node.setTooltipDelay(element->attr("tooltip-delay").asFloat()); + } + if (auto onclick = create_action(reader, element, "onclick")) { node.listenAction(onclick); } @@ -245,6 +252,9 @@ static std::shared_ptr readLabel(UiXmlReader& reader, xml::xmlelement el reader.getFilename() )); } + if (element->has("autoresize")) { + label->setAutoResize(element->attr("autoresize").asBool()); + } if (element->has("multiline")) { label->setMultiline(element->attr("multiline").asBool()); if (!element->has("valign")) { 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/CommandsInterpreter.cpp b/src/logic/CommandsInterpreter.cpp index 54a99901..e157cbb4 100644 --- a/src/logic/CommandsInterpreter.cpp +++ b/src/logic/CommandsInterpreter.cpp @@ -320,7 +320,7 @@ public: dynamic::Value value = dynamic::NONE; if (peek() == '~') { relative = true; - value = 0L; + value = static_cast(0); nextChar(); } diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index 8a2be95e..b9f36bb2 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -223,7 +223,8 @@ void EngineController::reconfigPacks( std::stringstream ss; for (const auto& id : packsToRemove) { - if (content->getPackRuntime(id)->getStats().hasSavingContent()) { + auto runtime = content->getPackRuntime(id); + if (runtime && runtime->getStats().hasSavingContent()) { if (hasIndices) { ss << ", "; } @@ -234,13 +235,22 @@ void EngineController::reconfigPacks( runnable removeFunc = [=]() { if (controller == nullptr) { - auto manager = engine->createPacksManager(fs::path("")); - manager.scan(); - std::vector names = engine->getBasePacks(); - for (auto& name : packsToAdd) { - names.push_back(name); + try { + auto manager = engine->createPacksManager(fs::path("")); + manager.scan(); + std::vector names = PacksManager::getNames(engine->getContentPacks()); + for (const auto& id : packsToAdd) { + names.push_back(id); + } + for (const auto& id : packsToRemove) { + manager.exclude(id); + names.erase(std::find(names.begin(), names.end(), id)); + } + names = manager.assembly(names); + engine->getContentPacks() = manager.getAll(names); + } catch (const contentpack_error& err) { + throw std::runtime_error(std::string(err.what())+" ["+err.getPackId()+"]"); } - engine->getContentPacks() = manager.getAll(names); } else { auto world = controller->getLevel()->getWorld(); auto wfile = world->wfile.get(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 5e0cb198..4e965f72 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -1,4 +1,6 @@ #include "LevelController.hpp" + +#include "../settings.hpp" #include "../files/WorldFiles.hpp" #include "../debug/Logger.hpp" #include "../world/Level.hpp" @@ -20,12 +22,12 @@ LevelController::LevelController(EngineSettings& settings, std::unique_ptrupdate(delta, input, pause); glm::vec3 position = player->getPlayer()->hitbox->position; level->loadMatrix(position.x, position.z, settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2); chunks->update(settings.chunks.loadSpeed.get()); + player->update(delta, input, pause); // erease null pointers level->objects.erase( diff --git a/src/logic/LevelController.hpp b/src/logic/LevelController.hpp index 4c23ad77..3530df68 100644 --- a/src/logic/LevelController.hpp +++ b/src/logic/LevelController.hpp @@ -2,7 +2,6 @@ #define LOGIC_LEVEL_CONTROLLER_HPP_ #include -#include "../settings.hpp" #include "PlayerController.hpp" #include "BlocksController.hpp" @@ -10,6 +9,7 @@ class Level; class Player; +struct EngineSettings; /// @brief LevelController manages other controllers class LevelController { diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 91bda4e8..beb452f6 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -22,6 +22,7 @@ #include "../items/ItemStack.hpp" #include "../items/Inventory.hpp" #include "../core_defs.hpp" +#include "../settings.hpp" const float CAM_SHAKE_OFFSET = 0.025f; const float CAM_SHAKE_OFFSET_Y = 0.031f; @@ -46,14 +47,16 @@ void CameraControl::refresh() { } void CameraControl::updateMouse(PlayerInput& input) { - glm::vec2& cam = player->cam; + glm::vec3& cam = player->cam; float sensitivity = (input.zoom ? settings.sensitivity.get() / 4.f : settings.sensitivity.get()); - cam -= glm::degrees(Events::delta / (float)Window::height * sensitivity); - + auto d = glm::degrees(Events::delta / (float)Window::height * sensitivity); + cam.x -= d.x; + cam.y -= d.y; + if (cam.y < -89.9f) { cam.y = -89.9f; } @@ -68,7 +71,7 @@ void CameraControl::updateMouse(PlayerInput& input) { } camera->rotation = glm::mat4(1.0f); - camera->rotate(glm::radians(cam.y), glm::radians(cam.x), 0); + camera->rotate(glm::radians(cam.y), glm::radians(cam.x), glm::radians(cam.z)); } glm::vec3 CameraControl::updateCameraShaking(float delta) { @@ -163,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, @@ -247,7 +249,7 @@ void PlayerController::update(float delta, bool input, bool pause) { updateInteraction(); } else { selectedBlockId = -1; - selectedBlockStates = 0; + selectedBlockRotation = 0; } } @@ -359,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; @@ -371,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)) @@ -408,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; @@ -424,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); @@ -439,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 bc2d1fb7..46e5cbca 100644 --- a/src/logic/PlayerController.hpp +++ b/src/logic/PlayerController.hpp @@ -1,7 +1,6 @@ #ifndef PLAYER_CONTROL_HPP_ #define PLAYER_CONTROL_HPP_ -#include "../settings.hpp" #include "../objects/Player.hpp" #include @@ -12,7 +11,9 @@ class Camera; class Level; class Block; +class Chunks; class BlocksController; +struct CameraSettings; class CameraControl { std::shared_ptr player; @@ -76,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/LuaState.cpp b/src/logic/scripting/lua/LuaState.cpp index 902e9bf3..49f83439 100644 --- a/src/logic/scripting/lua/LuaState.cpp +++ b/src/logic/scripting/lua/LuaState.cpp @@ -12,14 +12,16 @@ inline std::string LAMBDAS_TABLE = "$L"; static debug::Logger logger("lua-state"); +using namespace lua; + namespace scripting { - extern lua::LuaState* state; + extern LuaState* state; } -lua::luaerror::luaerror(const std::string& message) : std::runtime_error(message) { +luaerror::luaerror(const std::string& message) : std::runtime_error(message) { } -void lua::LuaState::removeLibFuncs(const char* libname, const char* funcs[]) { +void LuaState::removeLibFuncs(const char* libname, const char* funcs[]) { if (getglobal(libname)) { for (uint i = 0; funcs[i]; i++) { pushnil(); @@ -28,13 +30,13 @@ void lua::LuaState::removeLibFuncs(const char* libname, const char* funcs[]) { } } -lua::LuaState::LuaState() { +LuaState::LuaState() { logger.info() << LUA_VERSION; logger.info() << LUAJIT_VERSION; L = luaL_newstate(); if (L == nullptr) { - throw lua::luaerror("could not to initialize Lua"); + throw luaerror("could not to initialize Lua"); } // Allowed standard libraries luaopen_base(L); @@ -66,24 +68,24 @@ lua::LuaState::LuaState() { setglobal(LAMBDAS_TABLE); } -const std::string lua::LuaState::envName(int env) { +const std::string LuaState::envName(int env) { return "_ENV"+util::mangleid(env); } -lua::LuaState::~LuaState() { +LuaState::~LuaState() { lua_close(L); } -void lua::LuaState::logError(const std::string& text) { +void LuaState::logError(const std::string& text) { logger.error() << text; } -void lua::LuaState::addfunc(const std::string& name, lua_CFunction func) { +void LuaState::addfunc(const std::string& name, lua_CFunction func) { lua_pushcfunction(L, func); lua_setglobal(L, name.c_str()); } -bool lua::LuaState::getglobal(const std::string& name) { +bool LuaState::getglobal(const std::string& name) { lua_getglobal(L, name.c_str()); if (lua_isnil(L, lua_gettop(L))) { lua_pop(L, lua_gettop(L)); @@ -92,7 +94,7 @@ bool lua::LuaState::getglobal(const std::string& name) { return true; } -bool lua::LuaState::hasglobal(const std::string& name) { +bool LuaState::hasglobal(const std::string& name) { lua_getglobal(L, name.c_str()); if (lua_isnil(L, lua_gettop(L))) { lua_pop(L, lua_gettop(L)); @@ -102,11 +104,11 @@ bool lua::LuaState::hasglobal(const std::string& name) { return true; } -void lua::LuaState::setglobal(const std::string& name) { +void LuaState::setglobal(const std::string& name) { lua_setglobal(L, name.c_str()); } -bool lua::LuaState::rename(const std::string& from, const std::string& to) { +bool LuaState::rename(const std::string& from, const std::string& to) { const char* src = from.c_str(); lua_getglobal(L, src); if (lua_isnil(L, lua_gettop(L))) { @@ -121,12 +123,12 @@ bool lua::LuaState::rename(const std::string& from, const std::string& to) { return true; } -void lua::LuaState::remove(const std::string& name) { +void LuaState::remove(const std::string& name) { lua_pushnil(L); lua_setglobal(L, name.c_str()); } -void lua::LuaState::createLibs() { +void LuaState::createLibs() { openlib("audio", audiolib); openlib("block", blocklib); openlib("console", consolelib); @@ -140,28 +142,29 @@ void lua::LuaState::createLibs() { openlib("pack", packlib); openlib("player", playerlib); openlib("time", timelib); + openlib("toml", tomllib); openlib("world", worldlib); addfunc("print", lua_wrap_errors); } -void lua::LuaState::loadbuffer(int env, const std::string& src, const std::string& file) { +void LuaState::loadbuffer(int env, const std::string& src, const std::string& file) { if (luaL_loadbuffer(L, src.c_str(), src.length(), file.c_str())) { - throw lua::luaerror(lua_tostring(L, -1)); + throw luaerror(lua_tostring(L, -1)); } if (env && getglobal(envName(env))) { lua_setfenv(L, -2); } } -int lua::LuaState::call(int argc, int nresults) { +int LuaState::call(int argc, int nresults) { if (lua_pcall(L, argc, nresults, 0)) { - throw lua::luaerror(lua_tostring(L, -1)); + throw luaerror(lua_tostring(L, -1)); } return 1; } -int lua::LuaState::callNoThrow(int argc) { +int LuaState::callNoThrow(int argc) { if (lua_pcall(L, argc, LUA_MULTRET, 0)) { logError(lua_tostring(L, -1)); return 0; @@ -169,59 +172,59 @@ int lua::LuaState::callNoThrow(int argc) { return 1; } -int lua::LuaState::eval(int env, const std::string& src, const std::string& file) { +int LuaState::eval(int env, const std::string& src, const std::string& file) { auto srcText = "return "+src; loadbuffer(env, srcText, file); return call(0); } -int lua::LuaState::execute(int env, const std::string& src, const std::string& file) { +int LuaState::execute(int env, const std::string& src, const std::string& file) { loadbuffer(env, src, file); return callNoThrow(0); } -int lua::LuaState::gettop() const { +int LuaState::gettop() const { return lua_gettop(L); } -int lua::LuaState::pushinteger(luaint x) { +int LuaState::pushinteger(lua_Integer x) { lua_pushinteger(L, x); return 1; } -int lua::LuaState::pushnumber(luanumber x) { +int LuaState::pushnumber(lua_Number x) { lua_pushnumber(L, x); return 1; } -int lua::LuaState::pushboolean(bool x) { +int LuaState::pushboolean(bool x) { lua_pushboolean(L, x); return 1; } -int lua::LuaState::pushivec3(luaint x, luaint y, luaint z) { +int LuaState::pushivec3(lua_Integer x, lua_Integer y, lua_Integer z) { lua::pushivec3(L, x, y, z); return 3; } -int lua::LuaState::pushstring(const std::string& str) { +int LuaState::pushstring(const std::string& str) { lua_pushstring(L, str.c_str()); return 1; } -int lua::LuaState::pushenv(int env) { +int LuaState::pushenv(int env) { if (getglobal(envName(env))) { return 1; } return 0; } -int lua::LuaState::pushvalue(int idx) { +int LuaState::pushvalue(int idx) { lua_pushvalue(L, idx); return 1; } -int lua::LuaState::pushvalue(const dynamic::Value& value) { +int LuaState::pushvalue(const dynamic::Value& value) { using namespace dynamic; if (auto* flag = std::get_if(&value)) { @@ -252,26 +255,26 @@ int lua::LuaState::pushvalue(const dynamic::Value& value) { return 1; } -int lua::LuaState::pushglobals() { +int LuaState::pushglobals() { lua_pushvalue(L, LUA_GLOBALSINDEX); return 1; } -int lua::LuaState::pushcfunction(lua_CFunction function) { +int LuaState::pushcfunction(lua_CFunction function) { lua_pushcfunction(L, function); return 1; } -void lua::LuaState::pop(int n) { +void LuaState::pop(int n) { lua_pop(L, n); } -int lua::LuaState::pushnil() { +int LuaState::pushnil() { lua_pushnil(L); return 1; } -bool lua::LuaState::getfield(const std::string& name, int idx) { +bool LuaState::getfield(const std::string& name, int idx) { lua_getfield(L, idx, name.c_str()); if (lua_isnil(L, -1)) { lua_pop(L, -1); @@ -280,35 +283,35 @@ bool lua::LuaState::getfield(const std::string& name, int idx) { return true; } -void lua::LuaState::setfield(const std::string& name, int idx) { +void LuaState::setfield(const std::string& name, int idx) { lua_setfield(L, idx, name.c_str()); } -bool lua::LuaState::toboolean(int idx) { +bool LuaState::toboolean(int idx) { return lua_toboolean(L, idx); } -lua::luaint lua::LuaState::tointeger(int idx) { +lua_Integer LuaState::tointeger(int idx) { return lua_tointeger(L, idx); } -lua::luanumber lua::LuaState::tonumber(int idx) { +lua_Number LuaState::tonumber(int idx) { return lua_tonumber(L, idx); } -glm::vec2 lua::LuaState::tovec2(int idx) { +glm::vec2 LuaState::tovec2(int idx) { return lua::tovec2(L, idx); } -glm::vec4 lua::LuaState::tocolor(int idx) { +glm::vec4 LuaState::tocolor(int idx) { return lua::tocolor(L, idx); } -const char* lua::LuaState::tostring(int idx) { +const char* LuaState::tostring(int idx) { return lua_tostring(L, idx); } -dynamic::Value lua::LuaState::tovalue(int idx) { +dynamic::Value LuaState::tovalue(int idx) { using namespace dynamic; auto type = lua_type(L, idx); switch (type) { @@ -361,21 +364,21 @@ dynamic::Value lua::LuaState::tovalue(int idx) { } } -bool lua::LuaState::isstring(int idx) { +bool LuaState::isstring(int idx) { return lua_isstring(L, idx); } -bool lua::LuaState::isfunction(int idx) { +bool LuaState::isfunction(int idx) { return lua_isfunction(L, idx); } -void lua::LuaState::openlib(const std::string& name, const luaL_Reg* libfuncs) { +void LuaState::openlib(const std::string& name, const luaL_Reg* libfuncs) { lua_newtable(L); luaL_setfuncs(L, libfuncs, 0); lua_setglobal(L, name.c_str()); } -std::shared_ptr lua::LuaState::createLambdaHandler() { +std::shared_ptr LuaState::createLambdaHandler() { auto ptr = reinterpret_cast(lua_topointer(L, -1)); auto name = util::mangleid(ptr); lua_getglobal(L, LAMBDAS_TABLE.c_str()); @@ -392,7 +395,7 @@ std::shared_ptr lua::LuaState::createLambdaHandler() { }); } -runnable lua::LuaState::createRunnable() { +runnable LuaState::createRunnable() { auto funcptr = createLambdaHandler(); return [=]() { lua_getglobal(L, LAMBDAS_TABLE.c_str()); @@ -401,7 +404,7 @@ runnable lua::LuaState::createRunnable() { }; } -scripting::common_func lua::LuaState::createLambda() { +scripting::common_func LuaState::createLambda() { auto funcptr = createLambdaHandler(); return [=](const std::vector& args) { lua_getglobal(L, LAMBDAS_TABLE.c_str()); @@ -418,7 +421,14 @@ scripting::common_func lua::LuaState::createLambda() { }; } -int lua::LuaState::createEnvironment(int parent) { +const char* LuaState::requireString(int idx) { + if (!lua_isstring(L, idx)) { + throw luaerror("string expected at "+std::to_string(idx)); + } + return lua_tostring(L, idx); +} + +int LuaState::createEnvironment(int parent) { int id = nextEnvironment++; // local env = {} @@ -442,7 +452,7 @@ int lua::LuaState::createEnvironment(int parent) { } -void lua::LuaState::removeEnvironment(int id) { +void LuaState::removeEnvironment(int id) { if (id == 0) { return; } @@ -450,7 +460,7 @@ void lua::LuaState::removeEnvironment(int id) { setglobal(envName(id)); } -bool lua::LuaState::emit_event(const std::string &name, std::function args) { +bool LuaState::emit_event(const std::string &name, std::function args) { getglobal("events"); getfield("emit"); pushstring(name); @@ -461,7 +471,7 @@ bool lua::LuaState::emit_event(const std::string &name, std::function #include @@ -36,9 +32,9 @@ namespace lua { static const std::string envName(int env); void loadbuffer(int env, const std::string& src, const std::string& file); int gettop() const; - int pushivec3(luaint x, luaint y, luaint z); - int pushinteger(luaint x); - int pushnumber(luanumber x); + int pushivec3(lua_Integer x, lua_Integer y, lua_Integer z); + int pushinteger(lua_Integer x); + int pushnumber(lua_Number x); int pushboolean(bool x); int pushstring(const std::string& str); int pushenv(int env); @@ -51,8 +47,8 @@ namespace lua { bool getfield(const std::string& name, int idx = -1); void setfield(const std::string& name, int idx = -2); bool toboolean(int idx); - luaint tointeger(int idx); - luanumber tonumber(int idx); + lua_Integer tointeger(int idx); + lua_Number tonumber(int idx); glm::vec2 tovec2(int idx); glm::vec4 tocolor(int idx); dynamic::Value tovalue(int idx); @@ -73,6 +69,8 @@ namespace lua { runnable createRunnable(); scripting::common_func createLambda(); + const char* requireString(int idx); + int createEnvironment(int parent); void removeEnvironment(int id); bool emit_event(const std::string& name, std::function args=[](auto*){return 0;}); diff --git a/src/logic/scripting/lua/api_lua.hpp b/src/logic/scripting/lua/api_lua.hpp index 4d798d82..69526057 100644 --- a/src/logic/scripting/lua/api_lua.hpp +++ b/src/logic/scripting/lua/api_lua.hpp @@ -8,19 +8,20 @@ // Libraries extern const luaL_Reg audiolib []; extern const luaL_Reg blocklib []; +extern const luaL_Reg consolelib []; extern const luaL_Reg corelib []; extern const luaL_Reg filelib []; extern const luaL_Reg guilib []; extern const luaL_Reg hudlib []; +extern const luaL_Reg inputlib []; extern const luaL_Reg inventorylib []; extern const luaL_Reg itemlib []; +extern const luaL_Reg jsonlib []; extern const luaL_Reg packlib []; extern const luaL_Reg playerlib []; extern const luaL_Reg timelib []; +extern const luaL_Reg tomllib []; extern const luaL_Reg worldlib []; -extern const luaL_Reg jsonlib []; -extern const luaL_Reg inputlib []; -extern const luaL_Reg consolelib []; // Lua Overrides extern int l_print(lua_State* L); diff --git a/src/logic/scripting/lua/libaudio.cpp b/src/logic/scripting/lua/libaudio.cpp index 8f19d20e..833b00ae 100644 --- a/src/logic/scripting/lua/libaudio.cpp +++ b/src/logic/scripting/lua/libaudio.cpp @@ -23,22 +23,22 @@ inline int extract_channel_index(lua_State* L, int idx) { inline audio::speakerid_t play_sound( const char* name, bool relative, - lua::luanumber x, - lua::luanumber y, - lua::luanumber z, - lua::luanumber volume, - lua::luanumber pitch, + lua_Number x, + lua_Number y, + lua_Number z, + lua_Number volume, + lua_Number pitch, bool loop, int channel ) { - if (channel == -1) + if (channel == -1) { return 0; + } auto assets = scripting::engine->getAssets(); auto sound = assets->getSound(name); if (sound == nullptr) { return 0; } - return audio::play( sound, glm::vec3( @@ -58,20 +58,20 @@ inline audio::speakerid_t play_sound( inline audio::speakerid_t play_stream( const char* filename, bool relative, - lua::luanumber x, - lua::luanumber y, - lua::luanumber z, - lua::luanumber volume, - lua::luanumber pitch, + lua_Number x, + lua_Number y, + lua_Number z, + lua_Number volume, + lua_Number pitch, bool loop, int channel ) { - if (channel == -1) + if (channel == -1) { return 0; + } auto paths = scripting::engine->getResPaths(); - fs::path file = paths->find(filename); return audio::play_stream( - file, + paths->find(filename), glm::vec3( static_cast(x), static_cast(y), @@ -95,7 +95,7 @@ inline audio::speakerid_t play_stream( /// channel: string = "regular", /// loop: bool = false) static int l_audio_play_stream(lua_State* L) { - lua_pushinteger(L, static_cast( + lua_pushinteger(L, static_cast( play_stream( lua_tostring(L, 1), false, @@ -118,7 +118,7 @@ static int l_audio_play_stream(lua_State* L) { /// channel: string = "regular", /// loop: bool = false) static int l_audio_play_stream_2d(lua_State* L) { - lua_pushinteger(L, static_cast( + lua_pushinteger(L, static_cast( play_stream( lua_tostring(L, 1), true, @@ -142,7 +142,7 @@ static int l_audio_play_stream_2d(lua_State* L) { /// channel: string = "regular", /// loop: bool = false) static int l_audio_play_sound(lua_State* L) { - lua_pushinteger(L, static_cast( + lua_pushinteger(L, static_cast( play_sound( lua_tostring(L, 1), false, @@ -165,7 +165,7 @@ static int l_audio_play_sound(lua_State* L) { /// channel: string = "regular", /// loop: bool = false) static int l_audio_play_sound_2d(lua_State* L) { - lua_pushinteger(L, static_cast( + lua_pushinteger(L, static_cast( play_sound( lua_tostring(L, 1), true, @@ -181,8 +181,7 @@ static int l_audio_play_sound_2d(lua_State* L) { /// @brief audio.stop(speakerid: integer) -> nil static int l_audio_stop(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { speaker->stop(); } @@ -191,8 +190,7 @@ static int l_audio_stop(lua_State* L) { /// @brief audio.pause(speakerid: integer) -> nil static int l_audio_pause(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { speaker->pause(); } @@ -201,8 +199,7 @@ static int l_audio_pause(lua_State* L) { /// @brief audio.resume(speakerid: integer) -> nil static int l_audio_resume(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr && speaker->isPaused()) { speaker->play(); } @@ -211,8 +208,7 @@ static int l_audio_resume(lua_State* L) { /// @brief audio.set_loop(speakerid: integer, value: bool) -> nil static int l_audio_set_loop(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { bool value = lua_toboolean(L, 2); speaker->setLoop(value); @@ -222,45 +218,38 @@ static int l_audio_set_loop(lua_State* L) { /// @brief audio.set_volume(speakerid: integer, value: number) -> nil static int l_audio_set_volume(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { - lua::luanumber value = lua_tonumber(L, 2); - speaker->setVolume(static_cast(value)); + speaker->setVolume(static_cast(lua_tonumber(L, 2))); } return 0; } /// @brief audio.set_pitch(speakerid: integer, value: number) -> nil static int l_audio_set_pitch(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { - lua::luanumber value = lua_tonumber(L, 2); - speaker->setPitch(static_cast(value)); + speaker->setPitch(static_cast(lua_tonumber(L, 2))); } return 0; } /// @brief audio.set_time(speakerid: integer, value: number) -> nil static int l_audio_set_time(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { - lua::luanumber value = lua_tonumber(L, 2); - speaker->setTime(static_cast(value)); + speaker->setTime(static_cast(lua_tonumber(L, 2))); } return 0; } /// @brief audio.set_position(speakerid: integer, x: number, y: number, z: number) -> nil static int l_audio_set_position(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { - lua::luanumber x = lua_tonumber(L, 2); - lua::luanumber y = lua_tonumber(L, 3); - lua::luanumber z = lua_tonumber(L, 4); + auto x = lua_tonumber(L, 2); + auto y = lua_tonumber(L, 3); + auto z = lua_tonumber(L, 4); speaker->setPosition(glm::vec3( static_cast(x), static_cast(y), @@ -272,12 +261,11 @@ static int l_audio_set_position(lua_State* L) { /// @brief audio.set_velocity(speakerid: integer, x: number, y: number, z: number) -> nil static int l_audio_set_velocity(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { - lua::luanumber x = lua_tonumber(L, 2); - lua::luanumber y = lua_tonumber(L, 3); - lua::luanumber z = lua_tonumber(L, 4); + auto x = lua_tonumber(L, 2); + auto y = lua_tonumber(L, 3); + auto z = lua_tonumber(L, 4); speaker->setVelocity(glm::vec3( static_cast(x), static_cast(y), @@ -289,8 +277,7 @@ static int l_audio_set_velocity(lua_State* L) { /// @brief audio.is_playing(speakerid: integer) -> bool static int l_audio_is_playing(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushboolean(L, speaker->isPlaying()); return 1; @@ -301,8 +288,7 @@ static int l_audio_is_playing(lua_State* L) { /// @brief audio.is_paused(speakerid: integer) -> bool static int l_audio_is_paused(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushboolean(L, speaker->isPaused()); return 1; @@ -313,8 +299,7 @@ static int l_audio_is_paused(lua_State* L) { /// @brief audio.is_loop(speakerid: integer) -> bool static int l_audio_is_loop(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushboolean(L, speaker->isLoop()); return 1; @@ -325,8 +310,7 @@ static int l_audio_is_loop(lua_State* L) { /// @brief audio.get_volume(speakerid: integer) -> number static int l_audio_get_volume(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushnumber(L, speaker->getVolume()); return 1; @@ -337,8 +321,7 @@ static int l_audio_get_volume(lua_State* L) { /// @brief audio.get_pitch(speakerid: integer) -> number static int l_audio_get_pitch(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushnumber(L, speaker->getPitch()); return 1; @@ -349,8 +332,7 @@ static int l_audio_get_pitch(lua_State* L) { /// @brief audio.get_time(speakerid: integer) -> number static int l_audio_get_time(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushnumber(L, speaker->getTime()); return 1; @@ -361,8 +343,7 @@ static int l_audio_get_time(lua_State* L) { /// @brief audio.get_duration(speakerid: integer) -> number static int l_audio_get_duration(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { lua_pushnumber(L, speaker->getDuration()); return 1; @@ -373,11 +354,9 @@ static int l_audio_get_duration(lua_State* L) { /// @brief audio.get_position(speakerid: integer) -> number, number, number static int l_audio_get_position(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { - auto vec = speaker->getPosition(); - lua::pushvec3(L, vec); + lua::pushvec3(L, speaker->getPosition()); return 1; } return 0; @@ -385,8 +364,7 @@ static int l_audio_get_position(lua_State* L) { /// @brief audio.get_velocity(speakerid: integer) -> number, number, number static int l_audio_get_velocity(lua_State* L) { - lua::luaint id = lua_tonumber(L, 1); - auto speaker = audio::get_speaker(id); + auto speaker = audio::get_speaker(lua_tointeger(L, 1)); if (speaker != nullptr) { auto vec = speaker->getVelocity(); lua::pushvec3(L, vec); diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libblock.cpp index 2de58401..22e76b84 100644 --- a/src/logic/scripting/lua/libblock.cpp +++ b/src/logic/scripting/lua/libblock.cpp @@ -1,7 +1,9 @@ #include "lua_commons.hpp" + #include "api_lua.hpp" #include "lua_util.hpp" #include "../scripting.hpp" + #include "../../../world/Level.hpp" #include "../../../voxels/Chunks.hpp" #include "../../../voxels/Chunk.hpp" @@ -13,8 +15,8 @@ int l_block_name(lua_State* L) { auto indices = scripting::content->getIndices(); - lua::luaint id = lua_tointeger(L, 1); - if (id < 0 || size_t(id) >= indices->countBlockDefs()) { + lua_Integer id = lua_tointeger(L, 1); + if (static_cast(id) >= indices->countBlockDefs()) { return 0; } auto def = indices->getBlockDef(id); @@ -22,11 +24,10 @@ int l_block_name(lua_State* L) { return 1; } - int l_block_material(lua_State* L) { auto indices = scripting::content->getIndices(); - lua::luaint id = lua_tointeger(L, 1); - if (id < 0 || size_t(id) >= indices->countBlockDefs()) { + lua_Integer id = lua_tointeger(L, 1); + if (static_cast(id) >= indices->countBlockDefs()) { return 0; } auto def = indices->getBlockDef(id); @@ -35,9 +36,9 @@ int l_block_material(lua_State* L) { } int l_is_solid_at(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); lua_pushboolean(L, scripting::level->chunks->isSolidBlock(x, y, z)); return 1; @@ -49,35 +50,36 @@ int l_blocks_count(lua_State* L) { } int l_block_index(lua_State* L) { - auto name = lua_tostring(L, 1); + std::string name = lua_tostring(L, 1); lua_pushinteger(L, scripting::content->requireBlock(name).rt.id); return 1; } int l_set_block(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); - lua::luaint id = lua_tointeger(L, 4); - lua::luaint states = lua_tointeger(L, 5); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); + lua_Integer id = lua_tointeger(L, 4); + 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; } int l_get_block(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); voxel* vox = scripting::level->chunks->get(x, y, z); int id = vox == nullptr ? -1 : vox->id; lua_pushinteger(L, id); @@ -85,9 +87,9 @@ int l_get_block(lua_State* L) { } int l_get_block_x(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); voxel* vox = scripting::level->chunks->get(x, y, z); if (vox == nullptr) { return lua::pushivec3(L, 1, 0, 0); @@ -96,15 +98,15 @@ 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); } } int l_get_block_y(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); voxel* vox = scripting::level->chunks->get(x, y, z); if (vox == nullptr) { return lua::pushivec3(L, 0, 1, 0); @@ -113,15 +115,15 @@ 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); } } int l_get_block_z(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); voxel* vox = scripting::level->chunks->get(x, y, z); if (vox == nullptr) { return lua::pushivec3(L, 0, 0, 1); @@ -130,67 +132,67 @@ 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); } } int l_get_block_rotation(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + 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; } int l_set_block_rotation(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); - lua::luaint value = lua_tointeger(L, 4); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); + lua_Integer value = lua_tointeger(L, 4); voxel* vox = scripting::level->chunks->get(x, y, z); 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; } int l_get_block_states(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + 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; } int l_set_block_states(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); - lua::luaint states = lua_tointeger(L, 4); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); + lua_Integer states = lua_tointeger(L, 4); Chunk* chunk = scripting::level->chunks->getChunkByVoxel(x, y, z); if (chunk == nullptr) { 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; } int l_get_block_user_bits(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); - lua::luaint offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; - lua::luaint bits = lua_tointeger(L, 5); + 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 bits = lua_tointeger(L, 5); voxel* vox = scripting::level->chunks->get(x, y, z); if (vox == nullptr) { @@ -198,42 +200,59 @@ 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; } int l_set_block_user_bits(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); - lua::luaint offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; - lua::luaint bits = lua_tointeger(L, 5); + 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); + lua_Integer bits = lua_tointeger(L, 5); - uint mask = ((1 << bits) - 1) << offset; - lua::luaint value = (lua_tointeger(L, 6) << offset) & mask; + 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) { - int x = lua_tointeger(L, 1); - int y = lua_tointeger(L, 2); - int z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); lua_pushboolean(L, scripting::level->chunks->isReplaceableBlock(x, y, z)); 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/libconsole.cpp b/src/logic/scripting/lua/libconsole.cpp index 52b1010d..88238f65 100644 --- a/src/logic/scripting/lua/libconsole.cpp +++ b/src/logic/scripting/lua/libconsole.cpp @@ -14,11 +14,11 @@ namespace scripting { using namespace scripting; static int l_add_command(lua_State* L) { - if (!lua_isstring(L, 1) || !lua_isstring(L, 2) || !lua_isfunction(L, 3)) { - throw std::runtime_error("invalid argument type"); + if (!lua_isfunction(L, 3)) { + throw std::runtime_error("invalid callback"); } - auto scheme = lua_tostring(L, 1); - auto description = lua_tostring(L, 2); + auto scheme = state->requireString(1); + auto description = state->requireString(2); lua_pushvalue(L, 3); auto func = state->createLambda(); try { @@ -33,15 +33,15 @@ static int l_add_command(lua_State* L) { return 0; } -static int l_execute(lua_State* L) { - auto prompt = lua_tostring(L, 1); +static int l_execute(lua_State*) { + auto prompt = state->requireString(1); auto result = engine->getCommandsInterpreter()->execute(prompt); state->pushvalue(result); return 1; } -static int l_set(lua_State* L) { - auto name = lua_tostring(L, 1); +static int l_set(lua_State*) { + auto name = state->requireString(1); auto value = state->tovalue(2); (*engine->getCommandsInterpreter())[name] = value; return 0; @@ -62,7 +62,7 @@ static int l_get_commands_list(lua_State* L) { } static int l_get_command_info(lua_State* L) { - auto name = lua_tostring(L, 1); + auto name = state->requireString(1); auto interpreter = engine->getCommandsInterpreter(); auto repo = interpreter->getRepository(); auto command = repo->get(name); 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 d2e72d8a..3c738dde 100644 --- a/src/logic/scripting/lua/libgui.cpp +++ b/src/logic/scripting/lua/libgui.cpp @@ -10,6 +10,7 @@ #include "../../../graphics/ui/gui_util.hpp" #include "../../../graphics/ui/elements/UINode.hpp" #include "../../../graphics/ui/elements/Button.hpp" +#include "../../../graphics/ui/elements/Image.hpp" #include "../../../graphics/ui/elements/CheckBox.hpp" #include "../../../graphics/ui/elements/TextBox.hpp" #include "../../../graphics/ui/elements/TrackBar.hpp" @@ -49,8 +50,8 @@ static DocumentNode getDocumentNode(lua_State*, const std::string& name, const s static DocumentNode getDocumentNode(lua_State* L, int idx=1) { lua_getfield(L, idx, "docname"); lua_getfield(L, idx, "name"); - auto docname = lua_tostring(L, -2); - auto name = lua_tostring(L, -1); + auto docname = state->requireString(-2); + auto name = state->requireString(-1); auto node = getDocumentNode(L, docname, name); lua_pop(L, 2); return node; @@ -73,7 +74,7 @@ static int l_menu_reset(lua_State* L) { static int l_textbox_paste(lua_State* L) { auto node = getDocumentNode(L); auto box = dynamic_cast(node.node.get()); - auto text = lua_tostring(L, 2); + auto text = state->requireString(2); box->paste(util::str2wstr_utf8(text)); return 0; } @@ -81,7 +82,7 @@ static int l_textbox_paste(lua_State* L) { static int l_container_add(lua_State* L) { auto docnode = getDocumentNode(L); auto node = dynamic_cast(docnode.node.get()); - auto xmlsrc = lua_tostring(L, 2); + auto xmlsrc = state->requireString(2); try { auto subnode = guiutil::create(xmlsrc, docnode.document->getEnvironment()); node->add(subnode); @@ -92,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)) { @@ -100,7 +113,18 @@ static int l_container_clear(lua_State* L) { return 0; } -static int l_uinode_move_into(lua_State* L) { +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); UINode::moveInto(node.node, std::dynamic_pointer_cast(dest.node)); @@ -233,16 +257,34 @@ static int p_get_editable(UINode* node) { return 0; } -static int p_get_add(UINode* node) { - if (dynamic_cast(node)) { - return state->pushcfunction(l_container_add); +static int p_get_src(UINode* node) { + if (auto image = dynamic_cast(node)) { + return state->pushstring(image->getTexture()); } return 0; } +static int p_get_add(UINode* node) { + if (dynamic_cast(node)) { + 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; } @@ -256,9 +298,18 @@ static int p_get_hover_color(UINode* node) { static int p_get_pressed_color(UINode* node) { return lua::pushcolor_arr(state->getLua(), node->getPressedColor()); } +static int p_get_tooltip(UINode* node) { + return state->pushstring(util::wstr2str_utf8(node->getTooltip())); +} +static int p_get_tooltip_delay(UINode* node) { + return state->pushnumber(node->getTooltipDelay()); +} static int p_get_pos(UINode* node) { return lua::pushvec2_arr(state->getLua(), node->getPos()); } +static int p_get_wpos(UINode* node) { + return lua::pushvec2_arr(state->getLua(), node->calcPos()); +} static int p_get_size(UINode* node) { return lua::pushvec2_arr(state->getLua(), node->getSize()); } @@ -272,16 +323,16 @@ static int p_is_enabled(UINode* node) { return state->pushboolean(node->isEnabled()); } static int p_move_into(UINode*) { - return state->pushcfunction(l_uinode_move_into); + return state->pushcfunction(l_move_into); } static int p_get_focused(UINode* node) { return state->pushboolean(node->isFocused()); } static int l_gui_getattr(lua_State* L) { - auto docname = lua_tostring(L, 1); - auto element = lua_tostring(L, 2); - auto attr = lua_tostring(L, 3); + auto docname = state->requireString(1); + auto element = state->requireString(2); + auto attr = state->requireString(3); auto docnode = getDocumentNode(L, docname, element); auto node = docnode.node; @@ -289,19 +340,25 @@ static int l_gui_getattr(lua_State* L) { {"color", p_get_color}, {"hoverColor", p_get_hover_color}, {"pressedColor", p_get_pressed_color}, + {"tooltip", p_get_tooltip}, + {"tooltipDelay", p_get_tooltip_delay}, {"pos", p_get_pos}, + {"wpos", p_get_wpos}, {"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}, {"text", p_get_text}, {"editable", p_get_editable}, + {"src", p_get_src}, {"value", p_get_value}, {"min", p_get_min}, {"max", p_get_max}, @@ -332,9 +389,18 @@ static void p_set_hover_color(UINode* node, int idx) { static void p_set_pressed_color(UINode* node, int idx) { node->setPressedColor(state->tocolor(idx)); } +static void p_set_tooltip(UINode* node, int idx) { + node->setTooltip(util::str2wstr_utf8(state->requireString(idx))); +} +static void p_set_tooltip_delay(UINode* node, int idx) { + node->setTooltipDelay(state->tonumber(idx)); +} static void p_set_pos(UINode* node, int idx) { node->setPos(state->tovec2(idx)); } +static void p_set_wpos(UINode* node, int idx) { + node->setPos(state->tovec2(idx)-node->calcPos()); +} static void p_set_size(UINode* node, int idx) { node->setSize(state->tovec2(idx)); } @@ -349,21 +415,21 @@ static void p_set_enabled(UINode* node, int idx) { } static void p_set_placeholder(UINode* node, int idx) { if (auto box = dynamic_cast(node)) { - box->setPlaceholder(util::str2wstr_utf8(state->tostring(idx))); + box->setPlaceholder(util::str2wstr_utf8(state->requireString(idx))); } } static void p_set_text(UINode* node, int idx) { if (auto label = dynamic_cast(node)) { - label->setText(util::str2wstr_utf8(state->tostring(idx))); + label->setText(util::str2wstr_utf8(state->requireString(idx))); } else if (auto button = dynamic_cast(node)) { - button->setText(util::str2wstr_utf8(state->tostring(idx))); + button->setText(util::str2wstr_utf8(state->requireString(idx))); } else if (auto box = dynamic_cast(node)) { - box->setText(util::str2wstr_utf8(state->tostring(idx))); + box->setText(util::str2wstr_utf8(state->requireString(idx))); } } static void p_set_caret(UINode* node, int idx) { if (auto box = dynamic_cast(node)) { - box->setCaret(static_cast(state->tointeger(idx))); + box->setCaret(static_cast(state->tointeger(idx))); } } static void p_set_editable(UINode* node, int idx) { @@ -371,6 +437,11 @@ static void p_set_editable(UINode* node, int idx) { box->setEditable(state->toboolean(idx)); } } +static void p_set_src(UINode* node, int idx) { + if (auto image = dynamic_cast(node)) { + image->setTexture(state->requireString(idx)); + } +} static void p_set_value(UINode* node, int idx) { if (auto bar = dynamic_cast(node)) { bar->setValue(state->tonumber(idx)); @@ -410,7 +481,7 @@ static void p_set_checked(UINode* node, int idx) { } static void p_set_page(UINode* node, int idx) { if (auto menu = dynamic_cast(node)) { - menu->setPage(state->tostring(idx)); + menu->setPage(state->requireString(idx)); } } static void p_set_inventory(UINode* node, int idx) { @@ -432,9 +503,9 @@ static void p_set_focused(std::shared_ptr node, int idx) { } static int l_gui_setattr(lua_State* L) { - auto docname = lua_tostring(L, 1); - auto element = lua_tostring(L, 2); - auto attr = lua_tostring(L, 3); + auto docname = state->requireString(1); + auto element = state->requireString(2); + auto attr = state->requireString(3); auto docnode = getDocumentNode(L, docname, element); auto node = docnode.node; @@ -443,7 +514,10 @@ static int l_gui_setattr(lua_State* L) { {"color", p_set_color}, {"hoverColor", p_set_hover_color}, {"pressedColor", p_set_pressed_color}, + {"tooltip", p_set_tooltip}, + {"tooltipDelay", p_set_tooltip_delay}, {"pos", p_set_pos}, + {"wpos", p_set_wpos}, {"size", p_set_size}, {"interactive", p_set_interactive}, {"visible", p_set_visible}, @@ -451,6 +525,7 @@ static int l_gui_setattr(lua_State* L) { {"placeholder", p_set_placeholder}, {"text", p_set_text}, {"editable", p_set_editable}, + {"src", p_set_src}, {"caret", p_set_caret}, {"value", p_set_value}, {"min", p_set_min}, @@ -477,7 +552,7 @@ static int l_gui_setattr(lua_State* L) { } static int l_gui_get_env(lua_State* L) { - auto name = lua_tostring(L, 1); + auto name = state->requireString(1); auto doc = scripting::engine->getAssets()->getLayout(name); if (doc == nullptr) { throw std::runtime_error("document '"+std::string(name)+"' not found"); @@ -487,9 +562,9 @@ static int l_gui_get_env(lua_State* L) { } static int l_gui_str(lua_State* L) { - auto text = util::str2wstr_utf8(lua_tostring(L, 1)); + auto text = util::str2wstr_utf8(state->requireString(1)); if (!lua_isnoneornil(L, 2)) { - auto context = util::str2wstr_utf8(lua_tostring(L, 2)); + auto context = util::str2wstr_utf8(state->requireString(2)); lua_pushstring(L, util::wstr2str_utf8(langs::get(text, context)).c_str()); } else { lua_pushstring(L, util::wstr2str_utf8(langs::get(text)).c_str()); @@ -498,7 +573,7 @@ static int l_gui_str(lua_State* L) { } static int l_gui_reindex(lua_State* L) { - auto name = lua_tostring(L, 1); + auto name = state->requireString(1); auto doc = scripting::engine->getAssets()->getLayout(name); if (doc == nullptr) { throw std::runtime_error("document '"+std::string(name)+"' not found"); @@ -530,7 +605,7 @@ const luaL_Reg guilib [] = { {"setattr", lua_wrap_errors}, {"get_env", lua_wrap_errors}, {"str", lua_wrap_errors}, - {"reindex", lua_wrap_errors}, {"get_locales_info", lua_wrap_errors}, + {"__reindex", lua_wrap_errors}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/libhud.cpp b/src/logic/scripting/lua/libhud.cpp index b7c023f5..93d3505b 100644 --- a/src/logic/scripting/lua/libhud.cpp +++ b/src/logic/scripting/lua/libhud.cpp @@ -40,14 +40,14 @@ static int l_hud_close_inventory(lua_State*) { } static int l_hud_open_block(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + auto x = lua_tointeger(L, 1); + auto y = lua_tointeger(L, 2); + auto z = lua_tointeger(L, 3); bool playerInventory = !lua_toboolean(L, 4); voxel* vox = scripting::level->chunks->get(x, y, z); if (vox == nullptr) { - throw std::runtime_error("block does not exists at "+ + throw std::runtime_error("block does not exists at " + std::to_string(x) + " " + std::to_string(y) + " " + std::to_string(z) ); } diff --git a/src/logic/scripting/lua/libinput.cpp b/src/logic/scripting/lua/libinput.cpp index 7f3fbc20..309cc26b 100644 --- a/src/logic/scripting/lua/libinput.cpp +++ b/src/logic/scripting/lua/libinput.cpp @@ -20,21 +20,28 @@ namespace scripting { using namespace scripting; static int l_keycode(lua_State* L) { - const char* name = lua_tostring(L, 1); + const char* name = state->requireString(1); lua_pushinteger(L, static_cast(input_util::keycode_from(name))); return 1; } -static int l_add_callback(lua_State* L) { - auto bindname = lua_tostring(L, 1); +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/libinventory.cpp b/src/logic/scripting/lua/libinventory.cpp index 067c7943..f6d164e1 100644 --- a/src/logic/scripting/lua/libinventory.cpp +++ b/src/logic/scripting/lua/libinventory.cpp @@ -1,6 +1,8 @@ #include "lua_commons.hpp" + #include "api_lua.hpp" #include "lua_util.hpp" + #include "../scripting.hpp" #include "../../../content/Content.hpp" #include "../../../world/Level.hpp" @@ -38,8 +40,8 @@ static void validate_slotid(int slotid, Inventory* inv) { } static int l_inventory_get(lua_State* L) { - lua::luaint invid = lua_tointeger(L, 1); - lua::luaint slotid = lua_tointeger(L, 2); + lua_Integer invid = lua_tointeger(L, 1); + lua_Integer slotid = lua_tointeger(L, 2); auto inv = get_inventory(invid); validate_slotid(slotid, inv.get()); const ItemStack& item = inv->getSlot(slotid); @@ -49,10 +51,10 @@ static int l_inventory_get(lua_State* L) { } static int l_inventory_set(lua_State* L) { - lua::luaint invid = lua_tointeger(L, 1); - lua::luaint slotid = lua_tointeger(L, 2); - lua::luaint itemid = lua_tointeger(L, 3); - lua::luaint count = lua_tointeger(L, 4); + lua_Integer invid = lua_tointeger(L, 1); + lua_Integer slotid = lua_tointeger(L, 2); + lua_Integer itemid = lua_tointeger(L, 3); + lua_Integer count = lua_tointeger(L, 4); validate_itemid(itemid); auto inv = get_inventory(invid); @@ -64,16 +66,16 @@ static int l_inventory_set(lua_State* L) { } static int l_inventory_size(lua_State* L) { - lua::luaint invid = lua_tointeger(L, 1); + lua_Integer invid = lua_tointeger(L, 1); auto inv = get_inventory(invid); lua_pushinteger(L, inv->size()); return 1; } static int l_inventory_add(lua_State* L) { - lua::luaint invid = lua_tointeger(L, 1); - lua::luaint itemid = lua_tointeger(L, 2); - lua::luaint count = lua_tointeger(L, 3); + lua_Integer invid = lua_tointeger(L, 1); + lua_Integer itemid = lua_tointeger(L, 2); + lua_Integer count = lua_tointeger(L, 3); validate_itemid(itemid); auto inv = get_inventory(invid); @@ -84,33 +86,33 @@ static int l_inventory_add(lua_State* L) { } static int l_inventory_get_block(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); int64_t id = scripting::blocks->createBlockInventory(x, y, z); lua_pushinteger(L, id); return 1; } static int l_inventory_bind_block(lua_State* L) { - lua::luaint id = lua_tointeger(L, 1); - lua::luaint x = lua_tointeger(L, 2); - lua::luaint y = lua_tointeger(L, 3); - lua::luaint z = lua_tointeger(L, 4); + lua_Integer id = lua_tointeger(L, 1); + lua_Integer x = lua_tointeger(L, 2); + lua_Integer y = lua_tointeger(L, 3); + lua_Integer z = lua_tointeger(L, 4); scripting::blocks->bindInventory(id, x, y, z); return 0; } static int l_inventory_unbind_block(lua_State* L) { - lua::luaint x = lua_tointeger(L, 1); - lua::luaint y = lua_tointeger(L, 2); - lua::luaint z = lua_tointeger(L, 3); + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); scripting::blocks->unbindInventory(x, y, z); return 0; } static int l_inventory_clone(lua_State* L) { - lua::luaint id = lua_tointeger(L, 1); + lua_Integer id = lua_tointeger(L, 1); auto clone = scripting::level->inventories->clone(id); if (clone == nullptr) { lua_pushinteger(L, 0); @@ -121,13 +123,13 @@ static int l_inventory_clone(lua_State* L) { } static int l_inventory_move(lua_State* L) { - lua::luaint invAid = lua_tointeger(L, 1); - lua::luaint slotAid = lua_tointeger(L, 2); + lua_Integer invAid = lua_tointeger(L, 1); + lua_Integer slotAid = lua_tointeger(L, 2); auto invA = get_inventory(invAid, 1); validate_slotid(slotAid, invA.get()); - lua::luaint invBid = lua_tointeger(L, 3); - lua::luaint slotBid = lua_isnil(L, 4) ? -1 : lua_tointeger(L, 4); + lua_Integer invBid = lua_tointeger(L, 3); + lua_Integer slotBid = lua_isnil(L, 4) ? -1 : lua_tointeger(L, 4); auto invB = get_inventory(invBid, 3); auto& slot = invA->getSlot(slotAid); if (slotBid == -1) { diff --git a/src/logic/scripting/lua/libitem.cpp b/src/logic/scripting/lua/libitem.cpp index 62760099..c8d6c11a 100644 --- a/src/logic/scripting/lua/libitem.cpp +++ b/src/logic/scripting/lua/libitem.cpp @@ -1,13 +1,20 @@ #include "lua_commons.hpp" #include "api_lua.hpp" +#include "LuaState.hpp" #include "../scripting.hpp" #include "../../../content/Content.hpp" #include "../../../items/ItemDef.hpp" +namespace scripting { + extern lua::LuaState* state; +} + +using namespace scripting; + static int l_item_name(lua_State* L) { auto indices = scripting::content->getIndices(); - lua::luaint id = lua_tointeger(L, 1); - if (id < 0 || size_t(id) >= indices->countItemDefs()) { + lua_Number id = lua_tointeger(L, 1); + if (static_cast(id) >= indices->countItemDefs()) { return 0; } auto def = indices->getItemDef(id); @@ -16,14 +23,14 @@ static int l_item_name(lua_State* L) { } static int l_item_index(lua_State* L) { - auto name = lua_tostring(L, 1); + auto name = scripting::state->requireString(1); lua_pushinteger(L, scripting::content->requireItem(name).rt.id); return 1; } static int l_item_stack_size(lua_State* L) { auto indices = scripting::content->getIndices(); - lua::luaint id = lua_tointeger(L, 1); + lua_Integer id = lua_tointeger(L, 1); if (id < 0 || size_t(id) >= indices->countItemDefs()) { return 0; } diff --git a/src/logic/scripting/lua/libjson.cpp b/src/logic/scripting/lua/libjson.cpp index 2c72a75e..7d3be119 100644 --- a/src/logic/scripting/lua/libjson.cpp +++ b/src/logic/scripting/lua/libjson.cpp @@ -23,17 +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); - auto value = std::make_unique( - dynamic::Map_sptr(element.release()) - ); - scripting::state->pushvalue(*value); + 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 f5f168106..045aae37 100644 --- a/src/logic/scripting/lua/libplayer.cpp +++ b/src/logic/scripting/lua/libplayer.cpp @@ -9,10 +9,15 @@ #include +inline std::shared_ptr get_player(lua_State* L, int idx) { + return scripting::level->getObject(lua_tointeger(L, idx)); +} + static int l_player_get_pos(lua_State* L) { - int playerid = lua_tointeger(L, 1); - auto player = scripting::level->getObject(playerid); - if (!player) return 0; + auto player = get_player(L, 1); + if (!player) { + return 0; + } glm::vec3 pos = player->hitbox->position; lua_pushnumber(L, pos.x); lua_pushnumber(L, pos.y); @@ -21,19 +26,22 @@ static int l_player_get_pos(lua_State* L) { } static int l_player_set_pos(lua_State* L) { - int playerid = lua_tointeger(L, 1); - lua::luanumber x = lua_tonumber(L, 2); - lua::luanumber y = lua_tonumber(L, 3); - lua::luanumber z = lua_tonumber(L, 4); - auto player = scripting::level->getObject(playerid); - if (player) player->hitbox->position = glm::vec3(x, y, z); + auto player = get_player(L, 1); + if (!player) { + return 0; + } + auto x = lua_tonumber(L, 2); + auto y = lua_tonumber(L, 3); + auto z = lua_tonumber(L, 4); + player->hitbox->position = glm::vec3(x, y, z); return 0; } static int l_player_get_vel(lua_State* L) { - int playerid = lua_tointeger(L, 1); - auto player = scripting::level->getObject(playerid); - if (!player) return 0; + auto player = get_player(L, 1); + if (!player) { + return 0; + } glm::vec3 vel = player->hitbox->velocity; lua_pushnumber(L, vel.x); lua_pushnumber(L, vel.y); @@ -42,46 +50,102 @@ static int l_player_get_vel(lua_State* L) { } static int l_player_set_vel(lua_State* L) { - int playerid = lua_tointeger(L, 1); - lua::luanumber x = lua_tonumber(L, 2); - lua::luanumber y = lua_tonumber(L, 3); - lua::luanumber z = lua_tonumber(L, 4); - auto player = scripting::level->getObject(playerid); - if (player) player->hitbox->velocity = glm::vec3(x, y, z); + auto player = get_player(L, 1); + if (!player) { + return 0; + } + auto x = lua_tonumber(L, 2); + auto y = lua_tonumber(L, 3); + auto z = lua_tonumber(L, 4); + player->hitbox->velocity = glm::vec3(x, y, z); return 0; } static int l_player_get_rot(lua_State* L) { - int playerid = lua_tointeger(L, 1); - auto player = scripting::level->getObject(playerid); - if (!player) return 0; - glm::vec2 rot = player->cam; + auto player = get_player(L, 1); + if (!player) { + return 0; + } + const glm::vec3& rot = player->cam; lua_pushnumber(L, rot.x); lua_pushnumber(L, rot.y); - return 2; + lua_pushnumber(L, rot.z); + return 3; } static int l_player_set_rot(lua_State* L) { - int playerid = lua_tointeger(L, 1); - auto player = scripting::level->getObject(playerid); - if (!player) return 0; - lua::luanumber x = lua_tonumber(L, 2); - lua::luanumber y = lua_tonumber(L, 3); - glm::vec2& cam = player->cam; + auto player = get_player(L, 1); + if (!player) { + return 0; + } + glm::vec3& cam = player->cam; + + lua_Number x = lua_tonumber(L, 2); + lua_Number y = lua_tonumber(L, 3); + lua_Number z = cam.z; + if (lua_isnumber(L, 4)) { + z = lua_tonumber(L, 4); + } cam.x = x; cam.y = y; + cam.z = z; return 0; } static int l_player_get_inv(lua_State* L) { - int playerid = lua_tointeger(L, 1); - auto player = scripting::level->getObject(playerid); - if (!player) return 0; + auto player = get_player(L, 1); + if (!player) { + return 0; + } lua_pushinteger(L, player->getInventory()->getId()); lua_pushinteger(L, player->getChosenSlot()); return 2; } +static int l_player_is_flight(lua_State* L) { + if (auto player = get_player(L, 1)) { + lua_pushboolean(L, player->isFlight()); + return 1; + } + return 0; +} + +static int l_player_set_flight(lua_State* L) { + if (auto player = get_player(L, 1)) { + player->setFlight(lua_toboolean(L, 2)); + } + return 0; +} + +static int l_player_is_noclip(lua_State* L) { + if (auto player = get_player(L, 1)) { + lua_pushboolean(L, player->isNoclip()); + return 1; + } + return 0; +} + +static int l_player_set_noclip(lua_State* L) { + if (auto player = get_player(L, 1)) { + player->setNoclip(lua_toboolean(L, 2)); + } + 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}, @@ -90,5 +154,10 @@ const luaL_Reg playerlib [] = { {"get_rot", lua_wrap_errors}, {"set_rot", lua_wrap_errors}, {"get_inventory", lua_wrap_errors}, + {"is_flight", lua_wrap_errors}, + {"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 new file mode 100644 index 00000000..86411d42 --- /dev/null +++ b/src/logic/scripting/lua/libtoml.cpp @@ -0,0 +1,37 @@ +#include "api_lua.hpp" +#include "lua_commons.hpp" +#include "LuaState.hpp" + +#include "../../../coders/toml.hpp" +#include "../../../data/dynamic.hpp" + +namespace scripting { + extern lua::LuaState* state; +} +using namespace scripting; + +static int l_toml_stringify(lua_State* L) { + auto value = state->tovalue(1); + + if (auto mapptr = std::get_if(&value)) { + auto string = toml::stringify(**mapptr); + lua_pushstring(L, string.c_str()); + return 1; + } else { + throw std::runtime_error("table expected"); + } +} + +static int l_toml_parse(lua_State*) { + auto string = state->requireString(1); + auto element = toml::parse("", string); + auto value = std::make_unique(element); + state->pushvalue(*value); + return 1; +} + +const luaL_Reg tomllib [] = { + {"tostring", lua_wrap_errors}, + {"parse", lua_wrap_errors}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_commons.hpp b/src/logic/scripting/lua/lua_commons.hpp index 38d17ebc..f51b7aac 100644 --- a/src/logic/scripting/lua/lua_commons.hpp +++ b/src/logic/scripting/lua/lua_commons.hpp @@ -7,14 +7,14 @@ #else #include #endif + +#ifndef LUAJIT_VERSION +#error LuaJIT required +#endif + #include #include -namespace lua { - using luaint = lua_Integer; - using luanumber = lua_Number; -} - template int lua_wrap_errors(lua_State *L) { int result = 0; try { diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 4d9e37bf..0c4de9f3 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -14,7 +14,7 @@ #include namespace lua { - inline int pushivec3(lua_State* L, luaint x, luaint y, luaint z) { + inline int pushivec3(lua_State* L, lua_Integer x, lua_Integer y, lua_Integer z) { lua_pushinteger(L, x); lua_pushinteger(L, y); lua_pushinteger(L, z); @@ -100,9 +100,9 @@ namespace lua { throw std::runtime_error("value must be an array of two numbers"); } lua_rawgeti(L, -1, 1); - lua::luanumber x = lua_tonumber(L, -1); lua_pop(L, 1); + lua_Number x = lua_tonumber(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); - lua::luanumber y = lua_tonumber(L, -1); lua_pop(L, 1); + lua_Number y = lua_tonumber(L, -1); lua_pop(L, 1); lua_pop(L, 1); return glm::vec2(x, y); } @@ -113,13 +113,13 @@ namespace lua { throw std::runtime_error("RGBA array required"); } lua_rawgeti(L, -1, 1); - lua::luanumber r = lua_tonumber(L, -1); lua_pop(L, 1); + lua_Number r = lua_tonumber(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); - lua::luanumber g = lua_tonumber(L, -1); lua_pop(L, 1); + lua_Number g = lua_tonumber(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 3); - lua::luanumber b = lua_tonumber(L, -1); lua_pop(L, 1); + lua_Number b = lua_tonumber(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 4); - lua::luanumber a = lua_tonumber(L, -1); lua_pop(L, 1); + lua_Number a = lua_tonumber(L, -1); lua_pop(L, 1); lua_pop(L, 1); return glm::vec4(r/255, g/255, b/255, a/255); } diff --git a/src/logic/scripting/scripting_functional.cpp b/src/logic/scripting/scripting_functional.cpp index 7597ce66..644b2b95 100644 --- a/src/logic/scripting/scripting_functional.cpp +++ b/src/logic/scripting/scripting_functional.cpp @@ -137,7 +137,8 @@ doublesupplier scripting::create_number_supplier( if (state->isfunction(-1)) { state->callNoThrow(0); } - lua::luanumber x = state->tonumber(-1); state->pop(); + auto x = state->tonumber(-1); + state->pop(); return x; } return 0.0; @@ -169,8 +170,8 @@ vec2supplier scripting::create_vec2_supplier( if (state->isfunction(-1)) { state->callNoThrow(0); } - lua::luanumber y = state->tonumber(-1); state->pop(); - lua::luanumber x = state->tonumber(-1); state->pop(); + auto y = state->tonumber(-1); state->pop(); + auto x = state->tonumber(-1); state->pop(); return glm::vec2(x, y); } return glm::vec2(0, 0); diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index edd8acec..c441eff0 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -35,9 +35,10 @@ Player::~Player() { } void Player::updateInput( - Level* level, - PlayerInput& input, - float delta) { + Level* level, + PlayerInput& input, + float delta +) { bool crouch = input.shift && hitbox->grounded && !input.sprint; float speed = this->speed; if (flight){ @@ -76,9 +77,9 @@ void Player::updateInput( hitbox->velocity.z += dir.z * speed * delta * 9; } - float vel = std::max(glm::length(hitbox->velocity * 0.25f), 1.0f); - int substeps = int(delta * vel * 1000); - substeps = std::min(100, std::max(1, substeps)); + float vel = glm::length(hitbox->velocity); + int substeps = int(delta * vel * 20); + substeps = std::min(100, std::max(2, substeps)); level->physics->step( level->chunks.get(), hitbox.get(), @@ -166,6 +167,22 @@ float Player::getSpeed() const { return speed; } +bool Player::isFlight() const { + return flight; +} + +void Player::setFlight(bool flag) { + this->flight = flag; +} + +bool Player::isNoclip() const { + return noclip; +} + +void Player::setNoclip(bool flag) { + this->noclip = flag; +} + std::shared_ptr Player::getInventory() const { return inventory; } @@ -189,6 +206,7 @@ std::unique_ptr Player::serialize() const { auto& rotarr = root->putList("rotation"); rotarr.put(cam.x); rotarr.put(cam.y); + rotarr.put(cam.z); auto& sparr = root->putList("spawnpoint"); sparr.put(spawnpoint.x); @@ -213,6 +231,9 @@ void Player::deserialize(dynamic::Map *src) { auto rotarr = src->list("rotation"); cam.x = rotarr->num(0); cam.y = rotarr->num(1); + if (rotarr->size() > 2) { + cam.z = rotarr->num(2); + } if (src->has("spawnpoint")) { auto sparr = src->list("spawnpoint"); diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index 18b9cb13..0afeda5c 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -1,9 +1,9 @@ #ifndef SRC_OBJECTS_PLAYER_HPP_ #define SRC_OBJECTS_PLAYER_HPP_ +#include "../settings.hpp" #include "../data/dynamic.hpp" #include "../voxels/voxel.hpp" -#include "../settings.hpp" #include "../interfaces/Serializable.hpp" #include "../interfaces/Object.hpp" @@ -14,23 +14,22 @@ class Camera; class Hitbox; class Inventory; class ContentLUT; -class PhysicsSolver; -class Chunks; class Level; +struct EngineSettings; struct PlayerInput { - bool zoom; - bool cameraMode; - bool moveForward; - bool moveBack; - bool moveRight; - bool moveLeft; - bool sprint; - bool shift; - bool cheat; - bool jump; - bool noclip; - bool flight; + bool zoom : 1; + bool cameraMode : 1; + bool moveForward : 1; + bool moveBack : 1; + bool moveRight : 1; + bool moveLeft : 1; + bool sprint : 1; + bool shift : 1; + bool cheat : 1; + bool jump : 1; + bool noclip : 1; + bool flight : 1; }; class Player : public Object, public Serializable { @@ -38,16 +37,16 @@ class Player : public Object, public Serializable { int chosenSlot; glm::vec3 spawnpoint {}; std::shared_ptr inventory; + bool flight = false; + bool noclip = false; public: std::shared_ptr camera, spCamera, tpCamera; std::shared_ptr currentCamera; std::unique_ptr hitbox; - bool flight = false; - bool noclip = false; bool debug = false; - voxel selectedVoxel {0, 0}; - - glm::vec2 cam = {}; + voxel selectedVoxel {0, {}}; + glm::vec3 cam {}; + glm::ivec3 selectedBlockPosition {}; Player(glm::vec3 position, float speed, std::shared_ptr inv); ~Player(); @@ -61,6 +60,12 @@ public: int getChosenSlot() const; float getSpeed() const; + + bool isFlight() const; + void setFlight(bool flag); + + bool isNoclip() const; + void setNoclip(bool flag); std::shared_ptr getInventory() const; @@ -77,4 +82,4 @@ public: } }; -#endif /* SRC_OBJECTS_PLAYER_HPP_ */ +#endif // SRC_OBJECTS_PLAYER_HPP_ diff --git a/src/physics/Hitbox.cpp b/src/physics/Hitbox.cpp index 4e6c5ba3..df94d9f6 100644 --- a/src/physics/Hitbox.cpp +++ b/src/physics/Hitbox.cpp @@ -1,8 +1,8 @@ #include "Hitbox.hpp" Hitbox::Hitbox(glm::vec3 position, glm::vec3 halfsize) - : position(position), - halfsize(halfsize), - velocity(0.0f,0.0f,0.0f), - linear_damping(0.1f) { -} + : position(position), + halfsize(halfsize), + velocity(0.0f,0.0f,0.0f), + linear_damping(0.1f) +{} diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 0334e807..a32b0ada 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -14,4 +14,4 @@ public: Hitbox(glm::vec3 position, glm::vec3 halfsize); }; -#endif /* PHYSICS_HITBOX_HPP_ */ +#endif // PHYSICS_HITBOX_HPP_ diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 1c0f9cf1..4370be30 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -6,32 +6,34 @@ #include "../voxels/Chunks.hpp" #include "../voxels/voxel.hpp" -const double E = 0.03; -const double MAX_FIX = 0.1; +const float E = 0.03f; +const float MAX_FIX = 0.1f; PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) { } void PhysicsSolver::step( - Chunks* chunks, - Hitbox* hitbox, - float delta, - uint substeps, - bool shifting, - float gravityScale, - bool collisions) -{ - float dt = delta / float(substeps); + Chunks* chunks, + Hitbox* hitbox, + float delta, + uint substeps, + bool shifting, + float gravityScale, + bool collisions +) { + float dt = delta / static_cast(substeps); float linear_damping = hitbox->linear_damping; float s = 2.0f/BLOCK_AABB_GRID; + + const glm::vec3& half = hitbox->halfsize; + glm::vec3& pos = hitbox->position; + glm::vec3& vel = hitbox->velocity; bool prevGrounded = hitbox->grounded; hitbox->grounded = false; for (uint i = 0; i < substeps; i++) { - glm::vec3& pos = hitbox->position; - glm::vec3& half = hitbox->halfsize; - glm::vec3& vel = hitbox->velocity; float px = pos.x; + float py = pos.y; float pz = pos.z; vel += gravity * dt * gravityScale; @@ -41,7 +43,11 @@ void PhysicsSolver::step( } vel.x *= glm::max(0.0f, 1.0f - dt * linear_damping); vel.z *= glm::max(0.0f, 1.0f - dt * linear_damping); - pos += vel * dt; + + 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); @@ -103,7 +109,7 @@ void PhysicsSolver::colisionCalc( for (float z = (pos.z-half.z+E); z <= (pos.z+half.z-E); z+=s){ float x = (pos.x-half.x-E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.x *= 0.0f; + vel.x = 0.0f; float newx = floor(x) + aabb->max().x + half.x + E; if (glm::abs(newx-pos.x) <= MAX_FIX) { pos.x = newx; @@ -118,7 +124,7 @@ void PhysicsSolver::colisionCalc( for (float z = (pos.z-half.z+E); z <= (pos.z+half.z-E); z+=s){ float x = (pos.x+half.x+E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.x *= 0.0f; + vel.x = 0.0f; float newx = floor(x) - half.x + aabb->min().x - E; if (glm::abs(newx-pos.x) <= MAX_FIX) { pos.x = newx; @@ -134,7 +140,7 @@ void PhysicsSolver::colisionCalc( for (float x = (pos.x-half.x+E); x <= (pos.x+half.x-E); x+=s){ float z = (pos.z-half.z-E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.z *= 0.0f; + vel.z = 0.0f; float newz = floor(z) + aabb->max().z + half.z + E; if (glm::abs(newz-pos.z) <= MAX_FIX) { pos.z = newz; @@ -150,7 +156,7 @@ void PhysicsSolver::colisionCalc( for (float x = (pos.x-half.x+E); x <= (pos.x+half.x-E); x+=s){ float z = (pos.z+half.z+E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.z *= 0.0f; + vel.z = 0.0f; float newz = floor(z) - half.z + aabb->min().z - E; if (glm::abs(newz-pos.z) <= MAX_FIX) { pos.z = newz; @@ -166,7 +172,7 @@ void PhysicsSolver::colisionCalc( for (float z = (pos.z-half.z+E); z <= (pos.z+half.z-E); z+=s){ float y = (pos.y-half.y-E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.y *= 0.0f; + vel.y = 0.0f; float newy = floor(y) + aabb->max().y + half.y; if (glm::abs(newy-pos.y) <= MAX_FIX) { pos.y = newy; @@ -182,7 +188,7 @@ void PhysicsSolver::colisionCalc( for (float z = (pos.z-half.z+E); z <= (pos.z+half.z-E); z+=s){ float y = (pos.y-half.y+E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.y *= 0.0f; + vel.y = 0.0f; float newy = floor(y) + aabb->max().y + half.y; if (glm::abs(newy-pos.y) <= MAX_FIX+stepHeight) { pos.y = newy; @@ -197,7 +203,7 @@ void PhysicsSolver::colisionCalc( for (float z = (pos.z-half.z+E); z <= (pos.z+half.z-E); z+=s){ float y = (pos.y+half.y+E); if ((aabb = chunks->isObstacleAt(x,y,z))){ - vel.y *= 0.0f; + vel.y = 0.0f; float newy = floor(y) - half.y + aabb->min().y - E; if (glm::abs(newy-pos.y) <= MAX_FIX) { pos.y = newy; @@ -213,25 +219,23 @@ bool PhysicsSolver::isBlockInside(int x, int y, int z, Hitbox* hitbox) { const glm::vec3& pos = hitbox->position; const glm::vec3& half = hitbox->halfsize; return x >= floor(pos.x-half.x) && x <= floor(pos.x+half.x) && - z >= floor(pos.z-half.z) && z <= floor(pos.z+half.z) && - y >= floor(pos.y-half.y) && y <= floor(pos.y+half.y); + z >= floor(pos.z-half.z) && z <= floor(pos.z+half.z) && + 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->hitboxes; + ? 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.00001 && max.x > pos.x-half.x-x+0.00001 && - min.z < pos.z+half.z-z-0.00001 && max.z > pos.z-half.z-z+0.00001 && - min.y < pos.y+half.y-y-0.00001 && max.y > pos.y-half.y-y+0.00001) + 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 d1b91dbf..6e940f99 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -1,10 +1,12 @@ #ifndef PHYSICS_PHYSICSSOLVER_HPP_ #define PHYSICS_PHYSICSSOLVER_HPP_ -#include #include "../typedefs.hpp" -#include "../voxels/Block.hpp" +#include "../voxels/voxel.hpp" +#include + +class Block; class Hitbox; class Chunks; @@ -12,22 +14,25 @@ class PhysicsSolver { glm::vec3 gravity; public: PhysicsSolver(glm::vec3 gravity); - void step(Chunks* chunks, - Hitbox* hitbox, - float delta, - uint substeps, - bool shifting, - float gravityScale, - bool collisions); + void step( + Chunks* chunks, + Hitbox* hitbox, + float delta, + uint substeps, + bool shifting, + float gravityScale, + bool collisions + ); void colisionCalc( - Chunks* chunks, - Hitbox* hitbox, - glm::vec3& vel, - glm::vec3& pos, - const glm::vec3 half, - float stepHeight); + Chunks* chunks, + Hitbox* hitbox, + glm::vec3& vel, + glm::vec3& pos, + const glm::vec3 half, + 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_ */ +#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/util/stringutil.cpp b/src/util/stringutil.cpp index 9480af4c..804d9a99 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -443,3 +443,14 @@ std::string util::format_data_size(size_t size) { std::to_string(static_cast(round(remainder/1024.0f)))+ postfixes[group]; } + +std::pair util::split_at(std::string_view view, char c) { + size_t idx = view.find(c); + if (idx == std::string::npos) { + throw std::runtime_error(util::quote(std::string({c}))+" not found"); + } + return std::make_pair( + std::string(view.substr(0, idx)), + std::string(view.substr(idx+1)) + ); +} diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index 3270c92e..ffac1eab 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -57,6 +57,8 @@ namespace util { std::vector split(const std::wstring& str, char delimiter); std::string format_data_size(size_t size); + + std::pair split_at(std::string_view view, char c); } #endif // UTIL_STRINGUTIL_HPP_ diff --git a/src/voxel_engine.cpp b/src/voxel_engine.cpp index eac242c5..c6a2ec7e 100644 --- a/src/voxel_engine.cpp +++ b/src/voxel_engine.cpp @@ -1,4 +1,5 @@ #include "engine.hpp" +#include "settings.hpp" #include "files/settings_io.hpp" #include "files/engine_paths.hpp" #include "util/platform.hpp" 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/ChunksStorage.hpp b/src/voxels/ChunksStorage.hpp index 2f671ee7..ed9f6342 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/ChunksStorage.hpp @@ -14,19 +14,17 @@ class Level; class VoxelsVolume; class ChunksStorage { - Level* level; - std::unordered_map> chunksMap; + Level* level; + std::unordered_map> chunksMap; public: - ChunksStorage(Level* level); - ~ChunksStorage() = default; + ChunksStorage(Level* level); + ~ChunksStorage() = default; - std::shared_ptr get(int x, int z) const; - void store(std::shared_ptr chunk); - void remove(int x, int y); - void getVoxels(VoxelsVolume* volume, bool backlight=false) const; - std::shared_ptr create(int x, int z); - - light_t getLight(int x, int y, int z, ubyte channel) const; + std::shared_ptr get(int x, int z) const; + void store(std::shared_ptr chunk); + void remove(int x, int y); + void getVoxels(VoxelsVolume* volume, bool backlight=false) const; + std::shared_ptr create(int x, int z); }; 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/DefaultWorldGenerator.hpp b/src/voxels/DefaultWorldGenerator.hpp index 84daabb9..d42e2749 100644 --- a/src/voxels/DefaultWorldGenerator.hpp +++ b/src/voxels/DefaultWorldGenerator.hpp @@ -10,9 +10,9 @@ class Content; class DefaultWorldGenerator : WorldGenerator { public: - DefaultWorldGenerator(const Content* content) : WorldGenerator(content) {} + DefaultWorldGenerator(const Content* content) : WorldGenerator(content) {} - void generate(voxel* voxels, int x, int z, int seed); + void generate(voxel* voxels, int x, int z, int seed); }; -#endif /* VOXELS_DEFAULTWORLDGENERATOR_HPP_ */ +#endif // VOXELS_DEFAULTWORLDGENERATOR_HPP_ 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/FlatWorldGenerator.hpp b/src/voxels/FlatWorldGenerator.hpp index 67189119..c9a2d25f 100644 --- a/src/voxels/FlatWorldGenerator.hpp +++ b/src/voxels/FlatWorldGenerator.hpp @@ -10,9 +10,9 @@ class Content; class FlatWorldGenerator : WorldGenerator { public: - FlatWorldGenerator(const Content* content) : WorldGenerator(content) {} + FlatWorldGenerator(const Content* content) : WorldGenerator(content) {} - void generate(voxel* voxels, int x, int z, int seed); + void generate(voxel* voxels, int x, int z, int seed); }; -#endif /* VOXELS_FLATWORLDGENERATOR_HPP_ */ +#endif // VOXELS_FLATWORLDGENERATOR_HPP_ diff --git a/src/voxels/VoxelsVolume.cpp b/src/voxels/VoxelsVolume.cpp index 73bff62c..5da88add 100644 --- a/src/voxels/VoxelsVolume.cpp +++ b/src/voxels/VoxelsVolume.cpp @@ -1,30 +1,23 @@ #include "VoxelsVolume.hpp" -VoxelsVolume::VoxelsVolume(int x, int y, int z, int w, int h, int d) - : x(x), y(y), z(z), w(w), h(h), d(d) { - voxels = new voxel[w * h * d]; - for (int i = 0; i < w * h * d; i++) { - voxels[i].id = BLOCK_VOID; - } - lights = new light_t[w * h * d]; +VoxelsVolume::VoxelsVolume( + int x, int y, int z, int w, int h, int d +) : x(x), y(y), z(z), w(w), h(h), d(d), + voxels(std::make_unique(w * h * d)), + lights(std::make_unique(w * h * d)) +{ + for (int i = 0; i < w * h * d; i++) { + voxels[i].id = BLOCK_VOID; + } } -VoxelsVolume::VoxelsVolume(int w, int h, int d) - : x(0), y(0), z(0), w(w), h(h), d(d) { - voxels = new voxel[w * h * d]; - for (int i = 0; i < w * h * d; i++) { - voxels[i].id = BLOCK_VOID; - } - lights = new light_t[w * h * d]; -} +VoxelsVolume::VoxelsVolume(int w, int h, int d) : VoxelsVolume(0, 0, 0, w, h, d) +{} -VoxelsVolume::~VoxelsVolume() { - delete[] lights; - delete[] voxels; -} +VoxelsVolume::~VoxelsVolume() {} void VoxelsVolume::setPosition(int x, int y, int z) { - this->x = x; - this->y = y; - this->z = z; + this->x = x; + this->y = y; + this->z = z; } diff --git a/src/voxels/VoxelsVolume.hpp b/src/voxels/VoxelsVolume.hpp index 30406891..d6e6474a 100644 --- a/src/voxels/VoxelsVolume.hpp +++ b/src/voxels/VoxelsVolume.hpp @@ -6,62 +6,62 @@ #include "voxel.hpp" class VoxelsVolume { - int x, y, z; - int w, h, d; - voxel* voxels; - light_t* lights; + int x, y, z; + int w, h, d; + std::unique_ptr voxels; + std::unique_ptr lights; public: - VoxelsVolume(int w, int h, int d); - VoxelsVolume(int x, int y, int z, int w, int h, int d); - virtual ~VoxelsVolume(); + VoxelsVolume(int w, int h, int d); + VoxelsVolume(int x, int y, int z, int w, int h, int d); + virtual ~VoxelsVolume(); - void setPosition(int x, int y, int z); + void setPosition(int x, int y, int z); - int getX() const { - return x; - } + int getX() const { + return x; + } - int getY() const { - return y; - } + int getY() const { + return y; + } - int getZ() const { - return z; - } + int getZ() const { + return z; + } - int getW() const { - return w; - } + int getW() const { + return w; + } - int getH() const { - return h; - } + int getH() const { + return h; + } - int getD() const { - return d; - } + int getD() const { + return d; + } - voxel* getVoxels() const { - return voxels; - } + voxel* getVoxels() const { + return voxels.get(); + } - light_t* getLights() const { - return lights; - } + light_t* getLights() const { + return lights.get(); + } - inline blockid_t pickBlockId(int bx, int by, int bz) const { - if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { - return BLOCK_VOID; - } - return voxels[vox_index(bx - x, by - y, bz - z, w, d)].id; - } + inline blockid_t pickBlockId(int bx, int by, int bz) const { + if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { + return BLOCK_VOID; + } + return voxels[vox_index(bx - x, by - y, bz - z, w, d)].id; + } - inline light_t pickLight(int bx, int by, int bz) const { - if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { - return 0; - } - return lights[vox_index(bx - x, by - y, bz - z, w, d)]; - } + inline light_t pickLight(int bx, int by, int bz) const { + if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { + return 0; + } + return lights[vox_index(bx - x, by - y, bz - z, w, d)]; + } }; #endif // VOXELS_VOXELSVOLUME_HPP_ diff --git a/src/voxels/WorldGenerator.hpp b/src/voxels/WorldGenerator.hpp index 10b92da6..5b8839ac 100644 --- a/src/voxels/WorldGenerator.hpp +++ b/src/voxels/WorldGenerator.hpp @@ -26,4 +26,4 @@ public: virtual void generate(voxel* voxels, int x, int z, int seed) = 0; }; -#endif /* VOXELS_WORLDGENERATOR_HPP_ */ +#endif // VOXELS_WORLDGENERATOR_HPP_ diff --git a/src/voxels/voxel.hpp b/src/voxels/voxel.hpp index a39e6305..9cfaf698 100644 --- a/src/voxels/voxel.hpp +++ b/src/voxels/voxel.hpp @@ -3,29 +3,41 @@ #include "../typedefs.hpp" -const int BLOCK_DIR_NORTH = 0x0; -const int BLOCK_DIR_WEST = 0x1; -const int BLOCK_DIR_SOUTH = 0x2; -const int BLOCK_DIR_EAST = 0x3; -const int BLOCK_DIR_UP = 0x4; -const int BLOCK_DIR_DOWN = 0x5; +inline constexpr int BLOCK_DIR_NORTH = 0x0; +inline constexpr int BLOCK_DIR_WEST = 0x1; +inline constexpr int BLOCK_DIR_SOUTH = 0x2; +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 -const int BLOCK_ROT_MASK = 0b0000'0111; -// reserved bits -const 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_ */ +#endif // VOXELS_VOXEL_HPP_ diff --git a/src/window/Camera.cpp b/src/window/Camera.cpp index 0dc486b5..80b16af2 100644 --- a/src/window/Camera.cpp +++ b/src/window/Camera.cpp @@ -21,10 +21,9 @@ void Camera::updateVectors(){ } void Camera::rotate(float x, float y, float z){ - rotation = glm::rotate(rotation, z, glm::vec3(0,0,1)); rotation = glm::rotate(rotation, y, glm::vec3(0,1,0)); rotation = glm::rotate(rotation, x, glm::vec3(1,0,0)); - + rotation = glm::rotate(rotation, z, glm::vec3(0,0,1)); updateVectors(); } diff --git a/src/window/Events.cpp b/src/window/Events.cpp index 8ac783fd..1aab7881 100644 --- a/src/window/Events.cpp +++ b/src/window/Events.cpp @@ -1,6 +1,7 @@ #include "Events.hpp" #include "Window.hpp" #include "../debug/Logger.hpp" +#include "../util/stringutil.hpp" #include #include @@ -97,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)); } @@ -109,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()) { @@ -148,24 +161,28 @@ void Events::setPosition(float xpos, float ypos) { #include "../data/dynamic.hpp" #include "../coders/json.hpp" +#include "../coders/toml.hpp" 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; @@ -189,3 +206,28 @@ void Events::loadBindings(const std::string& filename, const std::string& source jentry->num("code", binding.code); } } + +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)) { + auto [prefix, codename] = util::split_at(*value, ':'); + inputtype type; + int code; + if (prefix == "key") { + type = inputtype::keyboard; + code = static_cast(input_util::keycode_from(codename)); + } else if (prefix == "mouse") { + type = inputtype::mouse; + code = static_cast(input_util::mousecode_from(codename)); + } else { + logger.error() << "unknown input type: " << prefix + << " (binding " << util::quote(entry.first) << ")"; + continue; + } + Events::bind(entry.first, type, code); + } else { + logger.error() << "invalid binding entry: " << entry.first; + } + } +} diff --git a/src/window/Events.hpp b/src/window/Events.hpp index 36544c2a..97405d2c 100644 --- a/src/window/Events.hpp +++ b/src/window/Events.hpp @@ -2,11 +2,11 @@ #define WINDOW_EVENTS_HPP_ #include "input.hpp" +#include "../typedefs.hpp" #include #include #include -#include "../typedefs.hpp" inline constexpr short KEYS_BUFFER_SIZE = 1036; @@ -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,6 +53,7 @@ public: static std::string writeBindings(); static void loadBindings(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 2d1d7c65..cfb06e84 100644 --- a/src/window/input.cpp +++ b/src/window/input.cpp @@ -15,6 +15,7 @@ static std::unordered_map keycodes { {"delete", GLFW_KEY_DELETE}, {"home", GLFW_KEY_HOME}, {"end", GLFW_KEY_END}, + {"tab", GLFW_KEY_TAB}, {"insert", GLFW_KEY_INSERT}, {"page-down", GLFW_KEY_PAGE_DOWN}, {"page-up", GLFW_KEY_PAGE_UP}, @@ -26,12 +27,38 @@ static std::unordered_map keycodes { {"right-alt", GLFW_KEY_RIGHT_ALT}, {"left-super", GLFW_KEY_LEFT_SUPER}, {"right-super", GLFW_KEY_RIGHT_SUPER}, + {"grave-accent", GLFW_KEY_GRAVE_ACCENT}, {"left", GLFW_KEY_LEFT}, {"right", GLFW_KEY_RIGHT}, {"down", GLFW_KEY_DOWN}, {"up", GLFW_KEY_UP}, }; +static std::unordered_map mousecodes { + {"left", GLFW_MOUSE_BUTTON_1}, + {"right", GLFW_MOUSE_BUTTON_2}, + {"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; @@ -50,10 +77,13 @@ 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::to_string(i)] = GLFW_KEY_A-'a'+i; + keycodes[std::string({i})] = GLFW_KEY_A-'a'+i; + } + for (const auto& entry : keycodes) { + keynames[entry.second] = entry.first; } } @@ -65,6 +95,14 @@ keycode input_util::keycode_from(const std::string& name) { return static_cast(found->second); } +mousecode input_util::mousecode_from(const std::string& name) { + const auto& found = mousecodes.find(name); + if (found == mousecodes.end()) { + return mousecode::UNKNOWN; + } + return static_cast(found->second); +} + std::string input_util::to_string(keycode code) { int icode_repr = static_cast(code); #ifdef _WIN32 @@ -126,9 +164,9 @@ std::string input_util::to_string(keycode code) { std::string input_util::to_string(mousecode code) { switch (code) { - case mousecode::BUTTON_1: return "LMB"; - case mousecode::BUTTON_2: return "RMB"; - case mousecode::BUTTON_3: return "MMB"; + case mousecode::BUTTON_1: return "LMB"; + case mousecode::BUTTON_2: return "RMB"; + case mousecode::BUTTON_3: return "MMB"; + default: return "unknown button"; } - return "unknown button"; } diff --git a/src/window/input.hpp b/src/window/input.hpp index 72aedb95..50e26446 100644 --- a/src/window/input.hpp +++ b/src/window/input.hpp @@ -103,6 +103,7 @@ enum class mousecode : int { BUTTON_1 = 0, // Left mouse button BUTTON_2 = 1, // Right mouse button BUTTON_3 = 2, // Middle mouse button + UNKNOWN = -1, }; inline mousecode MOUSECODES_ALL[] { @@ -115,10 +116,17 @@ namespace input_util { void initialize(); keycode keycode_from(const std::string& name); + mousecode mousecode_from(const std::string& name); + /// @return Key label by keycode 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.cpp b/src/world/Level.cpp index 36f75e30..1df81c74 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -1,6 +1,7 @@ #include "Level.hpp" #include "World.hpp" #include "LevelEvents.hpp" +#include "../settings.hpp" #include "../content/Content.hpp" #include "../lighting/Lighting.hpp" #include "../voxels/Chunk.hpp" diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 6b8f35c4..7b18f06f 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -1,7 +1,6 @@ #ifndef WORLD_LEVEL_HPP_ #define WORLD_LEVEL_HPP_ -#include "../settings.hpp" #include "../interfaces/Object.hpp" #include @@ -21,6 +20,7 @@ class LevelEvents; class Lighting; class PhysicsSolver; class ChunksStorage; +struct EngineSettings; /// @brief A level, contains chunks and objects class Level { diff --git a/src/world/World.cpp b/src/world/World.cpp index 6fb17b1e..380e2f48 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -2,6 +2,7 @@ #include "Level.hpp" +#include "../settings.hpp" #include "../content/Content.hpp" #include "../content/ContentLUT.hpp" #include "../debug/Logger.hpp" @@ -11,7 +12,6 @@ #include "../voxels/Chunk.hpp" #include "../voxels/Chunks.hpp" #include "../voxels/ChunksStorage.hpp" -#include "../window/Camera.hpp" #include "../world/WorldGenerators.hpp" #include @@ -57,11 +57,11 @@ void World::write(Level* level) { for (size_t i = 0; i < chunks->volume; i++) { auto chunk = chunks->chunks[i]; - if (chunk == nullptr || !chunk->isLighted()) + if (chunk == nullptr || !chunk->flags.lighted) continue; - bool lightsUnsaved = !chunk->isLoadedLights() && + bool lightsUnsaved = !chunk->flags.loadedLights && settings.debug.doWriteLights.get(); - if (!chunk->isUnsaved() && !lightsUnsaved) + if (!chunk->flags.unsaved && !lightsUnsaved) continue; regions.put(chunk.get()); } @@ -134,7 +134,6 @@ std::unique_ptr World::load( } } } - (void)world.release(); return level; } @@ -193,19 +192,20 @@ void World::deserialize(dynamic::Map* root) { if (generator == "") { generator = WorldGenerators::getDefaultGeneratorID(); } - auto verobj = root->map("version"); - if (verobj) { + if (auto verobj = root->map("version")) { int major=0, minor=-1; verobj->num("major", major); verobj->num("minor", minor); - std::cout << "world version: " << major << "." << minor << std::endl; + logger.info() << "world version: " << major << "." << minor; } - auto timeobj = root->map("time"); - if (timeobj) { + if (auto timeobj = root->map("time")) { timeobj->num("day-time", daytime); timeobj->num("day-time-speed", daytimeSpeed); timeobj->num("total-time", totalTime); } + if (auto weatherobj = root->map("weather")) { + weatherobj->num("fog", fog); + } nextInventoryId = root->get("next-inventory-id", 2); } @@ -225,6 +225,9 @@ std::unique_ptr World::serialize() const { timeobj.put("day-time-speed", daytimeSpeed); timeobj.put("total-time", totalTime); + auto& weatherobj = root->putMap("weather"); + weatherobj.put("fog", fog); + root->put("next-inventory-id", nextInventoryId); return root; } diff --git a/src/world/World.hpp b/src/world/World.hpp index 57d94956..e720473c 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -2,7 +2,6 @@ #define WORLD_WORLD_HPP_ #include "../typedefs.hpp" -#include "../settings.hpp" #include "../util/timeutil.hpp" #include "../data/dynamic.hpp" #include "../interfaces/Serializable.hpp" @@ -17,6 +16,7 @@ class Content; class WorldFiles; class Level; class ContentLUT; +struct EngineSettings; namespace fs = std::filesystem; @@ -49,6 +49,9 @@ public: /// @brief total time passed in the world (not depending on daytimeSpeed) double totalTime = 0.0; + /// @brief will be replaced with weather in future + float fog = 0.0f; + World( std::string name, std::string generator,