Release Healer Man 0.1.12
This commit is contained in:
1 parent
13970d03c8
commit
65e24f644e
201 files changed
+5061
-123
No files matched your search
@@ -0,0 +1,230 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import {
|
||||
auditPriestOriginalManifest,
|
||||
packagePriestOriginalEffects,
|
||||
priestSpellIdsFromCatalog,
|
||||
} from "./package-priest-original.mjs";
|
||||
|
||||
test("collects every ranked Priest spell id", () => {
|
||||
const ids = priestSpellIdsFromCatalog({
|
||||
classes: {
|
||||
priest: [
|
||||
{ ranks: [{ spellId: 2050 }, { spellId: 2052 }] },
|
||||
{ ranks: [{ spellId: 2061 }] },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(ids.has(2050), true);
|
||||
assert.equal(ids.has(2052), true);
|
||||
assert.equal(ids.has(2061), true);
|
||||
assert.equal(ids.has(585), true);
|
||||
});
|
||||
|
||||
test("packages source-backed Priest graphs and attaches only ready assets", () => {
|
||||
const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), "healer-man-priest-effects-"));
|
||||
try {
|
||||
const modelRoot = path.join(temporaryRoot, "models");
|
||||
const textureRoot = path.join(temporaryRoot, "textures");
|
||||
const outputRoot = path.join(temporaryRoot, "public-pack");
|
||||
fs.mkdirSync(path.join(modelRoot, "Spells"), { recursive: true });
|
||||
fs.mkdirSync(path.join(textureRoot, "Spells"), { recursive: true });
|
||||
const modelBuffer = Buffer.alloc(64);
|
||||
modelBuffer.writeUInt32LE(0, 60);
|
||||
fs.writeFileSync(path.join(modelRoot, "Spells", "Fixture.m2"), modelBuffer);
|
||||
fs.writeFileSync(path.join(textureRoot, "Spells", "Fixture.BLP"), Buffer.from("source-texture"));
|
||||
|
||||
const sourceManifest = {
|
||||
schemaVersion: 1,
|
||||
coverage: { requested: 1, authentic: 1, fallback: 0 },
|
||||
entries: [{
|
||||
key: "wow335:2061",
|
||||
source: "wow335",
|
||||
spellId: 2061,
|
||||
phases: { impact: [{ model: "Spells/Fixture.mdx" }] },
|
||||
}],
|
||||
};
|
||||
const decoded = {
|
||||
durationMs: 900,
|
||||
animations: [{ index: 0, id: 0, variationIndex: 0, durationMs: 900, moveSpeed: 0, flags: 0 }],
|
||||
textures: [{ type: 0, flags: 0, filename: "Spells/Fixture.BLP" }],
|
||||
ribbons: [],
|
||||
particles: [{
|
||||
id: 1,
|
||||
flags: 0,
|
||||
position: [0, 0, 0],
|
||||
parentBone: 0,
|
||||
textureIndex: 0,
|
||||
geometryModel: "",
|
||||
recursionModel: "",
|
||||
blendMode: 4,
|
||||
emitterType: 1,
|
||||
particleColorIndex: 0,
|
||||
particleType: 0,
|
||||
headOrTail: 0,
|
||||
textureTileRotation: 0,
|
||||
textureRows: 1,
|
||||
textureColumns: 1,
|
||||
lifespanVariation: 0,
|
||||
emissionRateVariation: 0,
|
||||
scaleVariation: [0, 0],
|
||||
tailLength: 0,
|
||||
tracks: Object.fromEntries([
|
||||
"emissionSpeed", "speedVariation", "verticalRange", "horizontalRange", "gravity",
|
||||
"lifespan", "emissionRate", "emissionAreaLength", "emissionAreaWidth", "zSource", "enabled",
|
||||
].map((name) => [name, {
|
||||
interpolation: name === "emissionRate" ? 1 : 0,
|
||||
globalSequence: -1,
|
||||
firstSequence: { samples: name === "enabled" ? [{ timeMs: 0, value: 1 }] : [] },
|
||||
}]).concat([
|
||||
["headCell", { samples: [] }],
|
||||
["tailCell", { samples: [] }],
|
||||
])),
|
||||
samples: {
|
||||
emissionRate: [{ timeMs: 0, value: 12 }],
|
||||
lifespan: [{ timeMs: 0, value: 0.8 }],
|
||||
emissionSpeed: [{ timeMs: 0, value: 1 }],
|
||||
color: [{ normalizedTime: 0, value: [255, 255, 255] }],
|
||||
alpha: [{ normalizedTime: 0, value: 32767 }],
|
||||
scale: [{ normalizedTime: 0, value: [0.2, 0.2] }],
|
||||
},
|
||||
}],
|
||||
};
|
||||
const { manifest, report } = packagePriestOriginalEffects(sourceManifest, {
|
||||
catalog: { classes: { priest: [{ ranks: [{ spellId: 2061 }] }] } },
|
||||
modelRoots: [modelRoot],
|
||||
textureRoots: [textureRoot],
|
||||
outputRoot,
|
||||
publicPrefix: "/fixture/priest",
|
||||
decodeM2: () => decoded,
|
||||
convertTexture: (source, output) => fs.copyFileSync(source, output),
|
||||
});
|
||||
|
||||
const asset = manifest.entries[0].phases.impact[0];
|
||||
assert.equal(asset.effectKind, "wow-m2-particles");
|
||||
assert.match(asset.effectGraphUrl, /^\/fixture\/priest\/graphs\/fixture-/);
|
||||
assert.equal(report.packagedModels, 1);
|
||||
assert.equal(report.readyParticleEmitters, 1);
|
||||
const graphFile = path.join(outputRoot, "graphs", path.posix.basename(asset.effectGraphUrl));
|
||||
const graph = JSON.parse(fs.readFileSync(graphFile, "utf8"));
|
||||
assert.equal(graph.sourceModel, "Spells/Fixture.mdx");
|
||||
assert.equal(graph.textures[0].url.startsWith("/fixture/priest/textures/"), true);
|
||||
assert.equal(graph.interpolation, undefined);
|
||||
assert.equal(graph.particles[0].interpolation.emissionRate, 1);
|
||||
assert.deepEqual(graph.particles[0].samples.enabled, [{ timeMs: 0, value: 1 }]);
|
||||
} finally {
|
||||
fs.rmSync(temporaryRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("packages the Power Word: Shield aura as a three-layer static mesh graph", () => {
|
||||
const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), "healer-man-priest-shield-"));
|
||||
try {
|
||||
const modelRoot = path.join(temporaryRoot, "models");
|
||||
const textureRoot = path.join(temporaryRoot, "textures");
|
||||
const outputRoot = path.join(temporaryRoot, "public-pack");
|
||||
fs.mkdirSync(path.join(modelRoot, "Spells"), { recursive: true });
|
||||
fs.mkdirSync(path.join(textureRoot, "Spells"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(modelRoot, "Spells", "HolyDivineShield_State_Base.m2"), Buffer.from("fixture-m2"),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(modelRoot, "Spells", "HolyDivineShield_State_Base00.skin"), Buffer.from("fixture-skin"),
|
||||
);
|
||||
for (const texture of ["ShieldA.BLP", "ShieldB.BLP", "ShieldC.BLP"]) {
|
||||
fs.writeFileSync(path.join(textureRoot, "Spells", texture), Buffer.from(`source-${texture}`));
|
||||
}
|
||||
const sourceManifest = {
|
||||
schemaVersion: 1,
|
||||
coverage: { requested: 1, authentic: 1, fallback: 0 },
|
||||
entries: [{
|
||||
key: "wow335:17",
|
||||
source: "wow335",
|
||||
spellId: 17,
|
||||
phases: {
|
||||
"aura-start": [{ model: "Spells/HolyDivineShield_State_Base.mdx" }],
|
||||
},
|
||||
}],
|
||||
};
|
||||
const decodedMesh = {
|
||||
bounds: { minimum: [-1, -1, 0], maximum: [1, 1, 0], radius: 1.5 },
|
||||
vertices: [
|
||||
{ position: [0, 0, 0], normal: [1, 0, 0], uv: [0, 0] },
|
||||
{ position: [0, 1, 0], normal: [1, 0, 0], uv: [1, 0] },
|
||||
{ position: [0, 0, 1], normal: [1, 0, 0], uv: [0, 1] },
|
||||
],
|
||||
indices: [0, 1, 2, 0, 1, 2, 0, 1, 2],
|
||||
textures: ["ShieldA.BLP", "ShieldB.BLP", "ShieldC.BLP"].map((filename) => ({
|
||||
type: 0, flags: 0, filename: `Spells/${filename}`,
|
||||
})),
|
||||
materials: [0, 1, 2].map(() => ({ flags: 19, blendMode: 4 })),
|
||||
skin: {
|
||||
textureUnits: [0, 1, 2].map((index) => ({
|
||||
submeshIndex: index,
|
||||
priorityPlane: 0,
|
||||
shaderId: 0,
|
||||
colorIndex: index,
|
||||
materialIndex: index,
|
||||
materialLayer: 0,
|
||||
textureIndices: [index],
|
||||
textureCoordinateIndices: [0],
|
||||
textureWeightIndices: [index],
|
||||
textureTransformIndices: [-1],
|
||||
drawRange: { indexStart: index * 3, indexCount: 3 },
|
||||
})),
|
||||
},
|
||||
};
|
||||
const { manifest, report } = packagePriestOriginalEffects(sourceManifest, {
|
||||
catalog: { classes: { priest: [{ ranks: [{ spellId: 17 }] }] } },
|
||||
modelRoots: [modelRoot],
|
||||
textureRoots: [textureRoot],
|
||||
outputRoot,
|
||||
publicPrefix: "/fixture/priest",
|
||||
decodeM2: () => { throw new Error("particle decoder should not handle the static shield"); },
|
||||
decodeMesh: () => decodedMesh,
|
||||
convertTexture: (source, output) => fs.copyFileSync(source, output),
|
||||
});
|
||||
|
||||
const asset = manifest.entries[0].phases["aura-start"][0];
|
||||
assert.equal(asset.effectKind, "wow-m2-static-mesh");
|
||||
assert.match(asset.effectGraphUrl, /^\/fixture\/priest\/graphs\/holydivineshield-state-base-/);
|
||||
assert.equal(report.readyStaticMeshes, 1);
|
||||
assert.equal(report.readyParticleEmitters, 0);
|
||||
const graphFile = path.join(outputRoot, "graphs", path.posix.basename(asset.effectGraphUrl));
|
||||
const graph = JSON.parse(fs.readFileSync(graphFile, "utf8"));
|
||||
assert.equal(graph.kind, "wow-m2-static-mesh");
|
||||
assert.deepEqual(graph.geometry, {
|
||||
positions: [0, 0, 0, 0, 1, 0, 0, 0, 1],
|
||||
normals: [1, 0, 0, 1, 0, 0, 1, 0, 0],
|
||||
uvs: [0, 0, 1, 0, 0, 1],
|
||||
indices: [0, 1, 2, 0, 1, 2, 0, 1, 2],
|
||||
});
|
||||
assert.equal(graph.layers.length, 3);
|
||||
assert.deepEqual(graph.layers.map((layer) => ({
|
||||
textureIndex: layer.textureIndex,
|
||||
blendMode: layer.blendMode,
|
||||
drawRange: [layer.indexStart, layer.indexCount],
|
||||
})), [
|
||||
{ textureIndex: 0, blendMode: 4, drawRange: [0, 3] },
|
||||
{ textureIndex: 1, blendMode: 4, drawRange: [3, 3] },
|
||||
{ textureIndex: 2, blendMode: 4, drawRange: [6, 3] },
|
||||
]);
|
||||
assert.ok(graph.textures.every((texture) => texture.url.startsWith("/fixture/priest/textures/")));
|
||||
const audit = auditPriestOriginalManifest(manifest, new Set([17]), new Set([17]));
|
||||
assert.deepEqual(audit.missingPowerWordShieldAuraStarts, []);
|
||||
const missingAudit = auditPriestOriginalManifest({
|
||||
...manifest,
|
||||
entries: manifest.entries.map((entry) => ({
|
||||
...entry,
|
||||
phases: { "aura-start": entry.phases["aura-start"].map((item) => ({ ...item, effectKind: "wrong" })) },
|
||||
})),
|
||||
}, new Set([17]), new Set([17]));
|
||||
assert.deepEqual(missingAudit.missingPowerWordShieldAuraStarts, [17]);
|
||||
} finally {
|
||||
fs.rmSync(temporaryRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user