Interaction with formspec: part II

Ability to move items within inventory or with open formspecs, and to craft items.
This commit is contained in:
Novatux 2015-02-13 09:41:08 +01:00
parent a906826a24
commit b50ad6e748
5 changed files with 248 additions and 10 deletions

223
api.lua
View File

@ -12,7 +12,7 @@ local function pointable(stack, node)
return nodedef and def and (nodedef.pointable or (nodedef.liquidtype ~= "none" and def.liquid_pointable))
end
local function create_turtle_player(turtle_id, dir, only_player)
function turtles.create_turtle_player(turtle_id, dir, only_player)
local info = turtles.get_turtle_info(turtle_id)
local inv = turtles.get_turtle_inventory(turtle_id)
local pitch
@ -187,7 +187,7 @@ end
local function turtle_dig(turtle, cptr, dir)
tl.close_form(turtle)
local player, pointed_thing = create_turtle_player(turtle, dir)
local player, pointed_thing = turtles.create_turtle_player(turtle, dir)
if pointed_thing == nil then return end
local info = turtles.get_turtle_info(turtle)
local wieldstack = player:get_wielded_item()
@ -224,7 +224,7 @@ end
local function turtle_place(turtle, cptr, dir)
tl.close_form(turtle)
local player, pointed_thing = create_turtle_player(turtle, dir)
local player, pointed_thing = turtles.create_turtle_player(turtle, dir)
if pointed_thing == nil then return end
local formspec = minetest.get_meta(pointed_thing.under):get_string("formspec")
if formspec ~= nil then
@ -306,13 +306,23 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
end)
local function get_turtle_formspec_player(turtle)
local info = turtles.get_turtle_info(turtle)
local dir
if info.formspec_type and info.formspec_type.type == "node" then
dir = vector.normalize(vector.sub(info.formspec_type.pos, info.spos))
else
dir = minetest.facedir_to_dir(info.dir)
end
return turtles.create_turtle_player(turtle, dir, true)
end
local function send_fields(turtle)
local info = turtles.get_turtle_info(turtle)
local fields = info.formspec_fields
info.formspec_fields = {}
local player = get_turtle_formspec_player(turtle)
if info.formspec_type.type == "show" then
local dir = minetest.facedir_to_dir(info.dir)
local player = create_turtle_player(turtle, dir, true)
for _, func in ipairs(minetest.registered_on_receive_fields) do
if func(player, info.formspec_type.formname, fields) then
return
@ -322,8 +332,6 @@ local function send_fields(turtle)
local pos = info.formspec_type.pos
local nodedef = minetest.registered_nodes[minetest.get_node(pos).name]
if nodedef and nodedef.on_receive_fields then
local dir = vector.normalize(vector.sub(pos, info.spos))
local player = create_turtle_player(turtle, dir, true)
nodedef.on_receive_fields(vector.new(pos), "", fields, player)
end
end
@ -507,6 +515,10 @@ local function get_element_by_id(formspec, elem_id)
return formspec.lists[elem_id + 1]
end
---------------
-- Inventory --
---------------
local function get_inventory_from_location(turtle, location)
if location == "current_player" then
return turtles.get_turtle_inventory(turtle)
@ -537,7 +549,7 @@ function tl.get_stack(turtle, cptr, elem_id, slot, addr)
local formspec = info.open_formspec
local elem = get_element_by_id(formspec, elem_id)
if elem and elem.location and
elem.start_index <= slot and slot <= elem.start_index + elem.size then
elem.start_index <= slot and slot < elem.start_index + elem.size then
local inv = get_inventory_from_location(turtle, elem.location)
if inv then
stack = inv:get_stack(elem.listname, slot)
@ -546,3 +558,198 @@ function tl.get_stack(turtle, cptr, elem_id, slot, addr)
end
push_stack(cptr, addr, stack)
end
local function same_location(location1, location2)
if location1.type ~= location2.type then
return false
end
if location1.type == "node" then
return vector.equals(location1.pos, location2.pos)
else
return location1.name == location2.name
end
end
local function get_callbacks(location)
if location.type == "node" then
local node = minetest.get_node(location.pos)
local nodedef = minetest.registered_nodes[node.name] or {}
return {allow_move = function(list1, index1, list2, index2, count, player)
return nodedef.allow_metadata_inventory_move and
nodedef.allow_metadata_inventory_move(location.pos, list1, index1, list2, index2, count, player) or
count
end,
allow_take = function(list, index, stack, player)
return nodedef.allow_metadata_inventory_take and
nodedef.allow_metadata_inventory_take(location.pos, list, index, stack, player) or
stack:get_count()
end,
allow_put = function(list, index, stack, player)
return nodedef.allow_metadata_inventory_put and
nodedef.allow_metadata_inventory_put(location.pos, list, index, stack, player) or
stack:get_count()
end,
on_move = function(list1, index1, list2, index2, count, player)
if nodedef.on_metadata_inventory_move then
nodedef.on_metadata_inventory_move(location.pos, list1, index1, list2, index2, count, player)
end
end,
on_take = function(list, index, stack, player)
if nodedef.on_metadata_inventory_take then
nodedef.on_metadata_inventory_take(location.pos, list, index, stack, player)
end
end,
on_put = function(list, index, stack, player)
if nodedef.on_metadata_inventory_put then
nodedef.on_metadata_inventory_put(location.pos, list, index, stack, player)
end
end,
}
elseif location.type == "detached" then
print("WARNING: not yet implemented")
return {allow_move = function(list1, index1, list2, index2, count, player)
return count
end,
allow_take = function(list, index, stack, player)
return stack:get_count()
end,
allow_put = function(list, index, stack, player)
return stack:get_count()
end,
on_move = function() end,
on_take = function() end,
on_put = function() end,
}
else
return {allow_move = function(list1, index1, list2, index2, count, player)
return count
end,
allow_take = function(list, index, stack, player)
return stack:get_count()
end,
allow_put = function(list, index, stack, player)
return stack:get_count()
end,
on_move = function() end,
on_take = function() end,
on_put = function() end,
}
end
end
local function room_for_item(stack, stack2)
return stack:is_empty() or stack2:is_empty() or
(stack:get_name() == stack2:get_name() and stack:get_free_space() >= stack2:get_count())
end
local function move(inv1, list1, index1, inv2, list2, index2, player, count)
local location1 = inv1:get_location()
local location2 = inv2:get_location()
local callbacks1 = get_callbacks(location1)
local callbacks2 = get_callbacks(location2)
local stack = inv1:get_stack(list1, index1)
local stack_to = inv2:get_stack(list2, index2)
if stack_to:get_name() ~= stack:get_name() and stack_to:get_count() > 0 then -- Disallow move to different type of item
return
end
if list2 == "craftpreview" then return end -- Disallow moving items into craftpreview
if list1 ~= "craftpreview" then
count = math.min(stack:get_count(), count)
stack:set_count(count)
count = math.min(count, stack_to:get_free_space())
else
local c = 0
local crafted_stack = ItemStack("")
while c < count do
local st = minetest.craft_predict(
minetest.get_craft_result(
{method = "normal",
items = inv1:get_list("craft"),
width = inv1:get_width("craft")}).item,
player,
inv1:get_list("craft"),
inv1)
--if not stack_to:room_for_item(st) then
if not room_for_item(stack_to, st) then
break
end
local old_grid = inv1:get_list("craft")
local out, decr_input = minetest.get_craft_result(
{method = "normal",
items = inv1:get_list("craft"),
width = inv1:get_width("craft")})
local item = out.item
inv1:set_list("craft", decr_input.items)
local crafted = minetest.on_craft(item, player, old_grid, inv1)
crafted_stack:add_item(crafted)
stack_to:add_item(crafted)
c = c + 1
end
stack = crafted_stack
count = stack:get_count()
end
if count <= 0 then return end
local count1, count2
if same_location(location1, location2) then
count1 = callbacks1.allow_move(list1, index1, list2, index2, count, player)
count2 = count1
else
count1 = callbacks1.allow_take(list1, index1, stack, player)
count2 = callbacks2.allow_put(list2, index2, stack, player)
end
if count1 >= 0 then
count = math.min(count, count1)
end
if count2 >= 0 then
count = math.min(count, count2)
end
if count == 0 then return end
stack:set_count(count)
if count1 >= 0 then
local s = inv1:get_stack(list1, index1)
s:take_item(count)
inv1:set_stack(list1, index1, s)
end
if count2 >= 0 then
local s = inv2:get_stack(list2, index2)
s:add_item(stack)
inv2:set_stack(list2, index2, s)
end
if same_location(location1, location2) then
callbacks1.on_move(list1, index1, list2, index2, count, player)
else
callbacks1.on_take(list1, index1, stack, player)
callbacks2.on_put(list2, index2, stack, player)
end
end
local function readC(cptr, addr)
return cptr[addr]
end
local function read(cptr, addr)
return cptr[addr] + 256 * cptr[u16(addr + 1)]
end
function tl.move_item(turtle, cptr, addr)
local from_id = readC(cptr, addr)
local to_id = readC(cptr, u16(addr + 1))
local from_index = read(cptr, u16(addr + 2))
local to_index = read(cptr, u16(addr + 4))
local count = read(cptr, u16(addr + 6))
local info = turtles.get_turtle_info(turtle)
local player = get_turtle_formspec_player(turtle)
if info.open_formspec then
local formspec = info.open_formspec
local from_elem = get_element_by_id(formspec, from_id)
local to_elem = get_element_by_id(formspec, to_id)
if from_elem and from_elem.location and to_elem and to_elem.location and
from_elem.start_index <= from_index and from_index < from_elem.start_index + from_elem.size and
to_elem.start_index <= to_index and to_index < to_elem.start_index + to_elem.size then
local from_inv = get_inventory_from_location(turtle, from_elem.location)
local to_inv = get_inventory_from_location(turtle, to_elem.location)
if from_inv and to_inv then
move(from_inv, from_elem.listname, from_index, to_inv, to_elem.listname, to_index, player, count)
end
end
end
end

View File

@ -312,6 +312,7 @@ ITABLE_RAW = {
[0x88] = "tl.open_inv(turtle, cptr)",
[0x89] = "tl.get_formspec(turtle, cptr, cptr.X)",
[0x8a] = "tl.get_stack(turtle, cptr, cptr.X, cptr.Y, cptr.Z)",
[0x8b] = "tl.move_item(turtle, cptr, cptr.X)",
}
ITABLE = {}

View File

@ -108,6 +108,7 @@ ASSEMBLER
: OPEN-INV 0x88 NXT ;
: GET-FORMSPEC PLX 0x89 NXT ;
: GET-STACK PLZ PLY PLX 0x8a NXT ;
: (move_item) PLX 0x8b NXT ;
ENVIRONMENT
256 CONSTANT /COUNTED-STRING
@ -307,6 +308,8 @@ FORTH
: GET-INV-ID >R S" current_player" S" main" R> GET-LIST-ID ;
: GET-CRAFT-ID >R S" current_player" S" craft" R> GET-LIST-ID ;
: GET-CRAFT-OUTPUT-ID >R S" current_player" S" craftpreview" R> GET-LIST-ID ;
\ from_id from_index to_id to_index count
: MOVE-ITEM 0xfffe ! 0xfffc ! 0xfff9 C! 0xfffa ! 0xfff8 C! 0xfff8 (move_item) ;
: GET-CURRENT CW @ ;
: SET-CURRENT LATEST @ CW @ ! DUP CW ! @ LATEST ! ;

File diff suppressed because one or more lines are too long

29
t2.lua
View File

@ -198,6 +198,19 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
return true
end)
local function update_craftpreview(turtle)
local inv = turtles.get_turtle_inventory(turtle)
local info = turtles.get_turtle_info(turtle)
local dir = minetest.facedir_to_dir(info.dir)
local player = turtles.create_turtle_player(turtle, dir, true)
inv:set_stack("craftpreview", 1,
minetest.craft_predict(
minetest.get_craft_result({method = "normal", items = inv:get_list("craft"), width = inv:get_width("craft")}).item,
player,
inv:get_list("craft"),
inv))
end
minetest.register_node("turtle:turtle", {
description = "Turtle",
drawtype = "airlike",
@ -211,7 +224,9 @@ minetest.register_node("turtle:turtle", {
inv:set_size("floppy", 1)
inv:set_size("main", 8 * 4)
inv:set_size("craft", 3 * 3)
inv:set_size("craft_output", 1)
inv:set_width("craft", 3)
inv:set_size("craftpreview", 1)
inv:set_size("craftresult", 1)
local id = turtles.create_turtle_id()
meta:set_int("turtle_id", id)
local info = turtles.get_turtle_info(id)
@ -227,6 +242,15 @@ minetest.register_node("turtle:turtle", {
info.turtle = le
le.turtle_id = id
end,
on_metadata_inventory_move = function(pos)
update_craftpreview(minetest.get_meta(pos):get_int("turtle_id"))
end,
on_metadata_inventory_take = function(pos)
update_craftpreview(minetest.get_meta(pos):get_int("turtle_id"))
end,
on_metadata_inventory_put = function(pos)
update_craftpreview(minetest.get_meta(pos):get_int("turtle_id"))
end,
on_rightclick = function(pos, node, clicker)
local turtle_id = minetest.get_meta(pos):get_int("turtle_id")
local info = turtles.get_turtle_info(turtle_id)
@ -327,12 +351,14 @@ minetest.register_entity("turtle:turtle", {
end
end,
on_rightclick = function(self, clicker)
if self.turtle_id == nil then return end
local info = turtles.get_turtle_info(self.turtle_id)
local name = clicker:get_player_name()
info.playernames[name] = true
minetest.show_formspec(name, "turtle:" .. tostring(self.turtle_id), info.formspec)
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if self.turtle_id == nil then return end
self.object:remove()
local info = turtles.get_turtle_info(self.turtle_id)
local pos = info.spos
@ -352,6 +378,7 @@ minetest.register_entity("turtle:turtle", {
turtle_infos[self.turtle_id] = nil
end,
get_staticdata = function(self)
if self.turtle_id == nil then return "" end
return tostring(self.turtle_id)
end,
})