Lots of content
This commit is contained in:
Executable
+162
@@ -0,0 +1,162 @@
|
||||
-- Bounded, bait-funded riverside fishing. A trap can hold one catch at a time.
|
||||
return function(hearth, api, construction)
|
||||
local CATCH_SECONDS = 600
|
||||
local function finite(value)
|
||||
value = tonumber(value)
|
||||
if not value or value ~= value or value == math.huge or value == -math.huge then return nil end
|
||||
return value
|
||||
end
|
||||
local function safe_now()
|
||||
local value = os.time()
|
||||
if type(value) ~= "number" or value ~= value or value < 0 or value == math.huge or value == -math.huge then return 0 end
|
||||
return math.floor(value)
|
||||
end
|
||||
local function node(pos)
|
||||
local value = api.get_node_or_nil(pos)
|
||||
if not value or value.name == "ignore" then return nil end
|
||||
return value
|
||||
end
|
||||
local function has_water(pos)
|
||||
for y = -1, 1 do for x = -3, 3 do for z = -3, 3 do
|
||||
local nearby = node({x = pos.x + x, y = pos.y + y, z = pos.z + z})
|
||||
if nearby and (nearby.name == "hearth:water_source" or nearby.name == "hearth:water_flowing") then
|
||||
return true
|
||||
end
|
||||
end end end
|
||||
return false
|
||||
end
|
||||
local function valid_site(pos)
|
||||
local here = node(pos)
|
||||
local below = node({x = pos.x, y = pos.y - 1, z = pos.z})
|
||||
local here_def = here and api.registered_nodes[here.name]
|
||||
return here_def and here_def.buildable_to == true and below
|
||||
and (below.name == "hearth:sand" or below.name == "hearth:grass")
|
||||
and has_water(pos)
|
||||
end
|
||||
local function clear_job(meta)
|
||||
meta:set_string("state", "idle")
|
||||
meta:set_string("ready_at", "0")
|
||||
meta:set_int("catch_count", 0)
|
||||
end
|
||||
local function valid_catch(meta)
|
||||
local count = meta:get_int("catch_count")
|
||||
return count == 1 or count == 2
|
||||
end
|
||||
local function finish(pos, meta, completed_at)
|
||||
local season = hearth.climate_at and hearth.climate_at(completed_at)
|
||||
local count = season and season.season == "Spring" and 2 or 1
|
||||
meta:set_string("state", "ready")
|
||||
meta:set_string("ready_at", "0")
|
||||
meta:set_int("catch_count", count)
|
||||
meta:set_string("infotext", count == 2
|
||||
and "River trap: spring run caught two fish; right-click empty-handed to collect."
|
||||
or "River trap: catch ready; right-click empty-handed to collect.")
|
||||
api.get_node_timer(pos):stop()
|
||||
end
|
||||
local function reconcile(pos, now)
|
||||
local current = node(pos)
|
||||
if not current or current.name ~= "hearth:fish_trap" then return false end
|
||||
local meta = api.get_meta(pos)
|
||||
local state = meta:get_string("state")
|
||||
if state == "ready" then
|
||||
if not valid_catch(meta) then clear_job(meta) end
|
||||
return false
|
||||
end
|
||||
if state ~= "baited" then
|
||||
clear_job(meta)
|
||||
return false
|
||||
end
|
||||
local ready = finite(meta:get_string("ready_at"))
|
||||
now = now or safe_now()
|
||||
if not ready or ready <= 0 or ready > now + CATCH_SECONDS then
|
||||
clear_job(meta)
|
||||
meta:set_string("infotext", "River trap: ready for bait beside water.")
|
||||
api.get_node_timer(pos):stop()
|
||||
return false
|
||||
end
|
||||
if ready <= now then
|
||||
finish(pos, meta, ready)
|
||||
return false
|
||||
end
|
||||
api.get_node_timer(pos):start(math.max(1, ready - now))
|
||||
meta:set_string("infotext", "River trap: baited; catch in " .. math.ceil(ready - now) .. " seconds.")
|
||||
return true
|
||||
end
|
||||
local function plant_trap(stack, player, pointed)
|
||||
if not pointed or pointed.type ~= "node" or not player or not player:is_player() then return stack end
|
||||
local pos = pointed.above
|
||||
if not valid_site(pos) then
|
||||
hearth.notify(player, "Set a fish trap on firm riverbank within three blocks of water.")
|
||||
return stack
|
||||
end
|
||||
if not construction.allowed(pos, player, "build") then return stack end
|
||||
api.set_node(pos, {name = "hearth:fish_trap"})
|
||||
local def = api.registered_nodes["hearth:fish_trap"]
|
||||
if def and def.after_place_node then def.after_place_node(pos, player) end
|
||||
stack:take_item(1)
|
||||
return stack
|
||||
end
|
||||
local function collect(pos, player, meta)
|
||||
local count = meta:get_int("catch_count")
|
||||
if count ~= 1 and count ~= 2 then clear_job(meta); return end
|
||||
local inventory = player:get_inventory()
|
||||
local output = ItemStack("hearth:raw_fish " .. count)
|
||||
if not inventory or not inventory:room_for_item("main", output) then
|
||||
hearth.notify(player, "Make room for the catch before emptying the river trap.")
|
||||
return
|
||||
end
|
||||
local leftover = inventory:add_item("main", output)
|
||||
if leftover and not leftover:is_empty() then
|
||||
meta:set_int("catch_count", leftover:get_count())
|
||||
hearth.notify(player, "Some fish were collected; make room for the rest.")
|
||||
return
|
||||
end
|
||||
clear_job(meta)
|
||||
meta:set_string("infotext", "River trap: ready for bait beside water.")
|
||||
end
|
||||
local function rightclick(pos, _, player, stack)
|
||||
if not player or not player:is_player() or not construction.allowed(pos, player, "use") then return stack end
|
||||
local meta = api.get_meta(pos)
|
||||
local state = meta:get_string("state")
|
||||
if state == "ready" then
|
||||
if stack:is_empty() then collect(pos, player, meta) else
|
||||
hearth.notify(player, "Collect the waiting catch before baiting this trap again.")
|
||||
end
|
||||
return stack
|
||||
end
|
||||
if stack:get_name() ~= "hearth:fish_bait" then
|
||||
local remaining = state == "baited"
|
||||
and math.max(0, (finite(meta:get_string("ready_at")) or safe_now()) - safe_now()) or nil
|
||||
hearth.notify(player, remaining and ("River trap: waiting " .. remaining .. " seconds.")
|
||||
or "River trap: add fish bait to set it for ten minutes.")
|
||||
return stack
|
||||
end
|
||||
if state == "baited" then
|
||||
hearth.notify(player, "This trap is already baited; return after the catch is ready.")
|
||||
return stack
|
||||
end
|
||||
if not has_water(pos) then
|
||||
hearth.notify(player, "This trap is too far from water to catch fish.")
|
||||
return stack
|
||||
end
|
||||
local now = safe_now()
|
||||
meta:set_string("state", "baited")
|
||||
meta:set_string("ready_at", tostring(now + CATCH_SECONDS))
|
||||
meta:set_int("catch_count", 0)
|
||||
meta:set_string("baiter", player:get_player_name())
|
||||
api.get_node_timer(pos):start(CATCH_SECONDS)
|
||||
meta:set_string("infotext", "River trap: baited; catch in " .. CATCH_SECONDS .. " seconds.")
|
||||
stack:take_item(1)
|
||||
return stack
|
||||
end
|
||||
local function on_timer(pos)
|
||||
return reconcile(pos)
|
||||
end
|
||||
local function can_dig(pos, player)
|
||||
return construction.allowed(pos, player, "dig")
|
||||
and api.get_meta(pos):get_string("state") == "idle"
|
||||
end
|
||||
return {plant_trap = plant_trap, rightclick = rightclick, reconcile = reconcile,
|
||||
on_timer = on_timer, can_dig = can_dig, has_water = has_water,
|
||||
CATCH_SECONDS = CATCH_SECONDS}
|
||||
end
|
||||
Reference in New Issue
Block a user