Implement avatar limb animations
Refactor helpers to take "impl" objects rather than functions
This commit is contained in:
parent
a89c3fcec5
commit
d1fb2daa41
16
avatars.txt
16
avatars.txt
|
@ -42,6 +42,8 @@ cels_affected_by_height:
|
||||||
; an index into a table of bitmasks, states are defined as an index into
|
; an index into a table of bitmasks, states are defined as an index into
|
||||||
; the table of cels directly. only one cel is visible per-limb at a time.
|
; the table of cels directly. only one cel is visible per-limb at a time.
|
||||||
|
|
||||||
|
; 1 - padding to match prop header? always zero.
|
||||||
|
; 2 - offset of start_end table
|
||||||
; 1-2 - unknown. first byte seems to always be zero. second byte seems
|
; 1-2 - unknown. first byte seems to always be zero. second byte seems
|
||||||
; to be correlated with the number of frames or cels, but isn't a direct
|
; to be correlated with the number of frames or cels, but isn't a direct
|
||||||
; count of either.
|
; count of either.
|
||||||
|
@ -88,6 +90,14 @@ which suggests these as valid values.
|
||||||
define AV_ACT_sit_front = 0x80 + 29
|
define AV_ACT_sit_front = 0x80 + 29
|
||||||
|
|
||||||
; choreography tables:
|
; choreography tables:
|
||||||
; an array of arrays of bytes, indicating "states". if the high bit is
|
; an array of arrays of bytes, indicating "states". Each byte has three
|
||||||
; set, this signals the end of the inner array.
|
; values packed into it: ElllAAAA
|
||||||
; unclear at this time how exactly these values are interpreted.
|
; E (0x80): "end" bit - if this is set, indicates that this is the last
|
||||||
|
; byte of the array.
|
||||||
|
; l (0x70): "limb" - value from 0-6, indexing the 6 limbs. If limb is 6,
|
||||||
|
; 0x10 is added to S and limb is set to 5. Stored in the X
|
||||||
|
; register and passed to `init_avatar_chores` (chore.m:252)
|
||||||
|
; A (0x0f): "animation" - index of animation in the limb's start_end table
|
||||||
|
|
||||||
|
; I believe all limbs default to animation 0 if no alternative animation is given
|
||||||
|
; for a given chore in the choreography table.
|
18
body.html
18
body.html
|
@ -15,10 +15,13 @@
|
||||||
line-height:1.2
|
line-height:1.2
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="index.js"></script>
|
<script src="index.js?v=1"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 id="filename"></h1>
|
<h1 id="filename"></h1>
|
||||||
|
<div id="limbs">
|
||||||
|
<h2>Limbs</h2>
|
||||||
|
</div>
|
||||||
<div id="cels">
|
<div id="cels">
|
||||||
<h2>Cels</h1>
|
<h2>Cels</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,19 +40,28 @@
|
||||||
container.appendChild(textNode(JSON.stringify(prop, propFilter, 2), "pre"))
|
container.appendChild(textNode(JSON.stringify(prop, propFilter, 2), "pre"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const limbNames = ["legs", "legs2", "left arm", "torso", "face", "right arm"]
|
||||||
|
const labelLimb = (container, ilimb) => {
|
||||||
|
container.appendChild(textNode(ilimb < limbNames.length ? limbNames[ilimb] : `Limb #${ilimb}??`, "div"))
|
||||||
|
}
|
||||||
const onload = async () => {
|
const onload = async () => {
|
||||||
const q = new URLSearchParams(window.location.search)
|
const q = new URLSearchParams(window.location.search)
|
||||||
const filename = q.get("f")
|
const filename = q.get("f")
|
||||||
document.getElementById("filename").innerText = filename
|
document.getElementById("filename").innerText = filename
|
||||||
try {
|
try {
|
||||||
const body = await decodeBinary(filename, decodeBody)
|
const body = await decodeBinary(filename, BodyImpl)
|
||||||
dumpProp(body, document.getElementById("data"))
|
dumpProp(body, document.getElementById("data"))
|
||||||
if (body.error) {
|
if (body.error) {
|
||||||
showError(body.error, filename)
|
showError(body.error, filename)
|
||||||
} else {
|
} else {
|
||||||
const celContainer = document.getElementById("cels")
|
const celContainer = document.getElementById("cels")
|
||||||
for (const limb of body.limbs) {
|
const limbContainer = document.getElementById("limbs")
|
||||||
|
for (const [ilimb, limb] of body.limbs.entries()) {
|
||||||
|
labelLimb(celContainer, ilimb)
|
||||||
showCels(limb, celContainer)
|
showCels(limb, celContainer)
|
||||||
|
|
||||||
|
labelLimb(limbContainer, ilimb)
|
||||||
|
showAnimations(limb, limbContainer, LimbImpl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
line-height:1.2
|
line-height:1.2
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="index.js"></script>
|
<script src="index.js?v=1"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 id="filename"></h1>
|
<h1 id="filename"></h1>
|
||||||
|
@ -48,12 +48,12 @@
|
||||||
const filename = q.get("f")
|
const filename = q.get("f")
|
||||||
document.getElementById("filename").innerText = filename
|
document.getElementById("filename").innerText = filename
|
||||||
try {
|
try {
|
||||||
const prop = await decodeBinary(filename, decodeProp)
|
const prop = await decodeBinary(filename, PropImpl)
|
||||||
dumpProp(prop, document.getElementById("data"))
|
dumpProp(prop, document.getElementById("data"))
|
||||||
if (prop.error) {
|
if (prop.error) {
|
||||||
showError(prop.error, filename)
|
showError(prop.error, filename)
|
||||||
} else {
|
} else {
|
||||||
showAnimations(prop, document.getElementById("animations"))
|
showAnimations(prop, document.getElementById("animations"), PropImpl)
|
||||||
showStates(prop, document.getElementById("states"))
|
showStates(prop, document.getElementById("states"))
|
||||||
showCels(prop, document.getElementById("cels"))
|
showCels(prop, document.getElementById("cels"))
|
||||||
}
|
}
|
||||||
|
|
14
index.html
14
index.html
|
@ -15,18 +15,18 @@
|
||||||
line-height:1.2
|
line-height:1.2
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="index.js"></script>
|
<script src="index.js?v=1"></script>
|
||||||
<script>
|
<script>
|
||||||
function showErrors() {
|
function showErrors() {
|
||||||
document.getElementById('errors').style.display = 'block'
|
document.getElementById('errors').style.display = 'block'
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayEverything = async () => {
|
const displayEverything = async () => {
|
||||||
await displayBodyList("bodies.json", "bodies")
|
await displayList("bodies.json", "bodies", BodyImpl)
|
||||||
await displayPropList("heads.json", "heads")
|
await displayList("heads.json", "heads", PropImpl)
|
||||||
await displayPropList("props.json", "props")
|
await displayList("props.json", "props", PropImpl)
|
||||||
await displayPropList("misc.json", "misc")
|
await displayList("misc.json", "misc", PropImpl)
|
||||||
await displayPropList("beta.json", "beta")
|
await displayList("beta.json", "beta", PropImpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
displayEverything()
|
displayEverything()
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<body>
|
<body>
|
||||||
<h1>Inhabitor - The Habitat Inspector</h1>
|
<h1>Inhabitor - The Habitat Inspector</h1>
|
||||||
<p>
|
<p>
|
||||||
You are looking at a haphazardly-gathered collection of object graphics from
|
You are looking at a collection of object graphics from
|
||||||
<a href="https://frandallfarmer.github.io/neohabitat-doc/docs/">Lucasfilm Games' Habitat</a>. These images are
|
<a href="https://frandallfarmer.github.io/neohabitat-doc/docs/">Lucasfilm Games' Habitat</a>. These images are
|
||||||
generated by parsing Habitat's internal binary image / animation format in
|
generated by parsing Habitat's internal binary image / animation format in
|
||||||
JavaScript. The full <a href="https://git.information-superhighway.net/SpindleyQ/inhabitor">
|
JavaScript. The full <a href="https://git.information-superhighway.net/SpindleyQ/inhabitor">
|
||||||
|
|
163
index.js
163
index.js
|
@ -412,6 +412,32 @@ const encodeWalkto = ({ fromSide, offset }) => {
|
||||||
return encodeSide(fromSide) | (offset & 0xfc)
|
return encodeSide(fromSide) | (offset & 0xfc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const decodeAnimations = (data, startEndTableOff, firstCelOff, stateCount) => {
|
||||||
|
const animations = []
|
||||||
|
// The prop structure also does not encode a count for how many frames there are, so we simply
|
||||||
|
// stop parsing once we find one that doesn't make sense.
|
||||||
|
// We also use the heuristic that this structure always precedes the first cel, as that seems to be
|
||||||
|
// consistently be the case with all the props in the Habitat source tree. We'll stop reading
|
||||||
|
// animation data if we cross that boundary. If we encounter a prop that has the animation data
|
||||||
|
// _after_ the cel data, which would be legal but doesn't happen in practice, then we ignore this
|
||||||
|
// heuristic rather than failing to parse any animation data.
|
||||||
|
// It's possible for there to be no frames, which is represented by an offset of 0 (no_animation)
|
||||||
|
if (startEndTableOff != 0) {
|
||||||
|
for (let frameOff = startEndTableOff; (startEndTableOff > firstCelOff) || (frameOff < firstCelOff); frameOff += 2) {
|
||||||
|
// each animation is two bytes: the starting state, and the ending state
|
||||||
|
// the first byte can have its high bit set to indicate that the animation should cycle
|
||||||
|
const cycle = (data.getUint8(frameOff) & 0x80) != 0
|
||||||
|
const startState = data.getUint8(frameOff) & 0x7f
|
||||||
|
const endState = data.getUint8(frameOff + 1)
|
||||||
|
if (startState >= stateCount || endState >= stateCount) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
animations.push({ cycle: cycle, startState: startState, endState: endState })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return animations
|
||||||
|
}
|
||||||
|
|
||||||
const decodeProp = (data) => {
|
const decodeProp = (data) => {
|
||||||
const prop = {
|
const prop = {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -419,7 +445,6 @@ const decodeProp = (data) => {
|
||||||
colorBitmask: data.getUint8(1),
|
colorBitmask: data.getUint8(1),
|
||||||
containerXYOff: data.getUint8(3), // TODO: parse this when nonzero
|
containerXYOff: data.getUint8(3), // TODO: parse this when nonzero
|
||||||
walkto: { left: decodeWalkto(data.getUint8(4)), right: decodeWalkto(data.getUint8(5)), yoff: data.getInt8(6) },
|
walkto: { left: decodeWalkto(data.getUint8(4)), right: decodeWalkto(data.getUint8(5)), yoff: data.getInt8(6) },
|
||||||
animations: [],
|
|
||||||
celmasks: [],
|
celmasks: [],
|
||||||
cels: []
|
cels: []
|
||||||
}
|
}
|
||||||
|
@ -451,33 +476,11 @@ const decodeProp = (data) => {
|
||||||
prop.cels.push(decodeCel(new DataView(data.buffer, celOff), (prop.colorBitmask & celbit) != 0))
|
prop.cels.push(decodeCel(new DataView(data.buffer, celOff), (prop.colorBitmask & celbit) != 0))
|
||||||
allCelsMask = (allCelsMask << 1) & 0xff
|
allCelsMask = (allCelsMask << 1) & 0xff
|
||||||
}
|
}
|
||||||
|
prop.animations = decodeAnimations(data, graphicStateOff, firstCelOff, stateCount)
|
||||||
// The prop structure also does not encode a count for how many frames there are, so we simply
|
|
||||||
// stop parsing once we find one that doesn't make sense.
|
|
||||||
// We also use the heuristic that this structure always precedes the first cel, as that seems to be
|
|
||||||
// consistently be the case with all the props in the Habitat source tree. We'll stop reading
|
|
||||||
// animation data if we cross that boundary. If we encounter a prop that has the animation data
|
|
||||||
// _after_ the cel data, which would be legal but doesn't happen in practice, then we ignore this
|
|
||||||
// heuristic rather than failing to parse any animation data.
|
|
||||||
// It's possible for there to be no frames, which is represented by an offset of 0 (no_animation)
|
|
||||||
if (graphicStateOff != 0) {
|
|
||||||
for (let frameOff = graphicStateOff; (graphicStateOff > firstCelOff) || (frameOff < firstCelOff); frameOff += 2) {
|
|
||||||
// each animation is two bytes: the starting state, and the ending state
|
|
||||||
// the first byte can have its high bit set to indicate that the animation should cycle
|
|
||||||
const cycle = (data.getUint8(frameOff) & 0x80) != 0
|
|
||||||
const startState = data.getUint8(frameOff) & 0x7f
|
|
||||||
const endState = data.getUint8(frameOff + 1)
|
|
||||||
if (startState >= stateCount || endState >= stateCount) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
prop.animations.push({ cycle: cycle, startState: startState, endState: endState })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prop
|
return prop
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodeLimb = (data, limb) => {
|
const decodeLimb = (data, limb) => {
|
||||||
limb.unknown = [data.getUint8(1), data.getUint8(2)]
|
|
||||||
let frameCount = data.getUint8(0) + 1
|
let frameCount = data.getUint8(0) + 1
|
||||||
limb.frames = []
|
limb.frames = []
|
||||||
for (let iframe = 0; iframe < frameCount; iframe ++) {
|
for (let iframe = 0; iframe < frameCount; iframe ++) {
|
||||||
|
@ -486,11 +489,17 @@ const decodeLimb = (data, limb) => {
|
||||||
const celOffsetsOff = 3 + frameCount
|
const celOffsetsOff = 3 + frameCount
|
||||||
const maxCelIndex = Math.max(...limb.frames)
|
const maxCelIndex = Math.max(...limb.frames)
|
||||||
limb.cels = []
|
limb.cels = []
|
||||||
|
let firstCelOff
|
||||||
for (let icel = 0; icel <= maxCelIndex; icel ++) {
|
for (let icel = 0; icel <= maxCelIndex; icel ++) {
|
||||||
const celOff = data.getUint16(celOffsetsOff + (icel * 2), LE)
|
const celOff = data.getUint16(celOffsetsOff + (icel * 2), LE)
|
||||||
|
if (icel == 0) {
|
||||||
|
firstCelOff = celOff
|
||||||
|
}
|
||||||
limb.cels.push(decodeCel(new DataView(data.buffer, data.byteOffset + celOff)))
|
limb.cels.push(decodeCel(new DataView(data.buffer, data.byteOffset + celOff)))
|
||||||
}
|
}
|
||||||
|
limb.animations = decodeAnimations(data, data.getUint8(2), firstCelOff, limb.frames.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
const choreographyActions = [
|
const choreographyActions = [
|
||||||
"init", "stand", "walk", "hand_back", "sit_floor", "bend_over",
|
"init", "stand", "walk", "hand_back", "sit_floor", "bend_over",
|
||||||
"bend_back", "point", "throw", "get_shot", "jump", "punch", "wave",
|
"bend_back", "point", "throw", "get_shot", "jump", "punch", "wave",
|
||||||
|
@ -614,23 +623,43 @@ const wrapLink = (element, href) => {
|
||||||
return link
|
return link
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkDetail = (element, filename) => {
|
const PropImpl = {
|
||||||
return wrapLink(element, `detail.html?f=${filename}`)
|
decode: decodeProp,
|
||||||
|
detailHref: (filename) => `detail.html?f=${filename}`,
|
||||||
|
celsForAnimationState: (prop, istate) => celsFromMask(prop, prop.celmasks[istate]),
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkBody = (element, filename) => {
|
const BodyImpl = {
|
||||||
return wrapLink(element, `body.html?f=${filename}`)
|
decode: decodeBody,
|
||||||
|
detailHref: (filename) => `body.html?f=${filename}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const createAnimation = (prop, animation) => {
|
const LimbImpl = {
|
||||||
|
celsForAnimationState: (limb, istate) => {
|
||||||
|
const iframe = limb.frames[istate]
|
||||||
|
if (iframe >= 0) {
|
||||||
|
return [limb.cels[iframe]]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkDetail = (element, filename, impl) => {
|
||||||
|
return impl && impl.detailHref ? wrapLink(element, impl.detailHref(filename)) : element
|
||||||
|
}
|
||||||
|
|
||||||
|
const createAnimation = (animation, value, impl) => {
|
||||||
const frames = []
|
const frames = []
|
||||||
for (let istate = animation.startState; istate <= animation.endState; istate ++) {
|
for (let istate = animation.startState; istate <= animation.endState; istate ++) {
|
||||||
const frame = compositeCels(celsFromMask(prop, prop.celmasks[istate]))
|
const frame = compositeCels(impl.celsForAnimationState(value, istate))
|
||||||
if (frame != null) {
|
if (frame != null) {
|
||||||
frames.push(frame)
|
frames.push(frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (frames.length == 1) {
|
if (frames.length == 0) {
|
||||||
|
return textNode("")
|
||||||
|
} else if (frames.length == 1) {
|
||||||
return imageFromCanvas(frames[0].canvas)
|
return imageFromCanvas(frames[0].canvas)
|
||||||
}
|
}
|
||||||
let minX = Number.POSITIVE_INFINITY
|
let minX = Number.POSITIVE_INFINITY
|
||||||
|
@ -663,9 +692,9 @@ const createAnimation = (prop, animation) => {
|
||||||
return canvas
|
return canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
const showAnimations = (prop, container) => {
|
const showAnimations = (value, container, impl) => {
|
||||||
for (const animation of prop.animations) {
|
for (const animation of value.animations) {
|
||||||
container.appendChild(linkDetail(createAnimation(prop, animation), prop.filename))
|
container.appendChild(linkDetail(createAnimation(animation, value, impl), value.filename, impl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,9 +717,9 @@ const showCels = (prop, container) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodeBinary = async (filename, decoder) => {
|
const decodeBinary = async (filename, impl) => {
|
||||||
try {
|
try {
|
||||||
const prop = decoder(await readBinary(filename))
|
const prop = impl.decode(await readBinary(filename))
|
||||||
prop.filename = filename
|
prop.filename = filename
|
||||||
return prop
|
return prop
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -698,11 +727,11 @@ const decodeBinary = async (filename, decoder) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showError = (e, filename, link = (x,_) => x) => {
|
const showError = (e, filename, impl) => {
|
||||||
const container = document.getElementById("errors")
|
const container = document.getElementById("errors")
|
||||||
const errNode = document.createElement("p")
|
const errNode = document.createElement("p")
|
||||||
console.error(e)
|
console.error(e)
|
||||||
errNode.appendChild(link(textNode(filename, "b"), filename))
|
errNode.appendChild(linkDetail(textNode(filename, "b"), filename, impl))
|
||||||
errNode.appendChild(textNode(e.toString(), "p"))
|
errNode.appendChild(textNode(e.toString(), "p"))
|
||||||
if (e.stack) {
|
if (e.stack) {
|
||||||
errNode.appendChild(textNode(e.stack.toString(), "pre"))
|
errNode.appendChild(textNode(e.stack.toString(), "pre"))
|
||||||
|
@ -710,22 +739,38 @@ const showError = (e, filename, link = (x,_) => x) => {
|
||||||
container.appendChild(errNode)
|
container.appendChild(errNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayFile = async (filename, container, decode, display, link = (x,_) => x) => {
|
const displayFile = async (filename, container, impl) => {
|
||||||
const prop = await decodeBinary(filename, decode)
|
const value = await decodeBinary(filename, impl)
|
||||||
if (prop.error) {
|
if (value.error) {
|
||||||
container.parentNode.removeChild(container)
|
container.parentNode.removeChild(container)
|
||||||
showError(prop.error, prop.filename, link)
|
showError(value.error, value.filename, impl)
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
display(prop, container)
|
impl.display(value, container)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
container.parentNode.removeChild(container)
|
container.parentNode.removeChild(container)
|
||||||
showError(e, prop.filename, link)
|
showError(e, value.filename, impl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayList = async (indexFile, containerId, decode, display, link = (x,_) => x) => {
|
PropImpl.display = (prop, container) => {
|
||||||
|
if (prop.filename == 'heads/fhead.bin') {
|
||||||
|
container.appendChild(textNode("CW: Pixel genitals"))
|
||||||
|
} else if (prop.animations.length > 0) {
|
||||||
|
showAnimations(prop, container, PropImpl)
|
||||||
|
} else {
|
||||||
|
showStates(prop, container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyImpl.display = (body, container) => {
|
||||||
|
for (const limb of body.limbs) {
|
||||||
|
showCels(limb, container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayList = async (indexFile, containerId, impl) => {
|
||||||
const response = await fetch(indexFile, { cache: "no-cache" })
|
const response = await fetch(indexFile, { cache: "no-cache" })
|
||||||
const filenames = await response.json()
|
const filenames = await response.json()
|
||||||
const container = document.getElementById(containerId)
|
const container = document.getElementById(containerId)
|
||||||
|
@ -735,32 +780,8 @@ const displayList = async (indexFile, containerId, decode, display, link = (x,_)
|
||||||
fileContainer.style.margin = "2px"
|
fileContainer.style.margin = "2px"
|
||||||
fileContainer.style.padding = "2px"
|
fileContainer.style.padding = "2px"
|
||||||
fileContainer.style.display = "inline-block"
|
fileContainer.style.display = "inline-block"
|
||||||
fileContainer.appendChild(link(textNode(filename, "div"), filename))
|
fileContainer.appendChild(linkDetail(textNode(filename, "div"), filename, impl))
|
||||||
container.appendChild(fileContainer)
|
container.appendChild(fileContainer)
|
||||||
displayFile(filename, fileContainer, decode, display, link)
|
displayFile(filename, fileContainer, impl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayProp = (prop, container) => {
|
|
||||||
if (prop.filename == 'heads/fhead.bin') {
|
|
||||||
container.appendChild(textNode("CW: Pixel genitals"))
|
|
||||||
} else if (prop.animations.length > 0) {
|
|
||||||
showAnimations(prop, container)
|
|
||||||
} else {
|
|
||||||
showStates(prop, container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayBody = (body, container) => {
|
|
||||||
for (const limb of body.limbs) {
|
|
||||||
showCels(limb, container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayPropList = async (indexFile, containerId) => {
|
|
||||||
await displayList(indexFile, containerId, decodeProp, displayProp, linkDetail)
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayBodyList = async (indexFile, containerId) => {
|
|
||||||
await displayList(indexFile, containerId, decodeBody, displayBody, linkBody)
|
|
||||||
}
|
|
Loading…
Reference in a new issue