diff --git a/msagent.js/core/src/structs/animation.ts b/msagent.js/core/src/structs/animation.ts index e5430a0..5a7705e 100644 --- a/msagent.js/core/src/structs/animation.ts +++ b/msagent.js/core/src/structs/animation.ts @@ -92,7 +92,10 @@ export class AcsAnimationFrameInfo { soundIndex = 0; frameDuration = 0; // The duration of the frame in (1/100)th seconds. - nextFrame = 0; // -2 = animation has ended (although, I imagine this could be detected in better ways!) + + // Index of frame to go to when exiting a branch + // -2 = animation has ended/no exit? idk (although, I imagine this could be detected in better ways!) + branchExitFrameIndex = 0; branchInfo: AcsBranchInfo[] = []; overlayInfo: AcsOverlayInfo[] = []; @@ -106,7 +109,7 @@ export class AcsAnimationFrameInfo { info.soundIndex = buffer.readS16LE(); info.frameDuration = buffer.readU16LE(); - info.nextFrame = buffer.readS16LE(); + info.branchExitFrameIndex = buffer.readS16LE(); info.branchInfo = buffer.readCountedList(() => { return AcsBranchInfo.read(buffer); diff --git a/msagent.js/web/src/agent.ts b/msagent.js/web/src/agent.ts index 29c727f..d023df2 100644 --- a/msagent.js/web/src/agent.ts +++ b/msagent.js/web/src/agent.ts @@ -1,4 +1,4 @@ -import { BufferStream, SeekDir, imageDrawToBuffer } from '@msagent.js/core'; +import { AcsBranchInfo, BufferStream, SeekDir, imageDrawToBuffer } from '@msagent.js/core'; import { AcsData } from '@msagent.js/core'; import { ContextMenu, ContextMenuItem } from './contextmenu.js'; import { AcsAnimation, AcsAnimationFrameInfo } from '@msagent.js/core'; @@ -6,8 +6,6 @@ import { AcsImageEntry } from '@msagent.js/core'; import { Point, Size } from '@msagent.js/core'; import { wordballoonDrawImage, wordballoonDrawText } from './wordballoon.js'; - - function randint(min: number, max: number) { return Math.floor(Math.random() * (max - min) + min); } @@ -42,6 +40,51 @@ class AgentAnimationState { nextFrame() { requestAnimationFrame(() => { if (this.cancelled) return; + + // Handle animation branching, if it is required + let bi = this.anim.frameInfo[this.frameIndex].branchInfo; + if (bi.length != 0) { + let biCopy = [...bi]; + + // This happens more often then you'd think, but this basically handles + // a branch that will always be taken. + // + // This is often used for looping from my understanding? + if (bi.length == 1 && bi[0].branchFrameProbability == 100) { + this.frameIndex = bi[0].branchFrameIndex; + } else { + let probabilityOnlyList = bi.map((bii) => bii.branchFrameProbability); + let totalProbability = probabilityOnlyList.reduce((sum, pro) => { + return sum + pro; + }); + + // Handles the off chance that there is a branch info list that sums less than 100%. + // (Office Logo 'Idle3', Victor has a couple, ...) + // + // I'm not entirely sure the correct action in this case but I just + // have this do nothing. + if (totalProbability != 100) { + let nothingBranchItem = new AcsBranchInfo(); + nothingBranchItem.branchFrameIndex = this.frameIndex; + nothingBranchItem.branchFrameProbability = 100 - totalProbability; + biCopy.push(nothingBranchItem); + } + + // Pick a random branch + let randProbability = randint(0, 100); + let cumulativeProbability = 0; + + for (const branchItem of biCopy) { + cumulativeProbability += branchItem.branchFrameProbability; + if (randProbability < cumulativeProbability) { + //console.log('picked', branchItem); + this.frameIndex = branchItem.branchFrameIndex; + break; + } + } + } + } + this.char.drawAnimationFrame(this.anim.frameInfo[this.frameIndex++]); if (this.frameIndex >= this.anim.frameInfo.length) {