diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2db70633..845cedc9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,138 +1,96 @@
-# 0.24 - 2024.11.07
+# 0.25 - 2024.12.01
-[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.24/doc/en/main-page.md) for 0.24
+[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.25/doc/en/main-page.md) for 0.25
Table of contents:
- [Added](#added)
- [Functions](#functions)
-- [Changes](#changes)
- [Fixes](#fixes)
## Added
-- particles
-- VEC3 models support
-- handhold item display
-- rules
-- events:
- - on_block_broken (documented)
- - on_block_placed (documented)
- - on_block_interact
+- 3dtext
+- blockwraps
+- network (http requests and sockets)
- libraries:
- - gfx.particles
- - utf8
- - rules
-- bindings:
- - player.destroy
- - player.fast_interaction
-- water overlay
-- block models from OBJ or VEC3
-- bicubic heightmaps interpolation method
-- unicode escapes support
-- fragments placements
-- console commands:
- - time.daycycle
- - fragment.place
- - rule.list
- - rule.set
-- text field 'subconsumer'
-- shader uniforms:
- - u_lightDir to main shader
- - u_dayTime to skybox shader
-- block properties:
- - overlay-texture
- - model-name
-- item properties:
- - model-name
-- 'Open content folder' buttons
-- 'Background framerate limit' setting
+ - base64
+ - gfx.text3d
+ - gfx.blockwraps
+ - network
+- events:
+ - on_replaced
+ - on_block_replaced
+ - on_player_tick
+- structures 'lowering' property
+- add 'hint' property to textbox
+- add 'taking' and 'placing' properties to slot and slotsgrid
+- add 'scroll-step' property to container
+- add 'line-numbers' and 'text-color' to textbox
+- modules:
+ - base:util
+- uinode property 'id'
+- block.materials table
+- block.properties table
+- item.properties table
+- add version to world info table
+- add 'sizeSpread' particles property
+- add user properties
### Functions
-- core.open_folder
-- world.get_generator
-- world.is_open
-- item.placing_block
-- item.model_name
-- item.emission
-- entities.get_hitbox
-- utf8.tobytes
-- utf8.tostring
-- utf8.length
-- utf8.codepoint
-- utf8.encode
-- utf8.sub
-- utf8.upper
-- utf8.lower
-- file.read_combined_object
-- fragment:place
-- rules.create
-- rules.listen
-- rules.unlisten
-- rules.get
-- rules.set
-- rules.reset
-- input.set_enabled
-- hud._is_content_access
-- hud._set_content_access
-- hud._set_debug_cheats
-- gfx.particles.emit
-- gfx.particles.stop
-- gfx.particles.get_origin
-- gfx.particles.set_origin
-- assets.load_texture
-
-Documented:
-- file.read_combined_list
-- file.list
-- file.list_all_res
-- input.is_active
-- table.copy
-- table.count_pairs
-- table.random
-- table.has
-- table.index
-- table.remove_value
-- table.tostring
-- string.explode
-- string.split
-- string.pattern_safe
-- string.formatted_time
-- string.replace
-- string.trim
-- string.trim_left
-- string.trim_right
-- string.starts_with
-- string.ends_with
-- math.clamp
-- math.rand
-- is_array
-- parse_path
-- timeit
-- sleep
-
-## Changes
-
-- major skybox optimization
-- chunks-renderer optimization
-- libspng replaced with libpng on Windows
-- console commands:
- - blocks.fill
- - fragment.save
-- added 'def' to core.get_setting_info tables
-- water texture
+- player.is_infinite_items
+- player.set_infinite_items
+- player.is_instant_destruction
+- player.set_instant_destruction
+- player.get_name
+- player.set_name
+- hud.open
+- base64.encode
+- base64.decode
+- utf8.escape
+- string.escape
+- textbox:lineAt
+- textbox:linePos
+- network.get
+- network.get_binary
+- network.tcp_connect
+- network.tcp_open
+- network.get_total_upload
+- network.get_total_download
+- gfx.text3d.show
+- gfx.text3d.hide
+- gfx.text3d.get_text
+- gfx.text3d.set_text
+- gfx.text3d.get_pos
+- gfx.text3d.set_pos
+- gfx.text3d.get_axis_x
+- gfx.text3d.set_axis_x
+- gfx.text3d.get_axis_y
+- gfx.text3d.set_axis_y
+- gfx.text3d.set_rotation
+- gfx.text3d.update_settings
## Fixes
-- [fix fatal error on editing texbox not having any consumer](https://github.com/MihailRis/VoxelEngine-Cpp/commit/22fa082fc6299ffa3196d62c67e01b849c35b8eb)
-- [fix commands boolean type support](https://github.com/MihailRis/VoxelEngine-Cpp/commit/a50cb109c8e3ca0f7a591bf126f07aee36c962e6)
-- [fix potential null dereferences on incorrect block.* functions use](https://github.com/MihailRis/VoxelEngine-Cpp/commit/961773c9f9745c15eb8d697c1538ac8e21f24da3)
-- [fix: draw-group not copied](https://github.com/MihailRis/VoxelEngine-Cpp/commit/dc8bad2af67e70b0b2346f516028e5795f597737)
-- [fix: generator-providing pack may be removed](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6f2f365278eb1866c773890471b7269a5ef45305)
-- [fix colision check on block place](https://github.com/MihailRis/VoxelEngine-Cpp/commit/726ee8ad703bc57530b881450b8839aaec6b97c9)
-- [fix collision detection bug](https://github.com/MihailRis/VoxelEngine-Cpp/commit/7fcc34ba4cf14097dfda26054b028c5e8771d26c)
-- [fix: blocks lighting bug fix](https://github.com/MihailRis/VoxelEngine-Cpp/commit/9d3e872f88de2648f8c0f2e4611b30f5ce8999cf)
-- [fix: inaccurate framerate limit on Windows](https://github.com/MihailRis/VoxelEngine-Cpp/commit/3f531bbf98da5ad751dce1220c5c5fdf35f86c92)
-- [fix block.get_hitbox again](https://github.com/MihailRis/VoxelEngine-Cpp/commit/edad594101e5808ccf14e0edefedbe87cb8f983b)
-- [fix string.replace](https://github.com/MihailRis/VoxelEngine-Cpp/commit/44fd5416a9a110a12f8b3f2d369e5638055b306e)
+- [fix translucent blocks render](https://github.com/MihailRis/VoxelEngine-Cpp/pull/370)
+- [fix blocks selection with semi-transparent blocks](https://github.com/MihailRis/VoxelEngine-Cpp/commit/171cbb48d099032d7e78c51a46c374104f96f0d1)
+- [fix: commands repository not reset before world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1a00a91b604399f3108aa995422d371e573e650b)
+- [mip-mapping related fixes](https://github.com/MihailRis/VoxelEngine-Cpp/commit/d9277e1b31714632bd7f5f601b8362a9e7cb8819)
+- [fix disabled slots display](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e8ee3e04b1398a3ada8445591267525304410571)
+- [fix attack](https://github.com/MihailRis/VoxelEngine-Cpp/commit/bc17abc8b3ee7ff9027f7e3c375ca0330bb8e7bc)
+- [fix: commands repository not reset before world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1a00a91b604399f3108aa995422d371e573e650b)
+- [fix stdlib.lua](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6ec33ab98c78523eaececf40f113f2323d25a33a)
+- [fix file.write_bytes](https://github.com/MihailRis/VoxelEngine-Cpp/commit/0fec17a8b69ac81255b77022f3af5addf8fcc8f8)
+- [fix World::nextInventoryId](https://github.com/MihailRis/VoxelEngine-Cpp/commit/371fdaedcef2c163edd226160f388068b2bf5e83)
+- [fix block inventory unbinding](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6f6c2a916afd6b9b79221111fc72b1a86109be13)
+- [fix xml text escapes handling](https://github.com/MihailRis/VoxelEngine-Cpp/commit/53c54dc91d132c221ff5fea2f7e9fb4568db9a0f)
+- [fix `\'` escape parsing](https://github.com/MihailRis/VoxelEngine-Cpp/commit/2bc6cbda2e809b14fa6cffe09161b53c1636675f)
+- [fix crosshair look](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e034bda477c35efe96548e78ecc722966a7a2197)
+- [fix: actual block inventory size not updating on inventory-size property update](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1ba5b0ce33103e539ccb199ee1cd52095e286a1f)
+- [fix falling block hitbox](https://github.com/MihailRis/VoxelEngine-Cpp/commit/352ef6485a4b796d1cdc8dd0e00ab1a1d72a2c0a)
+- [fix console position](https://github.com/MihailRis/VoxelEngine-Cpp/commit/3ea213e8d3cee7be55ec39ffb18dc557dec7557b)
+- [fix: fatal error on pack removal when no world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/78d5ab02c2ba8a3d05cf5639eb10a49c9ca14ec3)
+- [fix custom model lighting](https://github.com/MihailRis/VoxelEngine-Cpp/commit/a333cadfcaeb485a30833343d55faf01b28a5c5f)
+- [fix: emitter does not skip particles](https://github.com/MihailRis/VoxelEngine-Cpp/commit/983e516fb4ebc1f2def592f2b7f3195d968deed2)
+- [fix old custom models render](https://github.com/MihailRis/VoxelEngine-Cpp/commit/82733d38011b52a426cb74560521949c1cd43cc1)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f465fcb3..a56457bd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -81,4 +81,4 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR
if (VOXELENGINE_BUILD_TESTS)
enable_testing()
add_subdirectory(test)
-endif()
+endif()
\ No newline at end of file
diff --git a/README.md b/README.md
index 0b586d95..418d372c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## Latest release
- [Download](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest) | [Скачать](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest)
-- [Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/ru/main-page.md)
+- [Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/ru/main-page.md)
## Build project in Linux
@@ -62,6 +62,12 @@ If you use Wayland
sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl
```
+And you need entt. In yay you can use
+
+```sh
+yay -S entt
+```
+
### Build engine with CMake
```sh
diff --git a/doc/en/main-page.md b/doc/en/main-page.md
index 4d487f5a..0daa4729 100644
--- a/doc/en/main-page.md
+++ b/doc/en/main-page.md
@@ -1,8 +1,8 @@
# Documentation
-Documentation for the engine of in-development version 0.25.
+Documentation for in-development version 0.26.
-[Documentation for stable release 0.24.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/en/main-page.md)
+[Documentation for stable release 0.25.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/en/main-page.md)
## Sections
@@ -14,9 +14,10 @@ Documentation for the engine of in-development version 0.25.
- [Content-packs](content-packs.md)
- [Engine usage recommendations](engine-use-recommendations.md)
- [Item properties](item-properties.md)
+- [Particles](particles.md)
- [Resources (resources.json)](resources.md)
- [Rigging](rigging.md)
- [Scripting](scripting.md)
+- [Text styles](text-styles.md)
- [World generator engine](world-generator.md)
- [XML UI building](xml-ui-layouts.md)
-- [Particles](particles.md)
diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md
index 5ec2490f..3132bce3 100644
--- a/doc/en/scripting/builtins/libgui.md
+++ b/doc/en/scripting/builtins/libgui.md
@@ -39,3 +39,25 @@ gui.get_locales_info() -> table of tables {
```
Returns information about all loaded locales (res/texts/\*).
+
+```lua
+gui.clear_markup(
+ -- markup language ("md" - Markdown)
+ language: str,
+ -- text with markup
+ text: str
+) -> str
+```
+
+Removes markup from text.
+
+```lua
+gui.escape_markup(
+ -- markup language ("md" - Markdown)
+ language: str,
+ -- text with markup
+ text: str
+) -> str
+```
+
+Escapes markup in text.
diff --git a/doc/en/scripting/builtins/libnetwork.md b/doc/en/scripting/builtins/libnetwork.md
index 1e519d9d..48caefbd 100644
--- a/doc/en/scripting/builtins/libnetwork.md
+++ b/doc/en/scripting/builtins/libnetwork.md
@@ -39,7 +39,7 @@ The Socket class has the following methods:
```lua
-- Sends a byte array
-socket:send(table|ByteArray)
+socket:send(table|ByteArray|str)
-- Reads the received data
socket:recv(
@@ -59,6 +59,9 @@ socket:is_alive() --> bool
-- Checks if the connection is present (using socket:send(...) is available).
socket:is_connected() --> bool
+
+-- Returns the address and port of the connection.
+socket:get_address() --> str, int
```
```lua
@@ -80,6 +83,9 @@ server:close()
-- Checks if the TCP server exists and is open.
server:is_open() --> bool
+
+-- Returns the server port.
+server:get_port() --> int
```
## Analytics
diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md
index 4b13ec7b..cc820898 100644
--- a/doc/en/scripting/events.md
+++ b/doc/en/scripting/events.md
@@ -108,6 +108,12 @@ function on_block_placed(blockid, x, y, z, playerid)
Called on block placed by player
+```lua
+function on_block_replaced(blockid, x, y, z, playerid)
+```
+
+Called on block replaced with other by player
+
```lua
function on_block_broken(blockid, x, y, z, playerid)
```
diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md
index 4f132462..c990a197 100644
--- a/doc/en/scripting/ui.md
+++ b/doc/en/scripting/ui.md
@@ -32,7 +32,7 @@ document["worlds-panel"]:clear()
Properties that apply to all elements:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ------------- | ------ | ---- | ----- | ------------------------------------------- |
| id | string | yes | *no* | element id |
| pos | vec2 | yes | yes | element position inside a container |
@@ -60,17 +60,17 @@ Common element methods:
Common methods for containers (elements: container, panel, button, pagebox):
-| Method | Description |
-| ------------------------------- | ------------------------------------------------------------------ |
-| clear() | clears content |
-| add(xml) | adds an element, creating it using xml code. Example: `container:add("")` |
-| setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds |
+| Method | Description |
+| ------------------------------- | -------------------------------------------------------------------------------------------- |
+| clear() | clears content |
+| add(xml) | adds an element, creating it using xml code. Example: `container:add("")` |
+| setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds |
## Textbox
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ |
| text | string | yes | yes | entered text or placeholder |
| placeholder | string | yes | yes | placeholder (used if nothing has been entered) |
@@ -82,6 +82,8 @@ Properties:
| textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") |
| valid | bool | yes | no | is the entered text correct |
| textColor | vec4 | yes | yes | text color |
+| syntax | string | yes | yes | syntax highlighting ("lua" - Lua) |
+| markup | string | yes | yes | text markup language ("md" - Markdown) |
Methods:
@@ -95,7 +97,7 @@ Methods:
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ---------- | ----- | ---- | ----- | --------------------- |
| value | float | yes | yes | current value |
| min | float | yes | yes | minimum value |
@@ -108,7 +110,7 @@ Properties:
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ |
| page | string | yes | yes | current page |
@@ -123,7 +125,7 @@ Methods:
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ------- | ---- | ---- | ----- | ----------- |
| checked | bool | yes | yes | mark status |
@@ -131,7 +133,7 @@ Properties:
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ |
| text | string | yes | yes | button text |
@@ -139,15 +141,16 @@ Properties:
Properties:
-| Title | Type | Read | Write | Description |
-| ----- | ------ | ---- | ----- | ----------- |
-| text | string | yes | yes | label text |
+| Name | Type | Read | Write | Description |
+| ------ | ------ | ---- | ----- | -------------------------------------- |
+| text | string | yes | yes | label text |
+| markup | string | yes | yes | text markup language ("md" - Markdown) |
## Image
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ |
| src | string | yes | yes | texture name |
@@ -155,6 +158,6 @@ Properties:
Properties:
-| Title | Type | Read | Write | Description |
+| Name | Type | Read | Write | Description |
| --------- | ---- | ---- | ----- | ------------------------------------------------- |
| inventory | int | yes | yes | id of the inventory to which the element is bound |
diff --git a/doc/en/text-styles.md b/doc/en/text-styles.md
new file mode 100644
index 00000000..e85437e1
--- /dev/null
+++ b/doc/en/text-styles.md
@@ -0,0 +1,21 @@
+# Text styles
+
+A proprietary Markdown dialect is used to mark up text styles.
+Formatting works on UI elements: label and textbox, if `markup="md"` is explicitly specified.
+
+## Styles
+
+| Style | Example | Output |
+| ------------- | ------------------------ | -------------------------- |
+| Bold | `**Bold font**` | **Bold font** |
+| Italic | `*Text in italics*` | *Text in italics* |
+| Underline | `__Underlined text__` | Underlined text |
+| Strikethrough | `~~Strikethrough text~~` | ~~Strikethrough text~~ |
+
+Styles can be combined. Example:
+```md
+***__Message__*** using *~~combed~~ combined* styles__~~.~~__
+```
+Output:
+
+***Message*** using *~~combed~~ combined* styles~~.~~
diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md
index d77a93ed..e7008682 100644
--- a/doc/en/xml-ui-layouts.md
+++ b/doc/en/xml-ui-layouts.md
@@ -87,6 +87,7 @@ Inner text is a button text.
- `autoresize` - automatic change of element size (default - false). Does not affect font size.
- `multiline` - allows display of multiline text.
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true").
+- `markup` - text markup language ("md" - Markdown).
## *image*
@@ -112,6 +113,8 @@ Inner text - initially entered text
- `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct.
- `onup` - lua function called when the up arrow is pressed.
- `ondown` - lua function called when the down arrow is pressed.
+- `syntax` - syntax highlighting ("lua" - Lua).
+- `markup` - text markup language ("md" - Markdown).
## *trackbar*
diff --git a/doc/ru/main-page.md b/doc/ru/main-page.md
index 800415c6..10dff292 100644
--- a/doc/ru/main-page.md
+++ b/doc/ru/main-page.md
@@ -1,8 +1,8 @@
# Документация
-Документация движка разрабатываемой версии 0.25.
+Документация разрабатываемой версии 0.26.
-[Документация стабильной версии 0.24.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/ru/main-page.md)
+[Документация стабильной версии 0.25.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/ru/main-page.md)
## Разделы
@@ -19,4 +19,5 @@
- [Свойства блоков](block-properties.md)
- [Свойства предметов](item-properties.md)
- [Скриптинг](scripting.md)
+- [Стили текста](text-styles.md)
- [Частицы](particles.md)
diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md
index eb81cd5f..11dfde03 100644
--- a/doc/ru/scripting/builtins/libgui.md
+++ b/doc/ru/scripting/builtins/libgui.md
@@ -36,3 +36,25 @@ gui.get_locales_info() -> таблица таблиц где
```
Возвращает информацию о всех загруженных локалях (res/texts/\*).
+
+```lua
+gui.clear_markup(
+ -- язык разметки ("md" - Markdown)
+ language: str,
+ -- текст с разметкой
+ text: str
+) -> str
+```
+
+Удаляет разметку из текста.
+
+```lua
+gui.escape_markup(
+ -- язык разметки ("md" - Markdown)
+ language: str,
+ -- текст с разметкой
+ text: str
+) -> str
+```
+
+Экранирует разметку в тексте.
diff --git a/doc/ru/scripting/builtins/libnetwork.md b/doc/ru/scripting/builtins/libnetwork.md
index 8a937330..7fedf2bd 100644
--- a/doc/ru/scripting/builtins/libnetwork.md
+++ b/doc/ru/scripting/builtins/libnetwork.md
@@ -39,7 +39,7 @@ network.tcp_connect(
```lua
-- Отправляет массив байт
-socket:send(table|ByteArray)
+socket:send(table|ByteArray|str)
-- Читает полученные данные
socket:recv(
@@ -59,6 +59,9 @@ socket:is_alive() --> bool
-- Проверяет наличие соединения (доступно использование socket:send(...)).
socket:is_connected() --> bool
+
+-- Возвращает адрес и порт соединения.
+socket:get_address() --> str, int
```
```lua
@@ -80,6 +83,9 @@ server:close()
-- Проверяет, существует и открыт ли TCP сервер.
server:is_open() --> bool
+
+-- Возвращает порт сервера.
+server:get_port() --> int
```
## Аналитика
diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md
index 08192cfa..a5cc3e72 100644
--- a/doc/ru/scripting/events.md
+++ b/doc/ru/scripting/events.md
@@ -108,6 +108,12 @@ function on_block_placed(blockid, x, y, z, playerid)
Вызывается после установки блока игроком
+```lua
+function on_block_replaced(blockid, x, y, z, playerid)
+```
+
+Вызывается после замены блока игроком
+
```lua
function on_block_broken(blockid, x, y, z, playerid)
```
diff --git a/doc/ru/scripting/extensions.md b/doc/ru/scripting/extensions.md
index d4502f64..d0ddd365 100644
--- a/doc/ru/scripting/extensions.md
+++ b/doc/ru/scripting/extensions.md
@@ -10,6 +10,12 @@ table.copy(t: table) -> table
Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной.
+```lua
+table.deep_copy(t: table) -> table
+```
+
+Функция глубокого копирования создает полную копию исходной таблицы, включая все её вложенные таблицы.
+
```lua
table.count_pairs(t: table) -> integer
```
diff --git a/doc/ru/scripting/modules/core_bit_converter.md b/doc/ru/scripting/modules/core_bit_converter.md
index f9930560..44b915c7 100644
--- a/doc/ru/scripting/modules/core_bit_converter.md
+++ b/doc/ru/scripting/modules/core_bit_converter.md
@@ -1,93 +1,98 @@
# Модуль core:bit_converter
+## Доступные порядки байтов
+**LE (Little-Endian)**
+**BE (Big-Endian)**
+По умолчанию используется **LE**
+
## Конвертация значений в байты и обратно
```lua
-function bit_converter.string_to_bytes(string: str) -> table
+function bit_converter.string_to_bytes(str: string) -> table
```
Конвертирует строку в байты
```lua
-function bit_converter.bool_to_byte(boolean: bool) -> integer
+function bit_converter.bool_to_byte(bool: boolean) -> integer
```
Конвертирует логический булев в байт
```lua
-function bit_converter.single_to_bytes(number: single) -> table
+function bit_converter.float32_to_bytes(float: number, [опционально] order: string) -> table
```
Конвертирует плавающее значение одинарной точности в байты
```lua
-function bit_converter.double_to_bytes(number: double) -> table
+function bit_converter.float64_to_bytes(float: number, [опционально] order: string) -> table
```
Конвертирует плавающее значение двойной точности в байты
```lua
-function bit_converter.uint16_to_bytes(integer: int) -> table
+function bit_converter.uint16_to_bytes(int: integer, [опционально] order: string) -> table
```
Конвертирует беззнаковое 2-х байтовое целое число в байты
```lua
-function bit_converter.uint32_to_bytes(integer: int) -> table
+function bit_converter.uint32_to_bytes(int: integer, [опционально] order: string) -> table
```
Конвертирует беззнаковое 4-х байтовое целое число в байты
```lua
-function bit_converter.int16_to_bytes(integer: int) -> table
+function bit_converter.sint16_to_bytes(int: integer, [опционально] order: string) -> table
```
Конвертирует знаковое 2-х байтовое целое число в байты
```lua
-function bit_converter.int32_to_bytes(integer: int) -> table
+function bit_converter.sint32_to_bytes(int: integer, [опционально] order: string) -> table
```
Конвертирует знаковое 4-х байтовое целое число в байты
```lua
-function bit_converter.int64_to_bytes(integer: int) -> table
+function bit_converter.int64_to_bytes(int: integer, [опционально] order: string) -> table
```
Конвертирует знаковое 8-и байтовое целое число в байты
```lua
-function bit_converter.bytes_to_string(table: bytes) -> string
+function bit_converter.bytes_to_string(bytes: table) -> string
```
Конвертирует массив байтов в строку
```lua
-function bit_converter.byte_to_bool(integer: byte) -> boolean
+function bit_converter.byte_to_bool(byte: integer) -> boolean
```
Конвертирует байт в логическое булевое значение
```lua
-function bit_converter.bytes_to_single(table: bytes) -> number№
+function bit_converter.bytes_to_float32(bytes: table|Bytearray, [опционально] order: string) -> number
```
Конвертирует массив байтов в плавающее число одинарной точности
```lua
-function bit_converter.bytes_to_double(table: bytes) -> number
+function bit_converter.bytes_to_float64(bytes: table|Bytearray, [опционально] order: string) -> number
```
Конвертирует массив байтов в плавающее число двойной точности
```lua
-function bit_converter.bytes_to_uint16(table: bytes) -> integer
+function bit_converter.bytes_to_uint16(bytes: table|Bytearray, [опционально] order: string) -> integer
```
Конвертирует массив байтов в 2-х байтовое беззнаковое число
```lua
-function bit_converter.bytes_to_uint32(table: bytes) -> integer
+function bit_converter.bytes_to_uint32(bytes: table|Bytearray, [опционально] order: string) -> integer
```
Конвертирует массив байтов в 4-х байтовое беззнаковое число
```lua
-function bit_converter.bytes_to_int16(table: bytes) -> integer
+function bit_converter.bytes_to_sint16(bytes: table|Bytearray, [опционально] order: string) -> integer
```
Конвертирует массив байтов в 2-х байтовое знаковое число
```lua
-function bit_converter.bytes_to_int32(table: bytes) -> integer
+function bit_converter.bytes_to_sint32(bytes: table|Bytearray, [опционально] order: string) -> integer
```
Конвертирует массив байтов в 4-х байтовое знаковое число
```lua
-function bit_converter.bytes_to_int64(table: bytes) -> integer
+function bit_converter.bytes_to_int64(bytes: table|Bytearray, [опционально] order: string) -> integer
```
Конвертирует массив байтов в 8-х байтовое знаковое число
diff --git a/doc/ru/scripting/modules/core_data_buffer.md b/doc/ru/scripting/modules/core_data_buffer.md
index 60c36193..09b682b9 100644
--- a/doc/ru/scripting/modules/core_data_buffer.md
+++ b/doc/ru/scripting/modules/core_data_buffer.md
@@ -4,67 +4,78 @@
### Хранит в себе массив байтов и позволяет легко получать или добавлять разные значения
```lua
-function data_buffer(bytes)
+function data_buffer(
+ [опционально] bytes: table,
+ [опционально] order: string,
+ [опционально] useBytearray: boolean
+)
```
-Создаёт новый экземпляр data_buffer (параметр bytes необязательный)
+Создаёт новый экземпляр **data_buffer**.
+Если **useBytearray** равен **true**, то байты буффера будут хранится ввиде **Bytearray**. Это может снизить производительность, но также и уменьшить размер буффера в памяти
```lua
-function data_buffer:put_byte(integer: byte)
+function data_buffer:set_order(order: string)
+```
+Задаёт порядок байтов для чисел.
+Должен равняться одному из перечисленных в [**bit_converter**](core_bit_converter.md)
+
+```lua
+function data_buffer:put_byte(byte: integer)
```
Записывает байт в буффер
```lua
-function data_buffer:put_bytes(table: bytes)
+function data_buffer:put_bytes(bytes: table|Bytearray)
```
Записывает байты в буффер
```lua
-function data_buffer:put_string(string: str)
+function data_buffer:put_string(str: string)
```
Конвертирует строку в байты и записывает их в буффер
```lua
-function data_buffer:put_bool(boolean: bool)
+function data_buffer:put_bool(bool: boolean)
```
Конвертирует булевое значение в байт и записывает его в буффер
```lua
-function data_buffer:put_single(number: single)
+function data_buffer:put_float32(float: number)
```
Конвертирует плавающее число одинарной точности в байты и записывает их в буффер
```lua
-function data_buffer:put_double(number: double)
+function data_buffer:put_float64(float: number)
```
Конвертирует плавающее число двойной точности в байты и записывает их в буффер
```lua
-function data_buffer:put_uint16(integer: int)
+function data_buffer:put_uint16(int: integer)
```
Конвертирует беззнаковое 2-х байтовое число в байты и записывает их в буффер
```lua
-function data_buffer:put_uint32(integer: int)
+function data_buffer:put_uint32(int: integer)
```
Конвертирует беззнаковое 4-х байтовое число в байты и записывает их в буффер
```lua
-function data_buffer:put_int16(integer: int)
+function data_buffer:put_sint16(int: integer)
```
Конвертирует знаковое 2-х байтовое число в байты и записывает их в буффер
```lua
-function data_buffer:put_int32(integer: int)
+function data_buffer:put_sint32(int: integer)
```
Конвертирует знаковое 4-х байтовое число в байты и записывает их в буффер
```lua
-function data_buffer:put_int64(integer: int)
+function data_buffer:put_int64(int: integer)
```
Конвертирует знаковое 8-и байтовое число в байты и записывает их в буффер
```lua
-function data_buffer:put_number(number: num)
+function data_buffer:put_number(num: number)
```
Конвертирует любое число в байты и записывает их в буффер;
@@ -73,10 +84,10 @@ function data_buffer:put_number(number: num)
zero = 0
uint16 = 1
uint32 = 2
-int16 = 3
-int32 = 4
int64 = 5
-double = 6
+float64 = 6
+sint16 = 7
+sint32 = 8
```
```lua
@@ -85,9 +96,9 @@ function data_buffer:get_byte() -> integer
Возвращает следующий байт из буффера
```lua
-function data_buffer:get_bytes(n) -> table
+function data_buffer:get_bytes(n) -> table|Bytearray
```
-Возвращает n следующих байтов, если n равен nil или не указан, то возвращается массив всех байтов
+Возвращает **n** следующих байтов, если **n** равен **nil** или не указан, то возвращается массив всех байтов
```lua
function data_buffer:get_string() -> string
@@ -100,12 +111,12 @@ function data_buffer:get_bool() -> boolean
Читает следующий логический булев из буффера
```lua
-function data_buffer:get_single() -> number
+function data_buffer:get_float32() -> number
```
Читает следующее плавающее число одинарной точности из буффера
```lua
-function data_buffer:get_double() -> number
+function data_buffer:get_float64() -> number
```
Читает следующее плавающее число двойной точности из буффера
@@ -120,12 +131,12 @@ function data_buffer:get_uint32() -> integer
Читает следующее 4-х байтовое беззнаковое целое число из буффера
```lua
-function data_buffer:get_int16() -> integer
+function data_buffer:get_sint16() -> integer
```
Читает следующее 2-х байтовое знаковое целое число из буффера
```lua
-function data_buffer:get_int32() -> integer
+function data_buffer:get_sint32() -> integer
```
Читает следующее 4-х байтовое знаковое целое число из буффера
diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md
index 4d244210..07d93013 100644
--- a/doc/ru/scripting/ui.md
+++ b/doc/ru/scripting/ui.md
@@ -82,6 +82,8 @@ document["worlds-panel"]:clear()
| textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") |
| valid | bool | да | нет | является ли введенный текст корректным |
| textColor | vec4 | да | да | цвет текста |
+| syntax | string | да | да | подсветка синтаксиса ("lua" - Lua) |
+| markup | string | да | да | язык разметки текста ("md" - Markdown) |
Методы:
@@ -139,9 +141,10 @@ document["worlds-panel"]:clear()
Свойства:
-| Название | Тип | Чтение | Запись | Описание |
-| -------- | ------ | ------ | ------ | ----------- |
-| text | string | да | да | текст метки |
+| Название | Тип | Чтение | Запись | Описание |
+| -------- | ------ | ------ | ------ | -------------------------------------- |
+| text | string | да | да | текст метки |
+| markup | string | да | да | язык разметки текста ("md" - Markdown) |
## Изображение (image)
diff --git a/doc/ru/text-styles.md b/doc/ru/text-styles.md
new file mode 100644
index 00000000..0a09abe3
--- /dev/null
+++ b/doc/ru/text-styles.md
@@ -0,0 +1,21 @@
+# Стили текста
+
+Для разметки стилей текста используется собственный диалект Markdown.
+Форматирование работает на UI элементах: label и textbox, если явно указано `markup="md"`.
+
+## Стили
+
+| Стиль | Пример | Вывод |
+| ------------ | ------------------------- | ----------------------------- |
+| Жирный | `**Жирный шрифт**` | **Жирный шрифт** |
+| Курсив | `*Текст курсивом*` | *Текст курсивом* |
+| Подчеркнутый | `__Подчеркнутый текст__` | Подчеркнутый текст |
+| Зачеркнутый | `~~Зачеркнутый текст~~` | ~~Зачеркнутый текст~~ |
+
+Стили могут объединяться. Пример:
+```md
+***__Сообщение__***, демонстрирующее *~~обедненные~~ объединенные* стили__~~.~~__
+```
+Вывод:
+
+***Сообщение***, демонстрирующее *~~обедненные~~ объединенные* стили~~.~~
diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md
index 5c71690f..1ad93c4f 100644
--- a/doc/ru/xml-ui-layouts.md
+++ b/doc/ru/xml-ui-layouts.md
@@ -89,6 +89,7 @@
- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта.
- `multiline` - разрешает отображение многострочного текста.
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
+- `markup` - язык разметки текста ("md" - Markdown).
## Изображение - *image*
@@ -113,6 +114,8 @@
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
- `onup` - lua функция вызываемая при нажатии стрелки вверх.
- `ondown` - lua функция вызываемая при нажатии стрелки вниз.
+- `syntax` - подстветка синтаксиса ("lua" - Lua).
+- `markup` - язык разметки текста ("md" - Markdown).
## Ползунок - *trackbar*
diff --git a/flake.nix b/flake.nix
index a1c9d20b..2c392d27 100644
--- a/flake.nix
+++ b/flake.nix
@@ -8,7 +8,7 @@
flake-utils.lib.eachDefaultSystem (system: {
devShells.default = with nixpkgs.legacyPackages.${system}; mkShell {
nativeBuildInputs = [ cmake pkg-config ];
- buildInputs = [ glm glfw glew zlib libpng libvorbis openal luajit ]; # libglvnd
+ buildInputs = [ glm glfw glew zlib libpng libvorbis openal luajit curl ]; # libglvnd
packages = [ glfw mesa freeglut entt ];
LD_LIBRARY_PATH = "${wayland}/lib:$LD_LIBRARY_PATH";
};
diff --git a/res/content/base/package.json b/res/content/base/package.json
index 7645a6e0..2878d3a6 100644
--- a/res/content/base/package.json
+++ b/res/content/base/package.json
@@ -1,6 +1,6 @@
{
"id": "base",
"title": "Base",
- "version": "0.25",
+ "version": "0.26",
"description": "basic content package"
}
diff --git a/res/layouts/console.xml b/res/layouts/console.xml
index c367545c..43e72dd8 100644
--- a/res/layouts/console.xml
+++ b/res/layouts/console.xml
@@ -22,6 +22,7 @@
multiline='true'
size-func="gui.get_viewport()[1],40"
gravity="bottom-left"
+ markup="md"
>
MAX_UINT32 or int < MIN_UINT32 then
error("invalid uint32")
end
- return {
- intToByte(bit.rshift(int, 24)),
- intToByte(bit.rshift(int, 16)),
- intToByte(bit.rshift(int, 8)),
- intToByte(int)
- }
+ return uint32ToBytes(int, order)
end
-function bit_converter.uint16_to_bytes(int)
+function bit_converter.uint16_to_bytes(int, order)
if int > MAX_UINT16 or int < MIN_UINT16 then
error("invalid uint16")
end
- return {
- intToByte(bit.rshift(int, 8)),
- intToByte(int)
- }
+ return uint16ToBytes(int, order)
end
-function bit_converter.int64_to_bytes(int)
+function bit_converter.int64_to_bytes(int, order)
if int > MAX_INT64 or int < MIN_INT64 then
error("invalid int64")
end
- return {
- intToByte(bit.rshift(int, 56)),
- intToByte(bit.rshift(int, 48)),
- intToByte(bit.rshift(int, 40)),
- intToByte(bit.rshift(int, 32)),
- intToByte(bit.rshift(int, 24)),
- intToByte(bit.rshift(int, 16)),
- intToByte(bit.rshift(int, 8)),
- intToByte(int)
- }
+ return fromLE({
+ maskHighBytes(bit.rshift(int, 56)),
+ maskHighBytes(bit.rshift(int, 48)),
+ maskHighBytes(bit.rshift(int, 40)),
+ maskHighBytes(bit.rshift(int, 32)),
+ maskHighBytes(bit.rshift(int, 24)),
+ maskHighBytes(bit.rshift(int, 16)),
+ maskHighBytes(bit.rshift(int, 8)),
+ maskHighBytes(int)
+ }, order)
end
-function bit_converter.int32_to_bytes(int)
+function bit_converter.int32_to_bytes(int, order)
+ on_deprecated_call("bit_converter.int32_to_bytes", "bit_converter.sint32_to_bytes")
+
if int > MAX_INT32 or int < MIN_INT32 then
error("invalid int32")
end
- return bit_converter.uint32_to_bytes(int + MAX_INT32)
+ return uint32ToBytes(int + MAX_INT32, order)
end
-function bit_converter.int16_to_bytes(int)
+function bit_converter.int16_to_bytes(int, order)
+ on_deprecated_call("bit_converter.int32_to_bytes", "bit_converter.sint16_to_bytes")
+
if int > MAX_INT16 or int < MIN_INT16 then
error("invalid int16")
end
- return bit_converter.uint16_to_bytes(int + MAX_INT16)
+ return uint16ToBytes(int + MAX_INT16, order)
end
-function bit_converter.bytes_to_single(bytes)
- return bytesToFloatOrDouble(bytes, 'f')
+function bit_converter.sint32_to_bytes(int, order)
+ if int > MAX_INT32 or int < MIN_INT32 then
+ error("invalid sint32")
+ end
+
+ return uint32ToBytes(int + MAX_UINT32 + 1, order)
end
-function bit_converter.bytes_to_double(bytes)
- return bytesToFloatOrDouble(bytes, 'd')
+function bit_converter.sint16_to_bytes(int, order)
+ if int > MAX_INT16 or int < MIN_INT16 then
+ error("invalid sint16")
+ end
+
+ return uint16ToBytes(int + MAX_UINT16 + 1, order)
end
-function bit_converter.bytes_to_string(bytes)
+function bit_converter.bytes_to_float32(bytes, order)
+ return bytesToFloatOrDouble(toLE(bytes, order), 'f')
+end
+
+function bit_converter.bytes_to_float64(bytes, order)
+ return bytesToFloatOrDouble(toLE(bytes, order), 'd')
+end
+
+function bit_converter.bytes_to_single(bytes, order)
+ on_deprecated_call("bit_converter.bytes_to_single", "bit_converter.bytes_to_float32")
+ return bit_converter.bytes_to_float32(bytes, order)
+end
+
+function bit_converter.bytes_to_double(bytes, order)
+ on_deprecated_call("bit_converter.bytes_to_double", "bit_converter.bytes_to_float64")
+ return bit_converter.bytes_to_float64(bytes, order)
+end
+
+function bit_converter.bytes_to_string(bytes, order)
local len = bit_converter.bytes_to_uint16({ bytes[1], bytes[2] })
local str = ""
@@ -206,17 +287,13 @@ function bit_converter.byte_to_bool(byte)
return byte ~= 0
end
-function bit_converter.bytes_to_float(bytes)
- if #bytes < 8 then
- error("eof")
- end
- error("unsupported operation")
-end
-
-function bit_converter.bytes_to_uint32(bytes)
+function bit_converter.bytes_to_uint32(bytes, order)
if #bytes < 4 then
error("eof")
end
+
+ bytes = toLE(bytes, order)
+
return
bit.bor(
bit.bor(
@@ -226,20 +303,26 @@ function bit_converter.bytes_to_uint32(bytes)
bit.lshift(bytes[3], 8)),bytes[4])
end
-function bit_converter.bytes_to_uint16(bytes)
+function bit_converter.bytes_to_uint16(bytes, order)
if #bytes < 2 then
error("eof")
end
+
+ bytes = toLE(bytes, order)
+
return
bit.bor(
bit.lshift(bytes[1], 8),
bytes[2], 0)
end
-function bit_converter.bytes_to_int64(bytes)
+function bit_converter.bytes_to_int64(bytes, order)
if #bytes < 8 then
error("eof")
end
+
+ bytes = toLE(bytes, order)
+
return
bit.bor(
bit.bor(
@@ -257,12 +340,26 @@ function bit_converter.bytes_to_int64(bytes)
bit.lshift(bit.band(bytes[7], 0xFF), 8)),bit.band(bytes[8], 0xFF))
end
-function bit_converter.bytes_to_int32(bytes)
- return bit_converter.bytes_to_uint32(bytes) - MAX_INT32
+function bit_converter.bytes_to_int32(bytes, order)
+ on_deprecated_call("bit_converter.bytes_to_int32", "bit_converter.bytes_to_sint32")
+ return bit_converter.bytes_to_uint32(bytes, order) - MAX_INT32
end
-function bit_converter.bytes_to_int16(bytes)
- return bit_converter.bytes_to_uint16(bytes) - MAX_INT16
+function bit_converter.bytes_to_int16(bytes, order)
+ on_deprecated_call("bit_converter.bytes_to_int16", "bit_converter.bytes_to_sint16")
+ return bit_converter.bytes_to_uint16(bytes, order) - MAX_INT16
+end
+
+function bit_converter.bytes_to_sint32(bytes, order)
+ local num = bit_converter.bytes_to_uint32(bytes, order)
+
+ return MIN_INT32 * (bit.band(MAX_INT32 + 1, num) ~= 0 and 1 or 0) + bit.band(MAX_INT32, num)
+end
+
+function bit_converter.bytes_to_sint16(bytes, order)
+ local num = bit_converter.bytes_to_uint16(bytes, order)
+
+ return MIN_INT16 * (bit.band(MAX_INT16 + 1, num) ~= 0 and 1 or 0) + bit.band(MAX_INT16, num)
end
return bit_converter
diff --git a/res/modules/data_buffer.lua b/res/modules/data_buffer.lua
index aafe63e7..e3d207ff 100644
--- a/res/modules/data_buffer.lua
+++ b/res/modules/data_buffer.lua
@@ -18,22 +18,31 @@ local TYPE_UINT32 = 2
local TYPE_INT16 = 3
local TYPE_INT32 = 4
local TYPE_INT64 = 5
-local TYPE_DOUBLE = 6
+local TYPE_FLOAT64 = 6
+local TYPE_SINT16 = 7
+local TYPE_SINT32 = 8
-- Data buffer
local data_buffer =
{
__call =
- function(data_buffer, bytes)
- return data_buffer:new(bytes)
+ function(data_buffer, ...)
+ return data_buffer:new(...)
end
}
-function data_buffer:new(bytes)
+function data_buffer:new(bytes, order, useBytearray)
+ bytes = bytes or { }
+
+ if order then bit_converter.validate_order(order)
+ else order = bit_converter.default_order end
+
local obj = {
pos = 1,
- bytes = bytes or { }
+ order = order,
+ useBytearray = useBytearray or false,
+ bytes = useBytearray and Bytearray(bytes) or bytes
}
self.__index = self
@@ -42,6 +51,13 @@ function data_buffer:new(bytes)
return obj
end
+function data_buffer:set_order(order)
+ bit_converter.validate_order(order)
+
+ self.order = order
+ self.floatsOrder = order
+end
+
-- Push functions
function data_buffer:put_byte(byte)
@@ -49,7 +65,8 @@ function data_buffer:put_byte(byte)
error("invalid byte")
end
- self.bytes[self.pos] = byte
+ if self.useBytearray then self.bytes:insert(self.pos, byte)
+ else table.insert(self.bytes, self.pos, byte) end
self.pos = self.pos + 1
end
@@ -61,11 +78,21 @@ function data_buffer:put_bytes(bytes)
end
function data_buffer:put_single(single)
- self:put_bytes(bit_converter.single_to_bytes(single))
+ on_deprecated_call("data_buffer:put_single", "data_buffer:put_float32")
+ self:put_bytes(bit_converter.single_to_bytes(single, self.order))
end
function data_buffer:put_double(double)
- self:put_bytes(bit_converter.double_to_bytes(double))
+ on_deprecated_call("data_buffer:put_single", "data_buffer:put_float64")
+ self:put_bytes(bit_converter.double_to_bytes(double, self.order))
+end
+
+function data_buffer:put_float32(single)
+ self:put_bytes(bit_converter.float32_to_bytes(single, self.order))
+end
+
+function data_buffer:put_float64(float)
+ self:put_bytes(bit_converter.float64_to_bytes(float, self.order))
end
function data_buffer:put_string(str)
@@ -77,23 +104,33 @@ function data_buffer:put_bool(bool)
end
function data_buffer:put_uint16(uint16)
- self:put_bytes(bit_converter.uint16_to_bytes(uint16))
+ self:put_bytes(bit_converter.uint16_to_bytes(uint16, self.order))
end
function data_buffer:put_uint32(uint32)
- self:put_bytes(bit_converter.uint32_to_bytes(uint32))
+ self:put_bytes(bit_converter.uint32_to_bytes(uint32, self.order))
end
function data_buffer:put_int16(int16)
- self:put_bytes(bit_converter.int16_to_bytes(int16))
+ on_deprecated_call("data_buffer:put_int16", "data_buffer:put_sint16")
+ self:put_bytes(bit_converter.int16_to_bytes(int16, self.order))
end
function data_buffer:put_int32(int32)
- self:put_bytes(bit_converter.int32_to_bytes(int32))
+ on_deprecated_call("data_buffer:put_int32", "data_buffer:put_sint32")
+ self:put_bytes(bit_converter.int32_to_bytes(int32, self.order))
+end
+
+function data_buffer:put_sint16(int16)
+ self:put_bytes(bit_converter.sint16_to_bytes(int16, self.order))
+end
+
+function data_buffer:put_sint32(int32)
+ self:put_bytes(bit_converter.sint32_to_bytes(int32, self.order))
end
function data_buffer:put_int64(int64)
- self:put_bytes(bit_converter.int64_to_bytes(int64))
+ self:put_bytes(bit_converter.int64_to_bytes(int64, self.order))
end
function data_buffer:put_number(num)
@@ -101,8 +138,8 @@ function data_buffer:put_number(num)
local type
if math.floor(num) ~= num then
- type = TYPE_DOUBLE
- bytes = bit_converter.double_to_bytes(num)
+ type = TYPE_FLOAT64
+ bytes = bit_converter.float64_to_bytes(num)
elseif num == 0 then
type = TYPE_ZERO
bytes = { }
@@ -119,11 +156,11 @@ function data_buffer:put_number(num)
end
elseif num < 0 then
if num >= MIN_INT16 then
- type = TYPE_INT16
- bytes = bit_converter.int16_to_bytes(num)
+ type = TYPE_SINT16
+ bytes = bit_converter.sint16_to_bytes(num)
elseif num >= MIN_INT32 then
- type = TYPE_INT32
- bytes = bit_converter.int32_to_bytes(num)
+ type = TYPE_SINT32
+ bytes = bit_converter.sint32_to_bytes(num)
elseif num >= MIN_INT64 then
type = TYPE_INT64
bytes = bit_converter.int64_to_bytes(num)
@@ -155,9 +192,13 @@ function data_buffer:get_number()
return self:get_int16()
elseif type == TYPE_INT32 then
return self:get_int32()
+ elseif type == TYPE_SINT16 then
+ return self:get_sint16()
+ elseif type == TYPE_SINT32 then
+ return self:get_sint32()
elseif type == TYPE_INT64 then
return self:get_int64()
- elseif type == TYPE_DOUBLE then
+ elseif type == TYPE_FLOAT64 then
return self:get_double()
else
error("unknown lua number type: "..type)
@@ -165,11 +206,21 @@ function data_buffer:get_number()
end
function data_buffer:get_single()
- return bit_converter.bytes_to_single(self:get_bytes(4))
+ on_deprecated_call("data_buffer:get_single", "data_buffer:get_float32")
+ return bit_converter.bytes_to_single(self:get_bytes(4), self.order)
end
function data_buffer:get_double()
- return bit_converter.bytes_to_double(self:get_bytes(8))
+ on_deprecated_call("data_buffer:get_double", "data_buffer:get_float64")
+ return bit_converter.bytes_to_double(self:get_bytes(8), self.order)
+end
+
+function data_buffer:get_float32()
+ return bit_converter.bytes_to_float32(self:get_bytes(4), self.order)
+end
+
+function data_buffer:get_float64()
+ return bit_converter.bytes_to_float64(self:get_bytes(8), self.order)
end
function data_buffer:get_string()
@@ -193,23 +244,33 @@ function data_buffer:get_bool()
end
function data_buffer:get_uint16()
- return bit_converter.bytes_to_uint16(self:get_bytes(2))
+ return bit_converter.bytes_to_uint16(self:get_bytes(2), self.order)
end
function data_buffer:get_uint32()
- return bit_converter.bytes_to_uint32(self:get_bytes(4))
+ return bit_converter.bytes_to_uint32(self:get_bytes(4), self.order)
end
function data_buffer:get_int16()
- return bit_converter.bytes_to_int16(self:get_bytes(2))
+ on_deprecated_call("data_buffer:get_int16", "data_buffer:get_sint16")
+ return bit_converter.bytes_to_int16(self:get_bytes(2), self.order)
end
function data_buffer:get_int32()
- return bit_converter.bytes_to_int32(self:get_bytes(4))
+ on_deprecated_call("data_buffer:get_int32", "data_buffer:get_sint32")
+ return bit_converter.bytes_to_int32(self:get_bytes(4), self.order)
+end
+
+function data_buffer:get_sint16()
+ return bit_converter.bytes_to_sint16(self:get_bytes(2), self.order)
+end
+
+function data_buffer:get_sint32()
+ return bit_converter.bytes_to_sint32(self:get_bytes(4), self.order)
end
function data_buffer:get_int64()
- return bit_converter.bytes_to_int64(self:get_bytes(8))
+ return bit_converter.bytes_to_int64(self:get_bytes(8), self.order)
end
function data_buffer:size()
diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua
index 200f305f..909717cf 100644
--- a/res/scripts/classes.lua
+++ b/res/scripts/classes.lua
@@ -42,6 +42,7 @@ local Socket = {__index={
close=function(self) return network.__close(self.id) end,
is_alive=function(self) return network.__is_alive(self.id) end,
is_connected=function(self) return network.__is_connected(self.id) end,
+ get_address=function(self) return network.__get_address(self.id) end,
}}
network.tcp_connect = function(address, port, callback)
@@ -55,6 +56,7 @@ end
local ServerSocket = {__index={
close=function(self) return network.__closeserver(self.id) end,
is_open=function(self) return network.__is_serveropen(self.id) end,
+ get_port=function(self) return network.__get_serverport(self.id) end,
}}
network.tcp_open = function(port, handler)
diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua
index 14479c7d..13934e06 100644
--- a/res/scripts/stdmin.lua
+++ b/res/scripts/stdmin.lua
@@ -63,6 +63,20 @@ function table.copy(t)
return copied
end
+function table.deep_copy(t)
+ local copied = {}
+
+ for k, v in pairs(t) do
+ if type(v) == "table" then
+ copied[k] = table.deep_copy(v)
+ else
+ copied[k] = v
+ end
+ end
+
+ return copied
+end
+
function table.count_pairs(t)
local count = 0
diff --git a/res/texts/uz_UZ.txt b/res/texts/uz_UZ.txt
index 0ad6ec37..9d017ce9 100644
--- a/res/texts/uz_UZ.txt
+++ b/res/texts/uz_UZ.txt
@@ -1,4 +1,4 @@
-# Общее
+# Umumiy
Yes=Ha
No=Yo'q
Ok=Ок
@@ -10,18 +10,26 @@ Version=Versiya
Creator=Muallif
Dependencies=Bog'liqliklar
Description=Tavsif
-Converting world...=Dunyoni konvertatsiyalash amalga oshirilyapti...
+Converting world...=Dunyo konvertatsiya qilinmoqda...
Unlimited=Cheksiz
+Chat=Suhbat
+Console=Konsol
+Log=log
+Problems=Muammolar
+Monitor=Monitoring
+Debug=Xatolarni tuzatish (Debug)
+File=Fayl
+devtools.traceback=Chaqiruvlar steki (so‘nggisidan boshlab)
error.pack-not-found=Paketni topib bo'lmadi
error.dependency-not-found=Amaldagi qaramliklar topilmadi
-pack.remove-confirm=Paketlar bilan taqdim etilgan barcha mazmunni dunyodan olib tashlash (qaytarib bo'lmaydi)?
+pack.remove-confirm=Paketlar bilan taqdim etilgan barcha contentni dunyodan olib tashlash (qaytarib bo'lmaydi)?
-# Подсказки
+# Maslahatlar
graphics.gamma.tooltip=Yorug'lik yorqinligi egri chizig'i
graphics.backlight.tooltip=To'liq zulmatni oldini oladigan orqa yorug'ligi
-# Меню
+# Menyu
menu.Apply=Qo'llash
menu.Audio=Tovush
menu.Back to Main Menu=Menyuga qaytish
@@ -31,61 +39,73 @@ menu.Continue=Davom ettirish
menu.Controls=Boshqaruv
menu.Display=Displey
menu.Graphics=Grafika
-menu.missing-content=Kontent etishmayapti!
+menu.missing-content=Kontent mavjud emas!
menu.New World=Yangi Dunyo
-menu.Worlds=Dunyo
-menu.Open worlds folder=Dunyo papkasini ochish
+menu.Open worlds folder=Dunyolar papkasini ochish
+menu.Worlds=Dunyolar
menu.Page not found=Sahifa topilmadi
menu.Quit=Chiqish
-menu.Save and Quit to Menu=Saqlash va menyuga chiqish
+menu.Save and Quit to Menu=Saqlash va Menyuga chiqish
menu.Settings=Sozlamalar
-menu.Contents Menu=Kontent to'plamlari menyusi
+menu.Reset settings=Sozlamalarni tiklash
+menu.Contents Menu=Kontent paklar menyusi
+menu.Open data folder=Ma'lumotlar papkasini ochish
+menu.Open content folder=[content] papkasini ochish
world.Seed=Don
world.Name=Nom
-world.World generator=Dunyo yaratuvchi
-world.generators.default=Oddiy
-world.generators.flat=Yassi
-world.Create World=Dunyoni Yaratish
-world.convert-request=Indekslarda o‘zgarishlar mavjud! Dunyoni konvertatsiya qilish kerakmi?
-world.delete-confirm=Dunyoni qaytarib bo'lmaydigan tarzda olib tashlaysizmi?
+world.World generator=Dunyo generatori
+world.generators.default=Standart bo'yicha
+world.generators.flat=Tekis
+world.Create World=Dunyo yaratish
+world.convert-request=Indekslar bo'yicha o'zgarishlar bor! Dunyoni konvertatsiya qilasizmi?
+world.upgrade-request=Dunyo formati eskirgan! Dunyoni konvertatsiya qilasizmi?
+world.convert-with-loss=Dunyoni yo'qotishlar bilan konvertatsiya qilasizmi?
+world.convert-block-layouts=Blok maydonlarida o'zgarishlar bor! Dunyoni konvertatsiya qilasizmi?
+world.delete-confirm=Dunyoni doimiy o'chirasizmi?
-# Настройки
+# Sozlamalar
settings.Ambient=Fon
settings.Backlight=Yoritish
-settings.Camera Shaking=Kamera Silkinishi
-settings.Camera Inertia=Kamera Inertsiyasi
-settings.Fog Curve=Tuman Egri Chizig'i
-settings.FOV=Ko'rish Maydoni
+settings.Camera Shaking=Kamera titrashi
+settings.Camera Inertia=Kamera inertsiyasi
+settings.Camera FOV Effects=Ko'rish maydoni effektlari
+settings.Fog Curve=Tuman egri chizig'i
+settings.FOV=Ko'rish maydoni
settings.Fullscreen=To'liq ekran
-settings.Framerate=Kadr tezligi
+settings.Framerate=Kadrlar chastotasi
settings.Gamma=Gamma
settings.Language=Til
-settings.Load Distance=Yuklash Masofasi
-settings.Load Speed=Yuklash Tezligi
-settings.Master Volume=Umumiy Ovoz Balandligi
-settings.Mouse Sensitivity=Sichqoncha Sezgirligi
+settings.Load Distance=Yuklash masofasi
+settings.Load Speed=Yuklash tezligi
+settings.Master Volume=Umumiy ovoz balansi
+settings.Mouse Sensitivity=Sichqoncha sezgirligi
settings.Music=Musiqa
-settings.Regular Sounds=Oddiy Tovushlar
-settings.UI Sounds=Interfeys Tovushlari
-settings.V-Sync=Vertikal Sinxronizatsiya
+settings.Regular Sounds=Oddiy tovushlar
+settings.UI Sounds=Interfeys tovushlari
+settings.V-Sync=Vertikal sinxronizatsiya
+settings.Key=Tugma
+settings.Controls Search Mode=Tayinlangan boshqaruv tugmasi bo'yicha qidirish
+settings.Limit Background FPS=Fon kadrlar chastotasini cheklash
-# Управление
+# Boshqaruv
chunks.reload=Chanklarni qayta yuklash
devtools.console=Konsol
movement.forward=Oldinga
movement.back=Orqaga
movement.left=Chapga
-movement.right=O'ng tomonda
+movement.right=O'ngga
movement.jump=Sakrash
-movement.sprint=Tezlanish
+movement.sprint=Tez yugurish
movement.crouch=Egilish
-movement.cheat=Chit
-hud.inventory=inventar
+movement.cheat=Hiyla (Chit)
+hud.inventory=Inventar
player.pick=Blokni olish
-player.attack=Hujum Qilish / Sindirish
-player.build=Blokni Joylashtiring
+player.attack=Hujum qilish
+player.destroy=Sindirish
+player.build=Blokni qo'yish
+player.fast_interaction=Tezkor o'zaro ta'sir
player.flight=Parvoz
player.drop=Narsani tashlash
camera.zoom=Yaqinlashish
-camera.mode=Kamera Rejimini O'zgartiring
+camera.mode=Kamera Rejimini O'zgartiring
\ No newline at end of file
diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp
index de4fb755..cd9a2d4c 100644
--- a/src/coders/commons.cpp
+++ b/src/coders/commons.cpp
@@ -214,6 +214,28 @@ std::string_view BasicParser::readUntil(char c) {
return source.substr(start, pos - start);
}
+std::string_view BasicParser::readUntil(std::string_view s, bool nothrow) {
+ int start = pos;
+ size_t found = source.find(s, pos);
+ if (found == std::string::npos) {
+ if (nothrow) {
+ pos = source.size();
+ return source.substr(start);
+ }
+ throw error(util::quote(std::string(s))+" expected");
+ }
+ skip(found - pos);
+ return source.substr(start, pos - start);
+}
+
+std::string_view BasicParser::readUntilWhitespace() {
+ int start = pos;
+ while (hasNext() && !is_whitespace(source[pos])) {
+ pos++;
+ }
+ return source.substr(start, pos - start);
+}
+
std::string_view BasicParser::readUntilEOL() {
int start = pos;
while (hasNext() && source[pos] != '\r' && source[pos] != '\n') {
diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp
index abd4c01a..9251d57d 100644
--- a/src/coders/commons.hpp
+++ b/src/coders/commons.hpp
@@ -105,6 +105,8 @@ protected:
parsing_error error(const std::string& message);
public:
std::string_view readUntil(char c);
+ std::string_view readUntil(std::string_view s, bool nothrow);
+ std::string_view readUntilWhitespace();
std::string_view readUntilEOL();
std::string parseName();
std::string parseXmlName();
diff --git a/src/coders/json.cpp b/src/coders/json.cpp
index 017f7d26..0990ff47 100644
--- a/src/coders/json.cpp
+++ b/src/coders/json.cpp
@@ -11,15 +11,17 @@
using namespace json;
-class Parser : BasicParser {
- dv::value parseList();
- dv::value parseObject();
- dv::value parseValue();
-public:
- Parser(std::string_view filename, std::string_view source);
+namespace {
+ class Parser : BasicParser {
+ dv::value parseList();
+ dv::value parseObject();
+ dv::value parseValue();
+ public:
+ Parser(std::string_view filename, std::string_view source);
- dv::value parse();
-};
+ dv::value parse();
+ };
+}
inline void newline(
std::stringstream& ss, bool nice, uint indent, const std::string& indentstr
diff --git a/src/coders/lua_parsing.cpp b/src/coders/lua_parsing.cpp
new file mode 100644
index 00000000..f30aab37
--- /dev/null
+++ b/src/coders/lua_parsing.cpp
@@ -0,0 +1,188 @@
+#include "lua_parsing.hpp"
+
+#include
+
+#include "commons.hpp"
+
+using namespace lua;
+using namespace devtools;
+
+static std::set keywords {
+ "and", "break", "do", "else", "elseif", "end", "false", "for", "function",
+ "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true",
+ "until", "while"
+};
+
+bool lua::is_lua_keyword(std::string_view view) {
+ return keywords.find(view) != keywords.end();
+}
+
+inline bool is_lua_identifier_start(int c) {
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
+}
+
+inline bool is_lua_identifier_part(int c) {
+ return is_lua_identifier_start(c) || is_digit(c);
+}
+
+inline bool is_lua_operator_start(int c) {
+ return c == '=' || c == '~' || c == '+' || c == '-' || c == '/' || c == '*'
+ || c == '%' || c == '^' || c == '#' || c == '<' || c == '>' || c == ':'
+ || c == '.';
+}
+
+class Tokenizer : BasicParser {
+ std::vector tokens;
+public:
+ Tokenizer(std::string_view file, std::string_view source)
+ : BasicParser(file, source) {
+ }
+
+ std::string parseLuaName() {
+ char c = peek();
+ if (!is_identifier_start(c)) {
+ throw error("identifier expected");
+ }
+ int start = pos;
+ while (hasNext() && is_identifier_part(source[pos])) {
+ pos++;
+ }
+ return std::string(source.substr(start, pos - start));
+ }
+
+ inline Location currentLocation() const {
+ return Location {
+ static_cast(pos),
+ static_cast(linestart),
+ static_cast(line)};
+ }
+
+ void emitToken(
+ TokenTag tag, std::string name, Location start, bool standalone=false
+ ) {
+ tokens.emplace_back(
+ tag,
+ std::move(name),
+ std::move(start),
+ currentLocation()
+ );
+ if (standalone) skip(1);
+ }
+
+ /// @brief Get next operator token without checking operator for existing
+ std::string parseOperator() {
+ int start = pos;
+ char first = peek();
+ switch (first) {
+ case '#': case '+': case '/': case '*': case '^':
+ case '%':
+ skip(1);
+ return std::string({first});
+ case '-':
+ skip(1);
+ if (peekNoJump() == '-') {
+ skip(1);
+ return "--";
+ }
+ return std::string({first});
+ }
+ skip(1);
+ char second = peekNoJump();
+ if ((first == '=' && second == '=') || (first == '~' && second == '=') ||
+ (first == '<' && second == '=') || (first == '>' && second == '=')) {
+ skip(1);
+ return std::string(source.substr(start, pos - start));
+ }
+ if (first == '.' && second == '.') {
+ skip(1);
+ if (peekNoJump() == '.') {
+ skip(1);
+ }
+ }
+ return std::string(source.substr(start, pos - start));
+ }
+
+ std::vector tokenize() {
+ skipWhitespace();
+ while (hasNext()) {
+ skipWhitespace();
+ if (!hasNext()) {
+ continue;
+ }
+ char c = peek();
+ auto start = currentLocation();
+ if (is_lua_identifier_start(c)) {
+ auto name = parseLuaName();
+ emitToken(
+ is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME,
+ std::move(name),
+ start
+ );
+ continue;
+ } else if (is_digit(c)) {
+ dv::value value;
+ auto tag = TokenTag::UNEXPECTED;
+ try {
+ value = parseNumber(1);
+ tag = value.isInteger() ? TokenTag::INTEGER
+ : TokenTag::NUMBER;
+ } catch (const parsing_error& err) {}
+
+ auto literal = source.substr(start.pos, pos - start.pos);
+ emitToken(tag, std::string(literal), start);
+ continue;
+ }
+ switch (c) {
+ case '(': case '[': case '{':
+ if (isNext("[==[")) {
+ auto string = readUntil("]==]", true);
+ skip(4);
+ emitToken(TokenTag::COMMENT, std::string(string)+"]==]", start);
+ continue;
+ } else if (isNext("[[")) {
+ skip(2);
+ auto string = readUntil("]]", true);
+ skip(2);
+ emitToken(TokenTag::STRING, std::string(string), start);
+ continue;
+ }
+ emitToken(TokenTag::OPEN_BRACKET, std::string({c}), start, true);
+ continue;
+ case ')': case ']': case '}':
+ emitToken(TokenTag::CLOSE_BRACKET, std::string({c}), start, true);
+ continue;
+ case ',':
+ emitToken(TokenTag::COMMA, std::string({c}), start, true);
+ continue;
+ case ';':
+ emitToken(TokenTag::SEMICOLON, std::string({c}), start, true);
+ continue;
+ case '\'': case '"': {
+ skip(1);
+ auto string = parseString(c, false);
+ emitToken(TokenTag::STRING, std::move(string), start);
+ continue;
+ }
+ default: break;
+ }
+ if (is_lua_operator_start(c)) {
+ auto text = parseOperator();
+ if (text == "--") {
+ auto string = readUntilEOL();
+ emitToken(TokenTag::COMMENT, std::string(string), start);
+ skipLine();
+ continue;
+ }
+ emitToken(TokenTag::OPERATOR, std::move(text), start);
+ continue;
+ }
+ auto text = readUntilWhitespace();
+ emitToken(TokenTag::UNEXPECTED, std::string(text), start);
+ }
+ return std::move(tokens);
+ }
+};
+
+std::vector lua::tokenize(std::string_view file, std::string_view source) {
+ return Tokenizer(file, source).tokenize();
+}
diff --git a/src/coders/lua_parsing.hpp b/src/coders/lua_parsing.hpp
new file mode 100644
index 00000000..0054e4d0
--- /dev/null
+++ b/src/coders/lua_parsing.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include
+#include
+
+#include "devtools/syntax.hpp"
+
+namespace lua {
+ bool is_lua_keyword(std::string_view view);
+
+ std::vector tokenize(
+ std::string_view file, std::string_view source
+ );
+}
diff --git a/src/coders/png.cpp b/src/coders/png.cpp
index 60965ed1..b29afd34 100644
--- a/src/coders/png.cpp
+++ b/src/coders/png.cpp
@@ -13,7 +13,7 @@
static debug::Logger logger("png-coder");
// returns 0 if all-right, 1 otherwise
-int _png_write(
+static int png_write(
const char* filename, uint width, uint height, const ubyte* data, bool alpha
) {
uint pixsize = alpha ? 4 : 3;
@@ -112,7 +112,7 @@ static void read_in_memory(png_structp pngPtr, png_bytep dst, png_size_t toread)
}
std::unique_ptr png::load_image(const ubyte* bytes, size_t size) {
- if (!png_check_sig(bytes, size)) {
+ if (size < 8 || !png_check_sig(bytes, 8)) {
throw std::runtime_error("invalid png signature");
}
png_structp pngPtr = nullptr;
@@ -223,7 +223,7 @@ std::unique_ptr png::load_texture(const std::string& filename) {
}
void png::write_image(const std::string& filename, const ImageData* image) {
- _png_write(
+ png_write(
filename.c_str(),
image->getWidth(),
image->getHeight(),
diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp
index faf84f15..b2cb8809 100644
--- a/src/coders/xml.cpp
+++ b/src/coders/xml.cpp
@@ -107,8 +107,8 @@ glm::vec4 Attribute::asColor() const {
Node::Node(std::string tag) : tag(std::move(tag)) {
}
-void Node::add(const xmlelement& element) {
- elements.push_back(element);
+void Node::add(std::unique_ptr element) {
+ elements.push_back(std::move(element));
}
void Node::set(const std::string& name, const std::string& text) {
@@ -119,7 +119,7 @@ const std::string& Node::getTag() const {
return tag;
}
-const xmlattribute& Node::attr(const std::string& name) const {
+const Attribute& Node::attr(const std::string& name) const {
auto found = attrs.find(name);
if (found == attrs.end()) {
throw std::runtime_error(
@@ -129,7 +129,7 @@ const xmlattribute& Node::attr(const std::string& name) const {
return found->second;
}
-xmlattribute Node::attr(const std::string& name, const std::string& def) const {
+Attribute Node::attr(const std::string& name, const std::string& def) const {
auto found = attrs.find(name);
if (found == attrs.end()) {
return Attribute(name, def);
@@ -142,19 +142,23 @@ bool Node::has(const std::string& name) const {
return found != attrs.end();
}
-xmlelement Node::sub(size_t index) {
- return elements.at(index);
+Node& Node::sub(size_t index) {
+ return *elements.at(index);
+}
+
+const Node& Node::sub(size_t index) const {
+ return *elements.at(index);
}
size_t Node::size() const {
return elements.size();
}
-const std::vector& Node::getElements() const {
+const std::vector>& Node::getElements() const {
return elements;
}
-const xmlelements_map& Node::getAttributes() const {
+const std::unordered_map& Node::getAttributes() const {
return attrs;
}
@@ -162,12 +166,12 @@ Document::Document(std::string version, std::string encoding)
: version(std::move(version)), encoding(std::move(encoding)) {
}
-void Document::setRoot(const xmlelement& element) {
- this->root = element;
+void Document::setRoot(std::unique_ptr element) {
+ root = std::move(element);
}
-xmlelement Document::getRoot() const {
- return root;
+const Node* Document::getRoot() const {
+ return root.get();
}
const std::string& Document::getVersion() const {
@@ -178,82 +182,6 @@ const std::string& Document::getEncoding() const {
return encoding;
}
-Parser::Parser(std::string_view filename, std::string_view source)
- : BasicParser(filename, source) {
-}
-
-xmlelement Parser::parseOpenTag() {
- std::string tag = parseXMLName();
- auto node = std::make_shared(tag);
-
- char c;
- while (true) {
- skipWhitespace();
- c = peek();
- if (c == '/' || c == '>' || c == '?') break;
- std::string attrname = parseXMLName();
- std::string attrtext = "";
- skipWhitespace();
- if (peek() == '=') {
- nextChar();
- skipWhitespace();
-
- char quote = peek();
- if (quote != '\'' && quote != '"') {
- throw error("string literal expected");
- }
- skip(1);
- attrtext = parseString(quote);
- }
- node->set(attrname, attrtext);
- }
- return node;
-}
-
-void Parser::parseDeclaration() {
- std::string version = "1.0";
- std::string encoding = "UTF-8";
- expect('<');
- if (peek() == '?') {
- nextChar();
- xmlelement node = parseOpenTag();
- expect("?>");
- if (node->getTag() != "xml") {
- throw error("invalid declaration");
- }
- version = node->attr("version", version).getText();
- encoding = node->attr("encoding", encoding).getText();
- if (encoding != "utf-8" && encoding != "UTF-8") {
- throw error("UTF-8 encoding is only supported");
- }
- } else {
- goBack();
- }
- document = std::make_shared(version, encoding);
-}
-
-void Parser::parseComment() {
- expect("!--");
- if (skipTo("-->")) {
- skip(3);
- } else {
- throw error("comment close missing");
- }
-}
-
-std::string Parser::parseText() {
- size_t start = pos;
- while (hasNext()) {
- char c = peek();
- if (c == '<') {
- break;
- }
- nextChar();
- }
- return Parser("[string]", std::string(source.substr(start, pos - start)))
- .parseString('\0', false);
-}
-
inline bool is_xml_identifier_start(char c) {
return is_identifier_start(c) || c == ':';
}
@@ -262,82 +190,166 @@ inline bool is_xml_identifier_part(char c) {
return is_identifier_part(c) || c == '-' || c == '.' || c == ':';
}
-std::string Parser::parseXMLName() {
- char c = peek();
- if (!is_xml_identifier_start(c)) {
- throw error("identifier expected");
- }
- int start = pos;
- while (hasNext() && is_xml_identifier_part(source[pos])) {
- pos++;
- }
- return std::string(source.substr(start, pos - start));
-}
+namespace {
+class Parser : BasicParser {
+ std::unique_ptr document;
-xmlelement Parser::parseElement() {
- // text element
- if (peek() != '<') {
- auto element = std::make_shared("#");
- auto text = parseText();
- util::replaceAll(text, """, "\"");
- util::replaceAll(text, "'", "'");
- util::replaceAll(text, "<", "<");
- util::replaceAll(text, ">", ">");
- util::replaceAll(text, "&", "&");
- element->set("#", text);
+ std::unique_ptr parseOpenTag() {
+ std::string tag = parseXMLName();
+ auto node = std::make_unique(tag);
+
+ char c;
+ while (true) {
+ skipWhitespace();
+ c = peek();
+ if (c == '/' || c == '>' || c == '?') break;
+ std::string attrname = parseXMLName();
+ std::string attrtext = "";
+ skipWhitespace();
+ if (peek() == '=') {
+ nextChar();
+ skipWhitespace();
+
+ char quote = peek();
+ if (quote != '\'' && quote != '"') {
+ throw error("string literal expected");
+ }
+ skip(1);
+ attrtext = parseString(quote);
+ }
+ node->set(attrname, attrtext);
+ }
+ return node;
+ }
+
+ std::unique_ptr parseElement() {
+ // text element
+ if (peek() != '<') {
+ auto element = std::make_unique("#");
+ auto text = parseText();
+ util::replaceAll(text, """, "\"");
+ util::replaceAll(text, "'", "'");
+ util::replaceAll(text, "<", "<");
+ util::replaceAll(text, ">", ">");
+ util::replaceAll(text, "&", "&");
+ element->set("#", text);
+ return element;
+ }
+ nextChar();
+
+ //
+ if (peek() == '!') {
+ if (isNext("!DOCTYPE ")) {
+ throw error("XML DTD is not supported yet");
+ }
+ parseComment();
+ return nullptr;
+ }
+
+ auto element = parseOpenTag();
+ char c = nextChar();
+
+ //
+ if (c == '/') {
+ expect('>');
+ }
+ // ...
+ else if (c == '>') {
+ skipWhitespace();
+ while (!isNext("")) {
+ auto sub = parseElement();
+ if (sub) {
+ element->add(std::move(sub));
+ }
+ skipWhitespace();
+ }
+ skip(2);
+ expect(element->getTag());
+ expect('>');
+ }
+ //
+ else {
+ throw error("invalid syntax");
+ }
return element;
}
- nextChar();
- //
- if (peek() == '!') {
- if (isNext("!DOCTYPE ")) {
- throw error("XML DTD is not supported yet");
- }
- parseComment();
- return nullptr;
- }
-
- auto element = parseOpenTag();
- char c = nextChar();
-
- //
- if (c == '/') {
- expect('>');
- }
- // ...
- else if (c == '>') {
- skipWhitespace();
- while (!isNext("")) {
- auto sub = parseElement();
- if (sub) {
- element->add(sub);
+ void parseDeclaration() {
+ std::string version = "1.0";
+ std::string encoding = "UTF-8";
+ expect('<');
+ if (peek() == '?') {
+ nextChar();
+ auto node = parseOpenTag();
+ expect("?>");
+ if (node->getTag() != "xml") {
+ throw error("invalid declaration");
}
- skipWhitespace();
+ version = node->attr("version", version).getText();
+ encoding = node->attr("encoding", encoding).getText();
+ if (encoding != "utf-8" && encoding != "UTF-8") {
+ throw error("UTF-8 encoding is only supported");
+ }
+ } else {
+ goBack();
}
- skip(2);
- expect(element->getTag());
- expect('>');
+ document = std::make_unique(version, encoding);
}
- //
- else {
- throw error("invalid syntax");
+
+ void parseComment() {
+ expect("!--");
+ if (skipTo("-->")) {
+ skip(3);
+ } else {
+ throw error("comment close missing");
+ }
}
- return element;
+
+ std::string parseText() {
+ size_t start = pos;
+ while (hasNext()) {
+ char c = peek();
+ if (c == '<') {
+ break;
+ }
+ nextChar();
+ }
+ return Parser("[string]", std::string(source.substr(start, pos - start)))
+ .parseString('\0', false);
+ }
+
+ std::string parseXMLName() {
+ char c = peek();
+ if (!is_xml_identifier_start(c)) {
+ throw error("identifier expected");
+ }
+ int start = pos;
+ while (hasNext() && is_xml_identifier_part(source[pos])) {
+ pos++;
+ }
+ return std::string(source.substr(start, pos - start));
+ }
+public:
+ Parser(std::string_view filename, std::string_view source)
+ : BasicParser(filename, source) {
+ }
+
+ std::unique_ptr parse() {
+ parseDeclaration();
+
+ std::unique_ptr root;
+ while (root == nullptr) {
+ root = parseElement();
+ }
+ document->setRoot(std::move(root));
+ return std::move(document);
+ }
+};
}
-xmldocument Parser::parse() {
- parseDeclaration();
-
- xmlelement root = nullptr;
- while (root == nullptr) {
- root = parseElement();
- }
- document->setRoot(root);
- return document;
-}
-
-xmldocument xml::parse(std::string_view filename, std::string_view source) {
+std::unique_ptr xml::parse(
+ std::string_view filename, std::string_view source
+) {
Parser parser(filename, source);
return parser.parse();
}
@@ -354,13 +366,13 @@ inline void newline(
static void stringifyElement(
std::stringstream& ss,
- const xmlelement& element,
+ const Node& element,
bool nice,
const std::string& indentStr,
int indent
) {
- if (element->isText()) {
- std::string text = element->attr("#").getText();
+ if (element.isText()) {
+ std::string text = element.attr("#").getText();
util::replaceAll(text, "&", "&");
util::replaceAll(text, "\"", """);
util::replaceAll(text, "'", "'");
@@ -369,10 +381,10 @@ static void stringifyElement(
ss << text;
return;
}
- const std::string& tag = element->getTag();
+ const std::string& tag = element.getTag();
ss << '<' << tag;
- auto& attrs = element->getAttributes();
+ auto& attrs = element.getAttributes();
if (!attrs.empty()) {
ss << ' ';
int count = 0;
@@ -388,10 +400,10 @@ static void stringifyElement(
count++;
}
}
- auto& elements = element->getElements();
+ auto& elements = element.getElements();
if (elements.size() == 1 && elements[0]->isText()) {
ss << ">";
- stringifyElement(ss, elements[0], nice, indentStr, indent + 1);
+ stringifyElement(ss, *elements[0], nice, indentStr, indent + 1);
ss << "" << tag << ">";
return;
}
@@ -399,7 +411,7 @@ static void stringifyElement(
ss << '>';
for (auto& sub : elements) {
newline(ss, nice, indentStr, indent + 1);
- stringifyElement(ss, sub, nice, indentStr, indent + 1);
+ stringifyElement(ss, *sub, nice, indentStr, indent + 1);
}
newline(ss, nice, indentStr, indent);
ss << "" << tag << ">";
@@ -410,16 +422,16 @@ static void stringifyElement(
}
std::string xml::stringify(
- const xmldocument& document, bool nice, const std::string& indentStr
+ const Document& document, bool nice, const std::string& indentStr
) {
std::stringstream ss;
// XML declaration
- ss << "getVersion();
+ ss << "";
newline(ss, nice, indentStr, 0);
- stringifyElement(ss, document->getRoot(), nice, indentStr, 0);
+ stringifyElement(ss, *document.getRoot(), nice, indentStr, 0);
return ss.str();
}
diff --git a/src/coders/xml.hpp b/src/coders/xml.hpp
index 54a2b589..5831c29a 100644
--- a/src/coders/xml.hpp
+++ b/src/coders/xml.hpp
@@ -13,11 +13,6 @@ namespace xml {
class Attribute;
class Document;
- using xmlattribute = Attribute;
- using xmlelement = std::shared_ptr;
- using xmldocument = std::shared_ptr;
- using xmlelements_map = std::unordered_map;
-
class Attribute {
std::string name;
std::string text;
@@ -40,13 +35,15 @@ namespace xml {
/// 'text'
class Node {
std::string tag;
- std::unordered_map attrs;
- std::vector elements;
+ std::unordered_map attrs;
+ std::vector> elements;
public:
Node(std::string tag);
+ Node(const Node&) = delete;
+
/// @brief Add sub-element
- void add(const xmlelement& element);
+ void add(std::unique_ptr element);
/// @brief Set attribute value. Creates attribute if does not exists
/// @param name attribute name
@@ -67,15 +64,15 @@ namespace xml {
/// @brief Get attribute by name
/// @param name attribute name
/// @throws std::runtime_error if element has no attribute
- /// @return xmlattribute - {name, value}
- const xmlattribute& attr(const std::string& name) const;
+ /// @return xml attribute - {name, value}
+ const Attribute& attr(const std::string& name) const;
/// @brief Get attribute by name
/// @param name attribute name
/// @param def default value will be returned wrapped in xmlattribute
/// if element has no attribute
- /// @return xmlattribute - {name, value} or {name, def} if not found*/
- xmlattribute attr(const std::string& name, const std::string& def)
+ /// @return xml attribute - {name, value} or {name, def} if not found
+ Attribute attr(const std::string& name, const std::string& def)
const;
/// @brief Check if element has attribute
@@ -86,51 +83,37 @@ namespace xml {
/// @param index sub-element index
/// @throws std::out_of_range if an invalid index given
/// @return sub-element
- xmlelement sub(size_t index);
+ Node& sub(size_t index);
+ const Node& sub(size_t index) const;
/// @brief Get number of sub-elements
size_t size() const;
- const std::vector& getElements() const;
- const xmlelements_map& getAttributes() const;
+ const std::vector>& getElements() const;
+ const std::unordered_map& getAttributes() const;
};
class Document {
- xmlelement root = nullptr;
+ std::unique_ptr root = nullptr;
std::string version;
std::string encoding;
public:
Document(std::string version, std::string encoding);
- void setRoot(const xmlelement& element);
- xmlelement getRoot() const;
+ void setRoot(std::unique_ptr element);
+ const Node* getRoot() const;
const std::string& getVersion() const;
const std::string& getEncoding() const;
};
- class Parser : BasicParser {
- xmldocument document;
-
- xmlelement parseOpenTag();
- xmlelement parseElement();
- void parseDeclaration();
- void parseComment();
- std::string parseText();
- std::string parseXMLName();
- public:
- Parser(std::string_view filename, std::string_view source);
-
- xmldocument parse();
- };
-
/// @brief Serialize XML Document to string
/// @param document serializing document
/// @param nice use human readable format (with indents and line-separators)
/// @param indentStr indentation characters sequence (default - 4 spaces)
/// @return XML string
- extern std::string stringify(
- const xmldocument& document,
+ std::string stringify(
+ const Document& document,
bool nice = true,
const std::string& indentStr = " "
);
@@ -139,7 +122,9 @@ namespace xml {
/// @param filename file name will be shown in error messages
/// @param source xml source code string
/// @return xml document
- extern xmldocument parse(
+ std::unique_ptr parse(
std::string_view filename, std::string_view source
);
+
+ using xmlelement = Node;
}
diff --git a/src/constants.hpp b/src/constants.hpp
index 84d8b22c..c8de5a8d 100644
--- a/src/constants.hpp
+++ b/src/constants.hpp
@@ -6,7 +6,7 @@
#include
inline constexpr int ENGINE_VERSION_MAJOR = 0;
-inline constexpr int ENGINE_VERSION_MINOR = 25;
+inline constexpr int ENGINE_VERSION_MINOR = 26;
#ifdef NDEBUG
inline constexpr bool ENGINE_DEBUG_BUILD = false;
@@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false;
inline constexpr bool ENGINE_DEBUG_BUILD = true;
#endif // NDEBUG
-inline const std::string ENGINE_VERSION_STRING = "0.25";
+inline const std::string ENGINE_VERSION_STRING = "0.26";
/// @brief world regions format version
inline constexpr uint REGION_FORMAT_VERSION = 3;
diff --git a/src/devtools/syntax.hpp b/src/devtools/syntax.hpp
new file mode 100644
index 00000000..6c7d4b15
--- /dev/null
+++ b/src/devtools/syntax.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+
+namespace devtools {
+ struct Location {
+ int pos;
+ int lineStart;
+ int line;
+ };
+
+ enum class TokenTag {
+ KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING,
+ OPERATOR, COMMA, SEMICOLON, UNEXPECTED, COMMENT
+ };
+
+ struct Token {
+ TokenTag tag;
+ std::string text;
+ Location start;
+ Location end;
+
+ Token(TokenTag tag, std::string text, Location start, Location end)
+ : tag(tag),
+ text(std::move(text)),
+ start(std::move(start)),
+ end(std::move(end)) {
+ }
+ };
+}
diff --git a/src/devtools/syntax_highlighting.cpp b/src/devtools/syntax_highlighting.cpp
new file mode 100644
index 00000000..f68780df
--- /dev/null
+++ b/src/devtools/syntax_highlighting.cpp
@@ -0,0 +1,72 @@
+#include "syntax_highlighting.hpp"
+
+#include "coders/commons.hpp"
+#include "coders/lua_parsing.hpp"
+#include "graphics/core/Font.hpp"
+
+using namespace devtools;
+
+static std::unique_ptr build_styles(
+ const std::vector& tokens
+) {
+ using devtools::TokenTag;
+ FontStylesScheme styles {
+ {
+ {false, false, false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default
+ {true, false, false, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword
+ {false, false, false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string
+ {false, false, false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment
+ {true, false, false, false, glm::vec4(1.0f, 0.2f, 0.1f, 1)}, // unexpected
+ },
+ {}
+ };
+ size_t offset = 0;
+ for (int i = 0; i < tokens.size(); i++) {
+ const auto& token = tokens.at(i);
+ if (token.tag != TokenTag::KEYWORD &&
+ token.tag != TokenTag::STRING &&
+ token.tag != TokenTag::INTEGER &&
+ token.tag != TokenTag::NUMBER &&
+ token.tag != TokenTag::COMMENT &&
+ token.tag != TokenTag::UNEXPECTED) {
+ continue;
+ }
+ if (token.start.pos > offset) {
+ int n = token.start.pos - offset;
+ styles.map.insert(styles.map.end(), token.start.pos - offset, 0);
+ }
+ offset = token.end.pos;
+ int styleIndex;
+ switch (token.tag) {
+ case TokenTag::KEYWORD: styleIndex = SyntaxStyles::KEYWORD; break;
+ case TokenTag::STRING:
+ case TokenTag::INTEGER:
+ case TokenTag::NUMBER: styleIndex = SyntaxStyles::LITERAL; break;
+ case TokenTag::COMMENT: styleIndex = SyntaxStyles::COMMENT; break;
+ case TokenTag::UNEXPECTED: styleIndex = SyntaxStyles::ERROR; break;
+ default:
+ styleIndex = 0;
+ break;
+ }
+ styles.map.insert(
+ styles.map.end(), token.end.pos - token.start.pos, styleIndex
+ );
+ }
+ styles.map.push_back(0);
+ return std::make_unique(std::move(styles));
+}
+
+std::unique_ptr devtools::syntax_highlight(
+ const std::string& lang, std::string_view source
+) {
+ try {
+ if (lang == "lua") {
+ auto tokens = lua::tokenize("", source);
+ return build_styles(tokens);
+ } else {
+ return nullptr;
+ }
+ } catch (const parsing_error& err) {
+ return nullptr;
+ }
+}
diff --git a/src/devtools/syntax_highlighting.hpp b/src/devtools/syntax_highlighting.hpp
new file mode 100644
index 00000000..18db6389
--- /dev/null
+++ b/src/devtools/syntax_highlighting.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include
+#include
+
+struct FontStylesScheme;
+
+namespace devtools {
+ enum SyntaxStyles {
+ DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR
+ };
+
+ std::unique_ptr syntax_highlight(
+ const std::string& lang, std::string_view source
+ );
+}
diff --git a/src/engine.cpp b/src/engine.cpp
index 31db4537..129dc68d 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -206,7 +206,7 @@ void Engine::renderFrame(Batch2D& batch) {
Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, &batch);
- gui->draw(&ctx, assets.get());
+ gui->draw(ctx, *assets);
}
void Engine::processPostRunnables() {
diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp
index fff98f3f..2108b46e 100644
--- a/src/frontend/UiDocument.cpp
+++ b/src/frontend/UiDocument.cpp
@@ -67,9 +67,7 @@ std::unique_ptr UiDocument::read(
: scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(env);
- auto view = reader.readXML(
- file.u8string(), xmldoc->getRoot()
- );
+ auto view = reader.readXML(file.u8string(), *xmldoc->getRoot());
view->setId("root");
uidocscript script {};
auto scriptFile = fs::path(file.u8string()+".lua");
diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp
index 30659969..21111e18 100644
--- a/src/frontend/hud.cpp
+++ b/src/frontend/hud.cpp
@@ -124,7 +124,7 @@ std::shared_ptr Hud::createContentAccess() {
});
InventoryBuilder builder;
- builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout);
+ builder.addGrid(8, itemsCount-1, glm::vec2(), glm::vec4(8, 8, 12, 8), true, slotLayout);
auto view = builder.build();
view->bind(accessInventory, content);
view->setMargin(glm::vec4());
@@ -137,7 +137,7 @@ std::shared_ptr Hud::createHotbar() {
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
InventoryBuilder builder;
- builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout);
+ builder.addGrid(10, 10, glm::vec2(), glm::vec4(4), true, slotLayout);
auto view = builder.build();
view->setId("hud.hotbar");
view->setOrigin(glm::vec2(view->getSize().x/2, 0));
@@ -346,9 +346,9 @@ void Hud::update(bool visible) {
element.getNode()->setVisible(visible);
}
- glm::vec2 invSize = contentAccessPanel->getSize();
+ glm::vec2 caSize = contentAccessPanel->getSize();
contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel);
- contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height));
+ contentAccessPanel->setSize(glm::vec2(caSize.x, Window::height));
contentAccess->setMinSize(glm::vec2(1, Window::height));
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp
index 580ab54d..5594664a 100644
--- a/src/frontend/hud.hpp
+++ b/src/frontend/hud.hpp
@@ -95,16 +95,16 @@ class Hud : public util::ObjectsKeeper {
/// @brief Inventories interaction agent (grabbed item)
std::shared_ptr exchangeSlot;
/// @brief Exchange slot inventory (1 slot only)
- std::shared_ptr exchangeSlotInv = nullptr;
+ std::shared_ptr exchangeSlotInv;
/// @brief List of all controlled hud elements
std::vector elements;
/// @brief Player inventory view
- std::shared_ptr inventoryView = nullptr;
+ std::shared_ptr inventoryView;
/// @brief Block inventory view
- std::shared_ptr blockUI = nullptr;
+ std::shared_ptr blockUI;
/// @brief Secondary inventory view
- std::shared_ptr secondInvView = nullptr;
+ std::shared_ptr secondInvView;
/// @brief Position of the block open
glm::ivec3 blockPos {};
/// @brief Id of the block open (used to detect block destruction or replacement)
@@ -114,9 +114,9 @@ class Hud : public util::ObjectsKeeper {
/// @brief Provide cheat controllers to the debug panel
bool allowDebugCheats = true;
/// @brief UI element will be dynamicly positioned near to inventory or in screen center
- std::shared_ptr secondUI = nullptr;
+ std::shared_ptr secondUI;
- std::shared_ptr debugMinimap = nullptr;
+ std::shared_ptr debugMinimap;
std::unique_ptr debugImgWorldGen;
diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp
index c4c67db2..b7ccf5ff 100644
--- a/src/graphics/core/Batch2D.cpp
+++ b/src/graphics/core/Batch2D.cpp
@@ -261,6 +261,24 @@ void Batch2D::rect(
vertex(x+w, y+h, u+tx, v, r,g,b,a);
}
+void Batch2D::parallelogram(
+ float x, float y, float w, float h, float skew,
+ float u, float v, float tx, float ty,
+ float r, float g, float b, float a
+){
+ if (index + 6*B2D_VERTEX_SIZE >= capacity) {
+ flush();
+ }
+ setPrimitive(DrawPrimitive::triangle);
+ vertex(x-skew*w, y, u, v+ty, r,g,b,a);
+ vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a);
+ vertex(x+skew*w, y+h, u, v, r,g,b,a);
+
+ vertex(x-skew*w, y, u, v+ty, r,g,b,a);
+ vertex(x+w-skew*w, y, u+tx, v+ty, r,g,b,a);
+ vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a);
+}
+
void Batch2D::rect(
float x, float y, float w, float h,
float r0, float g0, float b0,
@@ -336,6 +354,22 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index
rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a);
}
+void Batch2D::sprite(
+ float x,
+ float y,
+ float w,
+ float h,
+ float skew,
+ int atlasRes,
+ int index,
+ glm::vec4 tint
+) {
+ float scale = 1.0f / (float)atlasRes;
+ float u = (index % atlasRes) * scale;
+ float v = 1.0f - ((index / atlasRes) * scale) - scale;
+ parallelogram(x, y, w, h, skew, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a);
+}
+
void Batch2D::flush() {
if (index == 0)
return;
diff --git a/src/graphics/core/Batch2D.hpp b/src/graphics/core/Batch2D.hpp
index 2877f5bd..6bfdb14d 100644
--- a/src/graphics/core/Batch2D.hpp
+++ b/src/graphics/core/Batch2D.hpp
@@ -45,6 +45,7 @@ public:
void setRegion(UVRegion region);
void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint);
void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint);
+ void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint);
void point(float x, float y, float r, float g, float b, float a);
inline void setColor(glm::vec4 color) {
@@ -79,6 +80,12 @@ public:
float r, float g, float b, float a
);
+ void parallelogram(
+ float x, float y, float w, float h, float skew,
+ float u, float v, float tx, float ty,
+ float r, float g, float b, float a
+ );
+
void rect(
float x, float y, float w, float h,
float r0, float g0, float b0,
diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp
index 9c40809f..2116fdd3 100644
--- a/src/graphics/core/Font.cpp
+++ b/src/graphics/core/Font.cpp
@@ -1,5 +1,6 @@
#include "Font.hpp"
+#include
#include
#include "Texture.hpp"
#include "Batch2D.hpp"
@@ -52,17 +53,21 @@ static inline void draw_glyph(
uint c,
const glm::vec3& right,
const glm::vec3& up,
- float glyphInterval
+ float glyphInterval,
+ const FontStyle& style
) {
- batch.sprite(
- pos.x + offset.x * right.x,
- pos.y + offset.y * right.y,
- right.x / glyphInterval,
- up.y,
- 16,
- c,
- batch.getColor()
- );
+ for (int i = 0; i <= style.bold; i++) {
+ batch.sprite(
+ pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x,
+ pos.y + offset.y * right.y,
+ right.x / glyphInterval,
+ up.y,
+ -0.15f * style.italic,
+ 16,
+ c,
+ batch.getColor() * style.color
+ );
+ }
}
static inline void draw_glyph(
@@ -72,17 +77,20 @@ static inline void draw_glyph(
uint c,
const glm::vec3& right,
const glm::vec3& up,
- float glyphInterval
+ float glyphInterval,
+ const FontStyle& style
) {
- batch.sprite(
- pos + right * offset.x + up * offset.y,
- up, right / glyphInterval,
- 0.5f,
- 0.5f,
- 16,
- c,
- batch.getColor()
- );
+ for (int i = 0; i <= style.bold; i++) {
+ batch.sprite(
+ pos + right * (offset.x + i) + up * offset.y,
+ up, right / glyphInterval,
+ 0.5f,
+ 0.5f,
+ 16,
+ c,
+ batch.getColor() * style.color
+ );
+ }
}
template
@@ -93,14 +101,32 @@ static inline void draw_text(
const glm::vec3& pos,
const glm::vec3& right,
const glm::vec3& up,
- float glyphInterval
+ float interval,
+ const FontStylesScheme* styles,
+ size_t styleMapOffset
) {
+ static FontStylesScheme defStyles {{{}}, {0}};
+
+ if (styles == nullptr) {
+ styles = &defStyles;
+ }
+
uint page = 0;
uint next = MAX_CODEPAGES;
int x = 0;
int y = 0;
+ bool hasLines = false;
+
do {
- for (uint c : text){
+ for (size_t i = 0; i < text.length(); i++) {
+ uint c = text[i];
+ size_t styleIndex = styles->map.at(
+ std::min(styles->map.size() - 1, i + styleMapOffset)
+ );
+ const FontStyle& style = styles->palette.at(styleIndex);
+ hasLines |= style.strikethrough;
+ hasLines |= style.underline;
+
if (!font.isPrintableChar(c)) {
x++;
continue;
@@ -109,7 +135,7 @@ static inline void draw_text(
if (charpage == page){
batch.texture(font.getPage(charpage));
draw_glyph(
- batch, pos, glm::vec2(x, y), c, right, up, glyphInterval
+ batch, pos, glm::vec2(x, y), c, right, up, interval, style
);
}
else if (charpage > page && charpage < next){
@@ -121,6 +147,31 @@ static inline void draw_text(
next = MAX_CODEPAGES;
x = 0;
} while (page < MAX_CODEPAGES);
+
+ if (!hasLines) {
+ return;
+ }
+ batch.texture(font.getPage(0));
+ for (size_t i = 0; i < text.length(); i++) {
+ uint c = text[i];
+ size_t styleIndex = styles->map.at(
+ std::min(styles->map.size() - 1, i + styleMapOffset)
+ );
+ const FontStyle& style = styles->palette.at(styleIndex);
+ FontStyle lineStyle = style;
+ lineStyle.bold = true;
+ if (style.strikethrough) {
+ draw_glyph(
+ batch, pos, glm::vec2(x, y), '-', right, up, interval, lineStyle
+ );
+ }
+ if (style.underline) {
+ draw_glyph(
+ batch, pos, glm::vec2(x, y), '_', right, up, interval, lineStyle
+ );
+ }
+ x++;
+ }
}
const Texture* Font::getPage(int charpage) const {
@@ -135,20 +186,30 @@ const Texture* Font::getPage(int charpage) const {
}
void Font::draw(
- Batch2D& batch, std::wstring_view text, int x, int y, float scale
+ Batch2D& batch,
+ std::wstring_view text,
+ int x,
+ int y,
+ const FontStylesScheme* styles,
+ size_t styleMapOffset,
+ float scale
) const {
draw_text(
*this, batch, text,
glm::vec3(x, y, 0),
glm::vec3(glyphInterval*scale, 0, 0),
glm::vec3(0, lineHeight*scale, 0),
- glyphInterval/static_cast(lineHeight)
+ glyphInterval/static_cast(lineHeight),
+ styles,
+ styleMapOffset
);
}
void Font::draw(
Batch3D& batch,
std::wstring_view text,
+ const FontStylesScheme* styles,
+ size_t styleMapOffset,
const glm::vec3& pos,
const glm::vec3& right,
const glm::vec3& up
@@ -157,6 +218,8 @@ void Font::draw(
*this, batch, text, pos,
right * static_cast(glyphInterval),
up * static_cast(lineHeight),
- glyphInterval/static_cast(lineHeight)
+ glyphInterval/static_cast(lineHeight),
+ styles,
+ styleMapOffset
);
}
diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp
index deb07534..07fe0532 100644
--- a/src/graphics/core/Font.hpp
+++ b/src/graphics/core/Font.hpp
@@ -11,10 +11,33 @@ class Batch2D;
class Batch3D;
class Camera;
-enum class FontStyle {
- none,
- shadow,
- outline
+struct FontStyle {
+ bool bold = false;
+ bool italic = false;
+ bool strikethrough = false;
+ bool underline = false;
+ glm::vec4 color {1, 1, 1, 1};
+
+ FontStyle() = default;
+
+ FontStyle(
+ bool bold,
+ bool italic,
+ bool strikethrough,
+ bool underline,
+ glm::vec4 color
+ )
+ : bold(bold),
+ italic(italic),
+ strikethrough(strikethrough),
+ underline(underline),
+ color(std::move(color)) {
+ }
+};
+
+struct FontStylesScheme {
+ std::vector palette;
+ std::vector map;
};
class Font {
@@ -45,12 +68,22 @@ public:
/// @brief Check if character is visible (non-whitespace)
/// @param codepoint character unicode codepoint
bool isPrintableChar(uint codepoint) const;
-
- void draw(Batch2D& batch, std::wstring_view text, int x, int y, float scale=1) const;
+
+ void draw(
+ Batch2D& batch,
+ std::wstring_view text,
+ int x,
+ int y,
+ const FontStylesScheme* styles,
+ size_t styleMapOffset,
+ float scale = 1
+ ) const;
void draw(
Batch3D& batch,
std::wstring_view text,
+ const FontStylesScheme* styles,
+ size_t styleMapOffset,
const glm::vec3& pos,
const glm::vec3& right={1, 0, 0},
const glm::vec3& up={0, 1, 0}
diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp
index ea9a69e4..32681ce5 100644
--- a/src/graphics/render/TextsRenderer.cpp
+++ b/src/graphics/render/TextsRenderer.cpp
@@ -65,12 +65,7 @@ void TextsRenderer::renderNote(
xvec *= 1.0f + scale;
yvec *= 1.0f + scale;
}
- if (preset.displayMode != NoteDisplayMode::PROJECTED) {
- if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f),
- pos + xvec * (width * 0.5f))) {
- return;
- }
- } else {
+ if (preset.displayMode == NoteDisplayMode::PROJECTED) {
float scale = 1.0f;
if (glm::abs(preset.perspective) > 0.0001f) {
float scale2 = scale /
@@ -99,12 +94,17 @@ void TextsRenderer::renderNote(
pos = screenPos / screenPos.w;
}
+ } else if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f * preset.scale),
+ pos + xvec * (width * 0.5f * preset.scale))) {
+ return;
}
auto color = preset.color;
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
font.draw(
batch,
text,
+ nullptr,
+ 0,
pos - xvec * (width * 0.5f) * preset.scale,
xvec * preset.scale,
yvec * preset.scale
diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp
index 40b74a4c..38bc2257 100644
--- a/src/graphics/ui/GUI.cpp
+++ b/src/graphics/ui/GUI.cpp
@@ -197,18 +197,18 @@ void GUI::act(float delta, const Viewport& vp) {
}
}
-void GUI::draw(const DrawContext* pctx, Assets* assets) {
- auto& viewport = pctx->getViewport();
+void GUI::draw(const DrawContext& pctx, const Assets& assets) {
+ auto& viewport = pctx.getViewport();
glm::vec2 wsize = viewport.size();
menu->setPos((wsize - menu->getSize()) / 2.0f);
uicamera->setFov(wsize.y);
- auto uishader = assets->get("ui");
+ auto uishader = assets.get("ui");
uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView());
- pctx->getBatch2D()->begin();
+ pctx.getBatch2D()->begin();
container->draw(pctx, assets);
}
diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp
index b2b20bd0..c0dec649 100644
--- a/src/graphics/ui/GUI.hpp
+++ b/src/graphics/ui/GUI.hpp
@@ -94,7 +94,7 @@ namespace gui {
/// @brief Draw all visible elements on main container
/// @param pctx parent graphics context
/// @param assets active assets storage
- void draw(const DrawContext* pctx, Assets* assets);
+ void draw(const DrawContext& pctx, const Assets& assets);
/// @brief Add element to the main container
/// @param node UI element
diff --git a/src/graphics/ui/elements/Button.cpp b/src/graphics/ui/elements/Button.cpp
index b6a11232..dfdf864d 100644
--- a/src/graphics/ui/elements/Button.cpp
+++ b/src/graphics/ui/elements/Button.cpp
@@ -52,7 +52,7 @@ Button::Button(
void Button::setText(std::wstring text) {
if (label) {
- label->setText(text);
+ label->setText(std::move(text));
}
}
@@ -77,9 +77,9 @@ void Button::refresh() {
}
}
-void Button::drawBackground(const DrawContext* pctx, Assets*) {
+void Button::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec2 pos = calcPos();
- auto batch = pctx->getBatch2D();
+ auto batch = pctx.getBatch2D();
batch->texture(nullptr);
batch->setColor(calcColor());
batch->rect(pos.x, pos.y, size.x, size.y);
diff --git a/src/graphics/ui/elements/Button.hpp b/src/graphics/ui/elements/Button.hpp
index 61bfea03..39b23426 100644
--- a/src/graphics/ui/elements/Button.hpp
+++ b/src/graphics/ui/elements/Button.hpp
@@ -7,7 +7,7 @@ namespace gui {
class Button : public Panel {
protected:
- std::shared_ptr