Merge branch 'dev' of https://github.com/Xertis/VoxelEngine-Cpp into dev
This commit is contained in:
commit
86053fa86d
167
CHANGELOG.md
167
CHANGELOG.md
@ -1,74 +1,131 @@
|
|||||||
# 0.28 - 2025.07.18
|
# 0.29 - 2025.09.20
|
||||||
|
|
||||||
[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.28/doc/en/main-page.md) for 0.28
|
[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.29/doc/en/main-page.md) for 0.29
|
||||||
|
|
||||||
Table of contents:
|
Table of contents:
|
||||||
|
|
||||||
- [Added](#added)
|
- [Added](#added)
|
||||||
- [Changes](#changes)
|
|
||||||
- [Functions](#functions)
|
- [Functions](#functions)
|
||||||
|
- [Changes](#changes)
|
||||||
- [Fixes](#fixes)
|
- [Fixes](#fixes)
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- advanced graphics mode
|
- pathfinding
|
||||||
- state bits based models
|
- components:
|
||||||
- post-effects
|
- core:pathfinding
|
||||||
- ui elements:
|
- core:player
|
||||||
- iframe
|
- core:mob
|
||||||
- select
|
- libraries:
|
||||||
- modelviewer
|
- random
|
||||||
- vcm models format
|
- gfx.skeletons
|
||||||
- bit.compile
|
- (documented) assets
|
||||||
- yaml encoder/decoder
|
- udp support
|
||||||
- error handler argument in http.get, http.post
|
- schedules
|
||||||
- ui properties:
|
- events:
|
||||||
- image.region
|
- on_physics_update (components)
|
||||||
- rotation profiles:
|
- on_block_tick(x, y, z, tps) (blocks)
|
||||||
- stairs
|
- custom hand controller
|
||||||
- libraries
|
- http headers
|
||||||
- gfx.posteffects
|
- named pipes
|
||||||
- yaml
|
- optimizations:
|
||||||
- stairs rotation profile
|
- speed up block.set
|
||||||
- models editing in console
|
- speed up vectors
|
||||||
- syntax highlighting: xml, glsl, vcm
|
- items description
|
||||||
- beginning of projects system
|
- item properties methods
|
||||||
|
- tab + shift+tab
|
||||||
|
- blocks, items tags
|
||||||
|
- pack dependencies versions
|
||||||
|
- ~~allow to disable autospawn position~~ use player.set_spawnpoint
|
||||||
|
- entity.spawn command
|
||||||
|
- project script
|
||||||
|
- gui.root document
|
||||||
|
- time.schedules.world.common: Schedule
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
- reserved 'project', 'pack', 'packid', 'root' entry points
|
- app.sleep_until - added 'timeout argument'
|
||||||
- Bytearray optimized with FFI
|
- network.get / post - added 'data' argument to error callback
|
||||||
- chunks non-unloading zone limited with circle
|
- autorefresh model preview
|
||||||
|
- move player controls to lua
|
||||||
|
- move hand control to lua
|
||||||
|
|
||||||
### Functions
|
### Functions
|
||||||
|
|
||||||
- yaml.tostring
|
- block.model_name
|
||||||
- yaml.parse
|
- block.has_tag
|
||||||
- gfx.posteffects.index
|
- item.has_tag
|
||||||
- gfx.posteffects.set_effect
|
- item.description
|
||||||
- gfx.posteffects.get_intensity
|
- base64.encode_urlsafe
|
||||||
- gfx.posteffects.set_intensity
|
- base64.decode_urlsafe
|
||||||
- gfx.posteffects.is_active
|
- vec2.rotate
|
||||||
- gfx.posteffects.set_params
|
- vecn.distance
|
||||||
- gfx.posteffects.set_array
|
- vecn.mix
|
||||||
- block.get_variant
|
- rigidbody:get_vdamping
|
||||||
- block.set_variant
|
- rigidbody:set_vdamping
|
||||||
- bit.compile
|
- entity:require_component
|
||||||
- Bytearray_as_string
|
- network.udp_connect
|
||||||
|
- random.random
|
||||||
|
- random.bytes
|
||||||
|
- random.uuid
|
||||||
|
- Random:random
|
||||||
|
- Random:seed
|
||||||
|
- hud.hand_controller
|
||||||
|
- inventory.get_caption
|
||||||
|
- inventory.set_caption
|
||||||
|
- inventory.get_description
|
||||||
|
- inventory.set_description
|
||||||
|
- pathfinding.create_agent
|
||||||
|
- pathfinding.remove_agent
|
||||||
|
- pathfinding.set_enabled
|
||||||
|
- pathfinding.is_enabled
|
||||||
|
- pathfinding.make_route
|
||||||
|
- pathfinding.make_route_async
|
||||||
|
- pathfinding.pull_route
|
||||||
|
- pathfinding.set_max_visited
|
||||||
|
- pathfinding.avoid_tag
|
||||||
|
- gfx.skeletons.get
|
||||||
|
- Skeleton:index
|
||||||
|
- Skeleton:get_model
|
||||||
|
- Skeleton:set_model
|
||||||
|
- Skeleton:get_matrix
|
||||||
|
- Skeleton:set_matrix
|
||||||
|
- Skeleton:get_texture
|
||||||
|
- Skeleton:set_texture
|
||||||
|
- Skeleton:is_visible
|
||||||
|
- Skeleton:set_visible
|
||||||
|
- Skeleton:get_color
|
||||||
|
- Skeleton:set_color
|
||||||
|
- Schedule:set_timeout(time_ms, callback)
|
||||||
|
- Schedule:set_interval(interval_ms, callback, [optional] repetions): int
|
||||||
|
- Schedule:remove_interval(id)
|
||||||
|
- ScheduleGroup:publish(schedule: Schedule)
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
|
||||||
- [fix: "unknown argument --memcheck" in vctest](https://github.com/MihailRis/voxelcore/commit/281d5e09e6f1c016646af6000f6b111695c994b3)
|
- fix 3d text position / culling
|
||||||
- [fix "upgrade square is not fully inside of area" error](https://github.com/MihailRis/voxelcore/commit/bf79f6bc75a7686d59fdd0dba8b9018d6191e980 )
|
- fix fragment:place rotation (#593)
|
||||||
- [fix generator area centering](https://github.com/MihailRis/voxelcore/commit/98813472a8c25b1de93dd5d843af38c5aec9b1d8 "fix generator area centering")
|
- fix server socket creation in macos
|
||||||
- [fix incomplete content reset](https://github.com/MihailRis/voxelcore/commit/61af8ba943a24f6544c6482def2e244cf0af4d18)
|
- fix: base packs not scanned for app scripts
|
||||||
- [fix stack traces](https://github.com/MihailRis/voxelcore/commit/05ddffb5c9902e237c73cdea55d4ac1e303c6a8e)
|
- fix lua::getfield and events registering
|
||||||
- [fix containers refreshing](https://github.com/MihailRis/voxelcore/commit/34295faca276b55c6e3c0ddd98b867a0aab3eb2a)
|
- fix UIDocument::rebuildIndices
|
||||||
- [fix toml encoder](https://github.com/MihailRis/voxelcore/commit/9cd95bb0eb73521bef07f6f0d5e8b78f3e309ebf)
|
- fix input library in headless mode
|
||||||
- [fix InputBindBox](https://github.com/MihailRis/voxelcore/commit/7c976a573b01e3fb6f43bacaab22e34037b55b73 "fix InputBindBox")
|
- fix rigidbody:set_gravity_scale
|
||||||
- [fix inventory.* functions error messages](https://github.com/MihailRis/voxelcore/commit/af3c315c04959eea6c11f5ae2854a6f253e3450f)
|
- fix extended blocks destruction particles spawn spread, offset
|
||||||
- [fix: validator not called after backspace](https://github.com/MihailRis/voxelcore/commit/df3640978d279b85653d647facb26ef15c509848)
|
- fix shaders recompiling
|
||||||
- [fix: missing pack.has_indices if content is not loaded](https://github.com/MihailRis/voxelcore/commit/b02b45457322e1ce8f6b9735caeb5b58b1e2ffb4)
|
- fix: C++ vecn functions precision loss
|
||||||
- [fix: entities despawn on F5](https://github.com/MihailRis/voxelcore/commit/6ab48fda935f3f1d97d76a833c8511522857ba6a)
|
- fix coroutines errors handling
|
||||||
- [bug fix [#549]](https://github.com/MihailRis/voxelcore/commit/49727ec02647e48323266fbf814c15f6d5632ee9)
|
- fix: viewport size on toggle fullscreen
|
||||||
- [fix player camera zoom with fov-effects disabled](https://github.com/MihailRis/voxelcore/commit/014ffab183687ed9acbb93ab90e43d8f82ed826a)
|
- fix: fullscreen monitor refresh rate
|
||||||
|
- fix: content menu panel height
|
||||||
|
- fix generation.create_fragment (#596)
|
||||||
|
- fix bytearray:insert (#594)
|
||||||
|
- fix: script overriding
|
||||||
|
- fix: hud.close after hud.show_overlay bug
|
||||||
|
- fix: 'cannot resume dead coroutine' (#569)
|
||||||
|
- fix: skybox is not visible behind translucent blocks
|
||||||
|
- fix: sampler arrays inbdexed with non-constant / uniform-based expressions are forbidden
|
||||||
|
- fix initial weather intensity
|
||||||
|
- fix drop count (560)
|
||||||
|
- fix BasicParser::parseNumber() out of range (560)
|
||||||
|
- fix rotation interpolation (#557)
|
||||||
|
|||||||
11
dev/tests/network_http.lua
Normal file
11
dev/tests/network_http.lua
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
local response_received = false
|
||||||
|
|
||||||
|
network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/latest", function (s)
|
||||||
|
print(json.parse(s).name)
|
||||||
|
response_received = true
|
||||||
|
end, function (code)
|
||||||
|
print("repond with code", code)
|
||||||
|
response_received = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
app.sleep_until(function () return response_received end, nil, 10)
|
||||||
45
dev/tests/network_tcp.lua
Normal file
45
dev/tests/network_tcp.lua
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
for i=1,3 do
|
||||||
|
print(string.format("iteration %s", i + 1))
|
||||||
|
local text = ""
|
||||||
|
local complete = false
|
||||||
|
|
||||||
|
for j=1,100 do
|
||||||
|
text = text .. math.random(0, 9)
|
||||||
|
end
|
||||||
|
|
||||||
|
local server = network.tcp_open(7645, function (client)
|
||||||
|
print("client connected")
|
||||||
|
start_coroutine(function()
|
||||||
|
print("client-listener started")
|
||||||
|
local received_text = ""
|
||||||
|
while client:is_alive() and #received_text < #text do
|
||||||
|
local received = client:recv(512)
|
||||||
|
if received then
|
||||||
|
received_text = received_text .. utf8.tostring(received)
|
||||||
|
print(string.format("received %s byte(s) from client", #received))
|
||||||
|
end
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
asserts.equals (text, received_text)
|
||||||
|
complete = true
|
||||||
|
end, "client-listener")
|
||||||
|
end)
|
||||||
|
|
||||||
|
network.tcp_connect("localhost", 7645, function (socket)
|
||||||
|
print("connected to server")
|
||||||
|
start_coroutine(function()
|
||||||
|
print("data-sender started")
|
||||||
|
local ptr = 1
|
||||||
|
while ptr <= #text do
|
||||||
|
local n = math.random(1, 20)
|
||||||
|
socket:send(string.sub(text, ptr, ptr + n - 1))
|
||||||
|
print(string.format("sent %s byte(s) to server", n))
|
||||||
|
ptr = ptr + n
|
||||||
|
end
|
||||||
|
socket:close()
|
||||||
|
end, "data-sender")
|
||||||
|
end)
|
||||||
|
|
||||||
|
app.sleep_until(function () return complete end, nil, 5)
|
||||||
|
server:close()
|
||||||
|
end
|
||||||
@ -297,3 +297,29 @@ Methods are used to manage the overwriting of properties when extending a block
|
|||||||
### `property_name@append`
|
### `property_name@append`
|
||||||
|
|
||||||
Adds elements to the end of the list instead of completely overwriting it.
|
Adds elements to the end of the list instead of completely overwriting it.
|
||||||
|
|
||||||
|
## Tags
|
||||||
|
|
||||||
|
Tags allow you to designate general properties of blocks. Names should be formatted as `prefix:tag_name`.
|
||||||
|
The prefix is optional, but helps avoid unwanted logical collisions. Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tags": [
|
||||||
|
"core:ore",
|
||||||
|
"base_survival:food",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Block tags can also be added from other packs using the `your_pack:tags.toml` file. Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
"prefix:tag_name" = [
|
||||||
|
"random_pack:some_block",
|
||||||
|
"another_pack:item",
|
||||||
|
]
|
||||||
|
"other_prefix:other_tag_name" = [
|
||||||
|
# ...
|
||||||
|
]
|
||||||
|
``
|
||||||
|
|||||||
@ -30,6 +30,11 @@ If prefix is not specified, '!' level will be used.
|
|||||||
|
|
||||||
Example: '~randutil' - weak dependency 'randutil'.
|
Example: '~randutil' - weak dependency 'randutil'.
|
||||||
|
|
||||||
|
Dependency version is indicated after '@' symbol and have operators to restrict acceptable versions.
|
||||||
|
If version is not specified, '\*' (any) version will be used.
|
||||||
|
|
||||||
|
Example: 'randutil@>=1.0' - dependency 'randutil' which requires version 1.0 or newer.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,6 +20,22 @@ Example:
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can pass values in ARGS from the entity configuration.
|
||||||
|
They will be passed both when creating a new entity and when loading a saved one.
|
||||||
|
The `args` list is used for this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "base:drop",
|
||||||
|
"args": {
|
||||||
|
"item": "base:stone.item",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
The components code should be in `scripts/components`.
|
The components code should be in `scripts/components`.
|
||||||
|
|
||||||
## Physics
|
## Physics
|
||||||
|
|||||||
@ -66,3 +66,29 @@ Property status is displayed in the inventory interface. Display method is defin
|
|||||||
- `number` - number
|
- `number` - number
|
||||||
- `relation` - current value to initial value (x/y)
|
- `relation` - current value to initial value (x/y)
|
||||||
- `vbar` - vertical scale (used by default)
|
- `vbar` - vertical scale (used by default)
|
||||||
|
|
||||||
|
## Tags
|
||||||
|
|
||||||
|
Tags allow you to designate general properties of items. Names should be formatted as `prefix:tag_name`.
|
||||||
|
The prefix is optional, but helps avoid unwanted logical collisions. Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tags": [
|
||||||
|
"core:fuel",
|
||||||
|
"base_survival:poison",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Tags can also be added to items from other packs using the `your_pack:tags.toml` file. Example
|
||||||
|
|
||||||
|
```toml
|
||||||
|
"prefix:tag_name" = [
|
||||||
|
"random_pack:item",
|
||||||
|
"another_pack:some_block",
|
||||||
|
]
|
||||||
|
"other_prefix:other_tag_name" = [
|
||||||
|
# ...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
Documentation for 0.29 (in-development).
|
Documentation for 0.30 (in development).
|
||||||
|
|
||||||
[Documentation for release 0.28](https://github.com/MihailRis/voxelcore/blob/release-0.28/doc/en/main-page.md)
|
[Documentation for 0.29.](https://github.com/MihailRis/voxelcore/blob/release-0.29/doc/ru/main-page.md)
|
||||||
|
|
||||||
## Sections
|
## Sections
|
||||||
|
|
||||||
|
|||||||
@ -32,8 +32,10 @@ Subsections:
|
|||||||
- [mat4](scripting/builtins/libmat4.md)
|
- [mat4](scripting/builtins/libmat4.md)
|
||||||
- [network](scripting/builtins/libnetwork.md)
|
- [network](scripting/builtins/libnetwork.md)
|
||||||
- [pack](scripting/builtins/libpack.md)
|
- [pack](scripting/builtins/libpack.md)
|
||||||
|
- [pathfinding](scripting/builtins/libpathfinding.md)
|
||||||
- [player](scripting/builtins/libplayer.md)
|
- [player](scripting/builtins/libplayer.md)
|
||||||
- [quat](scripting/builtins/libquat.md)
|
- [quat](scripting/builtins/libquat.md)
|
||||||
|
- [random](scripting/builtins/librandom.md)
|
||||||
- [rules](scripting/builtins/librules.md)
|
- [rules](scripting/builtins/librules.md)
|
||||||
- [time](scripting/builtins/libtime.md)
|
- [time](scripting/builtins/libtime.md)
|
||||||
- [utf8](scripting/builtins/libutf8.md)
|
- [utf8](scripting/builtins/libutf8.md)
|
||||||
|
|||||||
@ -25,9 +25,12 @@ Waits for the specified time in seconds, performing the main engine loop.
|
|||||||
app.sleep_until(
|
app.sleep_until(
|
||||||
-- function that checks the condition for ending the wait
|
-- function that checks the condition for ending the wait
|
||||||
predicate: function() -> bool,
|
predicate: function() -> bool,
|
||||||
-- the maximum number of engine loop ticks after which
|
-- maximum number of engine loop ticks after which
|
||||||
-- a "max ticks exceed" exception will be thrown
|
-- a "max ticks exceed" exception will be thrown
|
||||||
[optional] max_ticks = 1e9
|
[optional] max_ticks = 1e9,
|
||||||
|
-- maximum wait time in seconds.
|
||||||
|
-- (works with system time, including test mode)
|
||||||
|
[optional] timeout = 1e9
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -8,4 +8,10 @@ base64.encode(bytes: table|ByteArray) -> str
|
|||||||
|
|
||||||
-- Decode base64 string to ByteArray or lua table if second argument is set to true
|
-- Decode base64 string to ByteArray or lua table if second argument is set to true
|
||||||
base64.decode(base64string: str, [optional]usetable: bool=false) -> table|ByteArray
|
base64.decode(base64string: str, [optional]usetable: bool=false) -> table|ByteArray
|
||||||
|
|
||||||
|
-- Encode bytes to urlsafe-base64 string ('-', '_' instead of '+', '/')
|
||||||
|
base64.encode_urlsafe(bytes: table|ByteArray) -> str
|
||||||
|
|
||||||
|
-- Decodes urlsafe-base64 string to a ByteArray or a table of numbers if the second argument is set to true
|
||||||
|
base64.decode_urlsafe(base64string: str, [optional]usetable: bool=false) -> table|ByteArray
|
||||||
```
|
```
|
||||||
|
|||||||
@ -68,6 +68,9 @@ block.get_variant(x: int, y: int, z: int) -> int
|
|||||||
|
|
||||||
-- Sets the block variant by index
|
-- Sets the block variant by index
|
||||||
block.set_variant(x: int, y: int, z: int, index: int) -> int
|
block.set_variant(x: int, y: int, z: int, index: int) -> int
|
||||||
|
|
||||||
|
-- Checks if an block has specified tag
|
||||||
|
block.has_tag(id: int, tag: str) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rotation
|
## Rotation
|
||||||
|
|||||||
@ -33,4 +33,7 @@ item.emission(itemid: int) -> str
|
|||||||
|
|
||||||
-- Returns the value of the `uses` property
|
-- Returns the value of the `uses` property
|
||||||
item.uses(itemid: int) -> int
|
item.uses(itemid: int) -> int
|
||||||
|
|
||||||
|
-- Checks if an item has specified tag
|
||||||
|
item.has_tag(itemid: int, tag: str) -> bool
|
||||||
```
|
```
|
||||||
|
|||||||
@ -6,9 +6,15 @@ A library for working with the network.
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Performs a GET request to the specified URL.
|
-- Performs a GET request to the specified URL.
|
||||||
-- After receiving the response, passes the text to the callback function.
|
network.get(
|
||||||
-- In case of an error, the HTTP response code will be passed to onfailure.
|
url: str,
|
||||||
network.get(url: str, callback: function(str), [optional] onfailure: function(int))
|
-- Function to call when response is received
|
||||||
|
callback: function(str),
|
||||||
|
-- Error handler
|
||||||
|
[optional] onfailure: function(int, str),
|
||||||
|
-- List of additional request headers
|
||||||
|
[optional] headers: table<str>
|
||||||
|
)
|
||||||
|
|
||||||
-- Example:
|
-- Example:
|
||||||
network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/latest", function (s)
|
network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/latest", function (s)
|
||||||
@ -16,13 +22,28 @@ network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/lat
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
-- A variant for binary files, with a byte array instead of a string in the response.
|
-- A variant for binary files, with a byte array instead of a string in the response.
|
||||||
network.get_binary(url: str, callback: function(table|ByteArray), [optional] onfailure: function(int))
|
network.get_binary(
|
||||||
|
url: str,
|
||||||
|
callback: function(ByteArray),
|
||||||
|
[optional] onfailure: function(int, str),
|
||||||
|
[optional] headers: table<str>
|
||||||
|
)
|
||||||
|
|
||||||
-- Performs a POST request to the specified URL.
|
-- Performs a POST request to the specified URL.
|
||||||
-- Currently, only `Content-Type: application/json` is supported
|
-- Currently, only `Content-Type: application/json` is supported
|
||||||
-- After receiving the response, passes the text to the callback function.
|
-- After receiving the response, passes the text to the callback function.
|
||||||
-- In case of an error, the HTTP response code will be passed to onfailure.
|
-- In case of an error, the HTTP response code will be passed to onfailure.
|
||||||
network.post(url: str, data: table, callback: function(str), [optional] onfailure: function(int))
|
network.post(
|
||||||
|
url: str,
|
||||||
|
-- Request body as a table (will be converted to JSON) or string
|
||||||
|
body: table|str,
|
||||||
|
-- Function called when response is received
|
||||||
|
callback: function(str),
|
||||||
|
-- Error handler
|
||||||
|
[optional] onfailure: function(int, str),
|
||||||
|
-- List of additional request headers
|
||||||
|
[optional] headers: table<str>
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## TCP Connections
|
## TCP Connections
|
||||||
|
|||||||
66
doc/en/scripting/builtins/libpathfinding.md
Normal file
66
doc/en/scripting/builtins/libpathfinding.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# *pathfinding* library
|
||||||
|
|
||||||
|
The *pathfinding* library provides functions for working with the pathfinding system in the game world. It allows you to create and manage agents finding routes between points in the world.
|
||||||
|
|
||||||
|
When used in entity logic, the `core:pathfinding` component should be used.
|
||||||
|
|
||||||
|
## `core:pathfinding` component
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pf = entity:get_component("core:pathfinding")
|
||||||
|
|
||||||
|
--- ...
|
||||||
|
local x = ...
|
||||||
|
local y = ...
|
||||||
|
local z = ...
|
||||||
|
|
||||||
|
--- Set the target for the agent
|
||||||
|
pf.set_target({x, y, z})
|
||||||
|
|
||||||
|
--- Get the current target of the agent
|
||||||
|
local target = pf.get_target() --> vec3 or nil
|
||||||
|
--- ...
|
||||||
|
|
||||||
|
--- Get the current route of the agent
|
||||||
|
local route = pf.get_route() --> table<vec3> or nil
|
||||||
|
--- ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Library functions
|
||||||
|
|
||||||
|
```lua
|
||||||
|
--- Create a new agent. Returns the ID of the created agent
|
||||||
|
local agent = pathfinding.create_agent() --> int
|
||||||
|
|
||||||
|
--- Delete an agent by ID. Returns true if the agent existed, otherwise false
|
||||||
|
pathfinding.remove_agent(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Set the agent state (enabled/disabled)
|
||||||
|
pathfinding.set_enabled(agent: int, enabled: bool)
|
||||||
|
|
||||||
|
--- Check the agent state. Returns true if the agent is enabled, otherwise false
|
||||||
|
pathfinding.is_enabled(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Create a route based on the given points. Returns an array of route points
|
||||||
|
pathfinding.make_route(start: vec3, target: vec3) --> table<vec3>
|
||||||
|
|
||||||
|
--- Asynchronously create a route based on the given points.
|
||||||
|
--- This function allows to perform pathfinding in the background without blocking the main thread of execution
|
||||||
|
pathfinding.make_route_async(agent: int, start: vec3, target: vec3)
|
||||||
|
|
||||||
|
--- Get the route that the agent has already found. Used to get the route after an asynchronous search.
|
||||||
|
--- If the search has not yet completed, returns nil. If the route is not found, returns an empty table.
|
||||||
|
pathfinding.pull_route(agent: int) --> table<vec3> or nil
|
||||||
|
|
||||||
|
--- Set the maximum number of visited blocks for the agent. Used to limit the amount of work of the pathfinding algorithm.
|
||||||
|
pathfinding.set_max_visited(agent: int, max_visited: int)
|
||||||
|
|
||||||
|
--- Adding an avoided blocks tag
|
||||||
|
pathfinding.avoid_tag(
|
||||||
|
agent: int,
|
||||||
|
-- tag for avoided blocks
|
||||||
|
tag: string, [optional],
|
||||||
|
-- cost of crossing a block
|
||||||
|
cost: int = 10
|
||||||
|
)
|
||||||
|
```
|
||||||
@ -89,6 +89,13 @@ player.set_loading_chunks(playerid: int, bool)
|
|||||||
|
|
||||||
Getter and setter of the property that determines whether the player is loading chunks.
|
Getter and setter of the property that determines whether the player is loading chunks.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_interaction_distance(playerid: int) -> float
|
||||||
|
player.set_interaction_distance(playerid: int, distance: float)
|
||||||
|
```
|
||||||
|
|
||||||
|
Getter and setter of the property for max interaction distance.
|
||||||
|
|
||||||
``` lua
|
``` lua
|
||||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||||
|
|||||||
38
doc/en/scripting/builtins/librandom.md
Normal file
38
doc/en/scripting/builtins/librandom.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# *random* library
|
||||||
|
|
||||||
|
A library of functions for generating random numbers.
|
||||||
|
|
||||||
|
## Non-deterministic numbers
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Generates a random number in the range [0..1)
|
||||||
|
random.random() --> number
|
||||||
|
|
||||||
|
-- Generates a random integer in the range [0..n]
|
||||||
|
random.random(n) --> number
|
||||||
|
|
||||||
|
-- Generates a random integer in the range [a..b]
|
||||||
|
random.random(a, b) --> number
|
||||||
|
|
||||||
|
-- Generates a random byte array of length n
|
||||||
|
random.bytes(n: number) -> Bytearray
|
||||||
|
|
||||||
|
-- Generates a UUID version 4
|
||||||
|
random.uuid() -> str
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pseudorandom numbers
|
||||||
|
|
||||||
|
The library provides the Random class - a generator with its own isolated state.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local rng = random.Random()
|
||||||
|
|
||||||
|
-- Used similarly to math.random
|
||||||
|
local a = rng:random() --> [0..1)
|
||||||
|
local b = rng:random(10) --> [0..10]
|
||||||
|
local c = rng:random(5, 20) --> [5..20]
|
||||||
|
|
||||||
|
-- Sets the generator state to generate a reproducible sequence of random numbers
|
||||||
|
rng:seed(42)
|
||||||
|
```
|
||||||
@ -100,6 +100,13 @@ vecn.length(a: vector)
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Distance - *vecn.distance(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- returns the distance between two vectors
|
||||||
|
vecn.distance(a: vector, b: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Absolute value - *vecn.abs(...)*
|
#### Absolute value - *vecn.abs(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
@ -136,6 +143,16 @@ vecn.pow(v: vector, exponent: number, dst: vector)
|
|||||||
vecn.dot(a: vector, b: vector)
|
vecn.dot(a: vector, b: vector)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Mixing - *vecn.mix(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- returns vector a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number)
|
||||||
|
|
||||||
|
-- writes to dst vector a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number, dst: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Convert to string - *vecn.tostring(...)*
|
#### Convert to string - *vecn.tostring(...)*
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Returns only if the content is a vector
|
> Returns only if the content is a vector
|
||||||
@ -160,6 +177,12 @@ vec2.angle(v: vec2)
|
|||||||
|
|
||||||
-- returns the direction angle of the vector {x, y} in degrees [0, 360]
|
-- returns the direction angle of the vector {x, y} in degrees [0, 360]
|
||||||
vec2.angle(x: number, y: number)
|
vec2.angle(x: number, y: number)
|
||||||
|
|
||||||
|
-- returns the vector rotated by an angle in degrees counterclockwise
|
||||||
|
vec2.rotate(v: vec2, angle: number) -> vec2
|
||||||
|
|
||||||
|
-- writes the vector rotated by an angle in degrees counterclockwise to dst
|
||||||
|
vec2.rotate(v: vec2, angle: number, dst: vec2) -> vec2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -188,6 +211,10 @@ print("mul: " .. vec3.tostring(result_mul)) -- {10, 40, 80}
|
|||||||
local result_mul_scal = vec3.mul(v1_3d, scal)
|
local result_mul_scal = vec3.mul(v1_3d, scal)
|
||||||
print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
||||||
|
|
||||||
|
-- calculating distance between vectors
|
||||||
|
local result_distance = vec3.distance(v1_3d, v2_3d)
|
||||||
|
print("distance: " .. result_distance) -- 43
|
||||||
|
|
||||||
-- vector normalization
|
-- vector normalization
|
||||||
local result_norm = vec3.normalize(v1_3d)
|
local result_norm = vec3.normalize(v1_3d)
|
||||||
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
||||||
@ -211,3 +238,7 @@ print("pow: " .. vec3.tostring(result_pow)) -- {1, 4, 4}
|
|||||||
-- scalar product of vectors
|
-- scalar product of vectors
|
||||||
local result_dot = vec3.dot(v1_3d, v2_3d)
|
local result_dot = vec3.dot(v1_3d, v2_3d)
|
||||||
print("dot: " ..result_dot) -- 250
|
print("dot: " ..result_dot) -- 250
|
||||||
|
|
||||||
|
-- mixing vectors
|
||||||
|
local result_mix = vec3.mix(v1_3d, v2_3d, 0.25)
|
||||||
|
print("mix: " .. vec3.tostring(result_mix)) -- {3.25, 6.5, 11.5}
|
||||||
|
|||||||
@ -26,6 +26,8 @@ entity:get_uid() -> int
|
|||||||
entity:get_component(name: str) -> component or nil
|
entity:get_component(name: str) -> component or nil
|
||||||
-- Checks for the presence of a component by name
|
-- Checks for the presence of a component by name
|
||||||
entity:has_component(name: str) -> bool
|
entity:has_component(name: str) -> bool
|
||||||
|
-- Retrieves a component by name. Throws an exception if it does not exist
|
||||||
|
entity:require_component(name: str) -> component
|
||||||
|
|
||||||
-- Enables/disables the component
|
-- Enables/disables the component
|
||||||
entity:set_enabled(name: str, enable: bool)
|
entity:set_enabled(name: str, enable: bool)
|
||||||
@ -93,10 +95,12 @@ body:get_linear_damping() -> number
|
|||||||
-- Sets the linear velocity attenuation multiplier
|
-- Sets the linear velocity attenuation multiplier
|
||||||
body:set_linear_damping(value: number)
|
body:set_linear_damping(value: number)
|
||||||
|
|
||||||
-- Checks if vertical velocity attenuation is enabled
|
-- Checks if vertical damping is enabled
|
||||||
body:is_vdamping() -> bool
|
body:is_vdamping() -> bool
|
||||||
-- Enables/disables vertical velocity attenuation
|
-- Returns the vertical damping multiplier
|
||||||
body:set_vdamping(enabled: bool)
|
body:get_vdamping() -> number
|
||||||
|
-- Enables/disables vertical damping / sets vertical damping multiplier
|
||||||
|
body:set_vdamping(enabled: bool | number)
|
||||||
|
|
||||||
-- Checks if the entity is on the ground
|
-- Checks if the entity is on the ground
|
||||||
body:is_grounded() -> bool
|
body:is_grounded() -> bool
|
||||||
@ -188,6 +192,12 @@ function on_update(tps: int)
|
|||||||
|
|
||||||
Called every entities tick (currently 20 times per second).
|
Called every entities tick (currently 20 times per second).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_physics_update(delta: number)
|
||||||
|
```
|
||||||
|
|
||||||
|
Called after each physics step
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_render(delta: number)
|
function on_render(delta: number)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -46,6 +46,13 @@ function on_blocks_tick(tps: int)
|
|||||||
|
|
||||||
Called tps (20) times per second. Use 1/tps instead of `time.delta()`.
|
Called tps (20) times per second. Use 1/tps instead of `time.delta()`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_block_tick(x, y, z, tps: number)
|
||||||
|
```
|
||||||
|
|
||||||
|
Called tps (20 / tick-interval) times per second for a block.
|
||||||
|
Use 1/tps instead of `time.delta()`.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_player_tick(playerid: int, tps: int)
|
function on_player_tick(playerid: int, tps: int)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -306,3 +306,29 @@
|
|||||||
### `имя_свойства@append`
|
### `имя_свойства@append`
|
||||||
|
|
||||||
Добавляет элементы в конец списка, вместо его полной перезаписи.
|
Добавляет элементы в конец списка, вместо его полной перезаписи.
|
||||||
|
|
||||||
|
## Теги - *tags*
|
||||||
|
|
||||||
|
Теги позволяют обозначать обобщённые свойства блоков. Названия следует формировать как `префикс:имя_тега`.
|
||||||
|
Префикс не является обязательным, но позволяет избегать нежелательных логических коллизий. Пример:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tags": [
|
||||||
|
"core:ore",
|
||||||
|
"base_survival:food",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Теги блокам можно добавлять и из других паков, с помощью файла `ваш_пак:tags.toml`. Пример
|
||||||
|
|
||||||
|
```toml
|
||||||
|
"префикс:имя_тега" = [
|
||||||
|
"рандомный_пак:какой_то_блок",
|
||||||
|
"ещё_один_пак:предмет",
|
||||||
|
]
|
||||||
|
"другой_префикс:другое_имя_тега" = [
|
||||||
|
# ...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|||||||
@ -32,6 +32,11 @@
|
|||||||
|
|
||||||
Пример: '~randutil' - слабая зависимость 'randutil'.
|
Пример: '~randutil' - слабая зависимость 'randutil'.
|
||||||
|
|
||||||
|
Версии зависимостей указываются после '@' и имеют операторы для ограничения допустимых версий.
|
||||||
|
Отсутствие версии зависимости интерпретируется как '\*', т.е. любая версия.
|
||||||
|
|
||||||
|
Пример: 'randutil@>=1.0' - зависимость 'randutil' версии 1.0 и старше.
|
||||||
|
|
||||||
Пример:
|
Пример:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,6 +20,22 @@
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Из конфигурации сущности можно передавать значения в ARGS.
|
||||||
|
Они будут передаваться как при создании новой сущности, так и при загрузке сохранённой.
|
||||||
|
Для этого используется список `args`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "base:drop",
|
||||||
|
"args": {
|
||||||
|
"item": "base:stone.item",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Код компонентов должен находиться в `scripts/components`.
|
Код компонентов должен находиться в `scripts/components`.
|
||||||
|
|
||||||
## Физика
|
## Физика
|
||||||
|
|||||||
@ -65,3 +65,30 @@
|
|||||||
- `number` - число
|
- `number` - число
|
||||||
- `relation` - отношение текущего значения к изначальному (x/y)
|
- `relation` - отношение текущего значения к изначальному (x/y)
|
||||||
- `vbar` - вертикальная шкала (используется по-умолчанию)
|
- `vbar` - вертикальная шкала (используется по-умолчанию)
|
||||||
|
|
||||||
|
|
||||||
|
## Теги - *tags*
|
||||||
|
|
||||||
|
Теги позволяют обозначать обобщённые свойства предметов. Названия следует формировать как `префикс:имя_тега`.
|
||||||
|
Префикс не является обязательным, но позволяет избегать нежелательных логических коллизий. Пример:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tags": [
|
||||||
|
"core:fuel",
|
||||||
|
"base_survival:poison",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Теги предметам можно добавлять и из других паков, с помощью файла `ваш_пак:tags.toml`. Пример
|
||||||
|
|
||||||
|
```toml
|
||||||
|
"префикс:имя_тега" = [
|
||||||
|
"рандомный_пак:предмет",
|
||||||
|
"ещё_один_пак:какой_то_блок",
|
||||||
|
]
|
||||||
|
"другой_префикс:другое_имя_тега" = [
|
||||||
|
# ...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# Документация
|
# Документация
|
||||||
|
|
||||||
Документация версии 0.29 (в разработке).
|
Документация версии 0.30 (в разработке).
|
||||||
|
|
||||||
[Документация версии 0.28](https://github.com/MihailRis/voxelcore/blob/release-0.28/doc/ru/main-page.md)
|
[Документация 0.29.](https://github.com/MihailRis/voxelcore/blob/release-0.29/doc/ru/main-page.md)
|
||||||
|
|
||||||
## Разделы
|
## Разделы
|
||||||
|
|
||||||
|
|||||||
@ -32,8 +32,10 @@
|
|||||||
- [mat4](scripting/builtins/libmat4.md)
|
- [mat4](scripting/builtins/libmat4.md)
|
||||||
- [network](scripting/builtins/libnetwork.md)
|
- [network](scripting/builtins/libnetwork.md)
|
||||||
- [pack](scripting/builtins/libpack.md)
|
- [pack](scripting/builtins/libpack.md)
|
||||||
|
- [pathfinding](scripting/builtins/libpathfinding.md)
|
||||||
- [player](scripting/builtins/libplayer.md)
|
- [player](scripting/builtins/libplayer.md)
|
||||||
- [quat](scripting/builtins/libquat.md)
|
- [quat](scripting/builtins/libquat.md)
|
||||||
|
- [random](scripting/builtins/librandom.md)
|
||||||
- [rules](scripting/builtins/librules.md)
|
- [rules](scripting/builtins/librules.md)
|
||||||
- [time](scripting/builtins/libtime.md)
|
- [time](scripting/builtins/libtime.md)
|
||||||
- [utf8](scripting/builtins/libutf8.md)
|
- [utf8](scripting/builtins/libutf8.md)
|
||||||
|
|||||||
@ -27,7 +27,10 @@ app.sleep_until(
|
|||||||
predicate: function() -> bool,
|
predicate: function() -> bool,
|
||||||
-- максимальное количество тактов цикла движка, после истечения которых
|
-- максимальное количество тактов цикла движка, после истечения которых
|
||||||
-- будет брошено исключение "max ticks exceed"
|
-- будет брошено исключение "max ticks exceed"
|
||||||
[опционально] max_ticks = 1e9
|
[опционально] max_ticks = 1e9,
|
||||||
|
-- максимальное длительность ожидания в секундах.
|
||||||
|
-- (работает с системным временем, включая test-режим)
|
||||||
|
[опционально] timeout = 1e9
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -8,4 +8,10 @@ base64.encode(bytes: table|ByteArray) -> str
|
|||||||
|
|
||||||
-- Декодирует base64 строку в ByteArray или таблицу чисел, если второй аргумент установлен на true
|
-- Декодирует base64 строку в ByteArray или таблицу чисел, если второй аргумент установлен на true
|
||||||
base64.decode(base64string: str, [опционально]usetable: bool=false) -> table|ByteArray
|
base64.decode(base64string: str, [опционально]usetable: bool=false) -> table|ByteArray
|
||||||
|
|
||||||
|
-- Кодирует массив байт в urlsafe-base64 строку ('-', '_' вместо '+', '/')
|
||||||
|
base64.encode_urlsafe(bytes: table|ByteArray) -> str
|
||||||
|
|
||||||
|
-- Декодирует urlsafe-base64 строку в ByteArray или таблицу чисел, если второй аргумент установлен на true
|
||||||
|
base64.decode_urlsafe(base64string: str, [опционально]usetable: bool=false) -> table|ByteArray
|
||||||
```
|
```
|
||||||
|
|||||||
@ -67,6 +67,9 @@ block.get_variant(x: int, y: int, z: int) -> int
|
|||||||
|
|
||||||
-- Устанавливает вариант блока по индексу
|
-- Устанавливает вариант блока по индексу
|
||||||
block.set_variant(x: int, y: int, z: int, index: int) -> int
|
block.set_variant(x: int, y: int, z: int, index: int) -> int
|
||||||
|
|
||||||
|
-- Проверяет наличие тега у блока
|
||||||
|
block.has_tag(id: int, tag: str) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
### Raycast
|
### Raycast
|
||||||
|
|||||||
@ -33,6 +33,9 @@ item.emission(itemid: int) -> str
|
|||||||
|
|
||||||
-- Возвращает значение свойства `uses`
|
-- Возвращает значение свойства `uses`
|
||||||
item.uses(itemid: int) -> int
|
item.uses(itemid: int) -> int
|
||||||
|
|
||||||
|
-- Проверяет наличие тега у предмета
|
||||||
|
item.has_tag(itemid: int, tag: str) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,15 @@
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Выполняет GET запрос к указанному URL.
|
-- Выполняет GET запрос к указанному URL.
|
||||||
-- После получения ответа, передаёт текст в функцию callback.
|
network.get(
|
||||||
-- В случае ошибки в onfailure будет передан HTTP-код ответа.
|
url: str,
|
||||||
network.get(url: str, callback: function(str), [опционально] onfailure: function(int))
|
-- Функция, вызываемая при получении ответа
|
||||||
|
callback: function(str),
|
||||||
|
-- Обработчик ошибок
|
||||||
|
[опционально] onfailure: function(int, str),
|
||||||
|
-- Список дополнительных заголовков запроса
|
||||||
|
[опционально] headers: table<str>
|
||||||
|
)
|
||||||
|
|
||||||
-- Пример:
|
-- Пример:
|
||||||
network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/latest", function (s)
|
network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/latest", function (s)
|
||||||
@ -16,13 +22,28 @@ network.get("https://api.github.com/repos/MihailRis/VoxelEngine-Cpp/releases/lat
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
-- Вариант для двоичных файлов, с массивом байт вместо строки в ответе.
|
-- Вариант для двоичных файлов, с массивом байт вместо строки в ответе.
|
||||||
network.get_binary(url: str, callback: function(table|ByteArray), [опционально] onfailure: function(int))
|
network.get_binary(
|
||||||
|
url: str,
|
||||||
|
callback: function(ByteArray),
|
||||||
|
[опционально] onfailure: function(int, Bytearray),
|
||||||
|
[опционально] headers: table<str>
|
||||||
|
)
|
||||||
|
|
||||||
-- Выполняет POST запрос к указанному URL.
|
-- Выполняет POST запрос к указанному URL.
|
||||||
-- На данный момент реализована поддержка только `Content-Type: application/json`
|
-- На данный момент реализована поддержка только `Content-Type: application/json`
|
||||||
-- После получения ответа, передаёт текст в функцию callback.
|
-- После получения ответа, передаёт текст в функцию callback.
|
||||||
-- В случае ошибки в onfailure будет передан HTTP-код ответа.
|
-- В случае ошибки в onfailure будет передан HTTP-код ответа.
|
||||||
network.post(url: str, data: table, callback: function(str), [опционально] onfailure: function(int))
|
network.post(
|
||||||
|
url: str,
|
||||||
|
-- Тело запроса в виде таблицы, конвертируемой в JSON или строки
|
||||||
|
body: table|str,
|
||||||
|
-- Функция, вызываемая при получении ответа
|
||||||
|
callback: function(str),
|
||||||
|
-- Обработчик ошибок
|
||||||
|
[опционально] onfailure: function(int, str),
|
||||||
|
-- Список дополнительных заголовков запроса
|
||||||
|
[опционально] headers: table<str>
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## TCP-Соединения
|
## TCP-Соединения
|
||||||
|
|||||||
66
doc/ru/scripting/builtins/libpathfinding.md
Normal file
66
doc/ru/scripting/builtins/libpathfinding.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Библиотека *pathfinding*
|
||||||
|
|
||||||
|
Библиотека *pathfinding* предоставляет функции для работы с системой поиска пути в игровом мире. Она позволяет создавать и управлять агентами, которые могут находить маршруты между точками в мире.
|
||||||
|
|
||||||
|
При использовании в логике сущностей следует использовать компонент `core:pathfinding`.
|
||||||
|
|
||||||
|
## Компонент `core:pathfinding`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pf = entity:get_component("core:pathfinding")
|
||||||
|
|
||||||
|
--- ...
|
||||||
|
local x = ...
|
||||||
|
local y = ...
|
||||||
|
local z = ...
|
||||||
|
|
||||||
|
--- Установка цели для агента
|
||||||
|
pf.set_target({x, y, z})
|
||||||
|
|
||||||
|
--- Получение текущей цели агента
|
||||||
|
local target = pf.get_target() --> vec3 или nil
|
||||||
|
--- ...
|
||||||
|
|
||||||
|
--- Получение текущего маршрута агента
|
||||||
|
local route = pf.get_route() --> table<vec3> или nil
|
||||||
|
--- ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Функции библиотеки
|
||||||
|
|
||||||
|
```lua
|
||||||
|
--- Создание нового агента. Возвращает идентификатор созданного агента
|
||||||
|
local agent = pathfinding.create_agent() --> int
|
||||||
|
|
||||||
|
--- Удаление агента по идентификатору. Возвращает true, если агент существовал, иначе false
|
||||||
|
pathfinding.remove_agent(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Установка состояния агента (включен/выключен)
|
||||||
|
pathfinding.set_enabled(agent: int, enabled: bool)
|
||||||
|
|
||||||
|
--- Проверка состояния агента. Возвращает true, если агент включен, иначе false
|
||||||
|
pathfinding.is_enabled(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Создание маршрута на основе заданных точек. Возвращает массив точек маршрута
|
||||||
|
pathfinding.make_route(start: vec3, target: vec3) --> table<vec3>
|
||||||
|
|
||||||
|
--- Асинхронное создание маршрута на основе заданных точек.
|
||||||
|
--- Функция позволяет выполнять поиск пути в фоновом режиме, не блокируя основной поток выполнения
|
||||||
|
pathfinding.make_route_async(agent: int, start: vec3, target: vec3)
|
||||||
|
|
||||||
|
--- Получение маршрута, который агент уже нашел. Используется для получения маршрута после асинхронного поиска.
|
||||||
|
--- Если поиск ещё не завершён, возвращает nil. Если маршрут не найден, возвращает пустую таблицу.
|
||||||
|
pathfinding.pull_route(agent: int) --> table<vec3> или nil
|
||||||
|
|
||||||
|
--- Установка максимального количества посещенных блоков для агента. Используется для ограничения объема работы алгоритма поиска пути.
|
||||||
|
pathfinding.set_max_visited(agent: int, max_visited: int)
|
||||||
|
|
||||||
|
--- Добавление тега избегаемых блоков
|
||||||
|
pathfinding.avoid_tag(
|
||||||
|
agent: int,
|
||||||
|
-- тег избегаемых блоков
|
||||||
|
tag: string, [опционально],
|
||||||
|
-- стоимость пересечения блока
|
||||||
|
cost: int = 10
|
||||||
|
)
|
||||||
|
```
|
||||||
@ -89,6 +89,13 @@ player.set_loading_chunks(playerid: int, bool)
|
|||||||
|
|
||||||
Геттер и сеттер свойства, определяющего, прогружает ли игрок чанки вокруг.
|
Геттер и сеттер свойства, определяющего, прогружает ли игрок чанки вокруг.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_interaction_distance(playerid: int) -> float
|
||||||
|
player.set_interaction_distance(playerid: int, distance: float)
|
||||||
|
```
|
||||||
|
|
||||||
|
Геттер и сеттер свойства, определяющего максимальную дистанцию взаимодействия.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||||
|
|||||||
38
doc/ru/scripting/builtins/librandom.md
Normal file
38
doc/ru/scripting/builtins/librandom.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Библиотека *random*
|
||||||
|
|
||||||
|
Библиотека функций для генерации случайный чисел.
|
||||||
|
|
||||||
|
## Недетерминированные числа
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Генерирует случайное число в диапазоне [0..1)
|
||||||
|
random.random() --> number
|
||||||
|
|
||||||
|
-- Генерирует случайное целое число в диапазоне [0..n]
|
||||||
|
random.random(n) --> number
|
||||||
|
|
||||||
|
-- Генерирует случайное целое число в диапазоне [a..b]
|
||||||
|
random.random(a, b) --> number
|
||||||
|
|
||||||
|
-- Генерирует случайный массив байт длиной n
|
||||||
|
random.bytes(n: number) -> Bytearray
|
||||||
|
|
||||||
|
-- Генерирует UUID версии 4
|
||||||
|
random.uuid() -> str
|
||||||
|
```
|
||||||
|
|
||||||
|
## Псевдослучайные числа
|
||||||
|
|
||||||
|
Библиотека предоставляет класс Random - генератор с собственным изолированным состоянием.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local rng = random.Random()
|
||||||
|
|
||||||
|
-- Используется аналогично math.random
|
||||||
|
local a = rng:random() --> [0..1)
|
||||||
|
local b = rng:random(10) --> [0..10]
|
||||||
|
local c = rng:random(5, 20) --> [5..20]
|
||||||
|
|
||||||
|
-- Устанавливает состояние генератора для генерации воспроизводимой последовательности случайных чисел
|
||||||
|
rng:seed(42)
|
||||||
|
```
|
||||||
@ -100,6 +100,13 @@ vecn.length(a: vector)
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Дистанция - *vecn.distance(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- возвращает расстояние между двумя векторами
|
||||||
|
vecn.distance(a: vector, b: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Абсолютное значение - *vecn.abs(...)*
|
#### Абсолютное значение - *vecn.abs(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
@ -136,6 +143,16 @@ vecn.pow(v: vector, exponent: number, dst: vector)
|
|||||||
vecn.dot(a: vector, b: vector)
|
vecn.dot(a: vector, b: vector)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Смешивание - *vecn.mix(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- возвращает вектор a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number)
|
||||||
|
|
||||||
|
-- записывает в dst вектор a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number, dst: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Перевод в строку - *vecn.tostring(...)*
|
#### Перевод в строку - *vecn.tostring(...)*
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Возвращает только тогда, когда содержимым является вектор
|
> Возвращает только тогда, когда содержимым является вектор
|
||||||
@ -160,6 +177,12 @@ vec2.angle(v: vec2)
|
|||||||
|
|
||||||
-- возвращает угол направления вектора {x, y} в градусах [0, 360]
|
-- возвращает угол направления вектора {x, y} в градусах [0, 360]
|
||||||
vec2.angle(x: number, y: number)
|
vec2.angle(x: number, y: number)
|
||||||
|
|
||||||
|
-- возвращает повернутый вектор на угол в градусах против часовой стрелки
|
||||||
|
vec2.rotate(v: vec2, angle: number) -> vec2
|
||||||
|
|
||||||
|
-- записывает повернутый вектор на угол в градусах против часовой стрелки в dst
|
||||||
|
vec2.rotate(v: vec2, angle: number, dst: vec2) -> vec2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -192,6 +215,10 @@ print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
|||||||
local result_norm = vec3.normalize(v1_3d)
|
local result_norm = vec3.normalize(v1_3d)
|
||||||
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
||||||
|
|
||||||
|
-- дистанция между векторами
|
||||||
|
local result_distance = vec3.distance(v1_3d, v2_3d)
|
||||||
|
print("distance: " .. result_distance) -- 43
|
||||||
|
|
||||||
-- длина вектора
|
-- длина вектора
|
||||||
local result_len = vec3.length(v1_3d)
|
local result_len = vec3.length(v1_3d)
|
||||||
print("len: " .. result_len) -- 3
|
print("len: " .. result_len) -- 3
|
||||||
@ -211,4 +238,9 @@ print("pow: " .. vec3.tostring(result_pow)) -- {1, 4, 4}
|
|||||||
-- скалярное произведение векторов
|
-- скалярное произведение векторов
|
||||||
local result_dot = vec3.dot(v1_3d, v2_3d)
|
local result_dot = vec3.dot(v1_3d, v2_3d)
|
||||||
print("dot: " .. result_dot) -- 250
|
print("dot: " .. result_dot) -- 250
|
||||||
|
|
||||||
|
-- смешивание векторов
|
||||||
|
local result_mix = vec3.mix(v1_3d, v2_3d, 0.25)
|
||||||
|
print("mix: " .. vec3.tostring(result_mix)) -- {3.25, 6.5, 11.5}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -26,6 +26,8 @@ entity:get_uid() -> int
|
|||||||
entity:get_component(name: str) -> компонент или nil
|
entity:get_component(name: str) -> компонент или nil
|
||||||
-- Проверяет наличие компонента по имени
|
-- Проверяет наличие компонента по имени
|
||||||
entity:has_component(name: str) -> bool
|
entity:has_component(name: str) -> bool
|
||||||
|
-- Запрашивает компонент по имени. Бросает исключение при отсутствии
|
||||||
|
entity:require_component(name: str) -> компонент
|
||||||
|
|
||||||
-- Включает/выключает компонент по имени
|
-- Включает/выключает компонент по имени
|
||||||
entity:set_enabled(name: str, enable: bool)
|
entity:set_enabled(name: str, enable: bool)
|
||||||
@ -95,8 +97,10 @@ body:set_linear_damping(value: number)
|
|||||||
|
|
||||||
-- Проверяет, включено ли вертикальное затухание скорости
|
-- Проверяет, включено ли вертикальное затухание скорости
|
||||||
body:is_vdamping() -> bool
|
body:is_vdamping() -> bool
|
||||||
-- Включает/выключает вертикальное затухание скорости
|
-- Возвращает множитель вертикального затухания скорости
|
||||||
body:set_vdamping(enabled: bool)
|
body:get_vdamping() -> number
|
||||||
|
-- Включает/выключает вертикальное затухание скорости / устанавливает значение множителя
|
||||||
|
body:set_vdamping(enabled: bool | number)
|
||||||
|
|
||||||
-- Проверяет, находится ли сущность на земле (приземлена)
|
-- Проверяет, находится ли сущность на земле (приземлена)
|
||||||
body:is_grounded() -> bool
|
body:is_grounded() -> bool
|
||||||
@ -188,6 +192,12 @@ function on_update(tps: int)
|
|||||||
|
|
||||||
Вызывается каждый такт сущностей (на данный момент - 20 раз в секунду).
|
Вызывается каждый такт сущностей (на данный момент - 20 раз в секунду).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_physics_update(delta: number)
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызывается после каждого шага физики
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_render(delta: number)
|
function on_render(delta: number)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -46,6 +46,13 @@ function on_blocks_tick(tps: int)
|
|||||||
|
|
||||||
Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`.
|
Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_block_tick(x, y, z, tps: number)
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызывается tps (20 / tick-interval) раз в секунду для конкретного блока.
|
||||||
|
Используйте 1/tps вместо `time.delta()`.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_player_tick(playerid: int, tps: int)
|
function on_player_tick(playerid: int, tps: int)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"texture": "coal_ore",
|
"texture": "coal_ore",
|
||||||
|
"tags": ["base:ore"],
|
||||||
"base:durability": 16.0
|
"base:durability": 16.0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,5 +7,6 @@
|
|||||||
"obstacle": false,
|
"obstacle": false,
|
||||||
"selectable": false,
|
"selectable": false,
|
||||||
"replaceable": true,
|
"replaceable": true,
|
||||||
"translucent": true
|
"translucent": true,
|
||||||
|
"tags": ["core:liquid"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
{
|
{
|
||||||
"components": [
|
"components": [
|
||||||
"base:drop"
|
{
|
||||||
|
"name": "base:drop",
|
||||||
|
"args": {
|
||||||
|
"item": "base:stone.item",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
"hitbox": [0.4, 0.25, 0.4],
|
"hitbox": [0.4, 0.25, 0.4],
|
||||||
"sensors": [
|
"sensors": [
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
{
|
{
|
||||||
"components": [
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "core:mob",
|
||||||
|
"args": {
|
||||||
|
"jump_force": 8.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"core:player",
|
||||||
"base:player_animator"
|
"base:player_animator"
|
||||||
],
|
],
|
||||||
"hitbox": [0.6, 1.8, 0.6]
|
"hitbox": [0.6, 1.8, 0.6]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "base",
|
"id": "base",
|
||||||
"title": "Base",
|
"title": "Base",
|
||||||
"version": "0.29",
|
"version": "0.30",
|
||||||
"description": "basic content package"
|
"description": "basic content package"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,9 @@ timer = 0.3
|
|||||||
|
|
||||||
local def_index = entity:def_index()
|
local def_index = entity:def_index()
|
||||||
dropitem = ARGS
|
dropitem = ARGS
|
||||||
|
if dropitem.item then
|
||||||
|
dropitem.id = item.index(dropitem.item)
|
||||||
|
end
|
||||||
if dropitem then
|
if dropitem then
|
||||||
timer = dropitem.pickup_delay or timer
|
timer = dropitem.pickup_delay or timer
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
local tsf = entity.transform
|
local tsf = entity.transform
|
||||||
local body = entity.rigidbody
|
local body = entity.rigidbody
|
||||||
local rig = entity.skeleton
|
local rig = entity.skeleton
|
||||||
|
local mob = entity:require_component("core:mob")
|
||||||
|
|
||||||
local itemid = 0
|
local itemid = 0
|
||||||
local headIndex = rig:index("head")
|
|
||||||
local itemIndex = rig:index("item")
|
local itemIndex = rig:index("item")
|
||||||
local bodyIndex = rig:index("body")
|
|
||||||
|
|
||||||
local function refresh_model(id)
|
local function refresh_model(id)
|
||||||
itemid = id
|
itemid = id
|
||||||
@ -18,10 +17,11 @@ function on_render()
|
|||||||
if pid == -1 then
|
if pid == -1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local rx, ry, rz = player.get_rot(pid, pid ~= hud.get_player())
|
local rx, _, _ = player.get_rot(pid, pid ~= hud.get_player())
|
||||||
rig:set_matrix(headIndex, mat4.rotate({1, 0, 0}, ry))
|
|
||||||
rig:set_matrix(bodyIndex, mat4.rotate({0, 1, 0}, rx))
|
local dir = vec2.rotate({0, -1}, -rx)
|
||||||
|
mob.set_dir({dir[1], 0, dir[2]})
|
||||||
|
|
||||||
local invid, slotid = player.get_inventory(pid)
|
local invid, slotid = player.get_inventory(pid)
|
||||||
local id, _ = inventory.get(invid, slotid)
|
local id, _ = inventory.get(invid, slotid)
|
||||||
|
|||||||
@ -176,18 +176,105 @@ function place_pack(panel, packinfo, callback, position_func)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local Version = {};
|
||||||
|
|
||||||
|
function Version.matches_pattern(version)
|
||||||
|
for _, letter in string.gmatch(version, "%.+") do
|
||||||
|
if type(letter) ~= "number" or letter ~= "." then
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
local t = string.split(version, ".");
|
||||||
|
|
||||||
|
return #t == 2 or #t == 3;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.__equal(ver1, ver2)
|
||||||
|
return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3];
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.__more(ver1, ver2)
|
||||||
|
if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end;
|
||||||
|
if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end;
|
||||||
|
return ver1[3] > ver2[3];
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.__less(ver1, ver2)
|
||||||
|
return Version.__more(ver2, ver1);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.__more_or_equal(ver1, ver2)
|
||||||
|
return not Version.__less(ver1, ver2);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.__less_or_equal(ver1, ver2)
|
||||||
|
return not Version.__more(ver1, ver2);
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.compare(op, ver1, ver2)
|
||||||
|
ver1 = string.split(ver1, ".");
|
||||||
|
ver2 = string.split(ver2, ".");
|
||||||
|
|
||||||
|
if op == "=" then return Version.__equal(ver1, ver2);
|
||||||
|
elseif op == ">" then return Version.__more(ver1, ver2);
|
||||||
|
elseif op == "<" then return Version.__less(ver1, ver2);
|
||||||
|
elseif op == ">=" then return Version.__more_or_equal(ver1, ver2);
|
||||||
|
elseif op == "<=" then return Version.__less_or_equal(ver1, ver2);
|
||||||
|
else return false; end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Version.parse(version)
|
||||||
|
local op = string.sub(version, 1, 2);
|
||||||
|
if op == ">=" or op == "=>" then
|
||||||
|
return ">=", string.sub(version, #op + 1);
|
||||||
|
elseif op == "<=" or op == "=<" then
|
||||||
|
return "<=", string.sub(version, #op + 1);
|
||||||
|
end
|
||||||
|
|
||||||
|
op = string.sub(version, 1, 1);
|
||||||
|
if op == ">" or op == "<" then
|
||||||
|
return op, string.sub(version, #op + 1);
|
||||||
|
end
|
||||||
|
|
||||||
|
return "=", version;
|
||||||
|
end
|
||||||
|
|
||||||
|
local function compare_version(dependent_version, actual_version)
|
||||||
|
if Version.matches_pattern(dependent_version) and Version.matches_pattern(actual_version) then
|
||||||
|
local op, dep_ver = Version.parse_version(dependent_version);
|
||||||
|
Version.compare(op, dep_ver, actual_version);
|
||||||
|
elseif dependent_version == "*" or dependent_version == actual_version then
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function check_dependencies(packinfo)
|
function check_dependencies(packinfo)
|
||||||
if packinfo.dependencies == nil then
|
if packinfo.dependencies == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for i,dep in ipairs(packinfo.dependencies) do
|
for i,dep in ipairs(packinfo.dependencies) do
|
||||||
local depid = dep:sub(2,-1)
|
local depid, depver = unpack(string.split(dep:sub(2,-1), "@"))
|
||||||
if dep:sub(1,1) == '!' then
|
|
||||||
|
if dep:sub(1,1) == '!' then
|
||||||
if not table.has(packs_all, depid) then
|
if not table.has(packs_all, depid) then
|
||||||
return string.format(
|
return string.format(
|
||||||
"%s (%s)", gui.str("error.dependency-not-found"), depid
|
"%s (%s)", gui.str("error.dependency-not-found"), depid
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local dep_pack = pack.get_info(depid);
|
||||||
|
|
||||||
|
if not compare_version(depver, dep_pack.version) then
|
||||||
|
local op, ver = Version.parse(depver);
|
||||||
|
|
||||||
|
print(string.format("%s: %s !%s %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, op, ver, depid));
|
||||||
|
return string.format("%s: %s != %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, ver, depid);
|
||||||
|
end
|
||||||
|
|
||||||
if table.has(packs_installed, packinfo.id) then
|
if table.has(packs_installed, packinfo.id) then
|
||||||
table.insert(required, depid)
|
table.insert(required, depid)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -13,9 +13,9 @@ end
|
|||||||
function refresh()
|
function refresh()
|
||||||
document.list:clear()
|
document.list:clear()
|
||||||
|
|
||||||
local available = pack.get_available()
|
local allpacks = table.merge(pack.get_available(), pack.get_installed())
|
||||||
local infos = pack.get_info(available)
|
local infos = pack.get_info(allpacks)
|
||||||
for _, name in ipairs(available) do
|
for _, name in ipairs(allpacks) do
|
||||||
local info = infos[name]
|
local info = infos[name]
|
||||||
local scripts_dir = info.path.."/scripts/app"
|
local scripts_dir = info.path.."/scripts/app"
|
||||||
if not file.exists(scripts_dir) then
|
if not file.exists(scripts_dir) then
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<panel size='400' color='0' interval='1' context='menu'>
|
<panel size='400' color='0' interval='1' context='menu'>
|
||||||
|
|
||||||
<button onclick='menu.page="new_world"'>@New World</button>
|
<button onclick='menu.page="new_world"'>@New World</button>
|
||||||
|
|
||||||
<panel id='worlds' size='390,1' padding='5' color='#FFFFFF11' max-length='400'>
|
<panel id='worlds' size='390,1' padding='5' color='#FFFFFF11' max-length='400'>
|
||||||
|
|||||||
10
res/modules/internal/asserts.lua
Normal file
10
res/modules/internal/asserts.lua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
local this = {}
|
||||||
|
|
||||||
|
function this.equals(expected, fact)
|
||||||
|
assert(fact == expected, string.format(
|
||||||
|
"(fact == expected) assertion failed\n Expected: %s\n Fact: %s",
|
||||||
|
expected, fact
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
return this
|
||||||
@ -110,6 +110,21 @@ function vec3.dot(a, b)
|
|||||||
return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
|
return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function vec3.mix(a, b, t, dest)
|
||||||
|
if dest then
|
||||||
|
dest[1] = a[1] * (1.0 - t) + b[1] * t
|
||||||
|
dest[2] = a[2] * (1.0 - t) + b[2] * t
|
||||||
|
dest[3] = a[3] * (1.0 - t) + b[3] * t
|
||||||
|
return dest
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
a[1] * (1.0 - t) + b[1] * t,
|
||||||
|
a[2] * (1.0 - t) + b[2] * t,
|
||||||
|
a[3] * (1.0 - t) + b[3] * t,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- =================================================== --
|
-- =================================================== --
|
||||||
-- ====================== vec2 ======================= --
|
-- ====================== vec2 ======================= --
|
||||||
-- =================================================== --
|
-- =================================================== --
|
||||||
@ -210,3 +225,16 @@ end
|
|||||||
function vec2.dot(a, b)
|
function vec2.dot(a, b)
|
||||||
return a[1] * b[1] + a[2] * b[2]
|
return a[1] * b[1] + a[2] * b[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function vec2.mix(a, b, t, dest)
|
||||||
|
if dest then
|
||||||
|
dest[1] = a[1] * (1.0 - t) + b[1] * t
|
||||||
|
dest[2] = a[2] * (1.0 - t) + b[2] * t
|
||||||
|
return dest
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
a[1] * (1.0 - t) + b[1] * t,
|
||||||
|
a[2] * (1.0 - t) + b[2] * t,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
35
res/modules/internal/random_generator.lua
Normal file
35
res/modules/internal/random_generator.lua
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
local Random = {}
|
||||||
|
|
||||||
|
local M = 2 ^ 31
|
||||||
|
local A = 1103515245
|
||||||
|
local C = 12345
|
||||||
|
|
||||||
|
function Random.randint(self)
|
||||||
|
self._seed = (A * self._seed + C) % M
|
||||||
|
return self._seed
|
||||||
|
end
|
||||||
|
|
||||||
|
function Random.random(self, a, b)
|
||||||
|
local num = self:randint() % M / M
|
||||||
|
if b then
|
||||||
|
return math.floor(num * (b - a + 1) + a)
|
||||||
|
elseif a then
|
||||||
|
return math.floor(num * a + 1)
|
||||||
|
else
|
||||||
|
return num
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Random.seed(self, number)
|
||||||
|
if type(number) ~= "number" then
|
||||||
|
error("number expected")
|
||||||
|
end
|
||||||
|
self._seed = number
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(seed)
|
||||||
|
if seed and type(seed) ~= "number" then
|
||||||
|
error("number expected")
|
||||||
|
end
|
||||||
|
return setmetatable({_seed = seed or random.random(M)}, {__index = Random})
|
||||||
|
end
|
||||||
@ -25,6 +25,7 @@ local Rigidbody = {__index={
|
|||||||
get_linear_damping=function(self) return __rigidbody.get_linear_damping(self.eid) end,
|
get_linear_damping=function(self) return __rigidbody.get_linear_damping(self.eid) end,
|
||||||
set_linear_damping=function(self, f) return __rigidbody.set_linear_damping(self.eid, f) end,
|
set_linear_damping=function(self, f) return __rigidbody.set_linear_damping(self.eid, f) end,
|
||||||
is_vdamping=function(self) return __rigidbody.is_vdamping(self.eid) end,
|
is_vdamping=function(self) return __rigidbody.is_vdamping(self.eid) end,
|
||||||
|
get_vdamping=function(self) return __rigidbody.get_vdamping(self.eid) end,
|
||||||
set_vdamping=function(self, b) return __rigidbody.set_vdamping(self.eid, b) end,
|
set_vdamping=function(self, b) return __rigidbody.set_vdamping(self.eid, b) end,
|
||||||
is_grounded=function(self) return __rigidbody.is_grounded(self.eid) end,
|
is_grounded=function(self) return __rigidbody.is_grounded(self.eid) end,
|
||||||
is_crouching=function(self) return __rigidbody.is_crouching(self.eid) end,
|
is_crouching=function(self) return __rigidbody.is_crouching(self.eid) end,
|
||||||
@ -63,6 +64,13 @@ local Entity = {__index={
|
|||||||
get_skeleton=function(self) return entities.get_skeleton(self.eid) end,
|
get_skeleton=function(self) return entities.get_skeleton(self.eid) end,
|
||||||
set_skeleton=function(self, s) return entities.set_skeleton(self.eid, s) end,
|
set_skeleton=function(self, s) return entities.set_skeleton(self.eid, s) end,
|
||||||
get_component=function(self, name) return self.components[name] end,
|
get_component=function(self, name) return self.components[name] end,
|
||||||
|
require_component=function(self, name)
|
||||||
|
local component = self.components[name]
|
||||||
|
if not component then
|
||||||
|
error(("entity has no required component '%s'"):format(name))
|
||||||
|
end
|
||||||
|
return component
|
||||||
|
end,
|
||||||
has_component=function(self, name) return self.components[name] ~= nil end,
|
has_component=function(self, name) return self.components[name] ~= nil end,
|
||||||
get_uid=function(self) return self.eid end,
|
get_uid=function(self) return self.eid end,
|
||||||
def_index=function(self) return entities.get_def(self.eid) end,
|
def_index=function(self) return entities.get_def(self.eid) end,
|
||||||
@ -125,6 +133,19 @@ return {
|
|||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
physics_update = function(delta)
|
||||||
|
for uid, entity in pairs(entities) do
|
||||||
|
for _, component in pairs(entity.components) do
|
||||||
|
local callback = component.on_physics_update
|
||||||
|
if not component.__disabled and callback then
|
||||||
|
local result, err = pcall(callback, delta)
|
||||||
|
if err then
|
||||||
|
debug.error(err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
render = function(delta)
|
render = function(delta)
|
||||||
for _,entity in pairs(entities) do
|
for _,entity in pairs(entities) do
|
||||||
for _, component in pairs(entity.components) do
|
for _, component in pairs(entity.components) do
|
||||||
|
|||||||
@ -11,6 +11,9 @@ local Schedule = {
|
|||||||
self._next_interval = id + 1
|
self._next_interval = id + 1
|
||||||
return id
|
return id
|
||||||
end,
|
end,
|
||||||
|
set_timeout = function(self, ms, callback)
|
||||||
|
self:set_interval(ms, callback, 1)
|
||||||
|
end,
|
||||||
tick = function(self, dt)
|
tick = function(self, dt)
|
||||||
local timer = self._timer + dt
|
local timer = self._timer + dt
|
||||||
for id, interval in pairs(self._intervals) do
|
for id, interval in pairs(self._intervals) do
|
||||||
|
|||||||
@ -23,4 +23,5 @@ function on_menu_setup()
|
|||||||
menubg = gui.root.menubg
|
menubg = gui.root.menubg
|
||||||
controller.resize_menu_bg()
|
controller.resize_menu_bg()
|
||||||
menu.page = "main"
|
menu.page = "main"
|
||||||
|
menu.visible = true
|
||||||
end
|
end
|
||||||
|
|||||||
@ -74,6 +74,38 @@ local _tcp_client_callbacks = {}
|
|||||||
local _udp_server_callbacks = {}
|
local _udp_server_callbacks = {}
|
||||||
local _udp_client_datagram_callbacks = {}
|
local _udp_client_datagram_callbacks = {}
|
||||||
local _udp_client_open_callbacks = {}
|
local _udp_client_open_callbacks = {}
|
||||||
|
local _http_response_callbacks = {}
|
||||||
|
local _http_error_callbacks = {}
|
||||||
|
|
||||||
|
network.get = function(url, callback, errorCallback, headers)
|
||||||
|
local id = network.__get(url, headers)
|
||||||
|
if callback then
|
||||||
|
_http_response_callbacks[id] = callback
|
||||||
|
end
|
||||||
|
if errorCallback then
|
||||||
|
_http_error_callbacks[id] = errorCallback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
network.get_binary = function(url, callback, errorCallback, headers)
|
||||||
|
local id = network.__get_binary(url, headers)
|
||||||
|
if callback then
|
||||||
|
_http_response_callbacks[id] = callback
|
||||||
|
end
|
||||||
|
if errorCallback then
|
||||||
|
_http_error_callbacks[id] = errorCallback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
network.post = function(url, data, callback, errorCallback, headers)
|
||||||
|
local id = network.__post(url, data, headers)
|
||||||
|
if callback then
|
||||||
|
_http_response_callbacks[id] = callback
|
||||||
|
end
|
||||||
|
if errorCallback then
|
||||||
|
_http_error_callbacks[id] = errorCallback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
network.tcp_open = function (port, handler)
|
network.tcp_open = function (port, handler)
|
||||||
local socket = setmetatable({id=network.__open_tcp(port)}, ServerSocket)
|
local socket = setmetatable({id=network.__open_tcp(port)}, ServerSocket)
|
||||||
@ -131,10 +163,80 @@ local function clean(iterable, checkFun, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local updating_blocks = {}
|
||||||
|
local TYPE_REGISTER = 0
|
||||||
|
local TYPE_UNREGISTER = 1
|
||||||
|
|
||||||
|
block.__perform_ticks = function(delta)
|
||||||
|
for id, entry in pairs(updating_blocks) do
|
||||||
|
entry.timer = entry.timer + delta
|
||||||
|
local steps = math.floor(entry.timer / entry.delta * #entry / 3)
|
||||||
|
if steps == 0 then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
entry.timer = 0.0
|
||||||
|
local event = entry.event
|
||||||
|
local tps = entry.tps
|
||||||
|
for i=1, steps do
|
||||||
|
local x = entry[entry.pointer + 1]
|
||||||
|
local y = entry[entry.pointer + 2]
|
||||||
|
local z = entry[entry.pointer + 3]
|
||||||
|
entry.pointer = (entry.pointer + 3) % #entry
|
||||||
|
events.emit(event, x, y, z, tps)
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
block.__process_register_events = function()
|
||||||
|
local register_events = block.__pull_register_events()
|
||||||
|
if not register_events then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for i=1, #register_events, 4 do
|
||||||
|
local header = register_events[i]
|
||||||
|
local type = bit.band(header, 0xFFFF)
|
||||||
|
local id = bit.rshift(header, 16)
|
||||||
|
local x = register_events[i + 1]
|
||||||
|
local y = register_events[i + 2]
|
||||||
|
local z = register_events[i + 3]
|
||||||
|
|
||||||
|
local list = updating_blocks[id]
|
||||||
|
if type == TYPE_REGISTER then
|
||||||
|
if not list then
|
||||||
|
list = {}
|
||||||
|
list.event = block.name(id) .. ".blocktick"
|
||||||
|
list.tps = 20 / (block.properties[id]["tick-interval"] or 1)
|
||||||
|
list.delta = 1.0 / list.tps
|
||||||
|
list.timer = 0.0
|
||||||
|
list.pointer = 0
|
||||||
|
updating_blocks[id] = list
|
||||||
|
end
|
||||||
|
table.insert(list, x)
|
||||||
|
table.insert(list, y)
|
||||||
|
table.insert(list, z)
|
||||||
|
elseif type == TYPE_UNREGISTER then
|
||||||
|
if list then
|
||||||
|
for j=1, #list, 3 do
|
||||||
|
if list[j] == x and list[j + 1] == y and list[j + 2] == z then
|
||||||
|
for k=1,3 do
|
||||||
|
table.remove(list, j)
|
||||||
|
end
|
||||||
|
j = j - 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print(type, id, x, y, z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
network.__process_events = function()
|
network.__process_events = function()
|
||||||
local CLIENT_CONNECTED = 1
|
local CLIENT_CONNECTED = 1
|
||||||
local CONNECTED_TO_SERVER = 2
|
local CONNECTED_TO_SERVER = 2
|
||||||
local DATAGRAM = 3
|
local DATAGRAM = 3
|
||||||
|
local RESPONSE = 4
|
||||||
|
|
||||||
local ON_SERVER = 1
|
local ON_SERVER = 1
|
||||||
local ON_CLIENT = 2
|
local ON_CLIENT = 2
|
||||||
@ -160,6 +262,22 @@ network.__process_events = function()
|
|||||||
elseif side == ON_SERVER then
|
elseif side == ON_SERVER then
|
||||||
_udp_server_callbacks[sid](addr, port, data)
|
_udp_server_callbacks[sid](addr, port, data)
|
||||||
end
|
end
|
||||||
|
elseif etype == RESPONSE then
|
||||||
|
if event[2] / 100 == 2 then
|
||||||
|
local callback = _http_response_callbacks[event[3]]
|
||||||
|
_http_response_callbacks[event[3]] = nil
|
||||||
|
_http_error_callbacks[event[3]] = nil
|
||||||
|
if callback then
|
||||||
|
callback(event[4])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local callback = _http_error_callbacks[event[3]]
|
||||||
|
_http_response_callbacks[event[3]] = nil
|
||||||
|
_http_error_callbacks[event[3]] = nil
|
||||||
|
if callback then
|
||||||
|
callback(event[2], event[4])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- remove dead servers
|
-- remove dead servers
|
||||||
|
|||||||
177
res/scripts/components/mob.lua
Normal file
177
res/scripts/components/mob.lua
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
local body = entity.rigidbody
|
||||||
|
local tsf = entity.transform
|
||||||
|
local rig = entity.skeleton
|
||||||
|
|
||||||
|
local props = {}
|
||||||
|
|
||||||
|
local function def_prop(name, def_value)
|
||||||
|
props[name] = SAVED_DATA[name] or ARGS[name] or def_value
|
||||||
|
this["get_"..name] = function() return props[name] end
|
||||||
|
this["set_"..name] = function(value)
|
||||||
|
props[name] = value
|
||||||
|
if math.abs(value - def_value) < 1e-7 then
|
||||||
|
SAVED_DATA[name] = nil
|
||||||
|
else
|
||||||
|
SAVED_DATA[name] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def_prop("jump_force", 0.0)
|
||||||
|
def_prop("air_damping", 1.0)
|
||||||
|
def_prop("ground_damping", 1.0)
|
||||||
|
def_prop("movement_speed", 3.0)
|
||||||
|
def_prop("run_speed_mul", 1.5)
|
||||||
|
def_prop("crouch_speed_mul", 0.35)
|
||||||
|
def_prop("flight_speed_mul", 2.0)
|
||||||
|
def_prop("gravity_scale", 1.0)
|
||||||
|
|
||||||
|
local function normalize_angle(angle)
|
||||||
|
while angle > 180 do
|
||||||
|
angle = angle - 360
|
||||||
|
end
|
||||||
|
while angle <= -180 do
|
||||||
|
angle = angle + 360
|
||||||
|
end
|
||||||
|
return angle
|
||||||
|
end
|
||||||
|
|
||||||
|
local function angle_delta(a, b)
|
||||||
|
return normalize_angle(a - b)
|
||||||
|
end
|
||||||
|
|
||||||
|
local dir = mat4.mul(tsf:get_rot(), {0, 0, -1})
|
||||||
|
local flight = false
|
||||||
|
|
||||||
|
function jump(multiplier)
|
||||||
|
local vel = body:get_vel()
|
||||||
|
body:set_vel(
|
||||||
|
vec3.add(vel, {0, props.jump_force * (multiplier or 1.0), 0}, vel))
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_vertical(speed, vel)
|
||||||
|
vel = vel or body:get_vel()
|
||||||
|
vel[2] = vel[2] * 0.2 + props.movement_speed * speed * 0.8
|
||||||
|
body:set_vel(vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function move_horizontal(speed, dir, vel)
|
||||||
|
vel = vel or body:get_vel()
|
||||||
|
if vec2.length(dir) > 0.0 then
|
||||||
|
vec2.normalize(dir, dir)
|
||||||
|
|
||||||
|
local magnitude = vec2.length({vel[1], vel[3]})
|
||||||
|
|
||||||
|
if magnitude <= 1e-4 or (magnitude < speed or vec2.dot(
|
||||||
|
{vel[1] / magnitude, vel[3] / magnitude}, dir) < 0.9)
|
||||||
|
then
|
||||||
|
vel[1] = vel[1] * 0.2 + dir[1] * speed * 0.8
|
||||||
|
vel[3] = vel[3] * 0.2 + dir[2] * speed * 0.8
|
||||||
|
end
|
||||||
|
magnitude = vec3.length({vel[1], 0, vel[3]})
|
||||||
|
if vec2.dot({vel[1] / magnitude, vel[3] / magnitude}, dir) > 0.5 then
|
||||||
|
vel[1] = vel[1] / magnitude * speed
|
||||||
|
vel[3] = vel[3] / magnitude * speed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
body:set_vel(vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function go(dir, speed_multiplier, sprint, crouch, vel)
|
||||||
|
local speed = props.movement_speed * speed_multiplier
|
||||||
|
if flight then
|
||||||
|
speed = speed * props.flight_speed_mul
|
||||||
|
end
|
||||||
|
if sprint then
|
||||||
|
speed = speed * props.run_speed_mul
|
||||||
|
elseif crouch then
|
||||||
|
speed = speed * props.crouch_speed_mul
|
||||||
|
end
|
||||||
|
move_horizontal(speed, dir, vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local headIndex = rig:index("head")
|
||||||
|
|
||||||
|
function look_at(point, change_dir)
|
||||||
|
local pos = tsf:get_pos()
|
||||||
|
local viewdir = vec3.normalize(vec3.sub(point, pos))
|
||||||
|
|
||||||
|
local dot = vec3.dot(viewdir, dir)
|
||||||
|
if dot < 0.0 and not change_dir then
|
||||||
|
viewdir = mat4.mul(tsf:get_rot(), {0, 0, -1})
|
||||||
|
else
|
||||||
|
dir[1] = dir[1] * 0.8 + viewdir[1] * 0.13
|
||||||
|
dir[3] = dir[3] * 0.8 + viewdir[3] * 0.13
|
||||||
|
end
|
||||||
|
|
||||||
|
if not headIndex then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local headrot = mat4.idt()
|
||||||
|
local curdir = mat4.mul(mat4.mul(tsf:get_rot(),
|
||||||
|
rig:get_matrix(headIndex)), {0, 0, -1})
|
||||||
|
|
||||||
|
vec3.mix(curdir, viewdir, 0.2, viewdir)
|
||||||
|
|
||||||
|
headrot = mat4.inverse(mat4.look_at({0,0,0}, viewdir, {0, 1, 0}))
|
||||||
|
headrot = mat4.mul(mat4.inverse(tsf:get_rot()), headrot)
|
||||||
|
rig:set_matrix(headIndex, headrot)
|
||||||
|
end
|
||||||
|
|
||||||
|
function follow_waypoints(pathfinding)
|
||||||
|
pathfinding = pathfinding or entity:require_component("core:pathfinding")
|
||||||
|
local pos = tsf:get_pos()
|
||||||
|
local waypoint = pathfinding.next_waypoint()
|
||||||
|
if not waypoint then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local speed = props.movement_speed
|
||||||
|
local vel = body:get_vel()
|
||||||
|
dir = vec3.sub(
|
||||||
|
vec3.add(waypoint, {0.5, 0, 0.5}),
|
||||||
|
{pos[1], math.floor(pos[2]), pos[3]}
|
||||||
|
)
|
||||||
|
local upper = dir[2] > 0
|
||||||
|
dir[2] = 0.0
|
||||||
|
vec3.normalize(dir, dir)
|
||||||
|
move_horizontal(speed, {dir[1], dir[3]}, vel)
|
||||||
|
if upper and body:is_grounded() then
|
||||||
|
jump(1.0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_dir(new_dir)
|
||||||
|
dir = new_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
function is_flight() return flight end
|
||||||
|
|
||||||
|
function set_flight(flag) flight = flag end
|
||||||
|
|
||||||
|
local prev_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||||
|
|
||||||
|
function on_physics_update(delta)
|
||||||
|
local grounded = body:is_grounded()
|
||||||
|
body:set_vdamping(flight)
|
||||||
|
body:set_gravity_scale({0, flight and 0.0 or props.gravity_scale, 0})
|
||||||
|
body:set_linear_damping(
|
||||||
|
(flight or not grounded) and props.air_damping or props.ground_damping
|
||||||
|
)
|
||||||
|
|
||||||
|
local new_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||||
|
local angle = prev_angle
|
||||||
|
|
||||||
|
local adelta = angle_delta(
|
||||||
|
normalize_angle(new_angle),
|
||||||
|
normalize_angle(prev_angle)
|
||||||
|
)
|
||||||
|
local rotate_speed = entity:get_player() == -1 and 200 or 400
|
||||||
|
|
||||||
|
if math.abs(adelta) > 5 then
|
||||||
|
angle = angle + delta * rotate_speed * (adelta > 0 and 1 or -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
tsf:set_rot(mat4.rotate({0, 1, 0}, angle + 180))
|
||||||
|
prev_angle = angle
|
||||||
|
end
|
||||||
71
res/scripts/components/pathfinding.lua
Normal file
71
res/scripts/components/pathfinding.lua
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
local target
|
||||||
|
local route
|
||||||
|
local started
|
||||||
|
|
||||||
|
local tsf = entity.transform
|
||||||
|
local body = entity.rigidbody
|
||||||
|
|
||||||
|
agent = pathfinding.create_agent()
|
||||||
|
pathfinding.set_max_visited(agent, 1e3)
|
||||||
|
pathfinding.avoid_tag(agent, "core:liquid", 8)
|
||||||
|
|
||||||
|
function set_target(new_target)
|
||||||
|
target = new_target
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_jump_height(height)
|
||||||
|
pathfinding.set_jump_height(agent, height)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_target()
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_route()
|
||||||
|
return route
|
||||||
|
end
|
||||||
|
|
||||||
|
function next_waypoint()
|
||||||
|
if not route or #route == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local waypoint = route[#route]
|
||||||
|
local pos = tsf:get_pos()
|
||||||
|
local dst = vec2.length({
|
||||||
|
math.floor(waypoint[1] - math.floor(pos[1])),
|
||||||
|
math.floor(waypoint[3] - math.floor(pos[3]))
|
||||||
|
})
|
||||||
|
if dst < 1.0 then
|
||||||
|
table.remove(route, #route)
|
||||||
|
end
|
||||||
|
return route[#route]
|
||||||
|
end
|
||||||
|
|
||||||
|
local refresh_internal = 100
|
||||||
|
local frameid = math.random(0, refresh_internal)
|
||||||
|
|
||||||
|
function set_refresh_interval(interval)
|
||||||
|
refresh_internal = interval
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_update()
|
||||||
|
if not started then
|
||||||
|
frameid = frameid + 1
|
||||||
|
if body:is_grounded() then
|
||||||
|
if target and (frameid % refresh_internal == 1 or not route) then
|
||||||
|
pathfinding.make_route_async(agent, tsf:get_pos(), target)
|
||||||
|
started = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local new_route = pathfinding.pull_route(agent)
|
||||||
|
if new_route then
|
||||||
|
route = new_route
|
||||||
|
started = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_despawn()
|
||||||
|
pathfinding.remove_agent(agent)
|
||||||
|
end
|
||||||
62
res/scripts/components/player.lua
Normal file
62
res/scripts/components/player.lua
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
local tsf = entity.transform
|
||||||
|
local body = entity.rigidbody
|
||||||
|
local mob = entity:require_component("core:mob")
|
||||||
|
|
||||||
|
local cheat_speed_mul = 10.0
|
||||||
|
|
||||||
|
local function process_player_inputs(pid, delta)
|
||||||
|
if not hud or hud.is_inventory_open() or menu.page ~= "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cam = cameras.get("core:first-person")
|
||||||
|
local front = cam:get_front()
|
||||||
|
local right = cam:get_right()
|
||||||
|
front[2] = 0.0
|
||||||
|
vec3.normalize(front, front)
|
||||||
|
|
||||||
|
local isjump = input.is_active('movement.jump')
|
||||||
|
local issprint = input.is_active('movement.sprint')
|
||||||
|
local iscrouch = input.is_active('movement.crouch')
|
||||||
|
local isforward = input.is_active('movement.forward')
|
||||||
|
local ischeat = input.is_active('movement.cheat')
|
||||||
|
local isback = input.is_active('movement.back')
|
||||||
|
local isleft = input.is_active('movement.left')
|
||||||
|
local isright = input.is_active('movement.right')
|
||||||
|
mob.set_flight(player.is_flight(pid))
|
||||||
|
body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic")
|
||||||
|
body:set_crouching(iscrouch)
|
||||||
|
|
||||||
|
local vel = body:get_vel()
|
||||||
|
local speed = ischeat and cheat_speed_mul or 1.0
|
||||||
|
|
||||||
|
local dir = {0, 0, 0}
|
||||||
|
|
||||||
|
if isforward then vec3.add(dir, front, dir) end
|
||||||
|
if isback then vec3.sub(dir, front, dir) end
|
||||||
|
if isright then vec3.add(dir, right, dir) end
|
||||||
|
if isleft then vec3.sub(dir, right, dir) end
|
||||||
|
|
||||||
|
if vec3.length(dir) > 0.0 then
|
||||||
|
mob.go({dir[1], dir[3]}, speed, issprint, iscrouch, vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
if mob.is_flight() then
|
||||||
|
if isjump then
|
||||||
|
mob.move_vertical(speed * 3)
|
||||||
|
elseif iscrouch then
|
||||||
|
mob.move_vertical(-speed * 3)
|
||||||
|
end
|
||||||
|
elseif body:is_grounded() and isjump then
|
||||||
|
mob.jump()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_physics_update(delta)
|
||||||
|
local pid = entity:get_player()
|
||||||
|
if pid ~= -1 then
|
||||||
|
local pos = tsf:get_pos()
|
||||||
|
local cam = cameras.get("core:first-person")
|
||||||
|
process_player_inputs(pid, delta)
|
||||||
|
mob.look_at(vec3.add(pos, cam:get_front()))
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -22,6 +22,39 @@ local function configure_SSAO()
|
|||||||
-- for test purposes
|
-- for test purposes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function update_hand()
|
||||||
|
local skeleton = gfx.skeletons
|
||||||
|
local pid = hud.get_player()
|
||||||
|
local invid, slot = player.get_inventory(pid)
|
||||||
|
local itemid = inventory.get(invid, slot)
|
||||||
|
|
||||||
|
local cam = cameras.get("core:first-person")
|
||||||
|
local bone = skeleton.index("hand", "item")
|
||||||
|
|
||||||
|
local offset = vec3.mul(vec3.sub(cam:get_pos(), {player.get_pos(pid)}), -1)
|
||||||
|
|
||||||
|
local rotation = cam:get_rot()
|
||||||
|
|
||||||
|
local angle = player.get_rot(pid) - 90
|
||||||
|
local cos = math.cos(angle / (180 / math.pi))
|
||||||
|
local sin = math.sin(angle / (180 / math.pi))
|
||||||
|
|
||||||
|
local newX = offset[1] * cos - offset[3] * sin
|
||||||
|
local newZ = offset[1] * sin + offset[3] * cos
|
||||||
|
|
||||||
|
offset[1] = newX
|
||||||
|
offset[3] = newZ
|
||||||
|
|
||||||
|
local mat = mat4.translate(mat4.idt(), {0.06, 0.035, -0.1})
|
||||||
|
mat4.scale(mat, {0.1, 0.1, 0.1}, mat)
|
||||||
|
mat4.mul(rotation, mat, mat)
|
||||||
|
mat4.rotate(mat, {0, 1, 0}, -90, mat)
|
||||||
|
mat4.translate(mat, offset, mat)
|
||||||
|
|
||||||
|
skeleton.set_matrix("hand", bone, mat)
|
||||||
|
skeleton.set_model("hand", bone, item.model_name(itemid))
|
||||||
|
end
|
||||||
|
|
||||||
function on_hud_open()
|
function on_hud_open()
|
||||||
input.add_callback("player.pick", function ()
|
input.add_callback("player.pick", function ()
|
||||||
if hud.is_paused() or hud.is_inventory_open() then
|
if hud.is_paused() or hud.is_inventory_open() then
|
||||||
@ -63,7 +96,7 @@ function on_hud_open()
|
|||||||
player.set_noclip(pid, true)
|
player.set_noclip(pid, true)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
input.add_callback("player.flight", function ()
|
input.add_callback("player.flight", function ()
|
||||||
if hud.is_paused() or hud.is_inventory_open() then
|
if hud.is_paused() or hud.is_inventory_open() then
|
||||||
return
|
return
|
||||||
@ -81,39 +114,8 @@ function on_hud_open()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
configure_SSAO()
|
configure_SSAO()
|
||||||
end
|
|
||||||
|
|
||||||
local function update_hand()
|
hud.default_hand_controller = update_hand
|
||||||
local skeleton = gfx.skeletons
|
|
||||||
local pid = hud.get_player()
|
|
||||||
local invid, slot = player.get_inventory(pid)
|
|
||||||
local itemid = inventory.get(invid, slot)
|
|
||||||
|
|
||||||
local cam = cameras.get("core:first-person")
|
|
||||||
local bone = skeleton.index("hand", "item")
|
|
||||||
|
|
||||||
local offset = vec3.mul(vec3.sub(cam:get_pos(), {player.get_pos(pid)}), -1)
|
|
||||||
|
|
||||||
local rotation = cam:get_rot()
|
|
||||||
|
|
||||||
local angle = player.get_rot() - 90
|
|
||||||
local cos = math.cos(angle / (180 / math.pi))
|
|
||||||
local sin = math.sin(angle / (180 / math.pi))
|
|
||||||
|
|
||||||
local newX = offset[1] * cos - offset[3] * sin
|
|
||||||
local newZ = offset[1] * sin + offset[3] * cos
|
|
||||||
|
|
||||||
offset[1] = newX
|
|
||||||
offset[3] = newZ
|
|
||||||
|
|
||||||
local mat = mat4.translate(mat4.idt(), {0.06, 0.035, -0.1})
|
|
||||||
mat4.scale(mat, {0.1, 0.1, 0.1}, mat)
|
|
||||||
mat4.mul(rotation, mat, mat)
|
|
||||||
mat4.rotate(mat, {0, 1, 0}, -90, mat)
|
|
||||||
mat4.translate(mat, offset, mat)
|
|
||||||
|
|
||||||
skeleton.set_matrix("hand", bone, mat)
|
|
||||||
skeleton.set_model("hand", bone, item.model_name(itemid))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_hud_render()
|
function on_hud_render()
|
||||||
|
|||||||
@ -6,28 +6,29 @@ local names = {
|
|||||||
"shadeless", "ambient-occlusion", "breakable", "selectable", "grounded",
|
"shadeless", "ambient-occlusion", "breakable", "selectable", "grounded",
|
||||||
"hidden", "draw-group", "picking-item", "surface-replacement", "script-name",
|
"hidden", "draw-group", "picking-item", "surface-replacement", "script-name",
|
||||||
"ui-layout", "inventory-size", "tick-interval", "overlay-texture",
|
"ui-layout", "inventory-size", "tick-interval", "overlay-texture",
|
||||||
"translucent", "fields", "particles", "icon-type", "icon", "placing-block",
|
"translucent", "fields", "particles", "icon-type", "icon", "placing-block",
|
||||||
"stack-size", "name", "script-file", "culling"
|
"stack-size", "name", "script-file", "culling"
|
||||||
}
|
}
|
||||||
for name, _ in pairs(user_props) do
|
for name, _ in pairs(user_props) do
|
||||||
table.insert(names, name)
|
table.insert(names, name)
|
||||||
end
|
end
|
||||||
-- remove undefined properties
|
|
||||||
for id, blockprops in pairs(block.properties) do
|
-- remove undefined properties and build tags set
|
||||||
for propname, value in pairs(blockprops) do
|
local function process_properties(lib)
|
||||||
if not table.has(names, propname) then
|
for id, props in pairs(lib.properties) do
|
||||||
blockprops[propname] = nil
|
for propname, _ in pairs(props) do
|
||||||
end
|
if not table.has(names, propname) then
|
||||||
end
|
props[propname] = nil
|
||||||
end
|
end
|
||||||
for id, itemprops in pairs(item.properties) do
|
|
||||||
for propname, value in pairs(itemprops) do
|
|
||||||
if not table.has(names, propname) then
|
|
||||||
itemprops[propname] = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
props.tags_set = lib.__get_tags(id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
process_properties(block)
|
||||||
|
process_properties(item)
|
||||||
|
|
||||||
local function make_read_only(t)
|
local function make_read_only(t)
|
||||||
setmetatable(t, {
|
setmetatable(t, {
|
||||||
__newindex = function()
|
__newindex = function()
|
||||||
@ -57,6 +58,19 @@ local function cache_names(library)
|
|||||||
function library.index(name)
|
function library.index(name)
|
||||||
return indices[name]
|
return indices[name]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function library.has_tag(id, tag)
|
||||||
|
if id == nil then
|
||||||
|
error("id is nil")
|
||||||
|
end
|
||||||
|
local props = library.properties[id]
|
||||||
|
local tags_set = props.tags_set
|
||||||
|
if tags_set then
|
||||||
|
return tags_set[tag]
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
cache_names(block)
|
cache_names(block)
|
||||||
|
|||||||
@ -157,6 +157,16 @@ console.add_command(
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
console.add_command(
|
||||||
|
"entity.spawn name:str x:num~pos.x y:num~pos.y z:num~pos.z",
|
||||||
|
"Spawn entity with default parameters",
|
||||||
|
function(args, kwargs)
|
||||||
|
local eid = entities.spawn(args[1], {args[2], args[3], args[4]})
|
||||||
|
return string.format("spawned %s at %s, %s, %s", unpack(args))
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
console.add_command(
|
console.add_command(
|
||||||
"entity.despawn entity:sel=$entity.selected",
|
"entity.despawn entity:sel=$entity.selected",
|
||||||
"Despawn entity",
|
"Despawn entity",
|
||||||
|
|||||||
@ -79,13 +79,20 @@ local function complete_app_lib(app)
|
|||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
|
|
||||||
function app.sleep_until(predicate, max_ticks)
|
function app.sleep_until(predicate, max_ticks, max_time)
|
||||||
max_ticks = max_ticks or 1e9
|
max_ticks = max_ticks or 1e9
|
||||||
|
max_time = max_time or 1e9
|
||||||
local ticks = 0
|
local ticks = 0
|
||||||
while ticks < max_ticks and not predicate() do
|
local start_time = os.clock()
|
||||||
|
while ticks < max_ticks and
|
||||||
|
os.clock() - start_time < max_time
|
||||||
|
and not predicate() do
|
||||||
app.tick()
|
app.tick()
|
||||||
ticks = ticks + 1
|
ticks = ticks + 1
|
||||||
end
|
end
|
||||||
|
if os.clock() - start_time >= max_time then
|
||||||
|
error("timeout")
|
||||||
|
end
|
||||||
if ticks == max_ticks then
|
if ticks == max_ticks then
|
||||||
error("max ticks exceed")
|
error("max ticks exceed")
|
||||||
end
|
end
|
||||||
@ -174,6 +181,7 @@ if enable_experimental then
|
|||||||
require "core:internal/maths_inline"
|
require "core:internal/maths_inline"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
asserts = require "core:internal/asserts"
|
||||||
events = require "core:internal/events"
|
events = require "core:internal/events"
|
||||||
|
|
||||||
function pack.unload(prefix)
|
function pack.unload(prefix)
|
||||||
@ -429,6 +437,8 @@ function __vc_on_hud_open()
|
|||||||
hud.open_permanent("core:ingame_chat")
|
hud.open_permanent("core:ingame_chat")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local Schedule = require "core:schedule"
|
||||||
|
|
||||||
local ScheduleGroup_mt = {
|
local ScheduleGroup_mt = {
|
||||||
__index = {
|
__index = {
|
||||||
publish = function(self, schedule)
|
publish = function(self, schedule)
|
||||||
@ -440,10 +450,11 @@ local ScheduleGroup_mt = {
|
|||||||
for id, schedule in pairs(self._schedules) do
|
for id, schedule in pairs(self._schedules) do
|
||||||
schedule:tick(dt)
|
schedule:tick(dt)
|
||||||
end
|
end
|
||||||
|
self.common:tick(dt)
|
||||||
end,
|
end,
|
||||||
remove = function(self, id)
|
remove = function(self, id)
|
||||||
self._schedules[id] = nil
|
self._schedules[id] = nil
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,6 +462,7 @@ local function ScheduleGroup()
|
|||||||
return setmetatable({
|
return setmetatable({
|
||||||
_next_schedule = 1,
|
_next_schedule = 1,
|
||||||
_schedules = {},
|
_schedules = {},
|
||||||
|
common = Schedule()
|
||||||
}, ScheduleGroup_mt)
|
}, ScheduleGroup_mt)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -527,15 +539,18 @@ function start_coroutine(chunk, name)
|
|||||||
local co = coroutine.create(function()
|
local co = coroutine.create(function()
|
||||||
local status, error = xpcall(chunk, function(err)
|
local status, error = xpcall(chunk, function(err)
|
||||||
local fullmsg = "error: "..string.match(err, ": (.+)").."\n"..debug.traceback()
|
local fullmsg = "error: "..string.match(err, ": (.+)").."\n"..debug.traceback()
|
||||||
gui.alert(fullmsg, function()
|
|
||||||
if world.is_open() then
|
if hud then
|
||||||
__vc_app.close_world()
|
gui.alert(fullmsg, function()
|
||||||
else
|
if world.is_open() then
|
||||||
__vc_app.reset_content()
|
__vc_app.close_world()
|
||||||
menu:reset()
|
else
|
||||||
menu.page = "main"
|
__vc_app.reset_content()
|
||||||
end
|
menu:reset()
|
||||||
end)
|
menu.page = "main"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
return fullmsg
|
return fullmsg
|
||||||
end)
|
end)
|
||||||
if not status then
|
if not status then
|
||||||
@ -573,6 +588,8 @@ function __process_post_runnables()
|
|||||||
end
|
end
|
||||||
|
|
||||||
network.__process_events()
|
network.__process_events()
|
||||||
|
block.__process_register_events()
|
||||||
|
block.__perform_ticks(time.delta())
|
||||||
end
|
end
|
||||||
|
|
||||||
function time.post_runnable(runnable)
|
function time.post_runnable(runnable)
|
||||||
|
|||||||
@ -668,3 +668,5 @@ end
|
|||||||
|
|
||||||
bit.compile = require "core:bitwise/compiler"
|
bit.compile = require "core:bitwise/compiler"
|
||||||
bit.execute = require "core:bitwise/executor"
|
bit.execute = require "core:bitwise/executor"
|
||||||
|
|
||||||
|
random.Random = require "core:internal/random_generator"
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// lighting
|
// lighting
|
||||||
#define SKY_LIGHT_MUL 2.9
|
#define SKY_LIGHT_MUL 2.9
|
||||||
#define SKY_LIGHT_TINT vec3(0.9, 0.8, 1.0)
|
#define SKY_LIGHT_TINT (vec3(1.0, 0.95, 0.9) * 2.0)
|
||||||
#define MIN_SKY_LIGHT vec3(0.2, 0.25, 0.33)
|
#define MIN_SKY_LIGHT vec3(0.2, 0.25, 0.33)
|
||||||
|
|
||||||
// fog
|
// fog
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
#include <constants>
|
#include <constants>
|
||||||
|
|
||||||
vec3 pick_sky_color(samplerCube cubemap) {
|
vec3 pick_sky_color(samplerCube cubemap) {
|
||||||
vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.0f, 0.4f)).rgb;
|
vec3 skyLightColor = texture(cubemap, vec3(0.8f, 0.01f, 0.4f)).rgb;
|
||||||
skyLightColor *= SKY_LIGHT_TINT;
|
skyLightColor *= SKY_LIGHT_TINT;
|
||||||
skyLightColor = min(vec3(1.0f), skyLightColor * SKY_LIGHT_MUL);
|
skyLightColor = min(vec3(1.0f), skyLightColor * SKY_LIGHT_MUL);
|
||||||
skyLightColor = max(MIN_SKY_LIGHT, skyLightColor);
|
skyLightColor = max(MIN_SKY_LIGHT, skyLightColor);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ Grant %{0} pack modification permission?=Выдаць дазвол на мады
|
|||||||
Error at line %{0}=Памылка ў радку %{0}
|
Error at line %{0}=Памылка ў радку %{0}
|
||||||
Run=Запусціць
|
Run=Запусціць
|
||||||
Filter=Фільтр
|
Filter=Фільтр
|
||||||
|
Are you sure you want to open the link: =Ці вы ўпэўненыя, што хочаце адкрыць спасылку:
|
||||||
|
|
||||||
editor.info.tooltip=CTRL+S - Захаваць\nCTRL+R - Запусціць\nCTRL+Z - Скасаваць\nCTRL+Y - Паўтарыць
|
editor.info.tooltip=CTRL+S - Захаваць\nCTRL+R - Запусціць\nCTRL+Z - Скасаваць\nCTRL+Y - Паўтарыць
|
||||||
devtools.traceback=Стэк выклікаў (ад апошняга)
|
devtools.traceback=Стэк выклікаў (ад апошняга)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ Back=Zurück
|
|||||||
Continue=Weitermachen
|
Continue=Weitermachen
|
||||||
Add=Hinzufügen
|
Add=Hinzufügen
|
||||||
Converting world...=Weltkonvertierung im Gange...
|
Converting world...=Weltkonvertierung im Gange...
|
||||||
|
Are you sure you want to open the link: =Sind Sie sicher, dass Sie den Link öffnen möchten:
|
||||||
|
|
||||||
error.pack-not-found=Paket konnte nicht gefunden werden
|
error.pack-not-found=Paket konnte nicht gefunden werden
|
||||||
error.dependency-not-found=Die verwendete Abhängigkeit wurde nicht gefunden
|
error.dependency-not-found=Die verwendete Abhängigkeit wurde nicht gefunden
|
||||||
|
|||||||
@ -7,6 +7,7 @@ world.convert-block-layouts=Blocks fields have changes! Convert world files?
|
|||||||
pack.remove-confirm=Do you want to erase all pack(s) content from the world forever?
|
pack.remove-confirm=Do you want to erase all pack(s) content from the world forever?
|
||||||
error.pack-not-found=Could not to find pack
|
error.pack-not-found=Could not to find pack
|
||||||
error.dependency-not-found=Dependency pack is not found
|
error.dependency-not-found=Dependency pack is not found
|
||||||
|
error.dependency-version-not-met=Dependency pack version is not met.
|
||||||
world.delete-confirm=Do you want to delete world forever?
|
world.delete-confirm=Do you want to delete world forever?
|
||||||
world.generators.default=Default
|
world.generators.default=Default
|
||||||
world.generators.flat=Flat
|
world.generators.flat=Flat
|
||||||
|
|||||||
@ -19,6 +19,7 @@ Problems=Ongelmia
|
|||||||
Monitor=Valvonta
|
Monitor=Valvonta
|
||||||
Debug=Virheenkorjaus
|
Debug=Virheenkorjaus
|
||||||
File=Tiedosto
|
File=Tiedosto
|
||||||
|
Are you sure you want to open the link: =Haluatko varmasti avata linkin:
|
||||||
|
|
||||||
devtools.traceback=Puhelupino (viimeisestä)
|
devtools.traceback=Puhelupino (viimeisestä)
|
||||||
error.pack-not-found=Pakettia ei löytynyt!
|
error.pack-not-found=Pakettia ei löytynyt!
|
||||||
|
|||||||
@ -7,6 +7,7 @@ Back=Powrót
|
|||||||
Continue=Kontynuacja
|
Continue=Kontynuacja
|
||||||
Add=Dodać
|
Add=Dodać
|
||||||
Converting world...=Konwersja świata w toku...
|
Converting world...=Konwersja świata w toku...
|
||||||
|
Are you sure you want to open the link: =Czy na pewno chcesz otworzyć link:
|
||||||
|
|
||||||
error.pack-not-found=Nie udało się znaleźć pakietu
|
error.pack-not-found=Nie udało się znaleźć pakietu
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ Grant %{0} pack modification permission?=Выдать разрешение на
|
|||||||
Error at line %{0}=Ошибка на строке %{0}
|
Error at line %{0}=Ошибка на строке %{0}
|
||||||
Run=Запустить
|
Run=Запустить
|
||||||
Filter=Фильтр
|
Filter=Фильтр
|
||||||
|
Are you sure you want to open the link: =Вы уверены, что хотите открыть ссылку:
|
||||||
|
|
||||||
editor.info.tooltip=CTRL+S - Сохранить\nCTRL+R - Запустить\nCTRL+Z - Отменить\nCTRL+Y - Повторить
|
editor.info.tooltip=CTRL+S - Сохранить\nCTRL+R - Запустить\nCTRL+Z - Отменить\nCTRL+Y - Повторить
|
||||||
devtools.traceback=Стек вызовов (от последнего)
|
devtools.traceback=Стек вызовов (от последнего)
|
||||||
@ -32,6 +33,7 @@ devtools.output=Вывод
|
|||||||
|
|
||||||
error.pack-not-found=Не удалось найти пакет
|
error.pack-not-found=Не удалось найти пакет
|
||||||
error.dependency-not-found=Используемая зависимость не найдена
|
error.dependency-not-found=Используемая зависимость не найдена
|
||||||
|
error.dependency-version-not-met=Версия зависимости не соответствует необходимой
|
||||||
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
|
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
|
||||||
|
|
||||||
# Подсказки
|
# Подсказки
|
||||||
|
|||||||
@ -23,6 +23,7 @@ devtools.traceback=Стек викликів (від останнього)
|
|||||||
error.pack-not-found=Не вдалося знайти пакет
|
error.pack-not-found=Не вдалося знайти пакет
|
||||||
error.dependency-not-found=Використовувана залежність не знайдена
|
error.dependency-not-found=Використовувана залежність не знайдена
|
||||||
pack.remove-confirm=Видалити весь контент, що постачається паком/паками зі світу (безповоротно)?
|
pack.remove-confirm=Видалити весь контент, що постачається паком/паками зі світу (безповоротно)?
|
||||||
|
Are you sure you want to open the link: =Ви впевнені, що хочете відкрити посилання:
|
||||||
|
|
||||||
# Меню
|
# Меню
|
||||||
menu.Apply=Застосувати
|
menu.Apply=Застосувати
|
||||||
|
|||||||
@ -24,6 +24,7 @@ Save=Saqlash
|
|||||||
Grant %{0} pack modification permission?=%{0} to‘plamini o‘zgartirish ruxsatini berilsinmi?
|
Grant %{0} pack modification permission?=%{0} to‘plamini o‘zgartirish ruxsatini berilsinmi?
|
||||||
Error at line %{0}=%{0}-qatorida xatolik
|
Error at line %{0}=%{0}-qatorida xatolik
|
||||||
Run=Ishga tushirish
|
Run=Ishga tushirish
|
||||||
|
Are you sure you want to open the link: =Haqiqatan ham havolani ochmoqchimisiz:
|
||||||
|
|
||||||
editor.info.tooltip=CTRL+S - Saqlash\nCTRL+R - Ishga tushirish\nCTRL+Z - Bekor qilish\nCTRL+Y - Qayta bajarish
|
editor.info.tooltip=CTRL+S - Saqlash\nCTRL+R - Ishga tushirish\nCTRL+Z - Bekor qilish\nCTRL+Y - Qayta bajarish
|
||||||
devtools.traceback=Chaqiruvlar steki (so`nggisidan boshlab)
|
devtools.traceback=Chaqiruvlar steki (so`nggisidan boshlab)
|
||||||
|
|||||||
@ -71,8 +71,8 @@ static auto process_program(const ResPaths& paths, const std::string& filename)
|
|||||||
|
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
|
||||||
auto vertex = preprocessor.process(vertexFile, vertexSource);
|
auto vertex = preprocessor.process(vertexFile, vertexSource, false, {});
|
||||||
auto fragment = preprocessor.process(fragmentFile, fragmentSource);
|
auto fragment = preprocessor.process(fragmentFile, fragmentSource, false, {});
|
||||||
return std::make_pair(vertex, fragment);
|
return std::make_pair(vertex, fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ assetload::postfunc assetload::posteffect(
|
|||||||
|
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
preprocessor.addHeader(
|
preprocessor.addHeader(
|
||||||
"__effect__", preprocessor.process(effectFile, effectSource, true)
|
"__effect__", preprocessor.process(effectFile, effectSource, true, {})
|
||||||
);
|
);
|
||||||
|
|
||||||
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
|
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
|
||||||
|
|||||||
@ -22,6 +22,10 @@ void GLSLExtension::setPaths(const ResPaths* paths) {
|
|||||||
this->paths = paths;
|
this->paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLSLExtension::setTraceOutput(bool enabled) {
|
||||||
|
this->traceOutput = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
void GLSLExtension::loadHeader(const std::string& name) {
|
void GLSLExtension::loadHeader(const std::string& name) {
|
||||||
if (paths == nullptr) {
|
if (paths == nullptr) {
|
||||||
return;
|
return;
|
||||||
@ -29,7 +33,7 @@ void GLSLExtension::loadHeader(const std::string& name) {
|
|||||||
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
||||||
std::string source = io::read_string(file);
|
std::string source = io::read_string(file);
|
||||||
addHeader(name, {});
|
addHeader(name, {});
|
||||||
addHeader(name, process(file, source, true));
|
addHeader(name, process(file, source, true, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
|
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
|
||||||
@ -123,13 +127,22 @@ static Value default_value_for(Type type) {
|
|||||||
|
|
||||||
class GLSLParser : public BasicParser<char> {
|
class GLSLParser : public BasicParser<char> {
|
||||||
public:
|
public:
|
||||||
GLSLParser(GLSLExtension& glsl, std::string_view file, std::string_view source, bool header)
|
GLSLParser(
|
||||||
|
GLSLExtension& glsl,
|
||||||
|
std::string_view file,
|
||||||
|
std::string_view source,
|
||||||
|
bool header,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
|
)
|
||||||
: BasicParser(file, source), glsl(glsl) {
|
: BasicParser(file, source), glsl(glsl) {
|
||||||
if (!header) {
|
if (!header) {
|
||||||
ss << "#version " << GLSLExtension::VERSION << '\n';
|
ss << "#version " << GLSLExtension::VERSION << '\n';
|
||||||
}
|
for (auto& entry : defines) {
|
||||||
for (auto& entry : glsl.getDefines()) {
|
ss << "#define " << entry << '\n';
|
||||||
ss << "#define " << entry.first << " " << entry.second << '\n';
|
}
|
||||||
|
for (auto& entry : defines) {
|
||||||
|
ss << "#define " << entry << '\n';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uint linenum = 1;
|
uint linenum = 1;
|
||||||
source_line(ss, linenum);
|
source_line(ss, linenum);
|
||||||
@ -289,10 +302,34 @@ private:
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void trace_output(
|
||||||
|
const io::path& file,
|
||||||
|
const std::string& source,
|
||||||
|
const GLSLExtension::ProcessingResult& result
|
||||||
|
) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "export:trace/" << file.name();
|
||||||
|
io::path outfile = ss.str();
|
||||||
|
try {
|
||||||
|
io::create_directories(outfile.parent());
|
||||||
|
io::write_string(outfile, result.code);
|
||||||
|
} catch (const std::runtime_error& err) {
|
||||||
|
logger.error() << "error on saving GLSLExtension::preprocess output ("
|
||||||
|
<< outfile.string() << "): " << err.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GLSLExtension::ProcessingResult GLSLExtension::process(
|
GLSLExtension::ProcessingResult GLSLExtension::process(
|
||||||
const io::path& file, const std::string& source, bool header
|
const io::path& file,
|
||||||
|
const std::string& source,
|
||||||
|
bool header,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
) {
|
) {
|
||||||
std::string filename = file.string();
|
std::string filename = file.string();
|
||||||
GLSLParser parser(*this, filename, source, header);
|
GLSLParser parser(*this, filename, source, header, defines);
|
||||||
return parser.process();
|
auto result = parser.process();
|
||||||
|
if (traceOutput) {
|
||||||
|
trace_output(file, source, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "io/io.hpp"
|
#include "io/io.hpp"
|
||||||
|
#include "data/setting.hpp"
|
||||||
#include "graphics/core/PostEffect.hpp"
|
#include "graphics/core/PostEffect.hpp"
|
||||||
|
|
||||||
class ResPaths;
|
class ResPaths;
|
||||||
@ -19,6 +20,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void setPaths(const ResPaths* paths);
|
void setPaths(const ResPaths* paths);
|
||||||
|
void setTraceOutput(bool enabled);
|
||||||
|
|
||||||
void define(const std::string& name, std::string value);
|
void define(const std::string& name, std::string value);
|
||||||
void undefine(const std::string& name);
|
void undefine(const std::string& name);
|
||||||
@ -37,7 +39,8 @@ public:
|
|||||||
ProcessingResult process(
|
ProcessingResult process(
|
||||||
const io::path& file,
|
const io::path& file,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
bool header = false
|
bool header,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
);
|
);
|
||||||
|
|
||||||
static inline std::string VERSION = "330 core";
|
static inline std::string VERSION = "330 core";
|
||||||
@ -46,4 +49,5 @@ private:
|
|||||||
std::unordered_map<std::string, std::string> defines;
|
std::unordered_map<std::string, std::string> defines;
|
||||||
|
|
||||||
const ResPaths* paths = nullptr;
|
const ResPaths* paths = nullptr;
|
||||||
|
bool traceOutput = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
inline constexpr int ENGINE_VERSION_MAJOR = 0;
|
inline constexpr int ENGINE_VERSION_MAJOR = 0;
|
||||||
inline constexpr int ENGINE_VERSION_MINOR = 29;
|
inline constexpr int ENGINE_VERSION_MINOR = 30;
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
inline constexpr bool ENGINE_DEBUG_BUILD = false;
|
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;
|
inline constexpr bool ENGINE_DEBUG_BUILD = true;
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
|
|
||||||
inline const std::string ENGINE_VERSION_STRING = "0.29";
|
inline const std::string ENGINE_VERSION_STRING = "0.30";
|
||||||
|
|
||||||
/// @brief world regions format version
|
/// @brief world regions format version
|
||||||
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
||||||
|
|||||||
@ -35,13 +35,15 @@ Content::Content(
|
|||||||
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
||||||
ResourceIndicesSet resourceIndices,
|
ResourceIndicesSet resourceIndices,
|
||||||
dv::value defaults
|
dv::value defaults,
|
||||||
|
std::unordered_map<std::string, int> tags
|
||||||
)
|
)
|
||||||
: indices(std::move(indices)),
|
: indices(std::move(indices)),
|
||||||
packs(std::move(packs)),
|
packs(std::move(packs)),
|
||||||
blockMaterials(std::move(blockMaterials)),
|
blockMaterials(std::move(blockMaterials)),
|
||||||
skeletons(std::move(skeletons)),
|
skeletons(std::move(skeletons)),
|
||||||
defaults(std::move(defaults)),
|
defaults(std::move(defaults)),
|
||||||
|
tags(std::move(tags)),
|
||||||
blocks(std::move(blocks)),
|
blocks(std::move(blocks)),
|
||||||
items(std::move(items)),
|
items(std::move(items)),
|
||||||
entities(std::move(entities)),
|
entities(std::move(entities)),
|
||||||
|
|||||||
@ -176,6 +176,7 @@ class Content {
|
|||||||
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
||||||
dv::value defaults = nullptr;
|
dv::value defaults = nullptr;
|
||||||
|
std::unordered_map<std::string, int> tags;
|
||||||
public:
|
public:
|
||||||
ContentUnitDefs<Block> blocks;
|
ContentUnitDefs<Block> blocks;
|
||||||
ContentUnitDefs<ItemDef> items;
|
ContentUnitDefs<ItemDef> items;
|
||||||
@ -195,7 +196,8 @@ public:
|
|||||||
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
||||||
ResourceIndicesSet resourceIndices,
|
ResourceIndicesSet resourceIndices,
|
||||||
dv::value defaults
|
dv::value defaults,
|
||||||
|
std::unordered_map<std::string, int> tags
|
||||||
);
|
);
|
||||||
~Content();
|
~Content();
|
||||||
|
|
||||||
@ -211,6 +213,14 @@ public:
|
|||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getTagIndex(const std::string& tag) const {
|
||||||
|
const auto& found = tags.find(tag);
|
||||||
|
if (found == tags.end()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
const rigging::SkeletonConfig* getSkeleton(const std::string& id) const;
|
const rigging::SkeletonConfig* getSkeleton(const std::string& id) const;
|
||||||
const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const;
|
const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const;
|
||||||
const BlockMaterial* findBlockMaterial(const std::string& id) const;
|
const BlockMaterial* findBlockMaterial(const std::string& id) const;
|
||||||
|
|||||||
@ -28,6 +28,9 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
|||||||
// Generating runtime info
|
// Generating runtime info
|
||||||
def.rt.id = blockDefsIndices.size();
|
def.rt.id = blockDefsIndices.size();
|
||||||
def.rt.emissive = *reinterpret_cast<uint32_t*>(def.emission);
|
def.rt.emissive = *reinterpret_cast<uint32_t*>(def.emission);
|
||||||
|
for (const auto& tag : def.tags) {
|
||||||
|
def.rt.tags.insert(tags.add(tag));
|
||||||
|
}
|
||||||
|
|
||||||
if (def.variants) {
|
if (def.variants) {
|
||||||
for (auto& variant : def.variants->variants) {
|
for (auto& variant : def.variants->variants) {
|
||||||
@ -58,7 +61,7 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockDefsIndices.push_back(&def);
|
blockDefsIndices.push_back(&def);
|
||||||
groups->insert(def.defaults.drawGroup); // FIXME
|
groups->insert(def.defaults.drawGroup); // FIXME: variants
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ItemDef*> itemDefsIndices;
|
std::vector<ItemDef*> itemDefsIndices;
|
||||||
@ -93,7 +96,8 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
|||||||
std::move(blockMaterials),
|
std::move(blockMaterials),
|
||||||
std::move(skeletons),
|
std::move(skeletons),
|
||||||
std::move(resourceIndices),
|
std::move(resourceIndices),
|
||||||
std::move(defaults)
|
std::move(defaults),
|
||||||
|
std::move(tags.map)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Now, it's time to resolve foreign keys
|
// Now, it's time to resolve foreign keys
|
||||||
|
|||||||
@ -62,6 +62,27 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TagsIndices {
|
||||||
|
int nextIndex = 1;
|
||||||
|
std::unordered_map<std::string, int> map;
|
||||||
|
|
||||||
|
int add(const std::string& tag) {
|
||||||
|
const auto& found = map.find(tag);
|
||||||
|
if (found != map.end()) {
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
return map[tag] = nextIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexOf(const std::string& tag) {
|
||||||
|
const auto& found = map.find(tag);
|
||||||
|
if (found == map.end()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class ContentBuilder {
|
class ContentBuilder {
|
||||||
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
||||||
@ -74,6 +95,7 @@ public:
|
|||||||
ContentUnitBuilder<GeneratorDef> generators {allNames, ContentType::GENERATOR};
|
ContentUnitBuilder<GeneratorDef> generators {allNames, ContentType::GENERATOR};
|
||||||
ResourceIndicesSet resourceIndices {};
|
ResourceIndicesSet resourceIndices {};
|
||||||
dv::value defaults = nullptr;
|
dv::value defaults = nullptr;
|
||||||
|
TagsIndices tags {};
|
||||||
|
|
||||||
~ContentBuilder();
|
~ContentBuilder();
|
||||||
|
|
||||||
|
|||||||
@ -288,6 +288,7 @@ void ContentLoader::loadContent(const dv::value& root) {
|
|||||||
item.iconType = ItemIconType::BLOCK;
|
item.iconType = ItemIconType::BLOCK;
|
||||||
item.icon = def.name;
|
item.icon = def.name;
|
||||||
item.placingBlock = def.name;
|
item.placingBlock = def.name;
|
||||||
|
item.tags = def.tags;
|
||||||
|
|
||||||
for (uint j = 0; j < 4; j++) {
|
for (uint j = 0; j < 4; j++) {
|
||||||
item.emission[j] = def.emission[j];
|
item.emission[j] = def.emission[j];
|
||||||
@ -412,6 +413,25 @@ void ContentLoader::load() {
|
|||||||
if (io::exists(contentFile)) {
|
if (io::exists(contentFile)) {
|
||||||
loadContent(io::read_json(contentFile));
|
loadContent(io::read_json(contentFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load attached tags
|
||||||
|
io::path tagsFile = folder / "tags.toml";
|
||||||
|
if (io::exists(tagsFile)) {
|
||||||
|
auto tagsMap = io::read_object(tagsFile);
|
||||||
|
for (const auto& [key, list] : tagsMap.asObject()) {
|
||||||
|
for (const auto& id : list) {
|
||||||
|
const auto& stringId = id.asString();
|
||||||
|
if (auto block = builder.blocks.get(stringId)) {
|
||||||
|
block->tags.push_back(key);
|
||||||
|
if (auto item = builder.items.get(stringId + BLOCK_ITEM_SUFFIX)) {
|
||||||
|
item->tags.push_back(key);
|
||||||
|
}
|
||||||
|
} else if (auto item = builder.items.get(stringId)) {
|
||||||
|
item->tags.push_back(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
|||||||
@ -118,21 +118,58 @@ ContentPack ContentPack::read(const io::path& folder) {
|
|||||||
const auto& dependencies = *found;
|
const auto& dependencies = *found;
|
||||||
for (const auto& elem : dependencies) {
|
for (const auto& elem : dependencies) {
|
||||||
std::string depName = elem.asString();
|
std::string depName = elem.asString();
|
||||||
auto level = DependencyLevel::required;
|
auto level = DependencyLevel::REQUIRED;
|
||||||
switch (depName.at(0)) {
|
switch (depName.at(0)) {
|
||||||
case '!':
|
case '!':
|
||||||
depName = depName.substr(1);
|
depName = depName.substr(1);
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
depName = depName.substr(1);
|
depName = depName.substr(1);
|
||||||
level = DependencyLevel::optional;
|
level = DependencyLevel::OPTIONAL;
|
||||||
break;
|
break;
|
||||||
case '~':
|
case '~':
|
||||||
depName = depName.substr(1);
|
depName = depName.substr(1);
|
||||||
level = DependencyLevel::weak;
|
level = DependencyLevel::WEAK;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pack.dependencies.push_back({level, depName});
|
|
||||||
|
std::string depVer = "*";
|
||||||
|
std::string depVerOperator = "=";
|
||||||
|
|
||||||
|
size_t versionPos = depName.rfind("@");
|
||||||
|
if (versionPos != std::string::npos) {
|
||||||
|
depVer = depName.substr(versionPos + 1);
|
||||||
|
depName = depName.substr(0, versionPos);
|
||||||
|
|
||||||
|
if (depVer.size() >= 2) {
|
||||||
|
std::string op = depVer.substr(0, 2);
|
||||||
|
std::uint8_t op_size = 0;
|
||||||
|
|
||||||
|
// Two symbol operators
|
||||||
|
if (op == ">=" || op == "=>" || op == "<=" || op == "=<") {
|
||||||
|
op_size = 2;
|
||||||
|
depVerOperator = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One symbol operators
|
||||||
|
else {
|
||||||
|
op = depVer.substr(0, 1);
|
||||||
|
|
||||||
|
if (op == ">" || op == "<") {
|
||||||
|
op_size = 1;
|
||||||
|
depVerOperator = op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
depVer = depVer.substr(op_size);
|
||||||
|
} else {
|
||||||
|
if (depVer == ">" || depVer == "<"){
|
||||||
|
depVer = "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.dependencies.push_back({level, depName, depVer, depVerOperator});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,21 +20,28 @@ public:
|
|||||||
io::path folder,
|
io::path folder,
|
||||||
const std::string& message
|
const std::string& message
|
||||||
);
|
);
|
||||||
|
|
||||||
std::string getPackId() const;
|
std::string getPackId() const;
|
||||||
io::path getFolder() const;
|
io::path getFolder() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DependencyVersionOperator {
|
||||||
|
EQUAL, GREATHER, LESS,
|
||||||
|
GREATHER_OR_EQUAL, LESS_OR_EQUAL
|
||||||
|
};
|
||||||
|
|
||||||
enum class DependencyLevel {
|
enum class DependencyLevel {
|
||||||
required, // dependency must be installed
|
REQUIRED, // dependency must be installed
|
||||||
optional, // dependency will be installed if found
|
OPTIONAL, // dependency will be installed if found
|
||||||
weak, // only affects packs order
|
WEAK, // only affects packs order
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Content-pack that should be installed earlier the dependent
|
/// @brief Content-pack that should be installed earlier the dependent
|
||||||
struct DependencyPack {
|
struct DependencyPack {
|
||||||
DependencyLevel level;
|
DependencyLevel level;
|
||||||
std::string id;
|
std::string id;
|
||||||
|
std::string version;
|
||||||
|
std::string op;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ContentPackStats {
|
struct ContentPackStats {
|
||||||
|
|||||||
67
src/content/ContentPackVersion.cpp
Normal file
67
src/content/ContentPackVersion.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "ContentPackVersion.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "coders/commons.hpp"
|
||||||
|
|
||||||
|
Version::Version(const std::string& version) {
|
||||||
|
major = 0;
|
||||||
|
minor = 0;
|
||||||
|
patch = 0;
|
||||||
|
|
||||||
|
std::vector<int> parts;
|
||||||
|
|
||||||
|
std::stringstream ss(version);
|
||||||
|
std::string part;
|
||||||
|
while (std::getline(ss, part, '.')) {
|
||||||
|
if (!part.empty()) {
|
||||||
|
parts.push_back(std::stoi(part));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.size() > 0) major = parts[0];
|
||||||
|
if (parts.size() > 1) minor = parts[1];
|
||||||
|
if (parts.size() > 2) patch = parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyVersionOperator Version::string_to_operator(const std::string& op) {
|
||||||
|
if (op == "=")
|
||||||
|
return DependencyVersionOperator::EQUAL;
|
||||||
|
else if (op == ">")
|
||||||
|
return DependencyVersionOperator::GREATHER;
|
||||||
|
else if (op == "<")
|
||||||
|
return DependencyVersionOperator::LESS;
|
||||||
|
else if (op == ">=" || op == "=>")
|
||||||
|
return DependencyVersionOperator::GREATHER_OR_EQUAL;
|
||||||
|
else if (op == "<=" || op == "=<")
|
||||||
|
return DependencyVersionOperator::LESS_OR_EQUAL;
|
||||||
|
else
|
||||||
|
return DependencyVersionOperator::EQUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNumber(const std::string& s) {
|
||||||
|
return !s.empty() && std::all_of(s.begin(), s.end(), ::is_digit);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Version::matches_pattern(const std::string& version) {
|
||||||
|
for (char c : version) {
|
||||||
|
if (!isdigit(c) && c != '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss(version);
|
||||||
|
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
std::string part;
|
||||||
|
while (std::getline(ss, part, '.')) {
|
||||||
|
if (part.empty()) return false;
|
||||||
|
if (!isNumber(part)) return false;
|
||||||
|
|
||||||
|
parts.push_back(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.size() == 2 || parts.size() == 3;
|
||||||
|
}
|
||||||
57
src/content/ContentPackVersion.hpp
Normal file
57
src/content/ContentPackVersion.hpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "content/ContentPack.hpp"
|
||||||
|
|
||||||
|
class Version {
|
||||||
|
public:
|
||||||
|
int major;
|
||||||
|
int minor;
|
||||||
|
int patch;
|
||||||
|
|
||||||
|
Version(const std::string& version);
|
||||||
|
|
||||||
|
bool operator==(const Version& other) const {
|
||||||
|
return major == other.major && minor == other.minor &&
|
||||||
|
patch == other.patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Version& other) const {
|
||||||
|
if (major != other.major) return major < other.major;
|
||||||
|
if (minor != other.minor) return minor < other.minor;
|
||||||
|
return patch < other.patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const Version& other) const {
|
||||||
|
return other < *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const Version& other) const {
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const Version& other) const {
|
||||||
|
return !(*this > other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_operator(const std::string& op, const Version& other) const {
|
||||||
|
auto dep_op = Version::string_to_operator(op);
|
||||||
|
|
||||||
|
switch (dep_op) {
|
||||||
|
case DependencyVersionOperator::EQUAL:
|
||||||
|
return *this == other;
|
||||||
|
case DependencyVersionOperator::GREATHER:
|
||||||
|
return *this > other;
|
||||||
|
case DependencyVersionOperator::LESS:
|
||||||
|
return *this < other;
|
||||||
|
case DependencyVersionOperator::LESS_OR_EQUAL:
|
||||||
|
return *this <= other;
|
||||||
|
case DependencyVersionOperator::GREATHER_OR_EQUAL:
|
||||||
|
return *this >= other;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DependencyVersionOperator string_to_operator(const std::string& op);
|
||||||
|
static bool matches_pattern(const std::string& version);
|
||||||
|
};
|
||||||
@ -3,6 +3,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "ContentPackVersion.hpp"
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
|
|
||||||
PacksManager::PacksManager() = default;
|
PacksManager::PacksManager() = default;
|
||||||
@ -90,7 +91,7 @@ static bool resolve_dependencies(
|
|||||||
}
|
}
|
||||||
auto found = packs.find(dep.id);
|
auto found = packs.find(dep.id);
|
||||||
bool exists = found != packs.end();
|
bool exists = found != packs.end();
|
||||||
if (!exists && dep.level == DependencyLevel::required) {
|
if (!exists && dep.level == DependencyLevel::REQUIRED) {
|
||||||
throw contentpack_error(
|
throw contentpack_error(
|
||||||
dep.id, io::path(), "dependency of '" + pack->id + "'"
|
dep.id, io::path(), "dependency of '" + pack->id + "'"
|
||||||
);
|
);
|
||||||
@ -99,15 +100,32 @@ static bool resolve_dependencies(
|
|||||||
// ignored for optional or weak dependencies
|
// ignored for optional or weak dependencies
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (resolveWeaks && dep.level == DependencyLevel::weak) {
|
if (resolveWeaks && dep.level == DependencyLevel::WEAK) {
|
||||||
// dependency pack is found but not added yet
|
// dependency pack is found but not added yet
|
||||||
// resolveWeaks is used on second iteration, so it's will not be
|
// resolveWeaks is used on second iteration, so it's will not be
|
||||||
// added
|
// added
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto dep_pack = found -> second;
|
||||||
|
|
||||||
|
if (Version::matches_pattern(dep.version) && Version::matches_pattern(dep_pack.version)
|
||||||
|
&& Version(dep_pack.version)
|
||||||
|
.process_operator(dep.op, Version(dep.version))
|
||||||
|
) {
|
||||||
|
// dependency pack version meets the required one
|
||||||
|
continue;
|
||||||
|
} else if (dep.version == "*" || dep.version == dep_pack.version){
|
||||||
|
// fallback: dependency pack version also meets required one
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw contentpack_error(
|
||||||
|
dep.id, io::path(), "does not meet required version '" + dep.op + dep.version +"' of '" + pack->id + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!util::contains(allNames, dep.id) &&
|
if (!util::contains(allNames, dep.id) &&
|
||||||
dep.level != DependencyLevel::weak) {
|
dep.level != DependencyLevel::WEAK) {
|
||||||
allNames.push_back(dep.id);
|
allNames.push_back(dep.id);
|
||||||
queue.push(&found->second);
|
queue.push(&found->second);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#define VC_ENABLE_REFLECTION
|
#define VC_ENABLE_REFLECTION
|
||||||
#include "ContentUnitLoader.hpp"
|
#include "ContentUnitLoader.hpp"
|
||||||
|
#include "ContentLoadingCommons.hpp"
|
||||||
|
|
||||||
#include "../ContentBuilder.hpp"
|
#include "../ContentBuilder.hpp"
|
||||||
#include "coders/json.hpp"
|
#include "coders/json.hpp"
|
||||||
@ -87,20 +88,8 @@ template<> void ContentUnitLoader<Block>::loadUnit(
|
|||||||
Block& def, const std::string& name, const io::path& file
|
Block& def, const std::string& name, const io::path& file
|
||||||
) {
|
) {
|
||||||
auto root = io::read_json(file);
|
auto root = io::read_json(file);
|
||||||
if (def.properties == nullptr) {
|
process_properties(def, name, root);
|
||||||
def.properties = dv::object();
|
process_tags(def, root);
|
||||||
def.properties["name"] = name;
|
|
||||||
}
|
|
||||||
for (auto& [key, value] : root.asObject()) {
|
|
||||||
auto pos = key.rfind('@');
|
|
||||||
if (pos == std::string::npos) {
|
|
||||||
def.properties[key] = value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto field = key.substr(0, pos);
|
|
||||||
auto suffix = key.substr(pos + 1);
|
|
||||||
process_method(def.properties, suffix, field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.has("parent")) {
|
if (root.has("parent")) {
|
||||||
const auto& parentName = root["parent"].asString();
|
const auto& parentName = root["parent"].asString();
|
||||||
|
|||||||
37
src/content/loading/ContentLoadingCommons.hpp
Normal file
37
src/content/loading/ContentLoadingCommons.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "data/dv.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void process_properties(T& def, const std::string& name, const dv::value& root) {
|
||||||
|
if (def.properties == nullptr) {
|
||||||
|
def.properties = dv::object();
|
||||||
|
def.properties["name"] = name;
|
||||||
|
}
|
||||||
|
for (auto& [key, value] : root.asObject()) {
|
||||||
|
auto pos = key.rfind('@');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
def.properties[key] = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto field = key.substr(0, pos);
|
||||||
|
auto suffix = key.substr(pos + 1);
|
||||||
|
process_method(def.properties, suffix, field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void process_tags(T& def, const dv::value& root) {
|
||||||
|
if (!root.has("tags")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& tags = root["tags"];
|
||||||
|
for (const auto& tagValue : tags) {
|
||||||
|
if (!tagValue.isString()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
def.tags.push_back(tagValue.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,7 +30,18 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
|
|||||||
|
|
||||||
if (auto found = root.at("components")) {
|
if (auto found = root.at("components")) {
|
||||||
for (const auto& elem : *found) {
|
for (const auto& elem : *found) {
|
||||||
def.components.emplace_back(elem.asString());
|
std::string name;
|
||||||
|
dv::value params;
|
||||||
|
if (elem.isObject()) {
|
||||||
|
name = elem["name"].asString();
|
||||||
|
if (elem.has("args")) {
|
||||||
|
params = elem["args"];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = elem.asString();
|
||||||
|
}
|
||||||
|
def.components.push_back(ComponentInstance {
|
||||||
|
std::move(name), std::move(params)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto found = root.at("hitbox")) {
|
if (auto found = root.at("hitbox")) {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#define VC_ENABLE_REFLECTION
|
#define VC_ENABLE_REFLECTION
|
||||||
#include "ContentUnitLoader.hpp"
|
#include "ContentUnitLoader.hpp"
|
||||||
|
#include "ContentLoadingCommons.hpp"
|
||||||
|
|
||||||
#include "../ContentBuilder.hpp"
|
#include "../ContentBuilder.hpp"
|
||||||
#include "coders/json.hpp"
|
#include "coders/json.hpp"
|
||||||
@ -12,11 +13,13 @@
|
|||||||
|
|
||||||
static debug::Logger logger("item-content-loader");
|
static debug::Logger logger("item-content-loader");
|
||||||
|
|
||||||
|
|
||||||
template<> void ContentUnitLoader<ItemDef>::loadUnit(
|
template<> void ContentUnitLoader<ItemDef>::loadUnit(
|
||||||
ItemDef& def, const std::string& name, const io::path& file
|
ItemDef& def, const std::string& name, const io::path& file
|
||||||
) {
|
) {
|
||||||
auto root = io::read_json(file);
|
auto root = io::read_json(file);
|
||||||
def.properties = root;
|
process_properties(def, name, root);
|
||||||
|
process_tags(def, root);
|
||||||
|
|
||||||
if (root.has("parent")) {
|
if (root.has("parent")) {
|
||||||
const auto& parentName = root["parent"].asString();
|
const auto& parentName = root["parent"].asString();
|
||||||
|
|||||||
@ -46,12 +46,12 @@ namespace dv {
|
|||||||
if (!map.has(key)) {
|
if (!map.has(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& list = map[key];
|
const auto& srcList = map[key];
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
if constexpr (std::is_floating_point<T>()) {
|
if constexpr (std::is_floating_point<T>()) {
|
||||||
vec[i] = list[i].asNumber();
|
vec[i] = srcList[i].asNumber();
|
||||||
} else {
|
} else {
|
||||||
vec[i] = list[i].asInteger();
|
vec[i] = srcList[i].asInteger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,6 +143,12 @@ void Engine::initializeClient() {
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
|
keepAlive(settings.debug.doTraceShaders.observe(
|
||||||
|
[](bool value) {
|
||||||
|
Shader::preprocessor->setTraceOutput(value);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::initialize(CoreParameters coreParameters) {
|
void Engine::initialize(CoreParameters coreParameters) {
|
||||||
|
|||||||
@ -12,12 +12,14 @@
|
|||||||
#include "graphics/render/WorldRenderer.hpp"
|
#include "graphics/render/WorldRenderer.hpp"
|
||||||
#include "graphics/render/ParticlesRenderer.hpp"
|
#include "graphics/render/ParticlesRenderer.hpp"
|
||||||
#include "graphics/render/ChunksRenderer.hpp"
|
#include "graphics/render/ChunksRenderer.hpp"
|
||||||
|
#include "graphics/render/DebugLinesRenderer.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
#include "network/Network.hpp"
|
#include "network/Network.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/EntityDef.hpp"
|
#include "objects/EntityDef.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
@ -44,6 +46,7 @@ static std::shared_ptr<Label> create_label(GUI& gui, wstringsupplier supplier) {
|
|||||||
// TODO: move to xml
|
// TODO: move to xml
|
||||||
// TODO: move to xml finally
|
// TODO: move to xml finally
|
||||||
// TODO: move to xml finally
|
// TODO: move to xml finally
|
||||||
|
// TODO: move to xml finally
|
||||||
std::shared_ptr<UINode> create_debug_panel(
|
std::shared_ptr<UINode> create_debug_panel(
|
||||||
Engine& engine,
|
Engine& engine,
|
||||||
Level& level,
|
Level& level,
|
||||||
@ -260,6 +263,18 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
});
|
});
|
||||||
panel->add(checkbox);
|
panel->add(checkbox);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto checkbox = std::make_shared<FullCheckBox>(
|
||||||
|
gui, L"Show Paths", glm::vec2(400, 24)
|
||||||
|
);
|
||||||
|
checkbox->setSupplier([=]() {
|
||||||
|
return DebugLinesRenderer::showPaths;
|
||||||
|
});
|
||||||
|
checkbox->setConsumer([=](bool checked) {
|
||||||
|
DebugLinesRenderer::showPaths = checked;
|
||||||
|
});
|
||||||
|
panel->add(checkbox);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
auto checkbox = std::make_shared<FullCheckBox>(
|
auto checkbox = std::make_shared<FullCheckBox>(
|
||||||
gui, L"Show Generator Minimap", glm::vec2(400, 24)
|
gui, L"Show Generator Minimap", glm::vec2(400, 24)
|
||||||
|
|||||||
@ -207,6 +207,9 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Hud::~Hud() {
|
Hud::~Hud() {
|
||||||
|
if (input.isCursorLocked()) {
|
||||||
|
input.toggleCursor();
|
||||||
|
}
|
||||||
// removing all controlled ui
|
// removing all controlled ui
|
||||||
for (auto& element : elements) {
|
for (auto& element : elements) {
|
||||||
onRemove(element);
|
onRemove(element);
|
||||||
@ -339,7 +342,7 @@ void Hud::update(bool visible) {
|
|||||||
if (!gui.isFocusCaught()) {
|
if (!gui.isFocusCaught()) {
|
||||||
processInput(visible);
|
processInput(visible);
|
||||||
}
|
}
|
||||||
if ((isMenuOpen || inventoryOpen) == input.getCursor().locked) {
|
if ((isMenuOpen || inventoryOpen) == input.isCursorLocked()) {
|
||||||
input.toggleCursor();
|
input.toggleCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -176,7 +176,7 @@ void LevelScreen::saveWorldPreview() {
|
|||||||
static_cast<uint>(previewSize)}
|
static_cast<uint>(previewSize)}
|
||||||
);
|
);
|
||||||
|
|
||||||
renderer->draw(ctx, camera, false, true, 0.0f, *postProcessing);
|
renderer->renderFrame(ctx, camera, false, true, 0.0f, *postProcessing);
|
||||||
auto image = postProcessing->toImage();
|
auto image = postProcessing->toImage();
|
||||||
image->flipY();
|
image->flipY();
|
||||||
imageio::write("world:preview.png", image.get());
|
imageio::write("world:preview.png", image.get());
|
||||||
@ -263,7 +263,7 @@ void LevelScreen::draw(float delta) {
|
|||||||
if (!hud->isPause()) {
|
if (!hud->isPause()) {
|
||||||
scripting::on_entities_render(engine.getTime().getDelta());
|
scripting::on_entities_render(engine.getTime().getDelta());
|
||||||
}
|
}
|
||||||
renderer->draw(
|
renderer->renderFrame(
|
||||||
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
|
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "ImageData.hpp"
|
#include "ImageData.hpp"
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -187,6 +188,41 @@ void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<uint channels>
|
||||||
|
static void draw_rect(ImageData& image, int dstX, int dstY, int width, int height, const glm::ivec4& color) {
|
||||||
|
ubyte* data = image.getData();
|
||||||
|
int imageWidth = image.getWidth();
|
||||||
|
int imageHeight = image.getHeight();
|
||||||
|
|
||||||
|
int x1 = glm::min(glm::max(dstX, 0), imageWidth - 1);
|
||||||
|
int y1 = glm::min(glm::max(dstY, 0), imageHeight - 1);
|
||||||
|
|
||||||
|
int x2 = glm::min(glm::max(dstX + width, 0), imageWidth - 1);
|
||||||
|
int y2 = glm::min(glm::max(dstY + height, 0), imageHeight - 1);
|
||||||
|
|
||||||
|
for (int y = y1; y <= y2; y++) {
|
||||||
|
for (int x = x1; x <= x2; x++) {
|
||||||
|
int index = (y * imageWidth + x) * channels;
|
||||||
|
for (int i = 0; i < channels; i++) {
|
||||||
|
data[index + i] = color[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageData::drawRect(int x, int y, int width, int height, const glm::ivec4& color) {
|
||||||
|
switch (format) {
|
||||||
|
case ImageFormat::rgb888:
|
||||||
|
draw_rect<3>(*this, x, y, width, height, color);
|
||||||
|
break;
|
||||||
|
case ImageFormat::rgba8888:
|
||||||
|
draw_rect<4>(*this, x, y, width, height, color);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
|
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
|
||||||
ubyte* source = image.getData();
|
ubyte* source = image.getData();
|
||||||
uint srcwidth = image.getWidth();
|
uint srcwidth = image.getWidth();
|
||||||
|
|||||||
@ -28,6 +28,7 @@ public:
|
|||||||
void flipY();
|
void flipY();
|
||||||
|
|
||||||
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
|
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
|
||||||
|
void drawRect(int x, int y, int width, int height, const glm::ivec4& color);
|
||||||
void blit(const ImageData& image, int x, int y);
|
void blit(const ImageData& image, int x, int y);
|
||||||
void extrude(int x, int y, int w, int h);
|
void extrude(int x, int y, int w, int h);
|
||||||
void fixAlphaColor();
|
void fixAlphaColor();
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
@ -138,15 +137,21 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GLuint compile_program(
|
static GLuint compile_program(
|
||||||
const Shader::Source& vertexSource, const Shader::Source& fragmentSource
|
const Shader::Source& vertexSource,
|
||||||
|
const Shader::Source& fragmentSource,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
) {
|
) {
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
|
||||||
auto vertexCode = std::move(
|
auto vertexCode = std::move(
|
||||||
preprocessor.process(vertexSource.file, vertexSource.code).code
|
preprocessor
|
||||||
|
.process(vertexSource.file, vertexSource.code, false, defines)
|
||||||
|
.code
|
||||||
);
|
);
|
||||||
auto fragmentCode = std::move(
|
auto fragmentCode = std::move(
|
||||||
preprocessor.process(fragmentSource.file, fragmentSource.code).code
|
preprocessor
|
||||||
|
.process(fragmentSource.file, fragmentSource.code, false, defines)
|
||||||
|
.code
|
||||||
);
|
);
|
||||||
|
|
||||||
const GLchar* vCode = vertexCode.c_str();
|
const GLchar* vCode = vertexCode.c_str();
|
||||||
@ -176,8 +181,8 @@ static GLuint compile_program(
|
|||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::recompile() {
|
void Shader::recompile(const std::vector<std::string>& defines) {
|
||||||
GLuint newProgram = compile_program(vertexSource, fragmentSource);
|
GLuint newProgram = compile_program(vertexSource, fragmentSource, defines);
|
||||||
glDeleteProgram(id);
|
glDeleteProgram(id);
|
||||||
id = newProgram;
|
id = newProgram;
|
||||||
uniformLocations.clear();
|
uniformLocations.clear();
|
||||||
@ -188,7 +193,7 @@ std::unique_ptr<Shader> Shader::create(
|
|||||||
Source&& vertexSource, Source&& fragmentSource
|
Source&& vertexSource, Source&& fragmentSource
|
||||||
) {
|
) {
|
||||||
return std::make_unique<Shader>(
|
return std::make_unique<Shader>(
|
||||||
compile_program(vertexSource, fragmentSource),
|
compile_program(vertexSource, fragmentSource, {}),
|
||||||
std::move(vertexSource),
|
std::move(vertexSource),
|
||||||
std::move(fragmentSource)
|
std::move(fragmentSource)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ public:
|
|||||||
void uniform4v(const std::string& name, int length, const float* v);
|
void uniform4v(const std::string& name, int length, const float* v);
|
||||||
|
|
||||||
/// @brief Re-preprocess source code and re-compile shader program
|
/// @brief Re-preprocess source code and re-compile shader program
|
||||||
void recompile();
|
void recompile(const std::vector<std::string>& defines);
|
||||||
|
|
||||||
/// @brief Create shader program using vertex and fragment shaders source.
|
/// @brief Create shader program using vertex and fragment shaders source.
|
||||||
/// @return linked shader program containing vertex and fragment shaders
|
/// @return linked shader program containing vertex and fragment shaders
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
#include "ShadowMap.hpp"
|
|
||||||
|
|
||||||
#include <GL/glew.h>
|
|
||||||
|
|
||||||
ShadowMap::ShadowMap(int resolution) : resolution(resolution) {
|
|
||||||
glGenTextures(1, &depthMap);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, depthMap);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
|
||||||
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
||||||
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
|
|
||||||
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR, border);
|
|
||||||
|
|
||||||
glGenFramebuffers(1, &fbo);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
|
|
||||||
glDrawBuffer(GL_NONE);
|
|
||||||
glReadBuffer(GL_NONE);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowMap::~ShadowMap() {
|
|
||||||
glDeleteFramebuffers(1, &fbo);
|
|
||||||
glDeleteTextures(1, &depthMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowMap::bind() {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowMap::unbind() {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint ShadowMap::getDepthMap() const {
|
|
||||||
return depthMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ShadowMap::getResolution() const {
|
|
||||||
return resolution;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user