280 lines
15 KiB
Lua
Executable File
280 lines
15 KiB
Lua
Executable File
-- Focused Lua 5.1+ policy/registration tests; siblings also work flattened.
|
|
local target=assert(arg[1],"diversity.lua path required")
|
|
local passed=0
|
|
local api,hearth,D,season,world,fields,claims,protected,missing,lbms,writes
|
|
local p={x=0,y=1,z=0}
|
|
local function key(q) return q.x..":"..q.y..":"..q.z end
|
|
local function meta(q)
|
|
local k=key(q); fields[k]=fields[k] or {}; local f=fields[k]
|
|
return {get_string=function(_,n) return f[n] or "" end,
|
|
set_string=function(_,n,v) writes=writes+1; f[n]=tostring(v) end,
|
|
to_table=function() return {fields=f} end}
|
|
end
|
|
local function reset()
|
|
season,world,fields,claims,protected,missing,lbms,writes="Summer",{},{},{},{},{},{},0
|
|
api={registered_nodes={}}
|
|
function api.get_meta(q) return meta(q) end
|
|
function api.get_node_or_nil(q)
|
|
if missing[key(q)] then return nil end
|
|
return world[key(q)] or {name=q.y==0 and "hearth:grass" or "air"}
|
|
end
|
|
function api.swap_node(q,n) world[key(q)]=n end
|
|
function api.register_node(n,def) assert(not api.registered_nodes[n],n); api.registered_nodes[n]=def end
|
|
function api.register_lbm(def) lbms[def.name]=def end
|
|
hearth={climate_at=function() return {season=season} end,
|
|
claim_owner_at=function(q) return claims[key(q)] and "owner",claims.ambiguous end,
|
|
claim_allowed=function() error("nature must not call claim_allowed") end,
|
|
external_protected=function(q) return protected[key(q)] end}
|
|
hearth.seasons=dofile(target:gsub("[^/\\]+$","seasons.lua"))(hearth,api)
|
|
D=dofile(target)(hearth,api); hearth.diversity=D
|
|
end
|
|
local function test(label,fn)
|
|
reset(); local ok,err=pcall(fn); assert(ok,label..": "..tostring(err))
|
|
passed=passed+1; print("ok "..passed.." - "..label)
|
|
end
|
|
local function habitat(ground,light,moisture)
|
|
return {ground=ground,light=light or 15,moisture=moisture or 0.5,canopy=0,loaded=true,nutrients=2,heat=20}
|
|
end
|
|
local function register()
|
|
D.register_nodes(function(n,def) api.register_node("hearth:"..n,def) end)
|
|
for _,s in ipairs(D.species_order) do api.register_node("hearth:"..s.."_leaves",{tiles={"hearth_"..s.."_leaves.png"},groups={leaves=1},drop="hearth:sticks"}) end
|
|
hearth.seasons.register()
|
|
end
|
|
test("exact thirteen species and eight legacy understory plants have cube trunks and sprite seedlings",function()
|
|
register()
|
|
assert(table.concat(D.species_order,",")=="oak,birch,pine,willow,alder,beech,aspen,rowan,hazel,spruce,crab_apple,wild_cherry,field_maple")
|
|
assert(#D.plants==8)
|
|
for _,s in ipairs(D.species_order) do for _,stage in ipairs({"seedling","young","mature"}) do
|
|
local k=s.."_"..stage; local def=assert(api.registered_nodes["hearth:"..k])
|
|
assert(not def.mesh and not def.selection_box and not def.collision_box)
|
|
if stage=="seedling" then
|
|
assert(def.drawtype=="plantlike" and def.tiles[1]=="hearth_botany_"..s..".png")
|
|
else
|
|
assert(def.drawtype=="normal" and def.paramtype2=="facedir" and def.walkable)
|
|
assert(def.tiles[1]=="hearth_"..s.."_log_top.png" and def.tiles[2]==def.tiles[1])
|
|
assert(def.tiles[3]=="hearth_"..s.."_log_side.png" and #def.tiles==3)
|
|
assert(def.groups.tree==1 and def.groups.choppy==2 and not def.groups.snappy)
|
|
assert(def.groups.not_in_creative_inventory==1 and not def.sunlight_propagates)
|
|
end
|
|
assert(def.groups.hearth_ecosystem==1 and not def.buildable_to)
|
|
local a,b=D.tree_info(k); assert(a==s and b==stage)
|
|
assert(D.family(k)==({seedling="tree_seedling",young="young_tree",mature="mature_tree"})[stage])
|
|
if stage~="seedling" then assert(def.drop=="hearth:"..s.."_log") end
|
|
end end
|
|
for _,k in ipairs(D.plants) do
|
|
local def=assert(api.registered_nodes["hearth:"..k]); assert(def.tiles[1]=="hearth_"..k..".png" and def.drop=="")
|
|
end
|
|
assert(not D.tree_info("hearth:pine_log") and D.family("hearth:pine_leaves")=="leaves")
|
|
assert(D.family("hearth:willow_log")=="log" and not D.is_worldgen_flora("hearth:oak_leaves"))
|
|
end)
|
|
test("seasonal foliage changes never replace bark with atlases or meshes; both conifers stay evergreen",function()
|
|
register()
|
|
for _,s in ipairs(D.species_order) do
|
|
for _,stage in ipairs({"seedling","young","mature","leaves"}) do
|
|
for _,time in ipairs({"Spring","Autumn","Winter"}) do
|
|
season=time; local k=s.."_"..stage
|
|
local name=hearth.seasons.name_for(k)
|
|
assert(hearth.seasons.kind(name)==k)
|
|
if s=="pine" or s=="spruce" then
|
|
assert(name=="hearth:"..k and not api.registered_nodes[name.."_winter"])
|
|
assert(hearth.seasons.available(k))
|
|
elseif stage=="young" or stage=="mature" then
|
|
assert(name=="hearth:"..k.."_"..time:lower())
|
|
assert(api.registered_nodes[name].tiles[1]=="hearth_"..s.."_log_top.png")
|
|
assert(api.registered_nodes[name].tiles[3]=="hearth_"..s.."_log_side.png" and not api.registered_nodes[name].mesh)
|
|
assert(hearth.seasons.available(k)==(time~="Winter"))
|
|
else
|
|
local def=assert(api.registered_nodes[name])
|
|
assert(def.tiles[1]==(stage=="leaves" and "hearth_"..s.."_leaves_"..time:lower()..".png" or "hearth_botany_"..s..".png"))
|
|
assert(not def.mesh)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
test("all ten species can recruit deterministically on their supported habitat",function()
|
|
local found={}
|
|
for x=0,30 do for z=0,10 do
|
|
for _,g in ipairs({"grass","forest_floor","podzol","alpine_turf","alluvium","peat"}) do
|
|
for _,m in ipairs({0.4,0.6,0.9}) do
|
|
local h=habitat(g,10,m); h.canopy=g=="forest_floor" and 1 or 0
|
|
local q={x=x,y=1,z=z}; local k=D.choose_tree(q,h)
|
|
if k then
|
|
local s=D.tree_info(k); found[s]=true
|
|
assert(D.suitable(k,h) and D.choose_tree(q,h)==k)
|
|
end
|
|
end
|
|
end
|
|
end end
|
|
for _,s in ipairs(D.species_order) do assert(found[s],s) end
|
|
end)
|
|
test("tree plans share mapgen geometry with a canonical rooted identity and no world writes",function()
|
|
assert(not D.tree_plan("oak","mature")) -- missing biome integration fails closed
|
|
hearth.biomes=dofile(target:gsub("[^/\\]+$","biomes.lua"))(hearth,api,function() error("unexpected registration") end,{})
|
|
local before=writes
|
|
for _,s in ipairs(D.species_order) do for _,stage in ipairs({"young","mature"}) do
|
|
local plan=assert(D.tree_plan(s,stage)); local schematic=hearth.biomes.tree_schematic(s,false,stage)
|
|
assert(plan.size.x==schematic.size.x and plan.size.y==schematic.size.y and #plan>5)
|
|
local roots,leaves,trunks=0,0,0
|
|
local cx,cz=math.floor(plan.size.x/2),math.floor(plan.size.z/2)
|
|
for _,n in ipairs(plan) do
|
|
assert(n.name~="air" and n.param2==0)
|
|
local cell=schematic.data[1+n.x+cx+plan.size.x*(n.y+plan.size.y*(n.z+cz))]
|
|
if n.x==0 and n.y==0 and n.z==0 then
|
|
roots=roots+1; assert(n.name=="hearth:"..s.."_"..stage)
|
|
else assert(n.name==cell.name and cell.prob==255) end
|
|
if n.name:match("_leaves$") then leaves=leaves+1 else trunks=trunks+1 end
|
|
end
|
|
assert(roots==1 and leaves>=5 and trunks>=3)
|
|
plan[1].name="foreign:node"; plan.size.x=999
|
|
assert(D.tree_plan(s,stage)[1].name~="foreign:node" and D.tree_plan(s,stage).size.x<10)
|
|
end end
|
|
local generic=D.tree_plan("generic","mature"); local root
|
|
for _,n in ipairs(generic) do if n.x==0 and n.y==0 and n.z==0 then root=n.name end end
|
|
assert(root=="hearth:mature_tree" and writes==before)
|
|
assert(not D.tree_plan("unknown","mature") and not D.tree_plan("oak","seedling"))
|
|
end)
|
|
test("tree recruitment uses soils, cold podzol, pioneer gaps, woodland and river niches",function()
|
|
assert(D.choose_tree(p,habitat("podzol",15,0.4))=="pine_seedling")
|
|
assert(D.choose_tree(p,habitat("alpine_turf",15,0.35))=="pine_seedling")
|
|
local warm=habitat("podzol",15,0.4); warm.heat=60
|
|
assert(D.choose_tree(p,warm)=="birch_seedling")
|
|
assert(D.choose_tree(p,habitat("grass",15,0.3))=="birch_seedling")
|
|
local forest=habitat("forest_floor",10,0.6); forest.canopy=1
|
|
assert(D.choose_tree(p,forest)=="oak_seedling")
|
|
assert(D.choose_tree(p,habitat("alluvium",12,0.9))=="willow_seedling")
|
|
assert(D.choose_tree(p,habitat("peat",12,0.9))=="alder_seedling")
|
|
assert(not D.choose_tree(p,habitat("stone",15,0.1)))
|
|
end)
|
|
test("optional biome species policy is authoritative without requiring the biome module",function()
|
|
hearth.biomes={supports_species=function(s,ground) return s=="alder" and ground=="grass" end}
|
|
assert(not D.supports_species("oak","grass"))
|
|
assert(D.supports_species("alder","grass"))
|
|
assert(not D.choose_tree(p,habitat("podzol",15,0.4)))
|
|
end)
|
|
test("all eight understory niches recruit deterministically on the first establishment cycle",function()
|
|
local cases={clover={habitat("grass",15,0.5),{x=3,y=1,z=0},"Summer"},
|
|
nettle={habitat("grass",15,0.6),{x=6,y=1,z=0},"Summer"},
|
|
heather={habitat("podzol",15,0.4),p,"Summer"},
|
|
bluebell={habitat("forest_floor",8,0.6),p,"Spring"},
|
|
sedge={habitat("alluvium",15,0.9),p,"Summer"},
|
|
bulrush={habitat("peat",15,0.9),{x=3,y=1,z=0},"Summer"},
|
|
moss={habitat("forest_floor",8,0.7),p,"Summer"},
|
|
lichen={habitat("granite",15,0.1),p,"Summer"}}
|
|
for k,c in pairs(cases) do
|
|
season=c[3]; local recruit=D.choose_recruit(c[2],c[1],season)
|
|
assert(recruit==k,k.." got "..tostring(recruit))
|
|
assert(D.choose_recruit(c[2],c[1],season)==recruit)
|
|
assert(not D.choose_recruit(c[2],c[1],"Winter"))
|
|
end
|
|
assert(not D.suitable("clover",habitat("peat",15,1)))
|
|
assert(not D.suitable("heather",habitat("forest_floor",15,0.4)))
|
|
assert(not D.suitable("nettle",{ground="grass",light=12,moisture=0.6,nutrients=0}))
|
|
end)
|
|
test("dormancy gates herbivore food and spring bluebells while pollination is exposed",function()
|
|
register(); season="Spring"
|
|
assert(D.edible("hearth:clover") and D.edible("hearth:sedge"))
|
|
assert(not D.edible("hearth:bluebell") and D.pollinator("bluebell",season))
|
|
for _,s in ipairs({"Summer","Autumn","Winter"}) do
|
|
season=s; assert(not hearth.seasons.available("bluebell"))
|
|
assert(api.registered_nodes[hearth.seasons.name_for("bluebell")].tiles[1]=="hearth_bluebell_dormant.png")
|
|
end
|
|
assert(not D.edible("hearth:clover_winter") and not D.edible("hearth:sedge_winter"))
|
|
assert(hearth.seasons.available("moss") and hearth.seasons.available("lichen"))
|
|
assert(not D.pollinator("bluebell","Summer") and D.pollinator("heather","Autumn"))
|
|
end)
|
|
test("load bootstrap and scheduler adoption mark only known unmodified species flora",function()
|
|
register(); world[key(p)]={name="hearth:alder_seedling"}
|
|
assert(D.bootstrap(p) and meta(p):get_string("succ_natural")=="1")
|
|
local before=writes; assert(not D.bootstrap(p) and writes==before)
|
|
for _,k in ipairs({"oak_log","oak_leaves","log","leaves","wall","berry_bush"}) do
|
|
fields={}; world[key(p)]={name="hearth:"..k}; assert(not D.bootstrap(p),k)
|
|
end
|
|
fields={}; world[key(p)]={name="hearth:clover"}
|
|
lbms["hearth:diversity_natural_flora"].action(p,world[key(p)])
|
|
assert(meta(p):get_string("succ_natural")=="1")
|
|
end)
|
|
test("player markers, claims, ambiguity, support protection and unload prevent adoption",function()
|
|
register()
|
|
for _,field in ipairs({"owner","planter","player_placed","placed_by","planted_at","third_party:placed"}) do
|
|
fields={}; world[key(p)]={name="hearth:oak_mature"}; meta(p):set_string(field,"1")
|
|
local before=writes; assert(not D.bootstrap(p) and writes==before,field)
|
|
end
|
|
for _,mode in ipairs({"claim","ambiguous","protected","support","unloaded"}) do
|
|
reset(); world[key(p)]={name="hearth:clover"}
|
|
if mode=="claim" then claims[key(p)]=true end
|
|
if mode=="ambiguous" then claims.ambiguous=true end
|
|
if mode=="protected" then protected[key(p)]=true end
|
|
if mode=="support" then protected["0:0:0"]=true end
|
|
if mode=="unloaded" then missing["0:0:0"]=true end
|
|
assert(not D.bootstrap(p) and writes==0,mode)
|
|
end
|
|
reset(); register(); world[key(p)]={name="hearth:clover"}
|
|
api.registered_nodes["hearth:clover"].after_place_node(p)
|
|
assert(not D.bootstrap(p) and meta(p):get_string("player_placed")=="1")
|
|
end)
|
|
test("deadwood load adoption is idempotent and does not broaden generic flora policy",function()
|
|
register(); world[key(p)]={name="hearth:deadwood",param2=2}
|
|
local loader=lbms["hearth:diversity_natural_flora"]
|
|
local count=0
|
|
for _,n in ipairs(loader.nodenames) do if n=="hearth:deadwood" then count=count+1 end end
|
|
assert(count==1 and loader.run_at_every_load)
|
|
loader.action(p,world[key(p)])
|
|
assert(meta(p):get_string("succ_natural")=="1" and writes==1)
|
|
loader.action(p,world[key(p)]); assert(writes==1)
|
|
assert(world[key(p)].name=="hearth:deadwood" and world[key(p)].param2==2)
|
|
assert(meta(p):get_string("succ_age")=="")
|
|
for _,k in ipairs({"litter","fungi","log","oak_log","leaves","oak_leaves","planks"}) do
|
|
fields={}; world[key(p)]={name="hearth:"..k}
|
|
local before=writes; assert(not D.bootstrap(p) and writes==before,k)
|
|
end
|
|
end)
|
|
test("deadwood adoption fails closed for source and support provenance, authority, identity and unload",function()
|
|
local below={x=0,y=0,z=0}
|
|
for _,q in ipairs({p,below}) do
|
|
for _,mode in ipairs({"owner","planter","placed_by","player_placed","hearth_placed","hearth_player_placed","planted_at","third_party:placed",
|
|
"claim","ambiguous","protected","unloaded","ignore"}) do
|
|
reset(); world[key(p)]={name="hearth:deadwood"}
|
|
if mode=="claim" then claims[key(q)]=true
|
|
elseif mode=="ambiguous" then claims.ambiguous=true
|
|
elseif mode=="protected" then protected[key(q)]=true
|
|
elseif mode=="unloaded" then missing[key(q)]=true
|
|
elseif mode=="ignore" then world[key(q)]={name="ignore"}
|
|
else meta(q):set_string(mode,"1") end
|
|
local before=writes
|
|
assert(not D.bootstrap(p) and writes==before,mode.." at "..key(q))
|
|
end
|
|
end
|
|
for _,support in ipairs({"air","hearth:stone","hearth:log","hearth:water_source"}) do
|
|
reset(); world[key(p)]={name="hearth:deadwood"}; world[key(below)]={name=support}
|
|
assert(not D.bootstrap(p) and writes==0,support)
|
|
end
|
|
reset(); world[key(p)]={name="air"}
|
|
assert(not D.bootstrap(p,{name="hearth:deadwood"}) and writes==0)
|
|
end)
|
|
test("ground policy is separate from crops and lichen never needs to replace its rock",function()
|
|
for _,k in ipairs({"grass","soil","forest_floor","podzol","alluvium","peat","alpine_turf"}) do assert(D.is_ground("hearth:"..k)) end
|
|
assert(not D.is_ground("hearth:granite") and D.is_rock("hearth:granite"))
|
|
assert(not D.supports_ground("clover","hearth:granite"))
|
|
assert(D.supports_ground("lichen","hearth:granite"))
|
|
assert(not D.is_ground("hearth:crop_4") and not D.is_ground("ignore"))
|
|
end)
|
|
test("optional missing bases do not panic seasonal registration or install a scheduler",function()
|
|
local steps=0; api.register_globalstep=function() steps=steps+1 end
|
|
hearth.seasons.register()
|
|
assert(steps==0)
|
|
D.register_nodes(function(n,def) api.register_node("hearth:"..n,def) end)
|
|
hearth.seasons.register()
|
|
assert(api.registered_nodes["hearth:oak_seedling_winter"] and api.registered_nodes["hearth:oak_mature"].drawtype=="normal" and steps==0)
|
|
end)
|
|
test("bootstrap rechecks loaded source identity and explicit recruitment season wins over clock",function()
|
|
world[key(p)]={name="air"}
|
|
assert(not D.bootstrap(p,{name="hearth:clover"}) and writes==0)
|
|
world[key(p)]={name="hearth:clover"}; missing[key(p)]=true
|
|
assert(not D.bootstrap(p,{name="hearth:clover"}) and writes==0)
|
|
for _,bad in ipairs({{}, {x=0/0,y=1,z=0},{x=math.huge,y=1,z=0}}) do assert(not D.bootstrap(bad)) end
|
|
season="Winter"
|
|
assert(D.choose_recruit(p,habitat("forest_floor",8,0.6),"Spring")=="bluebell")
|
|
end)
|
|
print("LAST_HEARTH_DIVERSITY_PASS "..passed)
|