package d2datadict import ( "log" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common" ) // https://d2mods.info/forum/kb/viewarticle?a=360 type ( // MonStatsRecord represents a single row from `data/global/excel/monstats.txt` in the MPQ files. // These records are used for creating monsters. MonStatsRecord struct { // Key contains the pointer that will be used in other txt files // such as levels.txt and superuniques.txt. Key string // called `Id` in monstats.txt // Id is the actual internal ID of the unit (this is what the ID pointer // actually points at) remember that no two units can have the same ID, // this will result in lots of unpredictable behavior and crashes so please // don’t do it. This 'HarcCodedInDeX' is used for several things, such as // determining whenever the unit uses DCC or DC6 graphics (like mephisto // and the death animations of Diablo, the Maggoc Queen etc.), the hcIdx // column also links other hardcoded effects to the units, such as the // transparency on necro summons and the name-color change on unique boss // units (thanks to Kingpin for the info) Id string //nolint:golint,stylecheck // called `hcIdx` in monstats.txt // BaseKey is an ID pointer of the “base” unit for this specific // monster type (ex. There are five types of “Fallen”; all of them have // fallen1 as their “base” unit). BaseKey string // called `BaseId` in monstats.txt // NextKey is the ID of the next unit in the chain. (fallen1 has the ID pointer of fallen2 in here). // The game uses this for “map generated” monsters such as the fallen in the fallen camps, // which get picked based on area level. NextKey string // called `NextInClass` in monstats.txt // NameStringTableKey the string-key used in the TBL (string.tbl, // expansionstring.tbl and patchstring.tbl) files to make this monsters // name appear when you highlight it. NameStringTableKey string // called `NameStr` in monstats.txt // ExtraDataKey the ID pointer to an entry in MonStats2.txt. ExtraDataKey string // called `MonStatsEx` in monstats.txt // PropertiesKey contains the ID pointer to an entry in MonProp.txt which // controls what special modifiers are appended to the unit PropertiesKey string // called `MonProp` in monstats.txt // MonsterGroup contains the group ID of the “super group” this monster // belongs to, IE all skeletons belong to the "super group" skeleton. The MonsterGroup string // called `MonType` in monstats.txt // AiKey tells the game which AI to use for this monster. Every AI // needs a specific set of animation modes (GH, A1, A2, S1, WL, RN etc). AiKey string // called `AI` in monstats.txt // DescriptionStringTableKey contains the string-key used in the TBL (string.tbl, // expansionstring.tbl and patchstring.tbl) files for the monsters // description (leave it blank for no description). // NOTE: ever wondered how to make it say something below the monster // name (such as “Drains Mana and Stamina etc), well this is how you do it. // Just put the string-key of the string you want to display below the // monsters name in here. DescriptionStringTableKey string // called `DescStr` in monstats.txt // AnimationDirectoryToken controls which token (IE name of a folder that // contains animations) the game uses for this monster. AnimationDirectoryToken string // called `Code` in monstats.txt // SpawnKey contains the key of the unit to spawn. SpawnKey string // called `spawn` in monstats.txt // SpawnAnimationKey // which animation mode will the spawned monster be spawned in. SpawnAnimationKey string // called `spawnmode` in monstats.txt // MinionId1 is an Id of a minion that spawns when this monster is created MinionId1 string //nolint:golint,stylecheck // called `minion1` in monstats.txt // MinionId2 is an Id of a minion that spawns when this monster is created MinionId2 string //nolint:golint,stylecheck // called `minion2` in monstats.txt // SoundKeyNormal, SoundKeySpecial // specifies the ID pointer to this monsters “Sound Bank” in MonSound.txt // when this monster is normal. SoundKeyNormal string // called `MonSound` in monstats.txt SoundKeySpecial string // called `UMonSound` in monstats.txt // MissileA1 -- MissileSQ // these columns control “non-skill-related” missiles used by the monster. // For example if you enter a missile ID pointer (from Missiles.txt) in // MissA1 then, whenever the monster uses its A1 mode, it will shoot a // missile, this however will successfully prevent it from dealing any damage // with the swing of A1. // NOTE: for the beginners, A1=Attack1, A2=Attack2, S1=Skill1, S2=Skill2, // S3=Skill3, S4=Skill4, C=Cast, SQ=Sequence. MissileA1 string // called `MissA1` in monstats.txt MissileA2 string // called `MissA2` in monstats.txt MissileS1 string // called `MissS1` in monstats.txt MissileS2 string // called `MissS2` in monstats.txt MissileS3 string // called `MissS3` in monstats.txt MissileS4 string // called `MissS4` in monstats.txt MissileC string // called `MissC` in monstats.txt MissileSQ string // called `MissSQ` in monstats.txt // SkillId1 -- SkillId8 // the ID Pointer to the skill (from Skills.txt) the monster will cast when // this specific slot is accessed by the AI. Which slots are used is // determined by the units AI. SkillId1 string //nolint:golint,stylecheck // called `Skill1` in monstats.txt SkillId2 string //nolint:golint,stylecheck // called `Skill2` in monstats.txt SkillId3 string //nolint:golint,stylecheck // called `Skill3` in monstats.txt SkillId4 string //nolint:golint,stylecheck // called `Skill4` in monstats.txt SkillId5 string //nolint:golint,stylecheck // called `Skill5` in monstats.txt SkillId6 string //nolint:golint,stylecheck // called `Skill6` in monstats.txt SkillId7 string //nolint:golint,stylecheck // called `Skill7` in monstats.txt SkillId8 string //nolint:golint,stylecheck // called `Skill8` in monstats.txt // SkillAnimation1 -- SkillAnimation8 // the graphical MODE (or SEQUENCE) this unit uses when it uses this skill. SkillAnimation1 string // called `Sk1mode` in monstats.txt SkillAnimation2 string // called `Sk2mode` in monstats.txt SkillAnimation3 string // called `Sk3mode` in monstats.txt SkillAnimation4 string // called `Sk4mode` in monstats.txt SkillAnimation5 string // called `Sk5mode` in monstats.txt SkillAnimation6 string // called `Sk6mode` in monstats.txt SkillAnimation7 string // called `Sk7mode` in monstats.txt SkillAnimation8 string // called `Sk8mode` in monstats.txt // DamageSkillId // ID Pointer to the skill that controls this units damage. This is used for // the druids summons. IE their damage is specified solely by Skills.txt and // not by MonStats.txt. DamageSkillId string //nolint:golint,stylecheck // called `SkillDamage` in monstats.txt // ElementAttackMode1 -- ElementAttackMode3 // the mode to which the elemental damage is appended. The modes to which // you would usually attack elemental damage are A1, A2, S1, S2, S3, S4, SQ // or C as these are the only ones that naturally contain trigger bytes. ElementAttackMode1 string // called `El1Mode` in monstats.txt ElementAttackMode2 string // called `El2Mode` in monstats.txt ElementAttackMode3 string // called `El3Mode` in monstats.txt // ElementType1 -- ElementType3 // the type of the elemental damage appended to an attack. There are several // elements: fire=Fire Damage, ltng=Lightning Damage, cold=Cold Damage // (uses duration), pois = Poison Damage (uses duration), mag=Magic Damage, // life=Life Drain (the monster heals the specified amount when it hits // you), mana=Mana Drain (the monster steals the specified amount of mana // when it hits you), stam=Stamina Drain (the monster steals the specified // amount of stamina when it hits you), stun=Stun Damage (uses duration, // damage is not used, this only effects pets and mercs, players will not // get immobilized but they will get thrown into hit recovery whenever they // get hit by an attack, no matter what type of attack it is, thanks to // Brother Laz clearing this one up), rand=Random Damage (uses duration, // either does Poison, Cold, Fire or Lightning damage, randomly picked for // every attack), burn=Burning Damage (uses duration, this damage type // cannot be resisted or reduced in any way), frze=Freezing Damage (uses // duration, this will effect players like normal cold damage but will // freeze and shatter pets). If you want to give your monster knockback use // MonProp.txt. ElementType1 string // called `El1Type` in monstats.txt ElementType2 string // called `El2Type` in monstats.txt ElementType3 string // called `El3Type` in monstats.txt // TreasureClassNormal // Treasure class for normal monsters, champions, uniques, and quests // on the respective difficulties. TreasureClassNormal string // called `TreasureClass1` in monstats.txt TreasureClassNightmare string // called `TreasureClass1(N)` in monstats.txt TreasureClassHell string // called `TreasureClass1(H)` in monstats.txt TreasureClassChampionNormal string // called `TreasureClass2` in monstats.txt TreasureClassChampionNightmare string // called `TreasureClass2(N)` in monstats.txt TreasureClassChampionHell string // called `TreasureClass2(H)` in monstats.txt TreasureClass3UniqueNormal string // called `TreasureClass3` in monstats.txt TreasureClass3UniqueNightmare string // called `TreasureClass3(N)` in monstats.txt TreasureClass3UniqueHell string // called `TreasureClass3(H)` in monstats.txt TreasureClassQuestNormal string // called `TreasureClass4` in monstats.txt TreasureClassQuestNightmare string // called `TreasureClass4(N)` in monstats.txt TreasureClassQuestHell string // called `TreasureClass4(H)` in monstats.txt // TreasureClassQuestTriggerId // the ID of the Quest that triggers the Quest Treasureclass drop. TreasureClassQuestTriggerId string //nolint:golint,stylecheck // called `TCQuestId` in monstats.txt // TreasureClassQuestCompleteId // the ID of the Quest State that you need to complete to trigger the Quest // Treasureclass trop. TreasureClassQuestCompleteId string //nolint:golint,stylecheck // called `TCQuestCP` in monstats.txt // PaletteId indicates which palette (color) entry the unit will use, most // monsters have a palshift.dat file in their COF folder, this file // contains 8 palettes, starting from index 0. These palettes are used by // the game to make the various monster sub-types appear with color // variations. The game with use the palette from the palettes file // corresponding to the value in this column plus 2; eg: translvl = 0 will // use the third palette in the file. // NOTE: some tokens (token = IE name of a folder that contains animations) // such as FC do not accept their palettes. // NOTE no 2: some monsters got unused palettes, ZM (zombie) for example // will turn light-rotten-green with palette nr 5 and pink-creamy with 6. PaletteId int //nolint:golint,stylecheck // called `TransLvl` in monstats.txt // SpawnOffsetX, SpawnOffsetY // are the x/y offsets at which spawned monsters are placed. IE this prevents // the spawned monsters from being created at the same x/y coordinates as // the spawner itself. SpawnOffsetX int // called `spawnx` in monstats.txt SpawnOffsetY int // called `spawny` in monstats.txt // MinionPartyMin, MinionPartyMax controls how many minions are spawned together with this unit. MinionPartyMin int // called `PartyMin` in monstats.txt MinionPartyMax int // called `PartyMax` in monstats.txt // MinionGroupMin, MinionGroupMax // controls how many units of the base unit to spawn. MinionGroupMin int // called `MinGrp` in monstats.txt MinionGroupMax int // called `MaxGrp` in monstats.txt // PopulationReductionPercent controls the overall chance something will spawn in // percentages. Blank entries are the same as 100%. PopulationReductionPercent int // called `sparsePopulate` in monstats.txt // SpeedBase, SpeedRun // controls the walking and running speed of this monster respectively. // NOTE: RUN is only used if the monster has a RN mode and its AI uses that // mode. SpeedBase int // called `Velocity` in monstats.txt SpeedRun int // called `Run` in monstats.txt // Rarity controls the overall odds that this monster will be spawned. // IE Lets say in Levels.txt you have two monsters set to spawn - Monster A // has rarity of 10 whereas Monster B has rarity of 1 and the level in // question is limited to 1 monster type. First the game sums up the // chances (11) and then calculates the odds of the monster spawning. Which // would be 1/11 (9% chance) for Monster B and 10/11 (91% chance) for // Monster A, thus Monster A is a lot more common than monster B. If you set // this column to 0 then the monster will never be selected by Levels.txt // for obvious reasons. Rarity int // called `Rarity` in monstats.txt // LevelNormal, LevelNightmare, LevelHell // controls the monsters level on the specified difficulty. This setting is // only used on normal. On nightmare and hell the monsters level is // identical with the area level from Levels.txt, unless your monster has // BOSS column set to 1, in this case its level will be always taken from // these 3 columns. LevelNormal int // called `Level` in monstats.txt LevelNightmare int // called `Level(N)` in monstats.txt LevelHell int // called `Level(H)` in monstats.txt // used by the game to tell AIs which unit to target first. The higher this // is the higher the threat level. Setting this to 25 or so on Maggot Eggs // would make your Mercenary NPC try to destroy those first. ThreatLevel int // called `threat` in monstats.txt // AiDelayNormal, AiDelayNightmare, AiDelayHell // this controls delays between AI ticks (on normal, nightmare and hell). // The lower the number, the faster the AI's will attack thanks to reduced // delay between swings, casting spells, throwing missiles etc. Please // remember that some AI's got individual delays between attacks, this will // still make them faster and seemingly more deadly though. AiDelayNormal int // called `aidel` in monstats.txt AiDelayNightmare int // called `aidel(N)` in monstats.txt AiDelayHell int // called `aidel(H)` in monstats.txt // AiDistanceNormal, AiDistanceNightmare, AiDistanceHell // the distance in cells from which AI is activated. Most AI"s have base // hardcoded activation radius of 35 which stands for a distamnce of about // 1 screen, thus leaving these fields blank sets this to 35 automatically. AiDistanceNormal int // called `aidist` in monstats.txt AiDistanceNightmare int // called `aidist(N)` in monstats.txt AiDistanceHell int // called `aidist(H)` in monstats.txt // AiParameterNormal1, AiParameterNightmare1, AiParameterHell1 // these cells are very important, they pass on parameters (in percentage) // to the AI code. For descriptions about what all these AI's do, check // The AI Compendium. https://d2mods.info/forum/viewtopic.php?t=36230 // Warning: many people have trouble with the AI of the Imps, this AI is // special and uses multiple rows. AiParameterNormal1 int // called `aip1` in monstats.txt AiParameterNormal2 int // called `aip2` in monstats.txt AiParameterNormal3 int // called `aip3` in monstats.txt AiParameterNormal4 int // called `aip4` in monstats.txt AiParameterNormal5 int // called `aip5` in monstats.txt AiParameterNormal6 int // called `aip6` in monstats.txt AiParameterNormal7 int // called `aip7` in monstats.txt AiParameterNormal8 int // called `aip8` in monstats.txt AiParameterNightmare1 int // called `aip1(N)` in monstats.txt AiParameterNightmare2 int // called `aip2(N)` in monstats.txt AiParameterNightmare3 int // called `aip3(N)` in monstats.txt AiParameterNightmare4 int // called `aip4(N)` in monstats.txt AiParameterNightmare5 int // called `aip5(N)` in monstats.txt AiParameterNightmare6 int // called `aip6(N)` in monstats.txt AiParameterNightmare7 int // called `aip7(N)` in monstats.txt AiParameterNightmare8 int // called `aip8(N)` in monstats.txt AiParameterHell1 int // called `aip1(H)` in monstats.txt AiParameterHell2 int // called `aip2(H)` in monstats.txt AiParameterHell3 int // called `aip3(H)` in monstats.txt AiParameterHell4 int // called `aip4(H)` in monstats.txt AiParameterHell5 int // called `aip5(H)` in monstats.txt AiParameterHell6 int // called `aip6(H)` in monstats.txt AiParameterHell7 int // called `aip7(H)` in monstats.txt AiParameterHell8 int // called `aip8(H)` in monstats.txt // Alignment controls whenever the monster fights on your side or // fights against you (or if it just walks around, IE a critter). // If you want to turn some obsolete NPCs into enemies, this is // one of the settings you will need to modify. Setting it to 2 // without adjusting other settings (related to AI and also some // in MonStats2) it will simply attack everything. Alignment d2enum.MonsterAlignmentType // called `Align` in monstats.txt // SkillLevel1 -- SkillLevel8 // the skill level of the skill in question. This gets a bonus on nightmare // and hell which you can modify in DifficultyLevels.txt. SkillLevel1 int // called `Sk1lvl` in monstats.txt SkillLevel2 int // called `Sk2lvl` in monstats.txt SkillLevel3 int // called `Sk3lvl` in monstats.txt SkillLevel4 int // called `Sk4lvl` in monstats.txt SkillLevel5 int // called `Sk5lvl` in monstats.txt SkillLevel6 int // called `Sk6lvl` in monstats.txt SkillLevel7 int // called `Sk7lvl` in monstats.txt SkillLevel8 int // called `Sk8lvl` in monstats.txt // LeechSensitivityNormal / Nightmare / Hell // controls the effectiveness of Life and Mana steal from equipment on this // unit on the respective difficulties. 0=Can’t leech at all. Remember that // besides this, Life and Mana Steal is further limited by DifficultyLevels.txt. LeechSensitivityNormal int // called `Drain` in monstats.txt LeechSensitivityNightmare int // called `Drain(N)` in monstats.txt LeechSensitivityHell int // called `Drain(H)` in monstats.txt // ColdSensitivityNormal / Nightmare / Hell // controls the effectiveness of cold effect and its duration and freeze // duration on this unit. The lower this value is, the more speed this unit // looses when its under the effect of cold, also freezing/cold effect will // stay for longer. Positive values will make the unit faster (thanks to // Brother Laz for confirming my assumption), and 0 will make it // unfreezeable. Besides this, cold length and freeze length settings are // also set in DifficultyLevels.txt. ColdSensitivityNormal int // called `coldeffect` in monstats.txt ColdSensitivityNightmare int // called `coldeffect(N)` in monstats.txt ColdSensitivityHell int // called `coldeffect(H)` in monstats.txt // ResistancePhysicalNormal // Damage resistance on the respective difficulties. Negative values mean // that the unit takes more damage from this element, values at or above 100 // will result in immunity. ResistancePhysicalNormal int // called `ResDm` in monstats.txt ResistancePhysicalNightmare int // called `ResDm(N)` in monstats.txt ResistancePhysicalHell int // called `ResDm(H)` in monstats.txt ResistanceMagicNormal int // called `ResMa` in monstats.txt ResistanceMagicNightmare int // called `ResMa(N)` in monstats.txt ResistanceMagicHell int // called `ResMa(H)` in monstats.txt ResistanceFireNormal int // called `ResFi` in monstats.txt ResistanceFireNightmare int // called `ResFi(N)` in monstats.txt ResistanceFireHell int // called `ResFi(H)` in monstats.txt ResistanceLightningNormal int // called `ResLi` in monstats.txt ResistanceLightningNightmare int // called `ResLi(N)` in monstats.txt ResistanceLightningHell int // called `ResLi(H)` in monstats.txt ResistanceColdNormal int // called `ResCo` in monstats.txt ResistanceColdNightmare int // called `ResCo(N)` in monstats.txt ResistanceColdHell int // called `ResCo(H)` in monstats.txt ResistancePoisonNormal int // called `ResPo` in monstats.txt ResistancePoisonNightmare int // called `ResPo(N)` in monstats.txt ResistancePoisonHell int // called `ResPo(H)` in monstats.txt // HealthRegenPerFrame // this controls how much health this unit regenerates per frame. Sometimes // this is altered by the units AI. The formula is (REGEN * HP) / 4096. So // a monster with 200 hp and a regen rate of 10 would regenerate ~0,5 HP // (~12 per second) every frame (1 second = 25 frames). HealthRegenPerFrame int // called `DamageRegen` in monstats.txt // ChanceToBlockNormal / Nightmare / Hell // this units chance to block. See the above column for details when this // applies or not. Monsters are capped at 75% block as players are AFAIK. ChanceToBlockNormal int // called `ToBlock` in monstats.txt ChanceToBlockNightmare int // called `ToBlock(N)` in monstats.txt ChanceToBlockHell int // called `ToBlock(H)` in monstats.txt // ChanceDeadlyStrike // this units chance of scoring a critical hit (dealing double the damage). ChanceDeadlyStrike int // called `Crit` in monstats.txt // MinHPNormal -- MaxHPHell // minHp, maxHp, minHp(N), maxHp(N), minHp(H), maxHp(H): this units minimum // and maximum HP on the respective difficulties. // NOTE: Monster HitPoints are calculated as the following: (minHp * Hp from // MonLvl.txt)/100 for minimal hp and (maxHp * Hp from MonLvl.txt)/100 for // maximum hp. // To make this guide idiot-proof, we will calculate the hit points of a // Hungry Dead from vanilla on Normal difficulty and Single Player mode. // It has minHp = 101 and maxHp = 186 and level 2. Hp for level 2 in // MonLvl.txt = 9 // It means Hungry Dead has (101*9)/100 ~ 9 of minimum hp and // (186*9)/100 ~ 17 maximum hit points. You have to remember monsters on // nightmare and hell take their level (unless Boss = 1) from area level of // Levels.txt instead of Level column of MonStats.txt. I hope this is clear. MinHPNormal int // called `minHP` in monstats.txt MinHPNightmare int // called `MinHP(N)` in monstats.txt MinHPHell int // called `MinHP(H)` in monstats.txt MaxHPNormal int // called `maxHP` in monstats.txt MaxHPNightmare int // called `MaxHP(N)` in monstats.txt MaxHPHell int // called `MaxHP(H)` in monstats.txt // ArmorClassNormal -- Hell // this units Armor Class on the respective difficulties. The calculation is // the same (analogical) as for hit points. ArmorClassNormal int // called `AC` in monstats.txt ArmorClassNightmare int // called `AC(N)` in monstats.txt ArmorClassHell int // called `AC(H)` in monstats.txt // ExperienceNormal -- Hell // the experience you get when killing this unit on the respective // difficulty. The calculation is the same (analogical) as for hit points. ExperienceNormal int // called `Exp` in monstats.txt ExperienceNightmare int // called `Exp(N)` in monstats.txt ExperienceHell int // called `Exp(H)` in monstats.txt // DamageMinA1Normal / Nightmare / Hell // DamageMaxA1Normal / Nightmare /Hell // this units minimum and maximum damage when it uses A1/A2/S1 mode. // The calculation is the same (analogical) as for hit points. DamageMinA1Normal int // called `A1MinD` in monstats.txt DamageMinA1Nightmare int // called `A1MinD(N)` in monstats.txt DamageMinA1Hell int // called `A1MinD(H)` in monstats.txt DamageMaxA1Normal int // called `A1MaxD` in monstats.txt DamageMaxA1Nightmare int // called `A1MaxD(N)` in monstats.txt DamageMaxA1Hell int // called `A1MaxD(H)` in monstats.txt DamageMinA2Normal int // called `A2MinD` in monstats.txt DamageMinA2Nightmare int // called `A2MinD(N)` in monstats.txt DamageMinA2Hell int // called `A2MinD(H)` in monstats.txt DamageMaxA2Normal int // called `A2MaxD` in monstats.txt DamageMaxA2Nightmare int // called `A2MaxD(N)` in monstats.txt DamageMaxA2Hell int // called `A2MaxD(H)` in monstats.txt DamageMinS1Normal int // called `S1MinD` in monstats.txt DamageMinS1Nightmare int // called `S1MinD(N)` in monstats.txt DamageMinS1Hell int // called `S1MinD(H)` in monstats.txt DamageMaxS1Normal int // called `S1MaxD` in monstats.txt DamageMaxS1Nightmare int // called `S1MaxD(N)` in monstats.txt DamageMaxS1Hell int // called `S1MaxD(H)` in monstats.txt // AttackRatingA1Normal AttackRatingS1Hell // this units attack rating for A1/A2/S1 mode on the respective difficulties // The calculation is the same (analogical) as for hit points. AttackRatingA1Normal int // called `A1TH` in monstats.txt AttackRatingA1Nightmare int // called `A1TH(N)` in monstats.txt AttackRatingA1Hell int // called `A1TH(H)` in monstats.txt AttackRatingA2Normal int // called `A2TH` in monstats.txt AttackRatingA2Nightmare int // called `A2TH(N)` in monstats.txt AttackRatingA2Hell int // called `A2TH(H)` in monstats.txt AttackRatingS1Normal int // called `S1TH` in monstats.txt AttackRatingS1Nightmare int // called `S1TH(N)` in monstats.txt AttackRatingS1Hell int // called `S1TH(H)` in monstats.txt // ElementChance1Normal -- ElementChance3Hell // chance to append elemental damage to an attack on the respective // difficulties. 0=Never append, 100=Always append. ElementChance1Normal int // called `El1Pct` in monstats.txt ElementChance1Nightmare int // called `El1Pct(N)` in monstats.txt ElementChance1Hell int // called `El1Pct(H)` in monstats.txt ElementChance2Normal int // called `El2Pct` in monstats.txt ElementChance2Nightmare int // called `El2Pct(N)` in monstats.txt ElementChance2Hell int // called `El2Pct(H)` in monstats.txt ElementChance3Normal int // called `El3Pct` in monstats.txt ElementChance3Nightmare int // called `El3Pct(N)` in monstats.txt ElementChance3Hell int // called `El3Pct(H)` in monstats.txt // ElementDamageMin1Normal -- ElementDamageMax3Hell // minimum and Maximum elemental damage to append to the attack on the // respective difficulties. Note that you should only append elemental // damage to those missiles that don’t have any set in Missiles.txt. The // calculation is the same (analogical) as for hit points. ElementDamageMin1Normal int // called `El1MinD` in monstats.txt ElementDamageMin1Nightmare int // called `El1MinD(N)` in monstats.txt ElementDamageMin1Hell int // called `El1MinD(H)` in monstats.txt ElementDamageMin2Normal int // called `El2MinD` in monstats.txt ElementDamageMin2Nightmare int // called `El2MinD(N)` in monstats.txt ElementDamageMin2Hell int // called `El2MinD(H)` in monstats.txt ElementDamageMin3Normal int // called `El3MinD` in monstats.txt ElementDamageMin3Nightmare int // called `El3MinD(N)` in monstats.txt ElementDamageMin3Hell int // called `El3MinD(H)` in monstats.txt ElementDamageMax1Normal int // called `El1MaxD` in monstats.txt ElementDamageMax1Nightmare int // called `El1MaxD(N)` in monstats.txt ElementDamageMax1Hell int // called `El1MaxD(H)` in monstats.txt ElementDamageMax2Normal int // called `El2MaxD` in monstats.txt ElementDamageMax2Nightmare int // called `El2MaxD(N)` in monstats.txt ElementDamageMax2Hell int // called `El2MaxD(H)` in monstats.txt ElementDamageMax3Normal int // called `El3MaxD` in monstats.txt ElementDamageMax3Nightmare int // called `El3MaxD(N)` in monstats.txt ElementDamageMax3Hell int // called `El3MaxD(H)` in monstats.txt // ElementDuration1Normal -- ElementDuration3Hell // duration of the elemental effect (for freeze, burn, cold, poison and // stun) on the respective difficulties. ElementDuration1Normal int // called `El1Dur` in monstats.txt ElementDuration1Nightmare int // called `El1Dur(N)` in monstats.txt ElementDuration1Hell int // called `El1Dur(H)` in monstats.txt ElementDuration2Normal int // called `El2Dur` in monstats.txt ElementDuration2Nightmare int // called `El2Dur(N)` in monstats.txt ElementDuration2Hell int // called `El2Dur(H)` in monstats.txt ElementDuration3Normal int // called `El3Dur` in monstats.txt ElementDuration3Nightmare int // called `El3Dur(N)` in monstats.txt ElementDuration3Hell int // called `El3Dur(H)` in monstats.txt // SpecialEndDeath // 0 == no special death // 1 == spawn minion1 on death // 2 == kill mounted minion on death (ie the guard tower) SpecialEndDeath int // called `SplEndDeath` in monstats.txt // Enabled controls whenever the unit can be // used at all for any purpose whatsoever. This is not the only setting // that controls this; there are some other things that can also disable // the unit (Rarity and isSpawn columns see those for description). Enabled bool // called `enabled` in monstats.txt // SpawnsMinions tells the game whenever this // unit is a “nest”. IE, monsters that spawn new monsters have this set to // 1. Note that you can make any monster spawn new monsters, irregardless of // its AI, all you need to do is adjust spawn related columns and make sure // one of its skills is either “Nest” or “Minion Spawner”. SpawnsMinions bool // called `placespawn` in monstats.txt // IsLeader controls if a monster is the leader of minions it spawns // a leadercan order "raid on target" it causes group members to use // SK1 instead of A1 and A2 modes while raiding. IsLeader bool // called `SetBoss` in monstats.txt // TransferLeadership is connected with the previous one, // when "boss of the group" is killed, the "leadership" is passed to one of // his minions. TransferLeadership bool // called `BossXfer` in monstats.txt // Boolean, 1=spawnable, 0=not spawnable. This controls whenever this unit // can be spawned via Levels.txt. IsLevelSpawnable bool // called `isSpawn` in monstats.txt // IsMelee controls whenever // this unit can spawn with boss modifiers such as multiple shot or not. IsMelee bool // called `isMelee` in monstats.txt // IsNPC controls whenever the unit is a NPC or not. IsNpc bool // called `npc` in monstats.txt // IsInteractable // controls whenever you can interact with this unit. IE this controls // whenever it opens a speech-box or menu when you click on the unit. To // turn units like Kaeleen or Flavie into enemies you will need to set this // to 0 (you will also need to set NPC to 0 for that). IsInteractable bool // called `interact` in monstats.txt // IsRanged tells the game whenever this is a ranged attacker. It will make it possible for // monsters to spawn with multiple shot modifier. IsRanged bool // called `rangedtype` in monstats.txt // HasInventory Controls whenever this // NPC or UNIT can carry items with it. For NPCs this means that you can // access their Inventory and buy items (if you disable this and then try to // access this feature it will cause a crash so don’t do it unless you know // what you’re doing). For Monsters this means that they can access their // equipment data in MonEquip.txt. HasInventory bool // called `inventory` in monstats.txt // CanEnterTown // controls whenever enemies can follow you into a town or not. This should be set to // 1 for everything that spawns in a town for obvious reasons. According to // informations from Ogodei, it also disables/enables collision in // singleplayer and allows pets to walk/not walk in city in multiplayer. // In multiplayer collision is always set to 0 for pets. CanEnterTown bool // called `inTown` in monstats.txt // IsUndeadLow, IsUndeadHigh // Blizzard used this to differentiate High and Low Undead (IE low // undead like Zombies, Skeletons etc are set to 1 here), both this and // HUNDEAD will make the unit be considered undead. Low undeads can be // resurrected by high undeads. High undeads can't resurrect eachother. IsUndeadLow bool // called `lUndead` in monstats.txt IsUndeadHigh bool // called `hUndead` in monstats.txt // IsDemon makes the game consider this unit a demon. IsDemon bool // called `demon` in monstats.txt // IsFlying If you set this to 1 the monster will be able to move fly over // obstacles such as puddles and rivers. IsFlying bool // called `flying` in monstats.txt // CanOpenDoors controls whether monsters can open doors or not CanOpenDoors bool // called `opendoors` in monstats.txt // IsSpecialBoss controls whenever this unit // is a special boss, as mentioned already, monsters set as boss IGNORE the // level settings, IE they will always spawn with the levels specified in // MonStats.txt. Boss will gain some special resistances, such as immunity // to being stunned (!!!), also it will not be affected by things like // deadly strike the way normal monsters are. IsSpecialBoss bool // called `boss` in monstats.txt // IsActBoss // Setting this to 1 will give your monsters huge (300% IIRC) damage bonus // against hirelings and summons. Ever wondered why Diablo destroys your // skeletons with 1 fire nova while barely doing anything to you? Here is // your answer. IsActBoss bool // called `primeevil` in monstats.txt // IsKillable will make the monster absolutely unkillable. IsKillable bool // called `killable` in monstats.txt // IsAiSwitchable Gives controls if this units mind may // be altered by “mind altering skills” like Attract, Conversion, Revive IsAiSwitchable bool // called `switchai` in monstats.txt // DisableAura Monsters set to 0 here // will not be effected by friendly auras DisableAura bool // called `noAura` in monstats.txt // DisableMultiShot // This is another layer of security to prevent this modifier from spawning, // besides the ISMELEE layer. DisableMultiShot bool // called `nomultishot` in monstats.txt // DisableCounting // prevents your pets from being counted as population in said area, for // example thanks to this you can finish The Den Of Evil quest while having // pets summoned. DisableCounting bool // called `neverCount` in monstats.txt // IgnorePets // Summons and hirelings are ignored by this unit, 0=Summons and // hirelings are noticed by this unit. If you set this to 1 you will the // monsters going directly for the player. IgnorePets bool // called `petIgnore` in monstats.txt // DealsDamageOnDeath This works similar to corpse explosion (its based on // hitpoints) and damages the surrounding players when the unit dies. (Ever // wanted to prevent those undead stygian dolls from doing damage when they // die, this is all there is to it) DealsDamageOnDeath bool // called `deathDmg` in monstats.txt // GenericSpawn Has to do // something is with minions being transformed into suicide minions, the // exact purpose of this is a mystery. GenericSpawn bool // called `genericSpawn` in monstats.txt // IgnoreMonLevelTxt Does this unit use // MonLevel.txt or does it use the stats listed in MonStats.txt as is. // Setting this to 1 will result in an array of problems, such as the // appended elemental damage being completely ignored, irregardless of the // values in it. IgnoreMonLevelTxt bool // called `noRatio` in monstats.txt // CanBlockWithoutShield in order for a unit to // block it needs the BL mode, if this is set to 1 then it will block // irregardless of what modes it has. CanBlockWithoutShield bool // called `NoShldBlock` in monstats.txt // SpecialGetModeChart // Unknown but could be telling the game to look at some internal table. // This is used for some Act Bosses and monsters like Putrid Defilers. SpecialGetModeChart bool // called `SplGetModeChar` in monstats.txt // SpecialEndGeneric Works in conjunction with SPLCLIENTEND, this // makes the unit untargetable when it is first spawned (used for those monsters that are under water, under ground or fly above you) SpecialEndGeneric bool // called `SplEndGeneric` in monstats.txt // SpecialClientEnd Works in conjunction with SPLENDGENERIC, this // makes the unit invisible when it is first spawned (used for those // monsters that are under water, under ground or fly above you), this is // also used for units that have other special drawing setups. SpecialClientEnd bool // called `SplClientEnd` in monstats.txt } ) // MonStats stores all of the MonStat Records var MonStats map[string]*MonStatsRecord //nolint:gochecknoglobals // Currently global by design, only written once // LoadMonStats loads monstats func LoadMonStats(file []byte) { // nolint:funlen // Makes no sense to split MonStats = make(map[string]*MonStatsRecord) d := d2common.LoadDataDictionary(file) for d.Next() { record := &MonStatsRecord{ Key: d.String("Id"), Id: d.String("hcIdx"), BaseKey: d.String("BaseId"), NextKey: d.String("NextInClass"), PaletteId: d.Number("TransLvl"), NameStringTableKey: d.String("NameStr"), ExtraDataKey: d.String("MonStatsEx"), PropertiesKey: d.String("MonProp"), MonsterGroup: d.String("MonType"), AiKey: d.String("AI"), DescriptionStringTableKey: d.String("DescStr"), AnimationDirectoryToken: d.String("Code"), Enabled: d.Number("enabled") > 0, IsRanged: d.Number("rangedtype") > 0, SpawnsMinions: d.Number("placespawn") > 0, SpawnKey: d.String("spawn"), SpawnOffsetX: d.Number("spawnx"), SpawnOffsetY: d.Number("spawny"), SpawnAnimationKey: d.String("spawnmode"), MinionId1: d.String("minion1"), MinionId2: d.String("minion2"), IsLeader: d.Number("SetBoss") > 0, TransferLeadership: d.Number("BossXfer") > 0, MinionPartyMin: d.Number("PartyMin"), MinionPartyMax: d.Number("PartyMax"), MinionGroupMin: d.Number("MinGrp"), MinionGroupMax: d.Number("MaxGrp"), PopulationReductionPercent: d.Number("sparsePopulate"), SpeedBase: d.Number("Velocity"), SpeedRun: d.Number("Run"), Rarity: d.Number("Rarity"), LevelNormal: d.Number("Level"), LevelNightmare: d.Number("Level(N)"), LevelHell: d.Number("Level(H)"), SoundKeyNormal: d.String("MonSound"), SoundKeySpecial: d.String("UMonSound"), ThreatLevel: d.Number("threat"), AiDelayNormal: d.Number("aidel"), AiDelayNightmare: d.Number("aidel(N)"), AiDelayHell: d.Number("aidel(H)"), AiDistanceNormal: d.Number("aidist"), AiDistanceNightmare: d.Number("aidist(N)"), AiDistanceHell: d.Number("aidist(H)"), AiParameterNormal1: d.Number("aip1"), AiParameterNormal2: d.Number("aip2"), AiParameterNormal3: d.Number("aip3"), AiParameterNormal4: d.Number("aip4"), AiParameterNormal5: d.Number("aip5"), AiParameterNormal6: d.Number("aip6"), AiParameterNormal7: d.Number("aip7"), AiParameterNormal8: d.Number("aip8"), AiParameterNightmare1: d.Number("aip1(N)"), AiParameterNightmare2: d.Number("aip2(N)"), AiParameterNightmare3: d.Number("aip3(N)"), AiParameterNightmare4: d.Number("aip4(N)"), AiParameterNightmare5: d.Number("aip5(N)"), AiParameterNightmare6: d.Number("aip6(N)"), AiParameterNightmare7: d.Number("aip7(N)"), AiParameterNightmare8: d.Number("aip8(N)"), AiParameterHell1: d.Number("aip1(H)"), AiParameterHell2: d.Number("aip2(H)"), AiParameterHell3: d.Number("aip3(H)"), AiParameterHell4: d.Number("aip4(H)"), AiParameterHell5: d.Number("aip5(H)"), AiParameterHell6: d.Number("aip6(H)"), AiParameterHell7: d.Number("aip7(H)"), AiParameterHell8: d.Number("aip8(H)"), MissileA1: d.String("MissA1"), MissileA2: d.String("MissA2"), MissileS1: d.String("MissS1"), MissileS2: d.String("MissS2"), MissileS3: d.String("MissS3"), MissileS4: d.String("MissS4"), MissileC: d.String("MissC"), MissileSQ: d.String("MissSQ"), Alignment: d2enum.MonsterAlignmentType(d.Number("Align")), IsLevelSpawnable: d.Number("isSpawn") > 0, IsMelee: d.Number("isMelee") > 0, IsNpc: d.Number("npc") > 0, IsInteractable: d.Number("interact") > 0, HasInventory: d.Number("inventory") > 0, CanEnterTown: d.Number("inTown") > 0, IsUndeadLow: d.Number("lUndead") > 0, IsUndeadHigh: d.Number("hUndead") > 0, IsDemon: d.Number("demon") > 0, IsFlying: d.Number("flying") > 0, CanOpenDoors: d.Number("opendoors") > 0, IsSpecialBoss: d.Number("boss") > 0, IsActBoss: d.Number("primeevil") > 0, IsKillable: d.Number("killable") > 0, IsAiSwitchable: d.Number("switchai") > 0, DisableAura: d.Number("noAura") > 0, DisableMultiShot: d.Number("nomultishot") > 0, DisableCounting: d.Number("neverCount") > 0, IgnorePets: d.Number("petIgnore") > 0, DealsDamageOnDeath: d.Number("deathDmg") > 0, GenericSpawn: d.Number("genericSpawn") > 0, SkillId1: d.String("Skill1"), SkillId2: d.String("Skill2"), SkillId3: d.String("Skill3"), SkillId4: d.String("Skill4"), SkillId5: d.String("Skill5"), SkillId6: d.String("Skill6"), SkillId7: d.String("Skill7"), SkillId8: d.String("Skill8"), SkillAnimation1: d.String("Sk1mode"), SkillAnimation2: d.String("Sk2mode"), SkillAnimation3: d.String("Sk3mode"), SkillAnimation4: d.String("Sk4mode"), SkillAnimation5: d.String("Sk5mode"), SkillAnimation6: d.String("Sk6mode"), SkillAnimation7: d.String("Sk7mode"), SkillAnimation8: d.String("Sk8mode"), SkillLevel1: d.Number("Sk1lvl"), SkillLevel2: d.Number("Sk2lvl"), SkillLevel3: d.Number("Sk3lvl"), SkillLevel4: d.Number("Sk4lvl"), SkillLevel5: d.Number("Sk5lvl"), SkillLevel6: d.Number("Sk6lvl"), SkillLevel7: d.Number("Sk7lvl"), SkillLevel8: d.Number("Sk8lvl"), LeechSensitivityNormal: d.Number("Drain"), LeechSensitivityNightmare: d.Number("Drain(N)"), LeechSensitivityHell: d.Number("Drain(H)"), ColdSensitivityNormal: d.Number("coldeffect"), ColdSensitivityNightmare: d.Number("coldeffect(N)"), ColdSensitivityHell: d.Number("coldeffect(H)"), ResistancePhysicalNormal: d.Number("ResDm"), ResistancePhysicalNightmare: d.Number("ResDm(N)"), ResistancePhysicalHell: d.Number("ResDm(H)"), ResistanceMagicNormal: d.Number("ResMa"), ResistanceMagicNightmare: d.Number("ResMa(N)"), ResistanceMagicHell: d.Number("ResMa(H)"), ResistanceFireNormal: d.Number("ResFi"), ResistanceFireNightmare: d.Number("ResFi(N)"), ResistanceFireHell: d.Number("ResFi(H)"), ResistanceLightningNormal: d.Number("ResLi"), ResistanceLightningNightmare: d.Number("ResLi(N)"), ResistanceLightningHell: d.Number("ResLi(H)"), ResistanceColdNormal: d.Number("ResCo"), ResistanceColdNightmare: d.Number("ResCo(N)"), ResistanceColdHell: d.Number("ResCo(H)"), ResistancePoisonNormal: d.Number("ResPo"), ResistancePoisonNightmare: d.Number("ResPo(N)"), ResistancePoisonHell: d.Number("ResPo(H)"), HealthRegenPerFrame: d.Number("DamageRegen"), DamageSkillId: d.String("SkillDamage"), IgnoreMonLevelTxt: d.Number("noRatio") > 0, CanBlockWithoutShield: d.Number("NoShldBlock") > 0, ChanceToBlockNormal: d.Number("ToBlock"), ChanceToBlockNightmare: d.Number("ToBlock(N)"), ChanceToBlockHell: d.Number("ToBlock(H)"), ChanceDeadlyStrike: d.Number("Crit"), MinHPNormal: d.Number("minHP"), MinHPNightmare: d.Number("MinHP(N)"), MinHPHell: d.Number("MinHP(H)"), MaxHPNormal: d.Number("maxHP"), MaxHPNightmare: d.Number("MaxHP(N)"), MaxHPHell: d.Number("MaxHP(H)"), ArmorClassNormal: d.Number("AC"), ArmorClassNightmare: d.Number("AC(N)"), ArmorClassHell: d.Number("AC(H)"), ExperienceNormal: d.Number("Exp"), ExperienceNightmare: d.Number("Exp(N)"), ExperienceHell: d.Number("Exp(H)"), DamageMinA1Normal: d.Number("A1MinD"), DamageMinA1Nightmare: d.Number("A1MinD(N)"), DamageMinA1Hell: d.Number("A1MinD(H)"), DamageMaxA1Normal: d.Number("A1MaxD"), DamageMaxA1Nightmare: d.Number("A1MaxD(N)"), DamageMaxA1Hell: d.Number("A1MaxD(H)"), DamageMinA2Normal: d.Number("A2MinD"), DamageMinA2Nightmare: d.Number("A2MinD(N)"), DamageMinA2Hell: d.Number("A2MinD(H)"), DamageMaxA2Normal: d.Number("A2MaxD"), DamageMaxA2Nightmare: d.Number("A2MaxD(N)"), DamageMaxA2Hell: d.Number("A2MaxD(H)"), DamageMinS1Normal: d.Number("S1MinD"), DamageMinS1Nightmare: d.Number("S1MinD(N)"), DamageMinS1Hell: d.Number("S1MinD(H)"), DamageMaxS1Normal: d.Number("S1MaxD"), DamageMaxS1Nightmare: d.Number("S1MaxD(N)"), DamageMaxS1Hell: d.Number("S1MaxD(H)"), AttackRatingA1Normal: d.Number("A1TH"), AttackRatingA1Nightmare: d.Number("A1TH(N)"), AttackRatingA1Hell: d.Number("A1TH(H)"), AttackRatingA2Normal: d.Number("A2TH"), AttackRatingA2Nightmare: d.Number("A2TH(N)"), AttackRatingA2Hell: d.Number("A2TH(H)"), AttackRatingS1Normal: d.Number("S1TH"), AttackRatingS1Nightmare: d.Number("S1TH(N)"), AttackRatingS1Hell: d.Number("S1TH(H)"), ElementAttackMode1: d.String("El1Mode"), ElementAttackMode2: d.String("El2Mode"), ElementAttackMode3: d.String("El3Mode"), ElementType1: d.String("El1Type"), ElementType2: d.String("El2Type"), ElementType3: d.String("El3Type"), ElementChance1Normal: d.Number("El1Pct"), ElementChance1Nightmare: d.Number("El1Pct(N)"), ElementChance1Hell: d.Number("El1Pct(H)"), ElementChance2Normal: d.Number("El2Pct"), ElementChance2Nightmare: d.Number("El2Pct(N)"), ElementChance2Hell: d.Number("El2Pct(H)"), ElementChance3Normal: d.Number("El3Pct"), ElementChance3Nightmare: d.Number("El3Pct(N)"), ElementChance3Hell: d.Number("El3Pct(H)"), ElementDamageMin1Normal: d.Number("El1MinD"), ElementDamageMin1Nightmare: d.Number("El1MinD(N)"), ElementDamageMin1Hell: d.Number("El1MinD(H)"), ElementDamageMin2Normal: d.Number("El2MinD"), ElementDamageMin2Nightmare: d.Number("El2MinD(N)"), ElementDamageMin2Hell: d.Number("El2MinD(H)"), ElementDamageMin3Normal: d.Number("El3MinD"), ElementDamageMin3Nightmare: d.Number("El3MinD(N)"), ElementDamageMin3Hell: d.Number("El3MinD(H)"), ElementDamageMax1Normal: d.Number("El1MaxD"), ElementDamageMax1Nightmare: d.Number("El1MaxD(N)"), ElementDamageMax1Hell: d.Number("El1MaxD(H)"), ElementDamageMax2Normal: d.Number("El2MaxD"), ElementDamageMax2Nightmare: d.Number("El2MaxD(N)"), ElementDamageMax2Hell: d.Number("El2MaxD(H)"), ElementDamageMax3Normal: d.Number("El3MaxD"), ElementDamageMax3Nightmare: d.Number("El3MaxD(N)"), ElementDamageMax3Hell: d.Number("El3MaxD(H)"), ElementDuration1Normal: d.Number("El1Dur"), ElementDuration1Nightmare: d.Number("El1Dur(N)"), ElementDuration1Hell: d.Number("El1Dur(H)"), ElementDuration2Normal: d.Number("El2Dur"), ElementDuration2Nightmare: d.Number("El2Dur(N)"), ElementDuration2Hell: d.Number("El2Dur(H)"), ElementDuration3Normal: d.Number("El3Dur"), ElementDuration3Nightmare: d.Number("El3Dur(N)"), ElementDuration3Hell: d.Number("El3Dur(H)"), TreasureClassNormal: d.String("TreasureClass1"), TreasureClassNightmare: d.String("TreasureClass1(N)"), TreasureClassHell: d.String("TreasureClass1(H)"), TreasureClassChampionNormal: d.String("TreasureClass2"), TreasureClassChampionNightmare: d.String("TreasureClass2(N)"), TreasureClassChampionHell: d.String("TreasureClass2(H)"), TreasureClass3UniqueNormal: d.String("TreasureClass3"), TreasureClass3UniqueNightmare: d.String("TreasureClass3(N)"), TreasureClass3UniqueHell: d.String("TreasureClass3(H)"), TreasureClassQuestNormal: d.String("TreasureClass4"), TreasureClassQuestNightmare: d.String("TreasureClass4(N)"), TreasureClassQuestHell: d.String("TreasureClass4(H)"), TreasureClassQuestTriggerId: d.String("TCQuestId"), TreasureClassQuestCompleteId: d.String("TCQuestCP"), SpecialEndDeath: d.Number("SplEndDeath"), SpecialGetModeChart: d.Number("SplGetModeChart") > 0, SpecialEndGeneric: d.Number("SplEndGeneric") > 0, SpecialClientEnd: d.Number("SplClientEnd") > 0, } MonStats[record.Key] = record } if d.Err != nil { panic(d.Err) } log.Printf("Loaded %d MonStats records", len(MonStats)) }