Ruined
This commit is contained in:
+31
-15
@@ -11,6 +11,7 @@ import { PALETTE, SPECIES_NAMES, EVERGREEN_NAMES, worldArt } from '../scripts/wo
|
||||
import { BOTANY_ART_MANIFEST, generateAssets, png } from '../scripts/assets.mjs';
|
||||
import { packageGame, verifyPackage } from '../scripts/package.mjs';
|
||||
import { CHARACTER_ART_MANIFEST } from '../scripts/character-art.mjs';
|
||||
import { FAUNA_ART_MANIFEST, FAUNA_NAMES, FAUNA_ANIMATIONS, faunaB3D, faunaTexture, faunaPortraitSheet } from '../scripts/fauna-art.mjs';
|
||||
|
||||
const cross=(a,b)=>[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]];
|
||||
const sub=(a,b)=>a.map((v,i)=>v-b[i]);
|
||||
@@ -165,14 +166,14 @@ test('all four seasonal atlases preserve byte-exact bark and opaque 64x32 materi
|
||||
for(const season of TREE_SEASONS) assert.throws(()=>treeAtlas(new Map(),season),/requires/);
|
||||
});
|
||||
|
||||
test('deprecated 23-species-mesh inventory stays frozen; all ten cube species have 34 compatibility atlases',()=> {
|
||||
test('deprecated 23-species-mesh inventory stays frozen; all thirteen cube species have 46 compatibility atlases',()=> {
|
||||
const species=['oak','birch','pine','willow','alder'];
|
||||
const meshes=species.flatMap(s=>[`${s}_seedling`,`${s}_young`,`${s}_mature`,
|
||||
...(s==='pine'?[]:[`${s}_young_winter`,`${s}_mature_winter`])]);
|
||||
const atlases=SPECIES_NAMES.flatMap(s=>[`${s}_atlas`,...(EVERGREEN_NAMES.includes(s)?[]:['spring','autumn','winter'].map(n=>`${s}_atlas_${n}`))]);
|
||||
assert.deepEqual(LEGACY_MESH_SPECIES,species);
|
||||
assert.deepEqual(SPECIES_MESH_NAMES,meshes); assert.deepEqual(SPECIES_ATLAS_NAMES,atlases);
|
||||
assert.equal(meshes.length,23); assert.equal(atlases.length,34); assert.equal(FLORA_NAMES.length,29);
|
||||
assert.equal(meshes.length,23); assert.equal(atlases.length,46); assert.equal(FLORA_NAMES.length,29);
|
||||
for(const s of SPECIES_NAMES.slice(5)) for(const stage of ['seedling','young','mature','young_winter','mature_winter'])
|
||||
assert.throws(()=>floraOBJ(`${s}_${stage}`),/Unknown flora/,'new species must not grow unused irregular meshes');
|
||||
for(const s of species) assert.ok(!FLORA_NAMES.includes(`${s}_deadwood`));
|
||||
@@ -249,14 +250,15 @@ test('all species atlases use exact own bark and own seasonal leaves, opaque und
|
||||
if(leaf.data[from+3]) assert.deepEqual(image.data.slice(right,right+4),leaf.data.slice(from,from+4));
|
||||
else {
|
||||
const color=season==='summer'?{oak:'leafDark',birch:'leaf',pine:'pineShade',willow:'willowShade',alder:'leafDark',
|
||||
beech:'leafDark',aspen:'leafDark',rowan:'leafDark',hazel:'leafDark',spruce:'pineShade'}[s]:
|
||||
beech:'leafDark',aspen:'leafDark',rowan:'leafDark',hazel:'leafDark',spruce:'pineShade',
|
||||
crab_apple:'leafDark',wild_cherry:'leafDark',field_maple:'leafDark'}[s]:
|
||||
{spring:'springDark',autumn:'autumnDark',winter:'barkDark'}[season];
|
||||
assert.deepEqual([...image.data.slice(right,right+4)],[...PALETTE[color],255]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.equal(distinct.size,34);
|
||||
assert.equal(distinct.size,SPECIES_ATLAS_NAMES.length);
|
||||
for(const s of EVERGREEN_NAMES) for(const season of ['spring','autumn','winter'])
|
||||
assert.throws(()=>treeAtlas(art,season,s),/evergreen/);
|
||||
for(const bad of ['constructor','../oak','maple','']) assert.throws(()=>treeAtlas(art,'summer',bad),/Unknown tree species/);
|
||||
@@ -278,7 +280,7 @@ test('minimal fixture generation and integrity-verified package include every se
|
||||
assert.deepEqual(await fs.readFile(path.join(destination,`mods/hearth/textures/hearth_tree_atlas${season==='summer'?'':`_${season}`}.png`)),expected);
|
||||
}
|
||||
for(const name of SPECIES_ATLAS_NAMES) {
|
||||
const [s,,season='summer']=name.split('_'), atlas=treeAtlas(art,season,s);
|
||||
const [,s,season='summer']=name.match(/^([a-z]+(?:_[a-z]+)*)_atlas(?:_(spring|autumn|winter))?$/), atlas=treeAtlas(art,season,s);
|
||||
const expected=png(64,(x,y)=>atlas.data.subarray((y*64+x)*4,(y*64+x+1)*4),32);
|
||||
assert.deepEqual(await fs.readFile(path.join(destination,`mods/hearth/textures/hearth_${name}.png`)),expected);
|
||||
}
|
||||
@@ -288,15 +290,29 @@ test('minimal fixture generation and integrity-verified package include every se
|
||||
...SPECIES_ATLAS_NAMES.map(n=>`hearth_${n}.png`),
|
||||
...['knife','axe','pick','spear'].map(n=>`hearth_icon_${n}.png`),
|
||||
...CHARACTER_ART_MANIFEST.map(entry=>entry.filename),
|
||||
...FAUNA_ART_MANIFEST.map(entry=>entry.texture),'hearth_fauna_contact_sheet.png',
|
||||
'hearth_character_contact_sheet.png',
|
||||
'hearth_person.png','crosshair.png','hearth_art_contact_sheet.png','hearth_c7aa83.png','hearth_947853.png'];
|
||||
assert.equal(expectedTextures.length,578,'includes all 170 character layers and their review sheet');
|
||||
assert.equal(expectedTextures.length,675,'complete strict inventory: world + atlases + characters + fauna + two swatches');
|
||||
assert.deepEqual(JSON.parse(await fs.readFile(path.join(destination,'botany-art.json'),'utf8')),
|
||||
{format:1,images:BOTANY_ART_MANIFEST},'all 160 botanical image identities are packaged');
|
||||
{format:1,images:BOTANY_ART_MANIFEST},'all 200 botanical image identities are packaged');
|
||||
assert.deepEqual(JSON.parse(await fs.readFile(path.join(destination,'fauna-art.json'),'utf8')),
|
||||
{format:1,animations:FAUNA_ANIMATIONS,animals:FAUNA_ART_MANIFEST,
|
||||
portraitSheet:{filename:'hearth_fauna_contact_sheet.png',width:512,height:768,frame:9,columns:4,cellSize:128,order:FAUNA_NAMES}});
|
||||
for(const entry of FAUNA_ART_MANIFEST) {
|
||||
assert.deepEqual(await fs.readFile(path.join(destination,'mods/hearth/models',entry.mesh)),faunaB3D(entry.id));
|
||||
const image=faunaTexture(entry.id);
|
||||
assert.deepEqual(await fs.readFile(path.join(destination,'mods/hearth/textures',entry.texture)),
|
||||
png(64,(x,y)=>image.data.subarray((y*64+x)*4,(y*64+x+1)*4)));
|
||||
}
|
||||
const portraits=faunaPortraitSheet();
|
||||
assert.deepEqual(await fs.readFile(path.join(destination,'mods/hearth/textures/hearth_fauna_contact_sheet.png')),
|
||||
png(portraits.width,(x,y)=>portraits.data.subarray((y*portraits.width+x)*4,(y*portraits.width+x+1)*4),portraits.height));
|
||||
assert.deepEqual((await fs.readdir(path.join(destination,'mods/hearth/textures'))).sort(),expectedTextures.sort());
|
||||
const expectedModels=[...FLORA_NAMES,...['hare','fox','deer','knife','axe','pick','spear','person']]
|
||||
.map(n=>`hearth_${n}.obj`).concat('hearth_person.b3d','hearth_person_compact.b3d','hearth_person_broad.b3d');
|
||||
assert.equal(expectedModels.length,40,'37 OBJ files plus three animated player builds');
|
||||
.map(n=>`hearth_${n}.obj`).concat('hearth_person.b3d','hearth_person_compact.b3d','hearth_person_broad.b3d',
|
||||
...FAUNA_ART_MANIFEST.map(entry=>entry.mesh));
|
||||
assert.equal(expectedModels.length,63,'37 OBJ files plus three player builds plus 23 fauna rigs');
|
||||
assert.deepEqual((await fs.readdir(path.join(destination,'mods/hearth/models'))).sort(),expectedModels.sort());
|
||||
}
|
||||
await check(out);
|
||||
@@ -305,7 +321,7 @@ test('minimal fixture generation and integrity-verified package include every se
|
||||
engine:{name:'Luanti',version:'5.18.0'},node:'>=22',packageFormat:1,backupFormat:1}));
|
||||
await fs.mkdir(path.join(source,'tests/fixtures'),{recursive:true});
|
||||
await fs.writeFile(path.join(source,'tests/fixtures/scenarios.json'),'{}');
|
||||
const packaged=path.join(root,'package'); await packageGame(packaged,{source}); await check(packaged); await verifyPackage(packaged);
|
||||
const packaged=path.join(root,'package'); await packageGame(packaged,{source}); await check(packaged); await verifyPackage(packaged,{requireBundled:false});
|
||||
const outside=path.join(root,'outside'); await fs.mkdir(outside);
|
||||
await fs.rm(path.join(out,'mods/hearth/models'),{recursive:true});
|
||||
await fs.symlink(outside,path.join(out,'mods/hearth/models'),process.platform==='win32'?'junction':'dir');
|
||||
@@ -313,9 +329,9 @@ test('minimal fixture generation and integrity-verified package include every se
|
||||
assert.deepEqual(await fs.readdir(outside),[]);
|
||||
});
|
||||
|
||||
test('read-only cube diversity and biome contracts cover ten exact species; review layout is documented',async()=> {
|
||||
test('read-only cube diversity and biome contracts cover thirteen exact species; review layout is documented',async()=> {
|
||||
const source=await fs.readFile(new URL('../mods/hearth/diversity.lua',import.meta.url),'utf8');
|
||||
const species=source.match(/species_order=\{([^}]+)\}/)[1].match(/"[a-z]+"/g).map(s=>s.slice(1,-1));
|
||||
const species=source.match(/species_order=\{([^}]+)\}/)[1].match(/"[a-z_]+"/g).map(s=>s.slice(1,-1));
|
||||
assert.deepEqual(species,SPECIES_NAMES);
|
||||
assert.match(source,/drawtype=woody and "normal" or "plantlike"/);
|
||||
assert.match(source,/tiles=woody and \{"hearth_"\.\.s\.\."_log_top.png","hearth_"\.\.s\.\."_log_top.png","hearth_"\.\.s\.\."_log_side.png"\}/);
|
||||
@@ -323,16 +339,16 @@ test('read-only cube diversity and biome contracts cover ten exact species; revi
|
||||
assert.match(source,/evergreen=s=="pine" or s=="spruce"/);
|
||||
assert.match(source,/plant~="moss" and plant~="lichen"/,'persistent groundcover has no dormant alias');
|
||||
const biomes=await fs.readFile(new URL('../mods/hearth/biomes.lua',import.meta.url),'utf8');
|
||||
const biomeSpecies=biomes.match(/species_order\s*=\s*\{([^}]+)\}/)[1].match(/"[a-z]+"/g).map(s=>s.slice(1,-1));
|
||||
const biomeSpecies=biomes.match(/species_order\s*=\s*\{([^}]+)\}/)[1].match(/"[a-z_]+"/g).map(s=>s.slice(1,-1));
|
||||
assert.deepEqual(biomeSpecies,SPECIES_NAMES);
|
||||
const art=worldArt();
|
||||
for(const s of species) for(const face of ['log_top','log_side','leaves','planks'])
|
||||
assert.ok(art.has(`hearth_${s}_${face}.png`));
|
||||
const docs=await fs.readFile(new URL('../docs/art-direction.md',import.meta.url),'utf8');
|
||||
const current=docs.slice(0,docs.indexOf('<details>'));
|
||||
assert.match(current,/512×736/); assert.match(current,/1–512/); assert.match(current,/16 columns/);
|
||||
assert.match(current,/512×864/); assert.match(current,/1–512/); assert.match(current,/16 columns/);
|
||||
assert.match(current,/ceil\(count\/16\)\*32/);
|
||||
assert.match(current,/360/); assert.match(current,/160/); assert.match(current,/botanicalLatin/);
|
||||
assert.match(current,/421/); assert.match(current,/200/); assert.match(current,/botanicalLatin/);
|
||||
for(const s of SPECIES_NAMES) assert.ok(current.includes(s));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user