Merge pull request #374 from MihailRis/block-cracks
Block Cracks & update Player
@ -24,7 +24,9 @@ hud.open_block(x: int, y: int, z: int) -> int, str
|
||||
```lua
|
||||
-- Show overlay with layout specified.
|
||||
-- Shows player inventory also if playerinv is true.
|
||||
hud.show_overlay(layoutid: str, playerinv: bool)
|
||||
-- Using `args` you can specify an array of parameter values that will be passed
|
||||
-- to on_open of the overlay being shown.
|
||||
hud.show_overlay(layoutid: str, playerinv: bool, [optional] args: table)
|
||||
|
||||
-- Add element to the screen.
|
||||
-- The element will be removed on world close only.
|
||||
|
||||
@ -56,12 +56,26 @@ player.set_noclip(bool)
|
||||
|
||||
Getter and setter for player noclip mode (collisions disabled)
|
||||
|
||||
```lua
|
||||
player.is_infinite_items() -> bool
|
||||
player.set_infinite_items(bool)
|
||||
```
|
||||
|
||||
Getter and setter for infinite items (not removed from inventory after use)
|
||||
|
||||
```lua
|
||||
player.is_instant_destruction() -> bool
|
||||
player.set_instant_destruction(bool)
|
||||
```
|
||||
|
||||
Getter and setter for instant destruction of blocks when the `player.destroy` binding is activated.
|
||||
|
||||
``` lua
|
||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||
```
|
||||
|
||||
Point setter and getter added by player
|
||||
Spawn point setter and getter
|
||||
|
||||
```lua
|
||||
player.get_selected_block(playerid: int) -> x,y,z
|
||||
|
||||
@ -38,7 +38,13 @@ Called on random block update (grass growth)
|
||||
function on_blocks_tick(tps: int)
|
||||
```
|
||||
|
||||
Called tps (20) times per second.
|
||||
Called tps (20) times per second. Use 1/tps instead of `time.delta()`.
|
||||
|
||||
```lua
|
||||
function on_player_tick(playerid: int, tps: int)
|
||||
```
|
||||
|
||||
Called tps (20) times per second. Use 1/tps instead of `time.delta()`.
|
||||
|
||||
## Item events
|
||||
|
||||
|
||||
@ -25,7 +25,9 @@ hud.open_block(x: int, y: int, z: int) -> int, str
|
||||
```lua
|
||||
-- Показывает элемент в режиме оверлея.
|
||||
-- Также показывает инвентарь игрока, если playerinv - **true**.
|
||||
hud.show_overlay(layoutid: str, playerinv: bool)
|
||||
-- Через args можно указать массив значений параметров, что будут переданы
|
||||
-- в on_open показываемого оверлея.
|
||||
hud.show_overlay(layoutid: str, playerinv: bool, [опционально] args: table)
|
||||
|
||||
-- Добавляет постоянный элемент на экран. Элемент не удаляется при
|
||||
-- закрытии инвентаря. Чтобы не перекрывать затенение в режиме
|
||||
|
||||
@ -56,6 +56,20 @@ player.set_noclip(bool)
|
||||
|
||||
Геттер и сеттер noclip режима (выключенная коллизия игрока)
|
||||
|
||||
```lua
|
||||
player.is_infinite_items() -> bool
|
||||
player.set_infinite_items(bool)
|
||||
```
|
||||
|
||||
Геттер и сеттер бесконечных предметов (не удаляются из инвентаря при использовании)
|
||||
|
||||
```lua
|
||||
player.is_instant_destruction() -> bool
|
||||
player.set_instant_destruction(bool)
|
||||
```
|
||||
|
||||
Геттер и сеттер мнгновенного разрушения блоков при активации привязки `player.destroy`.
|
||||
|
||||
```lua
|
||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||
|
||||
@ -38,7 +38,13 @@ function on_random_update(x, y, z)
|
||||
function on_blocks_tick(tps: int)
|
||||
```
|
||||
|
||||
Вызывается tps (20) раз в секунду
|
||||
Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`.
|
||||
|
||||
```lua
|
||||
function on_player_tick(playerid: int, tps: int)
|
||||
```
|
||||
|
||||
Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`.
|
||||
|
||||
## События предметов
|
||||
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
{
|
||||
"atlases": [
|
||||
{"name": "cracks", "type": "separate"}
|
||||
],
|
||||
"sounds": [
|
||||
"blocks/door_open",
|
||||
"blocks/door_close",
|
||||
|
||||
BIN
res/content/base/textures/cracks/cracks_0.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
res/content/base/textures/cracks/cracks_1.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_10.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
res/content/base/textures/cracks/cracks_2.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/content/base/textures/cracks/cracks_3.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_4.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/content/base/textures/cracks/cracks_5.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/content/base/textures/cracks/cracks_6.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_7.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_8.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
res/content/base/textures/cracks/cracks_9.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
@ -202,9 +202,8 @@ function on_open(mode)
|
||||
debug=document.s_debug
|
||||
}, function (mode)
|
||||
set_mode(mode)
|
||||
end, "console")
|
||||
end
|
||||
if mode then
|
||||
end, mode or "console")
|
||||
elseif mode then
|
||||
modes:set(mode)
|
||||
end
|
||||
end
|
||||
|
||||
@ -269,7 +269,7 @@ function _rules.clear()
|
||||
_rules.create("allow-cheats", true)
|
||||
end
|
||||
|
||||
function __vc_create_hud_rules()
|
||||
function __vc_on_hud_open()
|
||||
_rules.create("allow-content-access", hud._is_content_access(), function(value)
|
||||
hud._set_content_access(value)
|
||||
input.set_enabled("player.pick", value)
|
||||
@ -295,6 +295,14 @@ function __vc_create_hud_rules()
|
||||
_rules.create("allow-debug-cheats", true, function(value)
|
||||
hud._set_debug_cheats(value)
|
||||
end)
|
||||
input.add_callback("devtools.console", function()
|
||||
if hud.is_paused() then
|
||||
return
|
||||
end
|
||||
time.post_runnable(function()
|
||||
hud.show_overlay("core:console", false, {"console"})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local RULES_FILE = "world:rules.toml"
|
||||
|
||||
@ -292,6 +292,10 @@ function __load_script(path, nocache)
|
||||
end
|
||||
|
||||
function require(path)
|
||||
if not string.find(path, ':') then
|
||||
local prefix, _ = parse_path(debug.getinfo(2).source)
|
||||
return require(prefix..':'..path)
|
||||
end
|
||||
local prefix, file = parse_path(path)
|
||||
return __load_script(prefix..":modules/"..file..".lua")
|
||||
end
|
||||
|
||||
@ -9,6 +9,7 @@ uniform samplerCube u_cubemap;
|
||||
uniform vec3 u_fogColor;
|
||||
uniform float u_fogFactor;
|
||||
uniform float u_fogCurve;
|
||||
uniform bool u_alphaClip;
|
||||
|
||||
void main() {
|
||||
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
||||
@ -16,7 +17,7 @@ void main() {
|
||||
float depth = (a_distance/256.0);
|
||||
float alpha = a_color.a * tex_color.a;
|
||||
// anyway it's any alpha-test alternative required
|
||||
if (alpha < 0.5f)
|
||||
if (alpha < (u_alphaClip ? 0.5f : 0.2f))
|
||||
discard;
|
||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
||||
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
||||
|
||||
@ -43,7 +43,11 @@ void AssetsLoader::add(
|
||||
const std::string& alias,
|
||||
std::shared_ptr<AssetCfg> settings
|
||||
) {
|
||||
if (enqueued.find({tag, alias}) != enqueued.end()){
|
||||
return;
|
||||
}
|
||||
entries.push(aloader_entry {tag, filename, alias, std::move(settings)});
|
||||
enqueued.insert({tag, alias});
|
||||
}
|
||||
|
||||
bool AssetsLoader::hasNext() const {
|
||||
@ -148,6 +152,16 @@ void AssetsLoader::processPreload(
|
||||
std::make_shared<SoundCfg>(map.at("keep-pcm").get(keepPCM)));
|
||||
break;
|
||||
}
|
||||
case AssetType::ATLAS: {
|
||||
std::string typeName = "atlas";
|
||||
map.at("type").get(typeName);
|
||||
auto type = AtlasType::ATLAS;
|
||||
if (typeName == "separate") {
|
||||
type = AtlasType::SEPARATE;
|
||||
}
|
||||
add(tag, path, name, std::make_shared<AtlasCfg>(type));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
add(tag, path, name);
|
||||
break;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
@ -37,6 +38,17 @@ struct SoundCfg : AssetCfg {
|
||||
}
|
||||
};
|
||||
|
||||
enum class AtlasType {
|
||||
ATLAS, SEPARATE
|
||||
};
|
||||
|
||||
struct AtlasCfg : AssetCfg {
|
||||
AtlasType type;
|
||||
|
||||
AtlasCfg(AtlasType type) : type(type) {
|
||||
}
|
||||
};
|
||||
|
||||
using aloader_func = std::function<
|
||||
assetload::
|
||||
postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
||||
@ -52,6 +64,7 @@ class AssetsLoader {
|
||||
Assets* assets;
|
||||
std::map<AssetType, aloader_func> loaders;
|
||||
std::queue<aloader_entry> entries;
|
||||
std::set<std::pair<AssetType, std::string>> enqueued;
|
||||
const ResPaths* paths;
|
||||
|
||||
void tryAddSound(const std::string& name);
|
||||
|
||||
@ -103,12 +103,25 @@ static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::atlas(
|
||||
AssetsLoader*,
|
||||
AssetsLoader* loader,
|
||||
const ResPaths* paths,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>&
|
||||
const std::shared_ptr<AssetCfg>& config
|
||||
) {
|
||||
auto atlasConfig = std::dynamic_pointer_cast<AtlasCfg>(config);
|
||||
if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) {
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!imageio::is_read_supported(file.extension().u8string()))
|
||||
continue;
|
||||
loader->add(
|
||||
AssetType::TEXTURE,
|
||||
directory + "/" + file.stem().u8string(),
|
||||
name + "/" + file.stem().u8string()
|
||||
);
|
||||
}
|
||||
return [](auto){};
|
||||
}
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!imageio::is_read_supported(file.extension().u8string())) continue;
|
||||
|
||||
@ -99,6 +99,7 @@ struct world_funcs_set {
|
||||
bool onblockplaced : 1;
|
||||
bool onblockbroken : 1;
|
||||
bool onblockinteract : 1;
|
||||
bool onplayertick : 1;
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
|
||||
@ -10,7 +10,6 @@ inline const std::string CORE_STRUCT_AIR = "core:struct_air";
|
||||
inline const std::string TEXTURE_NOTFOUND = "notfound";
|
||||
|
||||
// built-in bindings
|
||||
inline const std::string BIND_DEVTOOLS_CONSOLE = "devtools.console";
|
||||
inline const std::string BIND_CHUNKS_RELOAD = "chunks.reload";
|
||||
inline const std::string BIND_MOVE_FORWARD = "movement.forward";
|
||||
inline const std::string BIND_MOVE_BACK = "movement.back";
|
||||
|
||||
@ -229,17 +229,9 @@ void Hud::processInput(bool visible) {
|
||||
setPause(true);
|
||||
}
|
||||
}
|
||||
if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
|
||||
showOverlay(
|
||||
assets->get<UiDocument>("core:console"),
|
||||
false,
|
||||
std::string("console")
|
||||
);
|
||||
}
|
||||
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
||||
setPause(true);
|
||||
}
|
||||
|
||||
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
||||
if (inventoryOpen) {
|
||||
closeInventory();
|
||||
@ -470,7 +462,7 @@ void Hud::showExchangeSlot() {
|
||||
}
|
||||
|
||||
void Hud::showOverlay(
|
||||
UiDocument* doc, bool playerInventory, const dv::value& arg
|
||||
UiDocument* doc, bool playerInventory, const dv::value& args
|
||||
) {
|
||||
if (isInventoryOpen()) {
|
||||
closeInventory();
|
||||
@ -483,7 +475,7 @@ void Hud::showOverlay(
|
||||
inventoryOpen = true;
|
||||
}
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false),
|
||||
arg);
|
||||
args);
|
||||
}
|
||||
|
||||
void Hud::openPermanent(UiDocument* doc) {
|
||||
@ -515,13 +507,18 @@ void Hud::closeInventory() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::add(const HudElement& element, const dv::value& arg) {
|
||||
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
||||
gui->add(element.getNode());
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||
auto inventory = invview ? invview->getInventory() : nullptr;
|
||||
std::vector<dv::value> args {arg};
|
||||
std::vector<dv::value> args;
|
||||
if (argsArray != nullptr) {
|
||||
for (const auto& arg : argsArray) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
args.emplace_back(inventory ? inventory.get()->getId() : 0);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
args.emplace_back(static_cast<integer_t>(blockPos[i]));
|
||||
|
||||
108
src/graphics/render/BlockWrapsRenderer.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/render/MainBatch.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level)
|
||||
: assets(assets), level(level), batch(std::make_unique<MainBatch>(1024)) {
|
||||
}
|
||||
|
||||
BlockWrapsRenderer::~BlockWrapsRenderer() = default;
|
||||
|
||||
void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) {
|
||||
const auto& chunks = *level.chunks;
|
||||
|
||||
auto textureRegion = util::get_texture_region(assets, wrapper.texture, "");
|
||||
|
||||
auto& shader = assets.require<Shader>("entity");
|
||||
shader.use();
|
||||
shader.uniform1i("u_alphaClip", false);
|
||||
|
||||
const UVRegion& cracksRegion = textureRegion.region;
|
||||
UVRegion regions[6] {
|
||||
cracksRegion, cracksRegion, cracksRegion,
|
||||
cracksRegion, cracksRegion, cracksRegion
|
||||
};
|
||||
batch->setTexture(textureRegion.texture);
|
||||
|
||||
const voxel* vox = chunks.get(wrapper.position);
|
||||
if (vox == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (vox->id != BLOCK_VOID) {
|
||||
const auto& def =
|
||||
level.content->getIndices()->blocks.require(vox->id);
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
batch->cube(
|
||||
glm::vec3(wrapper.position) + glm::vec3(0.5f),
|
||||
glm::vec3(1.01f),
|
||||
regions,
|
||||
glm::vec4(0),
|
||||
false
|
||||
);
|
||||
break;
|
||||
case BlockModel::aabb: {
|
||||
const auto& aabb = def.rt.hitboxes[vox->state.rotation].at(0);
|
||||
const auto& size = aabb.size();
|
||||
regions[0].scale(size.z, size.y);
|
||||
regions[1].scale(size.z, size.y);
|
||||
regions[2].scale(size.x, size.z);
|
||||
regions[3].scale(size.x, size.z);
|
||||
regions[4].scale(size.x, size.y);
|
||||
regions[5].scale(size.x, size.y);
|
||||
batch->cube(
|
||||
glm::vec3(wrapper.position) + aabb.center(),
|
||||
size * glm::vec3(1.01f),
|
||||
regions,
|
||||
glm::vec4(0),
|
||||
false
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockWrapsRenderer::draw(const DrawContext& pctx, const Player& player) {
|
||||
auto ctx = pctx.sub();
|
||||
for (const auto& [_, wrapper] : wrappers) {
|
||||
draw(*wrapper);
|
||||
}
|
||||
batch->flush();
|
||||
}
|
||||
|
||||
u64id_t BlockWrapsRenderer::add(
|
||||
const glm::ivec3& position, const std::string& texture
|
||||
) {
|
||||
u64id_t id = nextWrapper++;
|
||||
wrappers[id] = std::make_unique<BlockWrapper>(
|
||||
BlockWrapper {position, texture}
|
||||
);
|
||||
return id;
|
||||
}
|
||||
|
||||
BlockWrapper* BlockWrapsRenderer::get(u64id_t id) const {
|
||||
const auto& found = wrappers.find(id);
|
||||
if (found == wrappers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
void BlockWrapsRenderer::remove(u64id_t id) {
|
||||
wrappers.erase(id);
|
||||
}
|
||||
40
src/graphics/render/BlockWrapsRenderer.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "MainBatch.hpp"
|
||||
#include "typedefs.hpp"
|
||||
|
||||
class Assets;
|
||||
class Player;
|
||||
class Level;
|
||||
class DrawContext;
|
||||
|
||||
struct BlockWrapper {
|
||||
glm::ivec3 position;
|
||||
std::string texture;
|
||||
};
|
||||
|
||||
class BlockWrapsRenderer {
|
||||
const Assets& assets;
|
||||
const Level& level;
|
||||
std::unique_ptr<MainBatch> batch;
|
||||
|
||||
std::unordered_map<u64id_t, std::unique_ptr<BlockWrapper>> wrappers;
|
||||
u64id_t nextWrapper = 1;
|
||||
|
||||
void draw(const BlockWrapper& wrapper);
|
||||
public:
|
||||
BlockWrapsRenderer(const Assets& assets, const Level& level);
|
||||
~BlockWrapsRenderer();
|
||||
|
||||
void draw(const DrawContext& ctx, const Player& player);
|
||||
|
||||
u64id_t add(const glm::ivec3& position, const std::string& texture);
|
||||
|
||||
BlockWrapper* get(u64id_t id) const;
|
||||
|
||||
void remove(u64id_t id);
|
||||
};
|
||||
@ -236,6 +236,7 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||
const auto& cameraPos = camera.position;
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
shader.use();
|
||||
atlas.getTexture()->bind();
|
||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||
shader.uniform1i("u_alphaClip", false);
|
||||
|
||||
@ -97,38 +97,38 @@ void MainBatch::cube(
|
||||
const glm::vec3 Z(0.0f, 0.0f, 1.0f);
|
||||
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, 0.0f, 0.0f),
|
||||
coord + Z * size.z * 0.5f,
|
||||
X, Y, glm::vec2(size.x, size.y),
|
||||
(shading ? do_tint(0.8) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[5]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(size.x, 0.0f, -size.z),
|
||||
coord - Z * size.z * 0.5f,
|
||||
-X, Y, glm::vec2(size.x, size.y),
|
||||
(shading ? do_tint(0.8) * tint : tint),
|
||||
(shading ? do_tint(0.9f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[4]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, size.y, 0.0f),
|
||||
X, -Z, glm::vec2(size.x, size.z),
|
||||
coord + Y * size.y * 0.5f,
|
||||
-X, Z, glm::vec2(size.x, size.z),
|
||||
(shading ? do_tint(1.0f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[3]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, 0.0f, -size.z),
|
||||
coord - Y * size.y * 0.5f,
|
||||
X, Z, glm::vec2(size.x, size.z),
|
||||
(shading ? do_tint(0.7f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[2]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, 0.0f, -size.z),
|
||||
Z, Y, glm::vec2(size.z, size.y),
|
||||
(shading ? do_tint(0.9f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[0]
|
||||
coord + X * size.x * 0.5f,
|
||||
-Z, Y, glm::vec2(size.z, size.y),
|
||||
(shading ? do_tint(0.8f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[1]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(size.x, 0.0f, 0.0f),
|
||||
-Z, Y, glm::vec2(size.z, size.y),
|
||||
coord - X * size.x * 0.5f,
|
||||
Z, Y, glm::vec2(size.z, size.y),
|
||||
(shading ? do_tint(0.9f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[1]
|
||||
);
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
#include "ParticlesRenderer.hpp"
|
||||
#include "TextsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
@ -80,7 +81,8 @@ WorldRenderer::WorldRenderer(
|
||||
*frustumCulling,
|
||||
frontend.getContentGfxCache(),
|
||||
engine->getSettings()
|
||||
)) {
|
||||
)),
|
||||
blockWraps(std::make_unique<BlockWrapsRenderer>(assets, level)) {
|
||||
auto& settings = engine->getSettings();
|
||||
level.events->listen(
|
||||
EVT_CHUNK_HIDDEN,
|
||||
@ -153,6 +155,7 @@ void WorldRenderer::renderLevel(
|
||||
frustumCulling->update(camera.getProjView());
|
||||
}
|
||||
|
||||
entityShader.uniform1i("u_alphaClip", true);
|
||||
level.entities->render(
|
||||
assets,
|
||||
*modelBatch,
|
||||
@ -169,6 +172,7 @@ void WorldRenderer::renderLevel(
|
||||
setupWorldShader(shader, camera, settings, fogFactor);
|
||||
|
||||
chunks->drawChunks(camera, shader);
|
||||
blockWraps->draw(ctx, *player);
|
||||
|
||||
if (hudVisible) {
|
||||
renderLines(camera, linesShader, ctx);
|
||||
|
||||
@ -17,6 +17,7 @@ class Batch3D;
|
||||
class LineBatch;
|
||||
class ChunksRenderer;
|
||||
class ParticlesRenderer;
|
||||
class BlockWrapsRenderer;
|
||||
class GuidesRenderer;
|
||||
class TextsRenderer;
|
||||
class Shader;
|
||||
@ -68,6 +69,7 @@ class WorldRenderer {
|
||||
public:
|
||||
std::unique_ptr<TextsRenderer> texts;
|
||||
std::unique_ptr<ParticlesRenderer> particles;
|
||||
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||
|
||||
static bool showChunkBorders;
|
||||
static bool showEntitiesDebug;
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
|
||||
BlocksController::BlocksController(Level* level, uint padding)
|
||||
BlocksController::BlocksController(const Level& level, uint padding)
|
||||
: level(level),
|
||||
chunks(level->chunks.get()),
|
||||
lighting(level->lighting.get()),
|
||||
chunks(*level.chunks),
|
||||
lighting(*level.lighting),
|
||||
randTickClock(20, 3),
|
||||
blocksTickClock(20, 1),
|
||||
worldTickClock(20, 1),
|
||||
@ -34,8 +34,8 @@ void BlocksController::updateSides(int x, int y, int z) {
|
||||
}
|
||||
|
||||
void BlocksController::updateSides(int x, int y, int z, int w, int h, int d) {
|
||||
voxel* vox = chunks->get(x, y, z);
|
||||
const auto& def = level->content->getIndices()->blocks.require(vox->id);
|
||||
voxel* vox = chunks.get(x, y, z);
|
||||
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||
const auto& rot = def.rotations.variants[vox->state.rotation];
|
||||
const auto& xaxis = rot.axisX;
|
||||
const auto& yaxis = rot.axisY;
|
||||
@ -62,8 +62,8 @@ void BlocksController::breakBlock(
|
||||
onBlockInteraction(
|
||||
player, glm::ivec3(x, y, z), def, BlockInteraction::destruction
|
||||
);
|
||||
chunks->set(x, y, z, 0, {});
|
||||
lighting->onBlockSet(x, y, z, 0);
|
||||
chunks.set(x, y, z, 0, {});
|
||||
lighting.onBlockSet(x, y, z, 0);
|
||||
scripting::on_block_broken(player, def, glm::ivec3(x, y, z));
|
||||
if (def.rt.extended) {
|
||||
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
||||
@ -78,8 +78,8 @@ void BlocksController::placeBlock(
|
||||
onBlockInteraction(
|
||||
player, glm::ivec3(x, y, z), def, BlockInteraction::placing
|
||||
);
|
||||
chunks->set(x, y, z, def.rt.id, state);
|
||||
lighting->onBlockSet(x, y, z, def.rt.id);
|
||||
chunks.set(x, y, z, def.rt.id, state);
|
||||
lighting.onBlockSet(x, y, z, def.rt.id);
|
||||
scripting::on_block_placed(player, def, glm::ivec3(x, y, z));
|
||||
if (def.rt.extended) {
|
||||
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
||||
@ -89,12 +89,12 @@ void BlocksController::placeBlock(
|
||||
}
|
||||
|
||||
void BlocksController::updateBlock(int x, int y, int z) {
|
||||
voxel* vox = chunks->get(x, y, z);
|
||||
voxel* vox = chunks.get(x, y, z);
|
||||
if (vox == nullptr) return;
|
||||
const auto& def = level->content->getIndices()->blocks.require(vox->id);
|
||||
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||
if (def.grounded) {
|
||||
const auto& vec = get_ground_direction(def, vox->state.rotation);
|
||||
if (!chunks->isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) {
|
||||
if (!chunks.isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) {
|
||||
breakBlock(nullptr, def, x, y, z);
|
||||
return;
|
||||
}
|
||||
@ -117,12 +117,11 @@ void BlocksController::update(float delta) {
|
||||
}
|
||||
|
||||
void BlocksController::onBlocksTick(int tickid, int parts) {
|
||||
auto content = level->content;
|
||||
auto indices = content->getIndices();
|
||||
const auto& indices = level.content->getIndices()->blocks;
|
||||
int tickRate = blocksTickClock.getTickRate();
|
||||
for (size_t id = 0; id < indices->blocks.count(); id++) {
|
||||
for (size_t id = 0; id < indices.count(); id++) {
|
||||
if ((id + tickid) % parts != 0) continue;
|
||||
auto& def = indices->blocks.require(id);
|
||||
auto& def = indices.require(id);
|
||||
auto interval = def.tickInterval;
|
||||
if (def.rt.funcsset.onblockstick && tickid / parts % interval == 0) {
|
||||
scripting::on_blocks_tick(def, tickRate / interval);
|
||||
@ -155,9 +154,9 @@ void BlocksController::randomTick(
|
||||
}
|
||||
|
||||
void BlocksController::randomTick(int tickid, int parts) {
|
||||
auto indices = level->content->getIndices();
|
||||
int width = chunks->getWidth();
|
||||
int height = chunks->getHeight();
|
||||
auto indices = level.content->getIndices();
|
||||
int width = chunks.getWidth();
|
||||
int height = chunks.getHeight();
|
||||
int segments = 4;
|
||||
|
||||
for (uint z = padding; z < height - padding; z++) {
|
||||
@ -166,7 +165,7 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
if ((index + tickid) % parts != 0) {
|
||||
continue;
|
||||
}
|
||||
auto& chunk = chunks->getChunks()[index];
|
||||
auto& chunk = chunks.getChunks()[index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
continue;
|
||||
}
|
||||
@ -176,7 +175,7 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
}
|
||||
|
||||
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
@ -184,21 +183,20 @@ int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
auto inv = chunk->getBlockInventory(lx, y, lz);
|
||||
if (inv == nullptr) {
|
||||
auto indices = level->content->getIndices();
|
||||
auto& def =
|
||||
indices->blocks.require(chunk->voxels[vox_index(lx, y, lz)].id);
|
||||
const auto& indices = level.content->getIndices()->blocks;
|
||||
auto& def = indices.require(chunk->voxels[vox_index(lx, y, lz)].id);
|
||||
int invsize = def.inventorySize;
|
||||
if (invsize == 0) {
|
||||
return 0;
|
||||
}
|
||||
inv = level->inventories->create(invsize);
|
||||
inv = level.inventories->create(invsize);
|
||||
chunk->addBlockInventory(inv, lx, y, lz);
|
||||
}
|
||||
return inv->getId();
|
||||
}
|
||||
|
||||
void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
@ -207,11 +205,11 @@ void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
chunk->addBlockInventory(level->inventories->get(invid), lx, y, lz);
|
||||
chunk->addBlockInventory(level.inventories->get(invid), lx, y, lz);
|
||||
}
|
||||
|
||||
void BlocksController::unbindInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
|
||||
@ -24,9 +24,9 @@ using on_block_interaction = std::function<
|
||||
|
||||
/// BlocksController manages block updates and data (inventories, metadata)
|
||||
class BlocksController {
|
||||
Level* level;
|
||||
Chunks* chunks;
|
||||
Lighting* lighting;
|
||||
const Level& level;
|
||||
Chunks& chunks;
|
||||
Lighting& lighting;
|
||||
util::Clock randTickClock;
|
||||
util::Clock blocksTickClock;
|
||||
util::Clock worldTickClock;
|
||||
@ -34,7 +34,7 @@ class BlocksController {
|
||||
FastRandom random {};
|
||||
std::vector<on_block_interaction> blockInteractionCallbacks;
|
||||
public:
|
||||
BlocksController(Level* level, uint padding);
|
||||
BlocksController(const Level& level, uint padding);
|
||||
|
||||
void updateSides(int x, int y, int z);
|
||||
void updateSides(int x, int y, int z, int w, int h, int d);
|
||||
|
||||
@ -22,15 +22,15 @@
|
||||
const uint MAX_WORK_PER_FRAME = 128;
|
||||
const uint MIN_SURROUNDING = 9;
|
||||
|
||||
ChunksController::ChunksController(Level* level, uint padding)
|
||||
ChunksController::ChunksController(Level& level, uint padding)
|
||||
: level(level),
|
||||
chunks(level->chunks.get()),
|
||||
lighting(level->lighting.get()),
|
||||
chunks(*level.chunks),
|
||||
lighting(*level.lighting),
|
||||
padding(padding),
|
||||
generator(std::make_unique<WorldGenerator>(
|
||||
level->content->generators.require(level->getWorld()->getGenerator()),
|
||||
level->content,
|
||||
level->getWorld()->getSeed()
|
||||
level.content->generators.require(level.getWorld()->getGenerator()),
|
||||
level.content,
|
||||
level.getWorld()->getSeed()
|
||||
)) {}
|
||||
|
||||
ChunksController::~ChunksController() = default;
|
||||
@ -56,8 +56,8 @@ void ChunksController::update(
|
||||
}
|
||||
|
||||
bool ChunksController::loadVisible() {
|
||||
int sizeX = chunks->getWidth();
|
||||
int sizeY = chunks->getHeight();
|
||||
int sizeX = chunks.getWidth();
|
||||
int sizeY = chunks.getHeight();
|
||||
|
||||
int nearX = 0;
|
||||
int nearZ = 0;
|
||||
@ -66,7 +66,7 @@ bool ChunksController::loadVisible() {
|
||||
for (uint z = padding; z < sizeY - padding; z++) {
|
||||
for (uint x = padding; x < sizeX - padding; x++) {
|
||||
int index = z * sizeX + x;
|
||||
auto& chunk = chunks->getChunks()[index];
|
||||
auto& chunk = chunks.getChunks()[index];
|
||||
if (chunk != nullptr) {
|
||||
if (chunk->flags.loaded && !chunk->flags.lighted) {
|
||||
if (buildLights(chunk)) {
|
||||
@ -87,12 +87,12 @@ bool ChunksController::loadVisible() {
|
||||
}
|
||||
}
|
||||
|
||||
const auto& chunk = chunks->getChunks()[nearZ * sizeX + nearX];
|
||||
const auto& chunk = chunks.getChunks()[nearZ * sizeX + nearX];
|
||||
if (chunk != nullptr || !assigned) {
|
||||
return false;
|
||||
}
|
||||
int offsetX = chunks->getOffsetX();
|
||||
int offsetY = chunks->getOffsetY();
|
||||
int offsetX = chunks.getOffsetX();
|
||||
int offsetY = chunks.getOffsetY();
|
||||
createChunk(nearX + offsetX, nearZ + offsetY);
|
||||
return true;
|
||||
}
|
||||
@ -101,15 +101,15 @@ bool ChunksController::buildLights(const std::shared_ptr<Chunk>& chunk) {
|
||||
int surrounding = 0;
|
||||
for (int oz = -1; oz <= 1; oz++) {
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
if (chunks->getChunk(chunk->x + ox, chunk->z + oz)) surrounding++;
|
||||
if (chunks.getChunk(chunk->x + ox, chunk->z + oz)) surrounding++;
|
||||
}
|
||||
}
|
||||
if (surrounding == MIN_SURROUNDING) {
|
||||
bool lightsCache = chunk->flags.loadedLights;
|
||||
if (!lightsCache) {
|
||||
lighting->buildSkyLight(chunk->x, chunk->z);
|
||||
lighting.buildSkyLight(chunk->x, chunk->z);
|
||||
}
|
||||
lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
||||
lighting.onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
||||
chunk->flags.lighted = true;
|
||||
return true;
|
||||
}
|
||||
@ -117,8 +117,8 @@ bool ChunksController::buildLights(const std::shared_ptr<Chunk>& chunk) {
|
||||
}
|
||||
|
||||
void ChunksController::createChunk(int x, int z) {
|
||||
auto chunk = level->chunksStorage->create(x, z);
|
||||
chunks->putChunk(chunk);
|
||||
auto chunk = level.chunksStorage->create(x, z);
|
||||
chunks.putChunk(chunk);
|
||||
auto& chunkFlags = chunk->flags;
|
||||
|
||||
if (!chunkFlags.loaded) {
|
||||
@ -128,7 +128,7 @@ void ChunksController::createChunk(int x, int z) {
|
||||
chunk->updateHeights();
|
||||
|
||||
if (!chunkFlags.loadedLights) {
|
||||
Lighting::prebuildSkyLight(chunk.get(), level->content->getIndices());
|
||||
Lighting::prebuildSkyLight(chunk.get(), level.content->getIndices());
|
||||
}
|
||||
chunkFlags.loaded = true;
|
||||
chunkFlags.ready = true;
|
||||
|
||||
@ -13,9 +13,9 @@ class WorldGenerator;
|
||||
/// @brief ChunksController manages chunks dynamic loading/unloading
|
||||
class ChunksController {
|
||||
private:
|
||||
Level* level;
|
||||
Chunks* chunks;
|
||||
Lighting* lighting;
|
||||
Level& level;
|
||||
Chunks& chunks;
|
||||
Lighting& lighting;
|
||||
uint padding;
|
||||
std::unique_ptr<WorldGenerator> generator;
|
||||
|
||||
@ -24,7 +24,7 @@ private:
|
||||
bool buildLights(const std::shared_ptr<Chunk>& chunk);
|
||||
void createChunk(int x, int y);
|
||||
public:
|
||||
ChunksController(Level* level, uint padding);
|
||||
ChunksController(Level& level, uint padding);
|
||||
~ChunksController();
|
||||
|
||||
/// @param maxDuration milliseconds reserved for chunks loading
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
|
||||
static debug::Logger logger("level-control");
|
||||
|
||||
LevelController::LevelController(Engine* engine, std::unique_ptr<Level> level)
|
||||
LevelController::LevelController(Engine* engine, std::unique_ptr<Level> levelPtr)
|
||||
: settings(engine->getSettings()),
|
||||
level(std::move(level)),
|
||||
level(std::move(levelPtr)),
|
||||
blocks(std::make_unique<BlocksController>(
|
||||
this->level.get(), settings.chunks.padding.get()
|
||||
*level, settings.chunks.padding.get()
|
||||
)),
|
||||
chunks(std::make_unique<ChunksController>(
|
||||
this->level.get(), settings.chunks.padding.get()
|
||||
*level, settings.chunks.padding.get()
|
||||
)),
|
||||
player(std::make_unique<PlayerController>(
|
||||
settings, this->level.get(), blocks.get()
|
||||
|
||||
@ -195,7 +195,8 @@ PlayerController::PlayerController(
|
||||
: settings(settings), level(level),
|
||||
player(level->getObject<Player>(0)),
|
||||
camControl(player, settings.camera),
|
||||
blocksController(blocksController) {
|
||||
blocksController(blocksController),
|
||||
playerTickClock(20, 3) {
|
||||
}
|
||||
|
||||
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
||||
@ -249,6 +250,13 @@ void PlayerController::update(float delta, bool input, bool pause) {
|
||||
resetKeyboard();
|
||||
}
|
||||
updatePlayer(delta);
|
||||
|
||||
if (playerTickClock.update(delta)) {
|
||||
if (player->getId() % playerTickClock.getParts() ==
|
||||
playerTickClock.getPart()) {
|
||||
scripting::on_player_tick(player.get(), playerTickClock.getTickRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +310,7 @@ void PlayerController::updatePlayer(float delta) {
|
||||
}
|
||||
|
||||
static int determine_rotation(
|
||||
const Block* def, const glm::ivec3& norm, glm::vec3& camDir
|
||||
const Block* def, const glm::ivec3& norm, const glm::vec3& camDir
|
||||
) {
|
||||
if (def && def->rotatable) {
|
||||
const std::string& name = def->rotations.name;
|
||||
@ -461,6 +469,10 @@ void PlayerController::processRightClick(const Block& def, const Block& target)
|
||||
}
|
||||
}
|
||||
if (chosenBlock != vox->id && chosenBlock) {
|
||||
if (!player->isInfiniteItems()) {
|
||||
auto& slot = player->getInventory()->getSlot(player->getChosenSlot());
|
||||
slot.setCount(slot.getCount()-1);
|
||||
}
|
||||
blocksController->placeBlock(
|
||||
player.get(), def, state, coord.x, coord.y, coord.z
|
||||
);
|
||||
@ -522,16 +534,18 @@ void PlayerController::updateInteraction(float delta) {
|
||||
auto iend = selection.position;
|
||||
if (lclick && !input.shift && item.rt.funcsset.on_block_break_by) {
|
||||
if (scripting::on_item_break_block(
|
||||
player.get(), item, iend.x, iend.y, iend.z
|
||||
)) {
|
||||
player.get(), item, iend.x, iend.y, iend.z
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto& target = indices->blocks.require(vox->id);
|
||||
if (lclick && target.breakable) {
|
||||
blocksController->breakBlock(
|
||||
player.get(), target, iend.x, iend.y, iend.z
|
||||
);
|
||||
if (lclick) {
|
||||
if (player->isInstantDestruction() && target.breakable) {
|
||||
blocksController->breakBlock(
|
||||
player.get(), target, iend.x, iend.y, iend.z
|
||||
);
|
||||
}
|
||||
}
|
||||
if (rclick && !input.shift) {
|
||||
bool preventDefault = false;
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "objects/Player.hpp"
|
||||
#include "util/Clock.hpp"
|
||||
|
||||
class Engine;
|
||||
class Camera;
|
||||
@ -53,6 +54,7 @@ class PlayerController {
|
||||
PlayerInput input {};
|
||||
CameraControl camControl;
|
||||
BlocksController* blocksController;
|
||||
util::Clock playerTickClock;
|
||||
|
||||
float interactionTimer = 0.0f;
|
||||
void updateKeyboard();
|
||||
|
||||
@ -18,6 +18,7 @@ extern const luaL_Reg audiolib[];
|
||||
extern const luaL_Reg base64lib[];
|
||||
extern const luaL_Reg bjsonlib[];
|
||||
extern const luaL_Reg blocklib[];
|
||||
extern const luaL_Reg blockwrapslib[]; // gfx.blockwraps
|
||||
extern const luaL_Reg cameralib[];
|
||||
extern const luaL_Reg consolelib[];
|
||||
extern const luaL_Reg corelib[];
|
||||
@ -32,10 +33,10 @@ extern const luaL_Reg itemlib[];
|
||||
extern const luaL_Reg jsonlib[];
|
||||
extern const luaL_Reg mat4lib[];
|
||||
extern const luaL_Reg packlib[];
|
||||
extern const luaL_Reg particleslib[];
|
||||
extern const luaL_Reg particleslib[]; // gfx.particles
|
||||
extern const luaL_Reg playerlib[];
|
||||
extern const luaL_Reg quatlib[];
|
||||
extern const luaL_Reg text3dlib[];
|
||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||
extern const luaL_Reg timelib[];
|
||||
extern const luaL_Reg tomllib[];
|
||||
extern const luaL_Reg utf8lib[];
|
||||
|
||||
43
src/logic/scripting/lua/libs/libblockwraps.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "api_lua.hpp"
|
||||
|
||||
#include "logic/scripting/scripting_hud.hpp"
|
||||
#include "graphics/render/WorldRenderer.hpp"
|
||||
#include "graphics/render/BlockWrapsRenderer.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static int l_wrap(lua::State* L) {
|
||||
auto position = lua::tovec3(L, 1);
|
||||
std::string texture = lua::require_string(L, 2);
|
||||
|
||||
return lua::pushinteger(
|
||||
L, renderer->blockWraps->add(position, std::move(texture))
|
||||
);
|
||||
}
|
||||
|
||||
static int l_unwrap(lua::State* L) {
|
||||
renderer->blockWraps->remove(lua::tointeger(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_pos(lua::State* L) {
|
||||
if (auto wrapper = renderer->blockWraps->get(lua::tointeger(L, 1))) {
|
||||
wrapper->position = lua::tovec3(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_texture(lua::State* L) {
|
||||
if (auto wrapper = renderer->blockWraps->get(lua::tointeger(L, 1))) {
|
||||
wrapper->texture = lua::require_string(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg blockwrapslib[] = {
|
||||
{"wrap", lua::wrap<l_wrap>},
|
||||
{"unwrap", lua::wrap<l_unwrap>},
|
||||
{"set_pos", lua::wrap<l_set_pos>},
|
||||
{"set_texture", lua::wrap<l_set_texture>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@ -98,7 +98,8 @@ static int l_show_overlay(lua::State* L) {
|
||||
if (layout == nullptr) {
|
||||
throw std::runtime_error("there is no ui layout " + util::quote(name));
|
||||
}
|
||||
hud->showOverlay(layout, playerInventory);
|
||||
auto args = lua::tovalue(L, 3);
|
||||
hud->showOverlay(layout, playerInventory, std::move(args));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -188,4 +189,5 @@ const luaL_Reg hudlib[] = {
|
||||
{"_is_content_access", lua::wrap<l_is_content_access>},
|
||||
{"_set_content_access", lua::wrap<l_set_content_access>},
|
||||
{"_set_debug_cheats", lua::wrap<l_set_debug_cheats>},
|
||||
{NULL, NULL}};
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -128,6 +128,34 @@ static int l_set_noclip(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_infinite_items(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
return lua::pushboolean(L, player->isInfiniteItems());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_infinite_items(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
player->setInfiniteItems(lua::toboolean(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_instant_destruction(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
return lua::pushboolean(L, player->isInstantDestruction());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_instant_destruction(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
player->setInstantDestruction(lua::toboolean(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_selected_block(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
if (player->selection.vox.id == BLOCK_VOID) {
|
||||
@ -220,6 +248,10 @@ const luaL_Reg playerlib[] = {
|
||||
{"set_flight", lua::wrap<l_set_flight>},
|
||||
{"is_noclip", lua::wrap<l_is_noclip>},
|
||||
{"set_noclip", lua::wrap<l_set_noclip>},
|
||||
{"is_infinite_items", lua::wrap<l_is_infinite_items>},
|
||||
{"set_infinite_items", lua::wrap<l_set_infinite_items>},
|
||||
{"is_instant_destruction", lua::wrap<l_is_instant_destruction>},
|
||||
{"set_instant_destruction", lua::wrap<l_set_instant_destruction>},
|
||||
{"get_selected_block", lua::wrap<l_get_selected_block>},
|
||||
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
|
||||
{"set_spawnpoint", lua::wrap<l_set_spawnpoint>},
|
||||
@ -228,4 +260,5 @@ const luaL_Reg playerlib[] = {
|
||||
{"set_entity", lua::wrap<l_set_entity>},
|
||||
{"get_camera", lua::wrap<l_get_camera>},
|
||||
{"set_camera", lua::wrap<l_set_camera>},
|
||||
{NULL, NULL}};
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -323,6 +323,21 @@ bool scripting::on_block_interact(
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_player_tick(Player* player, int tps) {
|
||||
auto args = [=](lua::State* L) {
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
lua::pushinteger(L, tps);
|
||||
return 2;
|
||||
};
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
if (pack->worldfuncsset.onplayertick) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(), packid + ":.playertick", args
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool scripting::on_item_use(Player* player, const ItemDef& item) {
|
||||
std::string name = item.name + ".use";
|
||||
return lua::emit_event(
|
||||
@ -724,6 +739,8 @@ void scripting::load_world_script(
|
||||
register_event(env, "on_block_broken", prefix + ":.blockbroken");
|
||||
funcsset.onblockinteract =
|
||||
register_event(env, "on_block_interact", prefix + ":.blockinteract");
|
||||
funcsset.onplayertick =
|
||||
register_event(env, "on_player_tick", prefix + ":.playertick");
|
||||
}
|
||||
|
||||
void scripting::load_layout_script(
|
||||
|
||||
@ -73,6 +73,7 @@ namespace scripting {
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
);
|
||||
bool on_block_interact(Player* player, const Block& block, const glm::ivec3& pos);
|
||||
void on_player_tick(Player* player, int tps);
|
||||
|
||||
/// @brief Called on RMB click with the item selected
|
||||
/// @return true if prevents default action
|
||||
|
||||
@ -32,12 +32,13 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
|
||||
auto L = lua::get_main_state();
|
||||
|
||||
lua::openlib(L, "hud", hudlib);
|
||||
lua::openlib(L, "gfx", "blockwraps", blockwrapslib);
|
||||
lua::openlib(L, "gfx", "particles", particleslib);
|
||||
lua::openlib(L, "gfx", "text3d", text3dlib);
|
||||
|
||||
load_script("hud_classes.lua");
|
||||
|
||||
if (lua::getglobal(L, "__vc_create_hud_rules")) {
|
||||
if (lua::getglobal(L, "__vc_on_hud_open")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -40,4 +40,15 @@ struct UVRegion {
|
||||
float h = getHeight();
|
||||
return glm::vec2(u1 + uv.x * w, v1 + uv.y * h);
|
||||
}
|
||||
|
||||
void scale(float x, float y) {
|
||||
float w = getWidth();
|
||||
float h = getHeight();
|
||||
float cx = (u1 + u2) * 0.5f;
|
||||
float cy = (v1 + v2) * 0.5f;
|
||||
u1 = cx - w * 0.5f * x;
|
||||
v1 = cy - h * 0.5f * y;
|
||||
u2 = cx + w * 0.5f * x;
|
||||
v2 = cy + h * 0.5f * y;
|
||||
}
|
||||
};
|
||||
|
||||
@ -240,6 +240,22 @@ void Player::setNoclip(bool flag) {
|
||||
this->noclip = flag;
|
||||
}
|
||||
|
||||
bool Player::isInfiniteItems() const {
|
||||
return infiniteItems;
|
||||
}
|
||||
|
||||
void Player::setInfiniteItems(bool flag) {
|
||||
infiniteItems = flag;
|
||||
}
|
||||
|
||||
bool Player::isInstantDestruction() const {
|
||||
return instantDestruction;
|
||||
}
|
||||
|
||||
void Player::setInstantDestruction(bool flag) {
|
||||
instantDestruction = flag;
|
||||
}
|
||||
|
||||
entityid_t Player::getEntity() const {
|
||||
return eid;
|
||||
}
|
||||
@ -273,6 +289,8 @@ dv::value Player::serialize() const {
|
||||
|
||||
root["flight"] = flight;
|
||||
root["noclip"] = noclip;
|
||||
root["infinite-items"] = infiniteItems;
|
||||
root["instant-destruction"] = instantDestruction;
|
||||
root["chosen-slot"] = chosenSlot;
|
||||
root["entity"] = eid;
|
||||
root["inventory"] = inventory->serialize();
|
||||
@ -300,6 +318,9 @@ void Player::deserialize(const dv::value& src) {
|
||||
|
||||
flight = src["flight"].asBoolean();
|
||||
noclip = src["noclip"].asBoolean();
|
||||
src.at("infinite-items").get(infiniteItems);
|
||||
src.at("instant-destruction").get(instantDestruction);
|
||||
|
||||
setChosenSlot(src["chosen-slot"].asInteger());
|
||||
eid = src["entity"].asNumber();
|
||||
|
||||
|
||||
@ -49,6 +49,8 @@ class Player : public Object, public Serializable {
|
||||
std::shared_ptr<Inventory> inventory;
|
||||
bool flight = false;
|
||||
bool noclip = false;
|
||||
bool infiniteItems = true;
|
||||
bool instantDestruction = true;
|
||||
entityid_t eid;
|
||||
entityid_t selectedEid;
|
||||
public:
|
||||
@ -86,6 +88,12 @@ public:
|
||||
bool isNoclip() const;
|
||||
void setNoclip(bool flag);
|
||||
|
||||
bool isInfiniteItems() const;
|
||||
void setInfiniteItems(bool flag);
|
||||
|
||||
bool isInstantDestruction() const;
|
||||
void setInstantDestruction(bool flag);
|
||||
|
||||
entityid_t getEntity() const;
|
||||
void setEntity(entityid_t eid);
|
||||
|
||||
|
||||
@ -438,7 +438,7 @@ voxel* Chunks::rayCast(
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter
|
||||
) {
|
||||
) const {
|
||||
float px = start.x;
|
||||
float py = start.y;
|
||||
float pz = start.z;
|
||||
@ -571,7 +571,7 @@ voxel* Chunks::rayCast(
|
||||
|
||||
glm::vec3 Chunks::rayCastToObstacle(
|
||||
const glm::vec3& start, const glm::vec3& dir, float maxDist
|
||||
) {
|
||||
) const {
|
||||
const float px = start.x;
|
||||
const float py = start.y;
|
||||
const float pz = start.z;
|
||||
|
||||
@ -102,11 +102,11 @@ public:
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter = {}
|
||||
);
|
||||
) const;
|
||||
|
||||
glm::vec3 rayCastToObstacle(
|
||||
const glm::vec3& start, const glm::vec3& dir, float maxDist
|
||||
);
|
||||
) const;
|
||||
|
||||
const AABB* isObstacleAt(float x, float y, float z) const;
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ public:
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::shared_ptr<T> getObject(uint64_t id) {
|
||||
std::shared_ptr<T> getObject(uint64_t id) const {
|
||||
static_assert(
|
||||
std::is_base_of<Object, T>::value,
|
||||
"T must be a derived of Object class"
|
||||
|
||||