@ -38,6 +38,9 @@ const char *MonsterData::spritePath() const
/** Contains the data related to each monster ID. */
std : : vector < MonsterData > MonstersData ;
/** Contains the data related to each unique monster ID. */
std : : vector < UniqueMonsterData > UniqueMonstersData ;
/**
* Map between . DUN file value and monster type enum
*/
@ -201,162 +204,269 @@ const _monster_id MonstConvTbl[] = {
MT_LRDSAYTR ,
} ;
namespace {
tl : : expected < _monster_id , std : : string > ParseMonsterId ( std : : string_view value )
{
if ( value = = " MT_NZOMBIE " ) return MT_NZOMBIE ;
if ( value = = " MT_BZOMBIE " ) return MT_BZOMBIE ;
if ( value = = " MT_GZOMBIE " ) return MT_GZOMBIE ;
if ( value = = " MT_YZOMBIE " ) return MT_YZOMBIE ;
if ( value = = " MT_RFALLSP " ) return MT_RFALLSP ;
if ( value = = " MT_DFALLSP " ) return MT_DFALLSP ;
if ( value = = " MT_YFALLSP " ) return MT_YFALLSP ;
if ( value = = " MT_BFALLSP " ) return MT_BFALLSP ;
if ( value = = " MT_WSKELAX " ) return MT_WSKELAX ;
if ( value = = " MT_TSKELAX " ) return MT_TSKELAX ;
if ( value = = " MT_RSKELAX " ) return MT_RSKELAX ;
if ( value = = " MT_XSKELAX " ) return MT_XSKELAX ;
if ( value = = " MT_RFALLSD " ) return MT_RFALLSD ;
if ( value = = " MT_DFALLSD " ) return MT_DFALLSD ;
if ( value = = " MT_YFALLSD " ) return MT_YFALLSD ;
if ( value = = " MT_BFALLSD " ) return MT_BFALLSD ;
if ( value = = " MT_NSCAV " ) return MT_NSCAV ;
if ( value = = " MT_BSCAV " ) return MT_BSCAV ;
if ( value = = " MT_WSCAV " ) return MT_WSCAV ;
if ( value = = " MT_YSCAV " ) return MT_YSCAV ;
if ( value = = " MT_WSKELBW " ) return MT_WSKELBW ;
if ( value = = " MT_TSKELBW " ) return MT_TSKELBW ;
if ( value = = " MT_RSKELBW " ) return MT_RSKELBW ;
if ( value = = " MT_XSKELBW " ) return MT_XSKELBW ;
if ( value = = " MT_WSKELSD " ) return MT_WSKELSD ;
if ( value = = " MT_TSKELSD " ) return MT_TSKELSD ;
if ( value = = " MT_RSKELSD " ) return MT_RSKELSD ;
if ( value = = " MT_XSKELSD " ) return MT_XSKELSD ;
if ( value = = " MT_SNEAK " ) return MT_SNEAK ;
if ( value = = " MT_STALKER " ) return MT_STALKER ;
if ( value = = " MT_UNSEEN " ) return MT_UNSEEN ;
if ( value = = " MT_ILLWEAV " ) return MT_ILLWEAV ;
if ( value = = " MT_NGOATMC " ) return MT_NGOATMC ;
if ( value = = " MT_BGOATMC " ) return MT_BGOATMC ;
if ( value = = " MT_RGOATMC " ) return MT_RGOATMC ;
if ( value = = " MT_GGOATMC " ) return MT_GGOATMC ;
if ( value = = " MT_FIEND " ) return MT_FIEND ;
if ( value = = " MT_GLOOM " ) return MT_GLOOM ;
if ( value = = " MT_BLINK " ) return MT_BLINK ;
if ( value = = " MT_FAMILIAR " ) return MT_FAMILIAR ;
if ( value = = " MT_NGOATBW " ) return MT_NGOATBW ;
if ( value = = " MT_BGOATBW " ) return MT_BGOATBW ;
if ( value = = " MT_RGOATBW " ) return MT_RGOATBW ;
if ( value = = " MT_GGOATBW " ) return MT_GGOATBW ;
if ( value = = " MT_NACID " ) return MT_NACID ;
if ( value = = " MT_RACID " ) return MT_RACID ;
if ( value = = " MT_BACID " ) return MT_BACID ;
if ( value = = " MT_XACID " ) return MT_XACID ;
if ( value = = " MT_SKING " ) return MT_SKING ;
if ( value = = " MT_FAT " ) return MT_FAT ;
if ( value = = " MT_MUDMAN " ) return MT_MUDMAN ;
if ( value = = " MT_TOAD " ) return MT_TOAD ;
if ( value = = " MT_FLAYED " ) return MT_FLAYED ;
if ( value = = " MT_WYRM " ) return MT_WYRM ;
if ( value = = " MT_CAVSLUG " ) return MT_CAVSLUG ;
if ( value = = " MT_DEVOUR " ) return MT_DEVOUR ;
if ( value = = " MT_DVLWYRM " ) return MT_DVLWYRM ;
if ( value = = " MT_NMAGMA " ) return MT_NMAGMA ;
if ( value = = " MT_YMAGMA " ) return MT_YMAGMA ;
if ( value = = " MT_BMAGMA " ) return MT_BMAGMA ;
if ( value = = " MT_WMAGMA " ) return MT_WMAGMA ;
if ( value = = " MT_HORNED " ) return MT_HORNED ;
if ( value = = " MT_MUDRUN " ) return MT_MUDRUN ;
if ( value = = " MT_FROSTC " ) return MT_FROSTC ;
if ( value = = " MT_OBLORD " ) return MT_OBLORD ;
if ( value = = " MT_BONEDMN " ) return MT_BONEDMN ;
if ( value = = " MT_REDDTH " ) return MT_REDDTH ;
if ( value = = " MT_LTCHDMN " ) return MT_LTCHDMN ;
if ( value = = " MT_UDEDBLRG " ) return MT_UDEDBLRG ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INCIN " ) return MT_INCIN ;
if ( value = = " MT_FLAMLRD " ) return MT_FLAMLRD ;
if ( value = = " MT_DOOMFIRE " ) return MT_DOOMFIRE ;
if ( value = = " MT_HELLBURN " ) return MT_HELLBURN ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_RSTORM " ) return MT_RSTORM ;
if ( value = = " MT_STORM " ) return MT_STORM ;
if ( value = = " MT_STORML " ) return MT_STORML ;
if ( value = = " MT_MAEL " ) return MT_MAEL ;
if ( value = = " MT_WINGED " ) return MT_WINGED ;
if ( value = = " MT_GARGOYLE " ) return MT_GARGOYLE ;
if ( value = = " MT_BLOODCLW " ) return MT_BLOODCLW ;
if ( value = = " MT_DEATHW " ) return MT_DEATHW ;
if ( value = = " MT_MEGA " ) return MT_MEGA ;
if ( value = = " MT_GUARD " ) return MT_GUARD ;
if ( value = = " MT_VTEXLRD " ) return MT_VTEXLRD ;
if ( value = = " MT_BALROG " ) return MT_BALROG ;
if ( value = = " MT_NSNAKE " ) return MT_NSNAKE ;
if ( value = = " MT_RSNAKE " ) return MT_RSNAKE ;
if ( value = = " MT_GSNAKE " ) return MT_GSNAKE ;
if ( value = = " MT_BSNAKE " ) return MT_BSNAKE ;
if ( value = = " MT_NBLACK " ) return MT_NBLACK ;
if ( value = = " MT_RTBLACK " ) return MT_RTBLACK ;
if ( value = = " MT_BTBLACK " ) return MT_BTBLACK ;
if ( value = = " MT_RBLACK " ) return MT_RBLACK ;
if ( value = = " MT_UNRAV " ) return MT_UNRAV ;
if ( value = = " MT_HOLOWONE " ) return MT_HOLOWONE ;
if ( value = = " MT_PAINMSTR " ) return MT_PAINMSTR ;
if ( value = = " MT_REALWEAV " ) return MT_REALWEAV ;
if ( value = = " MT_SUCCUBUS " ) return MT_SUCCUBUS ;
if ( value = = " MT_SNOWWICH " ) return MT_SNOWWICH ;
if ( value = = " MT_HLSPWN " ) return MT_HLSPWN ;
if ( value = = " MT_SOLBRNR " ) return MT_SOLBRNR ;
if ( value = = " MT_COUNSLR " ) return MT_COUNSLR ;
if ( value = = " MT_MAGISTR " ) return MT_MAGISTR ;
if ( value = = " MT_CABALIST " ) return MT_CABALIST ;
if ( value = = " MT_ADVOCATE " ) return MT_ADVOCATE ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_DIABLO " ) return MT_DIABLO ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_GOLEM " ) return MT_GOLEM ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_INVALID " ) return MT_INVALID ;
if ( value = = " MT_BIGFALL " ) return MT_BIGFALL ;
if ( value = = " MT_DARKMAGE " ) return MT_DARKMAGE ;
if ( value = = " MT_HELLBOAR " ) return MT_HELLBOAR ;
if ( value = = " MT_STINGER " ) return MT_STINGER ;
if ( value = = " MT_PSYCHORB " ) return MT_PSYCHORB ;
if ( value = = " MT_ARACHNON " ) return MT_ARACHNON ;
if ( value = = " MT_FELLTWIN " ) return MT_FELLTWIN ;
if ( value = = " MT_HORKSPWN " ) return MT_HORKSPWN ;
if ( value = = " MT_VENMTAIL " ) return MT_VENMTAIL ;
if ( value = = " MT_NECRMORB " ) return MT_NECRMORB ;
if ( value = = " MT_SPIDLORD " ) return MT_SPIDLORD ;
if ( value = = " MT_LASHWORM " ) return MT_LASHWORM ;
if ( value = = " MT_TORCHANT " ) return MT_TORCHANT ;
if ( value = = " MT_HORKDMN " ) return MT_HORKDMN ;
if ( value = = " MT_DEFILER " ) return MT_DEFILER ;
if ( value = = " MT_GRAVEDIG " ) return MT_GRAVEDIG ;
if ( value = = " MT_TOMBRAT " ) return MT_TOMBRAT ;
if ( value = = " MT_FIREBAT " ) return MT_FIREBAT ;
if ( value = = " MT_SKLWING " ) return MT_SKLWING ;
if ( value = = " MT_LICH " ) return MT_LICH ;
if ( value = = " MT_CRYPTDMN " ) return MT_CRYPTDMN ;
if ( value = = " MT_HELLBAT " ) return MT_HELLBAT ;
if ( value = = " MT_BONEDEMN " ) return MT_BONEDEMN ;
if ( value = = " MT_LICH " ) return MT_LICH ;
if ( value = = " MT_BICLOPS " ) return MT_BICLOPS ;
if ( value = = " MT_FLESTHNG " ) return MT_FLESTHNG ;
if ( value = = " MT_REAPER " ) return MT_REAPER ;
if ( value = = " MT_NAKRUL " ) return MT_NAKRUL ;
if ( value = = " MT_CLEAVER " ) return MT_CLEAVER ;
if ( value = = " MT_INVILORD " ) return MT_INVILORD ;
if ( value = = " MT_LRDSAYTR " ) return MT_LRDSAYTR ;
return tl : : make_unexpected ( " Unknown enum value " ) ;
}
tl : : expected < MonsterAvailability , std : : string > ParseMonsterAvailability ( std : : string_view value )
{
if ( value = = " Always " )
return MonsterAvailability : : Always ;
if ( value = = " Never " )
return MonsterAvailability : : Never ;
if ( value = = " Retail " )
return MonsterAvailability : : Retail ;
if ( value = = " Always " ) return MonsterAvailability : : Always ;
if ( value = = " Never " ) return MonsterAvailability : : Never ;
if ( value = = " Retail " ) return MonsterAvailability : : Retail ;
return tl : : make_unexpected ( " Expected one of: Always, Never, or Retail " ) ;
}
tl : : expected < MonsterAIID , std : : string > ParseAiId ( std : : string_view value )
{
if ( value = = " Zombie " )
return MonsterAIID : : Zombie ;
if ( value = = " Fat " )
return MonsterAIID : : Fat ;
if ( value = = " SkeletonMelee " )
return MonsterAIID : : SkeletonMelee ;
if ( value = = " SkeletonRanged " )
return MonsterAIID : : SkeletonRanged ;
if ( value = = " Scavenger " )
return MonsterAIID : : Scavenger ;
if ( value = = " Rhino " )
return MonsterAIID : : Rhino ;
if ( value = = " GoatMelee " )
return MonsterAIID : : GoatMelee ;
if ( value = = " GoatRanged " )
return MonsterAIID : : GoatRanged ;
if ( value = = " Fallen " )
return MonsterAIID : : Fallen ;
if ( value = = " Magma " )
return MonsterAIID : : Magma ;
if ( value = = " SkeletonKing " )
return MonsterAIID : : SkeletonKing ;
if ( value = = " Bat " )
return MonsterAIID : : Bat ;
if ( value = = " Gargoyle " )
return MonsterAIID : : Gargoyle ;
if ( value = = " Butcher " )
return MonsterAIID : : Butcher ;
if ( value = = " Succubus " )
return MonsterAIID : : Succubus ;
if ( value = = " Sneak " )
return MonsterAIID : : Sneak ;
if ( value = = " Storm " )
return MonsterAIID : : Storm ;
if ( value = = " FireMan " )
return MonsterAIID : : FireMan ;
if ( value = = " Gharbad " )
return MonsterAIID : : Gharbad ;
if ( value = = " Acid " )
return MonsterAIID : : Acid ;
if ( value = = " AcidUnique " )
return MonsterAIID : : AcidUnique ;
if ( value = = " Golem " )
return MonsterAIID : : Golem ;
if ( value = = " Zhar " )
return MonsterAIID : : Zhar ;
if ( value = = " Snotspill " )
return MonsterAIID : : Snotspill ;
if ( value = = " Snake " )
return MonsterAIID : : Snake ;
if ( value = = " Counselor " )
return MonsterAIID : : Counselor ;
if ( value = = " Mega " )
return MonsterAIID : : Mega ;
if ( value = = " Diablo " )
return MonsterAIID : : Diablo ;
if ( value = = " Lazarus " )
return MonsterAIID : : Lazarus ;
if ( value = = " LazarusSuccubus " )
return MonsterAIID : : LazarusSuccubus ;
if ( value = = " Lachdanan " )
return MonsterAIID : : Lachdanan ;
if ( value = = " Warlord " )
return MonsterAIID : : Warlord ;
if ( value = = " FireBat " )
return MonsterAIID : : FireBat ;
if ( value = = " Torchant " )
return MonsterAIID : : Torchant ;
if ( value = = " HorkDemon " )
return MonsterAIID : : HorkDemon ;
if ( value = = " Lich " )
return MonsterAIID : : Lich ;
if ( value = = " ArchLich " )
return MonsterAIID : : ArchLich ;
if ( value = = " Psychorb " )
return MonsterAIID : : Psychorb ;
if ( value = = " Necromorb " )
return MonsterAIID : : Necromorb ;
if ( value = = " BoneDemon " )
return MonsterAIID : : BoneDemon ;
if ( value = = " Zombie " ) return MonsterAIID : : Zombie ;
if ( value = = " Fat " ) return MonsterAIID : : Fat ;
if ( value = = " SkeletonMelee " ) return MonsterAIID : : SkeletonMelee ;
if ( value = = " SkeletonRanged " ) return MonsterAIID : : SkeletonRanged ;
if ( value = = " Scavenger " ) return MonsterAIID : : Scavenger ;
if ( value = = " Rhino " ) return MonsterAIID : : Rhino ;
if ( value = = " GoatMelee " ) return MonsterAIID : : GoatMelee ;
if ( value = = " GoatRanged " ) return MonsterAIID : : GoatRanged ;
if ( value = = " Fallen " ) return MonsterAIID : : Fallen ;
if ( value = = " Magma " ) return MonsterAIID : : Magma ;
if ( value = = " SkeletonKing " ) return MonsterAIID : : SkeletonKing ;
if ( value = = " Bat " ) return MonsterAIID : : Bat ;
if ( value = = " Gargoyle " ) return MonsterAIID : : Gargoyle ;
if ( value = = " Butcher " ) return MonsterAIID : : Butcher ;
if ( value = = " Succubus " ) return MonsterAIID : : Succubus ;
if ( value = = " Sneak " ) return MonsterAIID : : Sneak ;
if ( value = = " Storm " ) return MonsterAIID : : Storm ;
if ( value = = " FireMan " ) return MonsterAIID : : FireMan ;
if ( value = = " Gharbad " ) return MonsterAIID : : Gharbad ;
if ( value = = " Acid " ) return MonsterAIID : : Acid ;
if ( value = = " AcidUnique " ) return MonsterAIID : : AcidUnique ;
if ( value = = " Golem " ) return MonsterAIID : : Golem ;
if ( value = = " Zhar " ) return MonsterAIID : : Zhar ;
if ( value = = " Snotspill " ) return MonsterAIID : : Snotspill ;
if ( value = = " Snake " ) return MonsterAIID : : Snake ;
if ( value = = " Counselor " ) return MonsterAIID : : Counselor ;
if ( value = = " Mega " ) return MonsterAIID : : Mega ;
if ( value = = " Diablo " ) return MonsterAIID : : Diablo ;
if ( value = = " Lazarus " ) return MonsterAIID : : Lazarus ;
if ( value = = " LazarusSuccubus " ) return MonsterAIID : : LazarusSuccubus ;
if ( value = = " Lachdanan " ) return MonsterAIID : : Lachdanan ;
if ( value = = " Warlord " ) return MonsterAIID : : Warlord ;
if ( value = = " FireBat " ) return MonsterAIID : : FireBat ;
if ( value = = " Torchant " ) return MonsterAIID : : Torchant ;
if ( value = = " HorkDemon " ) return MonsterAIID : : HorkDemon ;
if ( value = = " Lich " ) return MonsterAIID : : Lich ;
if ( value = = " ArchLich " ) return MonsterAIID : : ArchLich ;
if ( value = = " Psychorb " ) return MonsterAIID : : Psychorb ;
if ( value = = " Necromorb " ) return MonsterAIID : : Necromorb ;
if ( value = = " BoneDemon " ) return MonsterAIID : : BoneDemon ;
return tl : : make_unexpected ( " Unknown enum value " ) ;
}
tl : : expected < monster_flag , std : : string > ParseMonsterFlag ( std : : string_view value )
{
if ( value = = " HIDDEN " )
return MFLAG_HIDDEN ;
if ( value = = " LOCK_ANIMATION " )
return MFLAG_LOCK_ANIMATION ;
if ( value = = " ALLOW_SPECIAL " )
return MFLAG_ALLOW_SPECIAL ;
if ( value = = " TARGETS_MONSTER " )
return MFLAG_TARGETS_MONSTER ;
if ( value = = " GOLEM " )
return MFLAG_GOLEM ;
if ( value = = " QUEST_COMPLETE " )
return MFLAG_QUEST_COMPLETE ;
if ( value = = " KNOCKBACK " )
return MFLAG_KNOCKBACK ;
if ( value = = " SEARCH " )
return MFLAG_SEARCH ;
if ( value = = " CAN_OPEN_DOOR " )
return MFLAG_CAN_OPEN_DOOR ;
if ( value = = " NO_ENEMY " )
return MFLAG_NO_ENEMY ;
if ( value = = " BERSERK " )
return MFLAG_BERSERK ;
if ( value = = " NOLIFESTEAL " )
return MFLAG_NOLIFESTEAL ;
if ( value = = " HIDDEN " ) return MFLAG_HIDDEN ;
if ( value = = " LOCK_ANIMATION " ) return MFLAG_LOCK_ANIMATION ;
if ( value = = " ALLOW_SPECIAL " ) return MFLAG_ALLOW_SPECIAL ;
if ( value = = " TARGETS_MONSTER " ) return MFLAG_TARGETS_MONSTER ;
if ( value = = " GOLEM " ) return MFLAG_GOLEM ;
if ( value = = " QUEST_COMPLETE " ) return MFLAG_QUEST_COMPLETE ;
if ( value = = " KNOCKBACK " ) return MFLAG_KNOCKBACK ;
if ( value = = " SEARCH " ) return MFLAG_SEARCH ;
if ( value = = " CAN_OPEN_DOOR " ) return MFLAG_CAN_OPEN_DOOR ;
if ( value = = " NO_ENEMY " ) return MFLAG_NO_ENEMY ;
if ( value = = " BERSERK " ) return MFLAG_BERSERK ;
if ( value = = " NOLIFESTEAL " ) return MFLAG_NOLIFESTEAL ;
return tl : : make_unexpected ( " Unknown enum value " ) ;
}
tl : : expected < MonsterClass , std : : string > ParseMonsterClass ( std : : string_view value )
{
if ( value = = " Undead " )
return MonsterClass : : Undead ;
if ( value = = " Demon " )
return MonsterClass : : Demon ;
if ( value = = " Animal " )
return MonsterClass : : Animal ;
if ( value = = " Undead " ) return MonsterClass : : Undead ;
if ( value = = " Demon " ) return MonsterClass : : Demon ;
if ( value = = " Animal " ) return MonsterClass : : Animal ;
return tl : : make_unexpected ( " Unknown enum value " ) ;
}
tl : : expected < monster_resistance , std : : string > ParseMonsterResistance ( std : : string_view value )
{
if ( value = = " RESIST_MAGIC " )
return RESIST_MAGIC ;
if ( value = = " RESIST_FIRE " )
return RESIST_FIRE ;
if ( value = = " RESIST_LIGHTNING " )
return RESIST_LIGHTNING ;
if ( value = = " IMMUNE_MAGIC " )
return IMMUNE_MAGIC ;
if ( value = = " IMMUNE_FIRE " )
return IMMUNE_FIRE ;
if ( value = = " IMMUNE_LIGHTNING " )
return IMMUNE_LIGHTNING ;
if ( value = = " IMMUNE_ACID " )
return IMMUNE_ACID ;
if ( value = = " RESIST_MAGIC " ) return RESIST_MAGIC ;
if ( value = = " RESIST_FIRE " ) return RESIST_FIRE ;
if ( value = = " RESIST_LIGHTNING " ) return RESIST_LIGHTNING ;
if ( value = = " IMMUNE_MAGIC " ) return IMMUNE_MAGIC ;
if ( value = = " IMMUNE_FIRE " ) return IMMUNE_FIRE ;
if ( value = = " IMMUNE_LIGHTNING " ) return IMMUNE_LIGHTNING ;
if ( value = = " IMMUNE_ACID " ) return IMMUNE_ACID ;
return tl : : make_unexpected ( " Unknown enum value " ) ;
}
void LoadMonsterData ( )
tl : : expected < UniqueMonsterPack , std : : string > ParseUniqueMonsterPack ( std : : string_view value )
{
if ( value = = " None " ) return UniqueMonsterPack : : None ;
if ( value = = " Independent " ) return UniqueMonsterPack : : Independent ;
if ( value = = " Leashed " ) return UniqueMonsterPack : : Leashed ;
return tl : : make_unexpected ( " Unknown enum value " ) ;
}
void LoadMonstDat ( )
{
const std : : string_view filename = " txtdata \\ monsters \\ monstdat.tsv " ;
tl : : expected < DataFile , DataFile : : Error > dataFileResult = DataFile : : load ( filename ) ;
@ -371,6 +481,7 @@ void LoadMonsterData()
}
MonstersData . clear ( ) ;
MonstersData . reserve ( dataFile . numRecords ( ) ) ;
std : : unordered_map < std : : string , size_t > spritePathToId ;
for ( DataFileRecord record : dataFile ) {
FieldIterator fieldIt = record . begin ( ) ;
@ -585,7 +696,7 @@ void LoadMonsterData()
}
// treasure
// TODO: Replace this hack with proper parsing once unique monsters have been migrated .
// TODO: Replace this hack with proper parsing once items have been migrated to data files .
advance ( ) ;
{
const std : : string_view value = ( * fieldIt ) . value ( ) ;
@ -608,6 +719,153 @@ void LoadMonsterData()
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " exp " , * fieldIt ) ;
}
}
MonstersData . shrink_to_fit ( ) ;
}
void LoadUniqueMonstDat ( )
{
const std : : string_view filename = " txtdata \\ monsters \\ unique_monstdat.tsv " ;
tl : : expected < DataFile , DataFile : : Error > dataFileResult = DataFile : : load ( filename ) ;
if ( ! dataFileResult . has_value ( ) ) {
DataFile : : reportFatalError ( dataFileResult . error ( ) , filename ) ;
}
DataFile & dataFile = dataFileResult . value ( ) ;
if ( tl : : expected < void , DataFile : : Error > result = dataFile . skipHeader ( ) ;
! result . has_value ( ) ) {
DataFile : : reportFatalError ( result . error ( ) , filename ) ;
}
UniqueMonstersData . clear ( ) ;
UniqueMonstersData . reserve ( dataFile . numRecords ( ) ) ;
for ( DataFileRecord record : dataFile ) {
FieldIterator fieldIt = record . begin ( ) ;
const FieldIterator endField = record . end ( ) ;
UniqueMonstersData . emplace_back ( ) ;
UniqueMonsterData & monster = UniqueMonstersData . back ( ) ;
const auto advance = [ & ] ( ) {
+ + fieldIt ;
if ( fieldIt = = endField ) {
DataFile : : reportFatalError ( DataFile : : Error : : NotEnoughColumns , filename ) ;
}
} ;
// type
if ( tl : : expected < _monster_id , std : : string > result = ParseMonsterId ( ( * fieldIt ) . value ( ) ) ; result . has_value ( ) ) {
monster . mtype = * std : : move ( result ) ;
} else {
DataFile : : reportFatalFieldError ( DataFileField : : Error : : InvalidValue , filename , " type " , * fieldIt , result . error ( ) ) ;
}
// name
advance ( ) ;
monster . mName = ( * fieldIt ) . value ( ) ;
// trn
advance ( ) ;
monster . mTrnName = ( * fieldIt ) . value ( ) ;
// level
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . mlevel ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " level " , * fieldIt ) ;
}
// maxHp
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . mmaxhp ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " maxHp " , * fieldIt ) ;
}
// ai
advance ( ) ;
if ( tl : : expected < MonsterAIID , std : : string > result = ParseAiId ( ( * fieldIt ) . value ( ) ) ; result . has_value ( ) ) {
monster . mAi = * std : : move ( result ) ;
} else {
DataFile : : reportFatalFieldError ( DataFileField : : Error : : InvalidValue , filename , " ai " , * fieldIt , result . error ( ) ) ;
}
// intelligence
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . mint ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " intelligence " , * fieldIt ) ;
}
// minDamage
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . mMinDamage ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " minDamage " , * fieldIt ) ;
}
// maxDamage
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . mMaxDamage ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " maxDamage " , * fieldIt ) ;
}
// resistance
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseEnumList ( monster . mMagicRes , ParseMonsterResistance ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " resistance " , * fieldIt ) ;
}
// monsterPack
advance ( ) ;
if ( tl : : expected < UniqueMonsterPack , std : : string > result = ParseUniqueMonsterPack ( ( * fieldIt ) . value ( ) ) ; result . has_value ( ) ) {
monster . monsterPack = * std : : move ( result ) ;
} else {
DataFile : : reportFatalFieldError ( DataFileField : : Error : : InvalidValue , filename , " monsterPack " , * fieldIt , result . error ( ) ) ;
}
// customToHit
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . customToHit ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " customToHit " , * fieldIt ) ;
}
// customArmorClass
advance ( ) ;
if ( tl : : expected < void , DataFileField : : Error > result = ( * fieldIt ) . parseInt ( monster . customArmorClass ) ; ! result . has_value ( ) ) {
DataFile : : reportFatalFieldError ( result . error ( ) , filename , " customArmorClass " , * fieldIt ) ;
}
// talkMessage
// TODO: Replace this hack with proper parsing once messages have been migrated to data files.
advance ( ) ;
{
const std : : string_view value = ( * fieldIt ) . value ( ) ;
if ( value . empty ( ) ) {
monster . mtalkmsg = TEXT_NONE ;
} else if ( value = = " TEXT_GARBUD1 " ) {
monster . mtalkmsg = TEXT_GARBUD1 ;
} else if ( value = = " TEXT_ZHAR1 " ) {
monster . mtalkmsg = TEXT_ZHAR1 ;
} else if ( value = = " TEXT_BANNER10 " ) {
monster . mtalkmsg = TEXT_BANNER10 ;
} else if ( value = = " TEXT_VILE13 " ) {
monster . mtalkmsg = TEXT_VILE13 ;
} else if ( value = = " TEXT_VEIL9 " ) {
monster . mtalkmsg = TEXT_VEIL9 ;
} else if ( value = = " TEXT_WARLRD9 " ) {
monster . mtalkmsg = TEXT_WARLRD9 ;
} else {
DataFile : : reportFatalFieldError ( DataFileField : : Error : : InvalidValue , filename , " talkMessage " , * fieldIt , " NOTE: Parser is incomplete " ) ;
}
}
}
UniqueMonstersData . shrink_to_fit ( ) ;
}
} // namespace
void LoadMonsterData ( )
{
LoadMonstDat ( ) ;
LoadUniqueMonstDat ( ) ;
}
size_t GetNumMonsterSprites ( )
@ -615,114 +873,4 @@ size_t GetNumMonsterSprites()
return MonsterSpritePaths . size ( ) ;
}
/** Contains the data related to each unique monster ID. */
const UniqueMonsterData UniqueMonstersData [ ] = {
// clang-format off
// mtype, mName, mTrnName, mlevel, mmaxhp, mAi, mint, mMinDamage, mMaxDamage, mMagicRes, monsterPack, customToHit, customArmorClass, mtalkmsg
// TRANSLATORS: Unique Monster Block start
{ MT_NGOATMC , P_ ( " monster " , " Gharbad the Weak " ) , " bsdb " , 4 , 120 , MonsterAIID : : Gharbad , 3 , 8 , 16 , IMMUNE_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_GARBUD1 } ,
{ MT_SKING , P_ ( " monster " , " Skeleton King " ) , " genrl " , 0 , 240 , MonsterAIID : : SkeletonKing , 3 , 6 , 16 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Independent , 0 , 0 , TEXT_NONE } ,
{ MT_COUNSLR , P_ ( " monster " , " Zhar the Mad " ) , " general " , 8 , 360 , MonsterAIID : : Zhar , 3 , 16 , 40 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_ZHAR1 } ,
{ MT_BFALLSP , P_ ( " monster " , " Snotspill " ) , " bng " , 4 , 220 , MonsterAIID : : Snotspill , 3 , 10 , 18 , RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_BANNER10 } ,
{ MT_ADVOCATE , P_ ( " monster " , " Arch-Bishop Lazarus " ) , " general " , 0 , 600 , MonsterAIID : : Lazarus , 3 , 30 , 50 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_VILE13 } ,
{ MT_HLSPWN , P_ ( " monster " , " Red Vex " ) , " redv " , 0 , 400 , MonsterAIID : : LazarusSuccubus , 3 , 30 , 50 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_VILE13 } ,
{ MT_HLSPWN , P_ ( " monster " , " Black Jade " ) , " blkjd " , 0 , 400 , MonsterAIID : : LazarusSuccubus , 3 , 30 , 50 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_VILE13 } ,
{ MT_RBLACK , P_ ( " monster " , " Lachdanan " ) , " bhka " , 14 , 500 , MonsterAIID : : Lachdanan , 3 , 0 , 0 , 0 , UniqueMonsterPack : : None , 0 , 0 , TEXT_VEIL9 } ,
{ MT_BTBLACK , P_ ( " monster " , " Warlord of Blood " ) , " general " , 13 , 850 , MonsterAIID : : Warlord , 3 , 35 , 50 , IMMUNE_MAGIC | IMMUNE_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_WARLRD9 } ,
{ MT_CLEAVER , P_ ( " monster " , " The Butcher " ) , " genrl " , 0 , 220 , MonsterAIID : : Butcher , 3 , 6 , 12 , RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_HORKDMN , P_ ( " monster " , " Hork Demon " ) , " genrl " , 19 , 300 , MonsterAIID : : HorkDemon , 3 , 20 , 35 , RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_DEFILER , P_ ( " monster " , " The Defiler " ) , " genrl " , 20 , 480 , MonsterAIID : : SkeletonMelee , 3 , 30 , 40 , RESIST_MAGIC | RESIST_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_NAKRUL , P_ ( " monster " , " Na-Krul " ) , " genrl " , 0 , 1332 , MonsterAIID : : SkeletonMelee , 3 , 40 , 50 , IMMUNE_MAGIC | IMMUNE_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_TSKELAX , P_ ( " monster " , " Bonehead Keenaxe " ) , " bhka " , 2 , 91 , MonsterAIID : : SkeletonMelee , 2 , 4 , 10 , IMMUNE_MAGIC , UniqueMonsterPack : : Leashed , 100 , 0 , TEXT_NONE } ,
{ MT_RFALLSD , P_ ( " monster " , " Bladeskin the Slasher " ) , " bsts " , 2 , 51 , MonsterAIID : : Fallen , 0 , 6 , 18 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 45 , TEXT_NONE } ,
{ MT_NZOMBIE , P_ ( " monster " , " Soulpus " ) , " general " , 2 , 133 , MonsterAIID : : Zombie , 0 , 4 , 8 , RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_RFALLSP , P_ ( " monster " , " Pukerat the Unclean " ) , " ptu " , 2 , 77 , MonsterAIID : : Fallen , 3 , 1 , 5 , RESIST_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_WSKELAX , P_ ( " monster " , " Boneripper " ) , " br " , 2 , 54 , MonsterAIID : : Bat , 0 , 6 , 15 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_NZOMBIE , P_ ( " monster " , " Rotfeast the Hungry " ) , " eth " , 2 , 85 , MonsterAIID : : SkeletonMelee , 3 , 4 , 12 , IMMUNE_MAGIC , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_DFALLSD , P_ ( " monster " , " Gutshank the Quick " ) , " gtq " , 3 , 66 , MonsterAIID : : Bat , 2 , 6 , 16 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_TSKELSD , P_ ( " monster " , " Brokenhead Bangshield " ) , " bhbs " , 3 , 108 , MonsterAIID : : SkeletonMelee , 3 , 12 , 20 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_YFALLSP , P_ ( " monster " , " Bongo " ) , " bng " , 3 , 178 , MonsterAIID : : Fallen , 3 , 9 , 21 , 0 , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BZOMBIE , P_ ( " monster " , " Rotcarnage " ) , " rcrn " , 3 , 102 , MonsterAIID : : Zombie , 3 , 9 , 24 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 45 , TEXT_NONE } ,
{ MT_NSCAV , P_ ( " monster " , " Shadowbite " ) , " shbt " , 2 , 60 , MonsterAIID : : SkeletonMelee , 3 , 3 , 20 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_WSKELBW , P_ ( " monster " , " Deadeye " ) , " de " , 2 , 49 , MonsterAIID : : GoatRanged , 0 , 6 , 9 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_RSKELAX , P_ ( " monster " , " Madeye the Dead " ) , " mtd " , 4 , 75 , MonsterAIID : : Bat , 0 , 9 , 21 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 30 , TEXT_NONE } ,
{ MT_BSCAV , P_ ( " monster " , " El Chupacabras " ) , " general " , 3 , 120 , MonsterAIID : : GoatMelee , 0 , 10 , 18 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_TSKELBW , P_ ( " monster " , " Skullfire " ) , " skfr " , 3 , 125 , MonsterAIID : : GoatRanged , 1 , 6 , 10 , IMMUNE_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_SNEAK , P_ ( " monster " , " Warpskull " ) , " tspo " , 3 , 117 , MonsterAIID : : Sneak , 2 , 6 , 18 , RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_GZOMBIE , P_ ( " monster " , " Goretongue " ) , " pmr " , 3 , 156 , MonsterAIID : : SkeletonMelee , 1 , 15 , 30 , IMMUNE_MAGIC , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_WSCAV , P_ ( " monster " , " Pulsecrawler " ) , " bhka " , 4 , 150 , MonsterAIID : : Scavenger , 0 , 16 , 20 , IMMUNE_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 45 , TEXT_NONE } ,
{ MT_BLINK , P_ ( " monster " , " Moonbender " ) , " general " , 4 , 135 , MonsterAIID : : Bat , 0 , 9 , 27 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BLINK , P_ ( " monster " , " Wrathraven " ) , " general " , 5 , 135 , MonsterAIID : : Bat , 2 , 9 , 22 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_YSCAV , P_ ( " monster " , " Spineeater " ) , " general " , 4 , 180 , MonsterAIID : : Scavenger , 1 , 18 , 25 , IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RSKELBW , P_ ( " monster " , " Blackash the Burning " ) , " bashtb " , 4 , 120 , MonsterAIID : : GoatRanged , 0 , 6 , 16 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BFALLSD , P_ ( " monster " , " Shadowcrow " ) , " general " , 5 , 270 , MonsterAIID : : Sneak , 2 , 12 , 25 , 0 , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_LRDSAYTR , P_ ( " monster " , " Blightstone the Weak " ) , " bhka " , 4 , 360 , MonsterAIID : : SkeletonMelee , 0 , 4 , 12 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 70 , 0 , TEXT_NONE } ,
{ MT_FAT , P_ ( " monster " , " Bilefroth the Pit Master " ) , " bftp " , 6 , 210 , MonsterAIID : : Bat , 1 , 16 , 23 , IMMUNE_MAGIC | IMMUNE_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_NGOATBW , P_ ( " monster " , " Bloodskin Darkbow " ) , " bsdb " , 5 , 207 , MonsterAIID : : GoatRanged , 0 , 3 , 16 , RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 55 , TEXT_NONE } ,
{ MT_GLOOM , P_ ( " monster " , " Foulwing " ) , " db " , 5 , 246 , MonsterAIID : : Rhino , 3 , 12 , 28 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_XSKELSD , P_ ( " monster " , " Shadowdrinker " ) , " shdr " , 5 , 300 , MonsterAIID : : Sneak , 1 , 18 , 26 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 45 , TEXT_NONE } ,
{ MT_UNSEEN , P_ ( " monster " , " Hazeshifter " ) , " bhka " , 5 , 285 , MonsterAIID : : Sneak , 3 , 18 , 30 , IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_NACID , P_ ( " monster " , " Deathspit " ) , " bfds " , 6 , 303 , MonsterAIID : : AcidUnique , 0 , 12 , 32 , RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RGOATMC , P_ ( " monster " , " Bloodgutter " ) , " bgbl " , 6 , 315 , MonsterAIID : : Bat , 1 , 24 , 34 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BGOATMC , P_ ( " monster " , " Deathshade Fleshmaul " ) , " dsfm " , 6 , 276 , MonsterAIID : : Rhino , 0 , 12 , 24 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : None , 0 , 65 , TEXT_NONE } ,
{ MT_WYRM , P_ ( " monster " , " Warmaggot the Mad " ) , " general " , 6 , 246 , MonsterAIID : : Bat , 3 , 15 , 30 , RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_STORM , P_ ( " monster " , " Glasskull the Jagged " ) , " bhka " , 7 , 354 , MonsterAIID : : Storm , 0 , 18 , 30 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RGOATBW , P_ ( " monster " , " Blightfire " ) , " blf " , 7 , 321 , MonsterAIID : : Succubus , 2 , 13 , 21 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_GARGOYLE , P_ ( " monster " , " Nightwing the Cold " ) , " general " , 7 , 342 , MonsterAIID : : Bat , 1 , 18 , 26 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_GGOATBW , P_ ( " monster " , " Gorestone " ) , " general " , 7 , 303 , MonsterAIID : : GoatRanged , 1 , 15 , 28 , RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 70 , 0 , TEXT_NONE } ,
{ MT_BMAGMA , P_ ( " monster " , " Bronzefist Firestone " ) , " general " , 8 , 360 , MonsterAIID : : Magma , 0 , 30 , 36 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_INCIN , P_ ( " monster " , " Wrathfire the Doomed " ) , " wftd " , 8 , 270 , MonsterAIID : : SkeletonMelee , 2 , 20 , 30 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_NMAGMA , P_ ( " monster " , " Firewound the Grim " ) , " bhka " , 8 , 303 , MonsterAIID : : Magma , 0 , 18 , 22 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_MUDMAN , P_ ( " monster " , " Baron Sludge " ) , " bsm " , 8 , 315 , MonsterAIID : : Sneak , 3 , 25 , 34 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 75 , TEXT_NONE } ,
{ MT_GGOATMC , P_ ( " monster " , " Blighthorn Steelmace " ) , " bhsm " , 7 , 250 , MonsterAIID : : Rhino , 0 , 20 , 28 , RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 45 , TEXT_NONE } ,
{ MT_RACID , P_ ( " monster " , " Chaoshowler " ) , " general " , 8 , 240 , MonsterAIID : : AcidUnique , 0 , 12 , 20 , 0 , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_REDDTH , P_ ( " monster " , " Doomgrin the Rotting " ) , " general " , 8 , 405 , MonsterAIID : : Storm , 3 , 25 , 50 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_FLAMLRD , P_ ( " monster " , " Madburner " ) , " general " , 9 , 270 , MonsterAIID : : Storm , 0 , 20 , 40 , IMMUNE_MAGIC | IMMUNE_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_LTCHDMN , P_ ( " monster " , " Bonesaw the Litch " ) , " general " , 9 , 495 , MonsterAIID : : Storm , 2 , 30 , 55 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_MUDRUN , P_ ( " monster " , " Breakspine " ) , " general " , 9 , 351 , MonsterAIID : : Rhino , 0 , 25 , 34 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_REDDTH , P_ ( " monster " , " Devilskull Sharpbone " ) , " general " , 9 , 444 , MonsterAIID : : Storm , 1 , 25 , 40 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_STORM , P_ ( " monster " , " Brokenstorm " ) , " general " , 9 , 411 , MonsterAIID : : Storm , 2 , 25 , 36 , IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RSTORM , P_ ( " monster " , " Stormbane " ) , " general " , 9 , 555 , MonsterAIID : : Storm , 3 , 30 , 30 , IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_TOAD , P_ ( " monster " , " Oozedrool " ) , " general " , 9 , 483 , MonsterAIID : : Fat , 3 , 25 , 30 , RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BLOODCLW , P_ ( " monster " , " Goldblight of the Flame " ) , " general " , 10 , 405 , MonsterAIID : : Gargoyle , 0 , 15 , 35 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 80 , TEXT_NONE } ,
{ MT_OBLORD , P_ ( " monster " , " Blackstorm " ) , " general " , 10 , 525 , MonsterAIID : : Rhino , 3 , 20 , 40 , IMMUNE_MAGIC | IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 90 , TEXT_NONE } ,
{ MT_RACID , P_ ( " monster " , " Plaguewrath " ) , " general " , 10 , 450 , MonsterAIID : : AcidUnique , 2 , 20 , 30 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RSTORM , P_ ( " monster " , " The Flayer " ) , " general " , 10 , 501 , MonsterAIID : : Storm , 1 , 20 , 35 , RESIST_MAGIC | RESIST_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_FROSTC , P_ ( " monster " , " Bluehorn " ) , " general " , 11 , 477 , MonsterAIID : : Rhino , 1 , 25 , 30 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 90 , TEXT_NONE } ,
{ MT_HELLBURN , P_ ( " monster " , " Warpfire Hellspawn " ) , " general " , 11 , 525 , MonsterAIID : : FireMan , 3 , 10 , 40 , RESIST_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_NSNAKE , P_ ( " monster " , " Fangspeir " ) , " general " , 11 , 444 , MonsterAIID : : SkeletonMelee , 1 , 15 , 32 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_UDEDBLRG , P_ ( " monster " , " Festerskull " ) , " general " , 11 , 600 , MonsterAIID : : Storm , 2 , 15 , 30 , IMMUNE_MAGIC , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_NBLACK , P_ ( " monster " , " Lionskull the Bent " ) , " general " , 12 , 525 , MonsterAIID : : SkeletonMelee , 2 , 25 , 25 , IMMUNE_MAGIC | IMMUNE_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_COUNSLR , P_ ( " monster " , " Blacktongue " ) , " general " , 12 , 360 , MonsterAIID : : Counselor , 3 , 15 , 30 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_DEATHW , P_ ( " monster " , " Viletouch " ) , " general " , 12 , 525 , MonsterAIID : : Gargoyle , 3 , 20 , 40 , IMMUNE_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RSNAKE , P_ ( " monster " , " Viperflame " ) , " general " , 12 , 570 , MonsterAIID : : SkeletonMelee , 1 , 25 , 35 , IMMUNE_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BSNAKE , P_ ( " monster " , " Fangskin " ) , " bhka " , 14 , 681 , MonsterAIID : : SkeletonMelee , 2 , 15 , 50 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_SUCCUBUS , P_ ( " monster " , " Witchfire the Unholy " ) , " general " , 12 , 444 , MonsterAIID : : Succubus , 3 , 10 , 20 , IMMUNE_MAGIC | IMMUNE_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_BALROG , P_ ( " monster " , " Blackskull " ) , " bhka " , 13 , 750 , MonsterAIID : : SkeletonMelee , 3 , 25 , 40 , IMMUNE_MAGIC | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_UNRAV , P_ ( " monster " , " Soulslash " ) , " general " , 12 , 450 , MonsterAIID : : SkeletonMelee , 0 , 25 , 25 , IMMUNE_MAGIC , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_VTEXLRD , P_ ( " monster " , " Windspawn " ) , " general " , 12 , 711 , MonsterAIID : : SkeletonMelee , 1 , 35 , 40 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_GSNAKE , P_ ( " monster " , " Lord of the Pit " ) , " general " , 13 , 762 , MonsterAIID : : SkeletonMelee , 2 , 25 , 42 , RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_RTBLACK , P_ ( " monster " , " Rustweaver " ) , " general " , 13 , 400 , MonsterAIID : : SkeletonMelee , 3 , 1 , 60 , IMMUNE_MAGIC | IMMUNE_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_HOLOWONE , P_ ( " monster " , " Howlingire the Shade " ) , " general " , 13 , 450 , MonsterAIID : : SkeletonMelee , 2 , 40 , 75 , RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_MAEL , P_ ( " monster " , " Doomcloud " ) , " general " , 13 , 612 , MonsterAIID : : Storm , 1 , 1 , 60 , RESIST_FIRE | IMMUNE_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_PAINMSTR , P_ ( " monster " , " Bloodmoon Soulfire " ) , " general " , 13 , 684 , MonsterAIID : : SkeletonMelee , 1 , 15 , 40 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_SNOWWICH , P_ ( " monster " , " Witchmoon " ) , " general " , 13 , 310 , MonsterAIID : : Succubus , 3 , 30 , 40 , RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_VTEXLRD , P_ ( " monster " , " Gorefeast " ) , " general " , 13 , 771 , MonsterAIID : : SkeletonMelee , 3 , 20 , 55 , RESIST_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_RTBLACK , P_ ( " monster " , " Graywar the Slayer " ) , " general " , 14 , 672 , MonsterAIID : : SkeletonMelee , 1 , 30 , 50 , RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_MAGISTR , P_ ( " monster " , " Dreadjudge " ) , " general " , 14 , 540 , MonsterAIID : : Counselor , 1 , 30 , 40 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_HLSPWN , P_ ( " monster " , " Stareye the Witch " ) , " general " , 14 , 726 , MonsterAIID : : Succubus , 2 , 30 , 50 , IMMUNE_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_BTBLACK , P_ ( " monster " , " Steelskull the Hunter " ) , " general " , 14 , 831 , MonsterAIID : : SkeletonMelee , 3 , 40 , 50 , RESIST_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_RBLACK , P_ ( " monster " , " Sir Gorash " ) , " general " , 16 , 1050 , MonsterAIID : : SkeletonMelee , 1 , 20 , 60 , 0 , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_CABALIST , P_ ( " monster " , " The Vizier " ) , " general " , 15 , 850 , MonsterAIID : : Counselor , 2 , 25 , 40 , IMMUNE_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_REALWEAV , P_ ( " monster " , " Zamphir " ) , " general " , 15 , 891 , MonsterAIID : : SkeletonMelee , 2 , 30 , 50 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_HLSPWN , P_ ( " monster " , " Bloodlust " ) , " general " , 15 , 825 , MonsterAIID : : Succubus , 1 , 20 , 55 , IMMUNE_MAGIC | IMMUNE_LIGHTNING , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_HLSPWN , P_ ( " monster " , " Webwidow " ) , " general " , 16 , 774 , MonsterAIID : : Succubus , 1 , 20 , 50 , IMMUNE_MAGIC | IMMUNE_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_SOLBRNR , P_ ( " monster " , " Fleshdancer " ) , " general " , 16 , 999 , MonsterAIID : : Succubus , 3 , 30 , 50 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
{ MT_OBLORD , P_ ( " monster " , " Grimspike " ) , " general " , 19 , 534 , MonsterAIID : : Sneak , 1 , 25 , 40 , IMMUNE_MAGIC | RESIST_FIRE , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
// TRANSLATORS: Unique Monster Block end
{ MT_STORML , P_ ( " monster " , " Doomlock " ) , " general " , 28 , 534 , MonsterAIID : : Sneak , 1 , 35 , 55 , IMMUNE_MAGIC | RESIST_FIRE | RESIST_LIGHTNING , UniqueMonsterPack : : Leashed , 0 , 0 , TEXT_NONE } ,
{ MT_INVALID , nullptr , nullptr , 0 , 0 , MonsterAIID : : Invalid , 0 , 0 , 0 , 0 , UniqueMonsterPack : : None , 0 , 0 , TEXT_NONE } ,
// clang-format on
} ;
} // namespace devilution