This commit is contained in:
2026-11-05 09:57:11 -05:00
parent d34388bf2c
commit 89e392cc2e
81 changed files with 12410 additions and 1135 deletions
+72 -12
View File
@@ -54,6 +54,9 @@ return function(hearth, api)
add("rowan","Rowan","Sorbus aucuparia","acid",grow,"pollinator,browse,nursery")
add("hazel","Common hazel","Corylus avellana","wood",grow,"browse,ectomycorrhiza,nursery")
add("spruce","Norway spruce","Picea abies","acid",year,"ectomycorrhiza,nursery,resin")
add("crab_apple","Crab apple","Malus sylvestris","wood",grow,"pollinator,browse,nursery")
add("wild_cherry","Wild cherry","Prunus avium","wood",grow,"pollinator,browse,nursery")
add("field_maple","Field maple","Acer campestre","wood",grow,"pollinator,browse,nursery")
group="shrubs"
add("bilberry","Bilberry","Vaccinium myrtillus","acid",grow,"pollinator,browse,nursery",{food=true})
add("blackberry","Pleated blackberry","Rubus plicatus","shrub",grow,"pollinator,browse,nursery",{food=true})
@@ -65,6 +68,10 @@ return function(hearth, api)
add("holly","European holly","Ilex aquifolium","wood",year,"pollinator,browse,nursery",{toxic=true})
add("juniper","Common juniper","Juniperus communis","acid",year,"browse,nursery,resin")
add("guelder_rose","Guelder rose","Viburnum opulus","wetwood",grow,"pollinator,browse,nursery",{toxic=true})
add("honeysuckle","Honeysuckle","Lonicera periclymenum","shrub",grow,"pollinator,nursery",{toxic=true})
add("ivy","Ivy","Hedera helix","shade",year,"pollinator,nursery",{toxic=true})
add("gorse","Gorse","Ulex europaeus","acid",year,"nitrogenfixer,pollinator,nursery",{toxic=true})
add("broom","Broom","Cytisus scoparius","acid",grow,"nitrogenfixer,pollinator,nursery",{toxic=true})
group="grasses"
add("sedge","Greater tussock-sedge","Carex paniculata","bank",grow,"browse,engineer,nursery",{existing=true})
add("bulrush","Bulrush (great reedmace)","Typha latifolia","bank",grow,"engineer,nursery",{existing=true})
@@ -87,6 +94,18 @@ return function(hearth, api)
add("wild_garlic","Wild garlic","Allium ursinum","shade","Spring","pollinator")
add("cowslip","Cowslip","Primula veris","meadow","Spring","pollinator")
add("wood_anemone","Wood anemone","Anemone nemorosa","shade","Spring","pollinator",{toxic=true})
add("dandelion","Dandelion","Taraxacum officinale","meadow",grow,"pollinator,browse")
add("daisy","Daisy","Bellis perennis","meadow",grow,"pollinator")
add("red_clover","Red clover","Trifolium pratense","meadow",grow,"nitrogenfixer,pollinator,browse")
add("birdsfoot_trefoil","Bird's-foot trefoil","Lotus corniculatus","meadow",grow,"nitrogenfixer,pollinator,browse",{toxic=true})
add("ribwort_plantain","Ribwort plantain","Plantago lanceolata","meadow",grow,"browse")
add("selfheal","Selfheal","Prunella vulgaris","meadow",grow,"pollinator")
add("knapweed","Common knapweed","Centaurea nigra","meadow",grow,"pollinator,nursery")
add("oxeye_daisy","Oxeye daisy","Leucanthemum vulgare","meadow",grow,"pollinator")
add("field_scabious","Field scabious","Knautia arvensis","meadow",grow,"pollinator")
add("teasel","Wild teasel","Dipsacus fullonum","meadow",grow,"pollinator,nursery")
add("cow_parsley","Cow parsley","Anthriscus sylvestris","shrub",grow,"pollinator,nursery")
add("garlic_mustard","Garlic mustard","Alliaria petiolata","shade",grow,"pollinator,nursery")
group="ferns_mosses"
add("bracken","Bracken","Pteridium aquilinum","acid",grow,"nursery,allelopathy",{toxic=true})
add("lady_fern","Lady-fern","Athyrium filix-femina","shade",grow,"nursery")
@@ -98,6 +117,7 @@ return function(hearth, api)
add("moss","Cypress-leaved plait-moss","Hypnum cupressiforme","moss",year,"engineer,nursery",{existing=true})
add("fork_moss","Broom fork-moss","Dicranum scoparium","moss",year,"nursery")
add("star_moss","Swan's-neck thyme-moss","Mnium hornum","moss",year,"nursery")
add("holly_fern","Soft shield-fern (contract ID holly fern)","Polystichum setiferum","shade",year,"nursery")
group="fungi"
add("fly_agaric","Fly agaric","Amanita muscaria","litter","Summer,Autumn","ectomycorrhiza",{toxic=true})
add("birch_bolete","Brown birch bolete","Leccinum scabrum","litter","Summer,Autumn","ectomycorrhiza")
@@ -137,6 +157,16 @@ return function(hearth, api)
niche("rowan",{light={5,15},moisture={.3,.85}})
niche("spruce",{moisture={.4,.85},light={4,15}})
niche("hazel",{ph={5,8}})
niche("crab_apple",{light={7,15},moisture={.3,.75},ph={5,8}})
niche("wild_cherry",{light={7,15},moisture={.3,.7},ph={5.5,8}})
niche("field_maple",{light={5,15},moisture={.25,.75},ph={5.5,8}})
niche("gorse",{light={10,15},moisture={.2,.65},ph={3.5,6.5}})
niche("broom",{light={9,15},moisture={.2,.65},ph={4,7}})
niche("ivy",{light={3,15},moisture={.3,.85},ph={5,8}})
niche("birdsfoot_trefoil",{moisture={.2,.7},ph={5,8}})
niche("ribwort_plantain",{moisture={.2,.8}})
niche("field_scabious",{moisture={.2,.65},ph={6,8}})
niche("holly_fern",{light={3,10},moisture={.5,.9},ph={5,8}})
niche("juniper",{ph={4,8},moisture={.15,.6}})
niche("nettle",{light={5,15},nutrients=1})
niche("cowslip",{ph={6,8},moisture={.2,.65}})
@@ -171,8 +201,21 @@ return function(hearth, api)
bluebell="Spring",yarrow="Summer,Autumn",foxglove="Summer",meadowsweet="Summer",
wild_garlic="Spring",cowslip="Spring",wood_anemone="Spring",yellow_waterlily="Summer",
white_waterlily="Summer",water_crowfoot="Spring,Summer",water_mint="Summer,Autumn",
water_forgetmenot="Spring,Summer",brooklime="Spring,Summer"}
water_forgetmenot="Spring,Summer",brooklime="Spring,Summer",
crab_apple="Spring",wild_cherry="Spring",field_maple="Spring",
dandelion="Spring,Summer,Autumn",daisy="Spring,Summer,Autumn",red_clover="Summer,Autumn",
birdsfoot_trefoil="Summer",selfheal="Summer,Autumn",knapweed="Summer,Autumn",
oxeye_daisy="Summer",field_scabious="Summer,Autumn",teasel="Summer",
cow_parsley="Spring,Summer",garlic_mustard="Spring",honeysuckle="Summer,Autumn",
ivy="Autumn",gorse="Spring,Summer,Autumn,Winter",broom="Spring,Summer"}
for id,text in pairs(flowers) do index[id].flowering=set(text) end
-- Seed-season windows are qualitative phenology, not permission to replay
-- every missed crop of seeds. Individual maturity is checked by succession.
for _,r in ipairs(records) do
r.seeding=set(r.group=="trees" and "Autumn" or "Summer,Autumn")
if r.id=="ivy" then r.seeding=set("Spring") end
if r.id=="garlic_mustard" or r.id=="cow_parsley" then r.seeding=set("Summer") end
end
for _,id in ipairs({"bilberry","blackberry","raspberry"}) do
index[id].fruiting=set(id=="blackberry" and "Summer,Autumn" or "Summer")
end
@@ -199,7 +242,7 @@ return function(hearth, api)
function B.kind(value)
local k=raw_kind(value)
if index[k] then return k end
local id,part=(k or ""):match("^([a-z]+)_([a-z]+)$")
local id,part=(k or ""):match("^([a-z_]+)_([a-z]+)$")
if index[id] and index[id].group=="trees" and
({seedling=true,young=true,mature=true,leaves=true,log=true,planks=true})[part] then return id end
end
@@ -235,8 +278,9 @@ return function(hearth, api)
end
end
function B.pollinator(value,season)
local r=record(value); return r~=nil and r.tags.pollinator==true and r.flowering[season]==true
local r=record(value); return r~=nil and r.tags.pollinator==true and r.flowering~=nil and r.flowering[season]==true
end
function B.seeding(value,season) local r=record(value); return r~=nil and r.seeding[season]==true end
function B.edible(value,season)
local r=record(value); return r~=nil and r.food==true and not r.toxic and r.fruiting[season]==true
end
@@ -285,10 +329,11 @@ return function(hearth, api)
end
end
local DAY=86400
local YEAR=365.2425*DAY -- shared with succession.frugivore; resource policy, not lifespan
local function now()
local n
if api.now then n=api.now() else n=os.time() end
if not finite(n) or n<0 or n>9007199254740991-DAY then return nil end
if not finite(n) or n<0 or n>9007199254740991-YEAR then return nil end
return math.floor(n)
end
function B.harvest(pos,player)
@@ -309,17 +354,32 @@ return function(hearth, api)
local stored=meta:get_string("botany_ready_at")
local ready=0
if stored~="" then ready=tonumber(stored) end
if not finite(ready) or ready<0 then return false,"metadata" end
if not finite(ready) or ready<0 or ready>9007199254740991 then return false,"metadata" end
if ready>time then return false,"cooldown" end
local pollen_stored=meta:get_string("succ_pollinated_until")
local pollen=pollen_stored=="" and 0 or tonumber(pollen_stored)
-- Match succession.pollinate's exact expiry and one-day future bound.
if not finite(pollen) or pollen<0 or pollen>time+DAY then return false,"metadata" end
local amount=2+(pollen>time and 1 or 0)
local inv=player:get_inventory()
if not inv or not inv:room_for_item("main","hearth:berries 2") then return false,"inventory" end
-- Lock before inventory callbacks: reentrant harvest cannot duplicate fruit.
meta:set_string("botany_ready_at",tostring(time+DAY))
local leftover=inv:add_item("main","hearth:berries 2")
if not inv then return false,"inventory" end
-- Reserve both budgets before even room_for_item: callbacks may reenter.
-- Pollination modifies an existing standing crop; it never creates fruit
-- on an empty/cooling bush and is a boolean bonus, not doubled inventory.
meta:set_string("botany_ready_at",tostring(time+YEAR))
meta:set_string("succ_pollinated_until","0")
local function rollback()
meta:set_string("botany_ready_at",stored)
meta:set_string("succ_pollinated_until",pollen_stored)
end
local item="hearth:berries "..amount
if not inv:room_for_item("main",item) then rollback(); return false,"inventory" end
local leftover=inv:add_item("main",item)
local count=leftover and leftover:get_count()
if not finite(count) or count<0 or count>2 then return false,"inventory" end
if count==2 then meta:set_string("botany_ready_at",stored); return false,"inventory" end
return true,2-count
-- An invalid delivery result cannot safely be retried: keep the lock.
if not finite(count) or count%1~=0 or count<0 or count>amount then return false,"inventory" end
if count==amount then rollback(); return false,"inventory" end
return true,amount-count
end
local registered=false
function B.register_nodes(register_node)