Merge pull request 'Add entity inventory api (chest minecarts, chest boats)' (#2674) from entity_invs into master
Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2674 Reviewed-by: Johannes Fritz <mrrar@noreply.git.minetest.land>
@ -413,13 +413,21 @@ end
|
|||||||
-- Register one entity for all boat types
|
-- Register one entity for all boat types
|
||||||
minetest.register_entity("mcl_boats:boat", boat)
|
minetest.register_entity("mcl_boats:boat", boat)
|
||||||
|
|
||||||
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian" }
|
local cboat = table.copy(boat)
|
||||||
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
|
cboat.mesh = "mcl_boats_boat_with_chest.b3d"
|
||||||
|
cboat.textures = {"mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png", "mcl_boats_texture_oak_chest_boat.png"}
|
||||||
|
cboat._itemstring = "mcl_boats:chest_boat"
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_boats:chest_boat", cboat)
|
||||||
|
mcl_entity_invs.register_inv("mcl_boats:chest_boat","Boat",27)
|
||||||
|
|
||||||
|
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian", "boat_mangrove", "chest_boat", "chest_boat_spruce", "chest_boat_birch", "chest_boat_jungle", "chest_boat_acacia", "chest_boat_dark_oak", "chest_boat_mangrove" }
|
||||||
|
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat"), S("Mangrove Boat"), S("Oak Chest Boat"), S("Spruce Chest Boat"), S("Birch Chest Boat"), S("Jungle Chest Boat"), S("Acacia Chest Boat"), S("Dark Oak Chest Boat"), S("Mangrove Chest Boat") }
|
||||||
local craftstuffs = {}
|
local craftstuffs = {}
|
||||||
if minetest.get_modpath("mcl_core") then
|
if minetest.get_modpath("mcl_core") then
|
||||||
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian" }
|
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian", "mcl_mangrove:mangrove_wood" }
|
||||||
end
|
end
|
||||||
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian" }
|
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian", "mangrove", "oak_chest", "spruce_chest", "birch_chest", "jungle_chest", "acacia_chest", "dark_oak_chest", "mangrove_chest" }
|
||||||
|
|
||||||
for b=1, #boat_ids do
|
for b=1, #boat_ids do
|
||||||
local itemstring = "mcl_boats:"..boat_ids[b]
|
local itemstring = "mcl_boats:"..boat_ids[b]
|
||||||
@ -469,7 +477,11 @@ for b=1, #boat_ids do
|
|||||||
else
|
else
|
||||||
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
||||||
end
|
end
|
||||||
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
local boat_ent = "mcl_boats:boat"
|
||||||
|
if itemstring:find("chest") then
|
||||||
|
boat_ent = "mcl_boats:chest_boat"
|
||||||
|
end
|
||||||
|
local boat = minetest.add_entity(pos, boat_ent)
|
||||||
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
||||||
boat:get_luaentity()._itemstring = itemstring
|
boat:get_luaentity()._itemstring = itemstring
|
||||||
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
|
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
|
||||||
@ -492,6 +504,14 @@ for b=1, #boat_ids do
|
|||||||
})
|
})
|
||||||
|
|
||||||
local c = craftstuffs[b]
|
local c = craftstuffs[b]
|
||||||
|
if not itemstring:find("chest") then
|
||||||
|
minetest.register_craft({
|
||||||
|
output = itemstring:gsub(":boat",":chest_boat"),
|
||||||
|
recipe = {
|
||||||
|
{"mcl_chests:chest"},
|
||||||
|
{itemstring},
|
||||||
|
},
|
||||||
|
})
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = itemstring,
|
output = itemstring,
|
||||||
recipe = {
|
recipe = {
|
||||||
@ -499,6 +519,7 @@ for b=1, #boat_ids do
|
|||||||
{c, c, c},
|
{c, c, c},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
name = mcl_boats
|
name = mcl_boats
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Adds drivable boats.
|
description = Adds drivable boats.
|
||||||
depends = mcl_player, flowlib, mcl_title
|
depends = mcl_player, flowlib, mcl_title, mcl_entity_invs
|
||||||
optional_depends = mcl_core, doc_identifier
|
optional_depends = mcl_core, doc_identifier
|
||||||
|
|
||||||
|
|
||||||
|
BIN
mods/ENTITIES/mcl_boats/models/boat_with_chest.blend
Normal file
BIN
mods/ENTITIES/mcl_boats/models/mcl_boats_boat_with_chest.b3d
Normal file
BIN
mods/ENTITIES/mcl_boats/textures/mcl_boats_acacia_chest_boat.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
mods/ENTITIES/mcl_boats/textures/mcl_boats_birch_chest_boat.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 6.3 KiB |
BIN
mods/ENTITIES/mcl_boats/textures/mcl_boats_jungle_chest_boat.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
mods/ENTITIES/mcl_boats/textures/mcl_boats_mangrove_boat.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 6.3 KiB |
BIN
mods/ENTITIES/mcl_boats/textures/mcl_boats_oak_chest_boat.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
mods/ENTITIES/mcl_boats/textures/mcl_boats_spruce_chest_boat.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 15 KiB |
11
mods/ENTITIES/mcl_entity_invs/api.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
mcl_entity_invs
|
||||||
|
===============
|
||||||
|
|
||||||
|
Inventories for your entities. It's simple. Depend on mcl_entity_invs and register your entity like so:
|
||||||
|
|
||||||
|
* mcl_entity_invs.register_inv("entity:name","Title shown in formspec",inventory_size,disable_on_righclick)
|
||||||
|
*If disable_on_righclick is true other mods can handle when to show the inventory themselves
|
||||||
|
|
||||||
|
* mcl_entity_invs.show_inv_form(entity,clicker,"Title shown in formspec")
|
||||||
|
|
||||||
|
It works by setting up a detached inventory per entity which is accessed by an id/hash generated from the entities position at creation, the progressed gametime at creation and a random salt.
|
151
mods/ENTITIES/mcl_entity_invs/init.lua
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
mcl_entity_invs = {}
|
||||||
|
|
||||||
|
local open_invs = {}
|
||||||
|
|
||||||
|
local function check_distance(inv,player,count)
|
||||||
|
for _,o in pairs(minetest.get_objects_inside_radius(player:get_pos(),5)) do
|
||||||
|
local l = o:get_luaentity()
|
||||||
|
if l and l._inv_id and inv:get_location().name == l._inv_id then return count end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local inv_callbacks = {
|
||||||
|
allow_take = function(inv, listname, index, stack, player)
|
||||||
|
return check_distance(inv,player,stack:get_count())
|
||||||
|
end,
|
||||||
|
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||||
|
return check_distance(inv,player,count)
|
||||||
|
end,
|
||||||
|
allow_put = function(inv, listname, index, stack, player)
|
||||||
|
return check_distance(inv,player,stack:get_count())
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function load_inv(ent,size)
|
||||||
|
if not ent._inv_id then return end
|
||||||
|
local inv = minetest.get_inventory({type="detached", name=ent._inv_id})
|
||||||
|
if not inv then
|
||||||
|
inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks)
|
||||||
|
inv:set_size("main", size)
|
||||||
|
if ent._items then
|
||||||
|
inv:set_list("main",ent._items)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return inv
|
||||||
|
end
|
||||||
|
|
||||||
|
local function save_inv(ent)
|
||||||
|
if ent._inv then
|
||||||
|
ent._items = {}
|
||||||
|
for i,it in ipairs(ent._inv:get_list("main")) do
|
||||||
|
ent._items[i] = it:to_string()
|
||||||
|
end
|
||||||
|
minetest.remove_detached_inventory(ent._inv_id)
|
||||||
|
ent._inv = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_entity_invs.show_inv_form(ent,player,show_name)
|
||||||
|
if not ent._inv_id then return end
|
||||||
|
if not open_invs[ent] then
|
||||||
|
open_invs[ent] = 0
|
||||||
|
end
|
||||||
|
ent._inv = load_inv(ent,ent._inv_size)
|
||||||
|
open_invs[ent] = open_invs[ent] + 1
|
||||||
|
local playername = player:get_player_name()
|
||||||
|
local rows = 3
|
||||||
|
local cols = (math.ceil(ent._inv_size/rows))
|
||||||
|
local spacing = (9 - cols) / 2
|
||||||
|
local formspec = "size[9,8.75]"
|
||||||
|
.. "label[0,0;" .. minetest.formspec_escape(
|
||||||
|
minetest.colorize("#313131", show_name)) .. "]"
|
||||||
|
.. "list[detached:"..ent._inv_id..";main;"..spacing..",0.5;"..cols..","..rows..";]"
|
||||||
|
.. mcl_formspec.get_itemslot_bg(spacing,0.5,cols,rows)
|
||||||
|
.. "label[0,4.0;" .. minetest.formspec_escape(
|
||||||
|
minetest.colorize("#313131", "Inventory")) .. "]"
|
||||||
|
.. "list[current_player;main;0,4.5;9,3;9]"
|
||||||
|
.. mcl_formspec.get_itemslot_bg(0,4.5,9,3)
|
||||||
|
.. "list[current_player;main;0,7.74;9,1;]"
|
||||||
|
.. mcl_formspec.get_itemslot_bg(0,7.74,9,1)
|
||||||
|
.. "listring[detached:"..ent._inv_id..";main]"
|
||||||
|
.. "listring[current_player;main]"
|
||||||
|
minetest.show_formspec(playername,ent._inv_id,formspec)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function drop_inv(ent)
|
||||||
|
local pos = ent.object:get_pos()
|
||||||
|
for i,it in pairs(ent._items) do
|
||||||
|
local p = vector.add(pos,vector.new(math.random() - 0.5, math.random()-0.5, math.random()-0.5))
|
||||||
|
minetest.add_item(p,it)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
for k,v in pairs(open_invs) do
|
||||||
|
if formname == k._inv_id then
|
||||||
|
open_invs[k] = open_invs[k] - 1
|
||||||
|
if open_invs[k] < 1 then
|
||||||
|
save_inv(k)
|
||||||
|
open_invs[k] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function mcl_entity_invs.register_inv(entity_name,show_name,size,no_on_righclick)
|
||||||
|
assert(minetest.registered_entities[entity_name],"mcl_entity_invs.register_inv called with invalid entity: "..tostring(entity_name))
|
||||||
|
minetest.registered_entities[entity_name]._inv_size = size
|
||||||
|
|
||||||
|
local old_oa = minetest.registered_entities[entity_name].on_activate
|
||||||
|
minetest.registered_entities[entity_name].on_activate = function(self,staticdata,dtime_s)
|
||||||
|
local r
|
||||||
|
if old_oa then r=old_oa(self,clicker) end
|
||||||
|
local d = minetest.deserialize(staticdata)
|
||||||
|
if type(d) == "table" and d._inv_id then
|
||||||
|
self._inv_id = d._inv_id
|
||||||
|
self._items = d._items
|
||||||
|
self._inv_size = d._inv_size
|
||||||
|
else
|
||||||
|
self._inv_id="entity_inv_"..minetest.sha1(minetest.get_gametime()..minetest.pos_to_string(self.object:get_pos())..tostring(math.random()))
|
||||||
|
--gametime and position for collision safety and math.random salt to protect against position brute-force
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
if not no_on_righclick then
|
||||||
|
local old_rc = minetest.registered_entities[entity_name].on_rightclick
|
||||||
|
minetest.registered_entities[entity_name].on_rightclick = function(self,clicker)
|
||||||
|
mcl_entity_invs.show_inv_form(self,clicker,show_name)
|
||||||
|
if old_rc then return old_rc(self,clicker) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_gsd = minetest.registered_entities[entity_name].get_staticdata
|
||||||
|
minetest.registered_entities[entity_name].get_staticdata = function(self)
|
||||||
|
local old_sd = old_gsd(self)
|
||||||
|
local d = minetest.deserialize(old_sd)
|
||||||
|
assert(type(d) == "table","mcl_entity_invs currently only works with entities that return a (serialized) table in get_staticdata. "..tostring(self.name).." returned: "..tostring(old_sd))
|
||||||
|
d._inv_id = self._inv_id
|
||||||
|
d._inv_size = self._inv_size
|
||||||
|
d._items = {}
|
||||||
|
if self._items then
|
||||||
|
for i,it in ipairs(self._items) do
|
||||||
|
d._items[i] = it
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return minetest.serialize(d)
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_ode = minetest.registered_entities[entity_name].on_deactivate
|
||||||
|
minetest.registered_entities[entity_name].on_deactivate = function(self,removal)
|
||||||
|
save_inv(self)
|
||||||
|
if old_ode then return old_ode(self,removal) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_od = minetest.registered_entities[entity_name].on_death
|
||||||
|
minetest.registered_entities[entity_name].on_death = function(self,clicker)
|
||||||
|
drop_inv(self)
|
||||||
|
minetest.remove_detached_inventory(self._inv_id)
|
||||||
|
if old_od then return old_od(self,clicker) end
|
||||||
|
end
|
||||||
|
end
|
3
mods/ENTITIES/mcl_entity_invs/mod.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
name = mcl_entity_invs
|
||||||
|
author = cora
|
||||||
|
depends = mcl_formspec
|
@ -677,7 +677,8 @@ register_minecart(
|
|||||||
{ "mcl_chests_normal.png", "mcl_minecarts_minecart.png" },
|
{ "mcl_chests_normal.png", "mcl_minecarts_minecart.png" },
|
||||||
"mcl_minecarts_minecart_chest.png",
|
"mcl_minecarts_minecart_chest.png",
|
||||||
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
||||||
nil, nil, false)
|
nil, nil, true)
|
||||||
|
mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27)
|
||||||
|
|
||||||
-- Minecart with Furnace
|
-- Minecart with Furnace
|
||||||
register_minecart(
|
register_minecart(
|
||||||
@ -847,6 +848,7 @@ minetest.register_craft({
|
|||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
--]]
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:chest_minecart",
|
output = "mcl_minecarts:chest_minecart",
|
||||||
@ -854,7 +856,7 @@ minetest.register_craft({
|
|||||||
{"mcl_chests:chest"},
|
{"mcl_chests:chest"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})]]
|
})
|
||||||
|
|
||||||
|
|
||||||
if has_mcl_wip then
|
if has_mcl_wip then
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
name = mcl_minecarts
|
name = mcl_minecarts
|
||||||
author = Krock
|
author = Krock
|
||||||
description = Minecarts are vehicles to move players quickly on rails.
|
description = Minecarts are vehicles to move players quickly on rails.
|
||||||
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs
|
||||||
optional_depends = doc_identifier, mcl_wip
|
optional_depends = doc_identifier, mcl_wip
|
||||||
|
|
||||||
|