Set up basic LP algorithm to optimize hunts. Fixed errors in data.

This commit is contained in:
Daan Vanden Bosch 2019-06-04 17:01:51 +02:00
parent 734ed1016d
commit 72ecdac71d
18 changed files with 3512 additions and 2772 deletions

2407
public/box_drops.ephinea.tsv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

440
public/items.ephinea.tsv Normal file
View File

@ -0,0 +1,440 @@
name
AddSlot
Agito (1975)
Agito (1977)
Agito (1980)
Agito (1983)
Agito (1991)
Agito (2001)
Alive Aqhu
Alliance Uniform
Amplifier of Anti
Amplifier of Barta
Amplifier of Blue
Amplifier of Deband
Amplifier of Foie
Amplifier of Gibarta
Amplifier of Gifoie
Amplifier of Gizonde
Amplifier of Rabarta
Amplifier of Rafoie
Amplifier of Razonde
Amplifier of Red
Amplifier of Resta
Amplifier of Shifta
Amplifier of Yellow
Amplifier of Zonde
Ancient Saber
Angel Harp
Angel/Luck
Angel/Mind
Angry Fist
Ano Bazooka
Ano Rifle
Anti Android Rifle
Anti-Light Ring
Arms
Assist Barrier
Asteron Belt
Asuka
Attribute Plate
Attribute Wall
Aura Field
Baton
Battle Verge
Beam
Belra's Right Arm
Berdys
Berdysh
Berill Photon
Black Hound Cuirass
Black Odoshi Domaru
Black Odoshi Red Nimaidou
Blade
Blade Dance
Blaster
Bloody Art
Blue Barrier
Blue Odoshi Violet Nimaidou
Bluefull Card
Book of Hitogata
Booma's Right Arm
Branch of Pakupaku
Bravace
Brave Hammer
Brave Knuckle
Breaker
Brightness Circle
Bringer's Right Arm
Brionac
Caduceus
Calibur
Cannon
Cannon Rouge
Celestial Armor
Centurion/Ability
Chain Sawd
Christmas Present
Cladding of Epsilon
Claymore
Clio
Club of Laconium
Club of Zumiuran
Combat Gear
Commander Uniform
Congeal Cloak
Crimson Coat
Cross Scar
Crush Bullet
Cure/Confuse
Cure/Freeze
Cure/Paralysis
Cure/Poison
Cure/Shock
Cure/Slow
Cursed Cloak
Custom Barrier ver.OO
Custom Frame ver.OO
Custom Ray ver.OO
Cutter
D-Parts ver1.01
D-Parts ver2.10
D-Photon Core
DB's Armor
DB's Saber
DB's Saber (3062)
DB's Saber (3064)
DB's Saber (3067)
DB's Saber (3069 Chris)
DB's Saber (3069 Torato)
DB's Saber (3070)
DB's Saber (3073)
DB's Saber (3075)
DB's Saber (3077)
DB's Shield
DF Field
DF Shield
Daylight Scar
De Rol Le Shell
Def Material
Delsaber's Left Arm
Delsaber's Right Arm
Demolition Comet
Demonic Fork
Devil/Battle
Devil/Technique
Diska
Diska of Braveman
Diska of Liberator
Divine Protection
Double Saber
Dragon Scale
Dragon Slayer
Dragon's Claw
Dragon/HP
Dress Plate
Durandal
Earth Wand: Brownie
Easter Egg
Edge
Electro Frame
Elysion
Evade Material
Evil Curst
Fatsia
Final Impact
Fire Scepter: Agni
Flamberge
Flame Garment
Flame Visit
Flapjack Flapper
Flight Cutter
Flight Fan
Flowen's Frame
Flowen's Shield
Flowen's Sword
Flowen's Sword (3060)
Flowen's Sword (3064)
Flowen's Sword (3067)
Flowen's Sword (3073)
Flowen's Sword (3077)
Flowen's Sword (3079)
Flowen's Sword (3082)
Flowen's Sword (3083)
Flowen's Sword (3084)
Frozen Shooter
Gae Bolg
Gal Gryphon's Wing
Galatine
Gatling
General/Arm
General/Legs
General/Mind
General/Power
Gi Gue's Body
Gigobooma's Right Arm
Girasole
Glaive
Glide Divine
Gobooma's Right Arm
God Hand
God/Ability
God/Arm
God/Battle
God/Body
God/HP
God/Legs
God/Luck
God/Mind
God/Power
God/TP
God/Technique
Grass Assassin's Arms
Gratia
Graviton Plate
Greenill Card
Guard Wave
Guardianna
Guilty Light
Gungnir
Guren
H&S25 Justice
HP Material
HP/Restorate
HP/Revival
Handgun: Guld
Handgun: Milla
Heart of Poumn
Heaven Punisher
Heaven Striker
Heaven Striker Coat
Heavenly/Ability
Heavenly/Arms
Heavenly/Battle
Heavenly/Body
Heavenly/HP
Heavenly/Legs
Heavenly/Luck
Heavenly/Mind
Heavenly/Power
Heavenly/Resist
Heavenly/TP
Hero/Ability
Hildebear's Head
Hildeblue's Head
Hitogata
Holy Ray
Ice Staff: Dagon
Ignition Cloak
Imperial Pick
Infantry Gear
Infantry Mantle
Inferno Bazooka
Invisible Guard
Jack-O'-Lantern
Justy-23ST
Kaladbolg
Kamui
Kasami Bracer
Kunai
Kusanagi
L&K14 Combat
L&K38 Combat
Laconium Axe
Lame d'Argent
Laser
Last Survivor
Launcher
Lavis Cannon
Liberta Kit
Lieutenant Gear
Lieutenant Mantle
Light Relief
Limiter
Luck Material
Luminous Field
M&A60 Vise
Mace of Adaman
Madam's Parasol
Madam's Umbrella
Mag
Magic Rock "Moola"
Magic Stone "Iritista"
Maguwa
Mahu
Maser Beam
Master/Ability
Meteor Cudgel
Meteor Smash
Mind Material
Monkey King Bar
Monogrinder
Morning Glory
Morning Prayer
Mother Garb
Mother Garb+
Musashi
NUG2000-Bazooka
Nei's Claw
Officer Uniform
Ophelie Seize
Oran Card
P-arm's Arms
PB/Create
Panzer Faust
Parasitic Gene "Flow"
Partisan of Lightning
Parts of Baranz
Parts of Egg Blaster
Perfect/Resist
Phoenix Claw
Phonon Maser
Photon Claw
Photon Crystal
Photon Launcher
Pillar
Pinkal Card
Plantain Leaf
Pole
Power Material
Prophets of Motav
Proto Regene Gear
Psycho Wand
Purplenum Card
Rabbit Wand
Rage de Feu
Rambling May
Rappy's Beak
Rappy's Wing
Recovery Barrier
Red Barrier
Red Coat
Red Dagger
Red Handgun
Red Mechgun
Red Odoshi Domaru
Red Partisan
Red Ring
Red Saber
Red Scorpio
Red Slicer
Red Sword
Redria Card
Regene Gear Adv.
Regenerate Gear
Regenerate Gear B.P.
Repeater
Resist/Fire
Resist/Flame
Resist/Freeze
Resist/Light
Resist/Thunder
Revival Cuirass
Revival Garment
Rianov 303SNR
Rianov 303SNR-1
Rianov 303SNR-2
Rianov 303SNR-3
Rianov 303SNR-4
Rianov 303SNR-5
Rico's Earring
Rico's Glasses
Rika's Claw
Ripper
Ruby Bullet
Rupika
S-Parts ver1.16
S-Parts ver2.01
S-beat's Arms
S-red's Arms
Sacred Cloth
Sacred Duster
Sacred Guard
Sange
Sawcer
Scape Doll
Scepter
Sealed J-Sword
Secret Gear
Secure Feet
Select Cloak
Sense Plate
Shouren
Silence Claw
Sinow Berill's Arms
Siren Glass Hammer
Skyly Card
Slicer of Assassin
Slicer of Fanatic
Smartlink
Smoking Plate
Snake Spire
Sol Atomizer
Solferino
Sorcerer's Right Arm
Soul Banish
Spirit Cuirass
Spirit Garment
Spread Needle
Staff
Stag Cutlery
Standstill Shield
Star Amplifier
Star Atomizer
Star Cuirass
Sting Tip
Stink Frame
Stink Shield
Storm Wand: Indra
Striker
Striker Plus
Suppressed Gun
Swordsman Lore
Syncesta
TP/Restorate
TP/Revival
Talis
Tanegashima
Technical Crozier
Tempest Cloak
The Sigh of a God
Thirteen
Trigrinder
Trimate
Tripolic Reflector
Tripolic Shield
Twin Blaze
Twin Brand
Twin Chakram
Twin Psychogun
Tyrell's Parasol
V101
V501
V502
V801
Valkyrie
Varista
Victor Axe
Viridia Card
Visk-235W
Vivienne
Vjaya
Vulcan
Wals-MK2
Wedding Dress
Whitill Card
Windmill
Yamato
Yamigarasu
Yasakani Magatama
Yasha
Yasminkov 2000H
Yasminkov 3000R
Yasminkov 7000V
Yasminkov 9000M
Yata Mirror
Yellow Barrier
Yellowboze Card
Yunchang
Zanba
Zero Divide
Can't render this file because it contains an unexpected character in line 261 and column 12.

View File

@ -200,6 +200,7 @@ function parseNpcData(episode: number, npcs: DatNpc[]): QuestNpc[] {
});
}
// TODO: detect Mothmant, St. Rappy, Hallo Rappy, Egg Rappy, Death Gunner, Bulk and Recon.
function getNpcType(episode: number, { typeId, unknown, skin, areaId }: DatNpc): NpcType {
const regular = (unknown[2][18] & 0x80) === 0;
@ -353,8 +354,6 @@ function getNpcType(episode: number, { typeId, unknown, skin, areaId }: DatNpc):
case 0x0FE: return NpcType.Nurse2;
}
// TODO: remove log statement:
console.log(`Unknown type ID: ${typeId} (0x${typeId.toString(16)}).`);
return NpcType.Unknown;
}

View File

@ -2,7 +2,7 @@ export class NpcType {
id: number;
code: string;
/**
* Unique name.
* Unique name. E.g. a Delsaber would have (Ep. II) appended to its name.
*/
name: string;
/**
@ -10,44 +10,59 @@ export class NpcType {
* Might conflict with other NPC names (e.g. Delsaber from ep. I and ep. II).
*/
simpleName: string;
ultimateName: string;
episode?: number;
enemy: boolean;
constructor(id: number, code: string, name: string, simpleName: string, episode: number | undefined, enemy: boolean) {
constructor(
id: number,
code: string,
name: string,
simpleName: string,
ultimateName: string,
episode: number | undefined,
enemy: boolean
) {
if (!Number.isInteger(id) || id < 1)
throw new Error(`Expected id to be an integer greater than or equal to 1, got ${id}.`);
if (!code) throw new Error('code is required.');
if (!name) throw new Error('name is required.');
if (!simpleName) throw new Error('simpleName is required.');
if (!ultimateName) throw new Error('ultimateName is required.');
if (episode != null && episode !== 1 && episode !== 2 && episode !== 4)
throw new Error(`episode should be undefined, 1, 2 or 4, got ${episode}.`);
if (typeof enemy !== 'boolean') throw new Error('enemy is required.');
this.id = id;
this.code = code;
this.name = name;
this.simpleName = simpleName;
this.ultimateName = ultimateName;
this.name = name;
this.episode = episode;
this.enemy = enemy;
if (episode) {
const map = NpcType.byEpAndSimpleName[episode];
if (map) map.set(name, this);
const map = NpcType.byEpAndName[episode];
if (map) {
map.set(simpleName, this);
map.set(ultimateName, this);
}
}
}
private static byEpAndSimpleName = [
private static byEpAndName = [
undefined, new Map<string, NpcType>(), new Map<string, NpcType>(), undefined, new Map<string, NpcType>()
];
/**
* Uniquely identifies an NPC.
* Uniquely identifies an NPC. Tries to match on simpleName and ultimateName.
*/
static bySimpleNameAndEpisode(simpleName: string, episode: number): NpcType | undefined {
const ep = this.byEpAndSimpleName[episode];
static byNameAndEpisode(name: string, episode: number): NpcType | undefined {
const ep = this.byEpAndName[episode];
if (!ep) throw new Error(`No NpcTypes for episode ${episode}.`);
return ep.get(simpleName);
return ep.get(name);
}
//
@ -89,6 +104,7 @@ export class NpcType {
static RagRappy: NpcType;
static AlRappy: NpcType;
static Monest: NpcType;
static Mothmant: NpcType;
static SavageWolf: NpcType;
static BarbarousWolf: NpcType;
static Booma: NpcType;
@ -108,6 +124,8 @@ export class NpcType {
static PofuillySlime: NpcType;
static PouillySlime: NpcType;
static PanArms: NpcType;
static Migium: NpcType;
static Hidoom: NpcType;
static DeRolLe: NpcType;
// Episode I Mines
@ -127,12 +145,14 @@ export class NpcType {
static Delsaber: NpcType;
static ChaosSorcerer: NpcType;
static DarkGunner: NpcType;
static DeathGunner: NpcType;
static ChaosBringer: NpcType;
static DarkBelra: NpcType;
static Dimenian: NpcType;
static LaDimenian: NpcType;
static SoDimenian: NpcType;
static Bulclaw: NpcType;
static Bulk: NpcType;
static Claw: NpcType;
static DarkFalz: NpcType;
@ -142,7 +162,11 @@ export class NpcType {
static Hildeblue2: NpcType;
static RagRappy2: NpcType;
static LoveRappy: NpcType;
static StRappy: NpcType;
static HalloRappy: NpcType;
static EggRappy: NpcType;
static Monest2: NpcType;
static Mothmant2: NpcType;
static PoisonLily2: NpcType;
static NarLily2: NpcType;
static GrassAssassin2: NpcType;
@ -157,6 +181,8 @@ export class NpcType {
static SavageWolf2: NpcType;
static BarbarousWolf2: NpcType;
static PanArms2: NpcType;
static Migium2: NpcType;
static Hidoom2: NpcType;
static Dubchic2: NpcType;
static Gilchic2: NpcType;
static Garanz2: NpcType;
@ -189,6 +215,7 @@ export class NpcType {
static Dolmdarl: NpcType;
static Morfos: NpcType;
static Recobox: NpcType;
static Recon: NpcType;
static Epsilon: NpcType;
static SinowZoa: NpcType;
static SinowZele: NpcType;
@ -228,29 +255,29 @@ export class NpcType {
// Unknown NPCs
//
NpcType.Unknown = new NpcType(id++, 'Unknown', 'Unknown', 'Unknown', undefined, false);
NpcType.Unknown = new NpcType(id++, 'Unknown', 'Unknown', 'Unknown', 'Unknown', undefined, false);
//
// Friendly NPCs
//
NpcType.FemaleFat = new NpcType(id++, 'FemaleFat', 'Female Fat', 'Female Fat', undefined, false);
NpcType.FemaleMacho = new NpcType(id++, 'FemaleMacho', 'Female Macho', 'Female Macho', undefined, false);
NpcType.FemaleTall = new NpcType(id++, 'FemaleTall', 'Female Tall', 'Female Tall', undefined, false);
NpcType.MaleDwarf = new NpcType(id++, 'MaleDwarf', 'Male Dwarf', 'Male Dwarf', undefined, false);
NpcType.MaleFat = new NpcType(id++, 'MaleFat', 'Male Fat', 'Male Fat', undefined, false);
NpcType.MaleMacho = new NpcType(id++, 'MaleMacho', 'Male Macho', 'Male Macho', undefined, false);
NpcType.MaleOld = new NpcType(id++, 'MaleOld', 'Male Old', 'Male Old', undefined, false);
NpcType.BlueSoldier = new NpcType(id++, 'BlueSoldier', 'Blue Soldier', 'Blue Soldier', undefined, false);
NpcType.RedSoldier = new NpcType(id++, 'RedSoldier', 'Red Soldier', 'Red Soldier', undefined, false);
NpcType.Principal = new NpcType(id++, 'Principal', 'Principal', 'Principal', undefined, false);
NpcType.Tekker = new NpcType(id++, 'Tekker', 'Tekker', 'Tekker', undefined, false);
NpcType.GuildLady = new NpcType(id++, 'GuildLady', 'Guild Lady', 'Guild Lady', undefined, false);
NpcType.Scientist = new NpcType(id++, 'Scientist', 'Scientist', 'Scientist', undefined, false);
NpcType.Nurse = new NpcType(id++, 'Nurse', 'Nurse', 'Nurse', undefined, false);
NpcType.Irene = new NpcType(id++, 'Irene', 'Irene', 'Irene', undefined, false);
NpcType.ItemShop = new NpcType(id++, 'ItemShop', 'Item Shop', 'Item Shop', undefined, false);
NpcType.Nurse2 = new NpcType(id++, 'Nurse2', 'Nurse', 'Nurse (Ep. II)', 2, false);
NpcType.FemaleFat = new NpcType(id++, 'FemaleFat', 'Female Fat', 'Female Fat', 'Female Fat', undefined, false);
NpcType.FemaleMacho = new NpcType(id++, 'FemaleMacho', 'Female Macho', 'Female Macho', 'Female Macho', undefined, false);
NpcType.FemaleTall = new NpcType(id++, 'FemaleTall', 'Female Tall', 'Female Tall', 'Female Tall', undefined, false);
NpcType.MaleDwarf = new NpcType(id++, 'MaleDwarf', 'Male Dwarf', 'Male Dwarf', 'Male Dwarf', undefined, false);
NpcType.MaleFat = new NpcType(id++, 'MaleFat', 'Male Fat', 'Male Fat', 'Male Fat', undefined, false);
NpcType.MaleMacho = new NpcType(id++, 'MaleMacho', 'Male Macho', 'Male Macho', 'Male Macho', undefined, false);
NpcType.MaleOld = new NpcType(id++, 'MaleOld', 'Male Old', 'Male Old', 'Male Old', undefined, false);
NpcType.BlueSoldier = new NpcType(id++, 'BlueSoldier', 'Blue Soldier', 'Blue Soldier', 'Blue Soldier', undefined, false);
NpcType.RedSoldier = new NpcType(id++, 'RedSoldier', 'Red Soldier', 'Red Soldier', 'Red Soldier', undefined, false);
NpcType.Principal = new NpcType(id++, 'Principal', 'Principal', 'Principal', 'Principal', undefined, false);
NpcType.Tekker = new NpcType(id++, 'Tekker', 'Tekker', 'Tekker', 'Tekker', undefined, false);
NpcType.GuildLady = new NpcType(id++, 'GuildLady', 'Guild Lady', 'Guild Lady', 'Guild Lady', undefined, false);
NpcType.Scientist = new NpcType(id++, 'Scientist', 'Scientist', 'Scientist', 'Scientist', undefined, false);
NpcType.Nurse = new NpcType(id++, 'Nurse', 'Nurse', 'Nurse', 'Nurse', undefined, false);
NpcType.Irene = new NpcType(id++, 'Irene', 'Irene', 'Irene', 'Irene', undefined, false);
NpcType.ItemShop = new NpcType(id++, 'ItemShop', 'Item Shop', 'Item Shop', 'Item Shop', undefined, false);
NpcType.Nurse2 = new NpcType(id++, 'Nurse2', 'Nurse (Ep. II)', 'Nurse', 'Nurse', 2, false);
//
// Enemy NPCs
@ -258,139 +285,151 @@ export class NpcType {
// Episode I Forest
NpcType.Hildebear = new NpcType(id++, 'Hildebear', 'Hildebear', 'Hildebear', 1, true);
NpcType.Hildeblue = new NpcType(id++, 'Hildeblue', 'Hildeblue', 'Hildeblue', 1, true);
NpcType.RagRappy = new NpcType(id++, 'RagRappy', 'Rag Rappy', 'Rag Rappy', 1, true);
NpcType.AlRappy = new NpcType(id++, 'AlRappy', 'Al Rappy', 'Al Rappy', 1, true);
NpcType.Monest = new NpcType(id++, 'Monest', 'Monest', 'Monest', 1, true);
NpcType.SavageWolf = new NpcType(id++, 'SavageWolf', 'Savage Wolf', 'Savage Wolf', 1, true);
NpcType.BarbarousWolf = new NpcType(id++, 'BarbarousWolf', 'Barbarous Wolf', 'Barbarous Wolf', 1, true);
NpcType.Booma = new NpcType(id++, 'Booma', 'Booma', 'Booma', 1, true);
NpcType.Gobooma = new NpcType(id++, 'Gobooma', 'Gobooma', 'Gobooma', 1, true);
NpcType.Gigobooma = new NpcType(id++, 'Gigobooma', 'Gigobooma', 'Gigobooma', 1, true);
NpcType.Dragon = new NpcType(id++, 'Dragon', 'Dragon', 'Dragon', 1, true);
NpcType.Hildebear = new NpcType(id++, 'Hildebear', 'Hildebear', 'Hildebear', 'Hildelt', 1, true);
NpcType.Hildeblue = new NpcType(id++, 'Hildeblue', 'Hildeblue', 'Hildeblue', 'Hildetorr', 1, true);
NpcType.RagRappy = new NpcType(id++, 'RagRappy', 'Rag Rappy', 'Rag Rappy', 'El Rappy', 1, true);
NpcType.AlRappy = new NpcType(id++, 'AlRappy', 'Al Rappy', 'Al Rappy', 'Pal Rappy', 1, true);
NpcType.Monest = new NpcType(id++, 'Monest', 'Monest', 'Monest', 'Mothvist', 1, true);
NpcType.Mothmant = new NpcType(id++, 'Mothmant', 'Mothmant', 'Mothmant', 'Mothvert', 1, true);
NpcType.SavageWolf = new NpcType(id++, 'SavageWolf', 'Savage Wolf', 'Savage Wolf', 'Gulgus', 1, true);
NpcType.BarbarousWolf = new NpcType(id++, 'BarbarousWolf', 'Barbarous Wolf', 'Barbarous Wolf', 'Gulgus-Gue', 1, true);
NpcType.Booma = new NpcType(id++, 'Booma', 'Booma', 'Booma', 'Bartle', 1, true);
NpcType.Gobooma = new NpcType(id++, 'Gobooma', 'Gobooma', 'Gobooma', 'Barble', 1, true);
NpcType.Gigobooma = new NpcType(id++, 'Gigobooma', 'Gigobooma', 'Gigobooma', 'Tollaw', 1, true);
NpcType.Dragon = new NpcType(id++, 'Dragon', 'Dragon', 'Dragon', 'Sil Dragon', 1, true);
// Episode I Caves
NpcType.GrassAssassin = new NpcType(id++, 'GrassAssassin', 'Grass Assassin', 'Grass Assassin', 1, true);
NpcType.PoisonLily = new NpcType(id++, 'PoisonLily', 'Poison Lily', 'Poison Lily', 1, true);
NpcType.NarLily = new NpcType(id++, 'NarLily', 'Nar Lily', 'Nar Lily', 1, true);
NpcType.NanoDragon = new NpcType(id++, 'NanoDragon', 'Nano Dragon', 'Nano Dragon', 1, true);
NpcType.EvilShark = new NpcType(id++, 'EvilShark', 'Evil Shark', 'Evil Shark', 1, true);
NpcType.PalShark = new NpcType(id++, 'PalShark', 'Pal Shark', 'Pal Shark', 1, true);
NpcType.GuilShark = new NpcType(id++, 'GuilShark', 'Guil Shark', 'Guil Shark', 1, true);
NpcType.PofuillySlime = new NpcType(id++, 'PofuillySlime', 'Pofuilly Slime', 'Pofuilly Slime', 1, true);
NpcType.PouillySlime = new NpcType(id++, 'PouillySlime', 'Pouilly Slime', 'Pouilly Slime', 1, true);
NpcType.PanArms = new NpcType(id++, 'PanArms', 'Pan Arms', 'Pan Arms', 1, true);
NpcType.DeRolLe = new NpcType(id++, 'DeRolLe', 'De Rol Le', 'De Rol Le', 1, true);
NpcType.GrassAssassin = new NpcType(id++, 'GrassAssassin', 'Grass Assassin', 'Grass Assassin', 'Crimson Assassin', 1, true);
NpcType.PoisonLily = new NpcType(id++, 'PoisonLily', 'Poison Lily', 'Poison Lily', 'Ob Lily', 1, true);
NpcType.NarLily = new NpcType(id++, 'NarLily', 'Nar Lily', 'Nar Lily', 'Mil Lily', 1, true);
NpcType.NanoDragon = new NpcType(id++, 'NanoDragon', 'Nano Dragon', 'Nano Dragon', 'Nano Dragon', 1, true);
NpcType.EvilShark = new NpcType(id++, 'EvilShark', 'Evil Shark', 'Evil Shark', 'Vulmer', 1, true);
NpcType.PalShark = new NpcType(id++, 'PalShark', 'Pal Shark', 'Pal Shark', 'Govulmer', 1, true);
NpcType.GuilShark = new NpcType(id++, 'GuilShark', 'Guil Shark', 'Guil Shark', 'Melqueek', 1, true);
NpcType.PofuillySlime = new NpcType(id++, 'PofuillySlime', 'Pofuilly Slime', 'Pofuilly Slime', 'Pofuilly Slime', 1, true);
NpcType.PouillySlime = new NpcType(id++, 'PouillySlime', 'Pouilly Slime', 'Pouilly Slime', 'Pouilly Slime', 1, true);
NpcType.PanArms = new NpcType(id++, 'PanArms', 'Pan Arms', 'Pan Arms', 'Pan Arms', 1, true);
NpcType.Migium = new NpcType(id++, 'Migium', 'Migium', 'Migium', 'Migium', 1, true);
NpcType.Hidoom = new NpcType(id++, 'Hidoom', 'Hidoom', 'Hidoom', 'Hidoom', 1, true);
NpcType.DeRolLe = new NpcType(id++, 'DeRolLe', 'De Rol Le', 'De Rol Le', 'Dal Ra Lie', 1, true);
// Episode I Mines
NpcType.Dubchic = new NpcType(id++, 'Dubchic', 'Dubchic', 'Dubchic', 1, true);
NpcType.Gilchic = new NpcType(id++, 'Gilchic', 'Gilchic', 'Gilchic', 1, true);
NpcType.Garanz = new NpcType(id++, 'Garanz', 'Garanz', 'Garanz', 1, true);
NpcType.SinowBeat = new NpcType(id++, 'SinowBeat', 'Sinow Beat', 'Sinow Beat', 1, true);
NpcType.SinowGold = new NpcType(id++, 'SinowGold', 'Sinow Gold', 'Sinow Gold', 1, true);
NpcType.Canadine = new NpcType(id++, 'Canadine', 'Canadine', 'Canadine', 1, true);
NpcType.Canane = new NpcType(id++, 'Canane', 'Canane', 'Canane', 1, true);
NpcType.Dubswitch = new NpcType(id++, 'Dubswitch', 'Dubswitch', 'Dubswitch', 1, true);
NpcType.VolOpt = new NpcType(id++, 'VolOpt', 'Vol Opt', 'Vol Opt', 1, true);
NpcType.Dubchic = new NpcType(id++, 'Dubchic', 'Dubchic', 'Dubchic', 'Dubchich', 1, true);
NpcType.Gilchic = new NpcType(id++, 'Gilchic', 'Gilchic', 'Gilchic', 'Gilchich', 1, true);
NpcType.Garanz = new NpcType(id++, 'Garanz', 'Garanz', 'Garanz', 'Baranz', 1, true);
NpcType.SinowBeat = new NpcType(id++, 'SinowBeat', 'Sinow Beat', 'Sinow Beat', 'Sinow Blue', 1, true);
NpcType.SinowGold = new NpcType(id++, 'SinowGold', 'Sinow Gold', 'Sinow Gold', 'Sinow Red', 1, true);
NpcType.Canadine = new NpcType(id++, 'Canadine', 'Canadine', 'Canadine', 'Canabin', 1, true);
NpcType.Canane = new NpcType(id++, 'Canane', 'Canane', 'Canane', 'Canune', 1, true);
NpcType.Dubswitch = new NpcType(id++, 'Dubswitch', 'Dubswitch', 'Dubswitch', 'Dubswitch', 1, true);
NpcType.VolOpt = new NpcType(id++, 'VolOpt', 'Vol Opt', 'Vol Opt', 'Vol Opt ver.2', 1, true);
// Episode I Ruins
NpcType.Delsaber = new NpcType(id++, 'Delsaber', 'Delsaber', 'Delsaber', 1, true);
NpcType.ChaosSorcerer = new NpcType(id++, 'ChaosSorcerer', 'Chaos Sorcerer', 'Chaos Sorcerer', 1, true);
NpcType.DarkGunner = new NpcType(id++, 'DarkGunner', 'Dark Gunner', 'Dark Gunner', 1, true);
NpcType.ChaosBringer = new NpcType(id++, 'ChaosBringer', 'Chaos Bringer', 'Chaos Bringer', 1, true);
NpcType.DarkBelra = new NpcType(id++, 'DarkBelra', 'Dark Belra', 'Dark Belra', 1, true);
NpcType.Dimenian = new NpcType(id++, 'Dimenian', 'Dimenian', 'Dimenian', 1, true);
NpcType.LaDimenian = new NpcType(id++, 'LaDimenian', 'La Dimenian', 'La Dimenian', 1, true);
NpcType.SoDimenian = new NpcType(id++, 'SoDimenian', 'So Dimenian', 'So Dimenian', 1, true);
NpcType.Bulclaw = new NpcType(id++, 'Bulclaw', 'Bulclaw', 'Bulclaw', 1, true);
NpcType.Claw = new NpcType(id++, 'Claw', 'Claw', 'Claw', 1, true);
NpcType.DarkFalz = new NpcType(id++, 'DarkFalz', 'Dark Falz', 'Dark Falz', 1, true);
NpcType.Delsaber = new NpcType(id++, 'Delsaber', 'Delsaber', 'Delsaber', 'Delsaber', 1, true);
NpcType.ChaosSorcerer = new NpcType(id++, 'ChaosSorcerer', 'Chaos Sorcerer', 'Chaos Sorcerer', 'Gran Sorcerer', 1, true);
NpcType.DarkGunner = new NpcType(id++, 'DarkGunner', 'Dark Gunner', 'Dark Gunner', 'Dark Gunner', 1, true);
NpcType.DeathGunner = new NpcType(id++, 'DeathGunner', 'Death Gunner', 'Death Gunner', 'Death Gunner', 1, true);
NpcType.ChaosBringer = new NpcType(id++, 'ChaosBringer', 'Chaos Bringer', 'Chaos Bringer', 'Dark Bringer', 1, true);
NpcType.DarkBelra = new NpcType(id++, 'DarkBelra', 'Dark Belra', 'Dark Belra', 'Indi Belra', 1, true);
NpcType.Dimenian = new NpcType(id++, 'Dimenian', 'Dimenian', 'Dimenian', 'Arlan', 1, true);
NpcType.LaDimenian = new NpcType(id++, 'LaDimenian', 'La Dimenian', 'La Dimenian', 'Merlan', 1, true);
NpcType.SoDimenian = new NpcType(id++, 'SoDimenian', 'So Dimenian', 'So Dimenian', 'Del-D', 1, true);
NpcType.Bulclaw = new NpcType(id++, 'Bulclaw', 'Bulclaw', 'Bulclaw', 'Bulclaw', 1, true);
NpcType.Bulk = new NpcType(id++, 'Bulk', 'Bulk', 'Bulk', 'Bulk', 1, true);
NpcType.Claw = new NpcType(id++, 'Claw', 'Claw', 'Claw', 'Claw', 1, true);
NpcType.DarkFalz = new NpcType(id++, 'DarkFalz', 'Dark Falz', 'Dark Falz', 'Dark Falz', 1, true);
// Episode II VR Temple
NpcType.Hildebear2 = new NpcType(id++, 'Hildebear2', 'Hildebear', 'Hildebear (Ep. II)', 2, true);
NpcType.Hildeblue2 = new NpcType(id++, 'Hildeblue2', 'Hildeblue', 'Hildeblue (Ep. II)', 2, true);
NpcType.RagRappy2 = new NpcType(id++, 'RagRappy2', 'Rag Rappy', 'Rag Rappy (Ep. II)', 2, true);
NpcType.LoveRappy = new NpcType(id++, 'LoveRappy', 'Love Rappy', 'Love Rappy', 2, true);
NpcType.Monest2 = new NpcType(id++, 'Monest2', 'Monest', 'Monest (Ep. II)', 2, true);
NpcType.PoisonLily2 = new NpcType(id++, 'PoisonLily2', 'Poison Lily', 'Poison Lily (Ep. II)', 2, true);
NpcType.NarLily2 = new NpcType(id++, 'NarLily2', 'Nar Lily', 'Nar Lily (Ep. II)', 2, true);
NpcType.GrassAssassin2 = new NpcType(id++, 'GrassAssassin2', 'Grass Assassin', 'Grass Assassin (Ep. II)', 2, true);
NpcType.Dimenian2 = new NpcType(id++, 'Dimenian2', 'Dimenian', 'Dimenian (Ep. II)', 2, true);
NpcType.LaDimenian2 = new NpcType(id++, 'LaDimenian2', 'La Dimenian', 'La Dimenian (Ep. II)', 2, true);
NpcType.SoDimenian2 = new NpcType(id++, 'SoDimenian2', 'So Dimenian', 'So Dimenian (Ep. II)', 2, true);
NpcType.DarkBelra2 = new NpcType(id++, 'DarkBelra2', 'Dark Belra', 'Dark Belra (Ep. II)', 2, true);
NpcType.BarbaRay = new NpcType(id++, 'BarbaRay', 'Barba Ray', 'Barba Ray', 2, true);
NpcType.Hildebear2 = new NpcType(id++, 'Hildebear2', 'Hildebear (Ep. II)', 'Hildebear', 'Hildelt', 2, true);
NpcType.Hildeblue2 = new NpcType(id++, 'Hildeblue2', 'Hildeblue (Ep. II)', 'Hildeblue', 'Hildetorr', 2, true);
NpcType.RagRappy2 = new NpcType(id++, 'RagRappy2', 'Rag Rappy (Ep. II)', 'Rag Rappy', 'El Rappy', 2, true);
NpcType.LoveRappy = new NpcType(id++, 'LoveRappy', 'Love Rappy', 'Love Rappy', 'Love Rappy', 2, true);
NpcType.StRappy = new NpcType(id++, 'StRappy', 'St. Rappy', 'St. Rappy', 'St. Rappy', 2, true);
NpcType.HalloRappy = new NpcType(id++, 'HalloRappy', 'Hallo Rappy', 'Hallo Rappy', 'Hallo Rappy', 2, true);
NpcType.EggRappy = new NpcType(id++, 'EggRappy', 'Egg Rappy', 'Egg Rappy', 'Egg Rappy', 2, true);
NpcType.Monest2 = new NpcType(id++, 'Monest2', 'Monest (Ep. II)', 'Monest', 'Mothvist', 2, true);
NpcType.Mothmant2 = new NpcType(id++, 'Mothmant2', 'Mothmant', 'Mothmant', 'Mothvert', 2, true);
NpcType.PoisonLily2 = new NpcType(id++, 'PoisonLily2', 'Poison Lily (Ep. II)', 'Poison Lily', 'Ob Lily', 2, true);
NpcType.NarLily2 = new NpcType(id++, 'NarLily2', 'Nar Lily (Ep. II)', 'Nar Lily', 'Mil Lily', 2, true);
NpcType.GrassAssassin2 = new NpcType(id++, 'GrassAssassin2', 'Grass Assassin (Ep. II)', 'Grass Assassin', 'Crimson Assassin', 2, true);
NpcType.Dimenian2 = new NpcType(id++, 'Dimenian2', 'Dimenian (Ep. II)', 'Dimenian', 'Arlan', 2, true);
NpcType.LaDimenian2 = new NpcType(id++, 'LaDimenian2', 'La Dimenian (Ep. II)', 'La Dimenian', 'Merlan', 2, true);
NpcType.SoDimenian2 = new NpcType(id++, 'SoDimenian2', 'So Dimenian (Ep. II)', 'So Dimenian', 'Del-D', 2, true);
NpcType.DarkBelra2 = new NpcType(id++, 'DarkBelra2', 'Dark Belra (Ep. II)', 'Dark Belra', 'Indi Belra', 2, true);
NpcType.BarbaRay = new NpcType(id++, 'BarbaRay', 'Barba Ray', 'Barba Ray', 'Barba Ray', 2, true);
// Episode II VR Spaceship
NpcType.SavageWolf2 = new NpcType(id++, 'SavageWolf2', 'Savage Wolf', 'Savage Wolf (Ep. II)', 2, true);
NpcType.BarbarousWolf2 = new NpcType(id++, 'BarbarousWolf2', 'Barbarous Wolf', 'Barbarous Wolf (Ep. II)', 2, true);
NpcType.PanArms2 = new NpcType(id++, 'PanArms2', 'Pan Arms', 'Pan Arms (Ep. II)', 2, true);
NpcType.Dubchic2 = new NpcType(id++, 'Dubchic2', 'Dubchic', 'Dubchic (Ep. II)', 2, true);
NpcType.Gilchic2 = new NpcType(id++, 'Gilchic2', 'Gilchic', 'Gilchic (Ep. II)', 2, true);
NpcType.Garanz2 = new NpcType(id++, 'Garanz2', 'Garanz', 'Garanz (Ep. II)', 2, true);
NpcType.Dubswitch2 = new NpcType(id++, 'Dubswitch2', 'Dubswitch', 'Dubswitch (Ep. II)', 2, true);
NpcType.Delsaber2 = new NpcType(id++, 'Delsaber2', 'Delsaber', 'Delsaber (Ep. II)', 2, true);
NpcType.ChaosSorcerer2 = new NpcType(id++, 'ChaosSorcerer2', 'Chaos Sorcerer', 'Chaos Sorcerer (Ep. II)', 2, true);
NpcType.GolDragon = new NpcType(id++, 'GolDragon', 'Gol Dragon', 'Gol Dragon', 2, true);
NpcType.SavageWolf2 = new NpcType(id++, 'SavageWolf2', 'Savage Wolf (Ep. II)', 'Savage Wolf', 'Gulgus', 2, true);
NpcType.BarbarousWolf2 = new NpcType(id++, 'BarbarousWolf2', 'Barbarous Wolf (Ep. II)', 'Barbarous Wolf', 'Gulgus-Gue', 2, true);
NpcType.PanArms2 = new NpcType(id++, 'PanArms2', 'Pan Arms (Ep. II)', 'Pan Arms', 'Pan Arms', 2, true);
NpcType.Migium2 = new NpcType(id++, 'Migium2', 'Migium (Ep. II)', 'Migium', 'Migium', 2, true);
NpcType.Hidoom2 = new NpcType(id++, 'Hidoom2', 'Hidoom (Ep. II)', 'Hidoom', 'Hidoom', 2, true);
NpcType.Dubchic2 = new NpcType(id++, 'Dubchic2', 'Dubchic (Ep. II)', 'Dubchic', 'Dubchich', 2, true);
NpcType.Gilchic2 = new NpcType(id++, 'Gilchic2', 'Gilchic (Ep. II)', 'Gilchic', 'Gilchich', 2, true);
NpcType.Garanz2 = new NpcType(id++, 'Garanz2', 'Garanz (Ep. II)', 'Garanz', 'Baranz', 2, true);
NpcType.Dubswitch2 = new NpcType(id++, 'Dubswitch2', 'Dubswitch (Ep. II)', 'Dubswitch', 'Dubswitch', 2, true);
NpcType.Delsaber2 = new NpcType(id++, 'Delsaber2', 'Delsaber (Ep. II)', 'Delsaber', 'Delsaber', 2, true);
NpcType.ChaosSorcerer2 = new NpcType(id++, 'ChaosSorcerer2', 'Chaos Sorcerer (Ep. II)', 'Chaos Sorcerer', 'Gran Sorcerer', 2, true);
NpcType.GolDragon = new NpcType(id++, 'GolDragon', 'Gol Dragon', 'Gol Dragon', 'Gol Dragon', 2, true);
// Episode II Central Control Area
NpcType.SinowBerill = new NpcType(id++, 'SinowBerill', 'Sinow Berill', 'Sinow Berill', 2, true);
NpcType.SinowSpigell = new NpcType(id++, 'SinowSpigell', 'Sinow Spigell', 'Sinow Spigell', 2, true);
NpcType.Merillia = new NpcType(id++, 'Merillia', 'Merillia', 'Merillia', 2, true);
NpcType.Meriltas = new NpcType(id++, 'Meriltas', 'Meriltas', 'Meriltas', 2, true);
NpcType.Mericarol = new NpcType(id++, 'Mericarol', 'Mericarol', 'Mericarol', 2, true);
NpcType.Mericus = new NpcType(id++, 'Mericus', 'Mericus', 'Mericus', 2, true);
NpcType.Merikle = new NpcType(id++, 'Merikle', 'Merikle', 'Merikle', 2, true);
NpcType.UlGibbon = new NpcType(id++, 'UlGibbon', 'Ul Gibbon', 'Ul Gibbon', 2, true);
NpcType.ZolGibbon = new NpcType(id++, 'ZolGibbon', 'Zol Gibbon', 'Zol Gibbon', 2, true);
NpcType.Gibbles = new NpcType(id++, 'Gibbles', 'Gibbles', 'Gibbles', 2, true);
NpcType.Gee = new NpcType(id++, 'Gee', 'Gee', 'Gee', 2, true);
NpcType.GiGue = new NpcType(id++, 'GiGue', 'Gi Gue', 'Gi Gue', 2, true);
NpcType.GalGryphon = new NpcType(id++, 'GalGryphon', 'Gal Gryphon', 'Gal Gryphon', 2, true);
NpcType.SinowBerill = new NpcType(id++, 'SinowBerill', 'Sinow Berill', 'Sinow Berill', 'Sinow Berill', 2, true);
NpcType.SinowSpigell = new NpcType(id++, 'SinowSpigell', 'Sinow Spigell', 'Sinow Spigell', 'Sinow Spigell', 2, true);
NpcType.Merillia = new NpcType(id++, 'Merillia', 'Merillia', 'Merillia', 'Merillia', 2, true);
NpcType.Meriltas = new NpcType(id++, 'Meriltas', 'Meriltas', 'Meriltas', 'Meriltas', 2, true);
NpcType.Mericarol = new NpcType(id++, 'Mericarol', 'Mericarol', 'Mericarol', 'Mericarol', 2, true);
NpcType.Mericus = new NpcType(id++, 'Mericus', 'Mericus', 'Mericus', 'Mericus', 2, true);
NpcType.Merikle = new NpcType(id++, 'Merikle', 'Merikle', 'Merikle', 'Merikle', 2, true);
NpcType.UlGibbon = new NpcType(id++, 'UlGibbon', 'Ul Gibbon', 'Ul Gibbon', 'Ul Gibbon', 2, true);
NpcType.ZolGibbon = new NpcType(id++, 'ZolGibbon', 'Zol Gibbon', 'Zol Gibbon', 'Zol Gibbon', 2, true);
NpcType.Gibbles = new NpcType(id++, 'Gibbles', 'Gibbles', 'Gibbles', 'Gibbles', 2, true);
NpcType.Gee = new NpcType(id++, 'Gee', 'Gee', 'Gee', 'Gee', 2, true);
NpcType.GiGue = new NpcType(id++, 'GiGue', 'Gi Gue', 'Gi Gue', 'Gi Gue', 2, true);
NpcType.GalGryphon = new NpcType(id++, 'GalGryphon', 'Gal Gryphon', 'Gal Gryphon', 'Gal Gryphon', 2, true);
// Episode II Seabed
NpcType.Deldepth = new NpcType(id++, 'Deldepth', 'Deldepth', 'Deldepth', 2, true);
NpcType.Delbiter = new NpcType(id++, 'Delbiter', 'Delbiter', 'Delbiter', 2, true);
NpcType.Dolmolm = new NpcType(id++, 'Dolmolm', 'Dolmolm', 'Dolmolm', 2, true);
NpcType.Dolmdarl = new NpcType(id++, 'Dolmdarl', 'Dolmdarl', 'Dolmdarl', 2, true);
NpcType.Morfos = new NpcType(id++, 'Morfos', 'Morfos', 'Morfos', 2, true);
NpcType.Recobox = new NpcType(id++, 'Recobox', 'Recobox', 'Recobox', 2, true);
NpcType.Epsilon = new NpcType(id++, 'Epsilon', 'Epsilon', 'Epsilon', 2, true);
NpcType.SinowZoa = new NpcType(id++, 'SinowZoa', 'Sinow Zoa', 'Sinow Zoa', 2, true);
NpcType.SinowZele = new NpcType(id++, 'SinowZele', 'Sinow Zele', 'Sinow Zele', 2, true);
NpcType.IllGill = new NpcType(id++, 'IllGill', 'Ill Gill', 'Ill Gill', 2, true);
NpcType.DelLily = new NpcType(id++, 'DelLily', 'Del Lily', 'Del Lily', 2, true);
NpcType.OlgaFlow = new NpcType(id++, 'OlgaFlow', 'Olga Flow', 'Olga Flow', 2, true);
NpcType.Deldepth = new NpcType(id++, 'Deldepth', 'Deldepth', 'Deldepth', 'Deldepth', 2, true);
NpcType.Delbiter = new NpcType(id++, 'Delbiter', 'Delbiter', 'Delbiter', 'Delbiter', 2, true);
NpcType.Dolmolm = new NpcType(id++, 'Dolmolm', 'Dolmolm', 'Dolmolm', 'Dolmolm', 2, true);
NpcType.Dolmdarl = new NpcType(id++, 'Dolmdarl', 'Dolmdarl', 'Dolmdarl', 'Dolmdarl', 2, true);
NpcType.Morfos = new NpcType(id++, 'Morfos', 'Morfos', 'Morfos', 'Morfos', 2, true);
NpcType.Recobox = new NpcType(id++, 'Recobox', 'Recobox', 'Recobox', 'Recobox', 2, true);
NpcType.Recon = new NpcType(id++, 'Recon', 'Recon', 'Recon', 'Recon', 2, true);
NpcType.Epsilon = new NpcType(id++, 'Epsilon', 'Epsilon', 'Epsilon', 'Epsilon', 2, true);
NpcType.SinowZoa = new NpcType(id++, 'SinowZoa', 'Sinow Zoa', 'Sinow Zoa', 'Sinow Zoa', 2, true);
NpcType.SinowZele = new NpcType(id++, 'SinowZele', 'Sinow Zele', 'Sinow Zele', 'Sinow Zele', 2, true);
NpcType.IllGill = new NpcType(id++, 'IllGill', 'Ill Gill', 'Ill Gill', 'Ill Gill', 2, true);
NpcType.DelLily = new NpcType(id++, 'DelLily', 'Del Lily', 'Del Lily', 'Del Lily', 2, true);
NpcType.OlgaFlow = new NpcType(id++, 'OlgaFlow', 'Olga Flow', 'Olga Flow', 'Olga Flow', 2, true);
// Episode IV
NpcType.SandRappy = new NpcType(id++, 'SandRappy', 'Sand Rappy', 'Sand Rappy', 4, true);
NpcType.DelRappy = new NpcType(id++, 'DelRappy', 'Del Rappy', 'Del Rappy', 4, true);
NpcType.Astark = new NpcType(id++, 'Astark', 'Astark', 'Astark', 4, true);
NpcType.SatelliteLizard = new NpcType(id++, 'SatelliteLizard', 'Satellite Lizard', 'Satellite Lizard', 4, true);
NpcType.Yowie = new NpcType(id++, 'Yowie', 'Yowie', 'Yowie', 4, true);
NpcType.MerissaA = new NpcType(id++, 'MerissaA', 'Merissa A', 'Merissa A', 4, true);
NpcType.MerissaAA = new NpcType(id++, 'MerissaAA', 'Merissa AA', 'Merissa AA', 4, true);
NpcType.Girtablulu = new NpcType(id++, 'Girtablulu', 'Girtablulu', 'Girtablulu', 4, true);
NpcType.Zu = new NpcType(id++, 'Zu', 'Zu', 'Zu', 4, true);
NpcType.Pazuzu = new NpcType(id++, 'Pazuzu', 'Pazuzu', 'Pazuzu', 4, true);
NpcType.Boota = new NpcType(id++, 'Boota', 'Boota', 'Boota', 4, true);
NpcType.ZeBoota = new NpcType(id++, 'ZeBoota', 'Ze Boota', 'Ze Boota', 4, true);
NpcType.BaBoota = new NpcType(id++, 'BaBoota', 'Ba Boota', 'Ba Boota', 4, true);
NpcType.Dorphon = new NpcType(id++, 'Dorphon', 'Dorphon', 'Dorphon', 4, true);
NpcType.DorphonEclair = new NpcType(id++, 'DorphonEclair', 'Dorphon Eclair', 'Dorphon Eclair', 4, true);
NpcType.Goran = new NpcType(id++, 'Goran', 'Goran', 'Goran', 4, true);
NpcType.PyroGoran = new NpcType(id++, 'PyroGoran', 'Pyro Goran', 'Pyro Goran', 4, true);
NpcType.GoranDetonator = new NpcType(id++, 'GoranDetonator', 'Goran Detonator', 'Goran Detonator', 4, true);
NpcType.SaintMillion = new NpcType(id++, 'SaintMillion', 'Saint-Million', 'Saint-Million', 4, true);
NpcType.Shambertin = new NpcType(id++, 'Shambertin', 'Shambertin', 'Shambertin', 4, true);
NpcType.Kondrieu = new NpcType(id++, 'Kondrieu', 'Kondrieu', 'Kondrieu', 4, true);
NpcType.SandRappy = new NpcType(id++, 'SandRappy', 'Sand Rappy', 'Sand Rappy', 'Sand Rappy', 4, true);
NpcType.DelRappy = new NpcType(id++, 'DelRappy', 'Del Rappy', 'Del Rappy', 'Del Rappy', 4, true);
NpcType.Astark = new NpcType(id++, 'Astark', 'Astark', 'Astark', 'Astark', 4, true);
NpcType.SatelliteLizard = new NpcType(id++, 'SatelliteLizard', 'Satellite Lizard', 'Satellite Lizard', 'Satellite Lizard', 4, true);
NpcType.Yowie = new NpcType(id++, 'Yowie', 'Yowie', 'Yowie', 'Yowie', 4, true);
NpcType.MerissaA = new NpcType(id++, 'MerissaA', 'Merissa A', 'Merissa A', 'Merissa A', 4, true);
NpcType.MerissaAA = new NpcType(id++, 'MerissaAA', 'Merissa AA', 'Merissa AA', 'Merissa AA', 4, true);
NpcType.Girtablulu = new NpcType(id++, 'Girtablulu', 'Girtablulu', 'Girtablulu', 'Girtablulu', 4, true);
NpcType.Zu = new NpcType(id++, 'Zu', 'Zu', 'Zu', 'Zu', 4, true);
NpcType.Pazuzu = new NpcType(id++, 'Pazuzu', 'Pazuzu', 'Pazuzu', 'Pazuzu', 4, true);
NpcType.Boota = new NpcType(id++, 'Boota', 'Boota', 'Boota', 'Boota', 4, true);
NpcType.ZeBoota = new NpcType(id++, 'ZeBoota', 'Ze Boota', 'Ze Boota', 'Ze Boota', 4, true);
NpcType.BaBoota = new NpcType(id++, 'BaBoota', 'Ba Boota', 'Ba Boota', 'Ba Boota', 4, true);
NpcType.Dorphon = new NpcType(id++, 'Dorphon', 'Dorphon', 'Dorphon', 'Dorphon', 4, true);
NpcType.DorphonEclair = new NpcType(id++, 'DorphonEclair', 'Dorphon Eclair', 'Dorphon Eclair', 'Dorphon Eclair', 4, true);
NpcType.Goran = new NpcType(id++, 'Goran', 'Goran', 'Goran', 'Goran', 4, true);
NpcType.PyroGoran = new NpcType(id++, 'PyroGoran', 'Pyro Goran', 'Pyro Goran', 'Pyro Goran', 4, true);
NpcType.GoranDetonator = new NpcType(id++, 'GoranDetonator', 'Goran Detonator', 'Goran Detonator', 'Goran Detonator', 4, true);
NpcType.SaintMillion = new NpcType(id++, 'SaintMillion', 'Saint-Million', 'Saint-Million', 'Saint-Million', 4, true);
NpcType.Shambertin = new NpcType(id++, 'Shambertin', 'Shambertin', 'Shambertin', 'Shambertin', 4, true);
NpcType.Kondrieu = new NpcType(id++, 'Kondrieu', 'Kondrieu', 'Kondrieu', 'Kondrieu', 4, true);
}());

View File

@ -1,9 +1,10 @@
import { Object3D } from 'three';
import { computed, observable } from 'mobx';
import { Object3D } from 'three';
import { ArrayBufferCursor } from '../bin-data/ArrayBufferCursor';
import { DatNpc, DatObject, DatUnknown } from '../bin-data/parsing/dat';
import { NpcType } from './NpcType';
import { ObjectType } from './ObjectType';
import { DatObject, DatNpc, DatUnknown } from '../bin-data/parsing/dat';
import { ArrayBufferCursor } from '../bin-data/ArrayBufferCursor';
import { enumValues } from '../enums';
export { NpcType } from './NpcType';
export { ObjectType } from './ObjectType';
@ -12,6 +13,8 @@ export enum Server {
Ephinea = 'Ephinea'
}
export const Servers: Server[] = enumValues(Server);
export enum SectionId {
Viridia = 'Viridia',
Greenill = 'Greenill',
@ -25,7 +28,16 @@ export enum SectionId {
Whitill = 'Whitill',
}
export const SectionIds = Object.keys(SectionId);
export const SectionIds: SectionId[] = enumValues(SectionId);
export enum Difficulty {
Normal = 'Normal',
Hard = 'Hard',
VHard = 'VHard',
Ultimate = 'Ultimate'
}
export const Difficulties: Difficulty[] = enumValues(Difficulty);
export class Vec3 {
x: number;
@ -292,6 +304,24 @@ export class Item {
constructor(public name: string) { }
}
type ItemDrop = {
item: Item,
anythingRate: number,
rareRate: number
}
export class EnemyDrop implements ItemDrop {
rate: number;
constructor(
public item: Item,
public anythingRate: number,
public rareRate: number
) {
this.rate = anythingRate * rareRate;
}
}
export class HuntMethod {
constructor(
public quest: SimpleQuest

40
src/enums.ts Normal file
View File

@ -0,0 +1,40 @@
export function enumValues<E>(e: any): E[] {
const values = Object.values(e);
const numberValues = values.filter(v => typeof v === 'number');
if (numberValues.length) {
return numberValues as any as E[];
} else {
return values as any as E[];
}
}
export function enumNames(e: any): string[] {
return Object.keys(e).filter(k => typeof (e as any)[k] === 'string');
}
/**
* Map with a guaranteed value per enum key.
*/
export class EnumMap<K, V> {
private keys: K[];
private values = new Map<K, V>();
constructor(enum_: any, initialValue: V | ((key: K) => V)) {
this.keys = enumValues(enum_);
if (!(initialValue instanceof Function)) {
for (const key of this.keys) {
this.values.set(key, initialValue);
}
} else {
for (const key of this.keys) {
this.values.set(key, initialValue(key));
}
}
}
get(key: K): V {
return this.values.get(key)!;
}
}

View File

@ -5,20 +5,52 @@ import fs from 'fs';
const SECTION_IDS = [
'Viridia', 'Greenill', 'Skyly', 'Bluefull', 'Purplenum', 'Pinkal', 'Redria', 'Oran', 'Yellowboze', 'Whitill',
];
const ENEMY_DROPS_HEADER = ['difficulty', 'episode', 'section_id', 'enemy', 'item', 'drop_rate', 'rare_rate'];
const BOX_DROPS_HEADER = ['difficulty', 'episode', 'section_id', 'box', 'item', 'drop_rate'];
const ITEMS_HEADER = ['name'];
async function update() {
const csv =
const normal = await download('normal');
const hard = await download('hard');
const vhard = await download('vhard', 'very-hard');
const ultimate = await download('ultimate');
const enemyCsv =
[
['mode', 'episode', 'section_id', 'monster', 'item', 'drop_rate', 'rare_rate'],
...await download('normal'),
...await download('hard'),
...await download('vhard', 'very-hard'),
...await download('ultimate')
ENEMY_DROPS_HEADER,
...normal.enemyDrops,
...hard.enemyDrops,
...vhard.enemyDrops,
...ultimate.enemyDrops
]
.map(r => r.join('\t'))
.join('\n')
.join('\n');
return fs.promises.writeFile('./public/drops.ephinea.tsv', csv);
await fs.promises.writeFile('./public/enemy_drops.ephinea.tsv', enemyCsv);
const boxCsv =
[
BOX_DROPS_HEADER,
...normal.boxDrops,
...hard.boxDrops,
...vhard.boxDrops,
...ultimate.boxDrops
]
.map(r => r.join('\t'))
.join('\n');
await fs.promises.writeFile('./public/box_drops.ephinea.tsv', boxCsv);
const items = new Set([...normal.items, ...hard.items, ...vhard.items, ...ultimate.items]);
const itemsCsv =
[
ITEMS_HEADER,
...[...items].sort()
]
.join('\n');
await fs.promises.writeFile('./public/items.ephinea.tsv', itemsCsv);
}
async function download(mode: string, modeUrl: string = mode) {
@ -27,7 +59,11 @@ async function download(mode: string, modeUrl: string = mode) {
const $ = cheerio.load(body);
let episode = 1;
const data: any[][] = [];
const data: {
enemyDrops: any[][], boxDrops: any[][], items: Set<string>
} = {
enemyDrops: [], boxDrops: [], items: new Set()
};
$('table').each((tableI, table) => {
const isBox = tableI >= 3;
@ -43,7 +79,19 @@ async function download(mode: string, modeUrl: string = mode) {
}
try {
const monster = monsterText.split('/')[mode === 'ultimate' ? 1 : 0] || monsterText;
let monster = monsterText.split('/')[mode === 'ultimate' ? 1 : 0] || monsterText;
if (monster === 'Halo Rappy') {
monster = 'Hallo Rappy';
} else if (monster === 'Dal Ral Lie') {
monster = 'Dal Ra Lie';
} else if (monster === 'Vol Opt ver. 2') {
monster = 'Vol Opt ver.2';
} else if (monster === 'Za Boota') {
monster = 'Ze Boota';
} else if (monster === 'Saint Million') {
monster = 'Saint-Million';
}
$('td', tr).each((tdI, td) => {
if (tdI === 0) {
@ -55,20 +103,21 @@ async function download(mode: string, modeUrl: string = mode) {
if (isBox) {
$('font font', td).each((_, font) => {
const item = $('b', font).text();
const rareRateNum = parseFloat($('sup', font).text());
const rareRateDenom = parseFloat($('sub', font).text());
const rateNum = parseFloat($('sup', font).text());
const rateDenom = parseFloat($('sub', font).text());
data.push(
data.boxDrops.push(
[
mode,
episode,
sectionId,
`${monster} Box`,
monster,
item,
1,
rareRateNum / rareRateDenom
rateNum / rateDenom
]
);
data.items.add(item);
});
return;
} else {
@ -85,7 +134,7 @@ async function download(mode: string, modeUrl: string = mode) {
const [, rareRateNum, rareRateDenom] =
/Rare Rate: (\d+)\/(\d+(\.\d+)?)/g.exec(title)!.map(parseFloat);
data.push(
data.enemyDrops.push(
[
mode,
episode,
@ -96,6 +145,8 @@ async function download(mode: string, modeUrl: string = mode) {
rareRateNum / rareRateDenom,
]
);
data.items.add(item);
} catch (e) {
console.error(`Error while processing item ${item} of ${monster} in episode ${episode} ${mode}.`, e);
}

View File

@ -1,21 +1,23 @@
import { observable } from "mobx";
import { HuntMethod, NpcType, Server, SimpleNpc, SimpleQuest } from "../domain";
import { Loadable } from "../Loadable";
import { PerServer } from "./PerServer";
import { ServerMap } from "./ServerMap";
class HuntMethodStore {
@observable methods: PerServer<Loadable<Array<HuntMethod>>> = new PerServer(server =>
@observable methods: ServerMap<Loadable<Array<HuntMethod>>> = new ServerMap(server =>
new Loadable([], () => this.loadHuntMethods(server))
);
private async loadHuntMethods(server: Server): Promise<HuntMethod[]> {
const response = await fetch(process.env.PUBLIC_URL + `/quests.${Server[server]}.tsv`);
const response = await fetch(
`${process.env.PUBLIC_URL}/quests.${Server[server].toLowerCase()}.tsv`
);
const data = await response.text();
const rows = data.split('\n').map(line => line.split('\t'));
const npcTypeByIndex = rows[0].slice(2, -2).map((episode, i) => {
const enemy = rows[1][i + 2];
return NpcType.bySimpleNameAndEpisode(enemy, parseInt(episode, 10))!;
return NpcType.byNameAndEpisode(enemy, parseInt(episode, 10))!;
});
return rows.slice(2).map(row => {
@ -26,9 +28,11 @@ class HuntMethodStore {
const type = npcTypeByIndex[cellI];
const enemies = [];
if (type) {
for (let i = 0; i < amount; i++) {
enemies.push(new SimpleNpc(type));
}
}
return enemies;
});

View File

@ -1,7 +1,8 @@
import { Solve } from 'javascript-lp-solver';
import { observable } from "mobx";
import { Item, SectionIds } from "../domain";
import { Difficulties, Item, NpcType, SectionIds } from "../domain";
import { huntMethodStore } from "./HuntMethodStore";
import { itemDropStore } from './ItemDropStore';
export class WantedItem {
@observable item: Item;
@ -19,30 +20,55 @@ class HuntOptimizerStore {
optimize = async () => {
if (!this.wantedItems.length) return;
const methods = await huntMethodStore.methods.current.promise;
const dropTable = await itemDropStore.enemyDrops.current.promise;
const constraints: { [itemName: string]: { min: number } } = {};
for (const item of this.wantedItems) {
constraints[item.item.name] = { min: item.amount };
for (const wanted of this.wantedItems) {
constraints[wanted.item.name] = { min: wanted.amount };
}
const items = new Set(this.wantedItems.map(i => i.item));
const variables: { [methodName: string]: { [itemName: string]: number } } = {};
for (const method of await huntMethodStore.methods.current.promise) {
for (const method of methods) {
const counts = new Map<NpcType, number>();
for (const enemy of method.quest.enemies) {
const count = counts.get(enemy.type);
counts.set(enemy.type, (count || 0) + 1);
}
for (const diff of Difficulties) {
for (const sectionId of SectionIds) {
const variable = {};
const variable: { [itemName: string]: number } = {
time: 0.5
};
// TODO
for (const [npcType, count] of counts.entries()) {
const drop = dropTable.getDrop(diff, sectionId, npcType);
variables[`${sectionId} ${method.quest.name}`] = variable;
if (drop && items.has(drop.item)) {
variable[drop.item.name] = count * drop.rate;
}
}
if (Object.keys(variable).length) {
variables[`${diff} ${sectionId} ${method.quest.name}`] = variable;
}
}
}
}
const result = Solve({
optimize: '',
optimize: 'time',
opType: 'min',
constraints,
variables
});
console.log(result);
}
}

102
src/stores/ItemDropStore.ts Normal file
View File

@ -0,0 +1,102 @@
import { observable } from "mobx";
import { Difficulty, EnemyDrop, NpcType, SectionId, Server } from "../domain";
import { EnumMap } from "../enums";
import { Loadable } from "../Loadable";
import { itemStore } from "./ItemStore";
import { ServerMap } from "./ServerMap";
class EnemyDropTable {
private map: EnumMap<Difficulty, EnumMap<SectionId, Map<NpcType, EnemyDrop>>> =
new EnumMap(Difficulty, new EnumMap(SectionId, new Map()));
getDrop(difficulty: Difficulty, sectionId: SectionId, npcType: NpcType): EnemyDrop | undefined {
return this.map.get(difficulty).get(sectionId).get(npcType);
}
setDrop(difficulty: Difficulty, sectionId: SectionId, npcType: NpcType, drop: EnemyDrop) {
this.map.get(difficulty).get(sectionId).set(npcType, drop);
}
}
class ItemDropStore {
@observable enemyDrops: ServerMap<Loadable<EnemyDropTable>> = new ServerMap(server =>
new Loadable(new EnemyDropTable(), () => this.loadEnemyDrops(server))
);
private loadEnemyDrops = async (server: Server): Promise<EnemyDropTable> => {
const response = await fetch(
`${process.env.PUBLIC_URL}/enemy_drops.${Server[server].toLowerCase()}.tsv`
);
const data = await response.text();
const lines = data.split('\n');
const lineCount = lines.length;
const drops = new EnemyDropTable();
for (let i = 1; i < lineCount; i++) {
const line = lines[i];
const lineNo = i + 1;
const cells = line.split('\t');
const diffStr = cells[0].toLowerCase();
const diff =
diffStr === 'normal' ? Difficulty.Normal
: diffStr === 'hard' ? Difficulty.Hard
: diffStr === 'vhard' ? Difficulty.VHard
: diffStr === 'ultimate' ? Difficulty.Ultimate
: undefined;
if (!diff) {
console.error(`Couldn't parse difficulty for line ${lineNo}.`);
continue;
}
const episode = parseInt(cells[1], 10);
if (episode !== 1 && episode !== 2 && episode !== 4) {
console.error(`Couldn't parse episode for line ${lineNo}.`);
continue;
}
const sectionId: SectionId | undefined = (SectionId as any)[cells[2]];
if (!sectionId) {
console.error(`Couldn't parse section_id for line ${lineNo}.`);
continue;
}
const enemyName = cells[3];
const anythingRate = parseFloat(cells[5]);
if (!isFinite(anythingRate)) {
console.error(`Couldn't parse drop_rate for line ${lineNo}.`);
continue;
}
const rareRate = parseFloat(cells[5]);
if (!rareRate) {
console.error(`Couldn't parse rare_rate for line ${lineNo}.`);
continue;
}
const npcType = NpcType.byNameAndEpisode(enemyName, episode);
if (!npcType) {
console.error(`Couldn't determine enemy type for line ${lineNo}.`);
continue;
}
drops.setDrop(diff, sectionId, npcType, new EnemyDrop(
itemStore.dedupItem(cells[4]),
anythingRate,
rareRate
));
}
return drops;
}
}
export const itemDropStore = new ItemDropStore();

View File

@ -1,20 +1,31 @@
import { sortedUniq } from "lodash";
import { observable } from "mobx";
import { Item, Server } from "../domain";
import { Loadable } from "../Loadable";
import { PerServer } from "./PerServer";
import { ServerMap } from "./ServerMap";
class ItemStore {
@observable items: PerServer<Loadable<Array<Item>>> = new PerServer(server =>
private itemMap = new Map<string, Item>();
@observable items: ServerMap<Loadable<Array<Item>>> = new ServerMap(server =>
new Loadable([], () => this.loadItems(server))
);
dedupItem(name: string): Item {
let item = this.itemMap.get(name);
if (!item) {
this.itemMap.set(name, item = new Item(name));
}
return item;
}
private async loadItems(server: Server): Promise<Item[]> {
const response = await fetch(process.env.PUBLIC_URL + `/drops.${Server[server]}.tsv`);
const response = await fetch(
`${process.env.PUBLIC_URL}/items.${Server[server].toLowerCase()}.tsv`
);
const data = await response.text();
return sortedUniq(
data.split('\n').slice(1).map(line => line.split('\t')[4]).sort()
).map(name => new Item(name));
return data.split('\n').slice(1).map(name => new Item(name));
}
}

View File

@ -1,30 +0,0 @@
import { Server } from "../domain";
import { computed } from "mobx";
import { applicationStore } from "./ApplicationStore";
/**
* Represents a value per server.
* E.g. drop tables differ per server, this can be represented by PerServer<DropTable>.
*/
export class PerServer<T> {
private values = new Map<Server, T>();
constructor(initialValue: T | ((server: Server) => T)) {
if (!(initialValue instanceof Function)) {
this.values.set(Server.Ephinea, initialValue);
} else {
this.values.set(Server.Ephinea, initialValue(Server.Ephinea));
}
}
get(server: Server): T {
return this.values.get(server)!;
}
/**
* @returns the value for the current server as set in {@link applicationStore}.
*/
@computed get current(): T {
return this.get(applicationStore.currentServer);
}
}

17
src/stores/ServerMap.ts Normal file
View File

@ -0,0 +1,17 @@
import { computed } from "mobx";
import { Server } from "../domain";
import { applicationStore } from "./ApplicationStore";
import { EnumMap } from "../enums";
export class ServerMap<V> extends EnumMap<Server, V> {
constructor(initialValue: V | ((server: Server) => V)) {
super(Server, initialValue)
}
/**
* @returns the value for the current server as set in {@link applicationStore}.
*/
@computed get current(): V {
return this.get(applicationStore.currentServer);
}
}

View File

@ -31,14 +31,3 @@
.ApplicationComponent-main>* {
flex: 1;
}
.ApplicationComponent-error {
display: flex;
flex-direction: column;
align-items: center;
}
.ApplicationComponent-error > div {
width: 300px;
margin-top: 30px;
}

View File

@ -1,11 +1,15 @@
import { Alert, Menu } from 'antd';
import { Menu } from 'antd';
import { ClickParam } from 'antd/lib/menu';
import { observer } from 'mobx-react';
import React from 'react';
import './ApplicationComponent.css';
import { withErrorBoundary } from './ErrorBoundary';
import { HuntOptimizerComponent } from './hunt-optimizer/HuntOptimizerComponent';
import { QuestEditorComponent } from './quest-editor/QuestEditorComponent';
const QuestEditor = withErrorBoundary(QuestEditorComponent);
const HuntOptimizer = withErrorBoundary(HuntOptimizerComponent);
@observer
export class ApplicationComponent extends React.Component {
state = { tool: 'huntOptimizer' }
@ -15,10 +19,10 @@ export class ApplicationComponent extends React.Component {
switch (this.state.tool) {
case 'questEditor':
toolComponent = <QuestEditorComponent />;
toolComponent = <QuestEditor />;
break;
case 'huntOptimizer':
toolComponent = <HuntOptimizerComponent />;
toolComponent = <HuntOptimizer />;
break;
}
@ -41,9 +45,9 @@ export class ApplicationComponent extends React.Component {
</Menu.Item>
</Menu>
</div>
<ErrorBoundary>
<div className="ApplicationComponent-main">
{toolComponent}
</ErrorBoundary>
</div>
</div>
);
}
@ -52,27 +56,3 @@ export class ApplicationComponent extends React.Component {
this.setState({ tool: e.key });
};
}
class ErrorBoundary extends React.Component {
state = {
hasError: false
}
render() {
return (
<div className="ApplicationComponent-main" >
{this.state.hasError ? (
<div className="ApplicationComponent-error">
<div>
<Alert type="error" message="Something went wrong." />
</div>
</div>
) : this.props.children}
</div>
);
}
static getDerivedStateFromError(_error: Error) {
return { hasError: true };
}
}

10
src/ui/ErrorBoundary.css Normal file
View File

@ -0,0 +1,10 @@
.ErrorBoundary-error {
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
}
.ErrorBoundary-error > * {
margin-top: 10%;
}

31
src/ui/ErrorBoundary.tsx Normal file
View File

@ -0,0 +1,31 @@
import { Alert } from 'antd';
import React from 'react';
import './ErrorBoundary.css';
export class ErrorBoundary extends React.Component {
state = {
hasError: false
};
render() {
if (this.state.hasError) {
return (
<div className="ErrorBoundary-error">
<div>
<Alert type="error" message="Something went wrong." />
</div>
</div>
);
} else {
return this.props.children;
}
}
static getDerivedStateFromError(_error: any) {
return { hasError: true };
}
}
export function withErrorBoundary(Component: React.ComponentType) {
return () => <ErrorBoundary><Component /></ErrorBoundary>;
}