Merge pull request #374 from MihailRis/block-cracks

Block Cracks & update Player
This commit is contained in:
MihailRis 2024-11-21 07:37:09 +03:00 committed by GitHub
commit 4c0c6dd51a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 518 additions and 115 deletions

View File

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

View File

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

View File

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

View File

@ -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)
-- Добавляет постоянный элемент на экран. Элемент не удаляется при
-- закрытии инвентаря. Чтобы не перекрывать затенение в режиме

View File

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

View File

@ -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()`.
## События предметов

View File

@ -1,4 +1,7 @@
{
"atlases": [
{"name": "cracks", "type": "separate"}
],
"sounds": [
"blocks/door_open",
"blocks/door_close",

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,6 +99,7 @@ struct world_funcs_set {
bool onblockplaced : 1;
bool onblockbroken : 1;
bool onblockinteract : 1;
bool onplayertick : 1;
};
class ContentPackRuntime {

View File

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

View File

@ -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]));

View 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);
}

View 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);
};

View File

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

View File

@ -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]
);

View File

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

View File

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

View File

@ -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");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();

View File

@ -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[];

View 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}
};

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

View File

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