Merge branch 'main' into release-0.26
This commit is contained in:
commit
8c884846f1
@ -77,12 +77,14 @@ Checks if content is loaded.
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
app.new_world(
|
app.new_world(
|
||||||
-- world name
|
-- world name, empty string will create a nameless world
|
||||||
name: str,
|
name: str,
|
||||||
-- generation seed
|
-- generation seed
|
||||||
seed: str,
|
seed: str,
|
||||||
-- generator name
|
-- generator name
|
||||||
generator: str
|
generator: str
|
||||||
|
-- local player id
|
||||||
|
[optional] local_player: int=0
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -62,4 +62,7 @@ hud.is_paused() -> bool
|
|||||||
|
|
||||||
-- Returns true if inventory is open or overlay is shown.
|
-- Returns true if inventory is open or overlay is shown.
|
||||||
hud.is_inventory_open() -> bool
|
hud.is_inventory_open() -> bool
|
||||||
|
|
||||||
|
-- Sets whether to allow pausing. If false, the pause menu will not pause the game.
|
||||||
|
hud.set_allow_pause(flag: bool)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -37,10 +37,10 @@ player.set_vel(playerid: int, x: number, y: number, z: number)
|
|||||||
Sets x, y, z player linear velocity
|
Sets x, y, z player linear velocity
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.get_rot(playerid: int) -> number, number, number
|
player.get_rot(playerid: int, interpolated: bool) -> number, number, number
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns x, y, z of camera rotation (radians)
|
Returns x, y, z of camera rotation (radians). Interpolation is relevant in cases where the rotation refresh rate is lower than the frame rate.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.set_rot(playerid: int, x: number, y: number, z: number)
|
player.set_rot(playerid: int, x: number, y: number, z: number)
|
||||||
|
|||||||
@ -56,6 +56,7 @@ Common element methods:
|
|||||||
| ------------------- | ----------------------------------------------------------------------------------- |
|
| ------------------- | ----------------------------------------------------------------------------------- |
|
||||||
| moveInto(container) | moves the element to the specified container (the element is specified, not the id) |
|
| moveInto(container) | moves the element to the specified container (the element is specified, not the id) |
|
||||||
| destruct() | removes element |
|
| destruct() | removes element |
|
||||||
|
| reposition() | updates the element position based on the `positionfunc` |
|
||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|
||||||
|
|||||||
@ -19,3 +19,34 @@ Styles can be combined. Example:
|
|||||||
Output:
|
Output:
|
||||||
|
|
||||||
***<ins>Message</ins>*** using *~~combed~~ combined* styles<ins>~~.~~</ins>
|
***<ins>Message</ins>*** using *~~combed~~ combined* styles<ins>~~.~~</ins>
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
|
||||||
|
Text color can be set using a color code: [#RRGGBB]
|
||||||
|
|
||||||
|
|
||||||
|
| Component | Purpose |
|
||||||
|
| --------- | --------------------------------- |
|
||||||
|
| R | Represents the intensity of red |
|
||||||
|
| G | Represents the intensity of green |
|
||||||
|
| B | Represents the intensity of blue |
|
||||||
|
|
||||||
|
### Example:
|
||||||
|
|
||||||
|
<span style="color: #ff0000">
|
||||||
|
<span style="color:rgb(105, 105, 105)">
|
||||||
|
[#ff0000]
|
||||||
|
</span>Red Text
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span style="color: #00ff00">
|
||||||
|
<span style="color:rgb(105, 105, 105)">
|
||||||
|
[#00ff00]
|
||||||
|
</span>Green Text
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span style="color: #0000ff">
|
||||||
|
<span style="color:rgb(105, 105, 105)">
|
||||||
|
[#0000ff]
|
||||||
|
</span>Blue Text
|
||||||
|
</span>
|
||||||
|
|||||||
@ -35,6 +35,7 @@ Examples:
|
|||||||
- `margin` - element margin. Type: 4D vector
|
- `margin` - element margin. Type: 4D vector
|
||||||
*left, top, right, bottom*
|
*left, top, right, bottom*
|
||||||
- `visible` - element visibility. Type: boolean (true/false)
|
- `visible` - element visibility. Type: boolean (true/false)
|
||||||
|
- `min-size` - minimal element size. Type: 2D vector.
|
||||||
- `position-func` - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open*
|
- `position-func` - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open*
|
||||||
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
|
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
|
||||||
- `onclick` - lua function called when an element is clicked.
|
- `onclick` - lua function called when an element is clicked.
|
||||||
@ -65,6 +66,7 @@ Buttons and panels are also containers.
|
|||||||
Buttons are also panels.
|
Buttons are also panels.
|
||||||
|
|
||||||
- `max-length` - maximal length of panel stretching before scrolling (if scrollable = true). Type: number
|
- `max-length` - maximal length of panel stretching before scrolling (if scrollable = true). Type: number
|
||||||
|
- `min-length` - minimal length of panel. Type: number
|
||||||
- `orientation` - panel orientation: horizontal/vertical.
|
- `orientation` - panel orientation: horizontal/vertical.
|
||||||
|
|
||||||
# Common elements
|
# Common elements
|
||||||
|
|||||||
@ -77,12 +77,14 @@ app.is_content_loaded() -> bool
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
app.new_world(
|
app.new_world(
|
||||||
-- название мира
|
-- название мира, пустая строка приведёт к созданию безымянного мира
|
||||||
name: str,
|
name: str,
|
||||||
-- зерно генерации
|
-- зерно генерации
|
||||||
seed: str,
|
seed: str,
|
||||||
-- название генератора
|
-- название генератора
|
||||||
generator: str
|
generator: str
|
||||||
|
-- id локального игрока
|
||||||
|
[опционально] local_player: int=0
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -65,4 +65,7 @@ hud.is_paused() -> bool
|
|||||||
|
|
||||||
-- Возвращает true если открыт инвентарь или оверлей.
|
-- Возвращает true если открыт инвентарь или оверлей.
|
||||||
hud.is_inventory_open() -> bool
|
hud.is_inventory_open() -> bool
|
||||||
|
|
||||||
|
-- Устанавливает разрешение на паузу. При значении false меню паузы не приостанавливает игру.
|
||||||
|
hud.set_allow_pause(flag: bool)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -37,10 +37,10 @@ player.set_vel(playerid: int, x: number, y: number, z: number)
|
|||||||
Устанавливает x, y, z линейной скорости игрока
|
Устанавливает x, y, z линейной скорости игрока
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.get_rot(playerid: int) -> number, number, number
|
player.get_rot(playerid: int, interpolated: bool=false) -> number, number, number
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает x, y, z вращения камеры (в радианах)
|
Возвращает x, y, z вращения камеры (в радианах). Интерполяция актуальна в случаях, когда частота обновления вращения ниже частоты кадров.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.set_rot(playerid: int, x: number, y: number, z: number)
|
player.set_rot(playerid: int, x: number, y: number, z: number)
|
||||||
|
|||||||
@ -56,6 +56,7 @@ document["worlds-panel"]:clear()
|
|||||||
| ------------------- | ----------------------------------------------------------------------- |
|
| ------------------- | ----------------------------------------------------------------------- |
|
||||||
| moveInto(container) | перемещает элемент в указанный контейнер (указывается элемент, а не id) |
|
| moveInto(container) | перемещает элемент в указанный контейнер (указывается элемент, а не id) |
|
||||||
| destruct() | удаляет элемент |
|
| destruct() | удаляет элемент |
|
||||||
|
| reposition() | обновляет позицию элемента на основе функции позиционирования |
|
||||||
|
|
||||||
## Контейнеры
|
## Контейнеры
|
||||||
|
|
||||||
|
|||||||
@ -19,3 +19,33 @@
|
|||||||
Вывод:
|
Вывод:
|
||||||
|
|
||||||
***<ins>Сообщение</ins>***, демонстрирующее *~~обедненные~~ объединенные* стили<ins>~~.~~</ins>
|
***<ins>Сообщение</ins>***, демонстрирующее *~~обедненные~~ объединенные* стили<ins>~~.~~</ins>
|
||||||
|
|
||||||
|
## Цвета
|
||||||
|
|
||||||
|
Цвет текста задается при помощи цветового кода: [#RRGGBB]
|
||||||
|
|
||||||
|
| Компонент | Назначение |
|
||||||
|
| ------------ | ------------------------- |
|
||||||
|
| R | Используется для интенсивности красного |
|
||||||
|
| G | Используется для интенсивности зеленого |
|
||||||
|
| B | Используется для интенсивности синего |
|
||||||
|
|
||||||
|
### Например:
|
||||||
|
|
||||||
|
<span style="color: #ff0000">
|
||||||
|
<span style="color:rgb(105, 105, 105)">
|
||||||
|
[#ff0000]
|
||||||
|
</span>Красный Текст
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span style="color: #00ff00">
|
||||||
|
<span style="color:rgb(105, 105, 105)">
|
||||||
|
[#00ff00]
|
||||||
|
</span>Зеленый Текст
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span style="color: #0000ff">
|
||||||
|
<span style="color:rgb(105, 105, 105)">
|
||||||
|
[#0000ff]
|
||||||
|
</span>Синий Текст
|
||||||
|
</span>
|
||||||
@ -39,6 +39,7 @@
|
|||||||
- `margin` - внешний отступ элемента. Тип: 4D вектор.
|
- `margin` - внешний отступ элемента. Тип: 4D вектор.
|
||||||
Порядок: `"left,top,right,bottom"`
|
Порядок: `"left,top,right,bottom"`
|
||||||
- `visible` - видимость элемента. Тип: логический ("true"/"false").
|
- `visible` - видимость элемента. Тип: логический ("true"/"false").
|
||||||
|
- `min-size` - минимальный размер элемента. Тип: 2D вектор.
|
||||||
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
||||||
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
||||||
- `onclick` - lua функция вызываемая при нажатии на элемент.
|
- `onclick` - lua функция вызываемая при нажатии на элемент.
|
||||||
@ -67,6 +68,7 @@
|
|||||||
|
|
||||||
В число панелей также входят кнопки.
|
В число панелей также входят кнопки.
|
||||||
- `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число
|
- `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число
|
||||||
|
- `min-length` - минимальная длина панели. Тип: число
|
||||||
- `orientation` - ориентация панели: horizontal/vertical.
|
- `orientation` - ориентация панели: horizontal/vertical.
|
||||||
|
|
||||||
# Основные элементы
|
# Основные элементы
|
||||||
|
|||||||
@ -3,7 +3,9 @@ local body = entity.rigidbody
|
|||||||
local rig = entity.skeleton
|
local rig = entity.skeleton
|
||||||
|
|
||||||
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
|
||||||
@ -12,7 +14,16 @@ local function refresh_model(id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_render()
|
function on_render()
|
||||||
local invid, slotid = player.get_inventory()
|
local pid = entity:get_player()
|
||||||
|
if pid == -1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local rx, ry, rz = player.get_rot(pid, true)
|
||||||
|
rig:set_matrix(headIndex, mat4.rotate({1, 0, 0}, ry))
|
||||||
|
rig:set_matrix(bodyIndex, mat4.rotate({0, 1, 0}, rx))
|
||||||
|
|
||||||
|
local invid, slotid = player.get_inventory(pid)
|
||||||
local id, _ = inventory.get(invid, slotid)
|
local id, _ = inventory.get(invid, slotid)
|
||||||
if id ~= itemid then
|
if id ~= itemid then
|
||||||
refresh_model(id)
|
refresh_model(id)
|
||||||
|
|||||||
@ -134,6 +134,10 @@ function add_to_history(text)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function submit(text)
|
function submit(text)
|
||||||
|
if #text == 0 then
|
||||||
|
document.prompt.focused = true
|
||||||
|
return
|
||||||
|
end
|
||||||
text = text:trim()
|
text = text:trim()
|
||||||
add_to_history(text)
|
add_to_history(text)
|
||||||
|
|
||||||
@ -204,4 +208,11 @@ function on_open(mode)
|
|||||||
elseif mode then
|
elseif mode then
|
||||||
modes:set(mode)
|
modes:set(mode)
|
||||||
end
|
end
|
||||||
|
hud.close("core:ingame_chat")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_close()
|
||||||
|
time.post_runnable(function()
|
||||||
|
hud.open_permanent("core:ingame_chat")
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
8
res/layouts/ingame_chat.xml
Normal file
8
res/layouts/ingame_chat.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<panel size="300,0" margin="0,0,0,70" max-length='300' min-length='0'
|
||||||
|
size-func="gui.get_viewport()[1]/2,-1"
|
||||||
|
padding="0"
|
||||||
|
min-size="0"
|
||||||
|
color="0"
|
||||||
|
interval="0"
|
||||||
|
gravity="bottom-left" interactive="false">
|
||||||
|
</panel>
|
||||||
59
res/layouts/ingame_chat.xml.lua
Normal file
59
res/layouts/ingame_chat.xml.lua
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
local lines = {}
|
||||||
|
local dead_lines = {}
|
||||||
|
local nextid = 0
|
||||||
|
local timeout = 7
|
||||||
|
local fadeout = 1
|
||||||
|
local initialized = false
|
||||||
|
local max_lines = 15
|
||||||
|
local animation_fps = 30
|
||||||
|
|
||||||
|
local function remove_line(line)
|
||||||
|
document[line[1]]:destruct()
|
||||||
|
time.post_runnable(function() document.root:reposition() end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update_line(line, uptime)
|
||||||
|
local diff = uptime - line[2]
|
||||||
|
if diff > timeout then
|
||||||
|
remove_line(line)
|
||||||
|
table.insert(dead_lines, i)
|
||||||
|
elseif diff > timeout-fadeout then
|
||||||
|
local opacity = (timeout - diff) / fadeout
|
||||||
|
document[line[1]].color = {0, 0, 0, opacity * 80}
|
||||||
|
document[line[1].."L"].color = {255, 255, 255, opacity * 255}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
events.on("core:chat", function(message)
|
||||||
|
local current_time = time.uptime()
|
||||||
|
local id = 'l'..tostring(nextid)
|
||||||
|
document.root:add(gui.template("chat_line", {id=id}))
|
||||||
|
document.root:reposition()
|
||||||
|
document[id.."L"].text = message
|
||||||
|
nextid = nextid + 1
|
||||||
|
if #lines == max_lines then
|
||||||
|
remove_line(lines[1])
|
||||||
|
table.remove(lines, 1)
|
||||||
|
end
|
||||||
|
table.insert(lines, {id, current_time})
|
||||||
|
end)
|
||||||
|
|
||||||
|
function on_open()
|
||||||
|
if not initialized then
|
||||||
|
initialized = true
|
||||||
|
|
||||||
|
document.root:setInterval(1/animation_fps * 1000, function ()
|
||||||
|
local uptime = time.uptime()
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
update_line(line, uptime)
|
||||||
|
end
|
||||||
|
if #dead_lines > 0 then
|
||||||
|
for i = #dead_lines, 1, -1 do
|
||||||
|
local index = dead_lines[i]
|
||||||
|
table.remove(lines, i)
|
||||||
|
end
|
||||||
|
dead_lines = {}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -37,4 +37,6 @@ end
|
|||||||
|
|
||||||
function on_open()
|
function on_open()
|
||||||
refresh()
|
refresh()
|
||||||
|
|
||||||
|
input.add_callback("key:f5", refresh, document.root)
|
||||||
end
|
end
|
||||||
|
|||||||
3
res/layouts/templates/chat_line.xml
Normal file
3
res/layouts/templates/chat_line.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<container id='%{id}' color="#00000050" size="-1,20">
|
||||||
|
<label pos='5' id='%{id}L' markup='md'/>
|
||||||
|
</container>
|
||||||
@ -1,36 +0,0 @@
|
|||||||
local gui_util = {}
|
|
||||||
|
|
||||||
--- Parse `pagename?arg1=value1&arg2=value2` queries
|
|
||||||
--- @param query page query string
|
|
||||||
--- @return page_name, args_table
|
|
||||||
function gui_util.parse_query(query)
|
|
||||||
local args = {}
|
|
||||||
local name
|
|
||||||
|
|
||||||
local index = string.find(query, '?')
|
|
||||||
if index then
|
|
||||||
local argstr = string.sub(query, index + 1)
|
|
||||||
name = string.sub(query, 1, index - 1)
|
|
||||||
|
|
||||||
for key, value in string.gmatch(argstr, "([^=&]*)=([^&]*)") do
|
|
||||||
args[key] = value
|
|
||||||
end
|
|
||||||
else
|
|
||||||
name = query
|
|
||||||
end
|
|
||||||
return name, args
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param query page query string
|
|
||||||
--- @return document_id
|
|
||||||
function gui_util.load_page(query)
|
|
||||||
local name, args = gui_util.parse_query(query)
|
|
||||||
local filename = file.find(string.format("layouts/pages/%s.xml", name))
|
|
||||||
if filename then
|
|
||||||
name = file.prefix(filename)..":pages/"..name
|
|
||||||
gui.load_document(filename, name, args)
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return gui_util
|
|
||||||
107
res/modules/internal/gui_util.lua
Normal file
107
res/modules/internal/gui_util.lua
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
local gui_util = {
|
||||||
|
local_dispatchers = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Parse `pagename?arg1=value1&arg2=value2` queries
|
||||||
|
--- @param query page query string
|
||||||
|
--- @return page_name, args_table
|
||||||
|
function gui_util.parse_query(query)
|
||||||
|
local args = {}
|
||||||
|
local name
|
||||||
|
|
||||||
|
local index = string.find(query, '?')
|
||||||
|
if index then
|
||||||
|
local argstr = string.sub(query, index + 1)
|
||||||
|
name = string.sub(query, 1, index - 1)
|
||||||
|
|
||||||
|
for key, value in string.gmatch(argstr, "([^=&]*)=([^&]*)") do
|
||||||
|
args[key] = value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
name = query
|
||||||
|
end
|
||||||
|
return name, args
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param query page query string
|
||||||
|
--- @return document_id
|
||||||
|
function gui_util.load_page(query)
|
||||||
|
local name, args = gui_util.parse_query(query)
|
||||||
|
for i = #gui_util.local_dispatchers, 1, -1 do
|
||||||
|
local newname, newargs = gui_util.local_dispatchers[i](name, args)
|
||||||
|
name = newname or name
|
||||||
|
args = newargs or args
|
||||||
|
end
|
||||||
|
local filename = file.find(string.format("layouts/pages/%s.xml", name))
|
||||||
|
if filename then
|
||||||
|
name = file.prefix(filename)..":pages/"..name
|
||||||
|
gui.load_document(filename, name, args)
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function gui_util.add_page_dispatcher(dispatcher)
|
||||||
|
if type(dispatcher) ~= "function" then
|
||||||
|
error("function expected")
|
||||||
|
end
|
||||||
|
table.insert(gui_util.local_dispatchers, dispatcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gui_util.reset_local()
|
||||||
|
gui_util.local_dispatchers = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- class designed for simple UI-nodes access via properties syntax
|
||||||
|
local Element = {}
|
||||||
|
function Element.new(docname, name)
|
||||||
|
return setmetatable({docname=docname, name=name}, {
|
||||||
|
__index=function(self, k)
|
||||||
|
return gui.getattr(self.docname, self.name, k)
|
||||||
|
end,
|
||||||
|
__newindex=function(self, k, v)
|
||||||
|
gui.setattr(self.docname, self.name, k, v)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- the engine automatically creates an instance for every ui document (layout)
|
||||||
|
local Document = {}
|
||||||
|
function Document.new(docname)
|
||||||
|
return setmetatable({name=docname}, {
|
||||||
|
__index=function(self, k)
|
||||||
|
local elem = Element.new(self.name, k)
|
||||||
|
rawset(self, k, elem)
|
||||||
|
return elem
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local RadioGroup = {}
|
||||||
|
function RadioGroup:set(key)
|
||||||
|
if type(self) ~= 'table' then
|
||||||
|
error("called as non-OOP via '.', use radiogroup:set")
|
||||||
|
end
|
||||||
|
if self.current then
|
||||||
|
self.elements[self.current].enabled = true
|
||||||
|
end
|
||||||
|
self.elements[key].enabled = false
|
||||||
|
self.current = key
|
||||||
|
if self.callback then
|
||||||
|
self.callback(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function RadioGroup:__call(elements, onset, default)
|
||||||
|
local group = setmetatable({
|
||||||
|
elements=elements,
|
||||||
|
callback=onset,
|
||||||
|
current=nil
|
||||||
|
}, {__index=self})
|
||||||
|
group:set(default)
|
||||||
|
return group
|
||||||
|
end
|
||||||
|
setmetatable(RadioGroup, RadioGroup)
|
||||||
|
|
||||||
|
gui_util.Document = Document
|
||||||
|
gui_util.RadioGroup = RadioGroup
|
||||||
|
|
||||||
|
return gui_util
|
||||||
@ -49,6 +49,7 @@ local Skeleton = {__index={
|
|||||||
set_visible=function(self, i, b) return __skeleton.set_visible(self.eid, i, b) end,
|
set_visible=function(self, i, b) return __skeleton.set_visible(self.eid, i, b) end,
|
||||||
get_color=function(self) return __skeleton.get_color(self.eid) end,
|
get_color=function(self) return __skeleton.get_color(self.eid) end,
|
||||||
set_color=function(self, color) return __skeleton.set_color(self.eid, color) end,
|
set_color=function(self, color) return __skeleton.set_color(self.eid, color) end,
|
||||||
|
set_interpolated=function(self, b) return __skeleton.set_interpolated(self.eid, b) end,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
local function new_Skeleton(eid)
|
local function new_Skeleton(eid)
|
||||||
@ -66,6 +67,7 @@ local Entity = {__index={
|
|||||||
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,
|
||||||
def_name=function(self) return entities.def_name(entities.get_def(self.eid)) end,
|
def_name=function(self) return entities.def_name(entities.get_def(self.eid)) end,
|
||||||
|
get_player=function(self) return entities.get_player(self.eid) end,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
local entities = {}
|
local entities = {}
|
||||||
|
|||||||
@ -260,7 +260,7 @@ console.add_command(
|
|||||||
"chat text:str",
|
"chat text:str",
|
||||||
"Send chat message",
|
"Send chat message",
|
||||||
function (args, kwargs)
|
function (args, kwargs)
|
||||||
console.log("[you] "..args[1])
|
console.chat("[you] "..args[1])
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -146,64 +146,16 @@ function events.emit(event, ...)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- class designed for simple UI-nodes access via properties syntax
|
gui_util = require "core:internal/gui_util"
|
||||||
local Element = {}
|
|
||||||
function Element.new(docname, name)
|
|
||||||
return setmetatable({docname=docname, name=name}, {
|
|
||||||
__index=function(self, k)
|
|
||||||
return gui.getattr(self.docname, self.name, k)
|
|
||||||
end,
|
|
||||||
__newindex=function(self, k, v)
|
|
||||||
gui.setattr(self.docname, self.name, k, v)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- the engine automatically creates an instance for every ui document (layout)
|
Document = gui_util.Document
|
||||||
Document = {}
|
RadioGroup = gui_util.RadioGroup
|
||||||
function Document.new(docname)
|
__vc_page_loader = gui_util.load_page
|
||||||
return setmetatable({name=docname}, {
|
|
||||||
__index=function(self, k)
|
|
||||||
local elem = Element.new(self.name, k)
|
|
||||||
rawset(self, k, elem)
|
|
||||||
return elem
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local _RadioGroup = {}
|
|
||||||
function _RadioGroup:set(key)
|
|
||||||
if type(self) ~= 'table' then
|
|
||||||
error("called as non-OOP via '.', use radiogroup:set")
|
|
||||||
end
|
|
||||||
if self.current then
|
|
||||||
self.elements[self.current].enabled = true
|
|
||||||
end
|
|
||||||
self.elements[key].enabled = false
|
|
||||||
self.current = key
|
|
||||||
if self.callback then
|
|
||||||
self.callback(key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function _RadioGroup:__call(elements, onset, default)
|
|
||||||
local group = setmetatable({
|
|
||||||
elements=elements,
|
|
||||||
callback=onset,
|
|
||||||
current=nil
|
|
||||||
}, {__index=_RadioGroup})
|
|
||||||
group:set(default)
|
|
||||||
return group
|
|
||||||
end
|
|
||||||
setmetatable(_RadioGroup, _RadioGroup)
|
|
||||||
RadioGroup = _RadioGroup
|
|
||||||
|
|
||||||
_GUI_ROOT = Document.new("core:root")
|
_GUI_ROOT = Document.new("core:root")
|
||||||
_MENU = _GUI_ROOT.menu
|
_MENU = _GUI_ROOT.menu
|
||||||
menu = _MENU
|
menu = _MENU
|
||||||
|
|
||||||
local gui_util = require "core:gui_util"
|
|
||||||
__vc_page_loader = gui_util.load_page
|
|
||||||
|
|
||||||
--- Console library extension ---
|
--- Console library extension ---
|
||||||
console.cheats = {}
|
console.cheats = {}
|
||||||
|
|
||||||
@ -225,6 +177,11 @@ function console.log(...)
|
|||||||
log_element:paste(text)
|
log_element:paste(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function console.chat(...)
|
||||||
|
console.log(...)
|
||||||
|
events.emit("core:chat", ...)
|
||||||
|
end
|
||||||
|
|
||||||
function gui.template(name, params)
|
function gui.template(name, params)
|
||||||
local text = file.read(file.find("layouts/templates/"..name..".xml"))
|
local text = file.read(file.find("layouts/templates/"..name..".xml"))
|
||||||
for k,v in pairs(params) do
|
for k,v in pairs(params) do
|
||||||
@ -385,6 +342,16 @@ function __vc_on_hud_open()
|
|||||||
hud.show_overlay("core:console", false, {"chat"})
|
hud.show_overlay("core:console", false, {"chat"})
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
input.add_callback("key:escape", function()
|
||||||
|
if hud.is_paused() then
|
||||||
|
hud.resume()
|
||||||
|
elseif hud.is_inventory_open() then
|
||||||
|
hud.close_inventory()
|
||||||
|
else
|
||||||
|
hud.pause()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
hud.open_permanent("core:ingame_chat")
|
||||||
end
|
end
|
||||||
|
|
||||||
local RULES_FILE = "world:rules.toml"
|
local RULES_FILE = "world:rules.toml"
|
||||||
@ -408,20 +375,15 @@ end
|
|||||||
|
|
||||||
function __vc_on_world_quit()
|
function __vc_on_world_quit()
|
||||||
_rules.clear()
|
_rules.clear()
|
||||||
|
gui_util:reset_local()
|
||||||
end
|
end
|
||||||
|
|
||||||
local __vc_coroutines = {}
|
local __vc_coroutines = {}
|
||||||
local __vc_named_coroutines = {}
|
local __vc_named_coroutines = {}
|
||||||
local __vc_next_coroutine = 1
|
local __vc_next_coroutine = 1
|
||||||
local __vc_coroutine_error = nil
|
|
||||||
|
|
||||||
function __vc_start_coroutine(chunk)
|
function __vc_start_coroutine(chunk)
|
||||||
local co = coroutine.create(function()
|
local co = coroutine.create(chunk)
|
||||||
local status, err = pcall(chunk)
|
|
||||||
if not status then
|
|
||||||
__vc_coroutine_error = err
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
local id = __vc_next_coroutine
|
local id = __vc_next_coroutine
|
||||||
__vc_next_coroutine = __vc_next_coroutine + 1
|
__vc_next_coroutine = __vc_next_coroutine + 1
|
||||||
__vc_coroutines[id] = co
|
__vc_coroutines[id] = co
|
||||||
@ -431,10 +393,10 @@ end
|
|||||||
function __vc_resume_coroutine(id)
|
function __vc_resume_coroutine(id)
|
||||||
local co = __vc_coroutines[id]
|
local co = __vc_coroutines[id]
|
||||||
if co then
|
if co then
|
||||||
coroutine.resume(co)
|
local success, err = coroutine.resume(co)
|
||||||
if __vc_coroutine_error then
|
if not success then
|
||||||
debug.error(__vc_coroutine_error)
|
debug.error(err)
|
||||||
error(__vc_coroutine_error)
|
error(err)
|
||||||
end
|
end
|
||||||
return coroutine.status(co) ~= "dead"
|
return coroutine.status(co) ~= "dead"
|
||||||
end
|
end
|
||||||
@ -476,7 +438,10 @@ function __process_post_runnables()
|
|||||||
|
|
||||||
local dead = {}
|
local dead = {}
|
||||||
for name, co in pairs(__vc_named_coroutines) do
|
for name, co in pairs(__vc_named_coroutines) do
|
||||||
coroutine.resume(co)
|
local success, err = coroutine.resume(co)
|
||||||
|
if not success then
|
||||||
|
debug.error(err)
|
||||||
|
end
|
||||||
if coroutine.status(co) == "dead" then
|
if coroutine.status(co) == "dead" then
|
||||||
table.insert(dead, name)
|
table.insert(dead, name)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -20,6 +20,9 @@ void main() {
|
|||||||
if (alpha < 0.2f)
|
if (alpha < 0.2f)
|
||||||
discard;
|
discard;
|
||||||
alpha = 1.0;
|
alpha = 1.0;
|
||||||
|
} else {
|
||||||
|
if (alpha < 0.002f)
|
||||||
|
discard;
|
||||||
}
|
}
|
||||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
||||||
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
||||||
|
|||||||
@ -113,8 +113,9 @@ public:
|
|||||||
auto start = currentLocation();
|
auto start = currentLocation();
|
||||||
if (is_lua_identifier_start(c)) {
|
if (is_lua_identifier_start(c)) {
|
||||||
auto name = parseLuaName();
|
auto name = parseLuaName();
|
||||||
|
TokenTag tag = (is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME);
|
||||||
emitToken(
|
emitToken(
|
||||||
is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME,
|
tag,
|
||||||
std::move(name),
|
std::move(name),
|
||||||
start
|
start
|
||||||
);
|
);
|
||||||
|
|||||||
@ -76,8 +76,7 @@ std::shared_ptr<ContentReport> ContentReport::create(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto root = files::read_json(filename);
|
auto root = files::read_json(filename);
|
||||||
// TODO: remove default value 2 in 0.24
|
uint regionsVersion = 2U; // old worlds compatibility (pre 0.23)
|
||||||
uint regionsVersion = 2U;
|
|
||||||
root.at("region-version").get(regionsVersion);
|
root.at("region-version").get(regionsVersion);
|
||||||
auto& blocklist = root["blocks"];
|
auto& blocklist = root["blocks"];
|
||||||
auto& itemlist = root["items"];
|
auto& itemlist = root["items"];
|
||||||
|
|||||||
@ -44,6 +44,9 @@ StructLayout StructLayout::create(const std::vector<Field>& fields) {
|
|||||||
}
|
}
|
||||||
std::sort(builtFields.begin(), builtFields.end(),
|
std::sort(builtFields.begin(), builtFields.end(),
|
||||||
[](const Field& a, const Field& b) {
|
[](const Field& a, const Field& b) {
|
||||||
|
if (a.size == b.size) {
|
||||||
|
return a.name < b.name;
|
||||||
|
}
|
||||||
return a.size > b.size;
|
return a.size > b.size;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -217,6 +217,7 @@ void Engine::renderFrame() {
|
|||||||
Viewport viewport(Window::width, Window::height);
|
Viewport viewport(Window::width, Window::height);
|
||||||
DrawContext ctx(nullptr, viewport, nullptr);
|
DrawContext ctx(nullptr, viewport, nullptr);
|
||||||
gui->draw(ctx, *assets);
|
gui->draw(ctx, *assets);
|
||||||
|
gui->postAct();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::saveSettings() {
|
void Engine::saveSettings() {
|
||||||
@ -272,7 +273,7 @@ PacksManager Engine::createPacksManager(const fs::path& worldFolder) {
|
|||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::setLevelConsumer(consumer<std::unique_ptr<Level>> levelConsumer) {
|
void Engine::setLevelConsumer(OnWorldOpen levelConsumer) {
|
||||||
this->levelConsumer = std::move(levelConsumer);
|
this->levelConsumer = std::move(levelConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,14 +447,14 @@ void Engine::setLanguage(std::string locale) {
|
|||||||
langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks);
|
langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::onWorldOpen(std::unique_ptr<Level> level) {
|
void Engine::onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer) {
|
||||||
logger.info() << "world open";
|
logger.info() << "world open";
|
||||||
levelConsumer(std::move(level));
|
levelConsumer(std::move(level), localPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::onWorldClosed() {
|
void Engine::onWorldClosed() {
|
||||||
logger.info() << "world closed";
|
logger.info() << "world closed";
|
||||||
levelConsumer(nullptr);
|
levelConsumer(nullptr, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::quit() {
|
void Engine::quit() {
|
||||||
|
|||||||
@ -53,6 +53,8 @@ struct CoreParameters {
|
|||||||
std::filesystem::path scriptFile;
|
std::filesystem::path scriptFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using OnWorldOpen = std::function<void(std::unique_ptr<Level>, int64_t)>;
|
||||||
|
|
||||||
class Engine : public util::ObjectsKeeper {
|
class Engine : public util::ObjectsKeeper {
|
||||||
CoreParameters params;
|
CoreParameters params;
|
||||||
EngineSettings settings;
|
EngineSettings settings;
|
||||||
@ -71,7 +73,7 @@ class Engine : public util::ObjectsKeeper {
|
|||||||
std::unique_ptr<gui::GUI> gui;
|
std::unique_ptr<gui::GUI> gui;
|
||||||
PostRunnables postRunnables;
|
PostRunnables postRunnables;
|
||||||
Time time;
|
Time time;
|
||||||
consumer<std::unique_ptr<Level>> levelConsumer;
|
OnWorldOpen levelConsumer;
|
||||||
bool quitSignal = false;
|
bool quitSignal = false;
|
||||||
|
|
||||||
void loadControls();
|
void loadControls();
|
||||||
@ -134,7 +136,7 @@ public:
|
|||||||
/// @brief Get engine resource paths controller
|
/// @brief Get engine resource paths controller
|
||||||
ResPaths* getResPaths();
|
ResPaths* getResPaths();
|
||||||
|
|
||||||
void onWorldOpen(std::unique_ptr<Level> level);
|
void onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer);
|
||||||
void onWorldClosed();
|
void onWorldClosed();
|
||||||
|
|
||||||
void quit();
|
void quit();
|
||||||
@ -166,7 +168,7 @@ public:
|
|||||||
|
|
||||||
PacksManager createPacksManager(const fs::path& worldFolder);
|
PacksManager createPacksManager(const fs::path& worldFolder);
|
||||||
|
|
||||||
void setLevelConsumer(consumer<std::unique_ptr<Level>> levelConsumer);
|
void setLevelConsumer(OnWorldOpen levelConsumer);
|
||||||
|
|
||||||
SettingsHandler& getSettingsHandler();
|
SettingsHandler& getSettingsHandler();
|
||||||
|
|
||||||
|
|||||||
@ -15,14 +15,16 @@ Mainloop::Mainloop(Engine& engine) : engine(engine) {
|
|||||||
void Mainloop::run() {
|
void Mainloop::run() {
|
||||||
auto& time = engine.getTime();
|
auto& time = engine.getTime();
|
||||||
|
|
||||||
engine.setLevelConsumer([this](auto level) {
|
engine.setLevelConsumer([this](auto level, int64_t localPlayer) {
|
||||||
if (level == nullptr) {
|
if (level == nullptr) {
|
||||||
// destroy LevelScreen and run quit callbacks
|
// destroy LevelScreen and run quit callbacks
|
||||||
engine.setScreen(nullptr);
|
engine.setScreen(nullptr);
|
||||||
// create and go to menu screen
|
// create and go to menu screen
|
||||||
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
} else {
|
} else {
|
||||||
engine.setScreen(std::make_shared<LevelScreen>(engine, std::move(level)));
|
engine.setScreen(std::make_shared<LevelScreen>(
|
||||||
|
engine, std::move(level), localPlayer
|
||||||
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ void ServerMainloop::run() {
|
|||||||
logger.info() << "nothing to do";
|
logger.info() << "nothing to do";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
engine.setLevelConsumer([this](auto level) {
|
engine.setLevelConsumer([this](auto level, auto) {
|
||||||
setLevel(std::move(level));
|
setLevel(std::move(level));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,9 @@
|
|||||||
#include "graphics/render/ParticlesRenderer.hpp"
|
#include "graphics/render/ParticlesRenderer.hpp"
|
||||||
#include "graphics/render/ChunksRenderer.hpp"
|
#include "graphics/render/ChunksRenderer.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
#include "network/Network.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
|
#include "objects/Players.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/EntityDef.hpp"
|
#include "objects/EntityDef.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
@ -41,6 +43,7 @@ static std::shared_ptr<Label> create_label(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
|
||||||
std::shared_ptr<UINode> create_debug_panel(
|
std::shared_ptr<UINode> create_debug_panel(
|
||||||
Engine& engine,
|
Engine& engine,
|
||||||
Level& level,
|
Level& level,
|
||||||
@ -56,6 +59,10 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
static int fpsMax = fps;
|
static int fpsMax = fps;
|
||||||
static std::wstring fpsString = L"";
|
static std::wstring fpsString = L"";
|
||||||
|
|
||||||
|
static size_t lastTotalDownload = 0;
|
||||||
|
static size_t lastTotalUpload = 0;
|
||||||
|
static std::wstring netSpeedString = L"";
|
||||||
|
|
||||||
panel->listenInterval(0.016f, [&engine]() {
|
panel->listenInterval(0.016f, [&engine]() {
|
||||||
fps = 1.0f / engine.getTime().getDelta();
|
fps = 1.0f / engine.getTime().getDelta();
|
||||||
fpsMin = std::min(fps, fpsMin);
|
fpsMin = std::min(fps, fpsMin);
|
||||||
@ -67,6 +74,19 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
fpsMin = fps;
|
fpsMin = fps;
|
||||||
fpsMax = fps;
|
fpsMax = fps;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
panel->listenInterval(1.0f, [&engine]() {
|
||||||
|
const auto& network = engine.getNetwork();
|
||||||
|
size_t totalDownload = network.getTotalDownload();
|
||||||
|
size_t totalUpload = network.getTotalUpload();
|
||||||
|
netSpeedString =
|
||||||
|
L"download: " + std::to_wstring(totalDownload - lastTotalDownload) +
|
||||||
|
L" B/s upload: " + std::to_wstring(totalUpload - lastTotalUpload) +
|
||||||
|
L" B/s";
|
||||||
|
lastTotalDownload = totalDownload;
|
||||||
|
lastTotalUpload = totalUpload;
|
||||||
|
});
|
||||||
|
|
||||||
panel->add(create_label([]() { return L"fps: "+fpsString;}));
|
panel->add(create_label([]() { return L"fps: "+fpsString;}));
|
||||||
|
|
||||||
panel->add(create_label([]() {
|
panel->add(create_label([]() {
|
||||||
@ -84,6 +104,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
panel->add(create_label([]() {
|
panel->add(create_label([]() {
|
||||||
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
|
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
|
||||||
}));
|
}));
|
||||||
|
panel->add(create_label([]() { return netSpeedString; }));
|
||||||
panel->add(create_label([&engine]() {
|
panel->add(create_label([&engine]() {
|
||||||
auto& settings = engine.getSettings();
|
auto& settings = engine.getSettings();
|
||||||
bool culling = settings.graphics.frustumCulling.get();
|
bool culling = settings.graphics.frustumCulling.get();
|
||||||
@ -103,6 +124,10 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
return L"entities: "+std::to_wstring(level.entities->size())+L" next: "+
|
return L"entities: "+std::to_wstring(level.entities->size())+L" next: "+
|
||||||
std::to_wstring(level.entities->peekNextID());
|
std::to_wstring(level.entities->peekNextID());
|
||||||
}));
|
}));
|
||||||
|
panel->add(create_label([&]() {
|
||||||
|
return L"players: "+std::to_wstring(level.players->size())+L" local: "+
|
||||||
|
std::to_wstring(player.getId());
|
||||||
|
}));
|
||||||
panel->add(create_label([&]() -> std::wstring {
|
panel->add(create_label([&]() -> std::wstring {
|
||||||
const auto& vox = player.selection.vox;
|
const auto& vox = player.selection.vox;
|
||||||
std::wstringstream stream;
|
std::wstringstream stream;
|
||||||
|
|||||||
@ -201,17 +201,6 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
|||||||
"' pos='0' size='256' gravity='top-right' margin='0,20,0,0'/>"
|
"' pos='0' size='256' gravity='top-right' margin='0,20,0,0'/>"
|
||||||
);
|
);
|
||||||
add(HudElement(hud_element_mode::permanent, nullptr, debugMinimap, true));
|
add(HudElement(hud_element_mode::permanent, nullptr, debugMinimap, true));
|
||||||
|
|
||||||
keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([this]() -> bool {
|
|
||||||
if (pause) {
|
|
||||||
setPause(false);
|
|
||||||
} else if (inventoryOpen) {
|
|
||||||
closeInventory();
|
|
||||||
} else {
|
|
||||||
setPause(true);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Hud::~Hud() {
|
Hud::~Hud() {
|
||||||
@ -234,7 +223,8 @@ void Hud::cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Hud::processInput(bool visible) {
|
void Hud::processInput(bool visible) {
|
||||||
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
auto menu = gui.getMenu();
|
||||||
|
if (!Window::isFocused() && !menu->hasOpenPage() && !isInventoryOpen()) {
|
||||||
setPause(true);
|
setPause(true);
|
||||||
}
|
}
|
||||||
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
||||||
@ -329,14 +319,13 @@ void Hud::update(bool visible) {
|
|||||||
if (!visible && inventoryOpen) {
|
if (!visible && inventoryOpen) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
}
|
}
|
||||||
if (pause && menu->getCurrent().panel == nullptr) {
|
if (pause && !menu->hasOpenPage()) {
|
||||||
setPause(false);
|
setPause(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gui.isFocusCaught()) {
|
if (!gui.isFocusCaught()) {
|
||||||
processInput(visible);
|
processInput(visible);
|
||||||
}
|
}
|
||||||
if ((pause || inventoryOpen) == Events::_cursor_locked) {
|
if ((menu->hasOpenPage() || inventoryOpen) == Events::isCursorLocked()) {
|
||||||
Events::toggleCursor();
|
Events::toggleCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,7 +590,9 @@ void Hud::draw(const DrawContext& ctx){
|
|||||||
const Viewport& viewport = ctx.getViewport();
|
const Viewport& viewport = ctx.getViewport();
|
||||||
const uint width = viewport.getWidth();
|
const uint width = viewport.getWidth();
|
||||||
const uint height = viewport.getHeight();
|
const uint height = viewport.getHeight();
|
||||||
|
auto menu = gui.getMenu();
|
||||||
|
|
||||||
|
darkOverlay->setVisible(menu->hasOpenPage());
|
||||||
updateElementsPosition(viewport);
|
updateElementsPosition(viewport);
|
||||||
|
|
||||||
uicamera->setFov(height);
|
uicamera->setFov(height);
|
||||||
@ -687,19 +678,20 @@ void Hud::setPause(bool pause) {
|
|||||||
if (this->pause == pause) {
|
if (this->pause == pause) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->pause = pause;
|
if (allowPause) {
|
||||||
|
this->pause = pause;
|
||||||
|
}
|
||||||
|
|
||||||
if (inventoryOpen) {
|
if (inventoryOpen) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& menu = gui.getMenu();
|
const auto& menu = gui.getMenu();
|
||||||
if (pause) {
|
if (menu->hasOpenPage()) {
|
||||||
menu->setPage("pause");
|
|
||||||
} else {
|
|
||||||
menu->reset();
|
menu->reset();
|
||||||
|
} else {
|
||||||
|
menu->setPage("pause");
|
||||||
}
|
}
|
||||||
darkOverlay->setVisible(pause);
|
|
||||||
menu->setVisible(pause);
|
menu->setVisible(pause);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,3 +724,12 @@ void Hud::setDebugCheats(bool flag) {
|
|||||||
debugPanel->setZIndex(2);
|
debugPanel->setZIndex(2);
|
||||||
gui.add(debugPanel);
|
gui.add(debugPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hud::setAllowPause(bool flag) {
|
||||||
|
if (pause) {
|
||||||
|
auto menu = gui.getMenu();
|
||||||
|
setPause(false);
|
||||||
|
menu->setPage("pause", true);
|
||||||
|
}
|
||||||
|
allowPause = flag;
|
||||||
|
}
|
||||||
|
|||||||
@ -113,6 +113,8 @@ class Hud : public util::ObjectsKeeper {
|
|||||||
bool showContentPanel = true;
|
bool showContentPanel = true;
|
||||||
/// @brief Provide cheat controllers to the debug panel
|
/// @brief Provide cheat controllers to the debug panel
|
||||||
bool allowDebugCheats = true;
|
bool allowDebugCheats = true;
|
||||||
|
/// @brief Allow actual pause
|
||||||
|
bool allowPause = true;
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
/// @brief UI element will be dynamicly positioned near to inventory or in screen center
|
/// @brief UI element will be dynamicly positioned near to inventory or in screen center
|
||||||
std::shared_ptr<gui::UINode> secondUI;
|
std::shared_ptr<gui::UINode> secondUI;
|
||||||
@ -206,6 +208,8 @@ public:
|
|||||||
|
|
||||||
void setDebugCheats(bool flag);
|
void setDebugCheats(bool flag);
|
||||||
|
|
||||||
|
void setAllowPause(bool flag);
|
||||||
|
|
||||||
static bool showGeneratorMinimap;
|
static bool showGeneratorMinimap;
|
||||||
|
|
||||||
/// @brief Runtime updating debug visualization texture
|
/// @brief Runtime updating debug visualization texture
|
||||||
|
|||||||
@ -36,9 +36,10 @@
|
|||||||
|
|
||||||
static debug::Logger logger("level-screen");
|
static debug::Logger logger("level-screen");
|
||||||
|
|
||||||
LevelScreen::LevelScreen(Engine& engine, std::unique_ptr<Level> levelPtr)
|
LevelScreen::LevelScreen(
|
||||||
: Screen(engine), postProcessing(std::make_unique<PostProcessing>())
|
Engine& engine, std::unique_ptr<Level> levelPtr, int64_t localPlayer
|
||||||
{
|
)
|
||||||
|
: Screen(engine), postProcessing(std::make_unique<PostProcessing>()) {
|
||||||
Level* level = levelPtr.get();
|
Level* level = levelPtr.get();
|
||||||
|
|
||||||
auto& settings = engine.getSettings();
|
auto& settings = engine.getSettings();
|
||||||
@ -46,7 +47,9 @@ LevelScreen::LevelScreen(Engine& engine, std::unique_ptr<Level> levelPtr)
|
|||||||
auto menu = engine.getGUI()->getMenu();
|
auto menu = engine.getGUI()->getMenu();
|
||||||
menu->reset();
|
menu->reset();
|
||||||
|
|
||||||
auto player = level->players->get(0);
|
auto player = level->players->get(localPlayer);
|
||||||
|
assert(player != nullptr);
|
||||||
|
|
||||||
controller =
|
controller =
|
||||||
std::make_unique<LevelController>(&engine, std::move(levelPtr), player);
|
std::make_unique<LevelController>(&engine, std::move(levelPtr), player);
|
||||||
playerController = std::make_unique<PlayerController>(
|
playerController = std::make_unique<PlayerController>(
|
||||||
@ -165,14 +168,16 @@ void LevelScreen::updateHotkeys() {
|
|||||||
|
|
||||||
void LevelScreen::update(float delta) {
|
void LevelScreen::update(float delta) {
|
||||||
gui::GUI* gui = engine.getGUI();
|
gui::GUI* gui = engine.getGUI();
|
||||||
|
auto menu = gui->getMenu();
|
||||||
|
|
||||||
bool inputLocked = hud->isPause() ||
|
bool inputLocked = menu->hasOpenPage() ||
|
||||||
hud->isInventoryOpen() ||
|
hud->isInventoryOpen() ||
|
||||||
gui->isFocusCaught();
|
gui->isFocusCaught();
|
||||||
if (!gui->isFocusCaught()) {
|
if (!gui->isFocusCaught()) {
|
||||||
updateHotkeys();
|
updateHotkeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto level = controller->getLevel();
|
||||||
auto player = playerController->getPlayer();
|
auto player = playerController->getPlayer();
|
||||||
auto camera = player->currentCamera;
|
auto camera = player->currentCamera;
|
||||||
|
|
||||||
@ -189,7 +194,6 @@ void LevelScreen::update(float delta) {
|
|||||||
camera->dir,
|
camera->dir,
|
||||||
glm::vec3(0, 1, 0)
|
glm::vec3(0, 1, 0)
|
||||||
);
|
);
|
||||||
auto level = controller->getLevel();
|
|
||||||
const auto& settings = engine.getSettings();
|
const auto& settings = engine.getSettings();
|
||||||
|
|
||||||
if (!hud->isPause()) {
|
if (!hud->isPause()) {
|
||||||
|
|||||||
@ -34,7 +34,9 @@ class LevelScreen : public Screen {
|
|||||||
void initializeContent();
|
void initializeContent();
|
||||||
void initializePack(ContentPackRuntime* pack);
|
void initializePack(ContentPackRuntime* pack);
|
||||||
public:
|
public:
|
||||||
LevelScreen(Engine& engine, std::unique_ptr<Level> level);
|
LevelScreen(
|
||||||
|
Engine& engine, std::unique_ptr<Level> level, int64_t localPlayer
|
||||||
|
);
|
||||||
~LevelScreen();
|
~LevelScreen();
|
||||||
|
|
||||||
void update(float delta) override;
|
void update(float delta) override;
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
|
#include "objects/Entities.hpp"
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
@ -156,9 +157,14 @@ void Decorator::update(float delta, const Camera& camera) {
|
|||||||
auto note = renderer.texts->get(textsIter->second);
|
auto note = renderer.texts->get(textsIter->second);
|
||||||
auto player = level.players->get(textsIter->first);
|
auto player = level.players->get(textsIter->first);
|
||||||
if (player == nullptr) {
|
if (player == nullptr) {
|
||||||
|
renderer.texts->remove(textsIter->second);
|
||||||
textsIter = playerTexts.erase(textsIter);
|
textsIter = playerTexts.erase(textsIter);
|
||||||
} else {
|
} else {
|
||||||
note->setPosition(player->getPosition() + glm::vec3(0, 1, 0));
|
glm::vec3 position = player->getPosition();
|
||||||
|
if (auto entity = level.entities->get(player->getEntity())) {
|
||||||
|
position = entity->getInterpolatedPosition();
|
||||||
|
}
|
||||||
|
note->setPosition(position + glm::vec3(0, 1, 0));
|
||||||
++textsIter;
|
++textsIter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -274,8 +274,9 @@ void WorldRenderer::renderHands(
|
|||||||
glm::mat4(1.0f), -glm::pi<float>() * 0.5f, glm::vec3(0, 1, 0)
|
glm::mat4(1.0f), -glm::pi<float>() * 0.5f, glm::vec3(0, 1, 0)
|
||||||
);
|
);
|
||||||
prevRotation = rotation;
|
prevRotation = rotation;
|
||||||
|
glm::vec3 cameraRotation = player.getRotation();
|
||||||
auto offset = -(camera.position - player.getPosition());
|
auto offset = -(camera.position - player.getPosition());
|
||||||
float angle = glm::radians(player.rotation.x - 90);
|
float angle = glm::radians(cameraRotation.x - 90);
|
||||||
float cos = glm::cos(angle);
|
float cos = glm::cos(angle);
|
||||||
float sin = glm::sin(angle);
|
float sin = glm::sin(angle);
|
||||||
|
|
||||||
|
|||||||
@ -166,7 +166,7 @@ void GUI::actFocused() {
|
|||||||
focus->keyPressed(key);
|
focus->keyPressed(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Events::_cursor_locked) {
|
if (!Events::isCursorLocked()) {
|
||||||
if (Events::clicked(mousecode::BUTTON_1) &&
|
if (Events::clicked(mousecode::BUTTON_1) &&
|
||||||
(Events::jclicked(mousecode::BUTTON_1) || Events::delta.x || Events::delta.y))
|
(Events::jclicked(mousecode::BUTTON_1) || Events::delta.x || Events::delta.y))
|
||||||
{
|
{
|
||||||
@ -178,18 +178,12 @@ void GUI::actFocused() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GUI::act(float delta, const Viewport& vp) {
|
void GUI::act(float delta, const Viewport& vp) {
|
||||||
while (!postRunnables.empty()) {
|
|
||||||
runnable callback = postRunnables.back();
|
|
||||||
postRunnables.pop();
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
container->setSize(vp.size());
|
container->setSize(vp.size());
|
||||||
container->act(delta);
|
container->act(delta);
|
||||||
auto prevfocus = focus;
|
auto prevfocus = focus;
|
||||||
|
|
||||||
updateTooltip(delta);
|
updateTooltip(delta);
|
||||||
if (!Events::_cursor_locked) {
|
if (!Events::isCursorLocked()) {
|
||||||
actMouse(delta);
|
actMouse(delta);
|
||||||
} else {
|
} else {
|
||||||
if (hover) {
|
if (hover) {
|
||||||
@ -206,6 +200,14 @@ void GUI::act(float delta, const Viewport& vp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI::postAct() {
|
||||||
|
while (!postRunnables.empty()) {
|
||||||
|
runnable callback = postRunnables.back();
|
||||||
|
postRunnables.pop();
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
auto ctx = pctx.sub(batch2D.get());
|
auto ctx = pctx.sub(batch2D.get());
|
||||||
|
|
||||||
|
|||||||
@ -106,6 +106,8 @@ namespace gui {
|
|||||||
/// @param assets active assets storage
|
/// @param assets active assets storage
|
||||||
void draw(const DrawContext& pctx, const Assets& assets);
|
void draw(const DrawContext& pctx, const Assets& assets);
|
||||||
|
|
||||||
|
void postAct();
|
||||||
|
|
||||||
/// @brief Add element to the main container
|
/// @brief Add element to the main container
|
||||||
/// @param node UI element
|
/// @param node UI element
|
||||||
void add(std::shared_ptr<UINode> node);
|
void add(std::shared_ptr<UINode> node);
|
||||||
|
|||||||
@ -91,6 +91,10 @@ Page& Menu::getCurrent() {
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Menu::hasOpenPage() const {
|
||||||
|
return current.panel != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::clearHistory() {
|
void Menu::clearHistory() {
|
||||||
pageStack = std::stack<Page>();
|
pageStack = std::stack<Page>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,5 +64,7 @@ namespace gui {
|
|||||||
|
|
||||||
/// @brief Get current page
|
/// @brief Get current page
|
||||||
Page& getCurrent();
|
Page& getCurrent();
|
||||||
|
|
||||||
|
bool hasOpenPage() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,14 @@ int Panel::getMaxLength() const {
|
|||||||
return maxLength;
|
return maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::setMinLength(int value) {
|
||||||
|
minLength = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Panel::getMinLength() const {
|
||||||
|
return minLength;
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::setPadding(glm::vec4 padding) {
|
void Panel::setPadding(glm::vec4 padding) {
|
||||||
this->padding = padding;
|
this->padding = padding;
|
||||||
refresh();
|
refresh();
|
||||||
@ -34,9 +42,11 @@ glm::vec4 Panel::getPadding() const {
|
|||||||
|
|
||||||
void Panel::cropToContent() {
|
void Panel::cropToContent() {
|
||||||
if (maxLength > 0.0f) {
|
if (maxLength > 0.0f) {
|
||||||
setSize(glm::vec2(getSize().x, glm::min(maxLength, actualLength)));
|
setSize(glm::vec2(
|
||||||
|
getSize().x, glm::max(minLength, glm::min(maxLength, actualLength))
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
setSize(glm::vec2(getSize().x, actualLength));
|
setSize(glm::vec2(getSize().x, glm::max(minLength, actualLength)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +62,11 @@ void Panel::add(const std::shared_ptr<UINode> &node) {
|
|||||||
fullRefresh();
|
fullRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::remove(const std::shared_ptr<UINode> &node) {
|
||||||
|
Container::remove(node);
|
||||||
|
fullRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::refresh() {
|
void Panel::refresh() {
|
||||||
UINode::refresh();
|
UINode::refresh();
|
||||||
std::stable_sort(nodes.begin(), nodes.end(), [](auto a, auto b) {
|
std::stable_sort(nodes.begin(), nodes.end(), [](auto a, auto b) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ namespace gui {
|
|||||||
Orientation orientation = Orientation::vertical;
|
Orientation orientation = Orientation::vertical;
|
||||||
glm::vec4 padding {2.0f};
|
glm::vec4 padding {2.0f};
|
||||||
float interval = 2.0f;
|
float interval = 2.0f;
|
||||||
|
int minLength = 0;
|
||||||
int maxLength = 0;
|
int maxLength = 0;
|
||||||
public:
|
public:
|
||||||
Panel(
|
Panel(
|
||||||
@ -24,6 +25,7 @@ namespace gui {
|
|||||||
Orientation getOrientation() const;
|
Orientation getOrientation() const;
|
||||||
|
|
||||||
virtual void add(const std::shared_ptr<UINode>& node) override;
|
virtual void add(const std::shared_ptr<UINode>& node) override;
|
||||||
|
virtual void remove(const std::shared_ptr<UINode>& node) override;
|
||||||
|
|
||||||
virtual void refresh() override;
|
virtual void refresh() override;
|
||||||
virtual void fullRefresh() override;
|
virtual void fullRefresh() override;
|
||||||
@ -31,6 +33,9 @@ namespace gui {
|
|||||||
virtual void setMaxLength(int value);
|
virtual void setMaxLength(int value);
|
||||||
int getMaxLength() const;
|
int getMaxLength() const;
|
||||||
|
|
||||||
|
virtual void setMinLength(int value);
|
||||||
|
int getMinLength() const;
|
||||||
|
|
||||||
virtual void setPadding(glm::vec4 padding);
|
virtual void setPadding(glm::vec4 padding);
|
||||||
glm::vec4 getPadding() const;
|
glm::vec4 getPadding() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -289,12 +289,16 @@ const std::string& UINode::getId() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UINode::reposition() {
|
void UINode::reposition() {
|
||||||
|
if (sizefunc) {
|
||||||
|
auto newSize = sizefunc();
|
||||||
|
setSize(
|
||||||
|
{newSize.x < 0 ? size.x : newSize.x,
|
||||||
|
newSize.y < 0 ? size.y : newSize.y}
|
||||||
|
);
|
||||||
|
}
|
||||||
if (positionfunc) {
|
if (positionfunc) {
|
||||||
setPos(positionfunc());
|
setPos(positionfunc());
|
||||||
}
|
}
|
||||||
if (sizefunc) {
|
|
||||||
setSize(sizefunc());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UINode::setGravity(Gravity gravity) {
|
void UINode::setGravity(Gravity gravity) {
|
||||||
|
|||||||
@ -91,6 +91,9 @@ static void _readUINode(
|
|||||||
if (element.has("pos")) {
|
if (element.has("pos")) {
|
||||||
node.setPos(element.attr("pos").asVec2());
|
node.setPos(element.attr("pos").asVec2());
|
||||||
}
|
}
|
||||||
|
if (element.has("min-size")) {
|
||||||
|
node.setMinSize(element.attr("min-size").asVec2());
|
||||||
|
}
|
||||||
if (element.has("size")) {
|
if (element.has("size")) {
|
||||||
node.setSize(element.attr("size").asVec2());
|
node.setSize(element.attr("size").asVec2());
|
||||||
}
|
}
|
||||||
@ -220,6 +223,9 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
|
|||||||
if (element.has("max-length")) {
|
if (element.has("max-length")) {
|
||||||
panel.setMaxLength(element.attr("max-length").asInt());
|
panel.setMaxLength(element.attr("max-length").asInt());
|
||||||
}
|
}
|
||||||
|
if (element.has("min-length")) {
|
||||||
|
panel.setMinLength(element.attr("min-length").asInt());
|
||||||
|
}
|
||||||
if (element.has("orientation")) {
|
if (element.has("orientation")) {
|
||||||
auto &oname = element.attr("orientation").getText();
|
auto &oname = element.attr("orientation").getText();
|
||||||
if (oname == "horizontal") {
|
if (oname == "horizontal") {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "markdown.hpp"
|
#include "markdown.hpp"
|
||||||
|
|
||||||
|
#include "coders/commons.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
|
|
||||||
using namespace markdown;
|
using namespace markdown;
|
||||||
@ -9,7 +10,7 @@ static inline void emit(
|
|||||||
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
|
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
|
||||||
) {
|
) {
|
||||||
ss << c;
|
ss << c;
|
||||||
styles.map.emplace_back(styles.palette.size()-1);
|
styles.map.emplace_back(styles.palette.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename CharT>
|
||||||
@ -20,6 +21,38 @@ static inline void emit_md(
|
|||||||
styles.map.emplace_back(0);
|
styles.map.emplace_back(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
static glm::vec4 parse_color(const std::basic_string_view<CharT>& color_code) {
|
||||||
|
if (color_code.size() != 8 || color_code[0] != '#') {
|
||||||
|
return glm::vec4(1, 1, 1, 1); // default to white
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hex_to_float = [](char high, char low) {
|
||||||
|
int high_val = hexchar2int(high);
|
||||||
|
int low_val = hexchar2int(low);
|
||||||
|
if (high_val == -1 || low_val == -1) {
|
||||||
|
return 1.0f; // default to max value on error
|
||||||
|
}
|
||||||
|
return (high_val * 16 + low_val) / 255.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
return glm::vec4(
|
||||||
|
hex_to_float(color_code[1], color_code[2]),
|
||||||
|
hex_to_float(color_code[3], color_code[4]),
|
||||||
|
hex_to_float(color_code[5], color_code[6]),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
static inline void apply_color(
|
||||||
|
const std::basic_string_view<CharT>& color_code, FontStylesScheme& styles
|
||||||
|
) {
|
||||||
|
FontStyle style = styles.palette.back();
|
||||||
|
style.color = parse_color(color_code);
|
||||||
|
styles.palette.push_back(style);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename CharT>
|
||||||
static inline void restyle(
|
static inline void restyle(
|
||||||
CharT c,
|
CharT c,
|
||||||
@ -43,13 +76,14 @@ Result<CharT> process_markdown(
|
|||||||
std::basic_stringstream<CharT> ss;
|
std::basic_stringstream<CharT> ss;
|
||||||
FontStylesScheme styles {
|
FontStylesScheme styles {
|
||||||
// markdown default
|
// markdown default
|
||||||
{{false, false, false, false, glm::vec4(1,1,1,0.5f)}, {}},
|
{{false, false, false, false, glm::vec4(1, 1, 1, 0.5f)}, {}},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
FontStyle style;
|
FontStyle style;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
while (pos < source.size()) {
|
while (pos < source.size()) {
|
||||||
CharT first = source[pos];
|
CharT first = source[pos];
|
||||||
|
|
||||||
if (first == '\\') {
|
if (first == '\\') {
|
||||||
if (pos + 1 < source.size()) {
|
if (pos + 1 < source.size()) {
|
||||||
CharT second = source[++pos];
|
CharT second = source[++pos];
|
||||||
@ -63,14 +97,37 @@ Result<CharT> process_markdown(
|
|||||||
emit(second, styles, ss);
|
emit(second, styles, ss);
|
||||||
pos++;
|
pos++;
|
||||||
continue;
|
continue;
|
||||||
|
case '[':
|
||||||
|
if (pos + 9 < source.size() && source[pos + 1] == '#' &&
|
||||||
|
source[pos + 8] == ']') {
|
||||||
|
if (!eraseMarkdown) {
|
||||||
|
emit_md(source[pos - 1], styles, ss);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 10; ++i) {
|
||||||
|
|
||||||
|
emit(source[pos + i], styles, ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += 10;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pos--;
|
pos--;
|
||||||
}
|
}
|
||||||
|
} else if (first == '[' && pos + 9 <= source.size() && source[pos + 1] == '#' && source[pos + 8] == ']') {
|
||||||
|
std::basic_string_view<CharT> color_code = source.substr(pos + 1, 8);
|
||||||
|
apply_color(color_code, styles);
|
||||||
|
if (!eraseMarkdown) {
|
||||||
|
for (int i = 0; i < 9; ++i) {
|
||||||
|
emit_md(source[pos + i], styles, ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += 9; // Skip past the color code
|
||||||
|
continue;
|
||||||
} else if (first == '*') {
|
} else if (first == '*') {
|
||||||
if (pos + 1 < source.size() && source[pos+1] == '*') {
|
if (pos + 1 < source.size() && source[pos + 1] == '*') {
|
||||||
pos++;
|
pos++;
|
||||||
if (!eraseMarkdown)
|
if (!eraseMarkdown) emit_md(first, styles, ss);
|
||||||
emit_md(first, styles, ss);
|
|
||||||
style.bold = !style.bold;
|
style.bold = !style.bold;
|
||||||
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
continue;
|
continue;
|
||||||
@ -78,17 +135,17 @@ Result<CharT> process_markdown(
|
|||||||
style.italic = !style.italic;
|
style.italic = !style.italic;
|
||||||
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
continue;
|
continue;
|
||||||
} else if (first == '_' && pos + 1 < source.size() && source[pos+1] == '_') {
|
} else if (first == '_' && pos + 1 < source.size() &&
|
||||||
|
source[pos + 1] == '_') {
|
||||||
pos++;
|
pos++;
|
||||||
if (!eraseMarkdown)
|
if (!eraseMarkdown) emit_md(first, styles, ss);
|
||||||
emit_md(first, styles, ss);
|
|
||||||
style.underline = !style.underline;
|
style.underline = !style.underline;
|
||||||
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
continue;
|
continue;
|
||||||
} else if (first == '~' && pos + 1 < source.size() && source[pos+1] == '~') {
|
} else if (first == '~' && pos + 1 < source.size() &&
|
||||||
|
source[pos + 1] == '~') {
|
||||||
pos++;
|
pos++;
|
||||||
if (!eraseMarkdown)
|
if (!eraseMarkdown) emit_md(first, styles, ss);
|
||||||
emit_md(first, styles, ss);
|
|
||||||
style.strikethrough = !style.strikethrough;
|
style.strikethrough = !style.strikethrough;
|
||||||
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
continue;
|
continue;
|
||||||
@ -106,6 +163,8 @@ Result<char> markdown::process(std::string_view source, bool eraseMarkdown) {
|
|||||||
return process_markdown(source, eraseMarkdown);
|
return process_markdown(source, eraseMarkdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<wchar_t> markdown::process(std::wstring_view source, bool eraseMarkdown) {
|
Result<wchar_t> markdown::process(
|
||||||
|
std::wstring_view source, bool eraseMarkdown
|
||||||
|
) {
|
||||||
return process_markdown(source, eraseMarkdown);
|
return process_markdown(source, eraseMarkdown);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,12 @@
|
|||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "util/timeutil.hpp"
|
#include "util/timeutil.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
static debug::Logger logger("lighting");
|
||||||
|
|
||||||
Lighting::Lighting(const Content& content, Chunks& chunks)
|
Lighting::Lighting(const Content& content, Chunks& chunks)
|
||||||
: content(content), chunks(chunks) {
|
: content(content), chunks(chunks) {
|
||||||
auto& indices = *content.getIndices();
|
auto& indices = *content.getIndices();
|
||||||
@ -63,6 +66,10 @@ void Lighting::buildSkyLight(int cx, int cz){
|
|||||||
const auto blockDefs = content.getIndices()->blocks.getDefs();
|
const auto blockDefs = content.getIndices()->blocks.getDefs();
|
||||||
|
|
||||||
Chunk* chunk = chunks.getChunk(cx, cz);
|
Chunk* chunk = chunks.getChunk(cx, cz);
|
||||||
|
if (chunk == nullptr) {
|
||||||
|
logger.error() << "attempted to build sky lights to chunk missing in local matrix";
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (int z = 0; z < CHUNK_D; z++){
|
for (int z = 0; z < CHUNK_D; z++){
|
||||||
for (int x = 0; x < CHUNK_W; x++){
|
for (int x = 0; x < CHUNK_W; x++){
|
||||||
int gx = x + cx * CHUNK_W;
|
int gx = x + cx * CHUNK_W;
|
||||||
@ -95,7 +102,10 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) {
|
|||||||
|
|
||||||
auto blockDefs = content.getIndices()->blocks.getDefs();
|
auto blockDefs = content.getIndices()->blocks.getDefs();
|
||||||
auto chunk = chunks.getChunk(cx, cz);
|
auto chunk = chunks.getChunk(cx, cz);
|
||||||
|
if (chunk == nullptr) {
|
||||||
|
logger.error() << "attempted to build lights to chunk missing in local matrix";
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (uint y = 0; y < CHUNK_H; y++){
|
for (uint y = 0; y < CHUNK_H; y++){
|
||||||
for (uint z = 0; z < CHUNK_D; z++){
|
for (uint z = 0; z < CHUNK_D; z++){
|
||||||
for (uint x = 0; x < CHUNK_W; x++){
|
for (uint x = 0; x < CHUNK_W; x++){
|
||||||
|
|||||||
@ -92,7 +92,7 @@ bool ChunksController::loadVisible(const Player& player, uint padding) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& chunk = chunks.getChunks()[nearZ * sizeX + nearX];
|
const auto& chunk = chunks.getChunks()[nearZ * sizeX + nearX];
|
||||||
if (chunk != nullptr || !assigned) {
|
if (chunk != nullptr || !assigned || !player.isLoadingChunks()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int offsetX = chunks.getOffsetX();
|
int offsetX = chunks.getOffsetX();
|
||||||
@ -101,7 +101,9 @@ bool ChunksController::loadVisible(const Player& player, uint padding) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunksController::buildLights(const Player& player, const std::shared_ptr<Chunk>& chunk) const {
|
bool ChunksController::buildLights(
|
||||||
|
const Player& player, const std::shared_ptr<Chunk>& chunk
|
||||||
|
) const {
|
||||||
int surrounding = 0;
|
int surrounding = 0;
|
||||||
for (int oz = -1; oz <= 1; oz++) {
|
for (int oz = -1; oz <= 1; oz++) {
|
||||||
for (int ox = -1; ox <= 1; ox++) {
|
for (int ox = -1; ox <= 1; ox++) {
|
||||||
|
|||||||
@ -143,7 +143,9 @@ static bool load_world_content(Engine& engine, const fs::path& folder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void load_world(
|
static void load_world(
|
||||||
Engine& engine, const std::shared_ptr<WorldFiles>& worldFiles
|
Engine& engine,
|
||||||
|
const std::shared_ptr<WorldFiles>& worldFiles,
|
||||||
|
int64_t localPlayer
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
auto content = engine.getContent();
|
auto content = engine.getContent();
|
||||||
@ -151,7 +153,7 @@ static void load_world(
|
|||||||
auto& settings = engine.getSettings();
|
auto& settings = engine.getSettings();
|
||||||
|
|
||||||
auto level = World::load(worldFiles, settings, *content, packs);
|
auto level = World::load(worldFiles, settings, *content, packs);
|
||||||
engine.onWorldOpen(std::move(level));
|
engine.onWorldOpen(std::move(level), localPlayer);
|
||||||
} catch (const world_load_error& error) {
|
} catch (const world_load_error& error) {
|
||||||
guiutil::alert(
|
guiutil::alert(
|
||||||
engine,
|
engine,
|
||||||
@ -233,7 +235,7 @@ void EngineController::openWorld(const std::string& name, bool confirmConvert) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
load_world(engine, std::move(worldFiles));
|
load_world(engine, std::move(worldFiles), localPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t str2seed(const std::string& seedstr) {
|
inline uint64_t str2seed(const std::string& seedstr) {
|
||||||
@ -279,9 +281,13 @@ void EngineController::createWorld(
|
|||||||
engine.getContentPacks()
|
engine.getContentPacks()
|
||||||
);
|
);
|
||||||
if (!engine.isHeadless()) {
|
if (!engine.isHeadless()) {
|
||||||
level->players->create();
|
level->players->create(localPlayer);
|
||||||
}
|
}
|
||||||
engine.onWorldOpen(std::move(level));
|
engine.onWorldOpen(std::move(level), localPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineController::setLocalPlayer(int64_t player) {
|
||||||
|
localPlayer = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineController::reopenWorld(World* world) {
|
void EngineController::reopenWorld(World* world) {
|
||||||
|
|||||||
@ -12,6 +12,7 @@ class LevelController;
|
|||||||
class EngineController {
|
class EngineController {
|
||||||
Engine& engine;
|
Engine& engine;
|
||||||
|
|
||||||
|
int64_t localPlayer = -1;
|
||||||
void onMissingContent(const std::shared_ptr<ContentReport>& report);
|
void onMissingContent(const std::shared_ptr<ContentReport>& report);
|
||||||
public:
|
public:
|
||||||
EngineController(Engine& engine);
|
EngineController(Engine& engine);
|
||||||
@ -37,5 +38,7 @@ public:
|
|||||||
const std::string& generatorID
|
const std::string& generatorID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void setLocalPlayer(int64_t player);
|
||||||
|
|
||||||
void reopenWorld(World* world);
|
void reopenWorld(World* world);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -50,6 +50,10 @@ LevelController::LevelController(
|
|||||||
do {
|
do {
|
||||||
confirmed = 0;
|
confirmed = 0;
|
||||||
for (const auto& [_, player] : *level->players) {
|
for (const auto& [_, player] : *level->players) {
|
||||||
|
if (!player->isLoadingChunks()) {
|
||||||
|
confirmed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
glm::vec3 position = player->getPosition();
|
glm::vec3 position = player->getPosition();
|
||||||
player->chunks->configure(
|
player->chunks->configure(
|
||||||
std::floor(position.x), std::floor(position.z), 1
|
std::floor(position.x), std::floor(position.z), 1
|
||||||
@ -66,6 +70,11 @@ LevelController::LevelController(
|
|||||||
|
|
||||||
void LevelController::update(float delta, bool pause) {
|
void LevelController::update(float delta, bool pause) {
|
||||||
for (const auto& [_, player] : *level->players) {
|
for (const auto& [_, player] : *level->players) {
|
||||||
|
if (player->isSuspended()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
player->rotationInterpolation.updateTimer(delta);
|
||||||
|
player->updateEntity();
|
||||||
glm::vec3 position = player->getPosition();
|
glm::vec3 position = player->getPosition();
|
||||||
player->chunks->configure(
|
player->chunks->configure(
|
||||||
position.x,
|
position.x,
|
||||||
@ -85,6 +94,9 @@ void LevelController::update(float delta, bool pause) {
|
|||||||
level->entities->updatePhysics(delta);
|
level->entities->updatePhysics(delta);
|
||||||
level->entities->update(delta);
|
level->entities->update(delta);
|
||||||
for (const auto& [_, player] : *level->players) {
|
for (const auto& [_, player] : *level->players) {
|
||||||
|
if (player->isSuspended()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (playerTickClock.update(delta)) {
|
if (playerTickClock.update(delta)) {
|
||||||
if (player->getId() % playerTickClock.getParts() ==
|
if (player->getId() % playerTickClock.getParts() ==
|
||||||
playerTickClock.getPart()) {
|
playerTickClock.getPart()) {
|
||||||
|
|||||||
@ -54,7 +54,7 @@ void CameraControl::refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CameraControl::updateMouse(PlayerInput& input) {
|
void CameraControl::updateMouse(PlayerInput& input) {
|
||||||
glm::vec3& rotation = player.rotation;
|
glm::vec3 rotation = player.getRotation();
|
||||||
|
|
||||||
float sensitivity =
|
float sensitivity =
|
||||||
(input.zoom ? settings.sensitivity.get() / 4.f
|
(input.zoom ? settings.sensitivity.get() / 4.f
|
||||||
@ -75,6 +75,8 @@ void CameraControl::updateMouse(PlayerInput& input) {
|
|||||||
rotation.x += 360.f;
|
rotation.x += 360.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player.setRotation(rotation);
|
||||||
|
|
||||||
camera->rotation = glm::mat4(1.0f);
|
camera->rotation = glm::mat4(1.0f);
|
||||||
camera->rotate(
|
camera->rotate(
|
||||||
glm::radians(rotation.y),
|
glm::radians(rotation.y),
|
||||||
@ -170,8 +172,8 @@ void CameraControl::update(
|
|||||||
switchCamera();
|
switchCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& spCamera = player.spCamera;
|
const auto& spCamera = player.spCamera;
|
||||||
auto& tpCamera = player.tpCamera;
|
const auto& tpCamera = player.tpCamera;
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
@ -305,7 +307,6 @@ void PlayerController::resetKeyboard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::updatePlayer(float delta) {
|
void PlayerController::updatePlayer(float delta) {
|
||||||
player.updateEntity();
|
|
||||||
player.updateInput(input, delta);
|
player.updateInput(input, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,15 +318,15 @@ static int determine_rotation(
|
|||||||
if (name == "pipe") {
|
if (name == "pipe") {
|
||||||
if (norm.x < 0.0f)
|
if (norm.x < 0.0f)
|
||||||
return BLOCK_DIR_WEST;
|
return BLOCK_DIR_WEST;
|
||||||
else if (norm.x > 0.0f)
|
if (norm.x > 0.0f)
|
||||||
return BLOCK_DIR_EAST;
|
return BLOCK_DIR_EAST;
|
||||||
else if (norm.y > 0.0f)
|
if (norm.y > 0.0f)
|
||||||
return BLOCK_DIR_UP;
|
return BLOCK_DIR_UP;
|
||||||
else if (norm.y < 0.0f)
|
if (norm.y < 0.0f)
|
||||||
return BLOCK_DIR_DOWN;
|
return BLOCK_DIR_DOWN;
|
||||||
else if (norm.z > 0.0f)
|
if (norm.z > 0.0f)
|
||||||
return BLOCK_DIR_NORTH;
|
return BLOCK_DIR_NORTH;
|
||||||
else if (norm.z < 0.0f)
|
if (norm.z < 0.0f)
|
||||||
return BLOCK_DIR_SOUTH;
|
return BLOCK_DIR_SOUTH;
|
||||||
} else if (name == "pane") {
|
} else if (name == "pane") {
|
||||||
if (abs(camDir.x) > abs(camDir.z)) {
|
if (abs(camDir.x) > abs(camDir.z)) {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ static int l_set_vel(lua::State* L) {
|
|||||||
|
|
||||||
static int l_is_enabled(lua::State* L) {
|
static int l_is_enabled(lua::State* L) {
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
lua::pushboolean(L, entity->getRigidbody().enabled);
|
return lua::pushboolean(L, entity->getRigidbody().enabled);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,6 +130,22 @@ static int l_set_color(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_is_interpolated(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
auto& skeleton = entity->getSkeleton();
|
||||||
|
return lua::pushboolean(L, skeleton.interpolation.isEnabled());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_interpolated(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
auto& skeleton = entity->getSkeleton();
|
||||||
|
skeleton.interpolation.setEnabled(lua::toboolean(L, 2));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg skeletonlib[] = {
|
const luaL_Reg skeletonlib[] = {
|
||||||
{"get_model", lua::wrap<l_get_model>},
|
{"get_model", lua::wrap<l_get_model>},
|
||||||
{"set_model", lua::wrap<l_set_model>},
|
{"set_model", lua::wrap<l_set_model>},
|
||||||
@ -142,4 +158,6 @@ const luaL_Reg skeletonlib[] = {
|
|||||||
{"set_visible", lua::wrap<l_set_visible>},
|
{"set_visible", lua::wrap<l_set_visible>},
|
||||||
{"get_color", lua::wrap<l_get_color>},
|
{"get_color", lua::wrap<l_get_color>},
|
||||||
{"set_color", lua::wrap<l_set_color>},
|
{"set_color", lua::wrap<l_set_color>},
|
||||||
|
{"is_interpolated", lua::wrap<l_is_interpolated>},
|
||||||
|
{"set_interpolated", lua::wrap<l_set_interpolated>},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}};
|
||||||
|
|||||||
@ -198,8 +198,8 @@ static int l_tpack(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const luaL_Reg byteutillib[] = {
|
const luaL_Reg byteutillib[] = {
|
||||||
{"pack", l_pack},
|
{"pack", lua::wrap<l_pack>},
|
||||||
{"tpack", l_tpack},
|
{"tpack", lua::wrap<l_tpack>},
|
||||||
{"unpack", l_unpack},
|
{"unpack", lua::wrap<l_unpack>},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -55,10 +55,15 @@ static int l_new_world(lua::State* L) {
|
|||||||
auto name = lua::require_string(L, 1);
|
auto name = lua::require_string(L, 1);
|
||||||
auto seed = lua::require_string(L, 2);
|
auto seed = lua::require_string(L, 2);
|
||||||
auto generator = lua::require_string(L, 3);
|
auto generator = lua::require_string(L, 3);
|
||||||
|
int64_t localPlayer = 0;
|
||||||
|
if (lua::gettop(L) >= 4) {
|
||||||
|
localPlayer = lua::tointeger(L, 4);
|
||||||
|
}
|
||||||
if (level != nullptr) {
|
if (level != nullptr) {
|
||||||
throw std::runtime_error("world must be closed before");
|
throw std::runtime_error("world must be closed before");
|
||||||
}
|
}
|
||||||
auto controller = engine->getController();
|
auto controller = engine->getController();
|
||||||
|
controller->setLocalPlayer(localPlayer);
|
||||||
controller->createWorld(name, seed, generator);
|
controller->createWorld(name, seed, generator);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -71,6 +76,7 @@ static int l_open_world(lua::State* L) {
|
|||||||
throw std::runtime_error("world must be closed before");
|
throw std::runtime_error("world must be closed before");
|
||||||
}
|
}
|
||||||
auto controller = engine->getController();
|
auto controller = engine->getController();
|
||||||
|
controller->setLocalPlayer(0);
|
||||||
controller->openWorld(name, false);
|
controller->openWorld(name, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,7 +54,7 @@ static int l_get_def(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_show(lua::State* L) {
|
static int l_spawn(lua::State* L) {
|
||||||
auto level = controller->getLevel();
|
auto level = controller->getLevel();
|
||||||
auto defname = lua::tostring(L, 1);
|
auto defname = lua::tostring(L, 1);
|
||||||
auto& def = content->entities.require(defname);
|
auto& def = content->entities.require(defname);
|
||||||
@ -81,6 +81,15 @@ static int l_get_skeleton(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_get_player(lua::State* L) {
|
||||||
|
entityid_t eid = lua::touinteger(L, 1);
|
||||||
|
auto level = controller->getLevel();
|
||||||
|
if (auto entity = level->entities->get(eid)) {
|
||||||
|
return lua::pushinteger(L, entity->getPlayer());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int l_set_skeleton(lua::State* L) {
|
static int l_set_skeleton(lua::State* L) {
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
std::string skeletonName = lua::require_string(L, 2);
|
std::string skeletonName = lua::require_string(L, 2);
|
||||||
@ -221,10 +230,11 @@ const luaL_Reg entitylib[] = {
|
|||||||
{"def_hitbox", lua::wrap<l_def_hitbox>},
|
{"def_hitbox", lua::wrap<l_def_hitbox>},
|
||||||
{"get_def", lua::wrap<l_get_def>},
|
{"get_def", lua::wrap<l_get_def>},
|
||||||
{"defs_count", lua::wrap<l_defs_count>},
|
{"defs_count", lua::wrap<l_defs_count>},
|
||||||
{"spawn", lua::wrap<l_show>},
|
{"spawn", lua::wrap<l_spawn>},
|
||||||
{"despawn", lua::wrap<l_despawn>},
|
{"despawn", lua::wrap<l_despawn>},
|
||||||
{"get_skeleton", lua::wrap<l_get_skeleton>},
|
{"get_skeleton", lua::wrap<l_get_skeleton>},
|
||||||
{"set_skeleton", lua::wrap<l_set_skeleton>},
|
{"set_skeleton", lua::wrap<l_set_skeleton>},
|
||||||
|
{"get_player", lua::wrap<l_get_player>},
|
||||||
{"get_all_in_box", lua::wrap<l_get_all_in_box>},
|
{"get_all_in_box", lua::wrap<l_get_all_in_box>},
|
||||||
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
|
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
|
||||||
{"raycast", lua::wrap<l_raycast>},
|
{"raycast", lua::wrap<l_raycast>},
|
||||||
|
|||||||
@ -101,6 +101,12 @@ static int l_node_destruct(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_node_reposition(lua::State* L) {
|
||||||
|
auto docnode = get_document_node(L);
|
||||||
|
docnode.node->reposition();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int l_container_clear(lua::State* L) {
|
static int l_container_clear(lua::State* L) {
|
||||||
auto node = get_document_node(L, 1);
|
auto node = get_document_node(L, 1);
|
||||||
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
|
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
|
||||||
@ -328,6 +334,10 @@ static int p_get_destruct(UINode*, lua::State* L) {
|
|||||||
return lua::pushcfunction(L, lua::wrap<l_node_destruct>);
|
return lua::pushcfunction(L, lua::wrap<l_node_destruct>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int p_get_reposition(UINode*, lua::State* L) {
|
||||||
|
return lua::pushcfunction(L, lua::wrap<l_node_reposition>);
|
||||||
|
}
|
||||||
|
|
||||||
static int p_get_clear(UINode* node, lua::State* L) {
|
static int p_get_clear(UINode* node, lua::State* L) {
|
||||||
if (dynamic_cast<Container*>(node)) {
|
if (dynamic_cast<Container*>(node)) {
|
||||||
return lua::pushcfunction(L, lua::wrap<l_container_clear>);
|
return lua::pushcfunction(L, lua::wrap<l_container_clear>);
|
||||||
@ -424,6 +434,7 @@ static int l_gui_getattr(lua::State* L) {
|
|||||||
{"moveInto", p_move_into},
|
{"moveInto", p_move_into},
|
||||||
{"add", p_get_add},
|
{"add", p_get_add},
|
||||||
{"destruct", p_get_destruct},
|
{"destruct", p_get_destruct},
|
||||||
|
{"reposition", p_get_reposition},
|
||||||
{"clear", p_get_clear},
|
{"clear", p_get_clear},
|
||||||
{"setInterval", p_set_interval},
|
{"setInterval", p_set_interval},
|
||||||
{"placeholder", p_get_placeholder},
|
{"placeholder", p_get_placeholder},
|
||||||
|
|||||||
@ -170,6 +170,11 @@ static int l_set_debug_cheats(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_set_allow_pause(lua::State* L) {
|
||||||
|
hud->setAllowPause(lua::toboolean(L, 1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg hudlib[] = {
|
const luaL_Reg hudlib[] = {
|
||||||
{"open_inventory", lua::wrap<l_open_inventory>},
|
{"open_inventory", lua::wrap<l_open_inventory>},
|
||||||
{"close_inventory", lua::wrap<l_close_inventory>},
|
{"close_inventory", lua::wrap<l_close_inventory>},
|
||||||
@ -187,5 +192,6 @@ const luaL_Reg hudlib[] = {
|
|||||||
{"_is_content_access", lua::wrap<l_is_content_access>},
|
{"_is_content_access", lua::wrap<l_is_content_access>},
|
||||||
{"_set_content_access", lua::wrap<l_set_content_access>},
|
{"_set_content_access", lua::wrap<l_set_content_access>},
|
||||||
{"_set_debug_cheats", lua::wrap<l_set_debug_cheats>},
|
{"_set_debug_cheats", lua::wrap<l_set_debug_cheats>},
|
||||||
|
{"set_allow_pause", lua::wrap<l_set_allow_pause>},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -45,11 +45,6 @@ static int l_add_callback(lua::State* L) {
|
|||||||
handler = Events::keyCallbacks[key].add(actual_callback);
|
handler = Events::keyCallbacks[key].add(actual_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& bind = Events::bindings.find(bindname);
|
|
||||||
if (bind == Events::bindings.end()) {
|
|
||||||
throw std::runtime_error("unknown binding " + util::quote(bindname));
|
|
||||||
}
|
|
||||||
auto callback = [=]() -> bool {
|
auto callback = [=]() -> bool {
|
||||||
if (!scripting::engine->getGUI()->isFocusCaught()) {
|
if (!scripting::engine->getGUI()->isFocusCaught()) {
|
||||||
return actual_callback();
|
return actual_callback();
|
||||||
@ -57,6 +52,10 @@ static int l_add_callback(lua::State* L) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if (handler == nullptr) {
|
if (handler == nullptr) {
|
||||||
|
const auto& bind = Events::bindings.find(bindname);
|
||||||
|
if (bind == Events::bindings.end()) {
|
||||||
|
throw std::runtime_error("unknown binding " + util::quote(bindname));
|
||||||
|
}
|
||||||
handler = bind->second.onactived.add(callback);
|
handler = bind->second.onactived.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -73,7 +73,7 @@ static int l_connect(lua::State* L) {
|
|||||||
static int l_close(lua::State* L) {
|
static int l_close(lua::State* L) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto connection = engine->getNetwork().getConnection(id)) {
|
if (auto connection = engine->getNetwork().getConnection(id)) {
|
||||||
connection->close();
|
connection->close(true);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -89,7 +89,8 @@ static int l_closeserver(lua::State* L) {
|
|||||||
static int l_send(lua::State* L) {
|
static int l_send(lua::State* L) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
auto connection = engine->getNetwork().getConnection(id);
|
auto connection = engine->getNetwork().getConnection(id);
|
||||||
if (connection == nullptr) {
|
if (connection == nullptr ||
|
||||||
|
connection->getState() == network::ConnectionState::CLOSED) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (lua::istable(L, 2)) {
|
if (lua::istable(L, 2)) {
|
||||||
@ -167,7 +168,9 @@ static int l_is_alive(lua::State* L) {
|
|||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto connection = engine->getNetwork().getConnection(id)) {
|
if (auto connection = engine->getNetwork().getConnection(id)) {
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
L, connection->getState() != network::ConnectionState::CLOSED
|
L,
|
||||||
|
connection->getState() != network::ConnectionState::CLOSED ||
|
||||||
|
connection->available() > 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return lua::pushboolean(L, false);
|
return lua::pushboolean(L, false);
|
||||||
|
|||||||
@ -52,6 +52,7 @@ static int l_set_vel(lua::State* L) {
|
|||||||
auto x = lua::tonumber(L, 2);
|
auto x = lua::tonumber(L, 2);
|
||||||
auto y = lua::tonumber(L, 3);
|
auto y = lua::tonumber(L, 3);
|
||||||
auto z = lua::tonumber(L, 4);
|
auto z = lua::tonumber(L, 4);
|
||||||
|
|
||||||
if (auto hitbox = player->getHitbox()) {
|
if (auto hitbox = player->getHitbox()) {
|
||||||
hitbox->velocity = glm::vec3(x, y, z);
|
hitbox->velocity = glm::vec3(x, y, z);
|
||||||
}
|
}
|
||||||
@ -60,7 +61,7 @@ static int l_set_vel(lua::State* L) {
|
|||||||
|
|
||||||
static int l_get_rot(lua::State* L) {
|
static int l_get_rot(lua::State* L) {
|
||||||
if (auto player = get_player(L, 1)) {
|
if (auto player = get_player(L, 1)) {
|
||||||
return lua::pushvec_stack(L, player->rotation);
|
return lua::pushvec_stack(L, player->getRotation(lua::toboolean(L, 2)));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -70,7 +71,7 @@ static int l_set_rot(lua::State* L) {
|
|||||||
if (!player) {
|
if (!player) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
glm::vec3& rotation = player->rotation;
|
glm::vec3 rotation = player->getRotation();
|
||||||
|
|
||||||
auto x = lua::tonumber(L, 2);
|
auto x = lua::tonumber(L, 2);
|
||||||
auto y = lua::tonumber(L, 3);
|
auto y = lua::tonumber(L, 3);
|
||||||
@ -81,6 +82,7 @@ static int l_set_rot(lua::State* L) {
|
|||||||
rotation.x = x;
|
rotation.x = x;
|
||||||
rotation.y = y;
|
rotation.y = y;
|
||||||
rotation.z = z;
|
rotation.z = z;
|
||||||
|
player->setRotation(rotation);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,15 +274,33 @@ static int l_set_name(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_create(lua::State* L) {
|
static int l_create(lua::State* L) {
|
||||||
auto player = level->players->create();
|
int64_t playerId = Players::NONE;
|
||||||
if (lua::isstring(L, 1)) {
|
if (lua::gettop(L) >= 2) {
|
||||||
player->setName(lua::require_string(L, 1));
|
playerId = lua::tointeger(L, 2);
|
||||||
}
|
}
|
||||||
|
auto player = level->players->create(playerId);
|
||||||
|
player->setName(lua::require_string(L, 1));
|
||||||
return lua::pushinteger(L, player->getId());
|
return lua::pushinteger(L, player->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_delete(lua::State* L) {
|
static int l_delete(lua::State* L) {
|
||||||
level->players->remove(lua::tointeger(L, 1));
|
auto id = lua::tointeger(L, 1);
|
||||||
|
level->players->suspend(id);
|
||||||
|
level->players->remove(id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_is_suspended(lua::State* L) {
|
||||||
|
if (auto player = get_player(L, 1)) {
|
||||||
|
return lua::pushboolean(L, player->isSuspended());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_suspended(lua::State* L) {
|
||||||
|
if (auto player = get_player(L, 1)) {
|
||||||
|
player->setSuspended(lua::toboolean(L, 2));
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +313,8 @@ const luaL_Reg playerlib[] = {
|
|||||||
{"set_rot", lua::wrap<l_set_rot>},
|
{"set_rot", lua::wrap<l_set_rot>},
|
||||||
{"get_dir", lua::wrap<l_get_dir>},
|
{"get_dir", lua::wrap<l_get_dir>},
|
||||||
{"get_inventory", lua::wrap<l_get_inv>},
|
{"get_inventory", lua::wrap<l_get_inv>},
|
||||||
|
{"is_suspended", lua::wrap<l_is_suspended>},
|
||||||
|
{"set_suspended", lua::wrap<l_set_suspended>},
|
||||||
{"is_flight", lua::wrap<l_is_flight>},
|
{"is_flight", lua::wrap<l_is_flight>},
|
||||||
{"set_flight", lua::wrap<l_set_flight>},
|
{"set_flight", lua::wrap<l_set_flight>},
|
||||||
{"is_noclip", lua::wrap<l_is_noclip>},
|
{"is_noclip", lua::wrap<l_is_noclip>},
|
||||||
|
|||||||
@ -160,13 +160,13 @@ static void integrate_chunk_client(Chunk& chunk) {
|
|||||||
static int l_set_chunk_data(lua::State* L) {
|
static int l_set_chunk_data(lua::State* L) {
|
||||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||||
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
|
auto buffer = lua::require_bytearray(L, 3);
|
||||||
auto chunk = level->chunks->getChunk(x, z);
|
auto chunk = level->chunks->getChunk(x, z);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
return 0;
|
return lua::pushboolean(L, false);
|
||||||
}
|
}
|
||||||
compressed_chunks::decode(
|
compressed_chunks::decode(
|
||||||
*chunk, buffer->data().data(), buffer->data().size()
|
*chunk, buffer.data(), buffer.size()
|
||||||
);
|
);
|
||||||
if (controller->getChunksController()->lighting == nullptr) {
|
if (controller->getChunksController()->lighting == nullptr) {
|
||||||
return lua::pushboolean(L, true);
|
return lua::pushboolean(L, true);
|
||||||
|
|||||||
@ -133,6 +133,13 @@ dv::value lua::tovalue(State* L, int idx) {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case LUA_TUSERDATA: {
|
||||||
|
if (auto bytes = touserdata<LuaBytearray>(L, idx)) {
|
||||||
|
const auto& data = bytes->data();
|
||||||
|
return std::make_shared<dv::objects::Bytes>(data.data(), data.size());
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"lua type " + std::string(lua_typename(L, type)) +
|
"lua type " + std::string(lua_typename(L, type)) +
|
||||||
@ -270,12 +277,13 @@ scripting::common_func lua::create_lambda(State* L) {
|
|||||||
return [=](const std::vector<dv::value>& args) -> dv::value {
|
return [=](const std::vector<dv::value>& args) -> dv::value {
|
||||||
if (!get_from(L, LAMBDAS_TABLE, *funcptr, false))
|
if (!get_from(L, LAMBDAS_TABLE, *funcptr, false))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
int top = gettop(L) + 1;
|
int top = gettop(L) - 1;
|
||||||
for (const auto& arg : args) {
|
for (const auto& arg : args) {
|
||||||
pushvalue(L, arg);
|
pushvalue(L, arg);
|
||||||
}
|
}
|
||||||
if (call(L, args.size(), 1)) {
|
if (call(L, args.size(), 1)) {
|
||||||
int nres = gettop(L) - top;
|
int nres = gettop(L) - top;
|
||||||
|
assert(nres >= 0);
|
||||||
if (nres) {
|
if (nres) {
|
||||||
auto result = tovalue(L, -1);
|
auto result = tovalue(L, -1);
|
||||||
pop(L, 1 + nres);
|
pop(L, 1 + nres);
|
||||||
|
|||||||
@ -19,6 +19,7 @@ namespace lua {
|
|||||||
int userdata_destructor(lua::State* L);
|
int userdata_destructor(lua::State* L);
|
||||||
|
|
||||||
std::string env_name(int env);
|
std::string env_name(int env);
|
||||||
|
void dump_stack(lua::State*);
|
||||||
|
|
||||||
inline bool getglobal(lua::State* L, const std::string& name) {
|
inline bool getglobal(lua::State* L, const std::string& name) {
|
||||||
lua_getglobal(L, name.c_str());
|
lua_getglobal(L, name.c_str());
|
||||||
@ -208,7 +209,7 @@ namespace lua {
|
|||||||
return lua_isnumber(L, idx);
|
return lua_isnumber(L, idx);
|
||||||
}
|
}
|
||||||
inline bool isstring(lua::State* L, int idx) {
|
inline bool isstring(lua::State* L, int idx) {
|
||||||
return lua_isstring(L, idx);
|
return lua_type(L, idx) == LUA_TSTRING;
|
||||||
}
|
}
|
||||||
inline bool istable(lua::State* L, int idx) {
|
inline bool istable(lua::State* L, int idx) {
|
||||||
return lua_istable(L, idx);
|
return lua_istable(L, idx);
|
||||||
@ -226,6 +227,11 @@ namespace lua {
|
|||||||
return lua_toboolean(L, idx);
|
return lua_toboolean(L, idx);
|
||||||
}
|
}
|
||||||
inline lua::Integer tointeger(lua::State* L, int idx) {
|
inline lua::Integer tointeger(lua::State* L, int idx) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (lua_type(L, idx) == LUA_TSTRING) {
|
||||||
|
throw std::runtime_error("integer expected, got string");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return lua_tointeger(L, idx);
|
return lua_tointeger(L, idx);
|
||||||
}
|
}
|
||||||
inline uint64_t touinteger(lua::State* L, int idx) {
|
inline uint64_t touinteger(lua::State* L, int idx) {
|
||||||
@ -236,6 +242,11 @@ namespace lua {
|
|||||||
return static_cast<uint64_t>(val);
|
return static_cast<uint64_t>(val);
|
||||||
}
|
}
|
||||||
inline lua::Number tonumber(lua::State* L, int idx) {
|
inline lua::Number tonumber(lua::State* L, int idx) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) {
|
||||||
|
throw std::runtime_error("integer expected");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return lua_tonumber(L, idx);
|
return lua_tonumber(L, idx);
|
||||||
}
|
}
|
||||||
inline const char* tostring(lua::State* L, int idx) {
|
inline const char* tostring(lua::State* L, int idx) {
|
||||||
@ -588,7 +599,6 @@ namespace lua {
|
|||||||
}
|
}
|
||||||
int create_environment(lua::State*, int parent);
|
int create_environment(lua::State*, int parent);
|
||||||
void remove_environment(lua::State*, int id);
|
void remove_environment(lua::State*, int id);
|
||||||
void dump_stack(lua::State*);
|
|
||||||
|
|
||||||
inline void close(lua::State* L) {
|
inline void close(lua::State* L) {
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
|
|||||||
@ -400,10 +400,15 @@ public:
|
|||||||
return readBatch.size();
|
return readBatch.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() override {
|
void close(bool discardAll=false) override {
|
||||||
if (state != ConnectionState::CLOSED) {
|
{
|
||||||
shutdown(descriptor, 2);
|
std::lock_guard lock(mutex);
|
||||||
closesocket(descriptor);
|
readBatch.clear();
|
||||||
|
|
||||||
|
if (state != ConnectionState::CLOSED) {
|
||||||
|
shutdown(descriptor, 2);
|
||||||
|
closesocket(descriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (thread) {
|
if (thread) {
|
||||||
thread->join();
|
thread->join();
|
||||||
|
|||||||
@ -49,7 +49,7 @@ namespace network {
|
|||||||
virtual void connect(runnable callback) = 0;
|
virtual void connect(runnable callback) = 0;
|
||||||
virtual int recv(char* buffer, size_t length) = 0;
|
virtual int recv(char* buffer, size_t length) = 0;
|
||||||
virtual int send(const char* buffer, size_t length) = 0;
|
virtual int send(const char* buffer, size_t length) = 0;
|
||||||
virtual void close() = 0;
|
virtual void close(bool discardAll=false) = 0;
|
||||||
virtual int available() = 0;
|
virtual int available() = 0;
|
||||||
|
|
||||||
virtual size_t pullUpload() = 0;
|
virtual size_t pullUpload() = 0;
|
||||||
|
|||||||
@ -38,6 +38,18 @@ void Transform::refresh() {
|
|||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entity::setInterpolatedPosition(const glm::vec3& position) {
|
||||||
|
getSkeleton().interpolation.refresh(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 Entity::getInterpolatedPosition() const {
|
||||||
|
const auto& skeleton = getSkeleton();
|
||||||
|
if (skeleton.interpolation.isEnabled()) {
|
||||||
|
return skeleton.interpolation.getCurrent();
|
||||||
|
}
|
||||||
|
return getTransform().pos;
|
||||||
|
}
|
||||||
|
|
||||||
void Entity::destroy() {
|
void Entity::destroy() {
|
||||||
if (isValid()) {
|
if (isValid()) {
|
||||||
entities.despawn(id);
|
entities.despawn(id);
|
||||||
@ -561,11 +573,14 @@ void Entities::render(
|
|||||||
if (transform.dirty) {
|
if (transform.dirty) {
|
||||||
transform.refresh();
|
transform.refresh();
|
||||||
}
|
}
|
||||||
|
if (skeleton.interpolation.isEnabled()) {
|
||||||
|
skeleton.interpolation.updateTimer(delta);
|
||||||
|
}
|
||||||
const auto& pos = transform.pos;
|
const auto& pos = transform.pos;
|
||||||
const auto& size = transform.size;
|
const auto& size = transform.size;
|
||||||
if (!frustum || frustum->isBoxVisible(pos - size, pos + size)) {
|
if (!frustum || frustum->isBoxVisible(pos - size, pos + size)) {
|
||||||
const auto* rigConfig = skeleton.config;
|
const auto* rigConfig = skeleton.config;
|
||||||
rigConfig->render(assets, batch, skeleton, transform.combined);
|
rigConfig->render(assets, batch, skeleton, transform.combined, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@ struct EntityId {
|
|||||||
entityid_t uid;
|
entityid_t uid;
|
||||||
const EntityDef& def;
|
const EntityDef& def;
|
||||||
bool destroyFlag = false;
|
bool destroyFlag = false;
|
||||||
|
int64_t player = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Transform {
|
struct Transform {
|
||||||
@ -161,6 +162,18 @@ public:
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t getPlayer() const {
|
||||||
|
return registry.get<EntityId>(entity).player;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPlayer(int64_t id) {
|
||||||
|
registry.get<EntityId>(entity).player = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInterpolatedPosition(const glm::vec3& position);
|
||||||
|
|
||||||
|
glm::vec3 getInterpolatedPosition() const;
|
||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -65,8 +65,14 @@ void Player::updateEntity() {
|
|||||||
if (eid == 0) {
|
if (eid == 0) {
|
||||||
auto& def = level.content.entities.require("base:player");
|
auto& def = level.content.entities.require("base:player");
|
||||||
eid = level.entities->spawn(def, getPosition());
|
eid = level.entities->spawn(def, getPosition());
|
||||||
|
if (auto entity = level.entities->get(eid)) {
|
||||||
|
entity->setPlayer(id);
|
||||||
|
}
|
||||||
} else if (auto entity = level.entities->get(eid)) {
|
} else if (auto entity = level.entities->get(eid)) {
|
||||||
position = entity->getTransform().pos;
|
position = entity->getTransform().pos;
|
||||||
|
if (auto entity = level.entities->get(eid)) {
|
||||||
|
entity->setPlayer(id);
|
||||||
|
}
|
||||||
} else if (chunks->getChunkByVoxel(position)) {
|
} else if (chunks->getChunkByVoxel(position)) {
|
||||||
logger.error() << "player entity despawned or deleted; "
|
logger.error() << "player entity despawned or deleted; "
|
||||||
"will be respawned";
|
"will be respawned";
|
||||||
@ -164,23 +170,9 @@ void Player::postUpdate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: ERASE & FORGET
|
||||||
auto& skeleton = entity->getSkeleton();
|
auto& skeleton = entity->getSkeleton();
|
||||||
|
|
||||||
skeleton.visible = currentCamera != fpCamera;
|
skeleton.visible = currentCamera != fpCamera;
|
||||||
|
|
||||||
auto body = skeleton.config->find("body");
|
|
||||||
auto head = skeleton.config->find("head");
|
|
||||||
|
|
||||||
if (body) {
|
|
||||||
skeleton.pose.matrices[body->getIndex()] = glm::rotate(
|
|
||||||
glm::mat4(1.0f), glm::radians(rotation.x), glm::vec3(0, 1, 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (head) {
|
|
||||||
skeleton.pose.matrices[head->getIndex()] = glm::rotate(
|
|
||||||
glm::mat4(1.0f), glm::radians(rotation.y), glm::vec3(1, 0, 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::teleport(glm::vec3 position) {
|
void Player::teleport(glm::vec3 position) {
|
||||||
@ -189,6 +181,7 @@ void Player::teleport(glm::vec3 position) {
|
|||||||
if (auto entity = level.entities->get(eid)) {
|
if (auto entity = level.entities->get(eid)) {
|
||||||
entity->getRigidbody().hitbox.position = position;
|
entity->getRigidbody().hitbox.position = position;
|
||||||
entity->getTransform().setPos(position);
|
entity->getTransform().setPos(position);
|
||||||
|
entity->setInterpolatedPosition(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,6 +217,14 @@ float Player::getSpeed() const {
|
|||||||
return speed;
|
return speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Player::isSuspended() const {
|
||||||
|
return suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setSuspended(bool flag) {
|
||||||
|
suspended = flag;
|
||||||
|
}
|
||||||
|
|
||||||
bool Player::isFlight() const {
|
bool Player::isFlight() const {
|
||||||
return flight;
|
return flight;
|
||||||
}
|
}
|
||||||
@ -296,6 +297,18 @@ glm::vec3 Player::getSpawnPoint() const {
|
|||||||
return spawnpoint;
|
return spawnpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 Player::getRotation(bool interpolated) const {
|
||||||
|
if (interpolated) {
|
||||||
|
return rotationInterpolation.getCurrent();
|
||||||
|
}
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setRotation(const glm::vec3& rotation) {
|
||||||
|
this->rotation = rotation;
|
||||||
|
rotationInterpolation.refresh(rotation);
|
||||||
|
}
|
||||||
|
|
||||||
dv::value Player::serialize() const {
|
dv::value Player::serialize() const {
|
||||||
auto root = dv::object();
|
auto root = dv::object();
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "interfaces/Serializable.hpp"
|
#include "interfaces/Serializable.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
|
#include "util/Interpolation.hpp"
|
||||||
|
|
||||||
class Chunks;
|
class Chunks;
|
||||||
class Camera;
|
class Camera;
|
||||||
@ -47,6 +48,7 @@ class Player : public Serializable {
|
|||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::vec3 spawnpoint {};
|
glm::vec3 spawnpoint {};
|
||||||
std::shared_ptr<Inventory> inventory;
|
std::shared_ptr<Inventory> inventory;
|
||||||
|
bool suspended = false;
|
||||||
bool flight = false;
|
bool flight = false;
|
||||||
bool noclip = false;
|
bool noclip = false;
|
||||||
bool infiniteItems = true;
|
bool infiniteItems = true;
|
||||||
@ -54,11 +56,15 @@ class Player : public Serializable {
|
|||||||
bool loadingChunks = true;
|
bool loadingChunks = true;
|
||||||
entityid_t eid;
|
entityid_t eid;
|
||||||
entityid_t selectedEid = 0;
|
entityid_t selectedEid = 0;
|
||||||
|
|
||||||
|
glm::vec3 rotation {};
|
||||||
public:
|
public:
|
||||||
|
util::VecInterpolation<3, float, true> rotationInterpolation {true};
|
||||||
|
|
||||||
std::unique_ptr<Chunks> chunks;
|
std::unique_ptr<Chunks> chunks;
|
||||||
std::shared_ptr<Camera> fpCamera, spCamera, tpCamera;
|
std::shared_ptr<Camera> fpCamera, spCamera, tpCamera;
|
||||||
std::shared_ptr<Camera> currentCamera;
|
std::shared_ptr<Camera> currentCamera;
|
||||||
glm::vec3 rotation {};
|
|
||||||
CursorSelection selection {};
|
CursorSelection selection {};
|
||||||
|
|
||||||
Player(
|
Player(
|
||||||
@ -85,6 +91,9 @@ public:
|
|||||||
int getChosenSlot() const;
|
int getChosenSlot() const;
|
||||||
float getSpeed() const;
|
float getSpeed() const;
|
||||||
|
|
||||||
|
bool isSuspended() const;
|
||||||
|
void setSuspended(bool flag);
|
||||||
|
|
||||||
bool isFlight() const;
|
bool isFlight() const;
|
||||||
void setFlight(bool flag);
|
void setFlight(bool flag);
|
||||||
|
|
||||||
@ -119,6 +128,9 @@ public:
|
|||||||
void setSpawnPoint(glm::vec3 point);
|
void setSpawnPoint(glm::vec3 point);
|
||||||
glm::vec3 getSpawnPoint() const;
|
glm::vec3 getSpawnPoint() const;
|
||||||
|
|
||||||
|
glm::vec3 getRotation(bool interpolated=false) const;
|
||||||
|
void setRotation(const glm::vec3& rotation);
|
||||||
|
|
||||||
dv::value serialize() const override;
|
dv::value serialize() const override;
|
||||||
void deserialize(const dv::value& src) override;
|
void deserialize(const dv::value& src) override;
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "objects/Entities.hpp"
|
||||||
|
|
||||||
Players::Players(Level& level) : level(level) {}
|
Players::Players(Level& level) : level(level) {}
|
||||||
|
|
||||||
@ -19,10 +20,19 @@ Player* Players::get(int64_t id) const {
|
|||||||
return found->second.get();
|
return found->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* Players::create() {
|
Player* Players::create(int64_t id) {
|
||||||
|
int64_t& nextPlayerID = level.getWorld()->getInfo().nextPlayerId;
|
||||||
|
if (id == NONE) {
|
||||||
|
id = nextPlayerID++;
|
||||||
|
} else {
|
||||||
|
if (auto player = get(id)) {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
nextPlayerID = std::max(id + 1, nextPlayerID);
|
||||||
|
}
|
||||||
auto playerPtr = std::make_unique<Player>(
|
auto playerPtr = std::make_unique<Player>(
|
||||||
level,
|
level,
|
||||||
level.getWorld()->getInfo().nextPlayerId++,
|
id,
|
||||||
"",
|
"",
|
||||||
glm::vec3(0, DEF_PLAYER_Y, 0),
|
glm::vec3(0, DEF_PLAYER_Y, 0),
|
||||||
DEF_PLAYER_SPEED,
|
DEF_PLAYER_SPEED,
|
||||||
@ -36,6 +46,26 @@ Player* Players::create() {
|
|||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Players::suspend(int64_t id) {
|
||||||
|
if (auto player = get(id)) {
|
||||||
|
if (player->isSuspended()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player->setSuspended(true);
|
||||||
|
level.entities->despawn(player->getEntity());
|
||||||
|
player->setEntity(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Players::resume(int64_t id) {
|
||||||
|
if (auto player = get(id)) {
|
||||||
|
if (!player->isSuspended()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player->setSuspended(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Players::remove(int64_t id) {
|
void Players::remove(int64_t id) {
|
||||||
players.erase(id);
|
players.erase(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,9 @@ class Level;
|
|||||||
class Player;
|
class Player;
|
||||||
|
|
||||||
class Players : public Serializable {
|
class Players : public Serializable {
|
||||||
|
public:
|
||||||
|
static inline int64_t NONE = -1;
|
||||||
|
private:
|
||||||
Level& level;
|
Level& level;
|
||||||
std::unordered_map<int64_t, std::unique_ptr<Player>> players;
|
std::unordered_map<int64_t, std::unique_ptr<Player>> players;
|
||||||
|
|
||||||
@ -23,7 +26,11 @@ public:
|
|||||||
|
|
||||||
Player* get(int64_t id) const;
|
Player* get(int64_t id) const;
|
||||||
|
|
||||||
Player* create();
|
Player* create(int64_t id=NONE);
|
||||||
|
|
||||||
|
void suspend(int64_t id);
|
||||||
|
|
||||||
|
void resume(int64_t id);
|
||||||
|
|
||||||
void remove(int64_t id);
|
void remove(int64_t id);
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,8 @@
|
|||||||
#include "graphics/commons/Model.hpp"
|
#include "graphics/commons/Model.hpp"
|
||||||
#include "graphics/render/ModelBatch.hpp"
|
#include "graphics/render/ModelBatch.hpp"
|
||||||
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/matrix_decompose.hpp>
|
||||||
|
|
||||||
using namespace rigging;
|
using namespace rigging;
|
||||||
|
|
||||||
@ -69,7 +68,7 @@ SkeletonConfig::SkeletonConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t SkeletonConfig::update(
|
size_t SkeletonConfig::update(
|
||||||
size_t index, Skeleton& skeleton, Bone* node, glm::mat4 matrix
|
size_t index, Skeleton& skeleton, Bone* node, const glm::mat4& matrix
|
||||||
) const {
|
) const {
|
||||||
auto boneMatrix = skeleton.pose.matrices[index];
|
auto boneMatrix = skeleton.pose.matrices[index];
|
||||||
auto boneOffset = node->getOffset();
|
auto boneOffset = node->getOffset();
|
||||||
@ -90,17 +89,32 @@ size_t SkeletonConfig::update(
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonConfig::update(Skeleton& skeleton, glm::mat4 matrix) const {
|
void SkeletonConfig::update(
|
||||||
update(0, skeleton, root.get(), matrix);
|
Skeleton& skeleton, const glm::mat4& matrix, const glm::vec3& position
|
||||||
|
) const {
|
||||||
|
if (skeleton.interpolation.isEnabled()) {
|
||||||
|
const auto& interpolation = skeleton.interpolation;
|
||||||
|
glm::vec3 scale, translation, skew;
|
||||||
|
glm::quat rotation;
|
||||||
|
glm::vec4 perspective;
|
||||||
|
glm::decompose(matrix, scale, rotation, translation, skew, perspective);
|
||||||
|
|
||||||
|
auto delta = interpolation.getCurrent() - position;
|
||||||
|
auto interpolatedMatrix = glm::translate(matrix, delta);
|
||||||
|
update(0, skeleton, root.get(), interpolatedMatrix);
|
||||||
|
} else {
|
||||||
|
update(0, skeleton, root.get(), matrix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonConfig::render(
|
void SkeletonConfig::render(
|
||||||
const Assets& assets,
|
const Assets& assets,
|
||||||
ModelBatch& batch,
|
ModelBatch& batch,
|
||||||
Skeleton& skeleton,
|
Skeleton& skeleton,
|
||||||
const glm::mat4& matrix
|
const glm::mat4& matrix,
|
||||||
|
const glm::vec3& position
|
||||||
) const {
|
) const {
|
||||||
update(skeleton, matrix);
|
update(skeleton, matrix, position);
|
||||||
|
|
||||||
if (!skeleton.visible) {
|
if (!skeleton.visible) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cmath>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "util/Interpolation.hpp"
|
||||||
|
|
||||||
class Assets;
|
class Assets;
|
||||||
class ModelBatch;
|
class ModelBatch;
|
||||||
@ -83,6 +86,8 @@ namespace rigging {
|
|||||||
bool visible;
|
bool visible;
|
||||||
glm::vec3 tint {1.0f, 1.0f, 1.0f};
|
glm::vec3 tint {1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
|
util::VecInterpolation<3, float> interpolation {false};
|
||||||
|
|
||||||
Skeleton(const SkeletonConfig* config);
|
Skeleton(const SkeletonConfig* config);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,7 +105,7 @@ namespace rigging {
|
|||||||
std::vector<Bone*> nodes;
|
std::vector<Bone*> nodes;
|
||||||
|
|
||||||
size_t update(
|
size_t update(
|
||||||
size_t index, Skeleton& skeleton, Bone* node, glm::mat4 matrix
|
size_t index, Skeleton& skeleton, Bone* node, const glm::mat4& matrix
|
||||||
) const;
|
) const;
|
||||||
public:
|
public:
|
||||||
SkeletonConfig(
|
SkeletonConfig(
|
||||||
@ -109,12 +114,18 @@ namespace rigging {
|
|||||||
size_t nodesCount
|
size_t nodesCount
|
||||||
);
|
);
|
||||||
|
|
||||||
void update(Skeleton& skeleton, glm::mat4 matrix) const;
|
void update(
|
||||||
|
Skeleton& skeleton,
|
||||||
|
const glm::mat4& matrix,
|
||||||
|
const glm::vec3& position
|
||||||
|
) const;
|
||||||
|
|
||||||
void render(
|
void render(
|
||||||
const Assets& assets,
|
const Assets& assets,
|
||||||
ModelBatch& batch,
|
ModelBatch& batch,
|
||||||
Skeleton& skeleton,
|
Skeleton& skeleton,
|
||||||
const glm::mat4& matrix
|
const glm::mat4& matrix,
|
||||||
|
const glm::vec3& position
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
Skeleton instance() const {
|
Skeleton instance() const {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ namespace util {
|
|||||||
int nextid = 1;
|
int nextid = 1;
|
||||||
std::unordered_map<int, std::function<bool(Types...)>> handlers;
|
std::unordered_map<int, std::function<bool(Types...)>> handlers;
|
||||||
std::vector<int> order;
|
std::vector<int> order;
|
||||||
std::recursive_mutex mutex;
|
std::mutex mutex;
|
||||||
public:
|
public:
|
||||||
HandlersList() = default;
|
HandlersList() = default;
|
||||||
|
|
||||||
@ -46,11 +46,15 @@ namespace util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void notify(Types...args) {
|
void notify(Types...args) {
|
||||||
std::lock_guard lock(mutex);
|
std::vector<int> orderCopy;
|
||||||
|
decltype(handlers) handlersCopy;
|
||||||
auto orderCopy = order;
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
orderCopy = order;
|
||||||
|
handlersCopy = handlers;
|
||||||
|
}
|
||||||
for (auto it = orderCopy.rbegin(); it != orderCopy.rend(); ++it) {
|
for (auto it = orderCopy.rbegin(); it != orderCopy.rend(); ++it) {
|
||||||
if (handlers.at(*it)(args...)) {
|
if (handlersCopy.at(*it)(args...)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/util/Interpolation.hpp
Normal file
60
src/util/Interpolation.hpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
template<int N, typename T, bool angular=false>
|
||||||
|
class VecInterpolation {
|
||||||
|
bool enabled;
|
||||||
|
glm::vec<N, T> prevPos {std::numeric_limits<T>::quiet_NaN()};
|
||||||
|
glm::vec<N, T> nextPos {};
|
||||||
|
T refreshInterval = 0.0;
|
||||||
|
T timer = 0.0;
|
||||||
|
T intervalUpdateFactor = 0.1;
|
||||||
|
public:
|
||||||
|
VecInterpolation(bool enabled) : enabled(enabled) {}
|
||||||
|
|
||||||
|
void refresh(const glm::vec<N, T>& position) {
|
||||||
|
auto current = getCurrent();
|
||||||
|
prevPos = current;
|
||||||
|
nextPos = position;
|
||||||
|
refreshInterval = timer * intervalUpdateFactor +
|
||||||
|
refreshInterval * (1.0 - intervalUpdateFactor);
|
||||||
|
timer = 0.0;
|
||||||
|
|
||||||
|
if constexpr (angular) {
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
T diff = nextPos[i] - prevPos[i];
|
||||||
|
if (glm::abs(diff) > 180.0f) {
|
||||||
|
nextPos[i] += (diff > 0.0f ? -360.0f : 360.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTimer(T delta) {
|
||||||
|
timer += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec<N, T> getCurrent() const {
|
||||||
|
if (refreshInterval < 0.001 || std::isnan(prevPos.x)) {
|
||||||
|
return nextPos;
|
||||||
|
}
|
||||||
|
T t = timer / refreshInterval;
|
||||||
|
return nextPos * t + prevPos * (1.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
T getRefreshInterval() const {
|
||||||
|
return refreshInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() const {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled(bool enabled) {
|
||||||
|
this->enabled = enabled;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -18,8 +18,8 @@ uint Events::currentFrame = 0;
|
|||||||
int Events::scroll = 0;
|
int Events::scroll = 0;
|
||||||
glm::vec2 Events::delta = {};
|
glm::vec2 Events::delta = {};
|
||||||
glm::vec2 Events::cursor = {};
|
glm::vec2 Events::cursor = {};
|
||||||
bool Events::cursor_drag = false;
|
bool Events::cursorDrag = false;
|
||||||
bool Events::_cursor_locked = false;
|
bool Events::cursorLocked = false;
|
||||||
std::vector<uint> Events::codepoints;
|
std::vector<uint> Events::codepoints;
|
||||||
std::vector<keycode> Events::pressedKeys;
|
std::vector<keycode> Events::pressedKeys;
|
||||||
std::unordered_map<std::string, Binding> Events::bindings;
|
std::unordered_map<std::string, Binding> Events::bindings;
|
||||||
@ -61,10 +61,10 @@ bool Events::jclicked(int button) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Events::toggleCursor() {
|
void Events::toggleCursor() {
|
||||||
cursor_drag = false;
|
cursorDrag = false;
|
||||||
_cursor_locked = !_cursor_locked;
|
cursorLocked = !cursorLocked;
|
||||||
Window::setCursorMode(
|
Window::setCursorMode(
|
||||||
_cursor_locked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL
|
cursorLocked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +170,11 @@ void Events::setButton(int button, bool b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Events::setPosition(float xpos, float ypos) {
|
void Events::setPosition(float xpos, float ypos) {
|
||||||
if (Events::cursor_drag) {
|
if (Events::cursorDrag) {
|
||||||
Events::delta.x += xpos - Events::cursor.x;
|
Events::delta.x += xpos - Events::cursor.x;
|
||||||
Events::delta.y += ypos - Events::cursor.y;
|
Events::delta.y += ypos - Events::cursor.y;
|
||||||
} else {
|
} else {
|
||||||
Events::cursor_drag = true;
|
Events::cursorDrag = true;
|
||||||
}
|
}
|
||||||
Events::cursor.x = xpos;
|
Events::cursor.x = xpos;
|
||||||
Events::cursor.y = ypos;
|
Events::cursor.y = ypos;
|
||||||
@ -249,3 +249,7 @@ void Events::enableBindings() {
|
|||||||
binding.enable = true;
|
binding.enable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Events::isCursorLocked() {
|
||||||
|
return cursorLocked;
|
||||||
|
}
|
||||||
|
|||||||
@ -19,12 +19,12 @@ class Events {
|
|||||||
static bool keys[KEYS_BUFFER_SIZE];
|
static bool keys[KEYS_BUFFER_SIZE];
|
||||||
static uint frames[KEYS_BUFFER_SIZE];
|
static uint frames[KEYS_BUFFER_SIZE];
|
||||||
static uint currentFrame;
|
static uint currentFrame;
|
||||||
static bool cursor_drag;
|
static bool cursorDrag;
|
||||||
|
static bool cursorLocked;
|
||||||
public:
|
public:
|
||||||
static int scroll;
|
static int scroll;
|
||||||
static glm::vec2 delta;
|
static glm::vec2 delta;
|
||||||
static glm::vec2 cursor;
|
static glm::vec2 cursor;
|
||||||
static bool _cursor_locked;
|
|
||||||
static std::vector<uint> codepoints;
|
static std::vector<uint> codepoints;
|
||||||
static std::vector<keycode> pressedKeys;
|
static std::vector<keycode> pressedKeys;
|
||||||
static std::unordered_map<std::string, Binding> bindings;
|
static std::unordered_map<std::string, Binding> bindings;
|
||||||
@ -65,4 +65,6 @@ public:
|
|||||||
BindType bindType
|
BindType bindType
|
||||||
);
|
);
|
||||||
static void enableBindings();
|
static void enableBindings();
|
||||||
|
|
||||||
|
static bool isCursorLocked();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -389,7 +389,9 @@ void Window::toggleFullscreen() {
|
|||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||||
|
|
||||||
if (Events::_cursor_locked) Events::toggleCursor();
|
if (Events::isCursorLocked()){
|
||||||
|
Events::toggleCursor();
|
||||||
|
}
|
||||||
|
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
glfwGetWindowPos(window, &posX, &posY);
|
glfwGetWindowPos(window, &posX, &posY);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user