OpenDiablo2/d2common/d2data/d2datadict/monstats.go

1059 lines
53 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package d2datadict
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// https://d2mods.info/forum/kb/viewarticle?a=360
// TODO: these types are described in the notes below, but they aren't used yet
type MonsterCombatType int
const (
// see notes on column `rangedtype`
MonsterMelee MonsterCombatType = iota
MonsterRanged
)
type MonsterAlignmentType int
const (
MonsterEnemy MonsterAlignmentType = iota
MonsterFriend
MonsterNeutral
)
// i've tried to choose better field names instead of what's inside of the txt
// file. after the definition, if there is a comment after the type, it will
// be the original column name corresponding to that field.
// i've tried to preserve the order of the columns, as well.
type MonStatsRecord struct {
// this column contains the pointer that will be used in other txt files
// such as levels.txt and superuniques.txt.
Key string // Id <-- this is the original field name
// this 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 behaviour and crashes so please
// dont 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 // hcIdx
// this column contains the 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). The baseID is responsible for some
// hardcoded behaviours, for example moving thru walls (ghosts), knowing
// what units to ressurect, create etc (putrid defilers, shamans etc), the
// explosion appended to suicide minions (either cold, fire or ice). Thanks
// to Kingpin for additional info on this column.
BaseKey string // BaseId
// this column contains the ID of the next unit in the chain. (Continuing
// on the above example, fallen1 has the ID pointer of fallen2 in here).
// If you want to make a monster subtype less you should simply leave this
// blank and make BaseId point at itself (its ID pointer). The game uses
// this for “map generated” monsters such as the fallen in the fallen camps,
// which get picked based on area level (the same camp, that in the cold
// plains contains normal fallen will contain carvers and devilkin
// elsewhere, read Levels.txt to check how to adjust area level).
NextKey string // NextInClass
// this 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 // TransLvl
// this column contains 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. Without that your monster will be
// displayed as "not used - tell ken" or "an evil force".
// Warning: string keys are case sensitive, so if you enter a key like “Foo”
// in monstats.txt you must enter exactly that in the TBL file! (IE if you
// enter “foo” or “fOO” you will get "not used - tell ken" or "an evil force").
NameStringTableKey string // NameStr
// this column contains the ID pointer to an entry in MonStats2.txt.
// In 1.10 Blizzard has moved all the graphical aspects (light radius,
// bleeding etc) to a new file to conserve space (MonStats.txt is one column
// short of 256, the maximum MS Excel can handle, and thats what they
// probably used for their files).
ExtraDataKey string // MonStatsEx
// this column contains the ID pointer to an entry in MonProp.txt which
// controls what special modifiers are appended to the unit, for example
// you can use it to give your monsters auras, states, random resistances
// or immunities, give them “get hit skills” and almost anything else.
PropertiesKey string // MonProp
// this column contains the group ID of the “super group” this monster
// belongs to, IE all skeletons belong to the "super group" skeleton. The
// 1.10 MonType.txt works exactly like ItemTypes.txt, furthermore this file
// is used for special modifiers such as additional damage vs. monster-class.
MonsterGroup string // MonType
// this column 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).
// Most of AI's require a configuration in aip columns (read about them
// below), without that they (most of them) will do absolutely nothing.
AiKey string // AI
// this column 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 // DescStr
// this is the only graphical setting (besides TransLvl) left in
// MonStats.txt, this controls which token (IE name of a folder that
// contains animations) the game uses for this monster.
AnimationDirectoryToken string // Code
// Boolean, 1=enabled, 0=disabled. This 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 // enabled
// Boolean, 1=ranged attacker, 0=melee attacker. This tells the game
// whenever this is a ranged attacker. It will make it possible for
// monsters to spawn with multiple shot modifier. Also, I suspect this has
// to do with the RANGEDSPAWN column in Levels.txt. (Could it be the game
// uses this for preference settings when spawning monsters to avoid areas
// being populated only with melee monsters, IE the game picks a set amount
// of monsters for every level, randomly, only based on their rarity values,
// from those specified in Levels.txt, now I assume that it could pick 4
// melee monsters, however in 1.10 Blizzard added a check to prevent this
// from happening AFAIK and this could be how they control it.)
IsRanged int // rangedtype
// Boolean, 1=spawner, 0=not a spawner. This 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 // placespawn
// this column contains the ID pointer of the unit to spawn. (in case it is
// a spawner that is), so if you want to make a new monster that generates
// Balrogs this is where you would put the Balrog ID pointer.
SpawnId string // spawn
// 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, albeit its not needed, Blizzards collision detection
// system is good enough to prevent them from getting stuck.
SpawnOffsetX int // spawnx
SpawnOffsetY int // spawny
// which animation mode will the spawned monster be spawned in. IE. If you
// make a golem summoner (yes I know, “very original”) you could put S1 in
// here to make it look as if the golems are really summoned (otherwise they
// would just appear), in most cases you will probably want to use NU mode
// or a sequence though.
SpawnAnimationKey string // spawnmode
// these columns contain the ID pointers to the minions that spawn around
// this unit when it is created.
// NOTE + MINI TUTORIAL: lets say you want your super-strong boss to spawn
// with 5 Oblivion Knights. To do this you would simply enter the Oblivion
// Knights ID pointer in the MINION1 column. And set PARTYMIN and PARTYMAX
// both to 5. MINION1/2 are used for several other things. If the monster
// spawns as unique or superunique then it will have the unit from MINION1/2
// set as its minion instead of monsters of its own type. Thats why
// Lord De Seis doesnt spawn with other oblivion knights anymore. To
// semi-circumvent this I suggest you simply put the monsters ID pointer in
// the MINION2 column (I.E. if you give the Oblivion Knights their own ID
// pointer in MINION2, Lord De Seis should spawn with both Doom Knights and
// Oblivion Knights again). The other use controls what monster is created
// when this unit dies. For example Flayer Shamans will spawn a regular
// Flayer when they are killed. To enable this you must set SPLENDDEATH to
// 1, make sure the unit you spawn this was has a raise or resurrect
// sequence otherwise it will look weird (but it works).
// NOTE: THESE ARE SPAWNED ON MONSTER CREATION, NOTHING TO DO WITH ABOVE
MinionId1 string // minion1
MinionId2 string // minion2
// Boolean, 1=set unit as boss, 0=dont set unit as boss. This is related to
// hardcoded behavior of some of the AI's. IE Scarabs are spawned a bit
// different than most of other monsters. There are 1 or 2 with minions of
// own kind. Thanks to this colums, the "bosses" of a group can have
// ('can" because you may set the chance in percentages ia aip5 column of
// Scarab AI) a chance to order "raid on tagret", what does it change is
// they will always use SK1 instead of A1 and A2 modes while raiding.
IsLeader bool // SetBoss
// Boolean, 1=true, 0=false. This field is connected with the previous one,
// when "boss of the group" is killed, his "leadership" is passed to one of
// his minions.
TransferLeadership bool // BossXfer
// how many minions are spawned together with this unit. As mentioned above
// in the MINION1/2 columns, this controls the quantity of minions this
// unit has.
MinionPartyMin int // PartyMin
MinionPartyMax int // PartyMax
// exactly like the previous two columns, just that this controls how many
// units of the base unit to spawn. In versions 1.00-1.06, setting the
// minimum to more then 99 would crash the game.
MinionGroupMin int // MinGrp
MinionGroupMax int // MaxGrp
// this column controls the overall chance something will spawn in
// percentages, leaving it blank is the same as 100%. If you enter "80" in
// this column then whenever the game chooses to spawn this unit it will
// first roll out the chances. So in 2 out of 10 cases the monster will not
// be spawned and other one may take its place. If you use some low number
// there can be a situation when the game doesn't roll out the monster and
// if this monster has units specified in minion1/minion2 columns it will
// only spawn those minions without the "main" unit.
PopulationReductionPercent int // sparsePopulate
// 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 // Velocity
SpeedRun int // Run
// this column 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 // Rarity
// 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 // Level
LevelNightmare int // Level(N)
LevelHell int // Level(H)
// specifies the ID pointer to this monsters “Sound Bank” in MonSound.txt
// when this monster is normal.
SoundKeyNormal string // MonSound
SoundKeySpecial string // UMonSound
// 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 Merc try to destroy those first.
ThreatLevel int // threat
// 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 // aidel
AiDelayNightmare int // aidel(N)
AiDelayHell int // aidel(H)
// 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 // aidist
AiDistanceNightmare int // aidist(N)
AiDistanceHell int // aidist(H)
// 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 // aip1
AiParameterNormal2 int // aip2
AiParameterNormal3 int // aip3
AiParameterNormal4 int // aip4
AiParameterNormal5 int // aip5
AiParameterNormal6 int // aip6
AiParameterNormal7 int // aip7
AiParameterNormal8 int // aip8
AiParameterNightmare1 int // aip1
AiParameterNightmare2 int // aip2
AiParameterNightmare3 int // aip3
AiParameterNightmare4 int // aip4
AiParameterNightmare5 int // aip5
AiParameterNightmare6 int // aip6
AiParameterNightmare7 int // aip7
AiParameterNightmare8 int // aip8
AiParameterHell1 int // aip1
AiParameterHell2 int // aip2
AiParameterHell3 int // aip3
AiParameterHell4 int // aip4
AiParameterHell5 int // aip5
AiParameterHell6 int // aip6
AiParameterHell7 int // aip7
AiParameterHell8 int // aip8
// 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 succesfully 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 // MissA1
MissileA2 string // MissA2
MissileS1 string // MissS1
MissileS2 string // MissS2
MissileS3 string // MissS3
MissileS4 string // MissS4
MissileC string // MissC
MissileSQ string // MissSQ
// Switch, 0=enemy, 1=aligned, 2=neutral. This setting 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 int // Align
// Boolean, 1=spawnable, 0=not spawnable. This controls whenever this unit
// can be spawned via Levels.txt.
IsLevelSpawnable bool // isSpawn
// Boolean, 1=melee attacker, 0=not a melee attacker. This controls whenever
// this unit can spawn with boss modifiers such as multiple shot or not.
// IE melee monsters will never spawn with multiple shot.
IsMelee bool // isMelee
// Boolean, 1=Im a NPC, 0=Im not. This controls whenever the unit is a NPC
// or not.
IsNpc bool // npc
// Boolean, 1=Special NPC features enabled, 0=No special NPC features. This
// 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 // interact
// Boolean, 1=Has an inventory, 0=Has no inventory. 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 dont do it unless you know
// what youre doing). For Monsters this means that they can access their
// equipment data in MonEquip.txt.
HasInventory bool // inventory
// Boolean, 1=I can enter towns, 0=I cant enter towns. This 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 // inTown
// Boolean. 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 // lUndead
IsUndeadHigh bool // hUndead
// Boolean, This makes the game consider this unit a demon.
IsDemon bool // demon
// Boolean, If you set this to 1 the monster will be able to move fly over
// obstacles such as puddles and rivers.
// TIP: Setting this to 1 for Mephisto will prevent the possibility of
// making him stuck and being slaughtered by blizzard or other ranged spells
// easily.
IsFlying bool // flying
// Boolean, 1=I can open doors, 0=Im too damn retarded to open doors. Ever
// wanted to make the game more like D1 (where closing doors could actually
// protect you), then this column is all you need. By setting this to 0 you
// will succesfully lobotomize the monster, thus he will not be able to open
// doors any more.
CanOpenDoors bool // opendoors
// Boolean, 1=Im a boss, 0=Im not a boss. This 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 // boss
// Boolean, 1=Im a prime evil, 0=Im not a prime evil. (=Act Boss).
// 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 // primeevil
// Setting this to 0 will make the monster absolutely unkillable.
IsKillable bool // killable
// Boolean, 1=Can change sides, 0=Cannot change sides. Gives your monster
// something what I call "Strong Mind", it decides if this units mind may
// be altered by “mind altering skills” like Attract, Conversion, Revive
// etc or not.
IsAiSwitchable bool // switchai
// Boolean, 1=Cant get an aura, 0=Can get an aura. Monsters set to 0 here
// will not be effected by friendly auras
DisableAura bool // noAura
// Boolean, 1=Cant get multishot modifier, 0=Can get multishot modifier.
// This is another layer of security to prevent this modifier from spawning,
// besides the ISMELEE layer.
DisableMultiShot bool // nomultishot
// Boolean, 1=Never accounted for, 0=Accounted for. Setting this to 1
// 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 // neverCount
// Boolean 1=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 // petIgnore
// Boolean, 1=Damage players colliding with my death animation, 0=Dont
// damage anything. 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 // deathDmg
// Boolean, 1=Use generic spawning, 0=Dont use generic spawning. Has to do
// something is with minions being transformed into suicide minions, the
// exact purpose of this is a mystery to me though.
GenericSpawn bool // genericSpawn
// Boolean, 1=true, 0=false. Unused.
// zoo
// Switch, 1=Unknown, 2=Used for assassin traps, 0=Dont send skills. This
// is only used by two of the Assassin traps, however it doesn't serve any
// purpose anymore.
// SendSkills
// 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 // Skill1
SkillId2 string // Skill2
SkillId3 string // Skill3
SkillId4 string // Skill4
SkillId5 string // Skill5
SkillId6 string // Skill6
SkillId7 string // Skill7
SkillId8 string // Skill8
// the graphical MODE (or SEQUENCE) this unit uses when it uses this skill.
SkillAnimation1 string // Sk1mode
SkillAnimation2 string // Sk2mode
SkillAnimation3 string // Sk3mode
SkillAnimation4 string // Sk4mode
SkillAnimation5 string // Sk5mode
SkillAnimation6 string // Sk6mode
SkillAnimation7 string // Sk7mode
SkillAnimation8 string // Sk8mode
// 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 // Sk1lvl
SkillLevel2 int // Sk2lvl
SkillLevel3 int // Sk3lvl
SkillLevel4 int // Sk4lvl
SkillLevel5 int // Sk5lvl
SkillLevel6 int // Sk6lvl
SkillLevel7 int // Sk7lvl
SkillLevel8 int // Sk8lvl
// controls the effectiveness of Life and Mana steal from equipment on this
// unit on the respective difficulties. 0=Cant leech at all. (negative
// values don't damage you, thanks to Doombreed-x for testing this), setting
// it to more then 100 would make LL and ML more effective. Remember that
// besides this, Life and Mana Steal is further limited by
// DifficultyLevels.txt.
LeechSensitivityNormal int // Drain
LeechSensitivityNightmare int // Drain(N)
LeechSensitivityHell int // Drain(H)
// 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 // coldeffect
ColdSensitivityNightmare int // coldeffect(N)
ColdSensitivityHell int // coldeffect(H)
// 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.
// NOTE: even though it may be quite obvious, I already met many people who
// do not know about it. Each point of resistance means 1% of reduction (or
// increase if the value is <0) of damage from said source. The same stands
// for player characters. Same stands for all other resistances. 81% of fire
// resist means 81% of incoming fire damage reduction.
// NOTE 2: when resistance is >100 (immunity), you need 5 points of
// resistance reducing stat applied DIRECTLY to the unit to reduce 1% of
// immunity. It means if you want to break 105% immunity, you will need at
// least 30 resist reduction stat (105 - 6 = 99 and 6 * 5 = 30).
// NOTE 3: yes, yes, indeed, "damage reduced by x%" stat that may spawn on
// items such as The Crown Of Ages or Ber rune is nothing else than physical
// resistance.
ResistancePhysicalNormal int // ResDm
ResistancePhysicalNightmare int // ResDm(N)
ResistancePhysicalHell int // ResDm(H)
ResistanceMagicNormal int // ResMa
ResistanceMagicNightmare int // ResMa(N)
ResistanceMagicHell int // ResMa(H)
ResistanceFireNormal int // ResFi
ResistanceFireNightmare int // ResFi(N)
ResistanceFireHell int // ResFi(H)
ResistanceLightningNormal int // ResLi
ResistanceLightningNightmare int // ResLi(N)
ResistanceLightningHell int // ResLi(H)
ResistanceColdNormal int // ResCo
ResistanceColdNightmare int // ResCo(N)
ResistanceColdHell int // ResCo(H)
ResistancePoisonNormal int // ResPo
ResistancePoisonNightmare int // ResPo(N)
ResistancePoisonHell int // ResPo(H)
// 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 // DamageRegen
// 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 // SkillDamage
// Boolean, 1=Dont use MonLevel.txt, 0=Use MonLevel.txt. 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 // noRatio
// Boolean, 1=Can block without a blocking animation, 0=Cant block without
// a blocking animation. Quite self explanatory, 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 // NoShldBlock
// 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 // ToBlock
ChanceToBlockNightmare int // ToBlock(N)
ChanceToBlockHell int // ToBlock(H)
// this units chance of scoring a critical hit (dealing double the damage).
ChanceDeadlyStrike int // Crit
// 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 // minHP
MinHPNightmare int // MinHP(N)
MinHPHell int // MinHP(H)
MaxHPNormal int // maxHP
MaxHPNightmare int // MaxHP(N)
MaxHPHell int // MaxHP(H)
// this units Armor Class on the respective difficulties. The calculation is
// the same (analogical) as for hit points.
ArmorClassNormal int // AC
ArmorClassNightmare int // AC(N)
ArmorClassHell int // AC(H)
// the experience you get when killing this unit on the respective
// difficulty. The calculation is the same (analogical) as for hit points.
ExperienceNormal int // Exp
ExperienceNightmare int // Exp(N)
ExperienceHell int // Exp(H)
// 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 // A1MinD
DamageMinA1Nightmare int // A1MinD(N)
DamageMinA1Hell int // A1MinD(H)
DamageMaxA1Normal int // A1MaxD
DamageMaxA1Nightmare int // A1MaxD(N)
DamageMaxA1Hell int // A1MaxD(H)
DamageMinA2Normal int // A2MinD
DamageMinA2Nightmare int // A2MinD(N)
DamageMinA2Hell int // A2MinD(H)
DamageMaxA2Normal int // A2MaxD
DamageMaxA2Nightmare int // A2MaxD(N)
DamageMaxA2Hell int // A2MaxD(H)
DamageMinS1Normal int // S1MinD
DamageMinS1Nightmare int // S1MinD(N)
DamageMinS1Hell int // S1MinD(H)
DamageMaxS1Normal int // S1MaxD
DamageMaxS1Nightmare int // S1MaxD(N)
DamageMaxS1Hell int // S1MaxD(H)
// 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 // A1TH
AttackRatingA1Nightmare int // A1TH(N)
AttackRatingA1Hell int // A1TH(H)
AttackRatingA2Normal int // A2TH
AttackRatingA2Nightmare int // A2TH(N)
AttackRatingA2Hell int // A2TH(H)
AttackRatingS1Normal int // S1TH
AttackRatingS1Nightmare int // S1TH(N)
AttackRatingS1Hell int // S1TH(H)
// 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 // El1Mode
ElementAttackMode2 string // El2Mode
ElementAttackMode3 string // El3Mode
// 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 // El1Type
ElementType2 string // El2Type
ElementType3 string // El3Type
// chance to append elemental damage to an attack on the respective
// difficulties. 0=Never append, 100=Always append.
ElementChance1Normal int // El1Pct
ElementChance1Nightmare int // El1Pct(N)
ElementChance1Hell int // El1Pct(H)
ElementChance2Normal int // El2Pct
ElementChance2Nightmare int // El2Pct(N)
ElementChance2Hell int // El2Pct(H)
ElementChance3Normal int // El3Pct
ElementChance3Nightmare int // El3Pct(N)
ElementChance3Hell int // El3Pct(H)
// 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 dont have any set in Missiles.txt. The
// calculation is the same (analogical) as for hit points.
ElementDamageMin1Normal int // El1MinD
ElementDamageMin1Nightmare int // El1MinD(N)
ElementDamageMin1Hell int // El1MinD(H)
ElementDamageMin2Normal int // El2MinD
ElementDamageMin2Nightmare int // El2MinD(N)
ElementDamageMin2Hell int // El2MinD(H)
ElementDamageMin3Normal int // El3MinD
ElementDamageMin3Nightmare int // El3MinD(N)
ElementDamageMin3Hell int // El3MinD(H)
ElementDamageMax1Normal int // El1MaxD
ElementDamageMax1Nightmare int // El1MaxD(N)
ElementDamageMax1Hell int // El1MaxD(H)
ElementDamageMax2Normal int // El2MaxD
ElementDamageMax2Nightmare int // El2MaxD(N)
ElementDamageMax2Hell int // El2MaxD(H)
ElementDamageMax3Normal int // El3MaxD
ElementDamageMax3Nightmare int // El3MaxD(N)
ElementDamageMax3Hell int // El3MaxD(H)
// duration of the elemental effect (for freeze, burn, cold, poison and
// stun) on the respective difficulties.
ElementDuration1Normal int // El1Dur
ElementDuration1Nightmare int // El1Dur(N)
ElementDuration1Hell int // El1Dur(H)
ElementDuration2Normal int // El2Dur
ElementDuration2Nightmare int // El2Dur(N)
ElementDuration2Hell int // El2Dur(H)
ElementDuration3Normal int // El3Dur
ElementDuration3Nightmare int // El3Dur(N)
ElementDuration3Hell int // El3Dur(H)
// the TreasureClass used by this unit as a normal monster on the respective
// difficulties.
// NOTE: because of the new TreasureClass system introduced in 1.10 and
// later patches, TC entries are only of minor influence regarding what TC
// is being selected unless you change the system by editing
// TreasureClassEX.txt.
TreasureClassNormal string // TreasureClass1
TreasureClassNightmare string // TreasureClass1(N)
TreasureClassHell string // TreasureClass1(H)
TreasureClassChampionNormal string // TreasureClass2
TreasureClassChampionNightmare string // TreasureClass2(N)
TreasureClassChampionHell string // TreasureClass2(H)
TreasureClass3UniqueNormal string // TreasureClass3
TreasureClass3UniqueNightmare string // TreasureClass3(N)
TreasureClass3UniqueHell string // TreasureClass3(H)
TreasureClassQuestNormal string // TreasureClass4
TreasureClassQuestNightmare string // TreasureClass4(N)
TreasureClassQuestHell string // TreasureClass4(H)
// the ID of the Quest that triggers the Quest Treasureclass drop.
TreasureClassQuestTriggerId string // TCQuestId
// the ID of the Quest State that you need to complete to trigger the Quest
// Treasureclass trop.
TreasureClassQuestComleteId string // TCQuestCP
// TODO: fix these last 4 field names, they're fucking ridiculous
// Switch, 0=no special death, 1=spawn the monster in the MINION1 column
// when I die, 2=kill whatever monster is mounted to me when I die (used by
// guard towers that kill the imps that are on top of them when they die).
SpecialEndDeath int // SplEndDeath
// Boolean, 1=Get Special Mode Chart, 0=Dont get special mode chart.
// 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 // SplGetModeChart
// Boolean, 1=true, 0=false. 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 // SplEndGeneric
// Boolean, 1=true, 0=false. 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 // SplClientEnd
}
var MonStats map[string]*MonStatsRecord
func LoadMonStats(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRecords := len(dict.Data)
MonStats = make(map[string]*MonStatsRecord, numRecords)
for idx := range dict.Data {
record := &MonStatsRecord{
Key: dict.GetString("Id", idx),
Id: dict.GetString("hcIdx", idx),
BaseKey: dict.GetString("BaseId", idx),
NextKey: dict.GetString("NextInClass", idx),
PaletteId: dict.GetNumber("TransLvl", idx),
NameStringTableKey: dict.GetString("NameStr", idx),
ExtraDataKey: dict.GetString("MonStatsEx", idx),
PropertiesKey: dict.GetString("MonProp", idx),
MonsterGroup: dict.GetString("MonType", idx),
AiKey: dict.GetString("AI", idx),
DescriptionStringTableKey: dict.GetString("DescStr", idx),
AnimationDirectoryToken: dict.GetString("Code", idx),
Enabled: dict.GetNumber("enabled", idx) > 0,
IsRanged: dict.GetNumber("rangedtype", idx),
SpawnsMinions: dict.GetNumber("placespawn", idx) > 0,
SpawnId: dict.GetString("spawn", idx),
SpawnOffsetX: dict.GetNumber("spawnx", idx),
SpawnOffsetY: dict.GetNumber("spawny", idx),
SpawnAnimationKey: dict.GetString("spawnmode", idx),
MinionId1: dict.GetString("minion1", idx),
MinionId2: dict.GetString("minion2", idx),
IsLeader: dict.GetNumber("SetBoss", idx) > 0,
TransferLeadership: dict.GetNumber("BossXfer", idx) > 0,
MinionPartyMin: dict.GetNumber("PartyMin", idx),
MinionPartyMax: dict.GetNumber("PartyMax", idx),
MinionGroupMin: dict.GetNumber("MinGrp", idx),
MinionGroupMax: dict.GetNumber("MaxGrp", idx),
PopulationReductionPercent: dict.GetNumber("sparsePopulate", idx),
SpeedBase: dict.GetNumber("Velocity", idx),
SpeedRun: dict.GetNumber("Run", idx),
Rarity: dict.GetNumber("Rarity", idx),
LevelNormal: dict.GetNumber("Level", idx),
LevelNightmare: dict.GetNumber("Level(N)", idx),
LevelHell: dict.GetNumber("Level(H)", idx),
SoundKeyNormal: dict.GetString("MonSound", idx),
SoundKeySpecial: dict.GetString("UMonSound", idx),
ThreatLevel: dict.GetNumber("threat", idx),
AiDelayNormal: dict.GetNumber("aidel", idx),
AiDelayNightmare: dict.GetNumber("aidel(N)", idx),
AiDelayHell: dict.GetNumber("aidel(H)", idx),
AiDistanceNormal: dict.GetNumber("aidist", idx),
AiDistanceNightmare: dict.GetNumber("aidist(N)", idx),
AiDistanceHell: dict.GetNumber("aidist(H)", idx),
AiParameterNormal1: dict.GetNumber("aip1", idx),
AiParameterNormal2: dict.GetNumber("aip2", idx),
AiParameterNormal3: dict.GetNumber("aip3", idx),
AiParameterNormal4: dict.GetNumber("aip4", idx),
AiParameterNormal5: dict.GetNumber("aip5", idx),
AiParameterNormal6: dict.GetNumber("aip6", idx),
AiParameterNormal7: dict.GetNumber("aip7", idx),
AiParameterNormal8: dict.GetNumber("aip8", idx),
AiParameterNightmare1: dict.GetNumber("aip1", idx),
AiParameterNightmare2: dict.GetNumber("aip2", idx),
AiParameterNightmare3: dict.GetNumber("aip3", idx),
AiParameterNightmare4: dict.GetNumber("aip4", idx),
AiParameterNightmare5: dict.GetNumber("aip5", idx),
AiParameterNightmare6: dict.GetNumber("aip6", idx),
AiParameterNightmare7: dict.GetNumber("aip7", idx),
AiParameterNightmare8: dict.GetNumber("aip8", idx),
AiParameterHell1: dict.GetNumber("aip1", idx),
AiParameterHell2: dict.GetNumber("aip2", idx),
AiParameterHell3: dict.GetNumber("aip3", idx),
AiParameterHell4: dict.GetNumber("aip4", idx),
AiParameterHell5: dict.GetNumber("aip5", idx),
AiParameterHell6: dict.GetNumber("aip6", idx),
AiParameterHell7: dict.GetNumber("aip7", idx),
AiParameterHell8: dict.GetNumber("aip8", idx),
MissileA1: dict.GetString("MissA1", idx),
MissileA2: dict.GetString("MissA2", idx),
MissileS1: dict.GetString("MissS1", idx),
MissileS2: dict.GetString("MissS2", idx),
MissileS3: dict.GetString("MissS3", idx),
MissileS4: dict.GetString("MissS4", idx),
MissileC: dict.GetString("MissC", idx),
MissileSQ: dict.GetString("MissSQ", idx),
Alignment: dict.GetNumber("Align", idx),
IsLevelSpawnable: dict.GetNumber("isSpawn", idx) > 0,
IsMelee: dict.GetNumber("isMelee", idx) > 0,
IsNpc: dict.GetNumber("npc", idx) > 0,
IsInteractable: dict.GetNumber("interact", idx) > 0,
HasInventory: dict.GetNumber("inventory", idx) > 0,
CanEnterTown: dict.GetNumber("inTown", idx) > 0,
IsUndeadLow: dict.GetNumber("lUndead", idx) > 0,
IsUndeadHigh: dict.GetNumber("hUndead", idx) > 0,
IsDemon: dict.GetNumber("demon", idx) > 0,
IsFlying: dict.GetNumber("flying", idx) > 0,
CanOpenDoors: dict.GetNumber("opendoors", idx) > 0,
IsSpecialBoss: dict.GetNumber("boss", idx) > 0,
IsActBoss: dict.GetNumber("primeevil", idx) > 0,
IsKillable: dict.GetNumber("killable", idx) > 0,
IsAiSwitchable: dict.GetNumber("switchai", idx) > 0,
DisableAura: dict.GetNumber("noAura", idx) > 0,
DisableMultiShot: dict.GetNumber("nomultishot", idx) > 0,
DisableCounting: dict.GetNumber("neverCount", idx) > 0,
IgnorePets: dict.GetNumber("petIgnore", idx) > 0,
DealsDamageOnDeath: dict.GetNumber("deathDmg", idx) > 0,
GenericSpawn: dict.GetNumber("genericSpawn", idx) > 0,
SkillId1: dict.GetString("Skill1", idx),
SkillId2: dict.GetString("Skill2", idx),
SkillId3: dict.GetString("Skill3", idx),
SkillId4: dict.GetString("Skill4", idx),
SkillId5: dict.GetString("Skill5", idx),
SkillId6: dict.GetString("Skill6", idx),
SkillId7: dict.GetString("Skill7", idx),
SkillId8: dict.GetString("Skill8", idx),
SkillAnimation1: dict.GetString("Sk1mode", idx),
SkillAnimation2: dict.GetString("Sk2mode", idx),
SkillAnimation3: dict.GetString("Sk3mode", idx),
SkillAnimation4: dict.GetString("Sk4mode", idx),
SkillAnimation5: dict.GetString("Sk5mode", idx),
SkillAnimation6: dict.GetString("Sk6mode", idx),
SkillAnimation7: dict.GetString("Sk7mode", idx),
SkillAnimation8: dict.GetString("Sk8mode", idx),
SkillLevel1: dict.GetNumber("Sk1lvl", idx),
SkillLevel2: dict.GetNumber("Sk2lvl", idx),
SkillLevel3: dict.GetNumber("Sk3lvl", idx),
SkillLevel4: dict.GetNumber("Sk4lvl", idx),
SkillLevel5: dict.GetNumber("Sk5lvl", idx),
SkillLevel6: dict.GetNumber("Sk6lvl", idx),
SkillLevel7: dict.GetNumber("Sk7lvl", idx),
SkillLevel8: dict.GetNumber("Sk8lvl", idx),
LeechSensitivityNormal: dict.GetNumber("Drain", idx),
LeechSensitivityNightmare: dict.GetNumber("Drain(N)", idx),
LeechSensitivityHell: dict.GetNumber("Drain(H)", idx),
ColdSensitivityNormal: dict.GetNumber("coldeffect", idx),
ColdSensitivityNightmare: dict.GetNumber("coldeffect(N)", idx),
ColdSensitivityHell: dict.GetNumber("coldeffect(H)", idx),
ResistancePhysicalNormal: dict.GetNumber("ResDm", idx),
ResistancePhysicalNightmare: dict.GetNumber("ResDm(N)", idx),
ResistancePhysicalHell: dict.GetNumber("ResDm(H)", idx),
ResistanceMagicNormal: dict.GetNumber("ResMa", idx),
ResistanceMagicNightmare: dict.GetNumber("ResMa(N)", idx),
ResistanceMagicHell: dict.GetNumber("ResMa(H)", idx),
ResistanceFireNormal: dict.GetNumber("ResFi", idx),
ResistanceFireNightmare: dict.GetNumber("ResFi(N)", idx),
ResistanceFireHell: dict.GetNumber("ResFi(H)", idx),
ResistanceLightningNormal: dict.GetNumber("ResLi", idx),
ResistanceLightningNightmare: dict.GetNumber("ResLi(N)", idx),
ResistanceLightningHell: dict.GetNumber("ResLi(H)", idx),
ResistanceColdNormal: dict.GetNumber("ResCo", idx),
ResistanceColdNightmare: dict.GetNumber("ResCo(N)", idx),
ResistanceColdHell: dict.GetNumber("ResCo(H)", idx),
ResistancePoisonNormal: dict.GetNumber("ResPo", idx),
ResistancePoisonNightmare: dict.GetNumber("ResPo(N)", idx),
ResistancePoisonHell: dict.GetNumber("ResPo(H)", idx),
HealthRegenPerFrame: dict.GetNumber("DamageRegen", idx),
DamageSkillId: dict.GetString("SkillDamage", idx),
IgnoreMonLevelTxt: dict.GetNumber("noRatio", idx) > 0,
CanBlockWithoutShield: dict.GetNumber("NoShldBlock", idx) > 0,
ChanceToBlockNormal: dict.GetNumber("ToBlock", idx),
ChanceToBlockNightmare: dict.GetNumber("ToBlock(N)", idx),
ChanceToBlockHell: dict.GetNumber("ToBlock(H)", idx),
ChanceDeadlyStrike: dict.GetNumber("Crit", idx),
MinHPNormal: dict.GetNumber("minHP", idx),
MinHPNightmare: dict.GetNumber("MinHP(N)", idx),
MinHPHell: dict.GetNumber("MinHP(H)", idx),
MaxHPNormal: dict.GetNumber("maxHP", idx),
MaxHPNightmare: dict.GetNumber("MaxHP(N)", idx),
MaxHPHell: dict.GetNumber("MaxHP(H)", idx),
ArmorClassNormal: dict.GetNumber("AC", idx),
ArmorClassNightmare: dict.GetNumber("AC(N)", idx),
ArmorClassHell: dict.GetNumber("AC(H)", idx),
ExperienceNormal: dict.GetNumber("Exp", idx),
ExperienceNightmare: dict.GetNumber("Exp(N)", idx),
ExperienceHell: dict.GetNumber("Exp(H)", idx),
DamageMinA1Normal: dict.GetNumber("A1MinD", idx),
DamageMinA1Nightmare: dict.GetNumber("A1MinD(N)", idx),
DamageMinA1Hell: dict.GetNumber("A1MinD(H)", idx),
DamageMaxA1Normal: dict.GetNumber("A1MaxD", idx),
DamageMaxA1Nightmare: dict.GetNumber("A1MaxD(N)", idx),
DamageMaxA1Hell: dict.GetNumber("A1MaxD(H)", idx),
DamageMinA2Normal: dict.GetNumber("A2MinD", idx),
DamageMinA2Nightmare: dict.GetNumber("A2MinD(N)", idx),
DamageMinA2Hell: dict.GetNumber("A2MinD(H)", idx),
DamageMaxA2Normal: dict.GetNumber("A2MaxD", idx),
DamageMaxA2Nightmare: dict.GetNumber("A2MaxD(N)", idx),
DamageMaxA2Hell: dict.GetNumber("A2MaxD(H)", idx),
DamageMinS1Normal: dict.GetNumber("S1MinD", idx),
DamageMinS1Nightmare: dict.GetNumber("S1MinD(N)", idx),
DamageMinS1Hell: dict.GetNumber("S1MinD(H)", idx),
DamageMaxS1Normal: dict.GetNumber("S1MaxD", idx),
DamageMaxS1Nightmare: dict.GetNumber("S1MaxD(N)", idx),
DamageMaxS1Hell: dict.GetNumber("S1MaxD(H)", idx),
AttackRatingA1Normal: dict.GetNumber("A1TH", idx),
AttackRatingA1Nightmare: dict.GetNumber("A1TH(N)", idx),
AttackRatingA1Hell: dict.GetNumber("A1TH(H)", idx),
AttackRatingA2Normal: dict.GetNumber("A2TH", idx),
AttackRatingA2Nightmare: dict.GetNumber("A2TH(N)", idx),
AttackRatingA2Hell: dict.GetNumber("A2TH(H)", idx),
AttackRatingS1Normal: dict.GetNumber("S1TH", idx),
AttackRatingS1Nightmare: dict.GetNumber("S1TH(N)", idx),
AttackRatingS1Hell: dict.GetNumber("S1TH(H)", idx),
ElementAttackMode1: dict.GetString("El1Mode", idx),
ElementAttackMode2: dict.GetString("El2Mode", idx),
ElementAttackMode3: dict.GetString("El3Mode", idx),
ElementType1: dict.GetString("El1Type", idx),
ElementType2: dict.GetString("El2Type", idx),
ElementType3: dict.GetString("El3Type", idx),
ElementChance1Normal: dict.GetNumber("El1Pct", idx),
ElementChance1Nightmare: dict.GetNumber("El1Pct(N)", idx),
ElementChance1Hell: dict.GetNumber("El1Pct(H)", idx),
ElementChance2Normal: dict.GetNumber("El2Pct", idx),
ElementChance2Nightmare: dict.GetNumber("El2Pct(N)", idx),
ElementChance2Hell: dict.GetNumber("El2Pct(H)", idx),
ElementChance3Normal: dict.GetNumber("El3Pct", idx),
ElementChance3Nightmare: dict.GetNumber("El3Pct(N)", idx),
ElementChance3Hell: dict.GetNumber("El3Pct(H)", idx),
ElementDamageMin1Normal: dict.GetNumber("El1MinD", idx),
ElementDamageMin1Nightmare: dict.GetNumber("El1MinD(N)", idx),
ElementDamageMin1Hell: dict.GetNumber("El1MinD(H)", idx),
ElementDamageMin2Normal: dict.GetNumber("El2MinD", idx),
ElementDamageMin2Nightmare: dict.GetNumber("El2MinD(N)", idx),
ElementDamageMin2Hell: dict.GetNumber("El2MinD(H)", idx),
ElementDamageMin3Normal: dict.GetNumber("El3MinD", idx),
ElementDamageMin3Nightmare: dict.GetNumber("El3MinD(N)", idx),
ElementDamageMin3Hell: dict.GetNumber("El3MinD(H)", idx),
ElementDamageMax1Normal: dict.GetNumber("El1MaxD", idx),
ElementDamageMax1Nightmare: dict.GetNumber("El1MaxD(N)", idx),
ElementDamageMax1Hell: dict.GetNumber("El1MaxD(H)", idx),
ElementDamageMax2Normal: dict.GetNumber("El2MaxD", idx),
ElementDamageMax2Nightmare: dict.GetNumber("El2MaxD(N)", idx),
ElementDamageMax2Hell: dict.GetNumber("El2MaxD(H)", idx),
ElementDamageMax3Normal: dict.GetNumber("El3MaxD", idx),
ElementDamageMax3Nightmare: dict.GetNumber("El3MaxD(N)", idx),
ElementDamageMax3Hell: dict.GetNumber("El3MaxD(H)", idx),
ElementDuration1Normal: dict.GetNumber("El1Dur", idx),
ElementDuration1Nightmare: dict.GetNumber("El1Dur(N)", idx),
ElementDuration1Hell: dict.GetNumber("El1Dur(H)", idx),
ElementDuration2Normal: dict.GetNumber("El2Dur", idx),
ElementDuration2Nightmare: dict.GetNumber("El2Dur(N)", idx),
ElementDuration2Hell: dict.GetNumber("El2Dur(H)", idx),
ElementDuration3Normal: dict.GetNumber("El3Dur", idx),
ElementDuration3Nightmare: dict.GetNumber("El3Dur(N)", idx),
ElementDuration3Hell: dict.GetNumber("El3Dur(H)", idx),
TreasureClassNormal: dict.GetString("TreasureClass1", idx),
TreasureClassNightmare: dict.GetString("TreasureClass1(N)", idx),
TreasureClassHell: dict.GetString("TreasureClass1(H)", idx),
TreasureClassChampionNormal: dict.GetString("TreasureClass2", idx),
TreasureClassChampionNightmare: dict.GetString("TreasureClass2(N)", idx),
TreasureClassChampionHell: dict.GetString("TreasureClass2(H)", idx),
TreasureClass3UniqueNormal: dict.GetString("TreasureClass3", idx),
TreasureClass3UniqueNightmare: dict.GetString("TreasureClass3(N)", idx),
TreasureClass3UniqueHell: dict.GetString("TreasureClass3(H)", idx),
TreasureClassQuestNormal: dict.GetString("TreasureClass4", idx),
TreasureClassQuestNightmare: dict.GetString("TreasureClass4(N)", idx),
TreasureClassQuestHell: dict.GetString("TreasureClass4(H)", idx),
TreasureClassQuestTriggerId: dict.GetString("TCQuestId", idx),
TreasureClassQuestComleteId: dict.GetString("TCQuestCP", idx),
SpecialEndDeath: dict.GetNumber("SplEndDeath", idx),
SpecialGetModeChart: dict.GetNumber("SplGetModeChart", idx) > 0,
SpecialEndGeneric: dict.GetNumber("SplEndGeneric", idx) > 0,
SpecialClientEnd: dict.GetNumber("SplClientEnd", idx) > 0,
}
MonStats[record.Key] = record
}
log.Printf("Loaded %d MonStats records", len(MonStats))
}