phantasmal-world/src/data_formats/parsing/quest/index.ts

864 lines
30 KiB
TypeScript

import Logger from "js-logger";
import {
Instruction,
InstructionSegment,
Segment,
SegmentType,
} from "../../../scripting/instructions";
import { Opcode } from "../../../scripting/opcodes";
import { prs_compress } from "../../compression/prs/compress";
import { prs_decompress } from "../../compression/prs/decompress";
import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor";
import { Cursor } from "../../cursor/Cursor";
import { ResizableBufferCursor } from "../../cursor/ResizableBufferCursor";
import { Endianness } from "../../Endianness";
import { Vec3 } from "../../vector";
import { AreaVariant, get_area_variant } from "./areas";
import { BinFile, parse_bin, write_bin } from "./bin";
import { DatFile, DatNpc, DatObject, DatUnknown, parse_dat, write_dat } from "./dat";
import { QuestNpc, QuestObject } from "./entities";
import { Episode } from "./Episode";
import { object_data, pso_id_to_object_type } from "./object_types";
import { parse_qst, QstContainedFile, write_qst } from "./qst";
import { NpcType } from "./npc_types";
const logger = Logger.get("data_formats/parsing/quest");
export type Quest = {
readonly id: number;
readonly language: number;
readonly name: string;
readonly short_description: string;
readonly long_description: string;
readonly episode: Episode;
readonly objects: QuestObject[];
readonly npcs: QuestNpc[];
/**
* (Partial) raw DAT data that can't be parsed yet by Phantasmal.
*/
readonly dat_unknowns: DatUnknown[];
readonly object_code: Segment[];
readonly shop_items: number[];
readonly area_variants: AreaVariant[];
};
/**
* High level parsing function that delegates to lower level parsing functions.
*
* Always delegates to parse_qst at the moment.
*/
export function parse_quest(cursor: Cursor, lenient: boolean = false): Quest | undefined {
const qst = parse_qst(cursor);
if (!qst) {
return;
}
let dat_file: QstContainedFile | undefined;
let bin_file: QstContainedFile | undefined;
for (const file of qst.files) {
const file_name = file.name.trim().toLowerCase();
if (file_name.endsWith(".dat")) {
dat_file = file;
} else if (file_name.endsWith(".bin")) {
bin_file = file;
}
}
if (!dat_file) {
logger.error("File contains no DAT file.");
return;
}
if (!bin_file) {
logger.error("File contains no BIN file.");
return;
}
const dat_decompressed = prs_decompress(
new ArrayBufferCursor(dat_file.data, Endianness.Little),
);
const dat = parse_dat(dat_decompressed);
const bin_decompressed = prs_decompress(
new ArrayBufferCursor(bin_file.data, Endianness.Little),
);
const bin = parse_bin(bin_decompressed, [0], lenient);
let episode = Episode.I;
let area_variants: AreaVariant[] = [];
if (bin.object_code.length) {
let label_0_segment: InstructionSegment | undefined;
for (const segment of bin.object_code) {
if (segment.type === SegmentType.Instructions && segment.labels.includes(0)) {
label_0_segment = segment;
break;
}
}
if (label_0_segment) {
episode = get_episode(label_0_segment.instructions);
area_variants = extract_area_variants(
dat,
episode,
label_0_segment.instructions,
lenient,
);
} else {
logger.warn(`No instruction for label 0 found.`);
}
} else {
logger.warn("File contains no instruction labels.");
}
return {
id: bin.quest_id,
language: bin.language,
name: bin.quest_name,
short_description: bin.short_description,
long_description: bin.long_description,
episode,
objects: parse_obj_data(dat.objs),
npcs: parse_npc_data(episode, dat.npcs),
dat_unknowns: dat.unknowns,
object_code: bin.object_code,
shop_items: bin.shop_items,
area_variants,
};
}
export function write_quest_qst(quest: Quest, file_name: string): ArrayBuffer {
const dat = write_dat({
objs: objects_to_dat_data(quest.objects),
npcs: npcs_to_dat_data(quest.npcs),
unknowns: quest.dat_unknowns,
});
const bin = write_bin(
new BinFile(
quest.id,
quest.language,
quest.name,
quest.short_description,
quest.long_description,
quest.object_code,
quest.shop_items,
),
);
const ext_start = file_name.lastIndexOf(".");
const base_file_name =
ext_start === -1 ? file_name.slice(0, 11) : file_name.slice(0, Math.min(11, ext_start));
return write_qst({
files: [
{
name: base_file_name + ".dat",
id: quest.id,
data: prs_compress(
new ResizableBufferCursor(dat, Endianness.Little),
).array_buffer(),
},
{
name: base_file_name + ".bin",
id: quest.id,
data: prs_compress(new ArrayBufferCursor(bin, Endianness.Little)).array_buffer(),
},
],
});
}
/**
* Defaults to episode I.
*/
function get_episode(func_0_instructions: Instruction[]): Episode {
const set_episode = func_0_instructions.find(
instruction => instruction.opcode === Opcode.SET_EPISODE,
);
if (set_episode) {
switch (set_episode.args[0].value) {
default:
case 0:
return Episode.I;
case 1:
return Episode.II;
case 2:
return Episode.IV;
}
} else {
logger.debug("Function 0 has no set_episode instruction.");
return Episode.I;
}
}
function extract_area_variants(
dat: DatFile,
episode: Episode,
func_0_instructions: Instruction[],
lenient: boolean,
): AreaVariant[] {
// Add area variants that have npcs or objects even if there are no BB_Map_Designate instructions for them.
const area_variants = new Map<number, number>();
for (const npc of dat.npcs) {
area_variants.set(npc.area_id, 0);
}
for (const obj of dat.objs) {
area_variants.set(obj.area_id, 0);
}
const bb_maps = func_0_instructions.filter(
instruction => instruction.opcode === Opcode.BB_MAP_DESIGNATE,
);
for (const bb_map of bb_maps) {
const area_id: number = bb_map.args[0].value;
const variant_id: number = bb_map.args[2].value;
area_variants.set(area_id, variant_id);
}
const area_variants_array: AreaVariant[] = [];
for (const [area_id, variant_id] of area_variants.entries()) {
try {
area_variants_array.push(get_area_variant(episode, area_id, variant_id));
} catch (e) {
if (lenient) {
logger.error(`Unknown area variant.`, e);
} else {
throw e;
}
}
}
// Sort by area order and then variant id.
return area_variants_array.sort((a, b) => a.area.order - b.area.order || a.id - b.id);
}
function parse_obj_data(objs: DatObject[]): QuestObject[] {
return objs.map(obj_data => {
return {
type: pso_id_to_object_type(obj_data.type_id),
area_id: obj_data.area_id,
section_id: obj_data.section_id,
position: obj_data.position,
rotation: obj_data.rotation,
scale: obj_data.scale,
unknown: obj_data.unknown,
};
});
}
function parse_npc_data(episode: number, npcs: DatNpc[]): QuestNpc[] {
return npcs.map(npc_data => {
return {
type: get_npc_type(episode, npc_data),
area_id: npc_data.area_id,
section_id: npc_data.section_id,
position: npc_data.position,
rotation: npc_data.rotation,
scale: npc_data.scale,
unknown: npc_data.unknown,
pso_type_id: npc_data.type_id,
pso_skin: npc_data.skin,
};
});
}
// TODO: detect Mothmant, St. Rappy, Hallo Rappy, Egg Rappy, Death Gunner, Bulk and Recon.
function get_npc_type(episode: number, { type_id, scale, skin, area_id }: DatNpc): NpcType {
const regular = Math.abs(scale.y - 1) > 0.00001;
switch (`${type_id}, ${skin % 3}, ${episode}`) {
case `${0x044}, 0, 1`:
return NpcType.Booma;
case `${0x044}, 1, 1`:
return NpcType.Gobooma;
case `${0x044}, 2, 1`:
return NpcType.Gigobooma;
case `${0x063}, 0, 1`:
return NpcType.EvilShark;
case `${0x063}, 1, 1`:
return NpcType.PalShark;
case `${0x063}, 2, 1`:
return NpcType.GuilShark;
case `${0x0a6}, 0, 1`:
return NpcType.Dimenian;
case `${0x0a6}, 0, 2`:
return NpcType.Dimenian2;
case `${0x0a6}, 1, 1`:
return NpcType.LaDimenian;
case `${0x0a6}, 1, 2`:
return NpcType.LaDimenian2;
case `${0x0a6}, 2, 1`:
return NpcType.SoDimenian;
case `${0x0a6}, 2, 2`:
return NpcType.SoDimenian2;
case `${0x0d6}, 0, 2`:
return NpcType.Mericarol;
case `${0x0d6}, 1, 2`:
return NpcType.Mericus;
case `${0x0d6}, 2, 2`:
return NpcType.Merikle;
case `${0x115}, 0, 4`:
return NpcType.Boota;
case `${0x115}, 1, 4`:
return NpcType.ZeBoota;
case `${0x115}, 2, 4`:
return NpcType.BaBoota;
case `${0x117}, 0, 4`:
return NpcType.Goran;
case `${0x117}, 1, 4`:
return NpcType.PyroGoran;
case `${0x117}, 2, 4`:
return NpcType.GoranDetonator;
}
switch (`${type_id}, ${skin % 2}, ${episode}`) {
case `${0x040}, 0, 1`:
return NpcType.Hildebear;
case `${0x040}, 0, 2`:
return NpcType.Hildebear2;
case `${0x040}, 1, 1`:
return NpcType.Hildeblue;
case `${0x040}, 1, 2`:
return NpcType.Hildeblue2;
case `${0x041}, 0, 1`:
return NpcType.RagRappy;
case `${0x041}, 0, 2`:
return NpcType.RagRappy2;
case `${0x041}, 0, 4`:
return NpcType.SandRappy;
case `${0x041}, 1, 1`:
return NpcType.AlRappy;
case `${0x041}, 1, 2`:
return NpcType.LoveRappy;
case `${0x041}, 1, 4`:
return NpcType.DelRappy;
case `${0x080}, 0, 1`:
return NpcType.Dubchic;
case `${0x080}, 0, 2`:
return NpcType.Dubchic2;
case `${0x080}, 1, 1`:
return NpcType.Gilchic;
case `${0x080}, 1, 2`:
return NpcType.Gilchic2;
case `${0x0d4}, 0, 2`:
return NpcType.SinowBerill;
case `${0x0d4}, 1, 2`:
return NpcType.SinowSpigell;
case `${0x0d5}, 0, 2`:
return NpcType.Merillia;
case `${0x0d5}, 1, 2`:
return NpcType.Meriltas;
case `${0x0d7}, 0, 2`:
return NpcType.UlGibbon;
case `${0x0d7}, 1, 2`:
return NpcType.ZolGibbon;
case `${0x0dd}, 0, 2`:
return NpcType.Dolmolm;
case `${0x0dd}, 1, 2`:
return NpcType.Dolmdarl;
case `${0x0e0}, 0, 2`:
return area_id > 15 ? NpcType.Epsilon : NpcType.SinowZoa;
case `${0x0e0}, 1, 2`:
return area_id > 15 ? NpcType.Epsilon : NpcType.SinowZele;
case `${0x112}, 0, 4`:
return NpcType.MerissaA;
case `${0x112}, 1, 4`:
return NpcType.MerissaAA;
case `${0x114}, 0, 4`:
return NpcType.Zu;
case `${0x114}, 1, 4`:
return NpcType.Pazuzu;
case `${0x116}, 0, 4`:
return NpcType.Dorphon;
case `${0x116}, 1, 4`:
return NpcType.DorphonEclair;
case `${0x119}, 0, 4`:
return regular ? NpcType.SaintMilion : NpcType.Kondrieu;
case `${0x119}, 1, 4`:
return regular ? NpcType.Shambertin : NpcType.Kondrieu;
}
switch (`${type_id}, ${episode}`) {
case `${0x042}, 1`:
return NpcType.Monest;
case `${0x042}, 2`:
return NpcType.Monest2;
case `${0x043}, 1`:
return regular ? NpcType.SavageWolf : NpcType.BarbarousWolf;
case `${0x043}, 2`:
return regular ? NpcType.SavageWolf2 : NpcType.BarbarousWolf2;
case `${0x060}, 1`:
return NpcType.GrassAssassin;
case `${0x060}, 2`:
return NpcType.GrassAssassin2;
case `${0x061}, 1`:
return area_id > 15 ? NpcType.DelLily : regular ? NpcType.PoisonLily : NpcType.NarLily;
case `${0x061}, 2`:
return area_id > 15
? NpcType.DelLily
: regular
? NpcType.PoisonLily2
: NpcType.NarLily2;
case `${0x062}, 1`:
return NpcType.NanoDragon;
case `${0x064}, 1`:
return regular ? NpcType.PofuillySlime : NpcType.PouillySlime;
case `${0x065}, 1`:
return NpcType.PanArms;
case `${0x065}, 2`:
return NpcType.PanArms2;
case `${0x081}, 1`:
return NpcType.Garanz;
case `${0x081}, 2`:
return NpcType.Garanz2;
case `${0x082}, 1`:
return regular ? NpcType.SinowBeat : NpcType.SinowGold;
case `${0x083}, 1`:
return NpcType.Canadine;
case `${0x084}, 1`:
return NpcType.Canane;
case `${0x085}, 1`:
return NpcType.Dubswitch;
case `${0x085}, 2`:
return NpcType.Dubswitch2;
case `${0x0a0}, 1`:
return NpcType.Delsaber;
case `${0x0a0}, 2`:
return NpcType.Delsaber2;
case `${0x0a1}, 1`:
return NpcType.ChaosSorcerer;
case `${0x0a1}, 2`:
return NpcType.ChaosSorcerer2;
case `${0x0a2}, 1`:
return NpcType.DarkGunner;
case `${0x0a4}, 1`:
return NpcType.ChaosBringer;
case `${0x0a5}, 1`:
return NpcType.DarkBelra;
case `${0x0a5}, 2`:
return NpcType.DarkBelra2;
case `${0x0a7}, 1`:
return NpcType.Bulclaw;
case `${0x0a8}, 1`:
return NpcType.Claw;
case `${0x0c0}, 1`:
return NpcType.Dragon;
case `${0x0c0}, 2`:
return NpcType.GalGryphon;
case `${0x0c1}, 1`:
return NpcType.DeRolLe;
// TODO:
// case `${0x0C2}, 1`: return NpcType.VolOptPart1;
case `${0x0c5}, 1`:
return NpcType.VolOpt;
case `${0x0c8}, 1`:
return NpcType.DarkFalz;
case `${0x0ca}, 2`:
return NpcType.OlgaFlow;
case `${0x0cb}, 2`:
return NpcType.BarbaRay;
case `${0x0cc}, 2`:
return NpcType.GolDragon;
case `${0x0d8}, 2`:
return NpcType.Gibbles;
case `${0x0d9}, 2`:
return NpcType.Gee;
case `${0x0da}, 2`:
return NpcType.GiGue;
case `${0x0db}, 2`:
return NpcType.Deldepth;
case `${0x0dc}, 2`:
return NpcType.Delbiter;
case `${0x0de}, 2`:
return NpcType.Morfos;
case `${0x0df}, 2`:
return NpcType.Recobox;
case `${0x0e1}, 2`:
return NpcType.IllGill;
case `${0x110}, 4`:
return NpcType.Astark;
case `${0x111}, 4`:
return regular ? NpcType.SatelliteLizard : NpcType.Yowie;
case `${0x113}, 4`:
return NpcType.Girtablulu;
}
switch (type_id) {
case 0x004:
return NpcType.FemaleFat;
case 0x005:
return NpcType.FemaleMacho;
case 0x007:
return NpcType.FemaleTall;
case 0x00a:
return NpcType.MaleDwarf;
case 0x00b:
return NpcType.MaleFat;
case 0x00c:
return NpcType.MaleMacho;
case 0x00d:
return NpcType.MaleOld;
case 0x019:
return NpcType.BlueSoldier;
case 0x01a:
return NpcType.RedSoldier;
case 0x01b:
return NpcType.Principal;
case 0x01c:
return NpcType.Tekker;
case 0x01d:
return NpcType.GuildLady;
case 0x01e:
return NpcType.Scientist;
case 0x01f:
return NpcType.Nurse;
case 0x020:
return NpcType.Irene;
case 0x0f1:
return NpcType.ItemShop;
case 0x0fe:
return NpcType.Nurse2;
}
return NpcType.Unknown;
}
function objects_to_dat_data(objects: QuestObject[]): DatObject[] {
return objects.map(object => ({
type_id: object_data(object.type).pso_id!,
section_id: object.section_id,
position: object.position,
rotation: object.rotation,
scale: object.scale,
area_id: object.area_id,
unknown: object.unknown,
}));
}
function npcs_to_dat_data(npcs: QuestNpc[]): DatNpc[] {
const dv = new DataView(new ArrayBuffer(4));
return npcs.map(npc => {
const type_data = npc_type_to_dat_data(npc.type) || {
type_id: npc.pso_type_id,
skin: npc.pso_skin,
regular: true,
};
dv.setFloat32(0, npc.scale.y);
dv.setUint32(0, (dv.getUint32(0) & ~0x800000) | (type_data.regular ? 0 : 0x800000));
const scale_y = dv.getFloat32(0);
let scale = new Vec3(npc.scale.x, scale_y, npc.scale.z);
return {
type_id: type_data.type_id,
section_id: npc.section_id,
position: npc.position,
rotation: npc.rotation,
scale,
skin: type_data.skin,
area_id: npc.area_id,
unknown: npc.unknown,
};
});
}
function npc_type_to_dat_data(
type: NpcType,
): { type_id: number; skin: number; regular: boolean } | undefined {
switch (type) {
default:
throw new Error(`Unexpected type ${NpcType[type]}.`);
case NpcType.Unknown:
return undefined;
case NpcType.FemaleFat:
return { type_id: 0x004, skin: 0, regular: true };
case NpcType.FemaleMacho:
return { type_id: 0x005, skin: 0, regular: true };
case NpcType.FemaleTall:
return { type_id: 0x007, skin: 0, regular: true };
case NpcType.MaleDwarf:
return { type_id: 0x00a, skin: 0, regular: true };
case NpcType.MaleFat:
return { type_id: 0x00b, skin: 0, regular: true };
case NpcType.MaleMacho:
return { type_id: 0x00c, skin: 0, regular: true };
case NpcType.MaleOld:
return { type_id: 0x00d, skin: 0, regular: true };
case NpcType.BlueSoldier:
return { type_id: 0x019, skin: 0, regular: true };
case NpcType.RedSoldier:
return { type_id: 0x01a, skin: 0, regular: true };
case NpcType.Principal:
return { type_id: 0x01b, skin: 0, regular: true };
case NpcType.Tekker:
return { type_id: 0x01c, skin: 0, regular: true };
case NpcType.GuildLady:
return { type_id: 0x01d, skin: 0, regular: true };
case NpcType.Scientist:
return { type_id: 0x01e, skin: 0, regular: true };
case NpcType.Nurse:
return { type_id: 0x01f, skin: 0, regular: true };
case NpcType.Irene:
return { type_id: 0x020, skin: 0, regular: true };
case NpcType.ItemShop:
return { type_id: 0x0f1, skin: 0, regular: true };
case NpcType.Nurse2:
return { type_id: 0x0fe, skin: 0, regular: true };
case NpcType.Hildebear:
return { type_id: 0x040, skin: 0, regular: true };
case NpcType.Hildeblue:
return { type_id: 0x040, skin: 1, regular: true };
case NpcType.RagRappy:
return { type_id: 0x041, skin: 0, regular: true };
case NpcType.AlRappy:
return { type_id: 0x041, skin: 1, regular: true };
case NpcType.Monest:
return { type_id: 0x042, skin: 0, regular: true };
case NpcType.SavageWolf:
return { type_id: 0x043, skin: 0, regular: true };
case NpcType.BarbarousWolf:
return { type_id: 0x043, skin: 0, regular: false };
case NpcType.Booma:
return { type_id: 0x044, skin: 0, regular: true };
case NpcType.Gobooma:
return { type_id: 0x044, skin: 1, regular: true };
case NpcType.Gigobooma:
return { type_id: 0x044, skin: 2, regular: true };
case NpcType.Dragon:
return { type_id: 0x0c0, skin: 0, regular: true };
case NpcType.GrassAssassin:
return { type_id: 0x060, skin: 0, regular: true };
case NpcType.PoisonLily:
return { type_id: 0x061, skin: 0, regular: true };
case NpcType.NarLily:
return { type_id: 0x061, skin: 1, regular: true };
case NpcType.NanoDragon:
return { type_id: 0x062, skin: 0, regular: true };
case NpcType.EvilShark:
return { type_id: 0x063, skin: 0, regular: true };
case NpcType.PalShark:
return { type_id: 0x063, skin: 1, regular: true };
case NpcType.GuilShark:
return { type_id: 0x063, skin: 2, regular: true };
case NpcType.PofuillySlime:
return { type_id: 0x064, skin: 0, regular: true };
case NpcType.PouillySlime:
return { type_id: 0x064, skin: 0, regular: false };
case NpcType.PanArms:
return { type_id: 0x065, skin: 0, regular: true };
case NpcType.DeRolLe:
return { type_id: 0x0c1, skin: 0, regular: true };
case NpcType.Dubchic:
return { type_id: 0x080, skin: 0, regular: true };
case NpcType.Gilchic:
return { type_id: 0x080, skin: 1, regular: true };
case NpcType.Garanz:
return { type_id: 0x081, skin: 0, regular: true };
case NpcType.SinowBeat:
return { type_id: 0x082, skin: 0, regular: true };
case NpcType.SinowGold:
return { type_id: 0x082, skin: 0, regular: false };
case NpcType.Canadine:
return { type_id: 0x083, skin: 0, regular: true };
case NpcType.Canane:
return { type_id: 0x084, skin: 0, regular: true };
case NpcType.Dubswitch:
return { type_id: 0x085, skin: 0, regular: true };
case NpcType.VolOpt:
return { type_id: 0x0c5, skin: 0, regular: true };
case NpcType.Delsaber:
return { type_id: 0x0a0, skin: 0, regular: true };
case NpcType.ChaosSorcerer:
return { type_id: 0x0a1, skin: 0, regular: true };
case NpcType.DarkGunner:
return { type_id: 0x0a2, skin: 0, regular: true };
case NpcType.ChaosBringer:
return { type_id: 0x0a4, skin: 0, regular: true };
case NpcType.DarkBelra:
return { type_id: 0x0a5, skin: 0, regular: true };
case NpcType.Dimenian:
return { type_id: 0x0a6, skin: 0, regular: true };
case NpcType.LaDimenian:
return { type_id: 0x0a6, skin: 1, regular: true };
case NpcType.SoDimenian:
return { type_id: 0x0a6, skin: 2, regular: true };
case NpcType.Bulclaw:
return { type_id: 0x0a7, skin: 0, regular: true };
case NpcType.Claw:
return { type_id: 0x0a8, skin: 0, regular: true };
case NpcType.DarkFalz:
return { type_id: 0x0c8, skin: 0, regular: true };
case NpcType.Hildebear2:
return { type_id: 0x040, skin: 0, regular: true };
case NpcType.Hildeblue2:
return { type_id: 0x040, skin: 1, regular: true };
case NpcType.RagRappy2:
return { type_id: 0x041, skin: 0, regular: true };
case NpcType.LoveRappy:
return { type_id: 0x041, skin: 1, regular: true };
case NpcType.Monest2:
return { type_id: 0x042, skin: 0, regular: true };
case NpcType.PoisonLily2:
return { type_id: 0x061, skin: 0, regular: true };
case NpcType.NarLily2:
return { type_id: 0x061, skin: 1, regular: true };
case NpcType.GrassAssassin2:
return { type_id: 0x060, skin: 0, regular: true };
case NpcType.Dimenian2:
return { type_id: 0x0a6, skin: 0, regular: true };
case NpcType.LaDimenian2:
return { type_id: 0x0a6, skin: 1, regular: true };
case NpcType.SoDimenian2:
return { type_id: 0x0a6, skin: 2, regular: true };
case NpcType.DarkBelra2:
return { type_id: 0x0a5, skin: 0, regular: true };
case NpcType.BarbaRay:
return { type_id: 0x0cb, skin: 0, regular: true };
case NpcType.SavageWolf2:
return { type_id: 0x043, skin: 0, regular: true };
case NpcType.BarbarousWolf2:
return { type_id: 0x043, skin: 0, regular: false };
case NpcType.PanArms2:
return { type_id: 0x065, skin: 0, regular: true };
case NpcType.Dubchic2:
return { type_id: 0x080, skin: 0, regular: true };
case NpcType.Gilchic2:
return { type_id: 0x080, skin: 1, regular: true };
case NpcType.Garanz2:
return { type_id: 0x081, skin: 0, regular: true };
case NpcType.Dubswitch2:
return { type_id: 0x085, skin: 0, regular: true };
case NpcType.Delsaber2:
return { type_id: 0x0a0, skin: 0, regular: true };
case NpcType.ChaosSorcerer2:
return { type_id: 0x0a1, skin: 0, regular: true };
case NpcType.GolDragon:
return { type_id: 0x0cc, skin: 0, regular: true };
case NpcType.SinowBerill:
return { type_id: 0x0d4, skin: 0, regular: true };
case NpcType.SinowSpigell:
return { type_id: 0x0d4, skin: 1, regular: true };
case NpcType.Merillia:
return { type_id: 0x0d5, skin: 0, regular: true };
case NpcType.Meriltas:
return { type_id: 0x0d5, skin: 1, regular: true };
case NpcType.Mericarol:
return { type_id: 0x0d6, skin: 0, regular: true };
case NpcType.Mericus:
return { type_id: 0x0d6, skin: 1, regular: true };
case NpcType.Merikle:
return { type_id: 0x0d6, skin: 2, regular: true };
case NpcType.UlGibbon:
return { type_id: 0x0d7, skin: 0, regular: true };
case NpcType.ZolGibbon:
return { type_id: 0x0d7, skin: 1, regular: true };
case NpcType.Gibbles:
return { type_id: 0x0d8, skin: 0, regular: true };
case NpcType.Gee:
return { type_id: 0x0d9, skin: 0, regular: true };
case NpcType.GiGue:
return { type_id: 0x0da, skin: 0, regular: true };
case NpcType.GalGryphon:
return { type_id: 0x0c0, skin: 0, regular: true };
case NpcType.Deldepth:
return { type_id: 0x0db, skin: 0, regular: true };
case NpcType.Delbiter:
return { type_id: 0x0dc, skin: 0, regular: true };
case NpcType.Dolmolm:
return { type_id: 0x0dd, skin: 0, regular: true };
case NpcType.Dolmdarl:
return { type_id: 0x0dd, skin: 1, regular: true };
case NpcType.Morfos:
return { type_id: 0x0de, skin: 0, regular: true };
case NpcType.Recobox:
return { type_id: 0x0df, skin: 0, regular: true };
case NpcType.Epsilon:
return { type_id: 0x0e0, skin: 0, regular: true };
case NpcType.SinowZoa:
return { type_id: 0x0e0, skin: 0, regular: true };
case NpcType.SinowZele:
return { type_id: 0x0e0, skin: 1, regular: true };
case NpcType.IllGill:
return { type_id: 0x0e1, skin: 0, regular: true };
case NpcType.DelLily:
return { type_id: 0x061, skin: 0, regular: true };
case NpcType.OlgaFlow:
return { type_id: 0x0ca, skin: 0, regular: true };
case NpcType.SandRappy:
return { type_id: 0x041, skin: 0, regular: true };
case NpcType.DelRappy:
return { type_id: 0x041, skin: 1, regular: true };
case NpcType.Astark:
return { type_id: 0x110, skin: 0, regular: true };
case NpcType.SatelliteLizard:
return { type_id: 0x111, skin: 0, regular: true };
case NpcType.Yowie:
return { type_id: 0x111, skin: 0, regular: false };
case NpcType.MerissaA:
return { type_id: 0x112, skin: 0, regular: true };
case NpcType.MerissaAA:
return { type_id: 0x112, skin: 1, regular: true };
case NpcType.Girtablulu:
return { type_id: 0x113, skin: 0, regular: true };
case NpcType.Zu:
return { type_id: 0x114, skin: 0, regular: true };
case NpcType.Pazuzu:
return { type_id: 0x114, skin: 1, regular: true };
case NpcType.Boota:
return { type_id: 0x115, skin: 0, regular: true };
case NpcType.ZeBoota:
return { type_id: 0x115, skin: 1, regular: true };
case NpcType.BaBoota:
return { type_id: 0x115, skin: 2, regular: true };
case NpcType.Dorphon:
return { type_id: 0x116, skin: 0, regular: true };
case NpcType.DorphonEclair:
return { type_id: 0x116, skin: 1, regular: true };
case NpcType.Goran:
return { type_id: 0x117, skin: 0, regular: true };
case NpcType.PyroGoran:
return { type_id: 0x117, skin: 1, regular: true };
case NpcType.GoranDetonator:
return { type_id: 0x117, skin: 2, regular: true };
case NpcType.SaintMilion:
return { type_id: 0x119, skin: 0, regular: true };
case NpcType.Shambertin:
return { type_id: 0x119, skin: 1, regular: true };
case NpcType.Kondrieu:
return { type_id: 0x119, skin: 0, regular: false };
}
}