114 lines
5.3 KiB
Lua
Executable File
114 lines
5.3 KiB
Lua
Executable File
-- Original bounded seasonal appearance. No timers, world lists or clock replay.
|
|
return function(hearth, api)
|
|
local S = {COLUMN_HEIGHT = 12, MAX_COLUMN_SWAPS = 12}
|
|
local variants, canonical, availability = {}, {}, {}
|
|
local function variant(kind, season, texture, mesh)
|
|
variants[kind] = variants[kind] or {}
|
|
local short = kind .. "_" .. season:lower()
|
|
variants[kind][season] = {name="hearth:"..short, texture=texture, mesh=mesh}
|
|
canonical["hearth:"..short] = kind
|
|
end
|
|
for _, kind in ipairs({"tree_seedling", "young_tree", "mature_tree", "leaves"}) do
|
|
for _, season in ipairs({"Spring", "Autumn", "Winter"}) do
|
|
local suffix = season:lower()
|
|
local mesh = kind ~= "leaves" and ("hearth_"..kind
|
|
.. (season == "Winter" and kind ~= "tree_seedling" and "_winter" or "") .. ".obj") or nil
|
|
variant(kind, season, "hearth_" .. (kind == "leaves" and "leaves" or "tree_atlas") .. "_" .. suffix .. ".png", mesh)
|
|
end
|
|
end
|
|
for _, kind in ipairs({"meadow_grass", "fern", "wildflowers"}) do
|
|
variant(kind, "Winter", "hearth_"..kind.."_dormant.png")
|
|
if kind == "wildflowers" then variant(kind, "Autumn", "hearth_wildflowers_dormant.png") end
|
|
end
|
|
-- Patch nodes and picked food items have different names: never overwrite a
|
|
-- registered node with a craftitem, and never let picked food reset a patch.
|
|
variant("spring_greens_patch", "Spring", "hearth_spring_greens.png")
|
|
variant("autumn_mushrooms_patch", "Autumn", "hearth_autumn_mushrooms.png")
|
|
for kind in pairs(variants) do canonical["hearth:"..kind] = kind end
|
|
-- Extensions are installed by botany/diversity before node registration. Missing
|
|
-- bases in minimal mocks/optional content are skipped, never asserted.
|
|
function S.add_variants(kind, definitions, active_seasons)
|
|
assert(type(kind)=="string" and kind:match("^[a-z_]+$"), "invalid flora kind")
|
|
canonical["hearth:"..kind] = kind
|
|
for season, def in pairs(definitions or {}) do
|
|
assert(({Spring=true,Summer=true,Autumn=true,Winter=true})[season], "invalid season")
|
|
variant(kind, season, assert(def.texture), def.mesh)
|
|
end
|
|
availability[kind] = active_seasons
|
|
end
|
|
function S.kind(n)
|
|
local name = type(n) == "table" and n.name or n
|
|
return type(name) == "string" and (canonical[name] or name:match("^hearth:(.+)$")) or nil
|
|
end
|
|
function S.season(now)
|
|
local climate = hearth.climate_at and hearth.climate_at(now or os.time())
|
|
return climate and climate.season or "Summer"
|
|
end
|
|
function S.name_for(kind, now)
|
|
kind = S.kind(kind) or kind
|
|
local v = variants[kind] and variants[kind][S.season(now)]
|
|
return v and v.name or "hearth:"..kind
|
|
end
|
|
function S.available(kind, now)
|
|
kind = S.kind(kind) or kind
|
|
local season = S.season(now)
|
|
if availability[kind] then return availability[kind][season] == true end
|
|
if kind == "spring_greens_patch" then return season == "Spring" end
|
|
if kind == "autumn_mushrooms_patch" then return season == "Autumn" end
|
|
if kind == "wildflowers" then return season == "Spring" or season == "Summer" end
|
|
return not variants[kind] or season ~= "Winter"
|
|
end
|
|
function S.names()
|
|
local names = {}
|
|
for name in pairs(canonical) do names[#names+1] = name end
|
|
return names
|
|
end
|
|
function S.reconcile(p, n, now)
|
|
-- Appearance alone can change a claimed/hand-placed plant, but never its
|
|
-- provenance, age, harvest deadline, dig permission, drops or inventory.
|
|
n = n or api.get_node_or_nil(p)
|
|
if not n or not canonical[n.name] then return false end
|
|
local protection = hearth.external_protected or api.is_protected
|
|
if protection and protection(p, "") then return false end
|
|
local target = S.name_for(canonical[n.name], now)
|
|
if n.name == target then return false end
|
|
-- Recheck loaded identity before swapping; no set_node metadata erasure.
|
|
local current = api.get_node_or_nil(p)
|
|
if not current or current.name ~= n.name then return false end
|
|
api.swap_node(p, {name=target, param1=current.param1, param2=current.param2})
|
|
return true
|
|
end
|
|
function S.register()
|
|
local names = {}
|
|
for kind, seasons in pairs(variants) do
|
|
local base_name = "hearth:"..kind
|
|
local base = (api.registered_nodes or {})[base_name]
|
|
if base then
|
|
names[#names+1] = base_name
|
|
for _, v in pairs(seasons) do
|
|
local def = {}
|
|
for k, value in pairs(base) do def[k] = value end
|
|
def.groups = {}
|
|
for k, value in pairs(base.groups or {}) do def.groups[k] = value end
|
|
def.groups.not_in_creative_inventory = 1
|
|
-- A stage may now be a bark cube rather than the old sculpted
|
|
-- tree. Recolour foliage/sprites only; never flatten end-grain
|
|
-- and bark faces or reintroduce an obsolete mesh on a cube.
|
|
if base.drawtype ~= "normal" then
|
|
def.tiles = {v.texture}
|
|
if base.inventory_image then def.inventory_image = v.texture end
|
|
if base.wield_image then def.wield_image = v.texture end
|
|
end
|
|
if base.drawtype == "mesh" and v.mesh then def.mesh = v.mesh end
|
|
def.drop = base.drop == nil and base_name or base.drop
|
|
if not api.registered_nodes[v.name] then api.register_node(v.name, def) end
|
|
names[#names+1] = v.name
|
|
end
|
|
end
|
|
end
|
|
api.register_lbm({name="hearth:seasonal_flora", label="Reconcile seasonal flora appearance",
|
|
nodenames=names, run_at_every_load=true, action=function(p) S.reconcile(p) end})
|
|
end
|
|
return S
|
|
end
|