Skip to content

Commit

Permalink
add animation config support
Browse files Browse the repository at this point in the history
still need some work to make the reflect stuff work for KitOrItems
  • Loading branch information
abextm committed Oct 23, 2024
1 parent 10ac082 commit 7e733c6
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 5 deletions.
14 changes: 13 additions & 1 deletion cache2-ts/src/Reader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CacheVersion } from "./Cache.js";
import { ParamID, Params } from "./types.js";
import { ItemID, KitID, KitOrItem, ParamID, Params } from "./types.js";

export const cp1252CharMap: string[] = (() => {
const ext = "€?‚ƒ„…†‡ˆ‰Š‹Œ?Ž??‘’“”•–—˜™š›œ?žŸ";
Expand Down Expand Up @@ -164,6 +164,18 @@ export class Reader {

return out;
}
public kit(): KitOrItem {
let id = this.u16();
if (id === 0) {
return undefined;
} else if (id >= 512) {
return { item: (id - 512) as ItemID };
} else if (id >= 256) {
return { kit: (id - 256) as KitID };
} else {
throw new Error(`invalid KitOrItem ${id}`);
}
}
public u32o16(): number { // rl BigSmart
if (this.view.getUint8(this.offset) & 0x80) {
return this.u16();
Expand Down
206 changes: 206 additions & 0 deletions cache2-ts/src/loaders/Animation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { PerFileLoadable } from "../Loadable.js";
import { Reader } from "../Reader.js";
import { Typed } from "../reflect.js";
import {
AnimationID,
AnimMayaID,
AnimMoveMode,
AnimRestartMode,
KitOrItem,
PoseID,
SkeletonID,
SoundEffectID,
} from "../types.js";

export class FrameSound {
constructor(
public id: SoundEffectID,
public weight: number,
public loops: number,
location: number,
public retain: number,
) {
this.offsetX = (location >> 16) & 0xFF;
this.offsetY = (location >> 8) & 0xFF;
this.maxDistance = location & 0xFF;
this.isAreaSound = location === 0;
}

public offsetX: number;
public offsetY: number;
public isAreaSound: boolean;
public maxDistance: number;
}

export class Animation extends PerFileLoadable {
constructor(public id: AnimationID) {
super();
}

declare public [Typed.type]: Typed.Any;

public static readonly index = 2;
public static readonly archive = 12;

public frameLengths?: number[] = undefined;
public frameIDs?: [SkeletonID, PoseID][] = undefined;
public chatheadFrameIDs?: [SkeletonID, PoseID][] = undefined;

public animMayaID?: AnimMayaID = undefined;
public animMayaStart = 0;
public animMayaEnd = 0;
public masks?: boolean[] = undefined;

public frameStep = -1;
public interleaveLeave?: number[] = undefined;
public stretches = true;
public priority = 5;
public leftHandItem?: KitOrItem = undefined;
public rightHandItem?: KitOrItem = undefined;
public maxLoops?: number = undefined;
public preAnimMove!: AnimMoveMode;
public postAnimMove!: AnimMoveMode;
public restartMode = AnimRestartMode.ResetLoops;
public sounds: Map<number, FrameSound[]> = new Map();

public static decode(r: Reader, id: AnimationID): Animation {
const v = new Animation(id);
let [legacyFrameSounds, animMayaID, frameSounds, animMayaBounds] = r.isAfter({ era: "osrs", indexRevision: 4470 })
? [-1, 13, 14, 15]
: [13, 14, 15, 16];
for (let opcode: number; (opcode = r.u8()) != 0;) {
switch (opcode) {
case 1: {
let len = r.u16();
v.frameLengths = new Array(len);
v.frameIDs = new Array(len);
for (let i = 0; i < len; i++) {
v.frameLengths[i] = r.u16();
}
for (let i = 0; i < len; i++) {
v.frameIDs[i] = [0 as SkeletonID, r.u16() as PoseID];
}
for (let i = 0; i < len; i++) {
v.frameIDs[i][0] = r.u16() as SkeletonID;
}
break;
}
case 2:
v.frameStep = r.u16();
break;
case 3: {
let len = r.u8();
v.interleaveLeave = new Array(len + 1);
for (let i = 0; i < len; i++) {
v.interleaveLeave[i] = r.u8();
}
v.interleaveLeave[len] = 9999999;
break;
}
case 4:
v.stretches = true;
break;
case 5:
v.priority = r.u8();
break;
case 6:
v.leftHandItem = r.kit();
break;
case 7:
v.rightHandItem = r.kit();
break;
case 8:
v.maxLoops = r.u8();
break;
case 9:
v.preAnimMove = r.u8() as AnimMoveMode;
break;
case 10:
v.postAnimMove = r.u8() as AnimMoveMode;
break;
case 11:
v.restartMode = r.u8() as AnimRestartMode;
break;
case 12: {
let len = r.u8();
v.chatheadFrameIDs = new Array(len);
for (let i = 0; i < len; i++) {
v.chatheadFrameIDs[i] = [0 as SkeletonID, r.u16() as PoseID];
}
for (let i = 0; i < len; i++) {
v.chatheadFrameIDs[i][0] = r.u16() as SkeletonID;
}
break;
}
case legacyFrameSounds: {
let len = r.u8();
for (let i = 0; i < len; i++) {
readFrameSound(v, r, i);
}
break;
}
case animMayaID:
v.animMayaID = r.i32() as AnimMayaID;
break;
case frameSounds: {
let len = r.u16();
for (let i = 0; i < len; i++) {
let frame = r.u16();
readFrameSound(v, r, frame);
}
break;
}
case animMayaBounds:
v.animMayaStart = r.u16();
v.animMayaEnd = r.u16();
break;
case 17: {
v.masks = new Array(256);
v.masks.fill(false);
let len = r.u8();
for (let i = 0; i < len; i++) {
v.masks[r.u8()] = true;
}
break;
}
default:
throw new Error(`unknown animation opcode ${opcode}`);
}
}

let defaultAnimMode = v.interleaveLeave === undefined && v.masks == undefined
? 0 as AnimMoveMode
: 2 as AnimMoveMode;

v.preAnimMove ??= defaultAnimMode;
v.postAnimMove ??= defaultAnimMode;

return v;
}
}

function readFrameSound(v: Animation, r: Reader, frame: number): void {
let sound: FrameSound;
if (r.isAfter({ era: "osrs", indexRevision: 4106 })) {
let id = r.u16();
let weight = r.isAfter({ era: "osrs", indexRevision: 4470 })
? r.u8()
: -1;
let loops = r.u8();
let location = r.u8();
let retain = r.u8();
sound = new FrameSound(id as SoundEffectID, weight, loops, location, retain);
} else {
let bits = r.u24();
sound = new FrameSound((bits >> 8) as SoundEffectID, -1, (bits >> 4) & 7, bits & 15, 0);
}

if (sound.id >= 1 && sound.loops >= 1) {
let list = v.sounds.get(frame);
if (!list) {
list = [];
v.sounds.set(frame, list);
}
list.push(sound);
}
}
1 change: 1 addition & 0 deletions cache2-ts/src/loaders/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./Animation.js";
export * from "./DBRow.js";
export * from "./Enum.js";
export * from "./HealthBar.js";
Expand Down
21 changes: 21 additions & 0 deletions cache2-ts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ export type FontID = NewType<number, "FontID">;
export type HealthBarID = NewType<number, "HealthBaID">;
export type HitsplatID = NewType<number, "HitsplatID">;
export type ItemID = NewType<number, "ItemID">;
export type KitID = NewType<number, "KitID">;
export type MapElementID = NewType<number, "MapElementID">;
export type MapSceneIconID = NewType<number, "MapSceneIconID">;
export type ModelID = NewType<number, "ModelID">;
export type NPCID = NewType<number, "NPCID">;
export type ObjID = NewType<number, "ObjID">;
export type ParamID = NewType<number, "ParamID">;
export type PoseID = NewType<number, "PoseID">;
export type SkeletonID = NewType<number, "SkeletonID">;
export type SoundEffectID = NewType<number, "SoundEffectID">;
export type SpriteID = NewType<number, "SpriteID">;
export type StructID = NewType<number, "StructID">;
Expand All @@ -68,9 +71,15 @@ export type RGB = AliasType<number, "RGB">;
export type WorldPoint = NewType<number, "WorldPoint">;
export type ObjType = NewType<number, "ObjType">;

export type AnimMoveMode = NewType<number, "AnimMoveMode">;
export type AnimRestartMode = NewType<number, "AnimRestartMode">;
export type AnimMayaID = NewType<number, "AnimMayaID">;

export class Params extends Map<ParamID, string | number> {
}

export type KitOrItem = { kit: KitID; } | { item: ItemID; } | undefined;

function makeByID<T extends number>(): (this: object, id: T) => string | undefined {
let byID: string[] | undefined;
return function(id: T) {
Expand Down Expand Up @@ -133,6 +142,18 @@ export namespace ObjType {
export const byID = makeByID<ObjType>();
}

export namespace AnimMoveMode {
export const byID = makeByID<AnimMoveMode>();
}

export namespace AnimRestartMode {
export const Continue = 0 as AnimRestartMode;
export const Restart = 1 as AnimRestartMode;
export const ResetLoops = 2 as AnimRestartMode;

export const byID = makeByID<AnimRestartMode>();
}

export namespace DBColumnID {
export function pack(table: DBTableID, column: number, tupleIndex: number = 0): DBColumnID {
return ((table << 12) | ((column & 0xFF) << 4) | (tupleIndex & 0xF)) as DBColumnID;
Expand Down
2 changes: 2 additions & 0 deletions viewer/src/common/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ServiceClient } from "./ServiceClient";
import { UIData } from "./uiobject";

export const lookupTypes = {
AnimationID: "animation",
DBRowID: "dbrow",
DBTableID: "dbtable",
EnumID: "enum",
Expand All @@ -21,6 +22,7 @@ export const lookupTypes = {
UnderlayID: "underlay",
} as const;
export type LookupType =
| "animation"
| "dbrow"
| "dbtable"
| "enum"
Expand Down
9 changes: 5 additions & 4 deletions viewer/src/main/viewer/Viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@
<div slot=1>
<ViewType name="index" bind:key={$key} index={255}>All Indexes</ViewType>
<Section name="Config" index={2}>
<ViewType name="underlay" bind:key={$key} index={1}/>
<ViewType name="obj" bind:key={$key} index={6}/>
<ViewType name="animation" bind:key={$key} index={12}/>
<ViewType name="dbrow" bind:key={$key} index={38}/>
<ViewType name="dbtable" bind:key={$key} index={39}/>
<ViewType name="enum" bind:key={$key} index={8}/>
<ViewType name="item" bind:key={$key} index={10}/>
<ViewType name="healthbar" bind:key={$key} index={33}/>
<ViewType name="hitsplat" bind:key={$key} index={32}/>
<ViewType name="npc" bind:key={$key} index={9}/>
<ViewType name="obj" bind:key={$key} index={6}/>
<ViewType name="param" bind:key={$key} index={11}/>
<ViewType name="struct" bind:key={$key} index={34}/>
<ViewType name="dbrow" bind:key={$key} index={38}/>
<ViewType name="dbtable" bind:key={$key} index={39}/>
<ViewType name="underlay" bind:key={$key} index={1}/>
</Section>
<ViewType name="sprite" bind:key={$key} index={8}>Sprites</ViewType>
</div>
Expand Down
1 change: 1 addition & 0 deletions viewer/src/runner/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ interface Filterable<T> {

let types: Record<LookupType, Filterable<unknown>> = {
index,
animation: c2.Animation,
dbrow: c2.DBRow,
dbtable: c2.DBTable,
enum: c2.Enum,
Expand Down

0 comments on commit 7e733c6

Please sign in to comment.