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;
}