-
Notifications
You must be signed in to change notification settings - Fork 68
Combat
The bow attack is possible every (256-pc.attr[SPEED])/16+4
frames.
If the melee attack didn't affect any targets, and one of 8 neighboring threatened cells contain a chest or a door, the bashing is performed.
(to do)
The speed of arrows and spells is 100 units/frame. Two collision checks are made per frame, for 50 and 100 unit distance.
The arrows and spells exist for 30 frames.
The spell projectiles have a light radius of 500.
The armor class is separate for each of 7 paperdoll slots. It is calculated as the sum of the corresponding item AC, shield AC if the shield covers the corresponding slot, and any AC-affecting items the (N)PC has equipped.
7-item byte arrays for each shield type are located @45159
, @45160
, @45168
, and @4516F
. If the corresponding element is non-zero, the shield AC is added.
Successful attacks break the invisibility for 20 frames.
if attacker.race in (REDGUARD, DARKELF, WOODELF) then
racial <- attacker.level*256/100
else
racial <- 0
endif
chance1 <- 128+((attacker.level-defender.level)*5)*256/100+(attacker.luckBonus-defender.luckBonus)+racial+(attacker.hitBonus-defender.defenceBonus)-defender.AC[slot]
chance2 <- player.level*256/100+51
return (rnd(256)<max(chance1,chance2))
int GetCriticalStrikeMultipler(NPCDATA* attacker)
{
int level = attacker->level;
int class = attacker->class;
if ((class & 0x40) != 0) // Has critical strikes
{
int percentChancePerLevel = 1;
if ((class & 0x1f) == 10) // Thief
{
percentChancePerLevel = 2;
}
else if (class == 0x4d) // Archer
{
if (!IsEquippedWeaponARangedWeaponOrDagger(attacker)) {
return 1; // No critical hits
}
percentChancePerLevel = 3;
goto rollforCriticalStrike;
}
else if (class == 0xcb) // Assassin
{
percentChancePerLevel = 3;
}
if (!IsEquippedWeaponARangedWeaponOrDagger(attacker))
{
if (class == 0x4c) // Monk
{
percentChancePerLevel = 3;
level = level >> 1;
}
rollforCriticalStrike:
int percentChance = percentChancePerLevel * level;
int random = rand() % 100;
int damageMultiplier = 1;
if (random < percentChance)
{
if (attacker == Player)
{
ShowCriticalStrikeMessage();
}
damageMultiplier = 3;
}
return damageMultiplier;
}
}
return 1; // No critical strike
}
IsEquippedWeaponARangedWeaponOrDagger()
returns true if a weapon is equipped and it's SlotID
matches any of the entries in the 3-byte array @3CF50
.
The damage total calculation and application when the player attacks an enemy is done in the order of the steps shown here:
- The base minimum and maximum damage is retrieved and a random value chosen from between them as the base damage.
The random value is chosen from the
minDamage .. (maxDamage - 1)
range. (This is probably a bug.)
If a weapon is equipped, that weapon's min-max damage is used.
If no weapon is equipped, damage depends on if and what kind of gauntlets are equipped. These min-max damage values are stored in pairs in a 6-byte array @475FC
.
- If there is a racial bonus it is added to the damage. (Redguards and Wood Elf bonus are probably reversed from what they were supposed to be):
- Dark Elves:
npc.level/4
- Redguards:
npc.level/3
if a ranged weapon is equipped (see above). - Wood Elves:
npc.level/3
if no ranged weapon is equipped.
-
The damage is multiplied by the critical strike multiplier.
-
For rangers, their
npc.level+1
is added to the damage if the target is not undead. (This is broken in the original game(see below).)
int ApplyRangerBonus(NPCDATA* npcData, int damage)
{
if (npcData->Class == 0xe) // is a Ranger
{
// There are 2 bugs here. The player's status flags are checked instead of the opponent's, and IsUndead() is
// passed the current damage total although it expects the enemy type ID.
// It may also be a bug that the bonus is given for non-creature enemies.
if ((npcData->StatusFlags & 0x1000) && IsUndead(damage)) // The 0x1000 flag means a creature (non-class) enemy.
{
return damage; // No bonus.
}
damage = damage + (npcData->Level + 1);
}
return damage;
}
bool IsUndead(int creatureID)
{
int i = 7;
int index = 0;
do {
if ((UndeadCreatureIDs[index] == creatureID)
{
return true;
}
index++;
i--;
} while (i != 0);
return false;
}
UndeadCreatureIDs
are the seven from the list @47624
.
-
If the target is undead and the weapon is silver, the damage is doubled.
-
The damage to the weapon is calculated and applied.
-
If the target is a Skeleton and the weapon is in blade/axe/bow family, the damage to the target is halved.
-
The damage to the target's armor is calculated and applied.
-
If the weapon is Volendrung or Auriel's Bow, the damage to the target is tripled.
-
If the weapon is Volendrung and the target is not a Knight, the target is paralyzed for 10 minutes (?), and the weapon's health is decreased by 800.
-
The damage to the target is subtracted from the target's health.
-
If the weapon is the Ebony Blade, half of the damage that was subtracted from the target is added to the player's health.
- Golems are resistant to materials below
ELVEN + (race - ICEGOLEM)
. - Fire demons and higher monsters are resistant to materials given in the 4-element array
@4762B
. - Vampires are also not resistant to silver.
Successful material resistance is equivalent to a miss.
itm <- defender.equipped[SHIELD]
if itm and not shieldHasAC(itm, slot) then itm <- none
if not itm then itm <- defender.equipped[slot]
if itm then
dmg <- (damage * materialStrength[itm.material] + 50) / 100
if itm.isArtifact then dmg <- dmg / 2
itm.damage(dmg)
endif
dmg <- damage
if itm.isArtifact then dmg <- dmg / 2
itm.damage(dmg)