-
Notifications
You must be signed in to change notification settings - Fork 68
NPCs
The sprite size formula:
# mul2 == mul3 == 160
# scale2 == 200
# var3 == ???
baseH <- ((sprite.H * obj.Scale)/256*scale2)/256
baseW <- (sprite.W * obj.Scale)/256
pX,pY <- project(obj.X-camera.X,obj.Y-camera.Y)
if pY<=20 then return
pX <- pX + var3
WW <- (baseW*mul2)/pY
HH <- (baseH*mul3)/pY
XX <- (pX*mul2)/pY + centerX - WW/2
YY <- (((sprite.Z+camera.Z)-baseH)*mul3)/pY + centerY
The monster races are 1-based. The lists below are indexed by race - 1
. There are 24 different monster types.
- Names:
szlist @3A8EE
- Level: byte array
@42096
(zero-based, as with the player) - Hit points: 48-entry
WORD
array@420DE
(min, max format) - Base experience:
DWORD
array@4213E
. - Experience multiplier: byte array
@4219E
- Sound: byte array
@4201E
. An index into theVOC
szlist @437CD
- Damage: 48-entry byte array
@421B6
(min, max format) - Magical effects:
WORD
array@420AE
(these go into the NPC'sActiveEffects
) - Scale:
WORD
array@42066
(in 1/256s, 0 = 100%) - Y offset: signed byte array
@42036
- Has no corpse: byte array
@4204E
- Blood: byte array
@4762F
(index into animation list@42EFC
) - Disease chance: signed byte array
@42212
. Negative values have special meaning.
For the final boss, npcData->KnownSpellCount
is set to 6, and a byte-array @421F9
is used for assigning to npcData->KnownSpellIDs
. These IDs (0-based) match the order of the spells as seen in the "SPELLS" files, such as SPELLS.LST
.
@421F9
6h Wizard's Fire
Ch Ice Bolt
14h Wyvern's Sting
1Ch Lightning
20h Far Silence
3Fh Spell Drain
For other enemy-types, the following is done. "creatureID" is a 0-based byte for the enemy index. "npcData" is a pointer to an NPCDATA structure.
if (creatureID != 0x23) { // If not the final boss
if (npcData->Race == 0x16 || npcData->Race == 0x0E || npcData->Race == 0x17) // If Vampire, Troll or Lich
{
AddRegeneration(); // TODO: Parameters passed to this function differ for these three enemy types
}
npcData->KnownSpellCount = 0;
if (creatureID == 4) { // Snow Wolf
npcData->KnownSpellCount = 1;
npcData->KnownSpellIDs[0] = 0xc; // Ice Bolt
*(byte *)(npcData + 8) = 0xff; // Unknown (number of spell casts? 0xFF for infinite?)
*(byte *)(npcData + 5) = 6; // Unknown (caster level?)
}
if (creatureID == 0xB) { // Ghost
npcData->KnownSpellCount = 1;
npcData->KnownSpellIDs[0] = 0x3f; // Spell Drain
*(byte *)(npcData + 8) = 0xff; // Unknown
*(byte *)(npcData + 5) = 5; // Unknown
}
if (creatureID == 0xA) { // Hell Hound
npcData->KnownSpellCount = 1;
npcData->KnownSpellIDs[0] = 0x10; // Fireball
*(byte *)(npcData + 8) = 0xff; // Unknown
*(byte *)(npcData + 5) = 6; // Unknown
}
if (creatureID >= 0xE) { // Wraith or higher
npcData->KnownSpellCount = CreatureKnownSpellCounts[creatureID]; // Byte-array at @421E6
npcData->KnownSpellIDs[0] = CreatureKnownSpellIDs[creatureID]; // Byte-array at @421F0
*(byte *)(npcData + 8) = CreatureSpellUnknowns[creatureID]; // Byte-array at @42209
*(byte *)(npcData + 5) = CreatureSpellUnknowns2[creatureID]; // Byte-array at @421FF
}
}
CreatureKnownSpellCounts
1h Wraith
1h Homonculus
0h Ice Golem
1h Stone Golem
0h Iron Golem
1h Fire Daemon
1h Medusa
1h Vampire
1h Lich
6h Final boss (unused?)
CreatureKnownSpellIDs
10h Wraith: Fireball
1Ch Homonculus: Lightning
FFh Ice Golem: None
1Ch Stone Golem: Lightning
FFh Iron Golem: None
23h Fire Daemon: Fire Storm
2Ah Medusa: Medusa's Gaze
10h Vampire: Fireball
1Ch Lich: Lightning
(From the next byte is the 6-element array for the final boss's spell IDs)
CreatureSpellUnknowns (numbers of spell casts?)
3h Wraith
5h Homonculus
0h Ice Golem
3h Stone Golem
0h Iron Golem
5h Fire Daemon
5h Medusa
5h Vampire
5h Lich
5h Final boss (unused?)
CreatureSpellUnknowns2 (caster levels?)
Bh Wraith
Ch Homonculus
0h Ice Golem
Eh Stone Golem
0h Iron Golem
10h Fire Daemon
11h Medusa
12h Vampire
13h Lich
13h Final boss (unused?)
TODO: spells for enemy humanoids, regeneration/ability details
Creature experience is calculated as baseExperience + maxHP * experienceMultiplier
.
- Animation:
@4222B
. Monsters have a special 3-frame death animation at the index 5 (...6
).
00..05 walk
06..07 look around (a special sequence of 6, 0, 7, 0)
08..11 attack (damage is done in the frame 10)
Humanoids have an animation combined of walk and attack part. The attack part is also overlaid with a weapon animation, when applicable.
- If an NPC has a cuirass, or at least 3 other items, of a certain armor type, that type is used for the sprite (indices 0..2)
- Otherwise, it is 3
- For spellcasters, it is 4, for Monks 5, and for Barbarians 6.
The sprite type array is located @45D20
.
- The weapon index is 0 to 2 for swords, axes, and maces correspondingly.
- If the sprite index was 0, weapon index increases by 3, if 6 by 6.
A female variant is not selected for the plate armor sprite (index 0).
The weapon sprite array is located @45D97
- For monks and mages, the special attack animations are used,
MNKKIC
,MAGSWD
, andMAGSTF
.
Humanoids do not have the death animation, they are replaced with a 'corpse' sprite (ITEM 2
).
Townsfolk do not have NPC properties. They are dying with a single hit, yielding 1..4 gold and corresponding message.
Male sprites (@4559A
) are chosen by the tileset. Special female sprites are chosen for the desert tileset (FMGEND
) and snow/snow overcast weather (FMGENW
).
For clothing transformation, the random value Data & 0x7fff
is used. colorBase
is a byte array @47096
.
val <- seed
for i <- 0, 16
flag <- val & 0x8000
val <- rol16(val,1)
if flag then
block <- val & 0xF
dest <- colorBase[i]
if dest == 128 and block == 11 then continue # no green hair
src <- colorBase[block]
for j <- 0, 10
new[src+j] <- old[dest+j]
For skin transformation, the following values are used:
- Bretons, Nords, Wood Elves, Khajiits - no transformation
- Dark Elves -
52
- High Elves -
192
- Argonians -
116
- Everyone else -
148
skinColor
is a byte array @470A6
.
for i <- 0, 10
new[skinColor[i]] <- old[VAL+i]