miijs
Version:
The most complete and easy to use Mii library available.
707 lines (650 loc) • 33.4 kB
JavaScript
import { decodeMii } from "./miiProcess.js";
import { ConsoleFormats, backPort, defaultMappings, mappings } from "./formats.js";
import { lookupTables } from "./data.js";
const ignoredInstructionDefaults = ["gender"];//Always return these in the instructions even when they're default
const gridColumn = type => (type.startsWith("SWITCH") ? [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] : [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]);
const gridRow = type => type.startsWith("SWITCH") ? [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2] : [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3];
const defaultRowNames = ["first", "second", "third", "fourth"];
function getLoc(table, val) {
for (let y = 0; y < table.length; y++) {
for (let x = 0; x < table[y].length; x++) {
if (table[y][x] === val) {
return [x, y];
}
}
}
return [-1, -1];
}
function getFlatGridLoc(table, val, grid = lookupTables.instrGrids.threeBy) {
const index = table.indexOf(val);
return index === -1 ? [-1, -1] : getLoc(grid, index);
}
function getNestedValue(obj, path) {
try {
const parts = path.split('.');
let current = obj;
for (const part of parts) {
if (current === undefined || current === null) {
return undefined;
}
current = current[part];
}
return current;
}
catch (e) {
return null;
}
}
function setNestedValue(obj, key, value) {
try {
const keys = key.split('.');
const lastKey = keys.pop();
const target = keys.reduce((current, key) => {
if (!current[key] || typeof current[key] !== 'object' || Array.isArray(current[key])) {
current[key] = {};
}
return current[key];
}, obj);
target[lastKey] = value;
return obj;
}
catch (e) {
throw new Error(`${key} was not usable.`);
}
}
function deleteNestedValue(obj, path) {
const parts = path.split(".");
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
if (current == null) return; // parent chain missing
if (!current.hasOwnProperty(parts[i])) return;
current = current[parts[i]];
}
delete current[parts[parts.length - 1]];
}
function defaultsEqual(a, b) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
return true;
}
return a === b;
}
function getDefaultFor(key, currentVal) {
if (defaultMappings.hasOwnProperty(key)) return defaultMappings[key];
// Fallback defaults to 0/false when undefined in defaultMappings
if (typeof currentVal === "boolean") return false;
return 0;
}
// Helper function to format grid position strings from array
const formatGridPos = (arr) => {
if (!Array.isArray(arr) || arr.some(value => !Number.isFinite(Number(value)) || Number(value) < 0)) {
return "closest available option";
}
if (arr.length === 3 && arr[0] !== undefined) {
// [page, x, y]
return `on page ${arr[0] + 1}, ${arr[1] + 1} from the left, ${arr[2] + 1} from the top`;
} else if (arr.length === 2) {
return `${arr[0] + 1} from the left, ${arr[1] + 1} from the top`;
} else if (arr.length === 1) {
// [y] or [x]
return `${arr[0] + 1} from the top`;
}
return "";
};
// Helper for color positions with expanded menu notation
const formatColorPos = (arr) => {
const hasExpanded = arr.length === 3 && arr[2] === 1;
const base = hasExpanded ? formatGridPos([arr[0], arr[1]]) : formatGridPos(arr);
return hasExpanded ? `${base}, in the expanded colors menu` : base;
};
// Special formatter for face.color and similar that use row encoding
const formatRowPos = (arr, rowNames = defaultRowNames) => {
if (!Array.isArray(arr) || arr.some(value => typeof value !== "string" && (!Number.isFinite(Number(value)) || Number(value) < 0))) {
return "closest available option";
}
if (arr.length === 2) {
const rowName = typeof arr[1] === "string" ? arr[1] : rowNames[arr[1]];
if (!rowName) return formatGridPos(arr);
return rowName === "first" || rowName === "top"
? `${arr[0] + 1} from the left`
: `${arr[0] + 1} from the left, on the ${rowName} row`;
}
return formatGridPos(arr);
};
const faceColorRowNames = type => type === ConsoleFormats.WIIU ? ["top", "middle", "bottom"] : ["first", "second"];
// Helper function to reverse getLoc - finds the index that produces the given [x, y] coordinates
function reverseGetLoc(grid, x, y) {
for (var i = 0; i < grid.length; i++) {
var loc = getLoc(grid, i);
if (loc[0] === x && loc[1] === y) {
return i;
}
}
return null; // Not found
}
// Helper function to reverse grid lookup for face properties
function reverseGridLookup(colGrid, rowGrid, x, row) {
for (var i = 0; i < colGrid.length; i++) {
if (colGrid[i] === x && rowGrid[i] === row) {
return i;
}
}
return null; // Not found
}
function getAs(mii, type, field) {
if(mappings.hasOwnProperty(field)){
field=mappings[field];
}
switch (field) {
// Face
case "face.color":
switch (type) {
case ConsoleFormats.WII:
return mii.face.color > 2
? [mii.face.color - 3, 1] // [x, row] where row: 0=first, 1=second
: [mii.face.color, 0];
case ConsoleFormats.DS:
case ConsoleFormats["3DS"]:
return [mii.face.color]; // [y]
case ConsoleFormats.WIIU:
return [
[0, 1, 0, 1, 0, 1][mii.face.color],
[0, 0, 1, 1, 2, 2][mii.face.color] // 0=top, 1=middle, 2=bottom
];
case ConsoleFormats.SWITCH:
case ConsoleFormats.SWITCH2:
var fc = lookupTables.switch.faceColors[mii.face.color];
return fc > 4
? [fc - 5, 1] // [x, row]
: [fc, 0];
}
case "face.type":
return [gridColumn(type)[mii.face.type], gridRow(type)[mii.face.type]]; // [x, row]
case "face.makeup":
return [gridColumn(type)[mii.face.makeup], gridRow(type)[mii.face.makeup]]; // [x, row]
case "face.feature":
return [gridColumn(type)[mii.face.feature], gridRow(type)[mii.face.feature]]; // [x, row]
// Hair
case "hair.type":
switch (type) {
case ConsoleFormats.DS:
case ConsoleFormats.WII:
var page = getLoc(lookupTables.pages.hair.wii, mii.hair.type);
var gridLoc = getLoc(lookupTables.instrGrids.threeBy, page[0]);
return [page[1], gridLoc[0], gridLoc[1]]; // [page, x, y]
case ConsoleFormats["3DS"]:
case ConsoleFormats.WIIU:
var page = getLoc(lookupTables.pages.hair["3ds"], mii.hair.type);
var gridLoc = getLoc(lookupTables.instrGrids.threeBy, page[0]);
return [page[1], gridLoc[0], gridLoc[1]]; // [page, x, y]
case ConsoleFormats.SWITCH:
case ConsoleFormats.SWITCH2:
return [
getLoc(lookupTables.grids.hairs, mii.hair.type)[0],
getLoc(lookupTables.grids.hairs, mii.hair.type)[1]
]; // [x, y]
}
case "hair.color":
if (type.endsWith("DS")) {
return [mii.hair.color]; // [y]
}
var grid = type.startsWith("SWITCH") ? lookupTables.grids.colors
: type === "WII" ? lookupTables.instrGrids.fourBy
: lookupTables.instrGrids.twoBy;
var loc = getLoc(grid, mii.hair.color);
return type.startsWith("SWITCH")
? [loc[0], loc[1], 1] // [x, y, expanded] where 1=expanded menu
: [loc[0], loc[1]]; // [x, y]
// Eyes
case "eyes.type":
if (type.startsWith("SWITCH")) {
return [
getLoc(lookupTables.grids.eyes, mii.eyes.type)[0],
getLoc(lookupTables.grids.eyes, mii.eyes.type)[1]
]; // [x, y]
}
var page = getLoc(lookupTables.pages.eyes, mii.eyes.type);
var gridLoc = getLoc(lookupTables.instrGrids.threeBy, page[0]);
return [page[1], gridLoc[0], gridLoc[1]]; // [page, x, y]
case "eyes.color":
if (type.endsWith("DS")) {
return [mii.eyes.color]; // [y]
}
var grid = type.startsWith("SWITCH") ? lookupTables.grids.colors
: type === "WII" ? lookupTables.instrGrids.threeBy
: lookupTables.instrGrids.twoBy;
var loc = getLoc(grid, mii.eyes.color);
return type.startsWith("SWITCH")
? [loc[0], loc[1], 1] // [x, y, expanded]
: [loc[0], loc[1]]; // [x, y]
// Eyebrows
case "eyebrows.type":
if (type.startsWith("SWITCH")) {
return [
getLoc(lookupTables.grids.eyebrows, mii.eyebrows.type)[0],
getLoc(lookupTables.grids.eyebrows, mii.eyebrows.type)[1]
]; // [x, y]
}
var page = getLoc(lookupTables.pages.eyebrows, mii.eyebrows.type);
var gridLoc = getLoc(lookupTables.instrGrids.threeBy, page[0]);
return [page[1], gridLoc[0], gridLoc[1]]; // [page, x, y]
case "eyebrows.color":
if (type.endsWith("DS")) {
return [mii.eyebrows.color]; // [y]
}
var grid = type.startsWith("SWITCH") ? lookupTables.grids.colors
: type === "WII" ? lookupTables.instrGrids.fourBy
: lookupTables.instrGrids.twoBy;
var loc = getLoc(grid, mii.eyebrows.color);
return type.startsWith("SWITCH")
? [loc[0], loc[1], 1] // [x, y, expanded]
: [loc[0], loc[1]]; // [x, y]
// Nose
case "nose.type":
switch (type) {
case ConsoleFormats.DS:
case ConsoleFormats.WII:
return getFlatGridLoc(lookupTables.pages.noses[0], mii.nose.type); // [x, y]
case ConsoleFormats["3DS"]:
case ConsoleFormats.WIIU:
var page = getLoc(lookupTables.pages.noses, mii.nose.type);
var gridLoc = getLoc(lookupTables.instrGrids.threeBy, page[0]);
return [page[1], gridLoc[0], gridLoc[1]]; // [page, x, y]
case ConsoleFormats.SWITCH:
case ConsoleFormats.SWITCH2:
return [
getLoc(lookupTables.grids.noses, mii.nose.type)[0],
getLoc(lookupTables.grids.noses, mii.nose.type)[1]
]; // [x, y]
}
// Mouth
case "mouth.type":
if (type.startsWith("SWITCH")) {
return [
getLoc(lookupTables.grids.mouths, mii.mouth.type)[0],
getLoc(lookupTables.grids.mouths, mii.mouth.type)[1]
]; // [x, y]
}
var page = getLoc(lookupTables.pages.mouths, mii.mouth.type);
var gridLoc = getLoc(lookupTables.instrGrids.threeBy, page[0]);
return [page[1], gridLoc[0], gridLoc[1]]; // [page, x, y]
case "mouth.color":
if (type.endsWith(ConsoleFormats.DS) || type === ConsoleFormats.WII) {
return type === ConsoleFormats.WII ? [mii.mouth.color] : [mii.mouth.color]; // [x] or [y]
}
var grid = type.startsWith("SWITCH") ? lookupTables.grids.colors : lookupTables.instrGrids.twoBy;
var loc = getLoc(grid, mii.mouth.color);
return type.startsWith("SWITCH")
? [loc[0], loc[1], 1] // [x, y, expanded]
: [loc[0], loc[1]]; // [x, y]
// Beard
case "beard.mustache.type":
var loc = getLoc(type.startsWith("SWITCH") ? lookupTables.instrGrids.threeBy : lookupTables.instrGrids.twoBy, mii.beard.mustache.type);
return [loc[0], loc[1]]; // [x, y]
case "beard.type":
var loc = getLoc(type.startsWith("SWITCH") ? lookupTables.instrGrids.threeBy : lookupTables.instrGrids.twoBy, mii.beard.type);
return [loc[0], loc[1]]; // [x, y]
case "beard.color":
if (type.endsWith("DS")) {
return [mii.beard.color]; // [y]
}
var grid = type.startsWith("SWITCH") ? lookupTables.grids.colors
: type === "WII" ? lookupTables.instrGrids.fourBy
: lookupTables.instrGrids.twoBy;
var loc = getLoc(grid, mii.beard.color);
return type.startsWith("SWITCH")
? [loc[0], loc[1], 1] // [x, y, expanded]
: [loc[0], loc[1]]; // [x, y]
// Glasses
case "glasses.type":
var loc = getLoc(type.startsWith("SWITCH") ? lookupTables.grids.glasses : lookupTables.instrGrids.threeBy, mii.glasses.type);
return [loc[0], loc[1]]; // [x, y]
case "glasses.color":
if (type.endsWith("DS")) {
return [mii.glasses.color]; // [y]
}
var grid = type.startsWith("SWITCH") ? lookupTables.grids.colors
: type === "WII" ? lookupTables.instrGrids.threeBy
: lookupTables.instrGrids.twoBy;
var loc = getLoc(grid, mii.glasses.color);
return type.startsWith("SWITCH")
? [loc[0], loc[1], 1] // [x, y, expanded]
: [loc[0], loc[1]]; // [x, y]
}
return null;
}
function setAs(mii, type, field, value) {
if(mappings.hasOwnProperty(field)){
field=mappings[field];
}
switch (field) {
// Face
case "face.color":
switch (type) {
case ConsoleFormats.WII:
// value = [x, row] where row: 0=first, 1=second
mii.face.color = value[1] === 1 ? value[0] + 3 : value[0];
break;
case ConsoleFormats.DS:
case ConsoleFormats["3DS"]:
// value = [y]
mii.face.color = value[0];
break;
case ConsoleFormats.WIIU:
// value = [x, y] where y: 0=top, 1=middle, 2=bottom
// Reverse mapping: [0,0]->0, [1,0]->1, [0,1]->2, [1,1]->3, [0,2]->4, [1,2]->5
mii.face.color = value[1] * 2 + value[0];
break;
case ConsoleFormats.SWITCH:
case ConsoleFormats.SWITCH2:
// value = [x, row]
var fc = value[1] === 1 ? value[0] + 5 : value[0];
// Reverse lookup in switch.faceColors
mii.face.color = lookupTables.switch.faceColors.indexOf(fc);
break;
}
break;
case "face.type":
// value = [x, row]
mii.face.type = reverseGridLookup(gridColumn(type), gridRow(type), value[0], value[1]);
break;
case "face.makeup":
// value = [x, row]
mii.face.makeup = reverseGridLookup(gridColumn(type), gridRow(type), value[0], value[1]);
break;
case "face.feature":
// value = [x, row]
mii.face.feature = reverseGridLookup(gridColumn(type), gridRow(type), value[0], value[1]);
break;
// Hair
case "hair.type":
switch (type) {
case ConsoleFormats.DS:
case ConsoleFormats.WII:
// value = [page, x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[1], value[2]);
mii.hair.type = reverseGetLoc(lookupTables.pages.hair.wii, gridIndex, value[0]);
break;
case ConsoleFormats["3DS"]:
case ConsoleFormats.WIIU:
// value = [page, x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[1], value[2]);
mii.hair.type = reverseGetLoc(lookupTables.pages.hair["3ds"], gridIndex, value[0]);
break;
case ConsoleFormats.SWITCH:
case ConsoleFormats.SWITCH2:
// value = [x, y]
mii.hair.type = reverseGetLoc(lookupTables.grids.hairs, value[0], value[1]);
break;
}
break;
case "hair.color":
if (type.endsWith("DS")) {
// value = [y]
mii.hair.color = value[0];
} else if (type.startsWith("SWITCH")) {
// value = [x, y, expanded]
mii.hair.color = reverseGetLoc(lookupTables.grids.colors, value[0], value[1]);
} else {
// value = [x, y]
var grid = type === "WII" ? lookupTables.instrGrids.fourBy : lookupTables.instrGrids.twoBy;
mii.hair.color = reverseGetLoc(grid, value[0], value[1]);
}
break;
// Eyes
case "eyes.type":
if (type.startsWith("SWITCH")) {
// value = [x, y]
mii.eyes.type = reverseGetLoc(lookupTables.grids.eyes, value[0], value[1]);
} else {
// value = [page, x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[1], value[2]);
mii.eyes.type = reverseGetLoc(lookupTables.pages.eyes, gridIndex, value[0]);
}
break;
case "eyes.color":
if (type.endsWith("DS")) {
// value = [y]
mii.eyes.color = value[0];
} else if (type.startsWith("SWITCH")) {
// value = [x, y, expanded]
mii.eyes.color = reverseGetLoc(lookupTables.grids.colors, value[0], value[1]);
} else {
// value = [x, y]
var grid = type === "WII" ? lookupTables.instrGrids.threeBy : lookupTables.instrGrids.twoBy;
mii.eyes.color = reverseGetLoc(grid, value[0], value[1]);
}
break;
// Eyebrows
case "eyebrows.type":
if (type.startsWith("SWITCH")) {
// value = [x, y]
mii.eyebrows.type = reverseGetLoc(lookupTables.grids.eyebrows, value[0], value[1]);
} else {
// value = [page, x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[1], value[2]);
mii.eyebrows.type = reverseGetLoc(lookupTables.pages.eyebrows, gridIndex, value[0]);
}
break;
case "eyebrows.color":
if (type.endsWith("DS")) {
// value = [y]
mii.eyebrows.color = value[0];
} else if (type.startsWith("SWITCH")) {
// value = [x, y, expanded]
mii.eyebrows.color = reverseGetLoc(lookupTables.grids.colors, value[0], value[1]);
} else {
// value = [x, y]
var grid = type === "WII" ? lookupTables.instrGrids.fourBy : lookupTables.instrGrids.twoBy;
mii.eyebrows.color = reverseGetLoc(grid, value[0], value[1]);
}
break;
// Nose
case "nose.type":
switch (type) {
case ConsoleFormats.DS:
case ConsoleFormats.WII:
// value = [x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[0], value[1]);
mii.nose.type = gridIndex === null ? null : lookupTables.pages.noses[0][gridIndex];
break;
case ConsoleFormats["3DS"]:
case ConsoleFormats.WIIU:
// value = [page, x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[1], value[2]);
mii.nose.type = reverseGetLoc(lookupTables.pages.noses, gridIndex, value[0]);
break;
case ConsoleFormats.SWITCH:
case ConsoleFormats.SWITCH2:
// value = [x, y]
mii.nose.type = reverseGetLoc(lookupTables.grids.noses, value[0], value[1]);
break;
}
break;
// Mouth
case "mouth.type":
if (type.startsWith("SWITCH")) {
// value = [x, y]
mii.mouth.type = reverseGetLoc(lookupTables.grids.mouths, value[0], value[1]);
} else {
// value = [page, x, y]
var gridIndex = reverseGetLoc(lookupTables.instrGrids.threeBy, value[1], value[2]);
mii.mouth.type = reverseGetLoc(lookupTables.pages.mouths, gridIndex, value[0]);
}
break;
case "mouth.color":
if (type.endsWith("DS") || type === "WII") {
// value = [x] or [y]
mii.mouth.color = value[0];
} else if (type.startsWith("SWITCH")) {
// value = [x, y, expanded]
mii.mouth.color = reverseGetLoc(lookupTables.grids.colors, value[0], value[1]);
} else {
// value = [x, y]
mii.mouth.color = reverseGetLoc(lookupTables.instrGrids.twoBy, value[0], value[1]);
}
break;
// Beard
case "beard.mustache.type":
// value = [x, y]
var grid = type.startsWith("SWITCH") ? lookupTables.instrGrids.threeBy : lookupTables.instrGrids.twoBy;
mii.beard.mustache.type = reverseGetLoc(grid, value[0], value[1]);
break;
case "beard.type":
// value = [x, y]
var grid = type.startsWith("SWITCH") ? lookupTables.instrGrids.threeBy : lookupTables.instrGrids.twoBy;
mii.beard.type = reverseGetLoc(grid, value[0], value[1]);
break;
case "beard.color":
if (type.endsWith("DS")) {
// value = [y]
mii.beard.color = value[0];
} else if (type.startsWith("SWITCH")) {
// value = [x, y, expanded]
mii.beard.color = reverseGetLoc(lookupTables.grids.colors, value[0], value[1]);
} else {
// value = [x, y]
var grid = type === "WII" ? lookupTables.instrGrids.fourBy : lookupTables.instrGrids.twoBy;
mii.beard.color = reverseGetLoc(grid, value[0], value[1]);
}
break;
// Glasses
case "glasses.type":
// value = [x, y]
var grid = type.startsWith("SWITCH") ? lookupTables.grids.glasses : lookupTables.instrGrids.threeBy;
mii.glasses.type = reverseGetLoc(grid, value[0], value[1]);
break;
case "glasses.color":
if (type.endsWith("DS")) {
// value = [y]
mii.glasses.color = value[0];
} else if (type.startsWith("SWITCH")) {
// value = [x, y, expanded]
mii.glasses.color = reverseGetLoc(lookupTables.grids.colors, value[0], value[1]);
} else {
// value = [x, y]
var grid = type === "WII" ? lookupTables.instrGrids.threeBy : lookupTables.instrGrids.twoBy;
mii.glasses.color = reverseGetLoc(grid, value[0], value[1]);
}
break;
}
return mii;
}
//Build instructions from the input, and the default.
function buildInstructions(mii, type) {
return {
"meta": {
"name": `Set the name to "${mii.meta.name}".`,
"creatorName": `The name of this Mii's creator was set to "${mii.meta.creatorName != undefined ? mii.meta.creatorName : ''}".`
},
"general": {
"gender": `Set the ${type === "SWITCH2" ? "style" : "gender"} to "${mii.general.gender == 0 ? "Male" : "Female"}".`,
"birthMonth": `Set the birth month to ${mii.general.birthMonth != undefined ? mii.general.birthMonth : 0}.`,
"birthday": `Set the birthday to ${mii.general.birthday != undefined ? mii.general.birthday : 0}.`,
"favoriteColor": `Set the favorite color to ${lookupTables.favoriteColors[mii.general.favoriteColor].toLowerCase()}.`,
"height": `Set the height to ${Math.round((mii.general.height / 127) * 100)}%.`,
"weight": `Set the weight to ${Math.round((mii.general.weight / 127) * 100)}%.`
},
"face": {
"color": `Set the face/skin color to the one ${formatRowPos(getAs(mii, type, "face.color"), faceColorRowNames(type))}.`,
"type": `Set the face type to the one ${formatRowPos(getAs(mii, type, "face.type"))}.`,
"makeup": `Set the makeup to the one ${formatRowPos(getAs(mii, type, "face.makeup"))}.`,
"feature": `Set the facial features/wrinkles to the one ${formatRowPos(getAs(mii, type, "face.feature"))}.`
},
"hair": {
"type": `Set the hair style to the one ${formatGridPos(getAs(mii, type, "hair.type"))}.`,
"flipped": mii.hair.flipped ? `Press the ${type === "SWITCH2" ? "Y " : ""}button to flip the hair.` : ``,
"color": `Set the hair color to the one ${formatColorPos(getAs(mii, type, "hair.color"))}.`
},
"eyes": {
"type": `Set the eyes to the one ${formatGridPos(getAs(mii, type, "eyes.type"))}.`,
"color": `Set the eye color to the one ${formatColorPos(getAs(mii, type, "eyes.color"))}.`,
"size": `Make the eyes ${mii.eyes.size < 4 ? `${4 - mii.eyes.size} smaller` : `${mii.eyes.size - 4} larger`}.`,
"squash": `${mii.eyes.squash < 3 ? `S` : `Uns`}quash the eyes ${mii.eyes.squash < 3 ? 3 - mii.eyes.squash : mii.eyes.squash - 3} ${type.startsWith("SWITCH") ? `to the ${mii.eyes.squash < 3 ? "left" : "right"}` : `times`}.`,
"rotation": `Rotate the outer edge of the eyes all the way down, then rotate it ${mii.eyes.rotation} ticks back up.`,
"distanceApart": `Move the eyes ${mii.eyes.distanceApart < 2 ? `${2 - mii.eyes.distanceApart} closer` : `${mii.eyes.distanceApart - 2} further apart`}.`,
"yPosition": `Move the eyes ${mii.eyes.yPosition < 12 ? `${12 - mii.eyes.yPosition} ticks up.` : `${mii.eyes.yPosition - 12} ticks down.`}`
},
"eyebrows": {
"type": `Set the eyebrows to the one ${formatGridPos(getAs(mii, type, "eyebrows.type"))}.`,
"color": `Set the eyebrows color to the one ${formatColorPos(getAs(mii, type, "eyebrows.color"))}.`,
"size": `Make the eyebrows ${mii.eyebrows.size < 4 ? `${4 - mii.eyebrows.size} smaller` : `${mii.eyebrows.size - 4} larger`}.`,
"squash": `${mii.eyebrows.squash < 3 ? `S` : `Uns`}quash the eyebrows ${mii.eyebrows.squash < 3 ? 3 - mii.eyebrows.squash : mii.eyebrows.squash - 3} ${type.startsWith("SWITCH") ? `to the ${mii.eyebrows.squash < 3 ? "left" : "right"}` : `times`}.`,
"rotation": `Rotate the outer edge of the eyebrows ${mii.eyebrows.rotation < 6 ? 6 - mii.eyebrows.rotation : mii.eyebrows.rotation - 6} ticks ${mii.eyebrows.rotation < 6 ? "down" : "up"}.`,
"distanceApart": `Move the eyebrows ${mii.eyebrows.distanceApart < 2 ? `${2 - mii.eyebrows.distanceApart} closer` : `${mii.eyebrows.distanceApart - 2} further apart`}.`,
"yPosition": `Move the eyebrows ${mii.eyebrows.yPosition < 7 ? `${7 - mii.eyebrows.yPosition} ticks up.` : `${mii.eyebrows.yPosition - 7} ticks down.`}`
},
"nose": {
"type": `Set the nose to the one ${formatGridPos(getAs(mii, type, "nose.type"))}.`,
"size": `Make the nose ${mii.nose.size < 4 ? `${4 - mii.nose.size} smaller` : `${mii.nose.size - 4} larger.`}`,
"yPosition": `Move the nose ${mii.nose.yPosition < 9 ? `${9 - mii.nose.yPosition} ticks up.` : `${mii.nose.yPosition - 9} ticks down.`}`
},
"mouth": {
"type": `Set the mouth to the one ${formatGridPos(getAs(mii, type, "mouth.type"))}.`,
"color": `Set the mouth color to the one ${formatColorPos(getAs(mii, type, "mouth.color"))}.`,
"size": `Make the mouth ${mii.mouth.size < 4 ? `${4 - mii.mouth.size} smaller` : `${mii.mouth.size - 4} larger.`}`,
"squash": `${mii.mouth.squash < 3 ? `S` : `Uns`}quash the mouth ${mii.mouth.squash < 3 ? 3 - mii.mouth.squash : mii.mouth.squash - 3} ${type === "SWITCH" ? `to the ${mii.mouth.squash < 3 ? "left" : "right"}` : `times`}.`,
"yPosition": `Move the mouth ${mii.mouth.yPosition < 13 ? `${13 - mii.mouth.yPosition} ticks up.` : `${mii.mouth.yPosition - 13} ticks down.`}`
},
"beard": {
"mustache": {
"type": `Set the mustache type to the one ${formatGridPos(getAs(mii, type, "beard.mustache.type"))}.`,
"size": `Make the mustache ${mii.beard.mustache.size < 4 ? `${4 - mii.beard.mustache.size} smaller` : `${mii.beard.mustache.size - 4} larger.`}`,
"yPosition": `Move the mustache ${mii.beard.mustache.yPosition < 10 ? `${10 - mii.beard.mustache.yPosition} ticks up.` : `${mii.beard.mustache.yPosition - 10} ticks down.`}`
},
"type": `Set the beard type to the one ${formatGridPos(getAs(mii, type, "beard.type"))}.`,
"color": `Set the beard color to the one ${formatColorPos(getAs(mii, type, "beard.color"))}.`
},
"glasses": {
"type": `Set the glasses type to the one ${formatGridPos(getAs(mii, type, "glasses.type"))}.`,
"color": `Set the glasses color to the one ${formatColorPos(getAs(mii, type, "glasses.color"))}.`,
"size": `Make the glasses ${mii.glasses.size < 4 ? `${4 - mii.glasses.size} smaller` : `${mii.glasses.size - 4} larger.`}`,
"yPosition": `Move the glasses ${mii.glasses.yPosition < 10 ? `${10 - mii.glasses.yPosition} ticks up.` : `${mii.glasses.yPosition - 10} ticks down.`}`
},
"mole": {
"on": `Turn the mole ${mii.mole.on ? "on" : "off"}.`,
"size": `Make the mole ${mii.mole.size < 4 ? `${4 - mii.mole.size} smaller` : `${mii.mole.size - 4} larger.`}`,
"xPosition": `Move the mole ${mii.mole.xPosition < 2 ? `${2 - mii.mole.xPosition} ticks to the left.` : `${mii.mole.xPosition - 2} ticks to the right.`}`,
"yPosition": `Move the mole ${mii.mole.yPosition < 20 ? `${20 - mii.mole.yPosition} ticks up.` : `${mii.mole.yPosition - 20} ticks down.`}`
}
};
}
//This is the entry function that returns the instructions
function makeInstructions(mii, type = "SWITCH") {
type = type.toUpperCase().replaceAll(" ", "");
if (!ConsoleFormats.hasOwnProperty(type)) {
throw new Error(`${type} is not a valid type, expected one of ${Object.keys(ConsoleFormats).join(", ")}.`);
}
mii = decodeMii(mii);
if (mii && typeof mii.then === "function") {
return mii.then(decoded => makeInstructions(decoded, type));
}
if (type !== "SWITCH") {
mii = backPort(mii, type);
}
Object.keys(mappings).forEach(def=>{
if(getNestedValue(mii,mappings[def])==undefined){
if(defaultMappings.hasOwnProperty(def)){
setNestedValue(mii,mappings[def],defaultMappings[def]);
}
else{
setNestedValue(mii,mappings[def],0);
}
}
});
var instrs = buildInstructions(mii, type);
Object.keys(mappings).forEach(key => {
const val = getNestedValue(mii, mappings[key]);
const def = getDefaultFor(key, val);
if (val === undefined || val === null || (defaultsEqual(val, def) && !ignoredInstructionDefaults.includes(key))) {
deleteNestedValue(instrs, mappings[key]);
}
});
return instrs;
}
export {
makeInstructions,
getAs,
setAs
}