View Single Post
  #7  
Old 06-05-2025, 04:25 AM
Torven
Sarnak
 
Join Date: Aug 2014
Posts: 76
Default

Code:
// CheckResistSpell() from November 29 2000 client.  Decompiled with Ghidra
// Note that many of the subclass references and NULL checks were removed for readability
// Also note that the compiler reuses variables.  I have renamed some variables with multiple names for readability
// A return of 100 means full resist.  0 means full hit

uint __thiscall CheckResistSpell(int this,int spell,byte caster_level,int no_resist_floor,int caster,int spell_id)
{
  byte *first_effect;
  char cVar2;
  ushort race;
  char *spell_slot;
  int iVar5;		// renamed to roll in some places
  int iVar6;
  uint roll;
  short sVar8;
  int iVar9;		// renamed to success_pct, slot and resist_pct depending on use
  bool bVar10;
  byte bVar11;		// renamed to this_level, charm_limit and spell_effect depending on use
  uint resist;
  int local_1c;
  int local_18;
  short level_diff;
  int temp_resist;
  uint final_resist;
  
  final_resist = 0;
  this_level = this->GetLevel();
  local_1c = -1;
  local_18 = -1;
  level_diff = this_level - caster_level;
  if (spell_id == 982) {	// Cazic Touch
    return 0;
  }
  if (spell == 0) {
    return 100;
  }
  if ((*(int *)(caster + 0xf4) == 27)) {	// we think this is body type but not 100% sure.  body type 27 is elementals, so this may be for pets
    caster_level = caster_level + 5;
  }
  
  // Guaranteed resist if spell is a fear and target has discipline 31 active.  This was added in the Nov 15 2000 client
  iVar9 = *(int *)(*(int *)(this + 0xa0) + 0x70);
  if (iVar9 != 0 && *(int *)(iVar9 + 0x2a4) == 31) {	// Fearless disc
    spell_slot = (char *)(spell + 0x1fa);
    do {
      if (*spell_slot != -2 && *spell_slot == SE_23_FEAR) {	// SE -2 seems to denote blank but SE_10_CHARISMA with value 0 is also used to denote blank
        return 100;
      }
      spell_slot = spell_slot + 1;
    } while ((int)(spell_slot + (-0x1fa - spell)) < 4);	// check 4 slots; return 100 if fear is one of the SEs
  }
  
  // Check for mez immunity.  This was moved here from HitBySpell() in the Nov 15 2000 client
  if (caster != NULL) {
    spell_slot = (char *)(spell + 0x1fa);	// first slot
    do {
      if (*spell_slot != -2 && *spell_slot == SE_31_MEZ) {		// -2 means blank
        if (caster->GetClass() == CLASS_8_BARD) {
          if (spell->type_number == 5) {						// single target
            if (spell_id == SPELL_1753_SONG_OF_TWILIGHT) {
              success_pct = 101;
            }
            else if (this_level < 25) {
              success_pct = 95;
            }
            else if (this_level < 30) {
              success_pct = 90;
            }
            else if (this_level < 40) {
              success_pct = 85;
            }
            else if (this_level < 45) {
              success_pct = 75;
            }
            else {
              success_pct = (-(uint)(this_level < 50) & 25) + 40;		// if level < 50 then value is 65, else value is 40
            }
          }
		  // AoE Mez
          else if (this_level < 25) {
            success_pct = 66;
          }
          else if (this_level < 30) {
            success_pct = 50;
          }
          else if (this_level < 40) {
            success_pct = 33;
          }
          else {
            success_pct = (-(uint)(this_level < 45) & 4) + 18;		// if level < 45 then value is 22, else value is 18
          }
          roll = Roll0(0,99);
          if (success_pct <= roll) {
            return 100;
          }
        }
		
		// HitBySpell() still does something like this, oddly, which would theoretically result in duplicate messages.  it does the StunMe() call
        DAT_005a82f4 = 0;
        if (spell_id == SPELL_1692_RAPTURE) {
          DAT_005a82f4 = 1;
        }
        else if (spell_id == SPELL_1691_GLAMOUR_OF_KINTAZ) {
          DAT_005a82f4 = 2;
        }
        iVar9 = IsStunImmune(this);
        if (iVar9 != 0 || (cVar2 = this->GetBodyType(), cVar2 == BODY_29_DRAGON2) || cVar2 == BODY_4_GIANT || cVar2 == BODY_26_DRAGON)
		{
          if (caster->IsNPC() == 0 && caster->IsClient()) {
            if (DAT_005a82f4 == 1) {
              spell_slot = "Your target cannot be mesmerized.";
            }
            else {
              spell_slot = "Your target cannot be mesmerized (with this spell)."
            }
            StringToClient(*(int *)(caster + 0x74),spell_slot,0x121);
          }
          DAT_005a82f4 = 0;
          return 100;
        }
        DAT_005a82f4 = 0;
        break;
      }
      spell_slot = spell_slot + 1;
    } while ((int)(spell_slot + (-0x1fa - spell)) < 4);
  }
  
  // Check for NPC fear immunity and resist all spells under Sancitification disc
  iVar9 = *(int *)(this + 0xa0);
  if (iVar9 != NULL) {
    if (this->IsNPC() == 1 && (*(char *)(this + 0x9eb) == 1)) {		// seems to be some kind of NPC fear immunity flag?
      spell_slot = (char *)(spell + 0x1fa);
      do {
        if (*spell_slot != -2 && *spell_slot == SE_23_FEAR) {
          return 100;
        }
        spell_slot = spell_slot + 1;
      } while ((int)(spell_slot + (-0x1fa - spell)) < 4);
    }
    if (iVar9 != NULL && *(int *)(iVar9 + 0x70) != NULL &&
       *(int *)(*(int *)(iVar9 + 0x70) + 0x2a4) == 23 &&		// Sanctification discipline
       spell->resist_type != 0 && spell->resist_type < 6)		// checking to make sure spell is MR FR CR PR DR
	{
      return 100;
    }
  }
  
  // check for valid spell effects in the first four spell slots (this era is probably limited to 4)
  slot = 0;
  do {
    if (local_1c == -1) {
      iVar5 = IsValidSpellSlot(spell,slot);
      if (iVar5 != 0) {
        local_1c = slot;
      }
    }
    else {
      iVar6 = IsValidSpellSlot(spell,slot);
      iVar5 = slot;
      if (iVar6 != 0) break;		// exit loop as soon as we have found two valid spell slots
    }
    slot = slot + 1;
    iVar5 = -1;				// I changed local_18 to -1 here for readability because local_18 is always -1 here
  } while (slot < 4);
  local_18 = iVar5;			// if iVar5 is -1 here that means the spell has only one effect in it
  if (local_1c == -1) {		// resist spell if no valid spell effects found
    return 100;
  }
  
  cVar2 = spell->resist_type;
  if (cVar2 == SPELL_TYPE_1_MAGIC) {
    temp_resist = GetMR(caster_level);
    if (34 < caster_level && caster->GetClass() == CLASS_12_WIZARD) {	// wizard class resist modifier; added January 20 2000
      if (*(char *)(spell + 0x1fa) != SE_0_HP) {
        bVar10 = *(char *)(spell + 0x1fa) == SE_79_INSTANTHP;		// these are checking the first slot for DD effects
        goto LAB_WIZ_CONTINUE;
      }
LAB_WIZ_DO_REDUCTION:
      temp_resist = temp_resist - 10;
    }
  }
  else {
    if (cVar2 == SPELL_TYPE_2_FIRE) {
      temp_resist = GetFR(caster_level);
    }
    else {
      if (cVar2 != SPELL_TYPE_3_COLD) {
        if (cVar2 == SPELL_TYPE_4_POISON) {
          temp_resist = GetPR(caster_level);
        }
        else {
          if (cVar2 != SPELL_TYPE_5_DISEASE) {
            return 0;							// if type is not 1-5 then spell just lands
          }
          temp_resist = GetDR(caster_level);
        }
        goto LAB_CHECK_LURE;
      }
      temp_resist = GetCR(caster_level);
    }
    if (34 < caster_level) {
      bVar10 = caster->GetClass() == CLASS_12_WIZARD;
LAB_WIZ_CONTINUE:
      if (bVar10) goto LAB_WIZ_DO_REDUCTION;
    }
  }

LAB_CHECK_LURE:
  iVar9 = IsStrongLureSpell(spell,spell_id);		// CHA -5 and -6
  if (iVar9 != 0) {
    if (temp_resist < 100) {
      return 0;
    }
    resist_pct = (temp_resist + -100) / 2 + 5;
    if (45 < resist_pct) {
      resist_pct = 45;
    }
LAB_DO_LURE_ROLL:
    roll = Roll0(1,100);
    return (resist_pct <= roll) - 1 & 100;		// if resist_pct >= roll then return 0 else 100
  }
  iVar9 = IsWeakerLureSpell(spell,spell_id);		// CHA -4
  if (iVar9 != 0) {
    if (temp_resist < 50) {
      return 0;
    }
    resist_pct = (temp_resist + -50) / 2 + 5;
    if (75 < resist_pct) {
      resist_pct = 75;
    }
    goto LAB_DO_LURE_ROLL;
  }
  iVar9 = IsWeakestLureSpell(spell,spell_id);		// CHA -3
  if (iVar9 != 0) {
    if (temp_resist < 25) {
      return 0;
    }
    resist_pct = (temp_resist + -25) / 2 + 5;
    if (85 < resist_pct) {
      resist_pct = 85;
    }
    goto LAB_DO_LURE_ROLL;
  }
  
  first_effect = (byte *)(local_1c + 0x1fa + spell);
  if (*first_effect == SE_23_FEAR && this->IsNPC() == true && this->GetLevel() > 52)
  {
    return 100;
  }
  
  if (*first_effect == SE_22_CHARM && caster->IsNPC() == false)
  {
    if (this->GetClass() == CLASS_15_MERCHANT) {		// 15 is beastlord in newer clients but back then was merchants
      return 100;
    }
    if (this->GetBodyType() == BODY_6_EXTRAPLANAR) {
      return 100;
    }
    race = *(ushort *)(this + 0x34);
    if (race < 89) {
      if (race == RACE_88_CLOCKWORK_GNOME) {
        return 100;
      }
      if (race == RACE_44_FREEPORT_GUARD) {
        return 100;
      }
      if (race == RACE_67_HIGHPASS_CITIZEN) {
        return 100;
      }
      if (race == RACE_71_QEYNOS_CITIZEN) {
        return 100;
      }
      if (76 < race) {
        if (race < 79) {
          return 100;
        }
        bVar10 = race == RACE_81_RIVERVALE_CITIZEN;
LAB_CHECK_RACE:
        if (bVar10) {
          return 100;
        }
      }
    }
    else {
      if (race == RACE_90_HALAS_CITIIZEN) {
        return 100;
      }
      if (91 < race) {
        if (race < 95) {
          return 100;
        }
        if (race == RACE_106_FELGUARD) {
          return 100;
        }
        if (race == RACE_112_FAYGUARD) {
          return 100;
        }
        bVar10 = race == RACE_139_IKSAR_CITIZEN;
        goto LAB_CHECK_RACE;
      }
    }
    if (spell_id < 726) {
      if (spell_id == SPELL_725_SOLONS_SONG_OF_THE_SIRENS) {
LAB_SET_CHARM_LIMIT_37:
        charm_limit = 37;
      }
      else {
        if (spell_id < SPELL_185_TEPID_DEEDS) {
          if (spell_id != SPELL_184_ALLURE) {
            if (spell_id == SPELL_141_BEGUILE_ANIMALS) {
              charm_limit = 43;
              goto LAB_CHECK_CHARM_LIMIT;
            }
            if (spell_id != SPELL_142_ALLURE_OF_THE_WILD) {
              if (spell_id != SPELL_182_BEGUILE) {
                if (spell_id != SPELL_183_CAJOLING_WHISPERS) goto LAB_SET_CHARM_LIMIT_25;
                goto LAB_SET_CHARM_LIMIT_46;
              }
              goto LAB_SET_CHARM_LIMIT_37;
            }
          }
          goto LAB_SET_CHARM_LIMIT_51;
        }
        if (spell_id == SPELL_196_DOMINATE_UNDEAD) {
          charm_limit = 32;
        }
        else if (spell_id == SPELL_197_BEGUILE_UNDEAD) {
LAB_SET_CHARM_LIMIT_46:
          charm_limit = 46;
        }
        else {
          if (spell_id == SPELL_198_CAJOLE_UNDEAD) goto LAB_SET_CHARM_LIMIT_51;
          if (spell_id != SPELL_260_CHARM_ANIMALS) goto LAB_SET_CHARM_LIMIT_25;
          charm_limit = 33;
        }
      }
    }
    else {
      if (spell_id < 1630) {
        if (spell_id == SPELL_1629_ENSLAVE_DEATH) {
LAB_SET_CHARM_LIMIT_52:
          charm_limit = 52;
          goto LAB_CHECK_CHARM_LIMIT;
        }
        if (spell_id != SPELL_750_SOLONS_BEWITCHING_BRAVURA && spell_id != SPELL_1553_CALL_OF_KARANA) {
          if (spell_id == SPELL_1556_TUNARES_REQUEST) goto LAB_SET_CHARM_LIMIT_35;
          if (spell_id != SPELL_1624_THRALL_OF_BONES) {
LAB_SET_CHARM_LIMIT_25:
            charm_limit = 25;
            goto LAB_CHECK_CHARM_LIMIT;
          }
        }
      }
      else if (spell_id != SPELL_1705_BOLTRANS_AGACERIE) {
        if (spell_id != SPELL_1707_DICTATE) {
          if (spell_id == SPELL_1817_ALLURING_WHISPERS) {
            charm_limit = 49;
            goto LAB_CHECK_CHARM_LIMIT;
          }
          if (spell_id != SPELL_1822_PERSUADE) goto LAB_SET_CHARM_LIMIT_25;
LAB_SET_CHARM_LIMIT_35:
          charm_limit = 35;
          goto LAB_CHECK_CHARM_LIMIT;
        }
        goto LAB_SET_CHARM_LIMIT_52;
      }
LAB_SET_CHARM_LIMIT_51:
      charm_limit = 51;
    }
LAB_CHECK_CHARM_LIMIT:
    if (caster->GetClass() == CLASS_8_BARD && caster->GetLevel() < this->GetLevel() && 37 < caster->GetLevel()) {
      return 100;							// bards prevented from charming yellows and reds
    }
    if (charm_limit < this->GetLevel()) {
      if (*(int *)(caster + 0x74) == 0) {	// checking if we're an NPC
        return 100;
      }
      StringToClient(*(int *)(caster + 0x74),s_Target_Too_High_level_for_your_c_0054f770,0x121);
      return 100;
    }
  }
  
  if (this->IsNPC() == 0) {
    if (temp_resist > 99) {
      temp_resist = 99;		// players/clients are capped at 99 here
    }
  }
  else if (temp_resist > 150) {
    return 100;				// an effective resist of 151+ will make everything resist, including lifetaps
  }
  
  if ( spell_id == SPELL_127_INVOKE_FEAR) || spell_id == SPELL_59_PANIC_THE_DEAD || spell_id == SPELL_514_TERRORIZE_ANIMAL )
  {
    if (caster_level < this->GetLevel()) {
      iVar9 = -5;
    }
    else {
      iVar9 = -(caster_level / 2);
    }
    temp_resist = temp_resist + iVar9;
    if (temp_resist < 5) {
      temp_resist = 5;
    }
  }
  if ( spell_id == SPELL_1527_TREPIDATION || spell_id == SPELL_1550_REPULSE_ANIMAL || spell_id == SPELL_1532_DREAD_OF_NIGHT )
  {
    if (caster_level < this->GetLevel()) {
      iVar9 = -15;
    }
    else {
      iVar9 = -5 - (caster_level / 2);
    }
    temp_resist = temp_resist + iVar9;
    if (temp_resist < 5) {
      temp_resist = 5;			// fear has a resist floor of 5 like charm
    }
  }
  
  sVar8 = temp_resist;
  if ( spell_id == SPELL_1544_ENFORCED_REVERENCE ||
    spell_id == SPELL_1553_CALL_OF_KARANA ||
    spell_id == SPELL_1624_THRALL_OF_BONES ||
    spell_id == SPELL_1690_FASCINATION ||
    spell_id == SPELL_1691_GLAMOUR_OF_KINTAZ ||
    spell_id == SPELL_1705_BOLTRANS_AGACERIE
  ) {
    sVar8 = sVar8 - 10;		// these spells get a resist bonus/reduction
  }
  iVar9 = IsUnresistableSpell(spell,spell_id);	// this returns true for Dictate, Rapture, Enslave Death
  if (iVar9 != 0) {
    return 0;
  }
  if (spell->type_number == 13) {	// type 13 is lifetap.  lifetaps land at this point.  they never did partial damage until Sept 4 2002
    return 0;
  }
  final_resist = final_resist + sVar8;
  spell_effect = *first_effect;
  if (spell_effect < 32) {
    if (spell_effect != SE_31_MEZ) {
      if (spell_effect == SE_0_HP) {
LAB_YELLOW_RED_DD_MOD:
        if (*(int *)(this + 0xa0) == NULL || this->IsNPC() != 1 || level_diff < 1 || this->GetLevel() < 17) goto LAB_TASH_SPELLS;
        resist = final_resist + level_diff * 2;		// this is adding +2 resist value for each level the target is above the player caster
      }
      else {
        if (spell_effect != SE_3_MOVEMENTSPEED && spell_effect != SE_20_BLIND) {
          if (spell_effect == SE_22_CHARM) {
            CharismaMod(&final_resist,caster);		// modify the resist value by the charisma bonus, which is -1 for every 8 CHA above 75
          }
          else if (spell_effect != SE_23_FEAR) goto LAB_TASH_SPELLS;
        }
        if (no_resist_floor != 0 || (resist = 5, 4 < final_resist)) goto LAB_TASH_SPELLS;
      }
      final_resist = resist;	// Charm, Root, Blind, Fear have a resist floor of 5 which is set here but the function has an override parameter
      goto LAB_TASH_SPELLS;
    }
  }
  else if (spell_effect != SE_34_CONFUSE) {
    if (spell_effect == SE_44_LYCANTHROPY) {
      final_resist = final_resist + 30;
      goto LAB_TASH_SPELLS;
    }
    if (spell_effect != SE_63_BLUR) {
      if (spell_effect != SE_79_CURRENT_HP_ONCE) goto LAB_TASH_SPELLS;
      goto LAB_YELLOW_RED_DD_MOD;
    }
  }
  CharismaMod(&final_resist,caster);		// Mez, Confuse and Blur spells call this.  They get the same CHA bonus charm does
  
LAB_TASH_SPELLS:
  if ( spell_id == SPELL_676_TASHAN || spell_id == SPELL_677_TASHANI || spell_id == SPELL_678_TASHANIA ) {
    final_resist = (final_resist * 2) / 3;	// this is weird since these spells are unresistable.  vestigial?
  }
  
  // Level based resist floor
  if (this->IsNPC() == 1) {
    if (level_diff > -11 && this->GetLevel() > 14 && final_resist < 10) {	// ten levels under the caster or higher unless it's a newbie mob
      final_resist = 10;
    }
    if (level_diff < -20 || this->GetLevel() < 15) {		// deep greens and newbie mobs
      resist = 2;
    }
    else {
      resist = 5;		// everything in between
    }
  }
  else {
    resist = 1;		// on players the floor is always 1
  }
  
  if (final_resist < resist) {
    final_resist = resist;
  }
  if (final_resist > 100) {		// this will cap players and NPCs but players were already capped at 99 before this
    final_resist = 100;
  }
  
  iVar9 = IsLullSpell(spell_id);
  if (iVar9 != 0) {
    if (spell_id == SPELL_728_KELINS_LUGUBRIOUS_LAMENT) {		// bard lull was good because it's only 3 ticks I guess
      resist = 5;
      final_resist = final_resist / 2;		// bard lull gets a big resist bonus here
      if (final_resist < 5) {
        final_resist = 5;
      }
    }
    else {					// lull/pacify resist floors; lull spells were bad apparently, except bard
      this_level = this->GetLevel();
      if (this_level < 15) {
        resist = 10;
      }
      else if (this_level < 25) {
        resist = 20;
      }
      else if (this_level < 35) {
        resist = 33;
      }
      else if (this_level < 40) {
        resist = 42;
      }
      else {
        resist = (-(uint)(this_level < 50) & 0xffffffd2) + 100;	// if level < 50 then value = 54 else value is 100.  50+ mobs were immune to non-bard lull
      }
    }
    if (final_resist < resist) {
      final_resist = resist;
    }
  }
  
  // our class list has necro GM as 30.  Sony's Kunark list had enchanter GM as 30
  if (caster->IsNPC() == 1 
    && (caster->GetClass() == CLASS_14_ENCHANTER || caster->GetClass() == CLASS_30_ENCHANTER_GM)
    && caster->GetLevel() > 24)
  {
    if (this->GetLevel() < final_resist) {
      final_resist = this->GetLevel();
    }
    if (final_resist > 40) {
      final_resist = 40;			// enchanter NPCs basically ignored your resists for the most part
    }
  }
  
  resist = final_resist;
  roll = Roll0(0,100);				// the 2nd number is really n - 1, so random range would be 0 to 99
  if (roll > 98 || resist < roll) {	// the attacker is doing the roll; a 99 and attacker auto-wins; roll a 0 or 1 and resist
    return 0;	// full hit
  }
  if (local_18 != -1									// two or more spell effects?
     && (*first_effect == 0 || *first_effect == 79) 			// first slot is DD effect
     && (12 < *(byte *)(spell + 0x212))					// enchanter minimum spell level > 12
     && ((cVar2 = *(char *)(local_18 + 0x1fa + spell), cVar2 != 27 && cVar2 != 92)))	// first effect is not SE 27 and not SE 92 (cancel magic, insta hate)
  {
    return 100;		// full resist
  }
  if (*first_effect == 0 || *first_effect == 79)
  {
    resist = (resist * 100 + roll * -100) / resist;
    if (this->IsNPC())
	{
      if (level_diff > 0 && 16 < this->GetLevel()) {
        resist = resist + 5;
      }
      this_level = this->GetLevel();
      if (this_level > 29) {
        resist = (resist - 25) + this_level;
      }
      if (this_level < 15) {
        resist = resist - 5;
      }
    }
    if (spell_id == 88) {		// Harm Touch
      resist = resist - 45;
    }
    if (resist < 0) {
      resist = 0;
    }
    if (resist < 101) {
      return resist;
    }
  }
  return 100;
}
Reply With Quote