diff --git a/assets/base/script/game/player_interact.lua b/assets/base/script/game/player_interact.lua index e7e57d0b..b5858fc2 100644 --- a/assets/base/script/game/player_interact.lua +++ b/assets/base/script/game/player_interact.lua @@ -1,6 +1,8 @@ + -- zepha.block_interact_or_place(player: Player , target: Target [, stack: ItemStack]): ItemStack, Vector | nil -- Calls zepha.block_interact if the targeted block can be interacted with, returns the passed in stack or wield stack and nil. -- Otherwise, calls zepha.block_place and returned the function's returned values. + function zepha.block_interact_or_place(player, target, stack) local stack = stack or player:get_wield_stack() local target_block = target.dim:get_block(target.pos) @@ -20,9 +22,11 @@ function zepha.block_interact_or_place(player, target, stack) return zepha.block_place(player, target, stack) end + -- zepha.block_interact(player: Player, target: Target): nil -- Interacts with the targeted block. Returns true if the block has an on_interact or -- on_interact_client callback, returns false otherwise. + function zepha.block_interact(player, target) local block = target.dim:get_block(target.pos) local def = zepha.registered_blocks[block] @@ -35,10 +39,12 @@ function zepha.block_interact(player, target) return def.on_interact or def.on_interact_client end + -- zepha.block_place(player: Player, target: Target [, stack: ItemStack]): ItemStack, Vector | nil -- Attempts to place `stack` or the wield stack in the world, on success returning the -- wield stack with one count removed, and the placed position, or on failure returning the -- original stack and nil. + function zepha.block_place(player, target, stack) local stack = stack or player:get_wield_stack() local stack_def = zepha.registered_blocks[stack and stack.name or nil] @@ -50,9 +56,11 @@ function zepha.block_place(player, target, stack) return stack, target.pos_above end + -- zepha.block_hit(player: Player, target: Target): float, float -- Hits the block at `target` using the player's wielded item. -- Returns the damage done followed by the timeout before the next hit can begin. + function zepha.block_hit(player, target) local block = player.dim:get_block(target.pos) local def = zepha.registered_blocks[block] @@ -71,8 +79,10 @@ function zepha.block_hit(player, target) return damage, timeout end + -- zepha.block_break(player: Player, target: Target): -- Breaks the block at `target`. + function zepha.block_break(player, target) local block = player.dim:get_block(target.pos) local def = zepha.registered_blocks[block] or {} @@ -88,4 +98,22 @@ function zepha.block_break(player, target) local cb = zepha.server and "after_break" or "after_break_client" if type(def[cb]) == "function" then def[cb](table.unpack(args)) end zepha.trigger(cb, table.unpack(args)) +end + + +-- zepha.item_use(player: Player, target: Target [, stack: ItemStack]): ItemStack +-- Attempts to use `stack` or the wield stack in, +-- returning a modified wieldstack or nil if the stack is unchanged. + +function zepha.item_use(player, target, stack) + local stack = stack or player:get_wield_stack() + local def = zepha.registered_items[stack and stack.name or nil] + + if stack == nil or def == nil then return stack end + + local args = { stack, target, player } + local cb = zepha.server and "on_use" or "on_use_client" + + if type(def[cb]) == "function" then return def[cb](table.unpack(args)) end + return nil end \ No newline at end of file diff --git a/src/client/conn/ClientNetworkInterpreter.cpp b/src/client/conn/ClientNetworkInterpreter.cpp index bc3d4680..559db4a3 100644 --- a/src/client/conn/ClientNetworkInterpreter.cpp +++ b/src/client/conn/ClientNetworkInterpreter.cpp @@ -123,6 +123,10 @@ void ClientNetworkInterpreter::blockPlaceOrInteract(const Target& target) { .packet(Packet::Type::BLOCK_PLACE_OR_INTERACT).sendTo(connection.getPeer(), Packet::Channel::INTERACT); } +void ClientNetworkInterpreter::wieldItemUse(const Target& target) { + Serializer().packet(Packet::Type::WIELD_ITEM_USE).sendTo(connection.getPeer(), Packet::Channel::INTERACT); +} + void ClientNetworkInterpreter::invWatch(const std::string& inv, const std::string& list) { Serializer().append(inv).append(list).packet(Packet::Type::INV_WATCH) .sendTo(connection.getPeer(), Packet::Channel::INTERACT); @@ -141,4 +145,4 @@ void ClientNetworkInterpreter::invInteract(const std::string& inv, const std::st void ClientNetworkInterpreter::sendPacket(const Packet& packet, Packet::Channel channel) { packet.sendTo(connection.getPeer(), channel); -} +} \ No newline at end of file diff --git a/src/client/conn/ClientNetworkInterpreter.h b/src/client/conn/ClientNetworkInterpreter.h index c347dbd3..715e0cb5 100644 --- a/src/client/conn/ClientNetworkInterpreter.h +++ b/src/client/conn/ClientNetworkInterpreter.h @@ -44,6 +44,8 @@ class ClientNetworkInterpreter { void blockPlaceOrInteract(const Target& target); + void wieldItemUse(const Target& target); + void invWatch(const std::string& inv, const std::string& list); void invUnwatch(const std::string& inv, const std::string& list); diff --git a/src/game/def/CraftItemDef.cpp b/src/game/def/CraftItemDef.cpp index 02e865c3..2cd61a29 100644 --- a/src/game/def/CraftItemDef.cpp +++ b/src/game/def/CraftItemDef.cpp @@ -103,4 +103,8 @@ void CraftItemDef::createModel(TextureAtlas& atlas) { mesh->create(vertices, indices); entityModel->fromMesh(std::move(mesh)); -} \ No newline at end of file +} + +bool CraftItemDef::hasUse() { + return callbacks.count(CraftItemDef::Callback::USE) || callbacks.count(CraftItemDef::Callback::USE_CLIENT); +} diff --git a/src/game/def/CraftItemDef.h b/src/game/def/CraftItemDef.h index f72555ef..9d610621 100644 --- a/src/game/def/CraftItemDef.h +++ b/src/game/def/CraftItemDef.h @@ -31,6 +31,8 @@ public: void createModel(TextureAtlas& atlas); + bool hasUse(); + std::vector textures {}; std::vector> textureRefs {}; diff --git a/src/lua/usertype/Player.h b/src/lua/usertype/Player.h index 34e60c09..65ff9dbe 100644 --- a/src/lua/usertype/Player.h +++ b/src/lua/usertype/Player.h @@ -71,7 +71,7 @@ namespace Api::Usertype { }; class LocalPlayer : public ServerPlayer { - public: + public: LocalPlayer(PlayerPtr player) : ServerPlayer(player) {} bool is_in_menu(); diff --git a/src/lua/usertype/Target.cpp b/src/lua/usertype/Target.cpp index ee59323a..3fb1e28f 100644 --- a/src/lua/usertype/Target.cpp +++ b/src/lua/usertype/Target.cpp @@ -6,24 +6,28 @@ #include "../Lua.h" -Api::Usertype::Target::Target(const ::Target& target) : - pos(target.data.block.pos), - type(target.type), - dim(Dimension(target.dim)), - pos_above(target.getAbovePos()) {} - -std::string Api::Usertype::Target::getType() { - return type == ::Target::Type::BLOCK ? "block" : - type == ::Target::Type::ENTITY ? "entity" : - "nothing"; +Api::Usertype::Target::Target(const ::Target& target) { + if (target.type == ::Target::Type::BLOCK) { + type = "block"; + dim = target.dim; + pos = target.data.block.pos; + pos_above = target.getAbovePos(); + } + else if (target.type == ::Target::Type::ENTITY) { + type = "entity"; + dim = target.dim; + id = target.data.entity.id; + } + else type = "nothing"; } void Api::Usertype::Target::bind(State, sol::state& lua, sol::table& core) { lua.new_usertype("Target", - "type", sol::property(&Target::getType), + "type", sol::readonly(&Target::type), - "pos", sol::readonly(&Target::pos), + "id", sol::readonly(&Target::id), "dim", sol::readonly(&Target::dim), + "pos", sol::readonly(&Target::pos), "pos_above", sol::readonly(&Target::pos_above) ); } \ No newline at end of file diff --git a/src/lua/usertype/Target.h b/src/lua/usertype/Target.h index 94318cac..749d132b 100644 --- a/src/lua/usertype/Target.h +++ b/src/lua/usertype/Target.h @@ -13,16 +13,14 @@ namespace Api::Usertype { class Target : public SubgameUsertype { - public: + public: Target(const ::Target& target); - std::string getType(); - - ::Target::Type type; - - Dimension dim; - glm::ivec3 pos; - glm::ivec3 pos_above; + std::string type; + std::optional id; + std::optional dim; + std::optional pos; + std::optional pos_above; static void bind(State state, sol::state& lua, sol::table& core); }; diff --git a/src/server/Server.cpp b/src/server/Server.cpp index c6135391..81e5b5f8 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -136,6 +136,10 @@ void Server::playerPacketReceived(PacketView& p, PlayerPtr player) { player->getDim()->blockPlaceOrInteract(Target(player->getDim(), pos, face), player); break; + case Packet::Type::WIELD_ITEM_USE: + player->getDim()->wieldItemUse(Target(), player); + break; + case Packet::Type::INV_WATCH: p.d.read(source).read(list); if (!world->getRefs().s()->addWatcher(source, list, player->getId())) diff --git a/src/util/net/Packet.h b/src/util/net/Packet.h index 970fecae..d7ea99e3 100644 --- a/src/util/net/Packet.h +++ b/src/util/net/Packet.h @@ -35,6 +35,8 @@ class Packet { // Block BLOCK_HIT, BLOCK_PLACE, BLOCK_INTERACT, BLOCK_PLACE_OR_INTERACT, BLOCK_SET, + // Item + WIELD_ITEM_USE, // Entity ENTITY_INFO, ENTITY_REMOVED, // Inventory diff --git a/src/world/dim/Dimension.h b/src/world/dim/Dimension.h index e13183bc..9fde271b 100644 --- a/src/world/dim/Dimension.h +++ b/src/world/dim/Dimension.h @@ -36,6 +36,8 @@ public: virtual void blockInteract(const Target& target, PlayerPtr player) = 0; + virtual void wieldItemUse(const Target& target, PlayerPtr player) = 0; + virtual long long nextEntityInd() = 0; // Calculate light propogation around MapBlock edges, diff --git a/src/world/dim/LocalDimension.cpp b/src/world/dim/LocalDimension.cpp index 09c85ac5..c85706e6 100644 --- a/src/world/dim/LocalDimension.cpp +++ b/src/world/dim/LocalDimension.cpp @@ -151,6 +151,17 @@ double LocalDimension::blockHit(const Target& target, PlayerPtr player) { return timeout; } +void LocalDimension::wieldItemUse(const Target& target, PlayerPtr player) { + sol::optional stack = game->getParser().safe_function( + game->getParser().core["item_use"], Api::Usertype::LocalPlayer(player.l()), Api::Usertype::Target(target)); + + static_cast(world).getNet().wieldItemUse(target); + + auto inv = player->getInventory(); + if (stack && inv->hasList(player->getWieldList())) + inv->getList(player->getWieldList())->setStack(player->getWieldIndex(), ItemStack(*stack, game)); +} + void LocalDimension::setMeshChunk(std::shared_ptr meshChunk) { if (renderRefs.count(meshChunk->getPos())) removeMeshChunk(meshChunk->getPos()); renderElems.push_back(std::static_pointer_cast(meshChunk)); diff --git a/src/world/dim/LocalDimension.h b/src/world/dim/LocalDimension.h index 1a277c91..726d6623 100644 --- a/src/world/dim/LocalDimension.h +++ b/src/world/dim/LocalDimension.h @@ -46,6 +46,8 @@ public: virtual double blockHit(const Target& target, PlayerPtr player) override; + virtual void wieldItemUse(const Target& target, PlayerPtr player) override; + void setMeshChunk(std::shared_ptr chunk); void removeMeshChunk(const glm::ivec3& pos); diff --git a/src/world/dim/ServerDimension.cpp b/src/world/dim/ServerDimension.cpp index 476631d6..ea0b424d 100644 --- a/src/world/dim/ServerDimension.cpp +++ b/src/world/dim/ServerDimension.cpp @@ -85,6 +85,15 @@ void ServerDimension::blockPlaceOrInteract(const Target& target, PlayerPtr playe inv->getList(player->getWieldList())->setStack(player->getWieldIndex(), ItemStack(*stack, game)); } +void ServerDimension::wieldItemUse(const Target& target, PlayerPtr player) { + sol::optional stack = game->getParser().safe_function( + game->getParser().core["item_use"], Api::Usertype::ServerPlayer(player), Api::Usertype::Target(target)); + + auto inv = player->getInventory(); + if (stack && inv->hasList(player->getWieldList())) + inv->getList(player->getWieldList())->setStack(player->getWieldIndex(), ItemStack(*stack, game)); +} + void ServerDimension::setChunk(std::shared_ptr chunk) { std::shared_ptr existing = getChunk(chunk->getPos()); if (existing) chunk = combineChunks(chunk, existing); diff --git a/src/world/dim/ServerDimension.h b/src/world/dim/ServerDimension.h index 332ec472..c9256d9a 100644 --- a/src/world/dim/ServerDimension.h +++ b/src/world/dim/ServerDimension.h @@ -33,6 +33,8 @@ class ServerDimension : public Dimension { virtual double blockHit(const Target& target, PlayerPtr player) override; + virtual void wieldItemUse(const Target& target, PlayerPtr player) override; + virtual long long nextEntityInd() override; void addLuaEntity(Api::Usertype::Entity entity); diff --git a/src/world/player/LocalPlayer.cpp b/src/world/player/LocalPlayer.cpp index 627f59a8..da941423 100644 --- a/src/world/player/LocalPlayer.cpp +++ b/src/world/player/LocalPlayer.cpp @@ -8,9 +8,12 @@ #include "world/LocalWorld.h" #include "game/def/BlockDef.h" #include "util/net/NetField.h" +#include "lua/usertype/Player.h" +#include "lua/usertype/Target.h" #include "client/graph/Renderer.h" #include "world/dim/chunk/Chunk.h" #include "util/net/Deserializer.h" +#include "game/def/CraftItemDef.h" #include "world/dim/ent/Collision.h" #include "client/conn/ClientNetworkInterpreter.h" @@ -41,7 +44,7 @@ void LocalPlayer::update(Input& input, double delta, glm::vec2 mouseDelta) { findTarget(input); updateWireframe(); - if (!gameGui.isInMenu()) interact(input, delta); + if (!gameGui.isInMenu()) updateInteract(input, delta); } void LocalPlayer::setPos(glm::vec3 pos, bool assert) { @@ -399,20 +402,42 @@ void LocalPlayer::findTarget(Input& input) { target = newTarget; } -void LocalPlayer::interact(Input& input, double delta) { - if (target.type == Target::Type::BLOCK) { - if (input.mouseDown(GLFW_MOUSE_BUTTON_LEFT) && breakTime == 0) { +void LocalPlayer::updateInteract(Input& input, double delta) { + if (breakTime > 0) breakTime += delta; + if (breakTime > breakInterval) breakTime = 0; + + if (input.mouseDown(GLFW_MOUSE_BUTTON_LEFT)) { + if (target.type == Target::Type::BLOCK && breakTime == 0) { + auto& targetedBlock = game->getDefs().blockFromId(dim->getBlock(target.data.block.pos)); breakInterval = dim->blockHit(target, static_cast(dim->getWorld()).getPlayer()); breakTime += delta; } - else if (input.mousePressed(GLFW_MOUSE_BUTTON_RIGHT)) - dim->blockPlaceOrInteract(target, static_cast(dim->getWorld()).getPlayer()); } - - if (breakTime > 0) breakTime += delta; - if (breakTime > breakInterval) breakTime = 0; + else if (input.mousePressed(GLFW_MOUSE_BUTTON_RIGHT)) { + if (target.type == Target::Type::BLOCK) { + auto& wieldItem = game->getDefs().fromId(this->wieldItem); + auto& targetedBlock = game->getDefs().blockFromId(dim->getBlock(target.data.block.pos)); + + if (targetedBlock.hasInteraction()) + dim->blockInteract(target, static_cast(dim->getWorld()).getPlayer()); + else if (wieldItem.type == ItemDef::Type::CRAFTITEM && static_cast(wieldItem).hasUse()) + dim->wieldItemUse(target, static_cast(dim->getWorld()).getPlayer()); + else if (wieldItem.type == ItemDef::Type::BLOCK) + dim->blockPlace(target, static_cast(dim->getWorld()).getPlayer()); + } + else if (target.type == Target::Type::ENTITY) { + auto& wieldItem = game->getDefs().fromId(this->wieldItem); + if (wieldItem.type == ItemDef::Type::CRAFTITEM && static_cast(wieldItem).hasUse()) + dim->wieldItemUse(target, static_cast(dim->getWorld()).getPlayer()); + } + else { + auto& wieldItem = game->getDefs().fromId(this->wieldItem); + if (wieldItem.type == ItemDef::Type::CRAFTITEM && static_cast(wieldItem).hasUse()) + dim->wieldItemUse(target, static_cast(dim->getWorld()).getPlayer()); + } + } } LocalPlayer::~LocalPlayer() { renderer.window.removeResizeCallback("player"); -} \ No newline at end of file +} diff --git a/src/world/player/LocalPlayer.h b/src/world/player/LocalPlayer.h index 99008820..6fe8ba37 100644 --- a/src/world/player/LocalPlayer.h +++ b/src/world/player/LocalPlayer.h @@ -216,7 +216,7 @@ private: * @param delta - The delta time elapsed in the last frame. */ - void interact(Input& input, double delta); + void updateInteract(Input& input, double delta); GameGui gameGui; Renderer& renderer; diff --git a/subgames/zeus/mods/zeus_default/script/entity/bee.lua b/subgames/zeus/mods/zeus_default/script/entity/bee.lua index fce86251..b9c4886e 100644 --- a/subgames/zeus/mods/zeus_default/script/entity/bee.lua +++ b/subgames/zeus/mods/zeus_default/script/entity/bee.lua @@ -57,7 +57,7 @@ zepha.register_entity("zeus:default:bee", { } local fly = false - local def = zepha.registered_blocks[self.object.dim:get_block(self.object.pos - V{ 0, 2, 0 })] + local def = zepha.registered_blocks[self.object.dim:get_block(self.object.pos:floor() - V{ 0, 2, 0 })] if def and (def.solid == nil or def.solid ~= false) then fly = true end if fly and self.object.vel.y <= 0 then @@ -68,22 +68,9 @@ zepha.register_entity("zeus:default:bee", { }) if zepha.server then - zepha.bind("message", function(channel, message, player) - if channel ~= "zeus:default:spawn" or message ~= "bee" then return end - player.dim:add_entity(player.pos + V(0, 1.7, 0), "zeus:default:bee") - end) - zepha.bind("new_player", function(player) for i = 0, 10 do player.dim:add_entity(player.pos + V { math.random(-100, 100), 30, math.random(-100, 100) }, "zeus:default:bee") end end) -else - zepha.register_keybind("zeus:default:spawn_bee", { - description = "Spawn Bee", - default = zepha.keys.b, - on_press = function() - zepha.send_message("zeus:default:spawn", "bee"); - end - }) end \ No newline at end of file diff --git a/subgames/zeus/mods/zeus_default/script/entity/rabbit.lua b/subgames/zeus/mods/zeus_default/script/entity/rabbit.lua index 9a161624..9d19660d 100644 --- a/subgames/zeus/mods/zeus_default/script/entity/rabbit.lua +++ b/subgames/zeus/mods/zeus_default/script/entity/rabbit.lua @@ -69,22 +69,9 @@ zepha.register_entity("zeus:default:rabbit", { }) if zepha.server then - zepha.bind("message", function(channel, message, player) - if channel ~= "zeus:default:spawn" or message ~= "rabbit" then return end - player.dim:add_entity(player.pos + V(0, 1.7, 0), "zeus:default:rabbit") - end) - zepha.bind("new_player", function(player) for i = 0, 5 do player.dim:add_entity(player.pos + V { math.random(-100, 100), 30, math.random(-100, 100) }, "zeus:default:rabbit") end end) -else - zepha.register_keybind("zeus:default:spawn_rabbit", { - description = "Spawn Rabbit", - default = zepha.keys.v, - on_press = function() - zepha.send_message("zeus:default:spawn", "rabbit"); - end - }) end \ No newline at end of file diff --git a/subgames/zeus/mods/zeus_world/conf.json b/subgames/zeus/mods/zeus_world/conf.json index 3f5dcfd4..80f23f82 100644 --- a/subgames/zeus/mods/zeus_world/conf.json +++ b/subgames/zeus/mods/zeus_world/conf.json @@ -2,6 +2,6 @@ "name": "zeus:world", "description": "World and Mapgen definitions for the Zeus subgame.", "version": "0.0.1", - "depends": [ "zeus:default", "zeus:flowers" ] + "depends": [ "zeus:default", "zeus:flowers", "@auri:hot_wheel" ] } diff --git a/subgames/zeus/mods/zeus_world/script/keys.lua b/subgames/zeus/mods/zeus_world/script/keys.lua index 2954e521..a23d6318 100644 --- a/subgames/zeus/mods/zeus_world/script/keys.lua +++ b/subgames/zeus/mods/zeus_world/script/keys.lua @@ -1,24 +1,42 @@ zepha.register_item(':key_iron', { name = 'Iron Key', - textures = { 'zeus:world:key_iron' } + textures = { 'zeus:world:key_iron' }, + + on_use = function(stack, target, player) + player.dim:add_entity(player.pos, "zeus:default:test") + stack.count = stack.count - 1 + return stack + end }) zepha.register_item(':key_silver', { name = 'Silver Key', - textures = { 'zeus:world:key_silver' } + textures = { 'zeus:world:key_silver' }, + + on_use = function(stack, target, player) + player.dim:add_entity(player.pos, "zeus:default:rabbit") + stack.count = stack.count - 1 + return stack + end }) zepha.register_item(':key_gold', { name = 'Gold Key', - textures = { 'zeus:world:key_gold' } + textures = { 'zeus:world:key_gold' }, + + on_use = function(stack, target, player) + player.dim:add_entity(player.pos, "zeus:default:bee") + stack.count = stack.count - 1 + return stack + end }) if zepha.server then zepha.bind("new_player", function(player) - local inv = player:get_inventory():get_list('main') - inv:add_stack({'zeus:world:key_iron', 8}) - inv:add_stack({'zeus:world:key_silver', 4}) - inv:add_stack({'zeus:world:key_gold', 2}) + local inv = player:get_inventory():get_list('hot_wheel_2') + inv:add_stack({'zeus:world:key_iron', 64}) + inv:add_stack({'zeus:world:key_silver', 64}) + inv:add_stack({'zeus:world:key_gold', 64}) end) end