Browse Source

Migrate unique monsters to data files

pull/6801/head
Gleb Mazovetskiy 2 years ago
parent
commit
99fea0d1c5
  1. 3
      .editorconfig
  2. 1
      CMake/Assets.cmake
  3. 101
      Packaging/resources/assets/txtdata/monsters/unique_monstdat.tsv
  4. 9
      Source/data/file.cpp
  5. 4
      Source/data/file.hpp
  6. 4
      Source/lua/modules/dev/monsters.cpp
  7. 632
      Source/monstdat.cpp
  8. 6
      Source/monstdat.h
  9. 2
      Source/monster.cpp
  10. 3
      Source/pack.cpp
  11. 100
      Source/translation_dummy.cpp
  12. 13
      tools/extract_translation_data.py

3
.editorconfig

@ -58,6 +58,9 @@ end_of_line = lf
[*.txt]
end_of_line = crlf
[*.tsv]
trim_trailing_whitespace = false
[AppRun]
end_of_line = lf

1
CMake/Assets.cmake

@ -156,6 +156,7 @@ set(devilutionx_assets
txtdata/classes/sorcerer/attributes.tsv
txtdata/classes/warrior/attributes.tsv
txtdata/monsters/monstdat.tsv
txtdata/monsters/unique_monstdat.tsv
ui_art/diablo.pal
ui_art/hellfire.pal
ui_art/creditsw.clx

101
Packaging/resources/assets/txtdata/monsters/unique_monstdat.tsv

@ -0,0 +1,101 @@
type name trn level maxHp ai intelligence minDamage maxDamage resistance monsterPack customToHit customArmorClass talkMessage
MT_NGOATMC Gharbad the Weak bsdb 4 120 Gharbad 3 8 16 IMMUNE_LIGHTNING None 0 0 TEXT_GARBUD1
MT_SKING Skeleton King genrl 0 240 SkeletonKing 3 6 16 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Independent 0 0
MT_COUNSLR Zhar the Mad general 8 360 Zhar 3 16 40 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING None 0 0 TEXT_ZHAR1
MT_BFALLSP Snotspill bng 4 220 Snotspill 3 10 18 RESIST_LIGHTNING None 0 0 TEXT_BANNER10
MT_ADVOCATE Arch-Bishop Lazarus general 0 600 Lazarus 3 30 50 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING None 0 0 TEXT_VILE13
MT_HLSPWN Red Vex redv 0 400 LazarusSuccubus 3 30 50 IMMUNE_MAGIC,RESIST_FIRE None 0 0 TEXT_VILE13
MT_HLSPWN Black Jade blkjd 0 400 LazarusSuccubus 3 30 50 IMMUNE_MAGIC,RESIST_LIGHTNING None 0 0 TEXT_VILE13
MT_RBLACK Lachdanan bhka 14 500 Lachdanan 3 0 0 None 0 0 TEXT_VEIL9
MT_BTBLACK Warlord of Blood general 13 850 Warlord 3 35 50 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING None 0 0 TEXT_WARLRD9
MT_CLEAVER The Butcher genrl 0 220 Butcher 3 6 12 RESIST_FIRE,RESIST_LIGHTNING None 0 0
MT_HORKDMN Hork Demon genrl 19 300 HorkDemon 3 20 35 RESIST_LIGHTNING None 0 0
MT_DEFILER The Defiler genrl 20 480 SkeletonMelee 3 30 40 RESIST_MAGIC,RESIST_FIRE,IMMUNE_LIGHTNING None 0 0
MT_NAKRUL Na-Krul genrl 0 1332 SkeletonMelee 3 40 50 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING Leashed 0 0
MT_TSKELAX Bonehead Keenaxe bhka 2 91 SkeletonMelee 2 4 10 IMMUNE_MAGIC Leashed 100 0
MT_RFALLSD Bladeskin the Slasher bsts 2 51 Fallen 0 6 18 RESIST_FIRE Leashed 0 45
MT_NZOMBIE Soulpus general 2 133 Zombie 0 4 8 RESIST_FIRE,RESIST_LIGHTNING None 0 0
MT_RFALLSP Pukerat the Unclean ptu 2 77 Fallen 3 1 5 RESIST_FIRE None 0 0
MT_WSKELAX Boneripper br 2 54 Bat 0 6 15 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
MT_NZOMBIE Rotfeast the Hungry eth 2 85 SkeletonMelee 3 4 12 IMMUNE_MAGIC Leashed 0 0
MT_DFALLSD Gutshank the Quick gtq 3 66 Bat 2 6 16 RESIST_FIRE Leashed 0 0
MT_TSKELSD Brokenhead Bangshield bhbs 3 108 SkeletonMelee 3 12 20 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
MT_YFALLSP Bongo bng 3 178 Fallen 3 9 21 Leashed 0 0
MT_BZOMBIE Rotcarnage rcrn 3 102 Zombie 3 9 24 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 45
MT_NSCAV Shadowbite shbt 2 60 SkeletonMelee 3 3 20 IMMUNE_FIRE Leashed 0 0
MT_WSKELBW Deadeye de 2 49 GoatRanged 0 6 9 IMMUNE_MAGIC,RESIST_FIRE None 0 0
MT_RSKELAX Madeye the Dead mtd 4 75 Bat 0 9 21 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 30
MT_BSCAV El Chupacabras general 3 120 GoatMelee 0 10 18 RESIST_FIRE Leashed 0 0
MT_TSKELBW Skullfire skfr 3 125 GoatRanged 1 6 10 IMMUNE_FIRE None 0 0
MT_SNEAK Warpskull tspo 3 117 Sneak 2 6 18 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_GZOMBIE Goretongue pmr 3 156 SkeletonMelee 1 15 30 IMMUNE_MAGIC None 0 0
MT_WSCAV Pulsecrawler bhka 4 150 Scavenger 0 16 20 IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 45
MT_BLINK Moonbender general 4 135 Bat 0 9 27 IMMUNE_FIRE Leashed 0 0
MT_BLINK Wrathraven general 5 135 Bat 2 9 22 IMMUNE_FIRE Leashed 0 0
MT_YSCAV Spineeater general 4 180 Scavenger 1 18 25 IMMUNE_LIGHTNING Leashed 0 0
MT_RSKELBW Blackash the Burning bashtb 4 120 GoatRanged 0 6 16 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
MT_BFALLSD Shadowcrow general 5 270 Sneak 2 12 25 Leashed 0 0
MT_LRDSAYTR Blightstone the Weak bhka 4 360 SkeletonMelee 0 4 12 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 70 0
MT_FAT Bilefroth the Pit Master bftp 6 210 Bat 1 16 23 IMMUNE_MAGIC,IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_NGOATBW Bloodskin Darkbow bsdb 5 207 GoatRanged 0 3 16 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 55
MT_GLOOM Foulwing db 5 246 Rhino 3 12 28 RESIST_FIRE Leashed 0 0
MT_XSKELSD Shadowdrinker shdr 5 300 Sneak 1 18 26 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING None 0 45
MT_UNSEEN Hazeshifter bhka 5 285 Sneak 3 18 30 IMMUNE_LIGHTNING Leashed 0 0
MT_NACID Deathspit bfds 6 303 AcidUnique 0 12 32 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_RGOATMC Bloodgutter bgbl 6 315 Bat 1 24 34 IMMUNE_FIRE Leashed 0 0
MT_BGOATMC Deathshade Fleshmaul dsfm 6 276 Rhino 0 12 24 IMMUNE_MAGIC,RESIST_FIRE None 0 65
MT_WYRM Warmaggot the Mad general 6 246 Bat 3 15 30 RESIST_LIGHTNING Leashed 0 0
MT_STORM Glasskull the Jagged bhka 7 354 Storm 0 18 30 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
MT_RGOATBW Blightfire blf 7 321 Succubus 2 13 21 IMMUNE_FIRE Leashed 0 0
MT_GARGOYLE Nightwing the Cold general 7 342 Bat 1 18 26 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
MT_GGOATBW Gorestone general 7 303 GoatRanged 1 15 28 RESIST_LIGHTNING Leashed 70 0
MT_BMAGMA Bronzefist Firestone general 8 360 Magma 0 30 36 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
MT_INCIN Wrathfire the Doomed wftd 8 270 SkeletonMelee 2 20 30 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_NMAGMA Firewound the Grim bhka 8 303 Magma 0 18 22 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
MT_MUDMAN Baron Sludge bsm 8 315 Sneak 3 25 34 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 75
MT_GGOATMC Blighthorn Steelmace bhsm 7 250 Rhino 0 20 28 RESIST_LIGHTNING Leashed 0 45
MT_RACID Chaoshowler general 8 240 AcidUnique 0 12 20 Leashed 0 0
MT_REDDTH Doomgrin the Rotting general 8 405 Storm 3 25 50 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_FLAMLRD Madburner general 9 270 Storm 0 20 40 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING Leashed 0 0
MT_LTCHDMN Bonesaw the Litch general 9 495 Storm 2 30 55 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_MUDRUN Breakspine general 9 351 Rhino 0 25 34 RESIST_FIRE Leashed 0 0
MT_REDDTH Devilskull Sharpbone general 9 444 Storm 1 25 40 IMMUNE_FIRE Leashed 0 0
MT_STORM Brokenstorm general 9 411 Storm 2 25 36 IMMUNE_LIGHTNING Leashed 0 0
MT_RSTORM Stormbane general 9 555 Storm 3 30 30 IMMUNE_LIGHTNING Leashed 0 0
MT_TOAD Oozedrool general 9 483 Fat 3 25 30 RESIST_LIGHTNING Leashed 0 0
MT_BLOODCLW Goldblight of the Flame general 10 405 Gargoyle 0 15 35 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 80
MT_OBLORD Blackstorm general 10 525 Rhino 3 20 40 IMMUNE_MAGIC,IMMUNE_LIGHTNING Leashed 0 90
MT_RACID Plaguewrath general 10 450 AcidUnique 2 20 30 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
MT_RSTORM The Flayer general 10 501 Storm 1 20 35 RESIST_MAGIC,RESIST_FIRE,IMMUNE_LIGHTNING Leashed 0 0
MT_FROSTC Bluehorn general 11 477 Rhino 1 25 30 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 90
MT_HELLBURN Warpfire Hellspawn general 11 525 FireMan 3 10 40 RESIST_MAGIC,IMMUNE_FIRE Leashed 0 0
MT_NSNAKE Fangspeir general 11 444 SkeletonMelee 1 15 32 IMMUNE_FIRE Leashed 0 0
MT_UDEDBLRG Festerskull general 11 600 Storm 2 15 30 IMMUNE_MAGIC Leashed 0 0
MT_NBLACK Lionskull the Bent general 12 525 SkeletonMelee 2 25 25 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING Leashed 0 0
MT_COUNSLR Blacktongue general 12 360 Counselor 3 15 30 RESIST_FIRE Leashed 0 0
MT_DEATHW Viletouch general 12 525 Gargoyle 3 20 40 IMMUNE_LIGHTNING Leashed 0 0
MT_RSNAKE Viperflame general 12 570 SkeletonMelee 1 25 35 IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_BSNAKE Fangskin bhka 14 681 SkeletonMelee 2 15 50 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
MT_SUCCUBUS Witchfire the Unholy general 12 444 Succubus 3 10 20 IMMUNE_MAGIC,IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_BALROG Blackskull bhka 13 750 SkeletonMelee 3 25 40 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
MT_UNRAV Soulslash general 12 450 SkeletonMelee 0 25 25 IMMUNE_MAGIC Leashed 0 0
MT_VTEXLRD Windspawn general 12 711 SkeletonMelee 1 35 40 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
MT_GSNAKE Lord of the Pit general 13 762 SkeletonMelee 2 25 42 RESIST_FIRE Leashed 0 0
MT_RTBLACK Rustweaver general 13 400 SkeletonMelee 3 1 60 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING None 0 0
MT_HOLOWONE Howlingire the Shade general 13 450 SkeletonMelee 2 40 75 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_MAEL Doomcloud general 13 612 Storm 1 1 60 RESIST_FIRE,IMMUNE_LIGHTNING None 0 0
MT_PAINMSTR Bloodmoon Soulfire general 13 684 SkeletonMelee 1 15 40 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_SNOWWICH Witchmoon general 13 310 Succubus 3 30 40 RESIST_LIGHTNING None 0 0
MT_VTEXLRD Gorefeast general 13 771 SkeletonMelee 3 20 55 RESIST_FIRE None 0 0
MT_RTBLACK Graywar the Slayer general 14 672 SkeletonMelee 1 30 50 RESIST_LIGHTNING None 0 0
MT_MAGISTR Dreadjudge general 14 540 Counselor 1 30 40 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_HLSPWN Stareye the Witch general 14 726 Succubus 2 30 50 IMMUNE_FIRE None 0 0
MT_BTBLACK Steelskull the Hunter general 14 831 SkeletonMelee 3 40 50 RESIST_LIGHTNING None 0 0
MT_RBLACK Sir Gorash general 16 1050 SkeletonMelee 1 20 60 None 0 0
MT_CABALIST The Vizier general 15 850 Counselor 2 25 40 IMMUNE_FIRE Leashed 0 0
MT_REALWEAV Zamphir general 15 891 SkeletonMelee 2 30 50 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
MT_HLSPWN Bloodlust general 15 825 Succubus 1 20 55 IMMUNE_MAGIC,IMMUNE_LIGHTNING None 0 0
MT_HLSPWN Webwidow general 16 774 Succubus 1 20 50 IMMUNE_MAGIC,IMMUNE_FIRE None 0 0
MT_SOLBRNR Fleshdancer general 16 999 Succubus 3 30 50 IMMUNE_MAGIC,RESIST_FIRE None 0 0
MT_OBLORD Grimspike general 19 534 Sneak 1 25 40 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
MT_STORML Doomlock general 28 534 Sneak 1 35 55 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
1 type name trn level maxHp ai intelligence minDamage maxDamage resistance monsterPack customToHit customArmorClass talkMessage
2 MT_NGOATMC Gharbad the Weak bsdb 4 120 Gharbad 3 8 16 IMMUNE_LIGHTNING None 0 0 TEXT_GARBUD1
3 MT_SKING Skeleton King genrl 0 240 SkeletonKing 3 6 16 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Independent 0 0
4 MT_COUNSLR Zhar the Mad general 8 360 Zhar 3 16 40 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING None 0 0 TEXT_ZHAR1
5 MT_BFALLSP Snotspill bng 4 220 Snotspill 3 10 18 RESIST_LIGHTNING None 0 0 TEXT_BANNER10
6 MT_ADVOCATE Arch-Bishop Lazarus general 0 600 Lazarus 3 30 50 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING None 0 0 TEXT_VILE13
7 MT_HLSPWN Red Vex redv 0 400 LazarusSuccubus 3 30 50 IMMUNE_MAGIC,RESIST_FIRE None 0 0 TEXT_VILE13
8 MT_HLSPWN Black Jade blkjd 0 400 LazarusSuccubus 3 30 50 IMMUNE_MAGIC,RESIST_LIGHTNING None 0 0 TEXT_VILE13
9 MT_RBLACK Lachdanan bhka 14 500 Lachdanan 3 0 0 None 0 0 TEXT_VEIL9
10 MT_BTBLACK Warlord of Blood general 13 850 Warlord 3 35 50 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING None 0 0 TEXT_WARLRD9
11 MT_CLEAVER The Butcher genrl 0 220 Butcher 3 6 12 RESIST_FIRE,RESIST_LIGHTNING None 0 0
12 MT_HORKDMN Hork Demon genrl 19 300 HorkDemon 3 20 35 RESIST_LIGHTNING None 0 0
13 MT_DEFILER The Defiler genrl 20 480 SkeletonMelee 3 30 40 RESIST_MAGIC,RESIST_FIRE,IMMUNE_LIGHTNING None 0 0
14 MT_NAKRUL Na-Krul genrl 0 1332 SkeletonMelee 3 40 50 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING Leashed 0 0
15 MT_TSKELAX Bonehead Keenaxe bhka 2 91 SkeletonMelee 2 4 10 IMMUNE_MAGIC Leashed 100 0
16 MT_RFALLSD Bladeskin the Slasher bsts 2 51 Fallen 0 6 18 RESIST_FIRE Leashed 0 45
17 MT_NZOMBIE Soulpus general 2 133 Zombie 0 4 8 RESIST_FIRE,RESIST_LIGHTNING None 0 0
18 MT_RFALLSP Pukerat the Unclean ptu 2 77 Fallen 3 1 5 RESIST_FIRE None 0 0
19 MT_WSKELAX Boneripper br 2 54 Bat 0 6 15 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
20 MT_NZOMBIE Rotfeast the Hungry eth 2 85 SkeletonMelee 3 4 12 IMMUNE_MAGIC Leashed 0 0
21 MT_DFALLSD Gutshank the Quick gtq 3 66 Bat 2 6 16 RESIST_FIRE Leashed 0 0
22 MT_TSKELSD Brokenhead Bangshield bhbs 3 108 SkeletonMelee 3 12 20 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
23 MT_YFALLSP Bongo bng 3 178 Fallen 3 9 21 Leashed 0 0
24 MT_BZOMBIE Rotcarnage rcrn 3 102 Zombie 3 9 24 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 45
25 MT_NSCAV Shadowbite shbt 2 60 SkeletonMelee 3 3 20 IMMUNE_FIRE Leashed 0 0
26 MT_WSKELBW Deadeye de 2 49 GoatRanged 0 6 9 IMMUNE_MAGIC,RESIST_FIRE None 0 0
27 MT_RSKELAX Madeye the Dead mtd 4 75 Bat 0 9 21 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 30
28 MT_BSCAV El Chupacabras general 3 120 GoatMelee 0 10 18 RESIST_FIRE Leashed 0 0
29 MT_TSKELBW Skullfire skfr 3 125 GoatRanged 1 6 10 IMMUNE_FIRE None 0 0
30 MT_SNEAK Warpskull tspo 3 117 Sneak 2 6 18 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
31 MT_GZOMBIE Goretongue pmr 3 156 SkeletonMelee 1 15 30 IMMUNE_MAGIC None 0 0
32 MT_WSCAV Pulsecrawler bhka 4 150 Scavenger 0 16 20 IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 45
33 MT_BLINK Moonbender general 4 135 Bat 0 9 27 IMMUNE_FIRE Leashed 0 0
34 MT_BLINK Wrathraven general 5 135 Bat 2 9 22 IMMUNE_FIRE Leashed 0 0
35 MT_YSCAV Spineeater general 4 180 Scavenger 1 18 25 IMMUNE_LIGHTNING Leashed 0 0
36 MT_RSKELBW Blackash the Burning bashtb 4 120 GoatRanged 0 6 16 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
37 MT_BFALLSD Shadowcrow general 5 270 Sneak 2 12 25 Leashed 0 0
38 MT_LRDSAYTR Blightstone the Weak bhka 4 360 SkeletonMelee 0 4 12 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 70 0
39 MT_FAT Bilefroth the Pit Master bftp 6 210 Bat 1 16 23 IMMUNE_MAGIC,IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 0
40 MT_NGOATBW Bloodskin Darkbow bsdb 5 207 GoatRanged 0 3 16 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 55
41 MT_GLOOM Foulwing db 5 246 Rhino 3 12 28 RESIST_FIRE Leashed 0 0
42 MT_XSKELSD Shadowdrinker shdr 5 300 Sneak 1 18 26 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING None 0 45
43 MT_UNSEEN Hazeshifter bhka 5 285 Sneak 3 18 30 IMMUNE_LIGHTNING Leashed 0 0
44 MT_NACID Deathspit bfds 6 303 AcidUnique 0 12 32 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
45 MT_RGOATMC Bloodgutter bgbl 6 315 Bat 1 24 34 IMMUNE_FIRE Leashed 0 0
46 MT_BGOATMC Deathshade Fleshmaul dsfm 6 276 Rhino 0 12 24 IMMUNE_MAGIC,RESIST_FIRE None 0 65
47 MT_WYRM Warmaggot the Mad general 6 246 Bat 3 15 30 RESIST_LIGHTNING Leashed 0 0
48 MT_STORM Glasskull the Jagged bhka 7 354 Storm 0 18 30 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
49 MT_RGOATBW Blightfire blf 7 321 Succubus 2 13 21 IMMUNE_FIRE Leashed 0 0
50 MT_GARGOYLE Nightwing the Cold general 7 342 Bat 1 18 26 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
51 MT_GGOATBW Gorestone general 7 303 GoatRanged 1 15 28 RESIST_LIGHTNING Leashed 70 0
52 MT_BMAGMA Bronzefist Firestone general 8 360 Magma 0 30 36 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
53 MT_INCIN Wrathfire the Doomed wftd 8 270 SkeletonMelee 2 20 30 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
54 MT_NMAGMA Firewound the Grim bhka 8 303 Magma 0 18 22 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
55 MT_MUDMAN Baron Sludge bsm 8 315 Sneak 3 25 34 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 75
56 MT_GGOATMC Blighthorn Steelmace bhsm 7 250 Rhino 0 20 28 RESIST_LIGHTNING Leashed 0 45
57 MT_RACID Chaoshowler general 8 240 AcidUnique 0 12 20 Leashed 0 0
58 MT_REDDTH Doomgrin the Rotting general 8 405 Storm 3 25 50 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
59 MT_FLAMLRD Madburner general 9 270 Storm 0 20 40 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING Leashed 0 0
60 MT_LTCHDMN Bonesaw the Litch general 9 495 Storm 2 30 55 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
61 MT_MUDRUN Breakspine general 9 351 Rhino 0 25 34 RESIST_FIRE Leashed 0 0
62 MT_REDDTH Devilskull Sharpbone general 9 444 Storm 1 25 40 IMMUNE_FIRE Leashed 0 0
63 MT_STORM Brokenstorm general 9 411 Storm 2 25 36 IMMUNE_LIGHTNING Leashed 0 0
64 MT_RSTORM Stormbane general 9 555 Storm 3 30 30 IMMUNE_LIGHTNING Leashed 0 0
65 MT_TOAD Oozedrool general 9 483 Fat 3 25 30 RESIST_LIGHTNING Leashed 0 0
66 MT_BLOODCLW Goldblight of the Flame general 10 405 Gargoyle 0 15 35 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 80
67 MT_OBLORD Blackstorm general 10 525 Rhino 3 20 40 IMMUNE_MAGIC,IMMUNE_LIGHTNING Leashed 0 90
68 MT_RACID Plaguewrath general 10 450 AcidUnique 2 20 30 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
69 MT_RSTORM The Flayer general 10 501 Storm 1 20 35 RESIST_MAGIC,RESIST_FIRE,IMMUNE_LIGHTNING Leashed 0 0
70 MT_FROSTC Bluehorn general 11 477 Rhino 1 25 30 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 90
71 MT_HELLBURN Warpfire Hellspawn general 11 525 FireMan 3 10 40 RESIST_MAGIC,IMMUNE_FIRE Leashed 0 0
72 MT_NSNAKE Fangspeir general 11 444 SkeletonMelee 1 15 32 IMMUNE_FIRE Leashed 0 0
73 MT_UDEDBLRG Festerskull general 11 600 Storm 2 15 30 IMMUNE_MAGIC Leashed 0 0
74 MT_NBLACK Lionskull the Bent general 12 525 SkeletonMelee 2 25 25 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING Leashed 0 0
75 MT_COUNSLR Blacktongue general 12 360 Counselor 3 15 30 RESIST_FIRE Leashed 0 0
76 MT_DEATHW Viletouch general 12 525 Gargoyle 3 20 40 IMMUNE_LIGHTNING Leashed 0 0
77 MT_RSNAKE Viperflame general 12 570 SkeletonMelee 1 25 35 IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 0
78 MT_BSNAKE Fangskin bhka 14 681 SkeletonMelee 2 15 50 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
79 MT_SUCCUBUS Witchfire the Unholy general 12 444 Succubus 3 10 20 IMMUNE_MAGIC,IMMUNE_FIRE,RESIST_LIGHTNING Leashed 0 0
80 MT_BALROG Blackskull bhka 13 750 SkeletonMelee 3 25 40 IMMUNE_MAGIC,RESIST_LIGHTNING Leashed 0 0
81 MT_UNRAV Soulslash general 12 450 SkeletonMelee 0 25 25 IMMUNE_MAGIC Leashed 0 0
82 MT_VTEXLRD Windspawn general 12 711 SkeletonMelee 1 35 40 IMMUNE_MAGIC,IMMUNE_FIRE Leashed 0 0
83 MT_GSNAKE Lord of the Pit general 13 762 SkeletonMelee 2 25 42 RESIST_FIRE Leashed 0 0
84 MT_RTBLACK Rustweaver general 13 400 SkeletonMelee 3 1 60 IMMUNE_MAGIC,IMMUNE_FIRE,IMMUNE_LIGHTNING None 0 0
85 MT_HOLOWONE Howlingire the Shade general 13 450 SkeletonMelee 2 40 75 RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
86 MT_MAEL Doomcloud general 13 612 Storm 1 1 60 RESIST_FIRE,IMMUNE_LIGHTNING None 0 0
87 MT_PAINMSTR Bloodmoon Soulfire general 13 684 SkeletonMelee 1 15 40 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
88 MT_SNOWWICH Witchmoon general 13 310 Succubus 3 30 40 RESIST_LIGHTNING None 0 0
89 MT_VTEXLRD Gorefeast general 13 771 SkeletonMelee 3 20 55 RESIST_FIRE None 0 0
90 MT_RTBLACK Graywar the Slayer general 14 672 SkeletonMelee 1 30 50 RESIST_LIGHTNING None 0 0
91 MT_MAGISTR Dreadjudge general 14 540 Counselor 1 30 40 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
92 MT_HLSPWN Stareye the Witch general 14 726 Succubus 2 30 50 IMMUNE_FIRE None 0 0
93 MT_BTBLACK Steelskull the Hunter general 14 831 SkeletonMelee 3 40 50 RESIST_LIGHTNING None 0 0
94 MT_RBLACK Sir Gorash general 16 1050 SkeletonMelee 1 20 60 None 0 0
95 MT_CABALIST The Vizier general 15 850 Counselor 2 25 40 IMMUNE_FIRE Leashed 0 0
96 MT_REALWEAV Zamphir general 15 891 SkeletonMelee 2 30 50 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0
97 MT_HLSPWN Bloodlust general 15 825 Succubus 1 20 55 IMMUNE_MAGIC,IMMUNE_LIGHTNING None 0 0
98 MT_HLSPWN Webwidow general 16 774 Succubus 1 20 50 IMMUNE_MAGIC,IMMUNE_FIRE None 0 0
99 MT_SOLBRNR Fleshdancer general 16 999 Succubus 3 30 50 IMMUNE_MAGIC,RESIST_FIRE None 0 0
100 MT_OBLORD Grimspike general 19 534 Sneak 1 25 40 IMMUNE_MAGIC,RESIST_FIRE Leashed 0 0
101 MT_STORML Doomlock general 28 534 Sneak 1 35 55 IMMUNE_MAGIC,RESIST_FIRE,RESIST_LIGHTNING Leashed 0 0

9
Source/data/file.cpp

@ -3,6 +3,7 @@
#include <fmt/format.h>
#include "engine/assets.hpp"
#include "utils/algorithm/container.hpp"
#include "utils/language.h"
namespace devilution {
@ -134,4 +135,12 @@ tl::expected<void, DataFile::Error> DataFile::skipHeader()
return {};
}
[[nodiscard]] size_t DataFile::numRecords() const
{
if (content_.empty()) return 0;
const auto numNewlines = static_cast<size_t>(c_count(content_, '\n') + (content_.back() == '\n' ? 0 : 1));
if (numNewlines < 2) return 0;
return static_cast<size_t>(numNewlines - 1);
}
} // namespace devilution

4
Source/data/file.hpp

@ -1,5 +1,6 @@
#pragma once
#include <cstddef>
#include <memory>
#include <string_view>
@ -119,6 +120,9 @@ public:
return {};
}
// Assumes a header
[[nodiscard]] size_t numRecords() const;
[[nodiscard]] const char *data() const
{
return content_.data();

4
Source/lua/modules/dev/monsters.cpp

@ -30,9 +30,9 @@ std::string DebugCmdSpawnUniqueMonster(std::string name, std::optional<unsigned>
int mtype = -1;
UniqueMonsterType uniqueIndex = UniqueMonsterType::None;
for (size_t i = 0; UniqueMonstersData[i].mtype != MT_INVALID; i++) {
for (size_t i = 0; UniqueMonstersData.size(); ++i) {
auto mondata = UniqueMonstersData[i];
const std::string monsterName = AsciiStrToLower(mondata.mName);
const std::string monsterName = AsciiStrToLower(std::string_view(mondata.mName));
if (monsterName.find(name) == std::string::npos)
continue;
mtype = mondata.mtype;

632
Source/monstdat.cpp

@ -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

6
Source/monstdat.h

@ -306,8 +306,8 @@ enum class UniqueMonsterPack : uint8_t {
struct UniqueMonsterData {
_monster_id mtype;
const char *mName;
const char *mTrnName;
std::string mName;
std::string mTrnName;
uint8_t mlevel;
uint16_t mmaxhp;
MonsterAIID mAi;
@ -328,7 +328,7 @@ struct UniqueMonsterData {
extern std::vector<MonsterData> MonstersData;
extern const _monster_id MonstConvTbl[];
extern const UniqueMonsterData UniqueMonstersData[];
extern std::vector<UniqueMonsterData> UniqueMonstersData;
void LoadMonsterData();

2
Source/monster.cpp

@ -479,7 +479,7 @@ void ClrAllMonsters()
void PlaceUniqueMonsters()
{
for (size_t u = 0; UniqueMonstersData[u].mtype != -1; u++) {
for (size_t u = 0; u < UniqueMonstersData.size(); ++u) {
if (UniqueMonstersData[u].mlevel != currlevel)
continue;

3
Source/pack.cpp

@ -118,8 +118,7 @@ bool IsUniqueMonsterItemValid(uint16_t iCreateInfo, uint32_t dwBuff)
const uint8_t level = iCreateInfo & CF_LEVEL;
// Check all unique monster levels to see if they match the item level
for (int i = 0; UniqueMonstersData[i].mName != nullptr; i++) {
const auto &uniqueMonsterData = UniqueMonstersData[i];
for (const UniqueMonsterData &uniqueMonsterData : UniqueMonstersData) {
const auto &uniqueMonsterLevel = static_cast<uint8_t>(MonstersData[uniqueMonsterData.mtype].level);
if (IsAnyOf(uniqueMonsterData.mtype, MT_DEFILER, MT_NAKRUL, MT_HORKDMN)) {

100
Source/translation_dummy.cpp

@ -144,3 +144,103 @@ const char *MT_BICLOPS_NAME = P_("monster", "Biclops");
const char *MT_FLESTHNG_NAME = P_("monster", "Flesh Thing");
const char *MT_REAPER_NAME = P_("monster", "Reaper");
const char *MT_NAKRUL_NAME = P_("monster", "Na-Krul");
const char *GHARBAD_THE_WEAK_NAME = P_("monster", "Gharbad the Weak");
const char *SKELETON_KING_NAME = P_("monster", "Skeleton King");
const char *ZHAR_THE_MAD_NAME = P_("monster", "Zhar the Mad");
const char *SNOTSPILL_NAME = P_("monster", "Snotspill");
const char *ARCH_BISHOP_LAZARUS_NAME = P_("monster", "Arch-Bishop Lazarus");
const char *RED_VEX_NAME = P_("monster", "Red Vex");
const char *BLACK_JADE_NAME = P_("monster", "Black Jade");
const char *LACHDANAN_NAME = P_("monster", "Lachdanan");
const char *WARLORD_OF_BLOOD_NAME = P_("monster", "Warlord of Blood");
const char *THE_BUTCHER_NAME = P_("monster", "The Butcher");
const char *HORK_DEMON_NAME = P_("monster", "Hork Demon");
const char *THE_DEFILER_NAME = P_("monster", "The Defiler");
const char *NA_KRUL_NAME = P_("monster", "Na-Krul");
const char *BONEHEAD_KEENAXE_NAME = P_("monster", "Bonehead Keenaxe");
const char *BLADESKIN_THE_SLASHER_NAME = P_("monster", "Bladeskin the Slasher");
const char *SOULPUS_NAME = P_("monster", "Soulpus");
const char *PUKERAT_THE_UNCLEAN_NAME = P_("monster", "Pukerat the Unclean");
const char *BONERIPPER_NAME = P_("monster", "Boneripper");
const char *ROTFEAST_THE_HUNGRY_NAME = P_("monster", "Rotfeast the Hungry");
const char *GUTSHANK_THE_QUICK_NAME = P_("monster", "Gutshank the Quick");
const char *BROKENHEAD_BANGSHIELD_NAME = P_("monster", "Brokenhead Bangshield");
const char *BONGO_NAME = P_("monster", "Bongo");
const char *ROTCARNAGE_NAME = P_("monster", "Rotcarnage");
const char *SHADOWBITE_NAME = P_("monster", "Shadowbite");
const char *DEADEYE_NAME = P_("monster", "Deadeye");
const char *MADEYE_THE_DEAD_NAME = P_("monster", "Madeye the Dead");
const char *EL_CHUPACABRAS_NAME = P_("monster", "El Chupacabras");
const char *SKULLFIRE_NAME = P_("monster", "Skullfire");
const char *WARPSKULL_NAME = P_("monster", "Warpskull");
const char *GORETONGUE_NAME = P_("monster", "Goretongue");
const char *PULSECRAWLER_NAME = P_("monster", "Pulsecrawler");
const char *MOONBENDER_NAME = P_("monster", "Moonbender");
const char *WRATHRAVEN_NAME = P_("monster", "Wrathraven");
const char *SPINEEATER_NAME = P_("monster", "Spineeater");
const char *BLACKASH_THE_BURNING_NAME = P_("monster", "Blackash the Burning");
const char *SHADOWCROW_NAME = P_("monster", "Shadowcrow");
const char *BLIGHTSTONE_THE_WEAK_NAME = P_("monster", "Blightstone the Weak");
const char *BILEFROTH_THE_PIT_MASTER_NAME = P_("monster", "Bilefroth the Pit Master");
const char *BLOODSKIN_DARKBOW_NAME = P_("monster", "Bloodskin Darkbow");
const char *FOULWING_NAME = P_("monster", "Foulwing");
const char *SHADOWDRINKER_NAME = P_("monster", "Shadowdrinker");
const char *HAZESHIFTER_NAME = P_("monster", "Hazeshifter");
const char *DEATHSPIT_NAME = P_("monster", "Deathspit");
const char *BLOODGUTTER_NAME = P_("monster", "Bloodgutter");
const char *DEATHSHADE_FLESHMAUL_NAME = P_("monster", "Deathshade Fleshmaul");
const char *WARMAGGOT_THE_MAD_NAME = P_("monster", "Warmaggot the Mad");
const char *GLASSKULL_THE_JAGGED_NAME = P_("monster", "Glasskull the Jagged");
const char *BLIGHTFIRE_NAME = P_("monster", "Blightfire");
const char *NIGHTWING_THE_COLD_NAME = P_("monster", "Nightwing the Cold");
const char *GORESTONE_NAME = P_("monster", "Gorestone");
const char *BRONZEFIST_FIRESTONE_NAME = P_("monster", "Bronzefist Firestone");
const char *WRATHFIRE_THE_DOOMED_NAME = P_("monster", "Wrathfire the Doomed");
const char *FIREWOUND_THE_GRIM_NAME = P_("monster", "Firewound the Grim");
const char *BARON_SLUDGE_NAME = P_("monster", "Baron Sludge");
const char *BLIGHTHORN_STEELMACE_NAME = P_("monster", "Blighthorn Steelmace");
const char *CHAOSHOWLER_NAME = P_("monster", "Chaoshowler");
const char *DOOMGRIN_THE_ROTTING_NAME = P_("monster", "Doomgrin the Rotting");
const char *MADBURNER_NAME = P_("monster", "Madburner");
const char *BONESAW_THE_LITCH_NAME = P_("monster", "Bonesaw the Litch");
const char *BREAKSPINE_NAME = P_("monster", "Breakspine");
const char *DEVILSKULL_SHARPBONE_NAME = P_("monster", "Devilskull Sharpbone");
const char *BROKENSTORM_NAME = P_("monster", "Brokenstorm");
const char *STORMBANE_NAME = P_("monster", "Stormbane");
const char *OOZEDROOL_NAME = P_("monster", "Oozedrool");
const char *GOLDBLIGHT_OF_THE_FLAME_NAME = P_("monster", "Goldblight of the Flame");
const char *BLACKSTORM_NAME = P_("monster", "Blackstorm");
const char *PLAGUEWRATH_NAME = P_("monster", "Plaguewrath");
const char *THE_FLAYER_NAME = P_("monster", "The Flayer");
const char *BLUEHORN_NAME = P_("monster", "Bluehorn");
const char *WARPFIRE_HELLSPAWN_NAME = P_("monster", "Warpfire Hellspawn");
const char *FANGSPEIR_NAME = P_("monster", "Fangspeir");
const char *FESTERSKULL_NAME = P_("monster", "Festerskull");
const char *LIONSKULL_THE_BENT_NAME = P_("monster", "Lionskull the Bent");
const char *BLACKTONGUE_NAME = P_("monster", "Blacktongue");
const char *VILETOUCH_NAME = P_("monster", "Viletouch");
const char *VIPERFLAME_NAME = P_("monster", "Viperflame");
const char *FANGSKIN_NAME = P_("monster", "Fangskin");
const char *WITCHFIRE_THE_UNHOLY_NAME = P_("monster", "Witchfire the Unholy");
const char *BLACKSKULL_NAME = P_("monster", "Blackskull");
const char *SOULSLASH_NAME = P_("monster", "Soulslash");
const char *WINDSPAWN_NAME = P_("monster", "Windspawn");
const char *LORD_OF_THE_PIT_NAME = P_("monster", "Lord of the Pit");
const char *RUSTWEAVER_NAME = P_("monster", "Rustweaver");
const char *HOWLINGIRE_THE_SHADE_NAME = P_("monster", "Howlingire the Shade");
const char *DOOMCLOUD_NAME = P_("monster", "Doomcloud");
const char *BLOODMOON_SOULFIRE_NAME = P_("monster", "Bloodmoon Soulfire");
const char *WITCHMOON_NAME = P_("monster", "Witchmoon");
const char *GOREFEAST_NAME = P_("monster", "Gorefeast");
const char *GRAYWAR_THE_SLAYER_NAME = P_("monster", "Graywar the Slayer");
const char *DREADJUDGE_NAME = P_("monster", "Dreadjudge");
const char *STAREYE_THE_WITCH_NAME = P_("monster", "Stareye the Witch");
const char *STEELSKULL_THE_HUNTER_NAME = P_("monster", "Steelskull the Hunter");
const char *SIR_GORASH_NAME = P_("monster", "Sir Gorash");
const char *THE_VIZIER_NAME = P_("monster", "The Vizier");
const char *ZAMPHIR_NAME = P_("monster", "Zamphir");
const char *BLOODLUST_NAME = P_("monster", "Bloodlust");
const char *WEBWIDOW_NAME = P_("monster", "Webwidow");
const char *FLESHDANCER_NAME = P_("monster", "Fleshdancer");
const char *GRIMSPIKE_NAME = P_("monster", "Grimspike");
const char *DOOMLOCK_NAME = P_("monster", "Doomlock");

13
tools/extract_translation_data.py

@ -5,6 +5,7 @@ import pathlib
root = pathlib.Path(__file__).resolve().parent.parent
translation_dummy_path = root.joinpath("Source/translation_dummy.cpp")
monstdat_path = root.joinpath("Packaging/resources/assets/txtdata/monsters/monstdat.tsv")
unique_monstdat_path = root.joinpath("Packaging/resources/assets/txtdata/monsters/unique_monstdat.tsv")
with open(translation_dummy_path, 'w') as temp_source:
temp_source.write(f'/**\n')
@ -18,6 +19,12 @@ with open(translation_dummy_path, 'w') as temp_source:
with open(monstdat_path, 'r') as tsv:
reader = csv.DictReader(tsv, delimiter='\t')
for row in reader:
translation_key = row['_monster_id'] + "_NAME"
name = row['name'];
temp_source.write(f'const char *' + translation_key + ' = P_("monster", "' + name + '");\n')
name = row['name']
var_name = row['_monster_id'] + "_NAME"
temp_source.write(f'const char *' + var_name + ' = P_("monster", "' + name + '");\n')
with open(unique_monstdat_path, 'r') as tsv:
reader = csv.DictReader(tsv, delimiter='\t')
for row in reader:
name = row['name']
var_name = name.upper().replace(' ', '_').replace('-', '_') + "_NAME"
temp_source.write(f'const char *' + var_name + ' = P_("monster", "' + name + '");\n')

Loading…
Cancel
Save