This commit is contained in:
@clasher113 2024-06-07 00:51:18 +03:00
commit eb59c3ae2f
160 changed files with 2935 additions and 1724 deletions

51
.github/workflows/macos.yml vendored Normal file
View File

@ -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

48
.github/workflows/windows.yml vendored Normal file
View File

@ -0,0 +1,48 @@
name: Windows Build
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build-windows:
strategy:
matrix:
include:
- os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
- name: Set up vcpkg
run: |
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
cd ..
- name: Configure and build project with CMake and vcpkg
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON ..
Remove-Item -Path CMakeFiles -Recurse -Force
cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON ..
cmake --build . --config Release
- name: Package for Windows
run: |
mkdir packaged
cp -r build/* packaged/
working-directory: ${{ github.workspace }}
- uses: actions/upload-artifact@v2
with:
name: Windows-Build
path: 'packaged/Release/*'

View File

@ -50,6 +50,9 @@ else()
-Wformat-nonliteral -Wcast-align -Wformat-nonliteral -Wcast-align
-Wpointer-arith -Wundef -Wpointer-arith -Wundef
-Wwrite-strings -Wno-unused-parameter) -Wwrite-strings -Wno-unused-parameter)
if (CMAKE_BUILD_TYPE MATCHES "Debug")
target_compile_options(${PROJECT_NAME} PRIVATE -Og)
endif()
endif() endif()
if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32)
@ -98,6 +101,18 @@ if (WIN32)
set(VORBISLIB vorbis vorbisfile) # not tested set(VORBISLIB vorbis vorbisfile) # not tested
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libs/glfw) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libs/glfw)
endif() 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() else()
find_package(PkgConfig) find_package(PkgConfig)
pkg_check_modules(LUAJIT REQUIRED luajit) pkg_check_modules(LUAJIT REQUIRED luajit)

View File

@ -52,10 +52,10 @@ player.set_pos(playerid: int, x: number, y: number, z: number)
Set player position Set player position
```python ```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 ```python
player.set_rot(playerid: int, x: number, y: number, z: number) 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) 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 ## *world* library
```python ```python

View File

@ -26,14 +26,31 @@
# Общие атрибуты элементов # Общие атрибуты элементов
- `enabled` - при значении false блокирует элемент, в отличие от interactive, обозначая это состояние визуально.
- `id` - идентификатор элемента. Тип: строка. - `id` - идентификатор элемента. Тип: строка.
- `pos` - позиция элемента. Тип: 2D вектор. - `pos` - позиция элемента. Тип: 2D вектор.
- `size` - размер элемента. Тип: 2D вектор. - `size` - размер элемента. Тип: 2D вектор.
- `context` - указывает контекст перевода для `@`-строк.
- `color` - цвет элемента. Тип: RGBA цвет. - `color` - цвет элемента. Тип: RGBA цвет.
- `hover-color` - цвет элемента при наведении курсора. Тип: RGBA цвет.
- `pressed-color` - цвет элемента при нажатии на элемент. Тип: RGBA цвет.
- `margin` - внешний отступ элемента. Тип: 4D вектор. - `margin` - внешний отступ элемента. Тип: 4D вектор.
Порядок: `"left,top,right,bottom"` Порядок: `"left,top,right,bottom"`
- `visible` - видимость элемента. Тип: логический ("true"/"false"). - `visible` - видимость элемента. Тип: логический ("true"/"false").
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open. - `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). Тип: число - `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число
- `orientation` - ориентация панели: horizontal/vertical.
# Основные элементы # Основные элементы
## Кнопка `button` ## Кнопка - *button*
Внутренний текст - текст кнопки. Внутренний текст - текст кнопки.
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка. - `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` - `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
# Текстовое поле `textbox` ## Текстовое поле - *textbox*
Внутренний текст - изначально введенный текст Внутренний текст - изначально введенный текст
- `placeholder` - текст подстановки (используется текстовое поле пусто) - `placeholder` - текст подстановки (используется текстовое поле пусто)
- `supplier` - поставщик текста (вызывается каждый кадр)
- `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода - `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода
- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта.
## Ползунок `trackbar` - `multiline` - разрешает отображение многострочного текста.
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
- `editable`- определяет возможность редактирования текста.
- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет.
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
- `onup` - lua функция вызываемая при нажатии стрелки вверх.
- `ondown` - lua функция вызываемая при нажатии стрелки вниз.
## Ползунок - *trackbar*
- `min` - минимальное значение. Тип: число. По-умолчанию: 0 - `min` - минимальное значение. Тип: число. По-умолчанию: 0
- `max` - максимальное значение. Тип: число. По-умолчанию: 1 - `max` - максимальное значение. Тип: число. По-умолчанию: 1
- `value` - изначальное значение. Тип: число. По-умолчанию: 0 - `value` - изначальное значение. Тип: число. По-умолчанию: 0
- `step` - размер деления ползунка. Тип: число. По-умолчанию: 1 - `step` - размер деления ползунка. Тип: число. По-умолчанию: 1
- `track-width` - ширина указателя (в делениях). Тип: число. По-умолчанию: 1 - `track-width` - ширина указателя (в пикселях). Тип: число. По-умолчанию: 12
- `track-color` - цвет указателя при наведении курсора. Тип: RGBA цвет.
- `consumer` - lua функция-приемник установленного значения - `consumer` - lua функция-приемник установленного значения
- `supplier` - lua функция-поставщик значения - `supplier` - lua функция-поставщик значения
# Элементы инвентаря # Элементы инвентаря
## Инвентарь `inventory` ## Инвентарь - *inventory*
Элемент является контейнером. На данный момент не имеет специфических атрибутов. Элемент является контейнером. На данный момент не имеет специфических атрибутов.
> [!WARNING] > [!WARNING]
> Расположение инвентарей управляется движком и не может быть изменено свойствами pos, margin и т.д. > Расположение инвентарей управляется движком и не может быть изменено свойствами pos, margin и т.д.
## Одиночный слот `slot` ## Одиночный слот - *slot*
Элемент должен находиться внутри `inventory` элемента, без посредников. Элемент должен находиться внутри `inventory` элемента, без посредников.
- `index` - индекс слота инвентаря. (Нумерация с 0) - `index` - индекс слота инвентаря. (Нумерация с 0)
@ -95,7 +134,7 @@
- `updatefunc` - lua событие вызываемое при изменении содержимого слота - `updatefunc` - lua событие вызываемое при изменении содержимого слота
- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота - `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота
## Решетка слотов `slots-grid` ## Сетка слотов - *slots-grid*
Элемент должен находиться внутри `inventory` элемента, без посредников. Элемент должен находиться внутри `inventory` элемента, без посредников.
- `start-index` - индекс первого слота - `start-index` - индекс первого слота

View File

@ -2,11 +2,7 @@
В качестве языка сценариев используется LuaJIT В качестве языка сценариев используется LuaJIT
## Функции, доступные в скриптах
```lua ```lua
load_script("контентпак:scripts/имя_скрипта.lua") -- загружает скрипт, если ещё не загружен
load_script("контентпак:scripts/имя_скрипта.lua", true) -- перезагружает скрипт
require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается) require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается)
``` ```
@ -46,10 +42,10 @@ player.set_pos(playerid: int, x: number, y: number, z: number)
Устанавливает x, y, z координаты игрока Устанавливает x, y, z координаты игрока
```python ```python
player.get_rot(playerid: int) -> number, number player.get_rot(playerid: int) -> number, number, number
``` ```
Возвращает x, y вращения камеры (в радианах) Возвращает x, y, z вращения камеры (в радианах)
```python ```python
player.set_rot(playerid: int, x: number, y: number, z: number) 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) Возвращает 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 ## Библиотека world
```python
world.get_list() -> массив таблиц {
name: str,
icon: str
}
```
Возвращает информацию о мирах: название и предпросмотр (автоматически загружаемая текстура).
```python ```python
world.get_day_time() -> number world.get_day_time() -> number
``` ```
@ -89,6 +114,12 @@ world.get_seed() -> int
Возвращает зерно мира. Возвращает зерно мира.
```python
world.exists() -> bool
```
Проверяет существование мира по имени.
## Библиотека pack ## Библиотека pack
```python ```python
@ -109,6 +140,38 @@ pack.get_installed() -> массив строк
Возращает id всех установленных в мире контент-паков Возращает 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 ## Библиотека gui
Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex: Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex:
@ -120,6 +183,33 @@ indentory_doc.some_button.text = "new text"
В скрипте макета `layouts/файл_макета.xml` - `layouts/файл_макета.xml.lua` уже доступна переменная **document** содержащая объект класса Document В скрипте макета `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 ## Библиотека inventory
Библиотека функций для работы с инвентарем. Библиотека функций для работы с инвентарем.
@ -197,6 +287,18 @@ block.index(name: str) -> int
Возвращает числовой id блока, принимая в качестве агрумента строковый Возвращает числовой id блока, принимая в качестве агрумента строковый
```python
block.material(blockid: int) -> str
```
Возвращает id материала блока.
```python
block.caption(blockid: int) -> str
```
Возвращает название блока, отображаемое в интерфейсе.
```python ```python
block.get(x: int, y: int, z: int) -> int block.get(x: int, y: int, z: int) -> int
``` ```
@ -359,206 +461,27 @@ hud.close(layoutid: str)
hud.get_block_inventory() -> int hud.get_block_inventory() -> int
``` ```
Получить ID инвентаря открытого блока или 0 Дает 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
Библиотека функций для работы с файлами
```python ```python
file.resolve(путь: str) -> str hud.get_player() -> int
``` ```
Функция приводит запись очкахода:путь` (например `user:worlds/house1`) к обычному пути. (например `C://Users/user/.voxeng/worlds/house1`) Дает ID игрока, к которому привязан пользовательский интерфейс
> [!NOTE]
> Функцию не нужно использовать в сочетании с другими функциями из библиотеки, так как они делают это автоматически
Возвращаемый путь не является каноническим и может быть как абсолютным, так и относительным.
```python ```python
file.read(путь: str) -> str hud.pause()
``` ```
Читает весь текстовый файл и возвращает в виде строки Открывает меню паузы
```python ```python
file.read_bytes(путь: str) -> array of integers hud.resume()
``` ```
Читает файл в массив байт. Закрывает меню паузы.
```python ## Библиотека time
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
```python ```python
time.uptime() -> float time.uptime() -> float
@ -566,24 +489,8 @@ time.uptime() -> float
Возвращает время с момента запуска движка в секундах Возвращает время с момента запуска движка в секундах
## Доступные модули ```python
time.delta() -> 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
``` ```
Возвращает дельту времени (время прошедшее с предыдущего кадра)

109
doc/ru/Консоль.md Normal file
View File

@ -0,0 +1,109 @@
# Консоль
Для работы с командным интерпретатором предоставляется библиотека **console**
## Создание команд
Для создания команды консоли используется следующая функция:
```python
console.add_command(схема: str, исполнитель: function)
```
Схема имеет следующий синтаксис:
```
название позиционные параметры {именованные параметры}
```
Название может содержать:
- латинницу
- цифры (кроме первого символа)
- `.`, `_`, `-`
Позиционные параметры разделяются пробелами и имеют следующий синтаксис:
```
название:тип (вариант 1)
название:тип=по-умолчанию (вариант 2)
название:тип~центральное-значение (вариант 3)
название:тип=по-умолчанию~центральное-значение (вариант 4)
```
Доступные типы:
- **int** - целое число
- **num** - дробное число
- **str** - строка
- **sel** - селектор (id объекта представленный целым числом)
- **enum** - перечисление
На вариантах 3 и 4 показан оператор `~` позволяющий использовать относительные значения. *Центральное значение* - значение, относительно которого будет указываться пользовательское. Например позиция игрока.
Относительный оператор работает только с числами (num или int)
В качестве центральных значений могут указываться переменные, назначаемые через **console.set**.
Пример:
```python
x:num~pos.x
```
Переменные можно указывать и в качестве значений по-умолчанию, при использовании префикса `$`:
```python
t:int=$time
```
Перечисления указывазываются в формате:
```python
mode:[replace|destruct|none]
```
Либо через переменную:
```python
mode:enum $modes
```
Селекторы указываются с префиксом `@`. На данный момент являются заглушкой, по причине отсутствия объектной модели. Следует делать опциональными и использовать переменные:
```python
obj:sel=$obj.id # obj.id - id игрока
```
Именованные аргументы указываются в специальном блоке, ограниченном фигурными скобками `{ }` по той же схеме.
Пример:
```python
eval name:str="World" {greeting:str='Hello'}
```
## Примеры схем команд
Схемы существующих команд можно найти в файле `res/script/stdcmd.lua`.
Пример - команда **tp**:
```python
tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z
```
Полный lua код создания команды:
```lua
console.add_command(
"tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z",
"Teleport object",
function (args, kwargs)
player.set_pos(unpack(args))
end
)
```
- В args передаются готовые значения позиционных аргументов.
- В kwargs передается таблица значений именованных аргументов.
Проверку и приведение типов интерпретатор команд производит автоматически.

View File

@ -0,0 +1,59 @@
# Пользовательский ввод
Обработка нажатий клавиш и кнопок мыши обрабатываются через привязки (bindings), которые назначаются в паке, в файле `config/bindings.toml` в формате:
```toml
packid.binding.name="inputtype:codename"
```
- packid - опционально, но желательно
- inputtype - key или mouse
- codename - имя клавиши или кнопки мыши (left/right/middle)
## Имена клавиш
- space, backspace, tab, enter, caps-lock, escape
- left-ctrl, left-shift, left-alt, left-super
- right-ctrl, right-shift, right-alt, right-super
- delete, home, end, insert, page-up, page-down
- left, right, down, up
- a..z
- 0..9
- f1..f25
## Библиотека input
```python
input.keycode(keyname: str) -> int
```
Возвращает код клавиши по имени, либо -1
```python
input.mousecode(mousename: str) -> int
```
Возвращает код кнопки мыши по имени, либо -1
```python
input.add_callback(bindname: str, callback: function)
```
Назначает функцию, которая будет вызываться при активации привязки. Пример:
```lua
input.add_callback("hud.inventory", function ()
print("Inventory open key pressed")
end)
```
```python
input.get_mouse_pos() -> {int, int}
```
Возвращает позицию курсора на экране.
```python
input.get_bindings() -> массив строк
```
Возвращает названия всех доступных привязок.

View File

@ -0,0 +1,126 @@
# События движка
## События блоков
Функции для обработки событий, прописываемые в скрипте блока.
```lua
function on_placed(x, y, z, playerid)
```
Вызывается после установки блока игроком
```lua
function on_broken(x, y, z, playerid)
```
Вызывается после разрушения блока игроком
```lua
function on_interact(x, y, z, playerid) -> bool
```
Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true`
```lua
function on_update(x, y, z)
```
Вызывается при обновлении блока (если изменился соседний блок)
```lua
function on_random_update(x, y, z)
```
Вызывается в случайные моменты времени (рост травы на блоках земли)
```lua
function on_blocks_tick(tps: int)
```
Вызывается tps (20) раз в секунду
## События предметов
Функции для обработки событий, прописываемые в скрипте предмета.
```lua
function on_use(playerid: int)
```
Вызывается при нажатии ПКМ не на блок.
```lua
function on_use_on_block(x: int, y: int, z: int, playerid: int)
```
Вызывается при нажатии ПКМ на блок. Предотвращает установку блока, прописанного в `placing-block` если возвращает `true`
```lua
function on_block_break_by(x: int, y: int, z: int, playerid: int)
```
Вызывается при нажатии ЛКМ на блок (в т.ч неразрушимый). Предотвращает разрушение блока, если возвращает `true`
## События мира
События мира для контент-пака прописываются в `scripts/world.lua`
```lua
function on_world_open()
```
Вызывается при загрузке мира
```lua
function on_world_save()
```
Вызывается перед сохранением мира
```lua
function on_world_tick()
```
Вызывается 20 раз в секунду
```lua
function on_world_quit()
```
Вызывается при выходе из мира (после сохранения)
## События макета
События прописываются в файле `layouts/имя_макета.xml.lua`.
```lua
function on_open(invid: int, x: int, y: int, z: int)
```
Вызывается при добавлении элемента на экран.
- При отсутствии привязки к инвентарю invid будет равен 0.
- При отсутствии привязки к блоку x, y, z так же будут равны 0.
```lua
function on_close(invid: int)
```
Вызывается при удалении элемента с экрана.
## События HUD
События связанные с игровым интерфейсом прописываются в файле `scripts/hud.lua`
```lua
function on_hud_open(playerid: int)
```
Вызывается после входа в мир, когда становится доступна библиотека hud. Здесь на экран добавляются постоянные элементы.
```lua
function on_hud_close(playerid: int)
```
Вызывается при выходе из мира, перед его сохранением.

View File

@ -0,0 +1,137 @@
## Библиотека *file*
Библиотека функций для работы с файлами
```python
file.resolve(путь: str) -> str
```
Функция приводит запись очкахода:путь` (например `user:worlds/house1`) к обычному пути. (например `C://Users/user/.voxeng/worlds/house1`)
> [!NOTE]
> Функцию не нужно использовать в сочетании с другими функциями из библиотеки, так как они делают это автоматически
Возвращаемый путь не является каноническим и может быть как абсолютным, так и относительным.
```python
file.read(путь: str) -> str
```
Читает весь текстовый файл и возвращает в виде строки
```python
file.read_bytes(путь: str) -> array of integers
```
Читает файл в массив байт.
```python
file.write(путь: str, текст: str) -> nil
```
Записывает текст в файл (с перезаписью)
```python
file.write_bytes(путь: str, data: array of integers)
```
Записывает массив байт в файл (с перезаписью)
```python
file.length(путь: str) -> int
```
Возвращает размер файла в байтах, либо -1, если файл не найден
```python
file.exists(путь: str) -> bool
```
Проверяет, существует ли по данному пути файл или директория
```python
file.isfile(путь: str) -> bool
```
Проверяет, существует ли по данному пути файл
```python
file.isdir(путь: str) -> bool
```
Проверяет, существует ли по данному пути директория
```python
file.mkdir(путь: str) -> bool
```
Создает директорию. Возвращает true если была создана новая директория
```python
file.mkdirs(путь: str) -> bool
```
Создает всю цепочку директорий. Возвращает true если были созданы директории.
```python
file.find(путь: str) -> str
```
Ищет файл от последнего пака до res. Путь указывается без префикса. Возвращает путь с нужным префиксом. Если файл не найден, возвращает nil.
```python
file.remove(путь: str) -> bool
```
Удаляет файл. Возращает **true** если файл существовал. Бросает исключение при нарушении доступа.
```python
file.remove_tree(путь: str) -> int
```
Рекурсивно удаляет файлы. Возвращает число удаленных файлов.
## Библиотека json
Библиотека содержит функции для сериализации и десериализации таблиц:
```python
json.tostring(object: table, human_readable: bool=false) -> str
```
Сериализует объект в JSON строку. При значении второго параметра **true** будет использовано многострочное форматирование, удобное для чтения человеком, а не компактное, использующееся по-умолчанию.
```python
json.parse(code: str) -> table
```
Парсит JSON строку в таблицу.
## Библиотека toml
Библиотека содержит функции для сериализации и десериализации таблиц:
```python
toml.tostring(object: table) -> str
```
Сериализует объект в TOML строку.
```python
toml.parse(code: str) -> table
```
Парсит TOML строку в таблицу.
## Сохранение данных в мире
При сохранении данных пака в мире следует использовать функцию
```python
pack.data_file(packid: str, filename: str) -> str
```
Функция возвращает путь к файлу данных по типу: `world:data/packid/filename`
и создает недостающие директории в пути.
При использовании путей не соответствующим `data/{packid}/...` возможна потеря данных при перезаписи мира.

24
fix_dylibs.sh Executable file
View File

@ -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"

18
res/config/bindings.toml Normal file
View File

@ -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"

1
res/config/builtins.list Normal file
View File

@ -0,0 +1 @@
base

View File

@ -1,15 +1,15 @@
function on_random_update(x, y, z) function on_random_update(x, y, z)
local dirtid = block_index('base:dirt'); local dirtid = block.index('base:dirt');
if is_solid_at(x, y+1, z) then if block.is_solid_at(x, y+1, z) then
set_block(x, y, z, dirtid, 0) block.set(x, y, z, dirtid, 0)
else else
local grassblockid = block_index('base:grass_block') local grassblockid = block.index('base:grass_block')
for lx=-1,1 do for lx=-1,1 do
for ly=-1,1 do for ly=-1,1 do
for lz=-1,1 do for lz=-1,1 do
if get_block(x + lx, y + ly, z + lz) == dirtid then if block.get(x + lx, y + ly, z + lz) == dirtid then
if not is_solid_at(x + lx, y + ly + 1, z + lz) then if not block.is_solid_at(x + lx, y + ly + 1, z + lz) then
set_block(x + lx, y + ly, z + lz, grassblockid, 0) block.set(x + lx, y + ly, z + lz, grassblockid, 0)
return return
end end
end end

View File

@ -35,6 +35,7 @@ function submit(text)
add_to_history(text) add_to_history(text)
setup_variables() setup_variables()
document.log.caret = -1
local status, result = pcall(function() return console.execute(text) end) local status, result = pcall(function() return console.execute(text) end)
if result ~= nil then if result ~= nil then
local prevtext = document.log.text local prevtext = document.log.text

View File

@ -9,10 +9,9 @@ function on_open()
table.sort(names) table.sort(names)
local panel = document.root local panel = document.root
for _,k in ipairs(names) do for _,name in ipairs(names) do
panel:add(string.format( panel:add(gui.template(
"<button onclick=%q>%s</button>", "language", {id=invlocales[name], name=name}
string.format("core.set_setting('ui.language', %q) menu:back()", invlocales[k]), k
)) ))
end end
panel:add("<button onclick='menu:back()'>@Back</button>") panel:add("<button onclick='menu:back()'>@Back</button>")

View File

@ -16,7 +16,8 @@ function on_open()
refresh_sensitivity() refresh_sensitivity()
local panel = document.bindings_panel 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 for i,name in ipairs(bindings) do
panel:add(gui.template("binding", { panel:add(gui.template("binding", {
id=name, name=gui.str(name) id=name, name=gui.str(name)

View File

@ -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) local info = core.get_setting_info(id)
postfix = postfix or "" postfix = postfix or ""
tooltip = tooltip or ""
document.root:add(gui.template("track_setting", { document.root:add(gui.template("track_setting", {
id=id, id=id,
name=gui.str(name, "settings"), name=gui.str(name, "settings"),
@ -8,7 +9,8 @@ function create_setting(id, name, step, postfix)
min=info.min, min=info.min,
max=info.max, max=info.max,
step=step, step=step,
postfix=postfix postfix=postfix,
tooltip=tooltip
})) }))
update_setting(core.get_setting(id), id, name, postfix) update_setting(core.get_setting(id), id, name, postfix)
end end
@ -24,10 +26,11 @@ function update_setting(x, id, name, postfix)
) )
end end
function create_checkbox(id, name) function create_checkbox(id, name, tooltip)
tooltip = tooltip or ''
document.root:add(string.format( document.root:add(string.format(
"<checkbox consumer='function(x) core.set_setting(\"%s\", x) end' checked='%s'>%s</checkbox>", "<checkbox consumer='function(x) core.set_setting(\"%s\", x) end' checked='%s' tooltip='%s'>%s</checkbox>",
id, core.str_setting(id), gui.str(name, "settings") id, core.str_setting(id), gui.str(tooltip, "settings"), gui.str(name, "settings")
)) ))
end end
@ -35,10 +38,10 @@ function on_open()
create_setting("chunks.load-distance", "Load Distance", 1) create_setting("chunks.load-distance", "Load Distance", 1)
create_setting("chunks.load-speed", "Load Speed", 1) create_setting("chunks.load-speed", "Load Speed", 1)
create_setting("graphics.fog-curve", "Fog Curve", 0.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_setting("camera.fov", "FOV", 1, "°")
create_checkbox("display.fullscreen", "Fullscreen") create_checkbox("display.fullscreen", "Fullscreen")
create_checkbox("display.vsync", "V-Sync") 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") create_checkbox("camera.shaking", "Camera Shaking")
end end

View File

@ -0,0 +1,4 @@
<button
onclick='core.set_setting("ui.language", "%{id}") menu:back()'>
%{name}
</button>

View File

@ -1,6 +1,6 @@
<panel color='0' context='settings'> <panel color='0' context='settings'>
<label id='%{id}.L' margin='0,3,0,0'>%{name}: %{value}%{postfix}</label> <label id='%{id}.L' margin='0,3,0,0'>%{name}: %{value}%{postfix}</label>
<trackbar <trackbar
value='%{value}' min='%{min}' max='%{max}' step='%{step}' value='%{value}' min='%{min}' max='%{max}' step='%{step}' tooltip='%{tooltip}'
consumer='function(x) update_setting(x, "%{id}", "%{name}", "%{postfix}") end'/> consumer='function(x) update_setting(x, "%{id}", "%{name}", "%{postfix}") end'/>
</panel> </panel>

View File

@ -1,65 +1,4 @@
-- TOML serialization module print("WARNING: toml is replaced with built-in library, just remove 'require \"core:toml\"'")
local toml = {} toml.serialize = toml.tostring
toml.deserialize = toml.parse
-- 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
return toml return toml

View File

@ -51,7 +51,7 @@ console.add_command(
) )
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", "Teleport object",
function (args, kwargs) function (args, kwargs)
player.set_pos(unpack(args)) player.set_pos(unpack(args))

View File

@ -12,47 +12,55 @@ error.pack-not-found=Не ўдалося знайсці пакет
error.dependency-not-found=Выкарыстоўваная залежнасць не знойдзена error.dependency-not-found=Выкарыстоўваная залежнасць не знойдзена
pack.remove-confirm=Выдаліць увесь кантэнт які пастаўляецца пакам са свету (беззваротна)? pack.remove-confirm=Выдаліць увесь кантэнт які пастаўляецца пакам са свету (беззваротна)?
# Подсказки
graphics.gamma.tooltip=Крывая яркасці асвятлення
graphics.backlight.tooltip=Падсветка, якая прадухіляе поўную цемру
# Меню # Меню
menu.New World=Новы Свет menu.Apply=Ужыць
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.Audio=Гук 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.Seed=Зерне
world.Name=Назва world.Name=Назва
world.World generator=Генератар свету world.World generator=Генератар свету
world.generators.default=Звычайны world.generators.default=Звычайны
world.generators.flat=Плоскі world.generators.flat=Плоскі
menu.Create World=Стварыць Свет world.Create World=Стварыць Свет
world.convert-request=Ёсць змены ў індэксах! Канвертаваць свет? world.convert-request=Ёсць змены ў індэксах! Канвертаваць свет?
world.delete-confirm=Выдаліць свет незваротна? 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 Distance=Дыстанцыя Загрузкі
settings.Load Speed=Хуткасць Загрузкі settings.Load Speed=Хуткасць Загрузкі
settings.Fog Curve=Крывая Туману
settings.Backlight=Падсветка
settings.V-Sync=Вертыкальная Сінхранізацыя
settings.Camera Shaking=Труска Камеры
settings.Master Volume=Агульная Гучнасць settings.Master Volume=Агульная Гучнасць
settings.Mouse Sensitivity=Адчувальнасць Мышы
settings.Music=Музыка
settings.Regular Sounds=Звычайныя Гукі settings.Regular Sounds=Звычайныя Гукі
settings.UI Sounds=Гукі Інтэрфейсу settings.UI Sounds=Гукі Інтэрфейсу
settings.Ambient=Фон settings.V-Sync=Вертыкальная Сінхранізацыя
settings.Music=Музыка
settings.FOV=Поле Зроку
settings.Mouse Sensitivity=Адчувальнасць Мышы
settings.Language=Мова
# Управление # Управление
devtools.console=Кансоль
movement.forward=Уперад movement.forward=Уперад
movement.back=Назад movement.back=Назад
movement.left=Улева movement.left=Улева

View File

@ -8,7 +8,12 @@ world.delete-confirm=Do you want to delete world forever?
world.generators.default=Default world.generators.default=Default
world.generators.flat=Flat world.generators.flat=Flat
# Tooltips
graphics.gamma.tooltip=Lighting brightness curve
graphics.backlight.tooltip=Backlight to prevent total darkness
# Bindings # Bindings
chunks.reload=Reload Chunks
devtools.console=Console devtools.console=Console
movement.forward=Forward movement.forward=Forward
movement.back=Back movement.back=Back

View File

@ -12,6 +12,10 @@ error.pack-not-found=Не удалось найти пакет
error.dependency-not-found=Используемая зависимость не найдена error.dependency-not-found=Используемая зависимость не найдена
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)? pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
# Подсказки
graphics.gamma.tooltip=Кривая яркости освещения
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
# Меню # Меню
menu.Apply=Применить menu.Apply=Применить
menu.Audio=Звук menu.Audio=Звук
@ -56,6 +60,7 @@ settings.UI Sounds=Звуки Интерфейса
settings.V-Sync=Вертикальная Синхронизация settings.V-Sync=Вертикальная Синхронизация
# Управление # Управление
chunks.reload=Перезагрузить Чанки
devtools.console=Консоль devtools.console=Консоль
movement.forward=Вперёд movement.forward=Вперёд
movement.back=Назад movement.back=Назад

View File

@ -18,8 +18,8 @@ Texture* Assets::getTexture(std::string name) const {
return found->second.get(); return found->second.get();
} }
void Assets::store(Texture* texture, std::string name){ void Assets::store(std::unique_ptr<Texture> texture, std::string name){
textures.emplace(name, texture); textures.emplace(name, std::move(texture));
} }
@ -30,8 +30,8 @@ Shader* Assets::getShader(std::string name) const{
return found->second.get(); return found->second.get();
} }
void Assets::store(Shader* shader, std::string name){ void Assets::store(std::unique_ptr<Shader> shader, std::string name){
shaders.emplace(name, shader); shaders.emplace(name, std::move(shader));
} }
Font* Assets::getFont(std::string name) const { Font* Assets::getFont(std::string name) const {
@ -41,8 +41,8 @@ Font* Assets::getFont(std::string name) const {
return found->second.get(); return found->second.get();
} }
void Assets::store(Font* font, std::string name){ void Assets::store(std::unique_ptr<Font> font, std::string name){
fonts.emplace(name, font); fonts.emplace(name, std::move(font));
} }
Atlas* Assets::getAtlas(std::string name) const { Atlas* Assets::getAtlas(std::string name) const {
@ -52,8 +52,8 @@ Atlas* Assets::getAtlas(std::string name) const {
return found->second.get(); return found->second.get();
} }
void Assets::store(Atlas* atlas, std::string name){ void Assets::store(std::unique_ptr<Atlas> atlas, std::string name){
atlases.emplace(name, atlas); atlases.emplace(name, std::move(atlas));
} }
audio::Sound* Assets::getSound(std::string name) const { audio::Sound* Assets::getSound(std::string name) const {
@ -63,8 +63,8 @@ audio::Sound* Assets::getSound(std::string name) const {
return found->second.get(); return found->second.get();
} }
void Assets::store(audio::Sound* sound, std::string name) { void Assets::store(std::unique_ptr<audio::Sound> sound, std::string name) {
sounds.emplace(name, sound); sounds.emplace(name, std::move(sound));
} }
const std::vector<TextureAnimation>& Assets::getAnimations() { const std::vector<TextureAnimation>& Assets::getAnimations() {
@ -82,6 +82,6 @@ UiDocument* Assets::getLayout(std::string name) const {
return found->second.get(); return found->second.get();
} }
void Assets::store(UiDocument* layout, std::string name) { void Assets::store(std::unique_ptr<UiDocument> layout, std::string name) {
layouts[name] = std::shared_ptr<UiDocument>(layout); layouts[name] = std::shared_ptr<UiDocument>(std::move(layout));
} }

View File

@ -39,25 +39,25 @@ public:
~Assets(); ~Assets();
Texture* getTexture(std::string name) const; Texture* getTexture(std::string name) const;
void store(Texture* texture, std::string name); void store(std::unique_ptr<Texture> texture, std::string name);
Shader* getShader(std::string name) const; Shader* getShader(std::string name) const;
void store(Shader* shader, std::string name); void store(std::unique_ptr<Shader> shader, std::string name);
Font* getFont(std::string name) const; Font* getFont(std::string name) const;
void store(Font* font, std::string name); void store(std::unique_ptr<Font> font, std::string name);
Atlas* getAtlas(std::string name) const; Atlas* getAtlas(std::string name) const;
void store(Atlas* atlas, std::string name); void store(std::unique_ptr<Atlas> atlas, std::string name);
audio::Sound* getSound(std::string name) const; audio::Sound* getSound(std::string name) const;
void store(audio::Sound* sound, std::string name); void store(std::unique_ptr<audio::Sound> sound, std::string name);
const std::vector<TextureAnimation>& getAnimations(); const std::vector<TextureAnimation>& getAnimations();
void store(const TextureAnimation& animation); void store(const TextureAnimation& animation);
UiDocument* getLayout(std::string name) const; UiDocument* getLayout(std::string name) const;
void store(UiDocument* layout, std::string name); void store(std::unique_ptr<UiDocument> layout, std::string name);
}; };
#endif // ASSETS_ASSETS_HPP_ #endif // ASSETS_ASSETS_HPP_

View File

@ -11,6 +11,7 @@
#include "../files/engine_paths.hpp" #include "../files/engine_paths.hpp"
#include "../content/Content.hpp" #include "../content/Content.hpp"
#include "../content/ContentPack.hpp" #include "../content/ContentPack.hpp"
#include "../voxels/Block.hpp"
#include "../graphics/core/Texture.hpp" #include "../graphics/core/Texture.hpp"
#include "../logic/scripting/scripting.hpp" #include "../logic/scripting/scripting.hpp"
@ -187,7 +188,7 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.processPreloadConfigs(content); loader.processPreloadConfigs(content);
for (auto& entry : content->getBlockMaterials()) { for (auto& entry : content->getBlockMaterials()) {
auto& material = entry.second; auto& material = *entry.second;
loader.tryAddSound(material.stepsSound); loader.tryAddSound(material.stepsSound);
loader.tryAddSound(material.placeSound); loader.tryAddSound(material.placeSound);
loader.tryAddSound(material.breakSound); loader.tryAddSound(material.breakSound);
@ -217,7 +218,7 @@ bool AssetsLoader::loadExternalTexture(
if (fs::exists(path)) { if (fs::exists(path)) {
try { try {
auto image = imageio::read(path.string()); auto image = imageio::read(path.string());
assets->store(Texture::from(image.get()).release(), name); assets->store(Texture::from(image.get()), name);
return true; return true;
} catch (const std::exception& err) { } catch (const std::exception& err) {
logger.error() << "error while loading external " logger.error() << "error while loading external "

View File

@ -6,6 +6,7 @@
#include "../audio/audio.hpp" #include "../audio/audio.hpp"
#include "../files/files.hpp" #include "../files/files.hpp"
#include "../files/engine_paths.hpp" #include "../files/engine_paths.hpp"
#include "../coders/commons.hpp"
#include "../coders/imageio.hpp" #include "../coders/imageio.hpp"
#include "../coders/json.hpp" #include "../coders/json.hpp"
#include "../coders/GLSLExtension.hpp" #include "../coders/GLSLExtension.hpp"
@ -43,7 +44,7 @@ assetload::postfunc assetload::texture(
imageio::read(paths->find(filename+".png").u8string()).release() imageio::read(paths->find(filename+".png").u8string()).release()
); );
return [name, image](auto assets) { 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(); Atlas* atlas = builder.build(2, false).release();
return [=](auto assets) { return [=](auto assets) {
atlas->prepare(); atlas->prepare();
assets->store(atlas, name); assets->store(std::unique_ptr<Atlas>(atlas), name);
for (const auto& file : names) { for (const auto& file : names) {
animation(assets, paths, name, directory, file, atlas); animation(assets, paths, name, directory, file, atlas);
} }
@ -128,7 +129,7 @@ assetload::postfunc assetload::font(
for (auto& page : *pages) { for (auto& page : *pages) {
textures.emplace_back(Texture::from(page.get())); textures.emplace_back(Texture::from(page.get()));
} }
assets->store(new Font(std::move(textures), res, 4), name); assets->store(std::make_unique<Font>(std::move(textures), res, 4), name);
}; };
} }
@ -142,8 +143,7 @@ assetload::postfunc assetload::layout(
return [=](auto assets) { return [=](auto assets) {
try { try {
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config); auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
auto document = UiDocument::read(cfg->env, name, file); assets->store(UiDocument::read(cfg->env, name, file), name);
assets->store(document.release(), name);
} catch (const parsing_error& err) { } catch (const parsing_error& err) {
throw std::runtime_error( throw std::runtime_error(
"failed to parse layout XML '"+file+"':\n"+err.errorLog() "failed to parse layout XML '"+file+"':\n"+err.errorLog()
@ -189,7 +189,7 @@ assetload::postfunc assetload::sound(
} }
auto sound = baseSound.release(); auto sound = baseSound.release();
return [=](auto assets) { return [=](auto assets) {
assets->store(sound, name); assets->store(std::unique_ptr<audio::Sound>(sound), name);
}; };
} }
@ -304,7 +304,7 @@ static bool animation(
auto animation = create_animation( auto animation = create_animation(
srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList 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); assets->store(animation);
return true; return true;
} }

View File

@ -389,7 +389,7 @@ std::unique_ptr<Stream> ALAudio::openStream(std::shared_ptr<PCMStream> stream, b
return std::make_unique<ALStream>(this, stream, keepSource); return std::make_unique<ALStream>(this, stream, keepSource);
} }
ALAudio* ALAudio::create() { std::unique_ptr<ALAudio> ALAudio::create() {
ALCdevice* device = alcOpenDevice(nullptr); ALCdevice* device = alcOpenDevice(nullptr);
if (device == nullptr) if (device == nullptr)
return nullptr; return nullptr;
@ -400,7 +400,7 @@ ALAudio* ALAudio::create() {
} }
AL_CHECK(); AL_CHECK();
logger.info() << "initialized"; logger.info() << "initialized";
return new ALAudio(device, context); return std::make_unique<ALAudio>(device, context);
} }
uint ALAudio::getFreeSource(){ uint ALAudio::getFreeSource(){

View File

@ -136,9 +136,8 @@ namespace audio {
std::vector<uint> freebuffers; std::vector<uint> freebuffers;
uint maxSources = 256; uint maxSources = 256;
ALAudio(ALCdevice* device, ALCcontext* context);
public: public:
ALAudio(ALCdevice* device, ALCcontext* context);
~ALAudio(); ~ALAudio();
uint getFreeSource(); uint getFreeSource();
@ -164,7 +163,7 @@ namespace audio {
return false; return false;
} }
static ALAudio* create(); static std::unique_ptr<ALAudio> create();
}; };
} }

View File

@ -17,6 +17,6 @@ std::unique_ptr<Stream> NoAudio::openStream(std::shared_ptr<PCMStream> stream, b
return std::make_unique<NoStream>(stream, keepSource); return std::make_unique<NoStream>(stream, keepSource);
} }
NoAudio* NoAudio::create() { std::unique_ptr<NoAudio> NoAudio::create() {
return new NoAudio(); return std::make_unique<NoAudio>();
} }

View File

@ -81,7 +81,7 @@ namespace audio {
return true; return true;
} }
static NoAudio* create(); static std::unique_ptr<NoAudio> create();
}; };
} }

View File

@ -147,11 +147,11 @@ public:
void audio::initialize(bool enabled) { void audio::initialize(bool enabled) {
if (enabled) { if (enabled) {
backend = ALAudio::create(); backend = ALAudio::create().release();
} }
if (backend == nullptr) { if (backend == nullptr) {
std::cerr << "could not to initialize audio" << std::endl; std::cerr << "could not to initialize audio" << std::endl;
backend = NoAudio::create(); backend = NoAudio::create().release();
} }
create_channel("master"); create_channel("master");
} }
@ -333,7 +333,7 @@ int audio::create_channel(const std::string& name) {
if (index != -1) { if (index != -1) {
return index; return index;
} }
channels.emplace_back(new Channel(name)); channels.emplace_back(std::make_unique<Channel>(name));
return channels.size()-1; return channels.size()-1;
} }

View File

@ -11,20 +11,20 @@ namespace dynamic {
} }
namespace json { namespace json {
const int BJSON_END = 0x0; inline constexpr int BJSON_END = 0x0;
const int BJSON_TYPE_DOCUMENT = 0x1; inline constexpr int BJSON_TYPE_DOCUMENT = 0x1;
const int BJSON_TYPE_LIST = 0x2; inline constexpr int BJSON_TYPE_LIST = 0x2;
const int BJSON_TYPE_BYTE = 0x3; inline constexpr int BJSON_TYPE_BYTE = 0x3;
const int BJSON_TYPE_INT16 = 0x4; inline constexpr int BJSON_TYPE_INT16 = 0x4;
const int BJSON_TYPE_INT32 = 0x5; inline constexpr int BJSON_TYPE_INT32 = 0x5;
const int BJSON_TYPE_INT64 = 0x6; inline constexpr int BJSON_TYPE_INT64 = 0x6;
const int BJSON_TYPE_NUMBER = 0x7; inline constexpr int BJSON_TYPE_NUMBER = 0x7;
const int BJSON_TYPE_STRING = 0x8; inline constexpr int BJSON_TYPE_STRING = 0x8;
const int BJSON_TYPE_BYTES = 0x9; inline constexpr int BJSON_TYPE_BYTES = 0x9;
const int BJSON_TYPE_FALSE = 0xA; inline constexpr int BJSON_TYPE_FALSE = 0xA;
const int BJSON_TYPE_TRUE = 0xB; inline constexpr int BJSON_TYPE_TRUE = 0xB;
const int BJSON_TYPE_NULL = 0xC; inline constexpr int BJSON_TYPE_NULL = 0xC;
const int BJSON_TYPE_CDOCUMENT = 0x1F; inline constexpr int BJSON_TYPE_CDOCUMENT = 0x1F;
extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false); extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false);
extern std::shared_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size); extern std::shared_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);

View File

@ -1,5 +1,7 @@
#include "json.hpp" #include "json.hpp"
#include "commons.hpp"
#include "../data/dynamic.hpp" #include "../data/dynamic.hpp"
#include "../util/stringutil.hpp" #include "../util/stringutil.hpp"
@ -240,11 +242,11 @@ Value Parser::parseValue() {
throw error("unexpected character '"+std::string({next})+"'"); throw error("unexpected character '"+std::string({next})+"'");
} }
std::unique_ptr<Map> 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); Parser parser(filename, source);
return parser.parse(); return parser.parse();
} }
std::unique_ptr<Map> json::parse(const std::string& source) { dynamic::Map_sptr json::parse(const std::string& source) {
return parse("<string>", source); return parse("<string>", source);
} }

View File

@ -1,21 +1,16 @@
#ifndef CODERS_JSON_HPP_ #ifndef CODERS_JSON_HPP_
#define CODERS_JSON_HPP_ #define CODERS_JSON_HPP_
#include "commons.hpp"
#include "binary_json.hpp" #include "binary_json.hpp"
#include "../data/dynamic.hpp" #include "../data/dynamic.hpp"
#include "../typedefs.hpp" #include "../typedefs.hpp"
#include <vector>
#include <string> #include <string>
#include <stdint.h>
#include <stdexcept>
#include <unordered_map>
namespace json { namespace json {
std::unique_ptr<dynamic::Map> parse(const std::string& filename, const std::string& source); dynamic::Map_sptr parse(const std::string& filename, const std::string& source);
std::unique_ptr<dynamic::Map> parse(const std::string& source); dynamic::Map_sptr parse(const std::string& source);
std::string stringify( std::string stringify(
const dynamic::Map* obj, const dynamic::Map* obj,

View File

@ -7,15 +7,14 @@
#include "../files/settings_io.hpp" #include "../files/settings_io.hpp"
#include <math.h> #include <math.h>
#include <iostream>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <assert.h> #include <assert.h>
using namespace toml; using namespace toml;
class Reader : BasicParser { class TomlReader : BasicParser {
SettingsHandler& handler; dynamic::Map_sptr root;
void skipWhitespace() override { void skipWhitespace() override {
BasicParser::skipWhitespace(); 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()) { while (hasNext()) {
skipWhitespace(); skipWhitespace();
if (!hasNext()) { if (!hasNext()) {
@ -36,43 +62,31 @@ class Reader : BasicParser {
if (c == '[') { if (c == '[') {
std::string name = parseName(); std::string name = parseName();
pos++; pos++;
readSection(name); readSection(name, getSection(name));
return; return;
} }
pos--; pos--;
std::string name = section+"."+parseName(); std::string name = parseName();
expect('='); expect('=');
c = peek(); c = peek();
if (is_digit(c)) { if (is_digit(c)) {
auto num = parseNumber(1); map.put(name, parseNumber(1));
if (handler.has(name)) {
handler.setValue(name, num);
}
} else if (c == '-' || c == '+') { } else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1; int sign = c == '-' ? -1 : 1;
pos++; pos++;
auto num = parseNumber(sign); map.put(name, parseNumber(sign));
if (handler.has(name)) {
handler.setValue(name, num);
}
} else if (is_identifier_start(c)) { } else if (is_identifier_start(c)) {
std::string identifier = parseName(); std::string identifier = parseName();
if (handler.has(name)) {
if (identifier == "true" || identifier == "false") { if (identifier == "true" || identifier == "false") {
bool flag = identifier == "true"; map.put(name, identifier == "true");
handler.setValue(name, flag);
} else if (identifier == "inf") { } else if (identifier == "inf") {
handler.setValue(name, INFINITY); map.put(name, INFINITY);
} else if (identifier == "nan") { } else if (identifier == "nan") {
handler.setValue(name, NAN); map.put(name, NAN);
}
} }
} else if (c == '"' || c == '\'') { } else if (c == '"' || c == '\'') {
pos++; pos++;
std::string str = parseString(c); map.put(name, parseString(c));
if (handler.has(name)) {
handler.setValue(name, str);
}
} else { } else {
throw error("feature is not supported"); throw error("feature is not supported");
} }
@ -81,29 +95,67 @@ class Reader : BasicParser {
} }
public: public:
Reader( TomlReader(
SettingsHandler& handler,
std::string_view file, std::string_view file,
std::string_view source) std::string_view source)
: BasicParser(file, source), handler(handler) { : BasicParser(file, source) {
root = dynamic::create_map();
} }
void read() { dynamic::Map_sptr read() {
skipWhitespace(); skipWhitespace();
if (!hasNext()) { 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( void toml::parse(
SettingsHandler& handler, SettingsHandler& handler, std::string_view file, std::string_view source
const std::string& file,
const std::string& source
) { ) {
Reader reader(handler, file, source); auto map = parse(file, source);
reader.read(); for (auto& entry : map->values) {
const auto& sectionName = entry.first;
auto sectionMap = std::get_if<dynamic::Map_sptr>(&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<dynamic::Map_sptr>(entry.second)) {
ss << entry.first << " = ";
ss << entry.second << "\n";
}
}
for (auto& entry : root.values) {
if (auto submap = std::get_if<dynamic::Map_sptr>(&entry.second)) {
ss << "\n" << toml::stringify(
**submap, name.empty() ? entry.first : name+"."+entry.first
);
}
}
return ss.str();
} }
std::string toml::stringify(SettingsHandler& handler) { std::string toml::stringify(SettingsHandler& handler) {

View File

@ -1,19 +1,20 @@
#ifndef CODERS_TOML_HPP_ #ifndef CODERS_TOML_HPP_
#define CODERS_TOML_HPP_ #define CODERS_TOML_HPP_
#include "commons.hpp" #include "../data/dynamic.hpp"
#include <string> #include <string>
class SettingsHandler; class SettingsHandler;
namespace toml { namespace toml {
std::string stringify(SettingsHandler& handler); 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( void parse(
SettingsHandler& handler, SettingsHandler& handler,
const std::string& file, std::string_view file,
const std::string& source std::string_view source
); );
} }

View File

@ -10,127 +10,6 @@
#include "ContentPack.hpp" #include "ContentPack.hpp"
#include "../logic/scripting/scripting.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<Block*> blockDefsIndices;
auto groups = std::make_unique<DrawGroups>();
for (const std::string& name : blockIds) {
Block* def = blockDefs[name];
// Generating runtime info
def->rt.id = blockDefsIndices.size();
def->rt.emissive = *reinterpret_cast<uint32_t*>(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<ItemDef*> itemDefsIndices;
for (const std::string& name : itemIds) {
ItemDef* def = itemDefs[name];
// Generating runtime info
def->rt.id = itemDefsIndices.size();
def->rt.emissive = *reinterpret_cast<uint32_t*>(def->emission);
itemDefsIndices.push_back(def);
}
auto indices = new ContentIndices(blockDefsIndices, itemDefsIndices);
auto content = std::make_unique<Content>(
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( ContentIndices::ContentIndices(
std::vector<Block*> blockDefs, std::vector<Block*> blockDefs,
std::vector<ItemDef*> itemDefs std::vector<ItemDef*> itemDefs
@ -139,27 +18,21 @@ ContentIndices::ContentIndices(
{} {}
Content::Content( Content::Content(
ContentIndices* indices, std::unique_ptr<ContentIndices> indices,
std::unique_ptr<DrawGroups> drawGroups, std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs, std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs, std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs,
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs, std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs,
std::unordered_map<std::string, BlockMaterial> blockMaterials std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials
) : blockDefs(blockDefs), ) : blockDefs(std::move(blockDefs)),
itemDefs(itemDefs), itemDefs(std::move(itemDefs)),
indices(indices), indices(std::move(indices)),
packs(std::move(packs)), packs(std::move(packs)),
blockMaterials(std::move(blockMaterials)), blockMaterials(std::move(blockMaterials)),
drawGroups(std::move(drawGroups)) drawGroups(std::move(drawGroups))
{} {}
Content::~Content() { Content::~Content() {
for (auto& entry : blockDefs) {
delete entry.second;
}
for (auto& entry : itemDefs) {
delete entry.second;
}
} }
Block* Content::findBlock(std::string id) const { Block* Content::findBlock(std::string id) const {
@ -167,7 +40,7 @@ Block* Content::findBlock(std::string id) const {
if (found == blockDefs.end()) { if (found == blockDefs.end()) {
return nullptr; return nullptr;
} }
return found->second; return found->second.get();
} }
Block& Content::requireBlock(std::string id) const { Block& Content::requireBlock(std::string id) const {
@ -183,7 +56,7 @@ ItemDef* Content::findItem(std::string id) const {
if (found == itemDefs.end()) { if (found == itemDefs.end()) {
return nullptr; return nullptr;
} }
return found->second; return found->second.get();
} }
ItemDef& Content::requireItem(std::string id) const { ItemDef& Content::requireItem(std::string id) const {
@ -199,7 +72,7 @@ const BlockMaterial* Content::findBlockMaterial(std::string id) const {
if (found == blockMaterials.end()) { if (found == blockMaterials.end()) {
return nullptr; return nullptr;
} }
return &found->second; return found->second.get();
} }
const ContentPackRuntime* Content::getPackRuntime(std::string id) const { const ContentPackRuntime* Content::getPackRuntime(std::string id) const {
@ -210,7 +83,7 @@ const ContentPackRuntime* Content::getPackRuntime(std::string id) const {
return found->second.get(); return found->second.get();
} }
const std::unordered_map<std::string, BlockMaterial>& Content::getBlockMaterials() const { const std::unordered_map<std::string, std::unique_ptr<BlockMaterial>>& Content::getBlockMaterials() const {
return blockMaterials; return blockMaterials;
} }

View File

@ -1,17 +1,19 @@
#ifndef CONTENT_CONTENT_HPP_ #ifndef CONTENT_CONTENT_HPP_
#define CONTENT_CONTENT_HPP_ #define CONTENT_CONTENT_HPP_
#include "../typedefs.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <unordered_map> #include <unordered_map>
#include <set> #include <set>
#include "../typedefs.hpp"
#include "../voxels/Block.hpp"
using DrawGroups = std::set<ubyte>; using DrawGroups = std::set<ubyte>;
class Block;
struct BlockMaterial;
class ItemDef; class ItemDef;
class Content; class Content;
class ContentPackRuntime; class ContentPackRuntime;
@ -41,33 +43,6 @@ public:
} }
}; };
class ContentBuilder {
std::unordered_map<std::string, Block*> blockDefs;
std::vector<std::string> blockIds;
std::unordered_map<std::string, ItemDef*> itemDefs;
std::vector<std::string> itemIds;
std::unordered_map<std::string, BlockMaterial> blockMaterials;
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> 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 /// @brief Runtime defs cache: indices
class ContentIndices { class ContentIndices {
std::vector<Block*> blockDefs; std::vector<Block*> blockDefs;
@ -110,21 +85,21 @@ public:
/* Content is a definitions repository */ /* Content is a definitions repository */
class Content { class Content {
std::unordered_map<std::string, Block*> blockDefs; std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs;
std::unordered_map<std::string, ItemDef*> itemDefs; std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs;
std::unique_ptr<ContentIndices> indices; std::unique_ptr<ContentIndices> indices;
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs; std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
std::unordered_map<std::string, BlockMaterial> blockMaterials; std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials;
public: public:
std::unique_ptr<DrawGroups> const drawGroups; std::unique_ptr<DrawGroups> const drawGroups;
Content( Content(
ContentIndices* indices, std::unique_ptr<ContentIndices> indices,
std::unique_ptr<DrawGroups> drawGroups, std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs, std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs, std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs,
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs, std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs,
std::unordered_map<std::string, BlockMaterial> blockMaterials std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials
); );
~Content(); ~Content();
@ -142,7 +117,7 @@ public:
const ContentPackRuntime* getPackRuntime(std::string id) const; const ContentPackRuntime* getPackRuntime(std::string id) const;
const std::unordered_map<std::string, BlockMaterial>& getBlockMaterials() const; const std::unordered_map<std::string, std::unique_ptr<BlockMaterial>>& getBlockMaterials() const;
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& getPacks() const; const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& getPacks() const;
}; };

View File

@ -0,0 +1,109 @@
#include "ContentBuilder.hpp"
ContentBuilder::~ContentBuilder() {}
void ContentBuilder::add(std::unique_ptr<ContentPackRuntime> 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<Block>(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<ItemDef>(id);
return *itemDefs[id];
}
BlockMaterial& ContentBuilder::createBlockMaterial(std::string id) {
blockMaterials[id] = std::make_unique<BlockMaterial>();
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<Content> ContentBuilder::build() {
std::vector<Block*> blockDefsIndices;
auto groups = std::make_unique<DrawGroups>();
for (const std::string& name : blockIds) {
Block& def = *blockDefs[name];
// Generating runtime info
def.rt.id = blockDefsIndices.size();
def.rt.emissive = *reinterpret_cast<uint32_t*>(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<ItemDef*> itemDefsIndices;
for (const std::string& name : itemIds) {
ItemDef& def = *itemDefs[name];
// Generating runtime info
def.rt.id = itemDefsIndices.size();
def.rt.emissive = *reinterpret_cast<uint32_t*>(def.emission);
itemDefsIndices.push_back(&def);
}
auto content = std::make_unique<Content>(
std::make_unique<ContentIndices>(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;
}

View File

@ -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 <memory>
#include <vector>
#include <unordered_map>
class ContentBuilder {
std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs;
std::vector<std::string> blockIds;
std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs;
std::vector<std::string> itemIds;
std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials;
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
public:
~ContentBuilder();
void add(std::unique_ptr<ContentPackRuntime> 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<Content> build();
};
#endif // CONTENT_CONTENT_BUILDER_HPP_

View File

@ -2,6 +2,7 @@
#include "Content.hpp" #include "Content.hpp"
#include "ContentPack.hpp" #include "ContentPack.hpp"
#include "ContentBuilder.hpp"
#include "../coders/json.hpp" #include "../coders/json.hpp"
#include "../core_defs.hpp" #include "../core_defs.hpp"
#include "../data/dynamic.hpp" #include "../data/dynamic.hpp"
@ -26,9 +27,11 @@ static debug::Logger logger("content-loader");
ContentLoader::ContentLoader(ContentPack* pack) : pack(pack) { ContentLoader::ContentLoader(ContentPack* pack) : pack(pack) {
} }
bool ContentLoader::fixPackIndices(fs::path folder, bool ContentLoader::fixPackIndices(
fs::path folder,
dynamic::Map* indicesRoot, dynamic::Map* indicesRoot,
std::string contentSection) { std::string contentSection
) {
std::vector<std::string> detected; std::vector<std::string> detected;
std::vector<std::string> indexed; std::vector<std::string> indexed;
if (fs::is_directory(folder)) { 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("script-name", def.scriptName);
root->str("ui-layout", def.uiLayout); root->str("ui-layout", def.uiLayout);
root->num("inventory-size", def.inventorySize); 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) { if (def.hidden && def.pickingItem == def.name+BLOCK_ITEM_SUFFIX) {
def.pickingItem = CORE_EMPTY; 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); auto root = files::read_json(file);
BlockMaterial material {full}; root->str("steps-sound", def.stepsSound);
root->str("steps-sound", material.stepsSound); root->str("place-sound", def.placeSound);
root->str("place-sound", material.placeSound); root->str("break-sound", def.breakSound);
root->str("break-sound", material.breakSound);
return material;
} }
void ContentLoader::load(ContentBuilder& builder) { void ContentLoader::load(ContentBuilder& builder) {
logger.info() << "loading pack [" << pack->id << "]"; logger.info() << "loading pack [" << pack->id << "]";
auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack)); auto runtime = std::make_unique<ContentPackRuntime>(
builder.add(runtime); *pack, scripting::create_pack_environment(*pack)
);
env = runtime->getEnvironment(); env = runtime->getEnvironment();
ContentPackStats& stats = runtime->getStatsWriteable(); ContentPackStats& stats = runtime->getStatsWriteable();
builder.add(std::move(runtime));
fixPackIndices(); fixPackIndices();
@ -350,7 +357,9 @@ void ContentLoader::load(ContentBuilder& builder) {
std::string full = colon == std::string::npos ? pack->id + ":" + name : name; std::string full = colon == std::string::npos ? pack->id + ":" + name : name;
if (colon != std::string::npos) name[colon] = '/'; if (colon != std::string::npos) name[colon] = '/';
auto& def = builder.createBlock(full); 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); loadBlock(def, full, name);
stats.totalBlocks++; stats.totalBlocks++;
if (!def.hidden) { if (!def.hidden) {
@ -388,7 +397,7 @@ void ContentLoader::load(ContentBuilder& builder) {
for (auto entry : fs::directory_iterator(materialsDir)) { for (auto entry : fs::directory_iterator(materialsDir)) {
fs::path file = entry.path(); fs::path file = entry.path();
std::string name = pack->id+":"+file.stem().u8string(); std::string name = pack->id+":"+file.stem().u8string();
builder.add(loadBlockMaterial(file, name)); loadBlockMaterial(builder.createBlockMaterial(name), file);
} }
} }
} }

View File

@ -1,13 +1,15 @@
#ifndef CONTENT_CONTENT_LOADER_HPP_ #ifndef CONTENT_CONTENT_LOADER_HPP_
#define CONTENT_CONTENT_LOADER_HPP_ #define CONTENT_CONTENT_LOADER_HPP_
#include "../voxels/Block.hpp" #include "../typedefs.hpp"
#include <string> #include <string>
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
class Block;
struct BlockMaterial;
class ItemDef; class ItemDef;
struct ContentPack; struct ContentPack;
class ContentBuilder; class ContentBuilder;
@ -23,7 +25,7 @@ class ContentLoader {
void loadBlock(Block& def, std::string full, std::string name); void loadBlock(Block& def, std::string full, std::string name);
void loadCustomBlockModel(Block& def, dynamic::Map* primitives); void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
void loadItem(ItemDef& def, std::string full, std::string name); 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: public:
ContentLoader(ContentPack* pack); ContentLoader(ContentPack* pack);

View File

@ -12,17 +12,11 @@ class EnginePaths;
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace scripting {
class Environment;
}
class contentpack_error : public std::runtime_error { class contentpack_error : public std::runtime_error {
std::string packId; std::string packId;
fs::path folder; fs::path folder;
public: public:
contentpack_error(std::string packId, contentpack_error(std::string packId, fs::path folder, std::string message);
fs::path folder,
std::string message);
std::string getPackId() const; std::string getPackId() const;
fs::path getFolder() const; fs::path getFolder() const;
@ -31,11 +25,11 @@ public:
enum class DependencyLevel { enum class DependencyLevel {
required, // dependency must be installed required, // dependency must be installed
optional, // dependency will be installed if found 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 { struct DependencyPack {
DependencyLevel level; DependencyLevel level;
std::string id; std::string id;

View File

@ -2,13 +2,16 @@
#include "items/ItemDef.hpp" #include "items/ItemDef.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
#include "content/ContentBuilder.hpp"
#include "files/files.hpp"
#include "files/engine_paths.hpp"
#include "window/Window.hpp" #include "window/Window.hpp"
#include "window/Events.hpp" #include "window/Events.hpp"
#include "window/input.hpp" #include "window/input.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
// All in-game definitions (blocks, items, etc..) // 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& block = builder->createBlock("core:air");
block.replaceable = true; block.replaceable = true;
block.drawGroup = 1; block.drawGroup = 1;
@ -21,24 +24,11 @@ void corecontent::setup(ContentBuilder* builder) {
ItemDef& item = builder->createItem("core:empty"); ItemDef& item = builder->createItem("core:empty");
item.iconType = item_icon_type::none; item.iconType = item_icon_type::none;
}
void corecontent::setup_bindings() { auto bindsFile = paths->getResources()/fs::path("bindings.toml");
Events::bind(BIND_DEVTOOLS_CONSOLE, inputtype::keyboard, keycode::GRAVE_ACCENT); if (fs::is_regular_file(bindsFile)) {
Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W); Events::loadBindings(
Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S); bindsFile.u8string(), files::read_string(bindsFile)
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);
} }

View File

@ -10,6 +10,7 @@ inline const std::string TEXTURE_NOTFOUND = "notfound";
// built-in bindings // built-in bindings
inline const std::string BIND_DEVTOOLS_CONSOLE = "devtools.console"; 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_FORWARD = "movement.forward";
inline const std::string BIND_MOVE_BACK = "movement.back"; inline const std::string BIND_MOVE_BACK = "movement.back";
inline const std::string BIND_MOVE_LEFT = "movement.left"; 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_PLAYER_PICK = "player.pick";
inline const std::string BIND_HUD_INVENTORY = "hud.inventory"; inline const std::string BIND_HUD_INVENTORY = "hud.inventory";
class EnginePaths;
class ContentBuilder; class ContentBuilder;
namespace corecontent { namespace corecontent {
void setup_bindings(); void setup(EnginePaths* paths, ContentBuilder* builder);
void setup(ContentBuilder* builder);
} }
#endif // CORE_DEFS_HPP_ #endif // CORE_DEFS_HPP_

View File

@ -214,7 +214,7 @@ void Map::flag(const std::string& key, bool& dst) const {
} }
Map& Map::put(std::string key, const Value& value) { Map& Map::put(std::string key, const Value& value) {
values.emplace(key, value); values[key] = value;
return *this; return *this;
} }

View File

@ -6,6 +6,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <ostream>
#include <variant> #include <variant>
#include <stdexcept> #include <stdexcept>
#include <unordered_map> #include <unordered_map>
@ -151,6 +152,9 @@ namespace dynamic {
Map& put(std::string key, bool value) { Map& put(std::string key, bool value) {
return put(key, Value(static_cast<bool>(value))); return put(key, Value(static_cast<bool>(value)));
} }
Map& put(std::string key, const char* value) {
return put(key, Value(value));
}
Map& put(std::string key, const Value& value); Map& put(std::string key, const Value& value);
void remove(const std::string& key); void remove(const std::string& key);

View File

@ -44,13 +44,14 @@ void Logger::log(LogLevel level, const std::string& name, std::string message) {
auto ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000; auto ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000;
ss << " " << std::put_time(std::localtime(&tm), "%Y/%m/%d %T"); ss << " " << std::put_time(std::localtime(&tm), "%Y/%m/%d %T");
ss << '.' << std::setfill('0') << std::setw(3) << ms.count(); 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; ss << message;
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
auto string = ss.str(); auto string = ss.str();
if (file.good()) { if (file.good()) {
file << string << '\n'; file << string << '\n';
file.flush();
} }
std::cout << string << std::endl; std::cout << string << std::endl;
} }

View File

@ -9,6 +9,7 @@
#include "coders/imageio.hpp" #include "coders/imageio.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "coders/toml.hpp" #include "coders/toml.hpp"
#include "content/ContentBuilder.hpp"
#include "content/ContentLoader.hpp" #include "content/ContentLoader.hpp"
#include "core_defs.hpp" #include "core_defs.hpp"
#include "files/files.hpp" #include "files/files.hpp"
@ -34,6 +35,7 @@
#include "window/input.hpp" #include "window/input.hpp"
#include "window/Window.hpp" #include "window/Window.hpp"
#include "world/WorldGenerators.hpp" #include "world/WorldGenerators.hpp"
#include "settings.hpp"
#include <iostream> #include <iostream>
#include <assert.h> #include <assert.h>
@ -56,20 +58,20 @@ inline void create_channel(Engine* engine, std::string name, NumberSetting& sett
} }
engine->keepAlive(setting.observe([=](auto value) { engine->keepAlive(setting.observe([=](auto value) {
audio::get_channel(name)->setVolume(value*value); audio::get_channel(name)->setVolume(value*value);
})); }, true));
} }
Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths) Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths)
: settings(settings), settingsHandler(settingsHandler), paths(paths), : settings(settings), settingsHandler(settingsHandler), paths(paths),
interpreter(std::make_unique<cmd::CommandsInterpreter>()) interpreter(std::make_unique<cmd::CommandsInterpreter>())
{ {
corecontent::setup_bindings();
loadSettings(); loadSettings();
controller = std::make_unique<EngineController>(this); controller = std::make_unique<EngineController>(this);
if (Window::initialize(&this->settings.display)){ if (Window::initialize(&this->settings.display)){
throw initialize_error("could not initialize window"); throw initialize_error("could not initialize window");
} }
loadControls();
audio::initialize(settings.audio.enabled.get()); audio::initialize(settings.audio.enabled.get());
create_channel(this, "master", settings.audio.volumeMaster); create_channel(this, "master", settings.audio.volumeMaster);
create_channel(this, "regular", settings.audio.volumeRegular); create_channel(this, "regular", settings.audio.volumeRegular);
@ -93,6 +95,9 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin
addWorldGenerators(); addWorldGenerators();
scripting::initialize(this); scripting::initialize(this);
auto resdir = paths->getResources();
basePacks = files::read_list(resdir/fs::path("config/builtins.list"));
} }
void Engine::loadSettings() { void Engine::loadSettings() {
@ -102,11 +107,22 @@ void Engine::loadSettings() {
std::string text = files::read_string(settings_file); std::string text = files::read_string(settings_file);
toml::parse(settingsHandler, settings_file.string(), text); toml::parse(settingsHandler, settings_file.string(), text);
} }
}
void Engine::loadControls() {
fs::path controls_file = paths->getControlsFile(); fs::path controls_file = paths->getControlsFile();
if (fs::is_regular_file(controls_file)) { if (fs::is_regular_file(controls_file)) {
logger.info() << "loading controls"; logger.info() << "loading controls";
std::string text = files::read_string(controls_file); std::string text = files::read_string(controls_file);
Events::loadBindings(controls_file.u8string(), text); 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()); 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() { void Engine::loadContent() {
auto resdir = paths->getResources(); auto resdir = paths->getResources();
ContentBuilder contentBuilder; ContentBuilder contentBuilder;
corecontent::setup(&contentBuilder); corecontent::setup(paths, &contentBuilder);
paths->setContentPacks(&contentPacks); paths->setContentPacks(&contentPacks);
std::vector<std::string> names; std::vector<std::string> names;
@ -272,8 +298,12 @@ void Engine::loadContent() {
ContentLoader loader(&pack); ContentLoader loader(&pack);
loader.load(contentBuilder); loader.load(contentBuilder);
load_configs(pack.folder);
} }
content.reset(contentBuilder.build()); load_configs(paths->getResources());
content = contentBuilder.build();
resPaths = std::make_unique<ResPaths>(resdir, resRoots); resPaths = std::make_unique<ResPaths>(resdir, resRoots);
langs::setup(resdir, langs::current->getId(), contentPacks); langs::setup(resdir, langs::current->getId(), contentPacks);

View File

@ -2,7 +2,6 @@
#define ENGINE_HPP_ #define ENGINE_HPP_
#include "delegates.hpp" #include "delegates.hpp"
#include "settings.hpp"
#include "typedefs.hpp" #include "typedefs.hpp"
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
@ -27,6 +26,7 @@ class ResPaths;
class Batch2D; class Batch2D;
class EngineController; class EngineController;
class SettingsHandler; class SettingsHandler;
struct EngineSettings;
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -57,7 +57,7 @@ class Engine : public util::ObjectsKeeper {
std::recursive_mutex postRunnablesMutex; std::recursive_mutex postRunnablesMutex;
std::unique_ptr<EngineController> controller; std::unique_ptr<EngineController> controller;
std::unique_ptr<cmd::CommandsInterpreter> interpreter; std::unique_ptr<cmd::CommandsInterpreter> interpreter;
std::vector<std::string> basePacks {"base"}; std::vector<std::string> basePacks;
uint64_t frame = 0; uint64_t frame = 0;
double lastTime = 0.0; double lastTime = 0.0;
@ -65,6 +65,7 @@ class Engine : public util::ObjectsKeeper {
std::unique_ptr<gui::GUI> gui; std::unique_ptr<gui::GUI> gui;
void loadControls();
void loadSettings(); void loadSettings();
void saveSettings(); void saveSettings();
void updateTimers(); void updateTimers();

View File

@ -13,6 +13,7 @@
#include "../objects/Player.hpp" #include "../objects/Player.hpp"
#include "../physics/Hitbox.hpp" #include "../physics/Hitbox.hpp"
#include "../typedefs.hpp" #include "../typedefs.hpp"
#include "../settings.hpp"
#include "../util/data_io.hpp" #include "../util/data_io.hpp"
#include "../voxels/Block.hpp" #include "../voxels/Block.hpp"
#include "../voxels/Chunk.hpp" #include "../voxels/Chunk.hpp"

View File

@ -5,7 +5,6 @@
#include "files.hpp" #include "files.hpp"
#include "../typedefs.hpp" #include "../typedefs.hpp"
#include "../settings.hpp"
#include "../content/ContentPack.hpp" #include "../content/ContentPack.hpp"
#include "../voxels/Chunk.hpp" #include "../voxels/Chunk.hpp"
@ -24,6 +23,7 @@ class Player;
class Content; class Content;
class ContentIndices; class ContentIndices;
class World; class World;
struct DebugSettings;
namespace fs = std::filesystem; namespace fs = std::filesystem;

View File

@ -374,7 +374,7 @@ void WorldRegions::put(Chunk* chunk){
chunk->encode(), CHUNK_DATA_LEN, true); chunk->encode(), CHUNK_DATA_LEN, true);
// Writing lights cache // Writing lights cache
if (doWriteLights && chunk->isLighted()) { if (doWriteLights && chunk->flags.lighted) {
put(chunk->x, chunk->z, REGION_LAYER_LIGHTS, put(chunk->x, chunk->z, REGION_LAYER_LIGHTS,
chunk->lightmap.encode(), LIGHTMAP_DATA_LEN, true); chunk->lightmap.encode(), LIGHTMAP_DATA_LEN, true);
} }

View File

@ -10,7 +10,7 @@
#include "WorldFiles.hpp" #include "WorldFiles.hpp"
const fs::path SCREENSHOTS_FOLDER {"screenshots"}; 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"}; const fs::path SETTINGS_FILE {"settings.toml"};
fs::path EnginePaths::getUserfiles() const { fs::path EnginePaths::getUserfiles() const {
@ -60,6 +60,10 @@ fs::path EnginePaths::getControlsFile() {
return userfiles/fs::path(CONTROLS_FILE); return userfiles/fs::path(CONTROLS_FILE);
} }
fs::path EnginePaths::getControlsFileOld() {
return userfiles/fs::path("controls.json");
}
fs::path EnginePaths::getSettingsFile() { fs::path EnginePaths::getSettingsFile() {
return userfiles/fs::path(SETTINGS_FILE); return userfiles/fs::path(SETTINGS_FILE);
} }
@ -135,7 +139,7 @@ static fs::path toCanonic(fs::path path) {
return path; return path;
} }
fs::path EnginePaths::resolve(std::string path) { fs::path EnginePaths::resolve(std::string path, bool throwErr) {
size_t separator = path.find(':'); size_t separator = path.find(':');
if (separator == std::string::npos) { if (separator == std::string::npos) {
throw files_access_error("no entry point specified"); throw files_access_error("no entry point specified");
@ -161,8 +165,11 @@ fs::path EnginePaths::resolve(std::string path) {
} }
} }
} }
if (throwErr) {
throw files_access_error("unknown entry point '"+prefix+"'"); throw files_access_error("unknown entry point '"+prefix+"'");
} }
return fs::path(filename);
}
ResPaths::ResPaths(fs::path mainRoot, std::vector<PathsRoot> roots) ResPaths::ResPaths(fs::path mainRoot, std::vector<PathsRoot> roots)
: mainRoot(mainRoot), roots(roots) { : mainRoot(mainRoot), roots(roots) {

View File

@ -29,6 +29,7 @@ public:
fs::path getWorldFolder(); fs::path getWorldFolder();
fs::path getWorldFolder(const std::string& name); fs::path getWorldFolder(const std::string& name);
fs::path getControlsFile(); fs::path getControlsFile();
fs::path getControlsFileOld(); // TODO: remove in 0.22
fs::path getSettingsFile(); fs::path getSettingsFile();
bool isWorldNameUsed(std::string name); bool isWorldNameUsed(std::string name);
@ -39,7 +40,7 @@ public:
std::vector<fs::path> scanForWorlds(); std::vector<fs::path> scanForWorlds();
fs::path resolve(std::string path); fs::path resolve(std::string path, bool throwErr=true);
}; };
struct PathsRoot { struct PathsRoot {

View File

@ -1,6 +1,8 @@
#include "files.hpp" #include "files.hpp"
#include "../coders/commons.hpp"
#include "../coders/json.hpp" #include "../coders/json.hpp"
#include "../coders/toml.hpp"
#include "../coders/gzip.hpp" #include "../coders/gzip.hpp"
#include "../util/stringutil.hpp" #include "../util/stringutil.hpp"
#include "../data/dynamic.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<dynamic::Map> files::read_json(fs::path filename) { std::shared_ptr<dynamic::Map> files::read_json(fs::path filename) {
std::string text = files::read_string(filename); std::string text = files::read_string(filename);
try { try {
auto obj = json::parse(filename.string(), text); return json::parse(filename.string(), text);;
return obj;
} catch (const parsing_error& error) { } catch (const parsing_error& error) {
std::cerr << error.errorLog() << std::endl; std::cerr << error.errorLog() << std::endl;
throw std::runtime_error("could not to parse "+filename.string()); throw std::runtime_error("could not to parse "+filename.string());
@ -121,6 +122,10 @@ std::shared_ptr<dynamic::Map> files::read_binary_json(fs::path file) {
return json::from_binary(bytes.get(), size); return json::from_binary(bytes.get(), size);
} }
std::shared_ptr<dynamic::Map> files::read_toml(fs::path file) {
return toml::parse(file.u8string(), files::read_string(file));
}
std::vector<std::string> files::read_list(fs::path filename) { std::vector<std::string> files::read_list(fs::path filename) {
std::ifstream file(filename); std::ifstream file(filename);
if (!file) { if (!file) {

View File

@ -20,7 +20,7 @@ namespace files {
std::ifstream file; std::ifstream file;
size_t filelength; size_t filelength;
public: public:
rafile(std::filesystem::path filename); rafile(fs::path filename);
void seekg(std::streampos pos); void seekg(std::streampos pos);
void read(char* buffer, std::streamsize size); void read(char* buffer, std::streamsize size);
@ -44,10 +44,7 @@ namespace files {
/// @brief Write dynamic data to the JSON file /// @brief Write dynamic data to the JSON file
/// @param nice if true, human readable format will be used, otherwise minimal /// @param nice if true, human readable format will be used, otherwise minimal
bool write_json( bool write_json(fs::path filename, const dynamic::Map* obj, bool nice=true);
fs::path filename,
const dynamic::Map* obj,
bool nice=true);
/// @brief Write dynamic data to the binary JSON file /// @brief Write dynamic data to the binary JSON file
/// (see src/coders/binary_json_spec.md) /// (see src/coders/binary_json_spec.md)
@ -66,6 +63,7 @@ namespace files {
/// @param file *.json or *.bjson file /// @param file *.json or *.bjson file
std::shared_ptr<dynamic::Map> read_json(fs::path file); std::shared_ptr<dynamic::Map> read_json(fs::path file);
std::shared_ptr<dynamic::Map> read_binary_json(fs::path file); std::shared_ptr<dynamic::Map> read_binary_json(fs::path file);
std::shared_ptr<dynamic::Map> read_toml(fs::path file);
std::vector<std::string> read_list(fs::path file); std::vector<std::string> read_list(fs::path file);
} }

View File

@ -19,7 +19,7 @@ LevelFrontend::LevelFrontend(LevelController* controller, Assets* assets)
contentCache(std::make_unique<ContentGfxCache>(level->content, assets)) contentCache(std::make_unique<ContentGfxCache>(level->content, assets))
{ {
assets->store( assets->store(
BlocksPreview::build(contentCache.get(), assets, level->content).release(), BlocksPreview::build(contentCache.get(), assets, level->content),
"block-previews" "block-previews"
); );
controller->getPlayerController()->listenBlockInteraction( controller->getPlayerController()->listenBlockInteraction(

View File

@ -1,6 +1,7 @@
#include "../audio/audio.hpp" #include "../audio/audio.hpp"
#include "../delegates.hpp" #include "../delegates.hpp"
#include "../engine.hpp" #include "../engine.hpp"
#include "../settings.hpp"
#include "../graphics/core/Mesh.hpp" #include "../graphics/core/Mesh.hpp"
#include "../graphics/ui/elements/CheckBox.hpp" #include "../graphics/ui/elements/CheckBox.hpp"
#include "../graphics/ui/elements/TextBox.hpp" #include "../graphics/ui/elements/TextBox.hpp"
@ -19,6 +20,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <bitset>
using namespace gui; using namespace gui;
@ -71,15 +73,24 @@ std::shared_ptr<UINode> create_debug_panel(
L" visible: "+std::to_wstring(level->chunks->visible); L" visible: "+std::to_wstring(level->chunks->visible);
})); }));
panel->add(create_label([=](){ panel->add(create_label([=](){
auto* indices = level->content->getIndices();
auto def = indices->getBlockDef(player->selectedVoxel.id);
std::wstringstream stream; std::wstringstream stream;
stream << std::hex << player->selectedVoxel.states; stream << "r:" << player->selectedVoxel.state.rotation << " s:"
if (def) { << player->selectedVoxel.state.segment << " u:"
stream << L" (" << util::str2wstr_utf8(def->name) << L")"; << 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)+ return L"block: "+std::to_wstring(player->selectedVoxel.id)+
L" "+stream.str(); 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"};
}
})); }));
panel->add(create_label([=](){ panel->add(create_label([=](){
return L"seed: "+std::to_wstring(level->getWorld()->getSeed()); return L"seed: "+std::to_wstring(level->getWorld()->getSeed());
@ -137,8 +148,8 @@ std::shared_ptr<UINode> create_debug_panel(
} }
{ {
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8); auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
bar->setSupplier([=]() {return WorldRenderer::fog;}); bar->setSupplier([=]() {return level->getWorld()->fog;});
bar->setConsumer([=](double val) {WorldRenderer::fog = val;}); bar->setConsumer([=](double val) {level->getWorld()->fog = val;});
panel->add(bar); panel->add(bar);
} }
{ {

View File

@ -333,7 +333,7 @@ void Hud::openInventory(
if (blockinv == nullptr) { if (blockinv == nullptr) {
blockinv = level->inventories->createVirtual(blockUI->getSlotsCount()); 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); blockUI->bind(blockinv, content);
blockPos = block; blockPos = block;
currentblockid = level->chunks->get(block.x, block.y, block.z)->id; currentblockid = level->chunks->get(block.x, block.y, block.z)->id;

View File

@ -62,9 +62,11 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) {
auto file = engine->getResPaths()->find("layouts/pages/"+name+".xml"); auto file = engine->getResPaths()->find("layouts/pages/"+name+".xml");
auto fullname = "core:pages/"+name; auto fullname = "core:pages/"+name;
auto document = UiDocument::read(scripting::get_root_environment(), fullname, file).release(); auto document_ptr = UiDocument::read(
engine->getAssets()->store(document, fullname); 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)); scripting::on_ui_open(document, std::move(args));
return document->getRoot(); return document->getRoot();
}; };
@ -75,8 +77,11 @@ UiDocument* menus::show(Engine* engine, const std::string& name, std::vector<dyn
auto file = engine->getResPaths()->find("layouts/"+name+".xml"); auto file = engine->getResPaths()->find("layouts/"+name+".xml");
auto fullname = "core:layouts/"+name; auto fullname = "core:layouts/"+name;
auto document = UiDocument::read(scripting::get_root_environment(), fullname, file).release(); auto document_ptr = UiDocument::read(
engine->getAssets()->store(document, fullname); 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)); scripting::on_ui_open(document, std::move(args));
menu->addPage(name, document->getRoot()); menu->addPage(name, document->getRoot());
menu->setPage(name); menu->setPage(name);

View File

@ -1,27 +1,30 @@
#include "LevelScreen.hpp" #include "LevelScreen.hpp"
#include "../../core_defs.hpp"
#include "../hud.hpp" #include "../hud.hpp"
#include "../LevelFrontend.hpp" #include "../LevelFrontend.hpp"
#include "../../debug/Logger.hpp"
#include "../../audio/audio.hpp" #include "../../audio/audio.hpp"
#include "../../coders/imageio.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/DrawContext.hpp"
#include "../../graphics/core/Viewport.hpp"
#include "../../graphics/core/ImageData.hpp" #include "../../graphics/core/ImageData.hpp"
#include "../../graphics/ui/GUI.hpp" #include "../../graphics/core/PostProcessing.hpp"
#include "../../graphics/ui/elements/Menu.hpp" #include "../../graphics/core/Viewport.hpp"
#include "../../graphics/render/WorldRenderer.hpp" #include "../../graphics/render/WorldRenderer.hpp"
#include "../../graphics/ui/elements/Menu.hpp"
#include "../../graphics/ui/GUI.hpp"
#include "../../logic/LevelController.hpp" #include "../../logic/LevelController.hpp"
#include "../../logic/scripting/scripting_hud.hpp" #include "../../logic/scripting/scripting_hud.hpp"
#include "../../util/stringutil.hpp"
#include "../../physics/Hitbox.hpp" #include "../../physics/Hitbox.hpp"
#include "../../voxels/Chunks.hpp" #include "../../voxels/Chunks.hpp"
#include "../../world/Level.hpp"
#include "../../world/World.hpp"
#include "../../window/Camera.hpp" #include "../../window/Camera.hpp"
#include "../../window/Events.hpp" #include "../../window/Events.hpp"
#include "../../window/Window.hpp" #include "../../window/Window.hpp"
#include "../../engine.hpp" #include "../../world/Level.hpp"
#include "../../world/World.hpp"
static debug::Logger logger("level-screen"); static debug::Logger logger("level-screen");
@ -45,6 +48,9 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> level)
keepAlive(settings.camera.fov.observe([=](double value) { keepAlive(settings.camera.fov.observe([=](double value) {
controller->getPlayer()->camera->setFov(glm::radians(value)); controller->getPlayer()->camera->setFov(glm::radians(value));
})); }));
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
controller->getLevel()->chunks->saveAndClear();
}));
animator = std::make_unique<TextureAnimator>(); animator = std::make_unique<TextureAnimator>();
animator->addAnimations(assets->getAnimations()); animator->addAnimations(assets->getAnimations());
@ -55,15 +61,18 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> level)
void LevelScreen::initializeContent() { void LevelScreen::initializeContent() {
auto content = controller->getLevel()->content; auto content = controller->getLevel()->content;
for (auto& entry : content->getPacks()) { for (auto& entry : content->getPacks()) {
auto pack = entry.second.get(); initializePack(entry.second.get());
}
scripting::on_frontend_init(hud.get());
}
void LevelScreen::initializePack(ContentPackRuntime* pack) {
const ContentPack& info = pack->getInfo(); const ContentPack& info = pack->getInfo();
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua"); fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
if (fs::is_regular_file(scriptFile)) { if (fs::is_regular_file(scriptFile)) {
scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile); scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile);
} }
} }
scripting::on_frontend_init(hud.get());
}
LevelScreen::~LevelScreen() { LevelScreen::~LevelScreen() {
saveWorldPreview(); saveWorldPreview();
@ -83,8 +92,11 @@ void LevelScreen::saveWorldPreview() {
// camera special copy for world preview // camera special copy for world preview
Camera camera = *player->camera; Camera camera = *player->camera;
camera.setFov(glm::radians(70.0f)); camera.setFov(glm::radians(70.0f));
DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get());
Viewport viewport(previewSize * 1.5, previewSize); 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()); worldRenderer->draw(ctx, &camera, false, postProcessing.get());
auto image = postProcessing->toImage(); auto image = postProcessing->toImage();
@ -106,9 +118,6 @@ void LevelScreen::updateHotkeys() {
if (Events::jpressed(keycode::F3)) { if (Events::jpressed(keycode::F3)) {
controller->getPlayer()->debug = !controller->getPlayer()->debug; controller->getPlayer()->debug = !controller->getPlayer()->debug;
} }
if (Events::jpressed(keycode::F5)) {
controller->getLevel()->chunks->saveAndClear();
}
} }
void LevelScreen::update(float delta) { void LevelScreen::update(float delta) {
@ -138,7 +147,7 @@ void LevelScreen::update(float delta) {
controller->getLevel()->getWorld()->updateTimers(delta); controller->getLevel()->getWorld()->updateTimers(delta);
animator->update(delta); animator->update(delta);
} }
controller->update(delta, !inputLocked, hud->isPause()); controller->update(glm::min(delta, 0.2f), !inputLocked, hud->isPause());
hud->update(hudVisible); hud->update(hudVisible);
} }

View File

@ -12,21 +12,23 @@ class LevelController;
class WorldRenderer; class WorldRenderer;
class TextureAnimator; class TextureAnimator;
class PostProcessing; class PostProcessing;
class ContentPackRuntime;
class Level; class Level;
class LevelScreen : public Screen { class LevelScreen : public Screen {
std::unique_ptr<LevelFrontend> frontend; std::unique_ptr<LevelFrontend> frontend;
std::unique_ptr<Hud> hud;
std::unique_ptr<LevelController> controller; std::unique_ptr<LevelController> controller;
std::unique_ptr<WorldRenderer> worldRenderer; std::unique_ptr<WorldRenderer> worldRenderer;
std::unique_ptr<TextureAnimator> animator; std::unique_ptr<TextureAnimator> animator;
std::unique_ptr<PostProcessing> postProcessing; std::unique_ptr<PostProcessing> postProcessing;
std::unique_ptr<Hud> hud;
void saveWorldPreview(); void saveWorldPreview();
bool hudVisible = true; bool hudVisible = true;
void updateHotkeys(); void updateHotkeys();
void initializeContent(); void initializeContent();
void initializePack(ContentPackRuntime* pack);
public: public:
LevelScreen(Engine* engine, std::unique_ptr<Level> level); LevelScreen(Engine* engine, std::unique_ptr<Level> level);
~LevelScreen(); ~LevelScreen();

View File

@ -5,6 +5,7 @@
#include "../../graphics/core/Batch2D.hpp" #include "../../graphics/core/Batch2D.hpp"
#include "../../graphics/core/Shader.hpp" #include "../../graphics/core/Shader.hpp"
#include "../../graphics/core/Texture.hpp" #include "../../graphics/core/Texture.hpp"
#include "../../maths/UVRegion.hpp"
#include "../../window/Window.hpp" #include "../../window/Window.hpp"
#include "../../window/Camera.hpp" #include "../../window/Camera.hpp"
#include "../../engine.hpp" #include "../../engine.hpp"

View File

@ -13,19 +13,18 @@ Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
{2}, {2}, {4}, {0} {2}, {2}, {4}, {0}
}; };
buffer = new float[capacity * B2D_VERTEX_SIZE]; buffer = std::make_unique<float[]>(capacity * B2D_VERTEX_SIZE);
mesh = std::make_unique<Mesh>(buffer, 0, attrs); mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
index = 0; index = 0;
ubyte pixels[] = { ubyte pixels[] = {
0xFF, 0xFF, 0xFF, 0xFF 0xFF, 0xFF, 0xFF, 0xFF
}; };
blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888); blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888);
_texture = nullptr; currentTexture = nullptr;
} }
Batch2D::~Batch2D(){ Batch2D::~Batch2D(){
delete[] buffer;
} }
void Batch2D::setPrimitive(DrawPrimitive primitive) { void Batch2D::setPrimitive(DrawPrimitive primitive) {
@ -37,7 +36,7 @@ void Batch2D::setPrimitive(DrawPrimitive primitive) {
} }
void Batch2D::begin(){ void Batch2D::begin(){
_texture = nullptr; currentTexture = nullptr;
blank->bind(); blank->bind();
color = glm::vec4(1.0f); color = glm::vec4(1.0f);
primitive = DrawPrimitive::triangle; primitive = DrawPrimitive::triangle;
@ -73,15 +72,17 @@ void Batch2D::vertex(
} }
void Batch2D::texture(Texture* new_texture){ void Batch2D::texture(Texture* new_texture){
if (_texture == new_texture) if (currentTexture == new_texture) {
return; return;
}
flush(); flush();
_texture = new_texture; currentTexture = new_texture;
if (new_texture == nullptr) if (new_texture == nullptr) {
blank->bind(); blank->bind();
else } else {
new_texture->bind(); new_texture->bind();
} }
}
void Batch2D::untexture() { void Batch2D::untexture() {
texture(nullptr); texture(nullptr);
@ -327,7 +328,7 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index
void Batch2D::flush() { void Batch2D::flush() {
if (index == 0) if (index == 0)
return; return;
mesh->reload(buffer, index / B2D_VERTEX_SIZE); mesh->reload(buffer.get(), index / B2D_VERTEX_SIZE);
mesh->draw(gl::to_glenum(primitive)); mesh->draw(gl::to_glenum(primitive));
index = 0; index = 0;
} }

View File

@ -12,13 +12,13 @@ class Texture;
struct UVRegion; struct UVRegion;
class Batch2D { class Batch2D {
float* buffer; std::unique_ptr<float[]> buffer;
size_t capacity; size_t capacity;
std::unique_ptr<Mesh> mesh; std::unique_ptr<Mesh> mesh;
std::unique_ptr<Texture> blank; std::unique_ptr<Texture> blank;
size_t index; size_t index;
glm::vec4 color; glm::vec4 color;
Texture* _texture; Texture* currentTexture;
DrawPrimitive primitive = DrawPrimitive::triangle; DrawPrimitive primitive = DrawPrimitive::triangle;
void setPrimitive(DrawPrimitive primitive); void setPrimitive(DrawPrimitive primitive);

View File

@ -14,28 +14,29 @@ Batch3D::Batch3D(size_t capacity)
{3}, {2}, {4}, {0} {3}, {2}, {4}, {0}
}; };
buffer = new float[capacity * B3D_VERTEX_SIZE]; buffer = std::make_unique<float[]>(capacity * B3D_VERTEX_SIZE);
mesh = std::make_unique<Mesh>(buffer, 0, attrs); mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
index = 0; index = 0;
ubyte pixels[] = { ubyte pixels[] = {
255, 255, 255, 255, 255, 255, 255, 255,
}; };
blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888); blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888);
_texture = nullptr; currentTexture = nullptr;
} }
Batch3D::~Batch3D(){ Batch3D::~Batch3D(){
delete[] buffer;
} }
void Batch3D::begin(){ void Batch3D::begin(){
_texture = nullptr; currentTexture = nullptr;
blank->bind(); blank->bind();
} }
void Batch3D::vertex(float x, float y, float z, float u, float v, void Batch3D::vertex(
float r, float g, float b, float a) { float x, float y, float z, float u, float v,
float r, float g, float b, float a
) {
buffer[index++] = x; buffer[index++] = x;
buffer[index++] = y; buffer[index++] = y;
buffer[index++] = z; 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++] = b;
buffer[index++] = a; buffer[index++] = a;
} }
void Batch3D::vertex(glm::vec3 coord, float u, float v, void Batch3D::vertex(
float r, float g, float b, float a) { glm::vec3 coord, float u, float v,
float r, float g, float b, float a
) {
buffer[index++] = coord.x; buffer[index++] = coord.x;
buffer[index++] = coord.y; buffer[index++] = coord.y;
buffer[index++] = coord.z; buffer[index++] = coord.z;
@ -58,9 +61,11 @@ void Batch3D::vertex(glm::vec3 coord, float u, float v,
buffer[index++] = b; buffer[index++] = b;
buffer[index++] = a; buffer[index++] = a;
} }
void Batch3D::vertex(glm::vec3 point, void Batch3D::vertex(
glm::vec3 point,
glm::vec2 uvpoint, glm::vec2 uvpoint,
float r, float g, float b, float a) { float r, float g, float b, float a
) {
buffer[index++] = point.x; buffer[index++] = point.x;
buffer[index++] = point.y; buffer[index++] = point.y;
buffer[index++] = point.z; buffer[index++] = point.z;
@ -99,10 +104,10 @@ void Batch3D::face(
} }
void Batch3D::texture(Texture* new_texture){ void Batch3D::texture(Texture* new_texture){
if (_texture == new_texture) if (currentTexture == new_texture)
return; return;
flush(); flush();
_texture = new_texture; currentTexture = new_texture;
if (new_texture == nullptr) if (new_texture == nullptr)
blank->bind(); blank->bind();
else else
@ -166,7 +171,9 @@ inline glm::vec4 do_tint(float value) {
return glm::vec4(value, value, value, 1.0f); 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( face(
glm::vec3(-w * 0.25f, 0.0f, -w * 0.25f), glm::vec3(-w * 0.25f, 0.0f, -w * 0.25f),
w, h, w, h,
@ -244,13 +251,13 @@ void Batch3D::point(glm::vec3 coord, glm::vec4 tint) {
} }
void Batch3D::flush() { void Batch3D::flush() {
mesh->reload(buffer, index / B3D_VERTEX_SIZE); mesh->reload(buffer.get(), index / B3D_VERTEX_SIZE);
mesh->draw(); mesh->draw();
index = 0; index = 0;
} }
void Batch3D::flushPoints() { void Batch3D::flushPoints() {
mesh->reload(buffer, index / B3D_VERTEX_SIZE); mesh->reload(buffer.get(), index / B3D_VERTEX_SIZE);
mesh->draw(GL_POINTS); mesh->draw(GL_POINTS);
index = 0; index = 0;
} }

View File

@ -12,28 +12,35 @@ class Mesh;
class Texture; class Texture;
class Batch3D { class Batch3D {
float* buffer; std::unique_ptr<float[]> buffer;
size_t capacity; size_t capacity;
std::unique_ptr<Mesh> mesh; std::unique_ptr<Mesh> mesh;
std::unique_ptr<Texture> blank; std::unique_ptr<Texture> blank;
size_t index; size_t index;
Texture* _texture; Texture* currentTexture;
void vertex(float x, float y, float z, void vertex(
float x, float y, float z,
float u, float v, float u, float v,
float r, float g, float b, float a); float r, float g, float b, float a
void vertex(glm::vec3 coord, );
void vertex(
glm::vec3 coord,
float u, float v, float u, float v,
float r, float g, float b, float a); 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 vertex(
glm::vec3 point, glm::vec2 uvpoint,
void face(const glm::vec3& coord, float w, float h, 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& axisX,
const glm::vec3& axisY, const glm::vec3& axisY,
const UVRegion& region, const UVRegion& region,
const glm::vec4& tint); const glm::vec4& tint
);
public: public:
Batch3D(size_t capacity); Batch3D(size_t capacity);

View File

@ -98,7 +98,7 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
return glshader(new GLuint(shader), shader_deleter); return glshader(new GLuint(shader), shader_deleter);
} }
Shader* Shader::create( std::unique_ptr<Shader> Shader::create(
const std::string& vertexFile, const std::string& vertexFile,
const std::string& fragmentFile, const std::string& fragmentFile,
const std::string& vertexCode, const std::string& vertexCode,
@ -125,5 +125,5 @@ Shader* Shader::create(
"shader program linking failed:\n"+std::string(infoLog) "shader program linking failed:\n"+std::string(infoLog)
); );
} }
return new Shader(id); return std::make_unique<Shader>(id);
} }

View File

@ -4,6 +4,7 @@
#include "../../typedefs.hpp" #include "../../typedefs.hpp"
#include <string> #include <string>
#include <memory>
#include <unordered_map> #include <unordered_map>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -36,7 +37,7 @@ public:
/// @param vertexSource vertex shader source code /// @param vertexSource vertex shader source code
/// @param fragmentSource fragment shader source code /// @param fragmentSource fragment shader source code
/// @return linked shader program containing vertex and fragment shaders /// @return linked shader program containing vertex and fragment shaders
static Shader* create( static std::unique_ptr<Shader> create(
const std::string& vertexFile, const std::string& vertexFile,
const std::string& fragmentFile, const std::string& fragmentFile,
const std::string& vertexSource, const std::string& vertexSource,

View File

@ -4,7 +4,6 @@
#include "../../typedefs.hpp" #include "../../typedefs.hpp"
#include "ImageData.hpp" #include "ImageData.hpp"
#include <string>
#include <memory> #include <memory>
class Texture { class Texture {

View File

@ -44,13 +44,18 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
break; break;
case BlockModel::aabb: case BlockModel::aabb:
{ {
glm::vec3 hitbox = glm::vec3(); glm::vec3 hitbox {};
for (const auto& box : def->hitboxes) for (const auto& box : def->hitboxes) {
hitbox = glm::max(hitbox, box.size()); 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)); shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
batch->blockCube(hitbox * glm::vec3(size * 0.63f), batch->cube(
texfaces, glm::vec4(1.0f), !def->rt.emissive); -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(); batch->flush();
break; break;
@ -138,7 +143,7 @@ std::unique_ptr<Atlas> BlocksPreview::build(
shader->uniformMatrix("u_projview", shader->uniformMatrix("u_projview",
glm::ortho(0.0f, float(iconSize), 0.0f, float(iconSize), glm::ortho(0.0f, float(iconSize), 0.0f, float(iconSize),
-100.0f, 100.0f) * -100.0f, 100.0f) *
glm::lookAt(glm::vec3(2, 2, 2), glm::lookAt(glm::vec3(0.57735f),
glm::vec3(0.0f), glm::vec3(0.0f),
glm::vec3(0, 1, 0))); glm::vec3(0, 1, 0)));

View File

@ -219,10 +219,13 @@ void BlocksRenderer::blockXSprite(int x, int y, int z,
// HINT: texture faces order: {east, west, bottom, top, south, north} // HINT: texture faces order: {east, west, bottom, top, south, north}
/* AABB blocks render method */ /* AABB blocks render method */
void BlocksRenderer::blockAABB(const ivec3& icoord, void BlocksRenderer::blockAABB(
const ivec3& icoord,
const UVRegion(&texfaces)[6], const UVRegion(&texfaces)[6],
const Block* block, ubyte rotation, const Block* block,
bool lights) { ubyte rotation,
bool lights
) {
if (block->hitboxes.empty()) { if (block->hitboxes.empty()) {
return; return;
} }
@ -301,11 +304,13 @@ void BlocksRenderer::blockCustomModel(const ivec3& icoord,
} }
/* Fastest solid shaded blocks render method */ /* Fastest solid shaded blocks render method */
void BlocksRenderer::blockCube(int x, int y, int z, void BlocksRenderer::blockCube(
int x, int y, int z,
const UVRegion(&texfaces)[6], const UVRegion(&texfaces)[6],
const Block* block, const Block* block,
ubyte states, blockstate states,
bool lights) { bool lights
) {
ubyte group = block->drawGroup; ubyte group = block->drawGroup;
vec3 X(1, 0, 0); vec3 X(1, 0, 0);
@ -314,7 +319,7 @@ void BlocksRenderer::blockCube(int x, int y, int z,
vec3 coord(x, y, z); vec3 coord(x, y, z);
if (block->rotatable) { if (block->rotatable) {
auto& rotations = block->rotations; auto& rotations = block->rotations;
auto& orient = rotations.variants[states & BLOCK_ROT_MASK]; auto& orient = rotations.variants[states.rotation];
X = orient.axisX; X = orient.axisX;
Y = orient.axisY; Y = orient.axisY;
Z = orient.axisZ; Z = orient.axisZ;
@ -423,7 +428,7 @@ void BlocksRenderer::render(const voxel* voxels) {
int z = (i / CHUNK_D) % CHUNK_W; int z = (i / CHUNK_D) % CHUNK_W;
switch (def.model) { switch (def.model) {
case BlockModel::block: 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; break;
case BlockModel::xsprite: { case BlockModel::xsprite: {
blockXSprite(x, y, z, vec3(1.0f), blockXSprite(x, y, z, vec3(1.0f),
@ -431,11 +436,11 @@ void BlocksRenderer::render(const voxel* voxels) {
break; break;
} }
case BlockModel::aabb: { 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; break;
} }
case BlockModel::custom: { 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; break;
} }
default: default:

View File

@ -70,16 +70,33 @@ class BlocksRenderer {
const UVRegion& texreg, const UVRegion& texreg,
bool lights); bool lights);
void blockCube(int x, int y, int z, const UVRegion(&faces)[6], const Block* block, ubyte states, bool lights); void blockCube(
void blockAABB(const glm::ivec3& coord, 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 UVRegion(&faces)[6],
const Block* block, const Block* block,
ubyte rotation, ubyte rotation,
bool lights); 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, void blockXSprite(
const Block* block, ubyte rotation, int x, int y, int z,
bool lights); 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 isOpenForLight(int x, int y, int z) const;
bool isOpen(int x, int y, int z, ubyte group) const; bool isOpen(int x, int y, int z, ubyte group) const;

View File

@ -56,19 +56,16 @@ ChunksRenderer::~ChunksRenderer() {
} }
std::shared_ptr<Mesh> ChunksRenderer::render(std::shared_ptr<Chunk> chunk, bool important) { std::shared_ptr<Mesh> ChunksRenderer::render(std::shared_ptr<Chunk> chunk, bool important) {
chunk->setModified(false); chunk->flags.modified = false;
if (important) { if (important) {
auto mesh = renderer->render(chunk.get(), level->chunksStorage.get()); auto mesh = renderer->render(chunk.get(), level->chunksStorage.get());
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh; meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
return mesh; return mesh;
} }
glm::ivec2 key(chunk->x, chunk->z); glm::ivec2 key(chunk->x, chunk->z);
if (inwork.find(key) != inwork.end()) { if (inwork.find(key) != inwork.end()) {
return nullptr; return nullptr;
} }
inwork[key] = true; inwork[key] = true;
threadPool.enqueueJob(chunk); threadPool.enqueueJob(chunk);
return nullptr; return nullptr;
@ -86,7 +83,7 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(std::shared_ptr<Chunk> chunk,
if (found == meshes.end()) { if (found == meshes.end()) {
return render(chunk, important); return render(chunk, important);
} }
if (chunk->isModified()) { if (chunk->flags.modified) {
render(chunk, important); render(chunk, important);
} }
return found->second; return found->second;

View File

@ -37,6 +37,9 @@
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
bool WorldRenderer::showChunkBorders = false; bool WorldRenderer::showChunkBorders = false;
WorldRenderer::WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player) WorldRenderer::WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player)
@ -76,7 +79,7 @@ bool WorldRenderer::drawChunk(
bool culling bool culling
){ ){
auto chunk = level->chunks->chunks[index]; auto chunk = level->chunks->chunks[index];
if (!chunk->isLighted()) { if (!chunk->flags.lighted) {
return false; return false;
} }
float distance = glm::distance( float distance = glm::distance(
@ -160,6 +163,7 @@ void WorldRenderer::renderLevel(
shader->use(); shader->use();
shader->uniformMatrix("u_proj", camera->getProjection()); shader->uniformMatrix("u_proj", camera->getProjection());
shader->uniformMatrix("u_view", camera->getView()); shader->uniformMatrix("u_view", camera->getView());
shader->uniform1f("u_timer", Window::time());
shader->uniform1f("u_gamma", settings.graphics.gamma.get()); shader->uniform1f("u_gamma", settings.graphics.gamma.get());
shader->uniform1f("u_fogFactor", fogFactor); shader->uniform1f("u_fogFactor", fogFactor);
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve.get()); shader->uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
@ -194,19 +198,19 @@ void WorldRenderer::renderBlockSelection(Camera* camera, Shader* linesShader) {
auto indices = level->content->getIndices(); auto indices = level->content->getIndices();
blockid_t id = PlayerController::selectedBlockId; blockid_t id = PlayerController::selectedBlockId;
auto block = indices->getBlockDef(id); 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 point = PlayerController::selectedPointPosition;
const glm::vec3 norm = PlayerController::selectedBlockNormal; const glm::vec3 norm = PlayerController::selectedBlockNormal;
const std::vector<AABB>& hitboxes = block->rotatable const std::vector<AABB>& hitboxes = block->rotatable
? block->rt.hitboxes[PlayerController::selectedBlockStates] ? block->rt.hitboxes[PlayerController::selectedBlockRotation]
: block->hitboxes; : block->hitboxes;
linesShader->use(); linesShader->use();
linesShader->uniformMatrix("u_projview", camera->getProjView()); linesShader->uniformMatrix("u_projview", camera->getProjView());
lineBatch->lineWidth(2.0f); lineBatch->lineWidth(2.0f);
for (auto& hitbox: hitboxes) { 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(); const glm::vec3 size = hitbox.size();
lineBatch->box(center, size + glm::vec3(0.02), glm::vec4(0.f, 0.f, 0.f, 0.5f)); lineBatch->box(center, size + glm::vec3(0.02), glm::vec4(0.f, 0.f, 0.f, 0.5f));
if (player->debug) { if (player->debug) {
@ -274,11 +278,12 @@ void WorldRenderer::draw(
bool hudVisible, bool hudVisible,
PostProcessing* postProcessing PostProcessing* postProcessing
){ ){
auto world = level->getWorld();
const Viewport& vp = pctx.getViewport(); const Viewport& vp = pctx.getViewport();
camera->aspect = vp.getWidth() / static_cast<float>(vp.getHeight()); camera->aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
const EngineSettings& settings = engine->getSettings(); 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(); Assets* assets = engine->getAssets();
Shader* linesShader = assets->getShader("lines"); Shader* linesShader = assets->getShader("lines");
@ -291,7 +296,7 @@ void WorldRenderer::draw(
Window::clearDepth(); Window::clearDepth();
// Drawing background sky plane // 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 // 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(); lineBatch->render();
} }
float WorldRenderer::fog = 0.0f;

View File

@ -8,8 +8,6 @@
#include <string> #include <string>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
class Level; class Level;
class Player; class Player;
@ -76,8 +74,6 @@ public:
Camera* camera, Camera* camera,
const EngineSettings& settings const EngineSettings& settings
); );
static float fog;
}; };

View File

@ -1,9 +1,14 @@
#include "GUI.hpp" #include "GUI.hpp"
#include "gui_util.hpp"
#include "elements/UINode.hpp" #include "elements/UINode.hpp"
#include "elements/Label.hpp"
#include "elements/Menu.hpp" #include "elements/Menu.hpp"
#include "../../assets/Assets.hpp" #include "../../assets/Assets.hpp"
#include "../../frontend/UiDocument.hpp" #include "../../frontend/UiDocument.hpp"
#include "../../frontend/locale.hpp"
#include "../../graphics/core/Batch2D.hpp" #include "../../graphics/core/Batch2D.hpp"
#include "../../graphics/core/Shader.hpp" #include "../../graphics/core/Shader.hpp"
#include "../../graphics/core/DrawContext.hpp" #include "../../graphics/core/DrawContext.hpp"
@ -27,6 +32,15 @@ GUI::GUI() {
menu->setId("menu"); menu->setId("menu");
container->add(menu); container->add(menu);
container->setScrollable(false); container->setScrollable(false);
tooltip = guiutil::create(
"<container color='#000000A0' interactive='false' z-index='999'>"
"<label id='tooltip.label' pos='2' autoresize='true'></label>"
"</container>"
);
store("tooltip", tooltip);
store("tooltip.label", UINode::find(tooltip, "tooltip.label"));
container->add(tooltip);
} }
GUI::~GUI() { GUI::~GUI() {
@ -37,7 +51,7 @@ std::shared_ptr<Menu> GUI::getMenu() {
} }
void GUI::onAssetsLoad(Assets* assets) { void GUI::onAssetsLoad(Assets* assets) {
assets->store(new UiDocument( assets->store(std::make_unique<UiDocument>(
"core:root", "core:root",
uidocscript {}, uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container), std::dynamic_pointer_cast<gui::UINode>(container),
@ -45,10 +59,41 @@ void GUI::onAssetsLoad(Assets* assets) {
), "core:root"); ), "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<gui::Label>(get("tooltip.label"));
const auto& text = hover->getTooltip();
if (text.empty() && tooltip->isVisible()) {
return resetTooltip();
}
if (label && !text.empty()) {
tooltip->setVisible(true);
label->setText(langs::get(text));
auto size = label->getSize()+glm::vec2(4.0f);
auto pos = Events::cursor+glm::vec2(10.0f);
auto rootSize = container->getSize();
pos.x = glm::min(pos.x, rootSize.x-size.x);
pos.y = glm::min(pos.y, rootSize.y-size.y);
tooltip->setSize(size);
tooltip->setPos(pos);
}
}
tooltipTimer += delta;
}
/// @brief Mouse related input and logic handling /// @brief Mouse related input and logic handling
void GUI::actMouse(float delta) { void GUI::actMouse(float delta) {
float mouseDelta = glm::length(Events::delta);
doubleClicked = false; doubleClicked = false;
doubleClickTimer += delta + glm::length(Events::delta) * 0.1f; doubleClickTimer += delta + mouseDelta * 0.1f;
auto hover = container->getAt(Events::cursor, nullptr); auto hover = container->getAt(Events::cursor, nullptr);
if (this->hover && this->hover != hover) { if (this->hover && this->hover != hover) {
@ -134,8 +179,14 @@ void GUI::act(float delta, const Viewport& vp) {
container->act(delta); container->act(delta);
auto prevfocus = focus; auto prevfocus = focus;
updateTooltip(delta);
if (!Events::_cursor_locked) { if (!Events::_cursor_locked) {
actMouse(delta); actMouse(delta);
} else {
if (hover) {
hover->setHover(false);
hover = nullptr;
}
} }
if (focus) { if (focus) {

View File

@ -54,21 +54,25 @@ namespace gui {
/// @brief The main UI controller /// @brief The main UI controller
class GUI { class GUI {
std::shared_ptr<Container> container; std::shared_ptr<Container> container;
std::shared_ptr<UINode> hover = nullptr; std::shared_ptr<UINode> hover;
std::shared_ptr<UINode> pressed = nullptr; std::shared_ptr<UINode> pressed;
std::shared_ptr<UINode> focus = nullptr; std::shared_ptr<UINode> focus;
std::shared_ptr<UINode> tooltip;
std::unordered_map<std::string, std::shared_ptr<UINode>> storage; std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
std::unique_ptr<Camera> uicamera; std::unique_ptr<Camera> uicamera;
std::shared_ptr<Menu> menu; std::shared_ptr<Menu> menu;
std::queue<runnable> postRunnables; std::queue<runnable> postRunnables;
float tooltipTimer = 0.0f;
float doubleClickTimer = 0.0f; float doubleClickTimer = 0.0f;
float doubleClickDelay = 0.5f; float doubleClickDelay = 0.5f;
bool doubleClicked = false; bool doubleClicked = false;
void actMouse(float delta); void actMouse(float delta);
void actFocused(); void actFocused();
void updateTooltip(float delta);
void resetTooltip();
public: public:
GUI(); GUI();
~GUI(); ~GUI();

View File

@ -7,7 +7,8 @@
using namespace gui; using namespace gui;
CheckBox::CheckBox(bool checked) : UINode(glm::vec2(32.0f)), checked(checked) { 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*) { void CheckBox::draw(const DrawContext* pctx, Assets*) {

View File

@ -6,7 +6,6 @@
namespace gui { namespace gui {
class CheckBox : public UINode { class CheckBox : public UINode {
protected: protected:
glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f};
glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f}; glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f};
boolsupplier supplier = nullptr; boolsupplier supplier = nullptr;
boolconsumer consumer = nullptr; boolconsumer consumer = nullptr;
@ -51,6 +50,11 @@ namespace gui {
virtual bool isChecked() const { virtual bool isChecked() const {
return checkbox->isChecked(); return checkbox->isChecked();
} }
virtual void setTooltip(const std::wstring& text) override {
Panel::setTooltip(text);
checkbox->setTooltip(text);
}
}; };
} }

View File

@ -13,7 +13,7 @@ Container::Container(glm::vec2 size) : UINode(size) {
} }
std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
if (!interactive || !isEnabled()) { if (!isInteractive() || !isEnabled()) {
return nullptr; return nullptr;
} }
if (!isInside(pos)) return nullptr; if (!isInside(pos)) return nullptr;
@ -127,6 +127,14 @@ void Container::remove(std::shared_ptr<UINode> selected) {
refresh(); refresh();
} }
void Container::remove(const std::string& id) {
for (auto& node : nodes) {
if (node->getId() == id) {
return remove(node);
}
}
}
void Container::clear() { void Container::clear() {
for (auto node : nodes) { for (auto node : nodes) {
node->setParent(nullptr); node->setParent(nullptr);

View File

@ -26,6 +26,7 @@ namespace gui {
virtual void add(std::shared_ptr<UINode> node, glm::vec2 pos); virtual void add(std::shared_ptr<UINode> node, glm::vec2 pos);
virtual void clear(); virtual void clear();
virtual void remove(std::shared_ptr<UINode> node); virtual void remove(std::shared_ptr<UINode> node);
virtual void remove(const std::string& id);
virtual void scrolled(int value) override; virtual void scrolled(int value) override;
virtual void setScrollable(bool flag); virtual void setScrollable(bool flag);
void listenInterval(float interval, ontimeout callback, int repeat=-1); void listenInterval(float interval, ontimeout callback, int repeat=-1);

View File

@ -33,3 +33,11 @@ void Image::setAutoResize(bool flag) {
bool Image::isAutoResize() const { bool Image::isAutoResize() const {
return autoresize; return autoresize;
} }
const std::string& Image::getTexture() const {
return texture;
}
void Image::setTexture(const std::string& name) {
texture = name;
}

View File

@ -15,6 +15,8 @@ namespace gui {
virtual void setAutoResize(bool flag); virtual void setAutoResize(bool flag);
virtual bool isAutoResize() const; virtual bool isAutoResize() const;
virtual const std::string& getTexture() const;
virtual void setTexture(const std::string& name);
}; };
} }

View File

@ -1,7 +1,9 @@
#include "InventoryView.hpp" #include "InventoryView.hpp"
#include "../../../assets/Assets.hpp" #include "../../../assets/Assets.hpp"
#include "../../../content/Content.hpp" #include "../../../content/Content.hpp"
#include "../../../frontend/LevelFrontend.hpp" #include "../../../frontend/LevelFrontend.hpp"
#include "../../../frontend/locale.hpp"
#include "../../../items/Inventories.hpp" #include "../../../items/Inventories.hpp"
#include "../../../items/Inventory.hpp" #include "../../../items/Inventory.hpp"
#include "../../../items/ItemDef.hpp" #include "../../../items/ItemDef.hpp"
@ -22,7 +24,6 @@
#include "../../render/BlocksPreview.hpp" #include "../../render/BlocksPreview.hpp"
#include "../GUI.hpp" #include "../GUI.hpp"
#include <iostream>
#include <glm/glm.hpp> #include <glm/glm.hpp>
using namespace gui; using namespace gui;
@ -108,6 +109,7 @@ SlotView::SlotView(
layout(layout) layout(layout)
{ {
setColor(glm::vec4(0, 0, 0, 0.2f)); setColor(glm::vec4(0, 0, 0, 0.2f));
setTooltipDelay(0.05f);
} }
void SlotView::draw(const DrawContext* pctx, Assets* assets) { void SlotView::draw(const DrawContext* pctx, Assets* assets) {
@ -251,11 +253,12 @@ void SlotView::clicked(gui::GUI* gui, mousecode button) {
stack.setCount(halfremain); stack.setCount(halfremain);
} }
} else { } else {
auto stackDef = content->getIndices()->getItemDef(stack.getItemId());
if (stack.isEmpty()) { if (stack.isEmpty()) {
stack.set(grabbed); stack.set(grabbed);
stack.setCount(1); stack.setCount(1);
grabbed.setCount(grabbed.getCount()-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); stack.setCount(stack.getCount()+1);
grabbed.setCount(grabbed.getCount()-1); grabbed.setCount(grabbed.getCount()-1);
} }
@ -270,6 +273,17 @@ void SlotView::onFocus(gui::GUI* gui) {
clicked(gui, mousecode::BUTTON_1); 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( void SlotView::bind(
int64_t inventoryid, int64_t inventoryid,
ItemStack& stack, ItemStack& stack,

View File

@ -63,6 +63,7 @@ namespace gui {
virtual void clicked(gui::GUI*, mousecode) override; virtual void clicked(gui::GUI*, mousecode) override;
virtual void onFocus(gui::GUI*) override; virtual void onFocus(gui::GUI*) override;
virtual const std::wstring getTooltip() const override;
void bind( void bind(
int64_t inventoryid, int64_t inventoryid,

View File

@ -64,12 +64,28 @@ Label::Label(std::wstring text, std::string fontName)
cache.update(this->text, multiline, textWrap); 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) { void Label::setText(std::wstring text) {
if (text == this->text && !cache.resetFlag) { if (text == this->text && !cache.resetFlag) {
return; return;
} }
this->text = text; this->text = text;
cache.update(this->text, multiline, textWrap); cache.update(this->text, multiline, textWrap);
if (cache.font && autoresize) {
setSize(calcSize());
}
} }
const std::wstring& Label::getText() const { const std::wstring& Label::getText() const {
@ -156,10 +172,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) {
lineHeight *= lineInterval; lineHeight *= lineInterval;
} }
glm::vec2 size = getSize(); glm::vec2 size = getSize();
glm::vec2 newsize ( glm::vec2 newsize = calcSize();
font->calcWidth(text), if (autoresize) {
lineHeight * cache.lines.size() + font->getYOffset() setSize(newsize);
); }
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
switch (align) { switch (align) {
@ -194,6 +210,13 @@ void Label::textSupplier(wstringsupplier supplier) {
this->supplier = supplier; this->supplier = supplier;
} }
void Label::setAutoResize(bool flag) {
this->autoresize = flag;
}
bool Label::isAutoResize() const {
return autoresize;
}
void Label::setMultiline(bool multiline) { void Label::setMultiline(bool multiline) {
if (multiline != this->multiline) { if (multiline != this->multiline) {

View File

@ -24,6 +24,8 @@ namespace gui {
class Label : public UINode { class Label : public UINode {
LabelCache cache; LabelCache cache;
glm::vec2 calcSize();
protected: protected:
std::wstring text; std::wstring text;
std::string fontName; std::string fontName;
@ -47,6 +49,9 @@ namespace gui {
/// @brief Text line height multiplied by line interval /// @brief Text line height multiplied by line interval
int totalLineHeight = 1; int totalLineHeight = 1;
/// @brief Auto resize label to fit text
bool autoresize = false;
public: public:
Label(std::string text, std::string fontName="normal"); Label(std::string text, std::string fontName="normal");
Label(std::wstring 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 textSupplier(wstringsupplier supplier);
virtual void setAutoResize(bool flag);
virtual bool isAutoResize() const;
virtual void setMultiline(bool multiline); virtual void setMultiline(bool multiline);
virtual bool isMultiline() const; virtual bool isMultiline() const;

View File

@ -337,6 +337,9 @@ inline std::wstring get_alphabet(wchar_t c) {
} }
void TextBox::tokenSelectAt(int index) { void TextBox::tokenSelectAt(int index) {
if (input.empty()) {
return;
}
int left = index; int left = index;
int right = index; int right = index;
@ -369,7 +372,7 @@ void TextBox::click(GUI*, int x, int y) {
} }
void TextBox::mouseMove(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); setCaret(index);
extendSelection(index); extendSelection(index);
resetMaxLocalCaret(); resetMaxLocalCaret();
@ -452,7 +455,7 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
uint offset = std::min(size_t(maxLocalCaret), getLineLength(caretLine-1)-1); uint offset = std::min(size_t(maxLocalCaret), getLineLength(caretLine-1)-1);
setCaret(label->getTextLineOffset(caretLine-1) + offset); setCaret(label->getTextLineOffset(caretLine-1) + offset);
} else { } else {
setCaret(0UL); setCaret(static_cast<size_t>(0));
} }
if (shiftPressed) { if (shiftPressed) {
if (selectionStart == selectionEnd) { if (selectionStart == selectionEnd) {
@ -652,11 +655,11 @@ void TextBox::setCaret(size_t position) {
if (realoffset-width > 0) { if (realoffset-width > 0) {
setTextOffset(textOffset + realoffset-width); setTextOffset(textOffset + realoffset-width);
} else if (realoffset < 0) { } else if (realoffset < 0) {
setTextOffset(std::max(textOffset + realoffset, 0LU)); setTextOffset(std::max(textOffset + realoffset, static_cast<size_t>(0)));
} }
} }
void TextBox::setCaret(ssize_t position) { void TextBox::setCaret(ptrdiff_t position) {
if (position < 0) { if (position < 0) {
setCaret(static_cast<size_t>(input.length() + position + 1)); setCaret(static_cast<size_t>(input.length() + position + 1));
} else { } else {

View File

@ -120,7 +120,7 @@ namespace gui {
/// @brief Set caret position in the text /// @brief Set caret position in the text
/// @param position integer in range [-text.length(), text.length()] /// @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 /// @brief Select part of the text
/// @param start index of the first selected character /// @param start index of the first selected character

View File

@ -13,6 +13,9 @@ UINode::~UINode() {
} }
bool UINode::isVisible() const { bool UINode::isVisible() const {
if (visible && parent) {
return parent->isVisible();
}
return visible; return visible;
} }
@ -107,7 +110,7 @@ bool UINode::isInside(glm::vec2 point) {
} }
std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) {
if (!interactive || !enabled) { if (!isInteractive() || !enabled) {
return nullptr; return nullptr;
} }
return isInside(point) ? self : nullptr; return isInside(point) ? self : nullptr;
@ -129,6 +132,22 @@ bool UINode::isResizing() const {
return resizing; 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 { glm::vec2 UINode::calcPos() const {
if (parent) { if (parent) {
return pos + parent->calcPos() + parent->contentOffset(); 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( void UINode::getIndices(
std::shared_ptr<UINode> node, const std::shared_ptr<UINode> node,
std::unordered_map<std::string, std::shared_ptr<UINode>>& map std::unordered_map<std::string, std::shared_ptr<UINode>>& map
) { ) {
const std::string& id = node->getId(); const std::string& id = node->getId();
@ -330,3 +359,19 @@ void UINode::getIndices(
} }
} }
} }
std::shared_ptr<UINode> UINode::find(
const std::shared_ptr<UINode> node,
const std::string& id
) {
if (node->getId() == id) {
return node;
}
if (auto container = std::dynamic_pointer_cast<Container>(node)) {
for (auto subnode : container->getNodes()) {
if (auto found = UINode::find(subnode, id)) {
return found;
}
}
}
return nullptr;
}

View File

@ -109,6 +109,10 @@ namespace gui {
ActionsSet actions; ActionsSet actions;
/// @brief 'ondoubleclick' callbacks /// @brief 'ondoubleclick' callbacks
ActionsSet doubleClickCallbacks; ActionsSet doubleClickCallbacks;
/// @brief element tooltip text
std::wstring tooltip;
/// @brief element tooltip delay
float tooltipDelay = 0.5f;
UINode(glm::vec2 size); UINode(glm::vec2 size);
public: public:
@ -197,6 +201,12 @@ namespace gui {
virtual void setResizing(bool flag); virtual void setResizing(bool flag);
virtual bool isResizing() const; 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; virtual glm::vec4 calcColor() const;
/// @brief Get inner content offset. Used for scroll /// @brief Get inner content offset. Used for scroll
@ -235,11 +245,18 @@ namespace gui {
virtual void setGravity(Gravity gravity); virtual void setGravity(Gravity gravity);
bool isSubnodeOf(const UINode* node);
/// @brief collect all nodes having id /// @brief collect all nodes having id
static void getIndices( static void getIndices(
std::shared_ptr<UINode> node, const std::shared_ptr<UINode> node,
std::unordered_map<std::string, std::shared_ptr<UINode>>& map std::unordered_map<std::string, std::shared_ptr<UINode>>& map
); );
static std::shared_ptr<UINode> find(
const std::shared_ptr<UINode> node,
const std::string& id
);
}; };
} }

View File

@ -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")) { if (auto onclick = create_action(reader, element, "onclick")) {
node.listenAction(onclick); node.listenAction(onclick);
} }
@ -245,6 +252,9 @@ static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, xml::xmlelement el
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("autoresize")) {
label->setAutoResize(element->attr("autoresize").asBool());
}
if (element->has("multiline")) { if (element->has("multiline")) {
label->setMultiline(element->attr("multiline").asBool()); label->setMultiline(element->attr("multiline").asBool());
if (!element->has("valign")) { if (!element->has("valign")) {

View File

@ -21,7 +21,7 @@ void LightSolver::add(int x, int y, int z, int emission) {
addqueue.push(lightentry {x, y, z, ubyte(emission)}); addqueue.push(lightentry {x, y, z, ubyte(emission)});
Chunk* chunk = chunks->getChunkByVoxel(x, y, z); 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); 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) { if (chunk) {
int lx = x - chunk->x * CHUNK_W; int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D; int lz = z - chunk->z * CHUNK_D;
chunk->setModified(true); chunk->flags.modified = true;
ubyte light = chunk->lightmap.get(lx,y,lz, channel); ubyte light = chunk->lightmap.get(lx,y,lz, channel);
if (light != 0 && light == entry.light-1){ if (light != 0 && light == entry.light-1){
@ -96,7 +96,7 @@ void LightSolver::solve(){
if (chunk) { if (chunk) {
int lx = x - chunk->x * CHUNK_W; int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D; int lz = z - chunk->z * CHUNK_D;
chunk->setModified(true); chunk->flags.modified = true;
ubyte light = chunk->lightmap.get(lx, y, lz, channel); ubyte light = chunk->lightmap.get(lx, y, lz, channel);
voxel& v = chunk->voxels[vox_index(lx, y, lz)]; voxel& v = chunk->voxels[vox_index(lx, y, lz)];

View File

@ -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) { 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); lighting->onBlockSet(x,y,z, 0);
if (def->rt.funcsset.onbroken) { if (def->rt.funcsset.onbroken) {
scripting::on_block_broken(player, def, x, y, z); 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) if ((id + tickid) % parts != 0)
continue; continue;
auto def = indices->getBlockDef(id); auto def = indices->getBlockDef(id);
if (def->rt.funcsset.onblockstick) { auto interval = def->tickInterval;
scripting::on_blocks_tick(def, tickRate); 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) if ((index + tickid) % parts != 0)
continue; continue;
auto& chunk = chunks->chunks[index]; auto& chunk = chunks->chunks[index];
if (chunk == nullptr || !chunk->isLighted()) if (chunk == nullptr || !chunk->flags.lighted)
continue; continue;
for (int s = 0; s < segments; s++) { for (int s = 0; s < segments; s++) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View File

@ -61,7 +61,7 @@ bool ChunksController::loadVisible(){
int index = z * w + x; int index = z * w + x;
auto& chunk = chunks->chunks[index]; auto& chunk = chunks->chunks[index];
if (chunk != nullptr){ if (chunk != nullptr){
if (chunk->isLoaded() && !chunk->isLighted()) { if (chunk->flags.loaded && !chunk->flags.lighted) {
if (buildLights(chunk)) { if (buildLights(chunk)) {
return true; return true;
} }
@ -99,12 +99,12 @@ bool ChunksController::buildLights(std::shared_ptr<Chunk> chunk) {
} }
} }
if (surrounding == MIN_SURROUNDING) { if (surrounding == MIN_SURROUNDING) {
bool lightsCache = chunk->isLoadedLights(); bool lightsCache = chunk->flags.loadedLights;
if (!lightsCache) { if (!lightsCache) {
lighting->buildSkyLight(chunk->x, chunk->z); lighting->buildSkyLight(chunk->x, chunk->z);
} }
lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache);
chunk->setLighted(true); chunk->flags.lighted = true;
return true; return true;
} }
return false; return false;
@ -114,20 +114,20 @@ void ChunksController::createChunk(int x, int z) {
auto chunk = level->chunksStorage->create(x, z); auto chunk = level->chunksStorage->create(x, z);
chunks->putChunk(chunk); chunks->putChunk(chunk);
if (!chunk->isLoaded()) { if (!chunk->flags.loaded) {
generator->generate( generator->generate(
chunk->voxels, x, z, chunk->voxels, x, z,
level->getWorld()->getSeed() level->getWorld()->getSeed()
); );
chunk->setUnsaved(true); chunk->flags.unsaved = true;
} }
chunk->updateHeights(); chunk->updateHeights();
if (!chunk->isLoadedLights()) { if (!chunk->flags.loadedLights) {
Lighting::prebuildSkyLight( Lighting::prebuildSkyLight(
chunk.get(), level->content->getIndices() chunk.get(), level->content->getIndices()
); );
} }
chunk->setLoaded(true); chunk->flags.loaded = true;
chunk->setReady(true); chunk->flags.ready = true;
} }

Some files were not shown because too many files have changed in this diff Show More