Skip to content
Allofich edited this page May 12, 2024 · 29 revisions

Weapons

Bows

The bow attack is possible every (256-pc.attr[SPEED])/16+4 frames.

Bashing

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)

Arrows and spells

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.

Armor class

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.

Hit chance

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]
if defender.class in (MONK, ACROBAT) then
   chance1 <- (chance1*8/256)*(defender.level+1)
endif
chance2 <- player.level*256/100+51

return (rnd(256)<max(chance1,chance2))

Critical strike

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; // 1x damage (no critical strike)
      }
      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; // 1x damage (no critical strike)
}

IsEquippedWeaponARangedWeaponOrDagger() returns true if a weapon is equipped and its SlotID matches any of the entries in the 3-byte array @3CF50.

Damage modifiers

Weapons

The damage total calculation and application when the player attacks an enemy is done in the order of the steps shown here:

  1. 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.

  1. 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.
  1. The damage is multiplied by the critical strike multiplier.

  2. 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) {
  int damageWithRangerBonus = damage;
  if (npcData->Class == 0xe) { // is a Ranger
    // 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 ((TargetNpcData->StatusFlags & 0x1000) && IsUndead(damage)) { // The 0x1000 flag means a creature (non-class) enemy.
      return damage; // No bonus.
    }
    damageWithRangerBonus += (npcData->Level) + 1;
  }
  return damageWithRangerBonus;
}

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.

  1. If the target is undead and the weapon is silver, the damage is doubled.

  2. The damage to the weapon is calculated and applied.

  3. If the target is a Skeleton and the weapon is in blade/axe/bow family, the damage to the target is halved.

  4. The damage to the target's armor is calculated and applied.

  5. If the weapon is Volendrung or Auriel's Bow, the damage to the target is tripled.

  6. 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.

  7. The damage to the target is subtracted from the target's health.

  8. If the weapon is the Ebony Blade, half of the damage that was subtracted from the target is added to the player's health.

Material resistance

  • 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.

Item degradation

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

From bashing

dmg <- damage
if itm.isArtifact then dmg <- dmg / 2
itm.damage(dmg)
Clone this wiki locally