mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 22:58:29 +08:00
Set up basic LP algorithm to optimize hunts. Fixed errors in data.
This commit is contained in:
parent
734ed1016d
commit
72ecdac71d
2407
public/box_drops.ephinea.tsv
Normal file
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
440
public/items.ephinea.tsv
Normal 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.
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}());
|
||||
|
@ -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
40
src/enums.ts
Normal 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)!;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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
102
src/stores/ItemDropStore.ts
Normal 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();
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
17
src/stores/ServerMap.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
10
src/ui/ErrorBoundary.css
Normal 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
31
src/ui/ErrorBoundary.tsx
Normal 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>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user