Adjust callbacks to operate from on_rightclick (delegated from potions and buckets).

This commit is contained in:
Phaethon H 2021-11-06 17:25:09 -07:00
parent 91fe85fd1b
commit 05a9968081
1 changed files with 314 additions and 6 deletions

View File

@ -92,11 +92,11 @@ function mcl_cauldrons.empty_cauldron_on_drain(node, change_levels, substance)
-- no change.
return nil
end
if (substance and not mcl_cauldrons.has_substance(node, substance)) then
if (substance and not mcl_cauldrons.node_has_substance(node, substance)) then
-- different substance, cannot drain.
return nil
end
local old_level = mcl_cauldrons.get_level(node)
local old_level = mcl_cauldrons.node_get_level(node)
local whole, fraction = math.modf(change_levels)
local new_level
if fraction > 0 then
@ -112,10 +112,305 @@ function mcl_cauldrons.empty_cauldron_on_drain(node, change_levels, substance)
new_level = old_level - whole
-- clamp 0 through 3 inclusive.
new_level = math.max(0, math.min(new_level, 3))
local newnode = mcl_cauldrons.set_level(node, new_level, substance)
local newnode = mcl_cauldrons.node_set_level(node, new_level, substance)
return newnode
end
-- test for membership in list (styled after R7RS)
local function member(obj, lis)
for i, v in ipairs(lis) do
if v == obj then return true end
end
return false
end
-- Predicate: item stack content is a bottle (potion).
local function is_a_bottle(itemstack)
-- determinants for bottle not yet finalized.
local itemdef = itemstack:get_definition()
if minetest.get_item_group(itemdef, "brewitem") ~= 0 then
return true
end
return false
end
-- Predicate: item stack content is glass (empty) bottle.
local function is_bottle_empty(itemstack)
if not is_a_bottle(itemstack) then return false end
-- Compare to item name; I don't like this (would prefer groups property or other itemdef property), but it works for now.
local BY_NAME = {
"mcl_potions:glass_bottle",
}
if member(itemstack:get_name(), BY_NAME) then return true end
end
-- Predicate: item stack content is a water bottle/potion.
local function is_bottle_water(itemstack)
if not is_a_bottle(itemstack) then return false end
-- works for now; prefer group/itemdef property.
local BY_NAME = {
"mcl_potions:water",
}
if member(itemstack:get_name(), BY_NAME) then return true end
end
-- Predicate: item stack content is a river water bottle.
local function is_bottle_river_water(itemstack)
if not is_a_bottle(itemstack) then return false end
-- works for now; prefer group/itemdef property.
local BY_NAME = {
"mcl_potions:river_water",
}
if member(itemstack:get_name(), BY_NAME) then return true end
end
-- Predicate: item definition is a bucket.
local function is_a_bucket(itemstack)
local itemdef = itemstack:get_definition()
if (minetest.get_item_group(itemdef, "bucket") ~= 0) then return true end
-- other tests?
return false
end
-- Predicate: item definition is a bucket of water.
local function is_bucket_water(itemdef)
local BY_NAME = {
"mcl_buckets:bucket_water",
}
if member(itemstack:get_name(), BY_NAME) then return true end
end
-- Predicate: item definition is a bucket of river water.
local function is_bucket_river_water(itemdef)
local BY_NAME = {
"mcl_buckets:bucket_water",
}
if member(itemstack:get_name(), BY_NAME) then return true end
end
-- Predicate: item definition is a banner (washable).
local function is_a_banner(itemdef)
end
-- Empty Bucket on Empty Cauldron - accomplish nothing.
function mcl_cauldrons.empty_cauldron_on_apply_bucket_empty(pos, node, user, itemstack)
return false
end
-- Water Bucket on Empty Cauldron - possibly fill with water.
function mcl_cauldrons.empty_cauldron_on_apply_bucket_water(pos, node, user, itemstack)
local newnode = mcl_cauldrons.fill_levels(node, mcl_cauldrons.BUCKET_FILL_LEVELS, "cauldron_water")
if newnode ~= node then
minetest.set_node(pos, node)
end
end
function mcl_cauldrons.empty_cauldron_on_apply_bucket_river_water(pos, node, user, itemstack)
end
-- apply an itemstack to Empty Cauldron.
function mcl_cauldrons.empty_cauldron_on_apply_item(pos, node, user, itemstack)
local DELEGATES = {
[mcl_cauldrons.is_a_bucket]=empty_cauldron_apply_bucket,
[mcl_cauldrons.is_a_bottle]=empty_cauldron_apply_bottle,
}
for predicate, handler in pairs(DELEGATES) do
if predicate(itemstack) then
return handler(pos, node, user, itemstack)
end
end
return false
end
local function extends(parent_class, child_class)
if child_class == nil then child_class = {} end
child_class.__index = parent_class
setmetatable(child_class, child_class)
return child_class
end
mcl_cauldrons.CauldronInteractable = {
-- check if a particular ItemStack may be applied to cauldron.
handles_item = function(itemstack)
return false
end,
-- table, key is a predicate function(itemstack), value is function(pos, node, user, itemstack).
itemstack_handlers = {
},
apply_item = function(pos, node, user, itemstack)
end,
}
-- Generalized vessels interaction with Cauldron (buckets, bottles).
-- * May change cauldron state (substance+fill_level; fill/drain)
-- * Exchanges one vessel item for another vessel item when applying to cauldron.
mcl_cauldrons.Vessel = extends(mcl_cauldrons.CauldronInteractable, {
__index = mcl_cauldrons.CauldronInteractable,
-- check that item is a member of specified group (minetest.item_get_group).
_groups_membership = {
},
-- common one-for-one exchange: put new_node at pos, and in exchange take 1 from itemstack, and give 1 of new_item to user.
common_exchange = function(pos, new_node, user, itemstack, new_item)
if new_node ~= nil then
minetest.set_node(pos, new_node)
end
if new_item ~= nil then
local new_itemstack = ItemStack(new_item)
if minetest.is_creative_enabled(user:get_player_name()) then
-- creative inventory: add new_item if missing.
local inv = user:get_inventory()
if not inv.contains_item("main", new_itemstack) then
inv:add_item("main", new_itemstack)
end
else
-- survival.
if itemstack:get_count() == 1 then
-- swap with new item.
return new_itemstack
else
-- subtract one, deposit new_itemstack
itemstack:take_item(1)
local inv = user:get_inventory()
if inv:room_for_item("main", new_itemstack) then
-- add to inventory
inv:add_item("main", new_itemstack)
else
-- drop on ground.
minetest.add_item(user:get_pos(), new_itemstack)
end
end
end
end
end,
handles_item = function(itemstack)
for i, v in ipairs(self._groups_membership) do
if minetest.get_item_group(itemstack:get_name(), v) ~= 0 then
return true
end
end
end,
})
-- Bucket(-like) interactions with Cauldron.
mcl_cauldrons.Bucket = extends(mcl_cauldrons.Vessel, {
_groups_membership = {
"bucket",
},
-- internal use, get a specific Bucket type fitting itemstack.
_get_specific = function(itemstack)
-- introspect for table members with {predicate,canonical_item} keys.
for scan_name, scan_specific in pairs(self) do
if type(scan_specific) == table and scan_specific.predicate and scan.canonical_item then
if scan_specific.predicate(itemstack) then
return scan_specific
end
end
end
return nil
end,
})
mcl_cauldrons.Bucket.Empty = {
-- returns true if itemstack corresponds to the item being handled (conditional before running apply()).
predicate = function(itemstack)
if member(itemstack:get_name(), {"mcl_buckets:bucket"}) then return true end
end,
delegate_apply = function(interactable, node, itemdef)
-- empty bucket may turn into a something bucket.
for scan_field, scan_specific in pairs(interactable) do
if scan_specific.cauldron_substance then
-- non-empty bucket
local newnode = mcl_cauldrons.node_drain_levels(node, mcl_cauldrons.BUCKET_FILL_LEVELS, scan_specific.cauldron_substance)
if newnode ~= node then
-- cauldron due to change; get exchanged item.
local newitem = scan_specific.canonical_item
return newnode, newitem
end
end
end
end,
canonical_item = "mcl_buckets:bucket",
}
-- Bucket of Water vs Cauldron
mcl_cauldrons.Bucket.Water = {
predicate = function(itemstack)
-- Compare to item name; I don't like this (would prefer groups property or other itemdef property), but it works for now.
if member(itemstack:get_name(), {"mcl_buckets:bucket_water"}) then return true end
end,
delegate_apply = function(interactable, node, itemdef)
-- water bucket turns into empty bucket.
local newnode = mcl_cauldrons.node_fill_levels(node, mcl_cauldrons.BUCKET_FILL_LEVELS, self.cauldron_substance)
-- exchange bucket even if cauldron didn't change.
local newitem = interactable.Empty.canonical_item
return newnode, newitem
end,
cauldron_substance = "cauldron_water",
canonical_item = "mcl_buckets:bucket_water",
}
-- Bucket of River Water vs Cauldron
mcl_cauldrons.Bucket.RiverWater = extends(mcl_cauldrons.Bucket.Water, {
predicate = function(itemstack)
-- would prefer group/itemdef properties instead of name comparison.
if member(itemstack:get_name(), {"mcl_buckets:bucket_river_water"}) then return true end
end,
-- inherit delegate_apply()
cauldron_substance = "cauldron_river_water",
canonical_item = "mcl_buckets:bucket_river_water",
})
function mcl_cauldrons.Bucket:apply(pos, node, user, itemstack)
local specific = self:get_specific(itemstack)
if specific == nil then return end
local newnode, newitem = specific:delegate_apply(self, node, itemstack:get_definition())
self:common_exchange(pos, newnode, user, itemstack, newitem)
end
function mcl_cauldrons.Bucket:handles_item(itemstack)
return (self:_get_specific(itemstack) ~= nil)
end
-- Bottle interactions with Cauldron.
mcl_cauldrons.Bottle = {
_group_membership = {
"potion",
}
}
setmetatable(mcl_cauldrons.Bottle, mcl_cauldrons.Bottle)
mcl_cauldrons.EmptyCauldron = {
-- registry of item(proxie)s that may interact with cauldron.
known_items = {
mcl_cauldrons.Bucket,
mcl_cauldrons.Bottle,
},
apply_item = function(pos, node, user, itemstack)
-- list of actions to carry out as a result of applying item.
-- allows for rollback/cancelling in case of errors or exceptions.
local plan = {}
for _, item_handler in ipairs(self.known_items) do
if item_handler:handles_item(itemstack) then
item_handler:apply(pos, node, user, itemstack)
end
end
end,
}
-- Empty cauldron
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron", {
description = S("Cauldron"),
@ -140,11 +435,24 @@ mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron", {
_mcl_hardness = 2,
_mcl_blast_resistance = 2,
--[[
_mcl_on_fill = function(node, change_levels, substance)
return mcl_cauldrons.empty_cauldron_on_fill(node, change_levels, substance)
return mcl_cauldrons.do_empty_cauldron_on_fill(node, change_levels, substance)
end,
_mcl_on_drain = function(node, change_levels, substance)
return mcl_cauldrons.empty_cauldron_on_drain(node, change_levels, substance)
return mcl_cauldrons.do_empty_cauldron_on_drain(node, change_levels, substance)
end,
_mcl_on_apply_item = function(node, vessel_def)
return mcl_cauldrons.do_empty_cauldron_apply_item(node, bucket_def)
end,
]]
-- on right-click with an item.
_mcl_on_right_click = function(place_pos, node, user, itemstack)
return EmptyCauldron:apply_item(place_pos, node, user, itemstack)
end,
on_rightclick = function(place_pos, node, user, itemstack)
print("ON_RIGHTCLICK", itemstack:get_name())
return mcl_cauldrons:EmptyCauldron.apply_item(place_pos, node, user, itemstack)
end,
})
@ -251,7 +559,7 @@ minetest.register_abm({
local EXTINGUISHING_RADIUS = 0.4
for _, obj in pairs(minetest.get_objects_inside_radius(pos, EXTINGUISHING_RADIUS)) do
if mcl_burning.is_burning(obj) then
local newnode = mcl_cauldrons.drain_levels(node, 1, "cauldron_water")
local newnode = mcl_cauldrons.node_drain_levels(node, 1, "cauldron_water")
if node ~= newnode then
-- extinguishing requires 1 level of water from cauldron.
minetest.swap_node(pos, newnode)