|
|
 |
 |
 |
 |
|
 |
 |
|
 |
 |
|
 |
|
Development::Development Forum for development topics and for those interested in EQEMu development. (Not a support forum) |
 |
|
 |

06-05-2025, 03:31 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
A Comprehensive Study of EverQuest's Classic Resist System
Over the last year I've been spending a lot of time trying to figure out as much of EverQuest's original resist system as I could and this document will detail my knowledge of it. This research is largely based on decompiles given to me by Kicnlag and some I've produced myself. Kicnlag discovered that the Kunark and earlier era clients had the server-side resist logic in them. (Sony stopped including it in the Velious and later clients) This is a huge discovery and it turns out that the classic resist system was considerably harsher than we realized. As it plays a very important part in the balance of the classes and the game's difficulty, it's important to get it accurate.
We used a program called Ghidra to spit out decompiles of the client binaries and we then figured out what most of the variable names and literals are to make the code understandable. Since I'm familiar with the modern resist math it wasn't that difficult for me to decipher what the variable names are. We have only two clients from the pre-Kunark era, which are February 16 1999 (the launch CD client) and March 21 2000 (one month before Kunark). There are many clients available in the Kunark era however and we are able to view some of the evolution of the algorithm up to Velious. The latest client with the resist logic is November 29 2000 (one week before Velious) and that is the most valuable binary for this research.
All but the February 1999 client binaries were obtained from here: https://web.archive.org/web/*/everqu...wnload/patch/*
Decompiles are extremely compelling but not necessarily 100% proof. I checked all available sources to try and verify the code authenticity, and I can say that everything found in the decompiles ended up correct and explainable. Not all of the resist logic is found in that one resist function however, and the Kunark clients do not have some of this remainder. For that I had to figure it out the old fashioned way. The only significant pieces I could not nail down were wizard and dragon breath lure behavior from December 5 2000 until Sept 4 2002 but otherwise this document should explain just about everything. Although there could be unknown unknowns.
Note: in this document I'll be referring to the entire time period from before September 4 2002 as "classic" unless I say "pre-K classic" to indicate the era before April 24 2000.
Classic Resist Rolls
The most significant finding we discovered is that in classic EQ, spell resist rolls were 0 to 99. This was the case up until September 4 2002 when it switched to 0-199. A "roll" here means a randomly generated number from X to Y, which is then compared against the effective resist value, which is the target's resist after all modifiers are applied to it. If the roll is lower than or equal to the effective resist value then the spell is resisted.
It's important to understand the significance of this. This means that every single point of resist from gear or buffs increased your chance to resist an all-or-nothing spell back then by 1%. Resists were essentially twice as powerful in the classic eras when resisting against most spells. (there are exceptions which I explain later) This meant that the buff Group Resist Magic by itself increased your chance to resist by 55%, so only having this buff on while naked for most races/classes made the magic resist rate 80%. Resisting all-or-nothing spells in classic EQ was much easier, for both PCs and NPCs.
This finding explains why some of Sony/Daybreak's developer comments mentioned a "resist chance" variable in place of what is obviously "effective resist value", which in the newer logic is one resist value = 0.5% resist chance. Prathun's 2010 pseudocode says this and some other sources also confused resist chance with resist value which I mention in my Charm, Root, Lull thread. The reason is simply because in September 2002 Sony changed the scale from 0-99 to 0-199.
This also explains why many resist modifiers seemed so weak/trivial. The resist modifier from level difference for example is found in the old clients, which is level^2 / 2. So an NPC five levels above you only reduces your effective resist by twelve points, which seemed rather trivial under the PoP era mechanics. This caps at -40 in the case of a higher level PC vs an NPC, meaning when fighting green cons you only gain a 40 resist value advantage. If the scale is 100 points wide instead of 200, then these modifiers are twice as significant.
The September 4 2002 patch was a massive buff to caster DPS for this and other reasons which I will go into. This is the primary reason why casters are so overpowered right now on the emu servers in the early game.
Other Evidence For The 0-99 Scale
I did check for logs to try and verify if the 0-99 scale applied in Velious and Luclin eras, and it was easy to find. In November 2001, three weeks before Luclin opened, I had produced logs against the mist panther NPC in Wakening Lands to parse my DPS with various weapons. One weapon was my Scepter of Destruction. This weapon procs Anarchy. Before September 4 2002 it was an all-or-nothing spell. The mist panther has a typical resist value of 35 MR and it's a level 60 mob so the level difference modifier does not apply in this case. I added up the resists in my mist panther logs and the resist rate was 36% in 300+ procs, which was the expected rate.
My personal logs yielded more evidence: I found in them that I was resisting certain spells very easily without a bard, like Thunder Blast. That spell is cast by blue drakes in Temple of Veeshan. I was a warrior with Halls of Testing armor, and I resisted 60 out of 61 casts, which agrees with a 0-99 scale, as with that equipment the MR would have been under 200. I have screenshots from the time period showing my MR value. (note: the spell was all-or-nothing at the time)
I also found a log from the tipa16384 archive ( https://github.com/tipa16384/harcour.../tree/main/DSR) which agreed with 0-99. The Nina character was a 48 halfling warrior with a cold resist buff on (so 65 CR) while in the Great Divide cave fighting a AoEing wurm, and the full hit rate was only 9 out of 36 casts (25%) of Shardwurm Breath. The partial damage also matched the Kunark logic and not the post-Sept 2002 logic.
PvP would have been dramatically impacted by the change to a 0-200 resist scale, so I went looking for posts made by PvP players and I found them.
Here are some comments made right after the September 4 2002 patch:
Thread title: Resist changes
"Bards can pvp at 55 again. People with 120+mr aren't invincible to me anymore. I engaged 6 people in about 20 minutes and dropped all of them. Slow/snare is USEFUL! YAY! And of course, the best thing about this is that the people with crappy low resist no drop gear get their butts totally handed to them every time now." (09/04/02)
"I GOT MEZED YESTERDAY FOR TEH FIRST TIME IN LIKE 8 MONTHS!" (09/06/02)
https://web.archive.org/web/20031127...b=5&o=&fpart=1
Thread title: verant has to change resists back
"As a warrior I am going to get owned by all classes now...including paladins and rangers. With gear on all my resists were well over 100, which I don't think is bad for a pure melee" (09/05/02)
"yea these resists are fucked up i get snared easy as hell with 122 mr on my sk, same with my wiz thats got 112 mr i meant wtf how am i sposed to get my mr to fuckin 250! such bullshit i really hope they change it back" (09/05/02)
"i was snared for the first time in months with a 141 MR i was shocked and it was some shity ranger" (09/06/02)
https://web.archive.org/web/20031127...b=5&o=&fpart=1
I also found some great Casters Realm threads from 2000 about players who did resist tests to find out the effectiveness of charisma on mez spells. This first guy did tests at 25 MR and 80 MR by casting on a second player. The resist rates were expected using the 0-99 scale:
https://web.archive.org/web/20001018...ML/002086.html
Another guy mezed himself at 25 MR, 50 MR, and 99 MR. The results were as expected using the 0-99 scale. (note that CHA will reduce resist rates a bit, thus the resist rate is not exactly equal to the resist value in these tests) He also did PvE mez tests on a guard NPC, and the reduction in resist rates from Tashania being applied agreed with a 0-99 roll.
https://web.archive.org/web/20020816...c&f=9&t=003257
Note: the PvP resist scale was modified shortly after September 4 2002 which I will explain in a later section.
The Level Difference Resist Floor
The second most significant discovery in the decompiles is the existence of a level difference resist floor which was removed in the September 4 2002 patch and applies to virtually all spells. This floor is similar to the tick save floor which I discussed in another thread. Spells with tick saves do a periodic resistance check and the reason why charm for example can never be guaranteed to last the full duration is because the magic resist is floored to a minimum value which is always above zero. This level difference floor cannot be debuffed under like tick saves, meaning it was not possible to debuff resists down to zero in classic EQ, meaning even debuffed level one newbie area mobs could resist spells from level 50s. This is not the case post Sept 2002 where effective resists can be and very often are zero after debuffs and level difference.
Some spells did ignore this floor however. Lifetaps, lures and unresistable spells were hardcoded to skip the final resist roll entirely thus were not subjected to the floor. Certain DoT spells used the partial damage resist scale even though they were essentially debuffs and this allowed them to hit through the floor because even when failing the roll the partial damage math allowed the spell to land. (Druid magic DoTs and Necro fire DoTs)
For NPC targets, the floor was computed like this:
Code:
level_diff = target_level - caster_level
if (level_diff > -11 and target_level > 14 and final_resist < 10) then
final_resist = 10
end
if (level_diff < -20 or target_level < 15) then
floor = 2
else
floor = 5
end
if (final_resist < floor) then
final_resist = floor
end
Which means: mobs within 10 levels of the caster (except newbie levels, so the vast majority of dark blues) and all NPCs above dark blues had a floor of 10. Most greens had a floor of 2. Stuff between had a floor of 5. Since the rolls were 0-99 and the comparison used a < instead of <= this means that the actual resist rates were 11%, 6% and 3%. (I discuss roll logic in more detail in a later section)
For player/client targets, the floor was always 1.
This is a controversial discovery so I realize that ample evidence is required to convince people. Part of EQ's original ethos was to not have guarantees, so a resist floor makes perfect sense to have existed. It shouldn't be that controversial but players hate nerfs. The following is log file and old user comment evidence of this floor.
We do have pre-Sept 2002 era logs to examine. Finding proof in these logs isn't difficult. I still have some of my own logs of my wizard casting AoE spells on NPCs low enough in level that the effective resist on them should be zero without a floor, but resists are seen. One case in particular was when I was AoEing mobs in the Fungus Grove zone, casting AoEs on mushrooms and dregs. Since AoE spells were used it provides a lot of spell hits. The resist rates and partial damage values matched the Kunark client spell resist logic. Dregs have a level range of 49 to 53, so most dregs would end up having a 10 resist floor, and the resists I saw in the log showed dregs resisting at a higher rate than the sub level 50 mushrooms, and the partial damage hits showed dregs having resist values of 10 and 5 as expected. This alone is quite irrefutable although my logs aren't public.
You need not take my word for this. You can download old 2001 logs from the tipa16384 archive ( https://github.com/tipa16384/harcour.../tree/main/DSR) and you will find her doing quad kites in Cobalt Scar on a mid 50s druid, and she gets snare resists on the wyverns.
Here is also a case of her level 55 druid getting a full Ensnare resist on a level 12 or under NPC in Crushbone: (she had an amusing snare macro)
File: etha_log_021502.txt
[Mon Dec 24 04:22:19 2001] You begin casting Ensnare.
[Mon Dec 24 04:22:19 2001] You say, 'Hopefully, orc legionnaire is snared now.'
[Mon Dec 24 04:22:21 2001] Your target resisted the Ensnare spell.
[Mon Dec 24 04:22:24 2001] You emote 'magically ensnares orc legionnaire, bringing it to a crawl!'
again, these aren't hard to find:
[Mon Dec 24 04:26:40 2001] You emote 'magically ensnares orc centurion, bringing it to a crawl!'
[Mon Dec 24 04:26:41 2001] You begin casting Ensnare.
[Mon Dec 24 04:26:41 2001] You say, 'Hopefully, orc centurion is snared now.'
[Mon Dec 24 04:26:42 2001] Your target resisted the Ensnare spell.
A Usenet poster in July 2002 posted an AoE log in Sebilis from a wizard's perspective here: https://groups.google.com/g/alt.game...m/l4m4vFVmMw8J
Notice that the Zephyr of Ice spell was getting partial hits. This spell has and had an innate -10 modifier on it and it's a level 56 spell so he is at least level 56. Combined with level difference he would have overtaken the entire 35 standard cold resist. In fact one of his partial hits was on a zol knight which is the lowest level frog in the zone. The partial damage numbers also agree with effective resist values of 5 and 10.
I found some discussions on an EQ Bard mailing list about bard mezes from early 2000. These bards cast thousands of mez songs on NPCs to get resist rates. The results clearly showed the 3% floor on level 1 greens and the 6% floor on high greens and low dark blues. In one post a bard mentions a "5% cap". This is extremely compelling:
https://dbsanfte.github.io/eq-archiv...tml/14644.html
https://dbsanfte.github.io/eq-archiv...tml/13968.html
"My experience is more with about 20-30 mob pulls than 50, but I seem to get about 10% resists. Ie, for each cast of stun, if we have about 25 mobs there, I see one to four resist messages, usually" (Jul 22, 2002)
https://groups.google.com/g/alt.game...m/waylPHHZsv8J
"Even things that are deep, deep green to him are resisting spells at annoyingly high rates" (April 2002)
https://dbsanfte.github.io/eq-archiv...msg-105695.txt
|
 |
|
 |
 |
|
 |

06-05-2025, 03:32 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
The Tick Save Resist Floor (Charm, Root, Blind, Fear)
As I mentioned previously, spells with tick save throws also have their own resist floor just for the tick saves, which is 5. This or the level difference floor is used for these spells, whichever is higher. I mention the existence of this floor in another older thread which I had discovered via other means but it's also clearly seen in these decompiles. This floor includes fear and blind which I previously was not sure of. This floor never changed and is still 5 on Live servers, except for fear spells which seem to have lost this floor at some point. (perhaps blind as well but I didn't check)
The level difference floor which was removed in the September 2002 patch had huge implications for spells with tick saves. These spells were simply much worse before September 4 2002 and they broke upwards of four times as frequently as they do using the later era resist logic. Since the rolls were 0-99, a floor of 10 is about the equivalent to a floor of 20 in the post Sept 2002 resist logic. These floors are set just above where the roll is done in the logic so there is no doubt when looking at the decompile how the code flowed.
Tick saves however get a +4 caster level to them, so the 10 MR floor would begin at -6 levels instead of -10 levels difference. This is likely part of why tick saves have this +4 level mod applied to them, so that the 10 resist floor isn't applied to as many dark blues. This +4 would have made a level 43 mob the ideal long-term pet for a level 50 enchanter player. If a level 50 player tried to charm planar mobs, fire/ice giants, or even a higher level bok ghoul knight using the old resist logic, the average duration would have been just 17 ticks because of the 10 resist floor and the break chance per tick would have been four times more frequent compared to the PoP era logic. On the TAKP and Quarm servers, which have PoP era resist logic, this average is 60 ticks assuming they were MR debuffed to the 5 MR tick floor. (using Allure)
Tick saves are not checked every single tick however. They have a preliminary roll done each tick that must succeed before a resist check is done. This preliminary roll is 50% for charm and 75% for root, blind and fear spells. (meaning most ticks will do resist checks for these three) As far as I am aware this has never changed but I can't be certain. These were not found in the decompiles and were told to me by an unnamed source, however the lull break early preliminary roll was found in the clients and this is a 1 in 4 roll like the others, minus charm. Lulls have a tick save throw as well but it doesn't have a floor because this spell line has MR overrides which I explain in a later section.
Lastly the charisma stat did apply to charm tick saves in classic era, according to the decompiles, and it was not limited to enchanters. This was seemingly removed in the September 4 2002 revamp, as some 2003 charm data I found showed no benefit from CHA. (see my other thread) Perhaps because they massively buffed charm at the same time so this removal was a small counter-balance. Enchanters didn't need to debuff charm pets in classic to extend durations in most cases. For more details on tick saves, check my dedicated thread about it.
Tick Resist Floor Evidence Not From Decompiles
I also found an old April 2001 Casters Realm thread titled "CHA and Charm - A numerical study at last" which had some charm duration tests in it. This kind of artifact is as rare as it is valuable and offers compelling evidence for how EQ used to be. The thread can be found here:
https://web.archive.org/web/20010513...04438&go=older
This enchanter was trying to determine if charisma made charms last longer. I can say that on Live servers right now, it does not. But the decompiles of the Kunark clients do show charisma lowering MR on tick saves, but not under the floors. So apparently charisma used to make charm last longer if the target's MR was not already at the MR floor, and this would agree with older Sony dev comments regarding CHA and charm.
His first two tests (at differing CHA amounts) were on an exp giving green con (so almost dark blue) seafury cyclops NPCs in OOT as a level 58. This NPC's MR is 35 and the level mod in this case is -40, so without a floor the MR would be 0. Both his tests would have been at the MR floor of 5 since charm has a floor of 5 and an NPC of that level relative to him would also be 5, meaning charisma would not have been a factor. The average durations for both tests ended up at 26 ticks using a 70 tick max spell, and combined were 50 charms. This agrees with a 5 MR floor at 0-99 scale with a 50% preliminary roll, which in my simulations results in 28.5 ticks average duration. For comparison, the current average duration for Boltran's at the MR floor on TAKP or Quarm is 43 ticks in this scenario.
This guy did more tests after his cyclops tests, and this time on Ssolet Dnaas in Warsliks Woods. This NPC is permarooted which makes doing tests much easier. He did four sets of 25 casts at two different CHA levels and two of the sets were with Resist Magic on the NPC. Since tick saves get a +4 levels to the caster in the algorithm, this would make the MR floor on this mob also 5 in the tests without resist magic. The results also matched the simulations as both tests at two different charisma levels (46 charms) added together ended up about 26.5 ticks average duration.
I tried to go through the limited pre-Sept 2002 logs I have to see if they had useable charm data and they do not. But they do have useable root data. I was able to verify (with high but not absolute certainty) that roots using the old resist system had the same preliminary roll (75%) and the break rates with a 5 MR floor were as expected. You can see this in the tipa16384 archive: the Etha log is of a druid character and she was killing haunted seachests in Cobalt Scar which is always level 40 and has a typical MR of 35. The average duration of a 30 tick root was 15.78 ticks in a sample of 27 when selecting only the first roots cast on the NPCs to remove bias. Simulated root average duration is 16.37 ticks. (using a 0-99 resist roll) I checked a second log that I obtained from a submission by a TAKP player and his 2001 log also agreed with 75%/5.
|
 |
|
 |
 |
|
 |

06-05-2025, 03:34 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Resist Caps and Lures
The third most significant discovery in the decompiles is the existence of resist caps. There are a lot of old comments which speculate about a resist cap before Planes of Power, and some of these comments weren't too far off. The caps were applied to the effective resist value, not the absolute resist value, meaning they were applied after the level difference modifier. So the caps would have shifted somewhat relative to the absolute resist value (the number players see in the UI) depending on the level of the attacker casting on them. (this level difference modifier is explained in detail in a later section, which you should read now if you have no familiarity) I say caps plural because there were more than one. I will include lure spells in this section because they are closely related to one of the caps. Note that 'lures' in this context includes Kunark dragon breath spells and various other raid NPC spells and not just the wizard DDs.
The 100 and 99 Hardcaps
Resists were capped at 100 for NPCs, and 99 for players, when defending against non-lure spells after factoring in level difference. This was the case until September 4 2002. This might be hard for people to believe at first because everybody is used to the PoP era system but the decompiles are explicit and the logs back it up. This meant that when fighting Lord Nagafen at level 50, any resists above 111 did nothing, since Nagafen was 5 levels higher this level difference subtracted 12 points from the player's resists. It also meant that when fighting dark blue or green NPCs, your resist level was capped at a value less than 99.
The September 4 '02 patch note said: "Resists matter more for PCs. There are now tangible differences between having 50, 150, and 250 in a given resistance, for example. Resistance buffs, bard songs, and resist gear have actual value, all the way up the line."
The reason why is because resists were capped this low making gear and bard songs useless much of the time. EQ didn't do stats very well and resists weren't implemented very well either.
Keep in mind however that for all-or-nothing spells this cap was almost meaningless. (because the rolls were 0-99) Also most bosses in Kunark, Velious and Luclin had lure AoEs so this cap wouldn't apply against these spells. There were two consequences to this cap: partial damage spells could not be mitigated by players beyond a certain average damage, which was 50%, and all-or-nothing spells will have a 1% chance to always land on players since players cap at 99 and not 100. Sony just didn't want players mitigating spell damage beyond 50% back then. The upside however was that hitting that 50% mitigation level was very easy and thus players could mostly ignore resists and keep their normal gear or focus on HP gear, although this made gearing less interesting.
I checked my old personal logs to see if this 1% chance to always land spells on PCs could be found in them, and I found these cases:
* A single Thunder Blast landed on my warrior out of 61 casts in a Oct 2001 NToV raid. Old screenshots show me with 101 buffless MR back then and I had just cast Aura of Blue Petals on myself right before the cast that landed.
* In a January 2002 log, Shrieker mushrooms in Fungus Grove landed only 3 spells in 336 casts when DR was certainly above 100, as Aura of Black Petals was cast just prior to the spells that landed and level difference granted a +40 resist advantage.
The 151 Resist Immunity Threshold for NPCs
Another significant resist value in classic era was that when casting on NPC targets, there was a threshold at 151 effective resist value, at or above which any spell cast on the NPC would guaranteed resist and no rolls were done. Like the 100/99 cap, this limit is checked after the level difference modifier and debuffs were applied, but it was obviously checked before the NPC's resist was capped at 100. This threshold existed from the start of classic era, as did the 99/100 caps. (they're in the February 1999 client)
This threshold applied to lifetaps and is how lifetaps could resist when they otherwise were always full damage, but (very) old comments suggest lifetaps bypassed this limit in early classic era. Lifetaps may have been unresistable like tash spells back in 1999 but I have no hard evidence of this. They could resist in 2000 for sure.
Lures and the Mythical 255 Hardcap
First let me explain what made a lure spell. In the old spell data, a lure spell was a spell with a negative charisma effect in the first slot. (not including SkunkSpray) This was Sony's hackish way of signaling different resist behavior instead of adding a proper spell field. Back then Sony always assumed that their current expac would be their last one, so they didn't plan for the long haul and took shortcuts. Charisma (with value 0) in spell slots was also used as a spacer so that valid spell effects could be in lower slots; i.e. if charisma with value 0 was in a slot, that slot was ignored. The primary reason to do this was to enable buff stacking.
Sony also hardcoded the Kunark dragon AoEs to be lures without giving those spells a -CHA spell effect. This was done by checking spell ID in the same function that also checked spells for a charisma code. Future dragon AoEs and raid boss spells would use a charisma code, so only the Kunark dragons had their AoEs hardcoded by spell ID.
There were four different charisma codes to denote lure behavior: -3 through -6. -6 and -5 were the hardest to resist (wizard lures, most dragon AoEs) and these had the same resist rate in Kunark even though there were two codes for it, but in Velious this may have changed. -4 was easier to resist and -3 was the easiest. If you check Lucy's spell history, you can see what kind of lure code spells used to have. For example under Lure of Ice it shows: "Removed Slot 1: Lure(5) 2002-09-04"
If you go searching for old claims of a resist cap you will find users saying that resists capped at or wrapped around at 255 due to Sony using an 8 bit variable for it. These claims were false. The partial damage formula requires that the resist variable be larger than 8 bits in the algorithm. The cap for lure spells was so low at 45% that the randomness of it was resulting in users not resisting any AoEs in long streaks even with bard songs and this was giving them crazy notions. You will also find players saying that resists capped around 180-200 and these guys were right, but only for lures.
In 2001 Ester the Tester responded to a player who brought up resists wrapping around to 0 and she shot down the notion explicitly: https://web.archive.org/web/20040927...p/t-10824.html
And another reply where she says, "I can say that we spend a LOT of time with logs, parsers, Excel etc making certain that they do not roll over."
https://web.archive.org/web/20020829...cID=2606.topic
Lures in Kunark
Lure spells were added in Kunark. In old EQ, these spells had their own separate resist roll that bypassed almost every restriction or limitation inside the resist algorithm, and were not merely a negative modifier like post Sept 4 2002 lures are. You might assume that -5 meant reduce the resist by X times 5 before rolling the spell resist check, but this was not the case.
Kunark only had one type of lure spell, and they all behaved in this way: first 100 was subtracted from the resist value. Then the remainder was divided in half and 5 was added to it. Then this result was capped at 45. This was the resist rate of the spell. As usual this was done after the level difference modifier was applied. If the target had less than 100 resist then the spell always landed. The effective resist cap in Kunark for lures and dragon AoEs was 180 + level difference, so 180 on white cons and 192 on the level 65 dragons if the player was level 60. (180 - 100) / 2 + 5 = 45%
Kunark lures: - Bypassed all previously mentioned caps and guaranteed hit if the target's effective resist was under 100.
- Wizard lures did benefit from the class innate -10 resist modifier.
- Didn't do partial damage. All lure spells were all-or-nothing.
- Could hit anything. Even lifetap immune targets. Wizard lures used the same logic as dragon AoEs, so ice lures could hit otherwise ice immune targets for example.
In mid-Kunark Sony made a new dragon roar spell which had the -6 charisma code for the outdoor dragons. The reason was seemingly because the -5 code only applied to DD spells, and since they needed a lure fear (which is all-or-nothing) they had to make a new code or modify the -5 code to work on all spells instead of just DDs. I'm not sure why they didn't do the latter, but -5 and -6 did the same resist behavior. (again, all the other Kunark dragon AoEs also had this same lure behavior even if they didn't have -5 or -6 codes in their spell data)
Lures in Velious
Sony removed the resist logic from clients in Velious. The last client with it is November 29 2000. So from Velious until September 2002 we have to try and figure out how Sony changed the algorithm without the benefit of decompiles.
Sony added two more charisma codes for Velious. These were added one week before Velious launch and are in the November 29 client. The codes are -3 and -4. They behaved much like the -5/-6 spells did only instead of subtracting 100 and capping at 45, they subtracted less and capped higher. These were to be easier to resist lure spells as you would expect.
-5 & -6 lures subtracted 100 from the resist value and capped the final resist chance at 45%.
-4 lures subtracted 50 from the resist value and capped the final resist chance at 75%.
-3 lures subtracted 25 from the resist value and capped the final resist chance at 85%.
Resists were effectively capped (after level difference mod) at 190 against -4 spells, and 185 against -3 spells. All three divided the remainder by 2 and added 5.
To be clear: that is what the November 29 client showed. A week later Sony changed lures again, and I can't say precisely how, but I will outline the limited available evidence and offer some speculation.
On Velious launch (December 5th) Sony made a change to wizard lures which resulted in them being almost useless on some NPCs. This is a Graffe thread about the drastic change in resist rates:
https://web.archive.org/web/20010523...tart=1&stop=20
A Graffe news page has several Sony rep quotes regarding the changes to lure spells at the time. That page is here:
https://web.archive.org/web/20010306...ive_123100.htm
Abashi at first says lures were supposed to behave like lifetaps and not dragon breaths, and the patch "fixed" them to behave as lifetaps, and this was not mentioned in patch notes because it was "low impact".
A Sony programmer, Roger Uzun, contradicted Absor and said that wizard lures still behave like dragon breath spells, and would not change. He also says quote, "dragon breath saves were adjusted, and that resulted in some few NPCS saving slightly better saves vs certain lures".
This is followed by a Geoff Zatkin comment saying that wizard lures should behave as lifetaps, or even better since wizards get an innate -10 on spells. Uzun however was the server side programmer and probably wrote the logic himself or at least saw it.
Absor responds a day later explaining that the change was intended to force wizards into using the appropriate lure for the target mob, i.e. a fire immune mob should resist fire lures and a cold lure should be used. This makes sense as the Kunark lure logic allows any lure spell to be used on any creature, although even in Kunark using the appropriate lure might still have ended up with higher hit rates depending on the mob's resist levels.
Absor then says that the discussion on lures has made them check the code and they found the real problem with it. The resist rates on lures post Velious launch were too high due to some NPCs getting a saving throw when the NPC was higher in level than the caster, says them, and this was removed to fix the spells. That is a vague statement and doesn't help us much. Nothing like that is seen in the November 29 decompile. Resist rates in Kunark would have been the same for all wizards on high resist targets (45%) regardless of level. This answer also doesn't explain why lures on lower level NPCs like Venril Sathir (level 55) and spite golems were getting resisted more after Velious launch, so this is yet another Sony answer that doesn't make much sense. My best guess would be they added a resist penalty to make lower level wizards resist more since a level 52 wizard would have the same resist rate as a level 60 but they botched it and level 60s were resisting more too.
In other post Absor says this: "Uzun is the server-side programmer. Basically, if anyone knows how this thing works, he's the guy." and "So, perhaps a fire breathing foe will now resist fire Lures a bit better. But it’s resistance to cold Lures wouldn’t have changed." So the intent was seemingly not change the resist rates on targets using the appropriate lure spells, and this is why they didn't have a patch note.
(link: https://web.archive.org/web/20011217...ve/arc43.shtml)
In the Graffe thread linked above, a user posts that the paladin epic proc was landing on VS but wizard lures were not. The paladin epic effect was made into a -4 spell on Velious launch. Another, lower level, wizard says that Enticement of Flame was landing better than the higher level lures. Enticement was also a -4 spell. So it seems the issue that affected the level -5 spells did not affect the -4 spells. Although to make mobs immune to the -3 and -4 spells they would have had to of applied similar restrictions to all three types but perhaps they neglected to do so or didn't care since not many player spells were -4 or -3.
I also noticed something important in the spell data changes. The November 29 2000 patch changed the handful of NPC spells which had CHA -5 to instead use -4, and the result is that -5 was from then on (Velious launch) reserved only for wizard lures. This was probably done so that wizard lures could be made useless on targets with extreme resist values without affecting dragon AoEs, but more could have been done to them. That wizard lures were resisting too much on blue cons for awhile suggests more might have changed. Sadly I have all of three wizard lure casts in my old logs and old comments are also lacking in data so I have virtually nothing to work with to try and figure it out.
I do have ample 2002 logs of CHA -4 spells landing on players however. Logs show the -4 CHA AoEs were all-or-nothing and the resist rates were not higher than the Nov 29 client logic would allow. For example I have a 2002 log of a player raiding Vyemm and Scream of Chaos resisted 60% out of 78 casts. I also found a 67% resist rate of wurm AoEs in 183 casts in one sample of those. CHA -6 Dragon AoEs also were not seen resisting more than the Nov 29 client logic would allow, even with a bard in group for one Warder kill, which showed a 50% resist rate in 12 casts. Old logs (certainly not the ones I have) are not going to be able to realistically tell us if the capped resist rate was higher than the Nov 29 client showed unless the resist rate was way higher, which it isn't. Logs do not show the player's resist value and players back then didn't care as much about their resists.
I also have a log of a cleric doing a raid in Kael in June 2002 (Luclin), and in this raid they fight the Idol. The fight only had a single wizard in it so I can make some third person deductions. The log shows all or almost all 30 lures of lightning and flame connecting after two "uncomfortable" messages. This suggests that the -100 resist portion of lures remained and they still bypassed the resist floor.
In short, the limited log data does not show lures deviating from the November 29 client logic, but the data is insufficient to show possible modest modifications to the November 29 client logic.
I do want to point out here that the raid bosses with CHA -6 spells are highly dependent on these spells to make the encounters challenging. Kunark dragon melee damage is rather weak for example, so if players could easily resist the breaths then the events would be trivial. If the resist cap for dragon breath lures changed in Velious then I don't think it changed very much.
This Graffe forum user says that wizard lures remained all-or-nothing until Sept 4 2002:
"Lures, and spells with a stun component, gained the ability to land partial hits (aka not all-or-nothing) when they redid the resist system last year." (5/16/03)
https://web.archive.org/web/20030725...ID=26511.topic
That suggests to me that wizard lures were still separate from the primary resist logic flow.
On December 19 2000 Sony made the paladin epic weapon proc partial hit, says the patch note, but it kept the CHA -4 on it. Sony seems to have hardcoded some kind of exception for this spell.
When I parsed NPC resists on Live servers, some of them had resists of 1000 or near it. It seems that at some point Sony used 1000 as "immune" although with Live's higher level debuffs and PoP era resist logic lures can now be made to land which is how I know the value is around 1000. Sony likely increased NPC resists with the Sept 2002 patch, but it's possible NPCs had upwards of 1000 resist during Velious. Pre-K classic and Kunark mobs however virtually all had 435 resist as the highest value. (only Vox's CR was higher that I found) So it seems that Sony raised the "immunity" value for NPCs starting in Velious. I think this was done to make lures always resist. Before September 4 2002, 425 would have been more than enough to make targets immune from non-lures, so making later NPCs 1000 only makes sense to make lures resist.
|
 |
|
 |
 |
|
 |

06-05-2025, 03:39 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
A Resist Softcap?
The September 4 2002 patch note includes this: "Previously, there was only the smallest benefit to having resists over a certain value" which suggests a softcap of some kind. That is the singular piece of evidence I could find for a softcap. There was some speculation of a softcap at the time, but players also speculated of an ATK softcap (which never existed) so they didn't know. A low hardcap of 99 existed from game launch up to Sept 4 2002 for most spells, so if there was some kind of softcap it would have to apply only to lures/dragon spells. Sony may have removed the lure hardcaps at Velious launch and replaced them with softcaps such that lures became useless on 1000 resist targets or some other very high resist value. I have doubts that a softcap existed but the evidence just isn't there to prove or disprove it. That patch note is very little to go by and patch notes are far from proof.
Sony seemed to have wanted to keep resist rates similar to what they were in Kunark since they intentionally didn't make a patch note about the change(s) to lures. Sony also has a history of not wanting to modify older era mechanics. (the Sept 2002 patch is a big exception) So I doubt the Velious lure changes were major. It's conceivable however that Sony put in a softcap for resists at some point since at or near Luclin launch Sony introduced a softcap for AC, but I wouldn't bet on it. (the resist softcap if it existed could have also been a Luclin addition)
The overcap returns for AC in Luclin were small, so I would also expect small returns for resists (like 1/20th or worse) if a softcap existed. Another issue is that there were three kinds of lures which each had their own code blocks. This makes implementing a softcap trickier as you'd either have to duplicate code or not apply it to all lures. Furthermore the CHA -4 lures were already quite easy to resist so applying a softcap on these doesn't really make a lot of sense.
I could find no old comments which asserted that raising resists to very high levels with a bard resulted in significantly higher resist rates. In fact many people still thought that resists did nothing or rolled over after 255, even well into Velious. This indicates to me that resist rates were always somewhere in the neighborhood of 50%, if not remained 45%. If resists are a coin flip then one could easily not resist a single breath against a dragon with 32k HP.
Next I will quote some player claims from the time period for completeness sake.
There is one comment I found which is somewhat contradictory to the others and I'll start with it:
"i'm beginning to believe that there is no cap on resists. for example, me versus phara dar with sanctus in group.
with puretone i was pegging 500 magic resist. buff out to 235 mr in normal resist gear.
regardless, while sanctus was alive i resisted every phara ae. when he lost the song due to a stun i began
failing all my mr checks .. even at level 60 and at 235 magic resist.
also, there is no longer a wrap over effect. 256 no longer wraps you back down to 1." (9/3/01)
https://web.archive.org/web/20010911....topic&index=5
That is also unfortunately the sole comment I found mentioning resists far above 300. This comment is almost useless by itself however because he discusses a single fight vs. a 32k hp mob, and the bard did get stunned with songs on, so the high resists didn't help Mr. bard very much.
Again, if resists are a coin flip then it's expected to see "resists are useless they do nothing" comments and "resists are fine I resisted most of them" comments like so:
"I recently had 250MR (and level 60) when we fought Phara Dar last. I didn't resist a stun. Most of us didn't. Talk about unlucky save rolls." (Jul 9, 2001)
https://groups.google.com/g/alt.game...m/IMMHcQGG0W0J
"Generally with an MR of over 220 at level 60, you can resist most of her AoEs--about 1 in 4 will get through." (Jul 8, 2001) (Phara Dar)
https://groups.google.com/g/alt.game...m/FQwBoXjob-IJ
This comment is somewhat interesting. He says Cursed's AoE almost never resisted until after the Sept 4 2002 patch:
"Later in the evening, I resisted a record number of I think 10 Caustic Mist AEs from Cursed (I think I have, maybe, resited 5 in all the times we've fought him before) with 500 disease resist." (9/5/02)
https://web.archive.org/web/20021120...cID=1220.topic
Caustic Mist is actually a CHA -5 spell. Earlier I mentioned that at Velious launch Sony changed the few boss AoEs with -5 to -4, and only wizard lures were -5. Well, in Luclin, Sony made boss AoEs with -5 again. This sounds like -5s may have been harder to resist than -6s, but the tipa archive has a log with 4 full resists out of 4 casts for the spell Rag`Zhezum's Deathly Embrace, which is also a CHA -5 spell. I just don't have enough logs/data to figure much out.
More comments:
Thread topic: Dragons resists = useless
"After doing VP innumerable times, I've decided that resists vs these dragons really are not very useful.
From now on, I'm going all +STA +HP." (05-18-2001)
"200 MR and I resist 40% of new style dragon fears." (05-31-2001)
https://web.archive.org/web/20010610...ML/000058.html
"Serv is a Bitz thoug, I had 252PR and about 250MR last time we did him and still stunned most of the time" (5/8/01)
"I fight wuoshi with 230p/180+m or so and still get stunned constantly and feared a fair bit." (5/8/01)
https://web.archive.org/web/20011224...cID=3188.topic
"On the 255 roll-over for resists, in my experience having more than 255 is not harmful.
- Last few Hoshkar attempts I've had well over 300 resist disease with a bard (158 unbuffed RD, I drink a
potion anyway in case bard is out of range or I don't get song effect) and was in good shape with the AE.
Every death was by melee. (Still in shock we finally killed him hehe)
- For Xygoz I've had base 150 resist magic and don't quite hit 255 without a bard (215-225). I'm silenced
a lot at 215-255 resist. With a bard in the group, and resist magic well over 255, I resist Xygoz's silence
almost reliably enough to choose when to heal. (If the bard doesn't stop the resist song to juggle songs--
and find out they're silenced now too)
- For Trakanon, with DMF and buffs, if you have a bard it's hard to not go over 255. Past few trak raids
I've been with bards and I don't think I've had to cast a single group heal." (03-06-2001)
https://web.archive.org/web/20040927...p/t-10799.html
"My unbuffed resists can go to the following.
MR-175, CR-186, FR-156,PR-116, DR-147.Against Kunark dragon that serves me well,
and i tend to resist the fire AOE of TOV drakes, wurms dragon, better then the cold AOE.
But when im grouped with a bard with resist song, i dont resist anything(Was fighting talendor with 295 in MR
and got feared everytime).
Im wondering why this is.
is there some number ye may not go over?
I find that if i have around 160-170 buffed in FR, CR, PR, DR i tend to resist alot, the DMG aoe. WIth fear
aoe MR around 190-200 works best. but with a bard that goes down the drain"
"Resists may be capped at 255 (or 200), but they don't roll over. I sit at 262 MR fully buffed, and I resist a lot more than I do unbuffed, and a whole lot more than I would at 7 MR, I'm sure"
"Im not talking about single incident. At one time me and one ranger stood alone against Talendor because we both resisted the fear..well few seconds later he had me crispy with red wine sauce. I have fought talendor around 6 times now, and only time i have resisted the fear, is when i dont have a bard
with me. Gorenaire is however harder to resist it seems, and Severlious(gaddemm stun) also.The fire damage from talendor seems to be unresistable, but im taking different damage every time, so resists help to reduce the damage."
"Resists are based on level a lot. A lvl 60 with worst resists will resist much more then a 55 with better resists. Resists cap at 255. They don't roll over, they just cap. Gore is extremely difficult to resist. Haven't fought the others ones, but i know this ;p. My lvl 60 friend with 255 resists said he was resisting about half the time."
(12-14-2001)
https://web.archive.org/web/20040929...p/t-10918.html
"You do get to a point of dimishing returns on resists, though. In my experience, most resists over 250 work about as well as they are going to work. I haven't noticed myself resisting anything more around 300 that I didn't already resist at 255"
"I am usually over 255 in Cold, Fire and Magic during HoT raids.
I resist or take low partial damage almost every time. Others who meet the 'bare mins' and end up with around 150 buffed get healed alot.
So it definately makes a difference there."
(thread date unknown; Probably Velious era)
https://search.eqarchives.org/search...Findex.html%22
"Personally I feel there is a soft cap, I don't notice a whole lot of difference in resists going from 200 to 300+. But this is just casual observances, I haven't done any hard testing." (9/2/01)
https://web.archive.org/web/20010911....topic&index=5
"3) Get your MR up way up there. Even at 200+, it's hard to resist the stuns. But, it can be done, so drag your bards with you on your next clearing of VP. You can maybe use some "geographics" to your favor, but I don't think you can avoid the AE stun entirely, without very high MR." (Aug 13 2001) (Phara Dar)
https://everquest.allakhazam.com/db/...99773625916336
"last week at a severilous killing i was well over 320 in most resist (buffed - i forgot some gear at bank, hehe) and i didnt resist a single spell from that dragon, while several months ago with less then 200 i was able to resist almost all off the spells (poisen stun and fear)"
"On 2 separate occasions Trak's poison chewed me up. Both times were when a bard was in the group and I had over 270 PR. Other Trak kills I've had significantly lower PR (200ish) and been a lower level but resisted the breath."
(2001 era thread)
https://search.eqarchives.org/search...replyto%3D5%22
"i had a bard grouped with me. My MR was 308, FR 290. At level 57, with resists this high, I still was only resisting about one in 3 waves of either fear or the dot."
"I am not sure if it is true or not but it appears that after you get to 200 on any resists it seems almost useless or your resist rate gets worse. I have seen numerous threads about resists post 200 and from my own experience with resists above 200. When I had a MR at 265 I failed fear 2 of 3 times and went running. With a MR of 178 I was only feared 1 of 3 times."
"Did Sev last night. Ran PR/MR at 230(ish) each. Resisted everything."
"There does seem to be some kind of bug when a bard is playing a selo's drum with the resist songs. For instance, one raid vs Gerenaire and Tal, MR 330 with bard using a selo's drum, the entire level 59-60 group gets repeatedly feared and hit with the AEs. Next raid same group, same levels, but bard not using selo's drum, one group member gets feared, most DoTs resisted, our resists were still in the 300 range."
"Ester the Tester "says" that resists do not roll over after 255...they work just as well. However, after my own testing with Severilous last night, I'm completely convinced beyond doubt that resists roll after after 255. First attempt mr/pr were both over 300, the only spell I resisted was a fear, which was after I had died and come back, full equipment, but buffless. Next attempt I tweaked my gear/bard songs so my mr was 233 and pr 253, resisted about 50% of the stuns and 70% of the fears, which is very good at my low lvl (56)."
(March 26 to April 7 2001 thread)
https://web.archive.org/web/20040927...p/t-10824.html
"The poison is crazy. I'm level 56, done three Trak raids (we've won all three times, btw), and had PR over 300 (thanks new Luclin bard song). His poison still ate me up two of the three times. One time only I completely resisted the poison." (Jan 9th 2002)
https://web.archive.org/web/20031018...=4503&start=75
"Dunno what everyone else thinks but he always seems a heck of a lot easier than Sev to me. With over 255MR/PR (and being lvl 60) Sev still manages to keep me almost perma-stunned or feared, Wuoshi I resist about 75% of his stuns/fears." (3/24/02)
https://web.archive.org/web/20020415....topic&index=5
"We made a couple of attempts on Gorenaire last night with no success. The thing that baffles me though is when I was in range of the bards AE song my Magic Resist was at 294 and Cold resist was at 308 but yet I would still get feared one out of four and was taking some max damage from her AE cold spell." (8/2/02)
https://web.archive.org/web/20020829...icID=544.topic
|
 |
|
 |
 |
|
 |

06-05-2025, 03:48 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Partial Damage
PoP and later era EQ has two resist scales: 0-199 and 0-599. All-or-nothing spells use the 0-199 scale and partial damage spells (DDs) use the 0-599 scale. Meaning the spells begin to always full resist after the target's effective resist hits 200 or 600. The average damage on DDs becomes very low well before 600 however. Although if a caster is anywhere from level 30 to 50 and the target is an NPC then the DD scale will vary and be as low as 0-399 due to the partial damage modifiers for level 50 and under casters.
Classic resists have an all-or-nothing scale (0-99) but also the resist caps create a strange region above 99 which is completely flat until the 151 immunity is reached. This allows DD spells to still land even after all-or-nothing spells become immune(ish) for another 50 resist values up until the hard 151 limit. I say this is strange because you would expect a target with 150 resist value to resist more than one with 100 resist value, particularly when resists were more potent back then, but this is not the case.
Here is the partial damage logic from 1999 launch until October 8 2001:
Code:
mitigation_pct = (resist_pct * 100 + roll * -100) / resist_pct; // this formula is used up until Sept 4 2002
if (IsNPC())
{
if (level_diff > 0 && defender_level > 16) { // yellows and reds
mitigation_pct = mitigation_pct + 5;
}
if (defender_level > 29) {
mitigation_pct = (mitigation_pct - 25) + defender_level; // gets quite large on the highest level mobs
}
if (defender_level < 15) {
mitigation_pct = mitigation_pct - 5; // landing on newbie mobs is easier than on player targets
}
}
if (mitigation_pct < 0) {
mitigation_pct = 0;
}
if (mitigation_pct > 100) {
return 100;
}
return mitigation_pct;
resist_pct is the effective resist value and roll is a random number from 0 to 99. Sony's resist algorithm returns the mitigation percentage, which is the inverse of the damage percentage, so the actual damage would be something like: spell_dmg = max_dmg * (100 - mitigation_pct) / 100. Logs show that Sony's servers rounded up using that math, so I'm not precisely sure how they did the final step.
Damage on players is very simple and is computed by that formula without the extra modifiers applied. The modifiers make NPCs targets resist more or less compared to players depending on the NPC's level.
On player targets and NPCs without qualifying modifiers, the above formula will result in a full resist only if, and always if, the roll is 0. Beyond that 1% chance, players could not fully resist DD spells prior to September 4 2002. The spells could still be heavily mitigated down to very little, but the full resist rate was always one percent. This will probably seem odd if you're used to the new resist system.
The maximum a player (and NPCs under level 30) could mitigate non-lure DD spells to was 50% average damage. It's 50% because at max resist (the player cap is 99 remember) the partial damage formula will mitigate the spell damage by about the amount of the roll; i.e. roll a 10, the spell does 11% damage; roll a 50, the spell does 51% damage; roll a 99, the spell does 100%. EQ math is fairly simple.
There are three modifiers for partial damage spells landing on NPCs. The first adds 5% more mitigation on yellow and red con NPCs which are also above level 16. The second adds a large resist bonus to level 30+ NPCs, growing larger the higher level the NPC is, and stacks with the previous modifier. The third reduces the mitigation by 5% for newbie level NPCs.
Partials After The October 8 2001 Patch
Velious was known to be a very melee-centric expansion with caster DPS falling behind the melee DPS. This was in large part due to the increased max hitpoints of NPCs and melee DPS scaling with gear a lot more, but the original resist algorithm was also punishing when casting on very high level targets. Sony addressed this two months prior to Luclin by significantly reducing DD spell mitigation by removing the partial damage modifiers on yellows+reds and on level 30+ NPCs, but only for level 51+ players. Sony also reduced NPC resist values.
Quote:
Originally Posted by October 8 2001 Patch Note
- The level-based spell resistance bonus inherent in super-high level NPCs has been reduced significantly.
|
A developer forum post explained it in more detail:
Quote:
Originally Posted by Abashi on Oct 5 2001
Through our testing we found that many higher level creatures, and especially those creatures that are above 60th level, were typically resisting magical damage to an extent far greater than they should have been. This lead to a couple of classes having their usefulness in these situations either negated or reduced to a pure utility role. As such we’ve made the following changes:
NPCs now have less of a chance to resist any direct damage spell caused by casters above level 50. Super-high level NPCs have received a large reduction in the level-difference bonus they receive to their resists based upon the level of the attacking player character. We’ve increased the effectiveness of resist debuffs against a large number of higher level NPCs in regard to damage spells being cast upon them. We’ve also reduced the resistances on a wide range of NPCs, especially for NPCs in Velious.
|
I don't know what "increased the effectiveness of resist debuffs against a large number of higher level NPCs" is referring to. Resist debuffs always reduced by the same amounts as far as I'm aware. (PvP aside)
Prathun's 2010 spell resist pseudocode shows the modifiers being limited to levels 50 and under:
Code:
Partial resist modifier is set to ((150 * (resist chance - roll)) / resist chance).
If target is a non-mercenary NPC...
If target is higher level than caster, and target is at least level 17, and caster is level 50 or below, add 5 to partial resist modifier.
If target is at least level 30 and caster is level 50 or below, add (casterlevel - 25) to partial resist modifier.
If target's level is less than 15, subtract 5 from partial resist modifier.
My old logs confirm that the modifiers were gone after that patch for my level 60 wizard which I just happened to have logs of from Oct 2001.
That patch did not remove the DD yellows and reds +resist modifier however. Prathun's pseudocode still has it:
Code:
If effect is damage and target is a non-mercenary NPC...
If target is at least level 67, level difference is set to (66 - caster level) or 0, whichever is greater.
If target is at least level 17 and level difference is greater than 0, add (2 * level difference) to resist chance.
They might have added that level 67 cap at the time but I have no way to know. There were other points in time where it made sense to add that, and I think Sept 2002 makes more sense because it made raid bosses much easier to land debuffs on and the intent at the time was seemingly to allow damage spells to land easier, not slows.
Removing those two partial damage modifiers for level 51+ casters meant that the damage 51+ players did to NPCs ended up the same as damage done to players, meaning that NPCs could not fully resist DD spells beyond the 1% chance unless it was entirely immune and the average damage players did to even highly resistant NPCs ended up about 50% average damage so it was a significant buff to casters. Before the patch, the most resistant non-immune NPCs would only take about 13% average damage.
I did find a discrepancy with Prathun's pseudocode and the classic logic in regards to the partial damage modifiers. Prathun mentions this: "add (casterlevel - 25) to partial resist modifier". Notice he says the caster's level there instead of the target's level. All of the old clients use target's level there. I strongly suspect that Prathun made an error in his post, because using the caster's level instead of target's level doesn't make much sense as the penalty would get worse as the caster went up in level even while casting on the same level NPCs.
Estimating NPC Resist Values From Spell Damage
Knowing how partial damage is calculated allows us to figure out the target's resist value if enough hits are in the log. Unfortunately I have very few logs from before PoP era so I can only calculate resists for a few NPCs, but if somebody finds old logs at some point they can get a resist estimate by following the instructions I will outline.
First you must understand that there are a limited number of possible partial damage values for a given spell. For any given resist value, only a subset of these values is possible. With enough partial damage hit values, it is possible to determine the target's resist value. I have made a table in a spreadsheet which will calculate every possible partial hit damage for a given spell. With that it is possible to find the resist value that best matches the partial damage numbers seen in an old log file.
Even a single partial hit can tell you that the resist value is over a certain number. For example, if Sunstrike hit for 33 damage, that means the effective resist value is 50 or higher, because 33 is not possible for lower resist values. Note that this is only true for logs from Oct 8 2001 until Sept 3 2002. Also note that you must account for the level difference of the caster and NPC, debuffs, spell resist adjusts (wizards' -10 for example), etc. and reverse apply these to get the NPC's real value.
Using a cell formula of =CEILING.MATH((MIN(MAX(INT((C$3*100+$B4*-100)/C$3), 0), 100)-100)*-1*$F$1/100) where column B is the random roll number and row 3 is the resist value and F1 is the max spell damage, you will get a table of partial damage hits for spells from Oct 8 2001 until Sept 3 2002. In Excel you are able to highlight cells with a specific value using conditional formatting and this makes it easier to find the best resist match. It's more complicated to make a table for damage prior to Oct 8 2001 due to the modifiers and I don't have enough logs of raid content from before that time to get any useful data from.
My October 28 2001 log has AoE damage on Fearplane mobs in it. I had parsed these NPCs on Live years ago, but I can use this log to verify my findings. The expected resist values for a level 60 wizard casting Jyll's Wave of Heat on most of these NPCs should be 10 or 5, which are the floors I mentioned previously, as level difference should give a me -40 and the spell itself another -10, and most of the NPCs in the zone parsed to the typical fire resist of 35 on Live servers. The random level range of these NPCs is 48-52, so 3 out of 5 of these NPCs will have an effective resist of 10 and the other 2 of out 5 will have an effective resist of 5 due to the resist floors.
[Sun Oct 28 17:59:28 2001] a glare lord was hit by non-melee for 65 points of damage.
[Sun Oct 28 17:59:28 2001] A glare lord is incinerated by an intense wave of heat.
[Sun Oct 28 17:59:29 2001] phoboplasm was hit by non-melee for 195 points of damage.
[Sun Oct 28 17:59:29 2001] Phoboplasm is incinerated by an intense wave of heat.
[Sun Oct 28 17:59:39 2001] a glare lord was hit by non-melee for 519 points of damage.
[Sun Oct 28 17:59:39 2001] A glare lord is incinerated by an intense wave of heat.
[Sun Oct 28 17:59:50 2001] a glare lord was hit by non-melee for 65 points of damage.
[Sun Oct 28 17:59:51 2001] A glare lord is incinerated by an intense wave of heat.
The log shows these partial damage hits. 519 is only possible for a resist value of 5, 10, 15, 20, 24 and gets more common above 24. 65 partial damage is only possible for resists 10, 11, 20 or higher. 195 is possible at 10 resist, 17 resist, 20, 24 and higher. So only having these few partial damage hits, we are able to verify that 10 effective resist (effective meaning after all modifiers are applied) value is likely the correct answer for these casts, and the NPCs' resists are 35 because that's the standard resist value. (more on standard resist values later) The next possibilities would be 60 or 70. If you had another log of a player round level 50 you could determine if the resist rate was 35 or 60 just from eyeballing it. (incidentally I did parse them at 35 when I parsed Live server NPCs)
Amygdalans have a higher MR, FR and CR. I parsed them at 95 on Live servers. For these mobs the level difference modifier is either -40 or -32 since my caster was level 60. Fear mobs have a level range of 48-52, so the level 52 ones would end up with a level modifier of -32.
Jyll's Wave of Heat partial damages on Amygdalan warrior: 52, 150, 279, 318, 389, 558
Jyll's Wave of Heat partial damages on Amygdalan knight: 428, 597
Jyll's Zephyr of Ice partial damages on Amygdalan warrior: 292, 565, 571
Jyll's Zephyr of Ice partial damages on Amygdalan knight: 72, 494
The lowest effective resist value that may produce a partial hit of 318 damage using Wave of Heat is 27. The same is true for 292 and Zephyr of Ice. So we can know that Amygdalans had at or above 69 absolute resist value just from those hits. (27+32+10) 95 absolute resist value would translate to either 45 or 53 effective resist value for these spells, and the data matches this for all hits except 428 and 558. I can't explain why those wouldn't match except if a level 52 one with temperate flux staff debuffed, then it would fit. There were a few other wizards there. 95 still fits the best for the data. The next best fit is 85, but if Sony had modified resists I would think they would have raised it by more than 10 on Sept 4 2002. This is fairly strong evidence that common Fearplane NPCs did not have their resists changed since 2001. (probably never)
[Sat Oct 13 23:23:14 2001] Ymmeln was hit by non-melee for 198 points of damage.
[Sat Oct 13 23:23:14 2001] Ymmeln is caught in a torrent of jagged ice.
[Sat Oct 13 23:23:00 2001] Ymmeln was hit by non-melee for 249 points of damage.
[Sat Oct 13 23:23:00 2001] Ymmeln is caught in a torrent of jagged ice.
[Sat Oct 13 23:23:08 2001] Ymmeln was hit by non-melee for 408 points of damage.
[Sat Oct 13 23:23:08 2001] Ymmeln is struck down by Solists spear of ice.
[Sat Oct 13 23:22:01 2001] Ymmeln was hit by non-melee for 960 points of damage.
[Sat Oct 13 23:22:01 2001] Ymmeln is struck down by Solists spear of ice.
I parsed two of these named HoT drakes on Live, although not this particular one, and they also parsed to about 85 cold resist. This 2001 log shows it was maloed, so that means -45 or -60. Reduce by another -10 because of wizard nukes. The NPC is level 60, so there is no level difference modifier. 15 and 30 effective resist value fits for this and not much else. We would expect the resist to be a multiple of 5 which eliminates everything else under 45. The log also shows 11 full damage casts on it, which is a full damage cast rate of 73%, which more closely matches -45 malo. This log data matches Live's value and indicates that the resist value has not changed since 2001 with relatively high confidence.
I have precious few logs of spells landing on raid content prior to September 2002 otherwise I'd give better examples. But this illustrates how it can be done.
A simple way to get an estimate for a resist is by getting the resist rate of full damage DDs or landed all-or-nothing spells on a mob from a log. Since resist rate = effective resist value in this era, it's straightforward to come up with an MR estimate once you account for level difference and other modifiers. This works on Live servers too but you have to multiply the resist rate by two since the scale is 1-200 instead of 0-99 and this is one way I get resist estimates from Live NPCs. On Live servers you can cast hundreds or even thousands of times in carefully prepared logs, but in older in-era logs your estimates will have a much higher margin of error for obvious reasons.
As an example, I have a 2001 log of my warrior attacking Vulak with a Sceptre of Destruction. I can use the proc resist rate to estimate Vulak's magic resist.
In this log, 24 anarchy procs landed on Vulak. 4 resisted. This is a resist rate of ~14%, which would make the effective MR estimate 14. Now we reverse apply every modifier: -40 for tash, -60 for malosini, +50 for level difference modifier and +20 for damage spell penalty on yellows/reds. This makes the modifier -30, so we add 30 to the 14 = 44 estimated MR. That's the procedure, however bards also kept Occlusion on this Vulak for at least some of the fight which throws the numbers off. The resist floor would keep the resist rate at least 10%, so that floor could have been hit here. These factors make for a bad example but I have so few logs to use. I'd expect Vulak's MR to be somewhere around 35-60 looking at this.
I also happen to have five logs of Lady Vox raids of my wizard from before Kunark. In total there are 29 hits and 16 full resists of the Conflagration spell. 12 were full damage. I parsed Lady Vox's fire resist at 60 points on Live servers a decade ago. I also wrote a script to simulate spell casts, so we can test to see if it matches the old logs. Simulating 60 resist value (wizard DDs get -10 but the yellow+red penalty cancels it out) in this scenario results in a full hit rate of about 17% and a full resist rate of 29%. That is somewhat close to the log results of 27% and 35%, particularly when small differences in resist value change the results quite a bit. The margin of error is large with a sample size of 45. One of the logs was from before wizards got the -10 modifier however and the damage was noticeably worse in that, including 3 of the 16 full resists. Also the resist value might be off by 5 as the parse has a margin of error as well. The results at least do not conflict with the classic era resist logic.
|
 |
|
 |
 |
|
 |

06-05-2025, 03:52 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
What Qualifies As Partialable In Classic
When spells resist, they're either all-or-nothing, or partialable meaning they may do damage between 100% and zero. Partial spells have an easier time landing but the full damage hit rate is generally the same as all-or-nothing spells.
In the vast majority of pre-K classic and Kunark, partial spells were defined as such:
* A spell with only one effect in it, with that effect being either SE 0 or SE 79.
* A multi-effect spell with with the first effect being SE 0 or SE 79, and the second effect being a dispel. (a hardcode for dragon AoEs)
* Multi-effect Enchanter spells under level 13 which have SE 0 or SE 79 as the first effect. (lets newbie enchanters hit stuff better)
Both of those SEs (SE stands for Spell Effect ID) are used for DDs and heals. SE 79 is used in multi-effect spells with a DD or heal component, such as buffs. Both SEs are also used in DoT spells. This is what gave druid magic DoTs and the Heat Blood line lure-like behavior before Sony put in spell resist modifier field, because they only had to pass a partial resist check to land instead of an all-or-nothing check. Those two DoT lines just had a SE 0 effect and nothing else. The other DoTs had counters or some other effect in them.
Another noteworthy spell is Flame Song of Ro, which is cast by the Queen in Chardok and the Shaman epic quest mob in City of Mist. This had lure-like behavior because it has SE 0 as the only spell effect, like the druid and necro DoTs. As I mentioned previously, DD spells landing on players always had a 1% full resist rate; this meant that it was impossible to resist Flame Song of Ro above a rate of 1 in 100 no matter how much resist you had in classic.
Geoffrey Zatkin in May 1999 made a comment about the level 4 Enchanter spell Suffocating Sphere being made easier to land. That appears to be the time they added this enchanter spells under level 13 exception:
Quote:
Originally Posted by Geoffrey Zatkin May 1999
Suffocating Sphere was made much harder to resist. As a result they also threw in the chance for damage to be lowered on a successful cast. Probably better for the low levellers, they have more chance to hurt that tree snake now.
|
In late Kunark (it's in the Nov 15 client but not the Sept 19 client) they added another exception for spells with SE 92 in slot two plus SE 0/79 in slot one. This appears to be a hardcode to allow warrior epic procs to be partial hittable.
I am unaware of any changes or additions to partiality qualification in Velious and Luclin up to September 4, other than the Paladin epic proc which is a lure spell.
Partial Spells After The September 4 2002 Patch (PoP era and beyond)
What constituted a partialable spell changed in the big resist system patch of September 4 2002. Sony wanted more flexibility in designing spells and wanted to make spells with multiple effects that could partially hit, so not long before PoP launched they made it such that any spell with SE 0 or SE 79 as the first valid slot could partially hit, but also added a new spell field to override this. You can find this field in spell databases and it might be called "no partial save" or something similar. For example, if you go to Allakhazam and look up the spell Clinging Darkness, then click on raw spell data, you will find a field called "no_partial_save" and the value is '1'. That kept the spell all-or-nothing even though the first effect is SE 0.
To be clear, 'valid slot' excludes Charisma 0 being used as a spacer, meaning slot 1 might be 'blank' and the SE 0/79 might be in slot 2 and it would still partial hit. SE -2 was also used as blank.
Sony mentioned this partial spells change in the patch notes, but it singles out enchanter spells only. This change however applied to more than enchanter spells. I first noticed this while parsing a Luclin raid boss which was landing spells on me when I should have otherwise been immune from having high resists if the spell were using the 1-200 scale. I need to mention however that I'm not entirely certain what they changed the criteria to. Just from observations I've made it looks as though any spell with SE 0 or SE 79 as the first valid slot seemed to partial hit, assuming the no_partial_save flag is 0. There may be more to it than that.
Quote:
Originally Posted by September 4 2002 Patch Note
- Enchanter spells that have a hit point component and a stun component can now be partially resisted.
|
Druid magic DoTs and the Heat Blood line were also given -100 resist modifiers after the September 4 patch, so after that patch they could actually be landed on targets up to 700 effective resist value which is 100 more than typical DD spells can land on. The reason why Sony gave these spells -100 modifiers is because before these spells had a 99% hit rate on anything that wasn't outright immune, and without the -100 modifier the spells would have resisted at a rate of 1% for every 6 resist value, so the resist rate on most things would have gone up considerably without it.
Standard NPC Resist Values
The client decompiles also included the routines which granted PCs and NPCs their base resist values. The same function was seemingly used for both. This mostly verifies what I already knew, but I did learn a couple of important things from it.
The standard NPC resist (I'm making this term up) amount is calculated just like player resists are, minus the class bonuses. (e.g. warrior NPCs do not gain extra MR) NPCs do gain the racial bonuses and penalties however. This means that, for example, troll NPCs have a base fire resist of 5. Most base resists for NPCs however are 25 like players to start.
NPCs get a +10 bonus to resists starting at level 25 that players do not get. I had discovered this years ago but it's clearly in the decompiles and is evidence that NPCs used these resist functions. The function also shows that NPCs will gain resist from worn equipment as well as players.
35 is the standard resist value for magic, cold and fire resist for NPCs at or above level 25. The vast majority of NPCs in the game have this much resist value. The ones that do not are generally raid mobs or mobs with resistances to certain types (e.g. gire giants resist fire) which have additional resists added to them. The standard resist for NPCs under level 25 is 25.
15 is the standard resist value for poison and disease resist. These do not get the +10 at level 25. Interestingly this makes disease and poison spells easier to land and I'm not sure why Sony did this. Perhaps because those spells are mostly all-or-nothing. In my discussions, when I say "standard NPC resist value" I usually mean 35, or 25 for low level NPCs, but technically it can change by race and PR/DR are lower.
If an NPC has a resist value higher than standard then it indicates that extra resist was added outside of this base resist function. When I parsed NPC resists on Live servers, I noticed that a lot of classic era raid NPCs ended up around 435 resist and I thought that was strange or possibly indicated that my methods were off. But I now understand why. NPCs have database fields which are something like "extra MR resist" and are added on top of the standard/base resists. So these raid bosses have a database resist value of 400 which then ends up at 435 due to NPCs running the same GetResist() functions that players use.
Knowing that allows us to better estimate NPC resist values. Parsing NPC resists results in an estimate with a margin of error. If the result is near a number like 85, 185, 335, 435 etc. then we can probably assume that those are the actual values, because they would be 35+50, 35+150, 300+35, 400+35.
435 (or +400 if you will) was the highest resist value I've parsed on classic and Kunark era raid NPCs, other than Vox which was probably increased later at some point. That value seems to have been used as 'immune' in that era but this may have been increased on September 4 2002, which I suspect happened and it may have been 335 (or something else) prior.
Changes Sony Made to NPC Resists Over Time
There are known cases of Sony having selectively modified the resist values of certain NPCs, although the details are little known.
During the Velious expansion Sony reduced NPC resists in at least two patches. This expansion was very melee-centric and Sony had to make adjustments so casters were more competitive.
Quote:
Originally Posted by April 17 2001 Patch Note
- We have reduced resistances to some spell types on some NPCs in Sky Shrine and the Tower of Frozen Shadow. This is the beginning of our overview of the resistances of NPCs in Velious.
|
Sony also reduced NPC resists in the October 8 2001 patch. Abashi said this in a Developer's Corner post at the time: "We’ve also reduced the resistances on a wide range of NPCs, especially for NPCs in Velious" although the patch note merely says "several NPCs". Part of the reason for this was because Sony added immunity flags for for certain spell effects, e.g. slow, so they could lower magic resist on bosses that were intended to be immune to these effects.
Just about everything if not everything that is immune to slow in that era parsed with an MR low enough for spells to hit (at least after debuffs), so we can assume that all of these had their MR reduced from something like 335 down to a lower amount. This is at least a dozen mobs and includes Tormax, Vindicator, Yelinak etc. Kunark and older NPCs seemed to have kept their old MR immune status and I can't think of one that didn't. Beyond that it's pretty much impossible for me to know what NPCs they reduced resists (i.e. cold and fire resist) on in this patch so unfortunately we'll probably be kept in the dark on this forever, although some old user comments might give clues to a handful of NPCs.
I did find one old comment about this patch that I think is worthy of quoting. He/she suggests that MR was only changed on mobs that were flagged slow immune and this sounds right to me:
Quote:
Originally Posted by Player Comment From Early Luclin Era
First the October resist patch was done after severals casters class complain about their dmg output. Only wizards with DB was able to do a significant Dmg to dragons.
In this patch IV reduiced resist check on fire and cold on most of the mobs in ToV ( Normals drakes, normal Vurms , Dragons).
They also reduiced resist check on magic for all the mobs who was 100% mr and they added a new concept (Mobs immune to slow). For all the dragons who were already slowable they DON'T changed the magic resist. (dagarn was one of those, he have the same MR pre and post patch).
In all the post you are making lately you seam to say , NToV was hell before this patch and a piece of cake after. You seam to say that it was not possible to slow any of those NToV Dragons.It is not true. Kreizen Eachen Ikatiar are the only dragons it is and it was not able to slow. About Vyemm it is not possible to slow him but it is not since the resist patch but since another and recent one.
NToV is almost the same Pre and Post patch. The only difference is that mage and druids can be effectives Dmg dealers .
|
https://web.archive.org/web/20020319...cID=2282.topic
It's also very likely that Sony increased NPC resists with the big September 4 2002 patch which doubled the resist roll. The reason is simply because it makes landing spells much, much easier if you double the roll but leave resists the same.
The first altered NPCs I noticed where in Halls of Testing. I had parsed Temple of Veeshan trash NPCs on Live servers and their MR ended up at 185 for almost all of them. If these were 185 MR when spell resists had a 0-99 roll, then landing slow on them would have required malo, tash and occlusion, and one of my old 2001 logs shows that tash+malo was sufficient to land slows. They were however still getting resists after malo+tash which would rule out +100, so 135 pre-patch MR on these seems very plausible. It could have been somewhat more, like +60, but we're not realistically going to be able to narrow it down further without more logs turning up.
A peculiarity I discovered when I was parsing NPCs is the 435 resist values found on classic and Kunark raid NPCs. 435 is actually way higher than is required to make NPCs immune to spells in the old resist system. The immunity threshold is 151, and with unresistable malo + tash that's -85. Occlusion is a Velious song. 335 would have been more than enough to ensure immunity during Kunark and even Velious, even with a -40 level advantage. One problem with leaving them at 335 (if they were 335) would be that non-lure nukes would be able to hit these NPCs decently due to the new 600 scale, which is much larger than the previous 151 limit. I think there is a reasonable chance Sony raised the resists on these raid mobs to make non-lures not land well, then gave wizard lures large negative modifiers to allow them to hit. Wizard lures are -300 after all which is twice as large as a typical NPC AoE lure of -150. On the other hand, lots of Velious trash have 435 cold resist, and it seems somewhat unlikely to me that they'd have edited all those NPCs, so this theory is a bit flimsy and perhaps Sony always had them at 435.
Unfortunately Sony having modified NPC resists like this makes gating content on the emulators difficult if the old resist system were to be put in. It is however very important to look through and adjust NPC resist values downward where it looks likely to have been raised by Sony in the September 4 patch, otherwise stuff will be unslowable and resist far too much. I believe it most likely that Sony went back and raised resists by hand and did not do it algorithmically, which means to be accurate we must do the same, if the old resist system is used. Given that Sony completely neglected to apply no_partial flags to CHA 4 lures, I don't think Sony was particularly thorough about adjusting resists on NPCs either.
Velious Raid Boss Slowability
Slowability of raid targets is by far the most important consideration when adjusting resists, and is the most likely topic that would produce results when searching in old commentary. I believe the available data agrees with my hypothesis that resists were raised by up to 100. I parsed Velious NPCs for resists on Live servers:
Klandicar and Zlandicar have 235 MR
ST Warders have about 255 MR except Hraashna which has about 80. (these have a wider margin of error)
Dagarn the Destroyer has 255
Sontalak 195
Aaryonar 225
Lord Feshlak 195
Lady Mirenilla 210
Dozekar the Cursed 190
The Idol of Rallos Zek 245
Wuoshi 255
The Statue of Rallos Zek 260
Dagarn was known for being hard to slow. I parsed his MR on Live thoroughly to make certain it was 255. Because the resist roll increased from 0-99 to 0-199, the most obvious thing for Sony to have done is increase MR by 100 to maintain his hard-to-slow status. Lets assume they did that. Subtract 100 and you get 155 theoretical old EQ set resist for the NPC. Add 50 for level difference because it is level 70, the effective resist becomes 205 for level 60 players. Subtract 113 from debuffs (Malo, Tash, Occlusion), you get 92 effective MR and a 7% chance to land slow. This seems correct, and if the MR were any different then Dagarn would either be unslowable or easy to slow. I think this is fairly good evidence for my theory that Sony added (up to) 100 points of resist to certain NPCs on Sept 4 2002.
I found this old post regarding the Statue:
Quote:
Didnt have malo/mala last night...so I couldnt do it. Tash, puretone occlusion, malosini bounced 10+ times....after 5-6 mins I FINALLY got malosini to land...at that point clerics were close to oom and people were dropping like flies...after I got malosini to land I canni'd up just enuf mana for a turgurs...bounced.
Done the statue before, had malo and it seemed easy as pie.
|
https://web.archive.org/web/20050501...dex.php/t-1057
Subtract 100 points from Statue's Live MR, you get 160. Subtract Tash and Occlusion and the MR becomes 92, which results in a 7% chance to land spells. Most (but not all) of the mobs I list above were probably given 100 MR, and their other resists were also likely elevated.
Aaryonar is level 66, so the level difference modifier would be +18 MR for him vs. a level 60 caster instead of +50. So the effective MR would be 125+18=143 in his case. The resist rate (with malo and not malosini) would only be 31% after debuffs. This seems too low; old comments mentioning slowing him being reasonably difficult. I think his resists were increased by 50 instead of 100. The other NToV dragons have even less MR than Aaryonar, and after debuffs their resist level would be at the floor if you subtracted 100, so I think all the NToV dragons gained 50 instead of 100. Their secondary resists tend to agree with this as well.
NPCs can wear the items on their loot tables, but not always. Sony's content developers had the option of selecting which drops could be worn by the NPC and which couldn't. For example the Statue of Rallos Zek will never equip the Reaver but he will equip his one-handers which can make him quad attack. I parsed Aaryonar's MR twice and the results indicated that he gained 10 MR when he dropped his neck item. (the 2nd parse ended up at 235) This is a tricky thing to try and figure out and particularly for the First Brood dragons since they always drop a resist neck item. I did find some old commentary that Klandicar was sometimes hard to slow, which could suggest that he gained MR from wearing the armor on his loot table and Sontalak for sure could equip one of his weapons because logs show this. If Dagarn wore his robe he would become unslowable but I did not find commentary mentioning this anywhere. The Dagarn I parsed did not drop the robe. Other than Aary I did not check the loot of the other mobs to have any idea what they had.
Three of the ST warders also parsed to about 255 MR and they are the same level as Dagarn. Old webpages were not unanimous on their slowability but I think generally considered them unslowable. I have a few logs of Warder fights in 2001 that were entirely unslowed. This actually makes sense if you duplicate the Dagarn results, resulting in a 7% chance to land slow before you account for equipped loot. Warders were known to equip their loot, and primals have MR on them. This would be enough to make them unslowable unless the bard proc exploit were done.
Incidentally Cazic Thule after the Plane of Fear revamp in March 2001 was slowable for a couple of months before Sony increased his MR to an unslowable amount. They later applied the slow immunity flag on him and lowered the MR for magic nukes to hit.
|
 |
|
 |
 |
|
 |

06-05-2025, 03:57 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
The Bard Proc Exploit
I don't know when this was discovered, but in 2002 it was publicly known that bards could stack tash debuffs with enchanters by using proc weapons. A couple of Safehouse threads described it:
Quote:
Originally Posted by Safehouse Thread April 2002
If a bard casts a spell that is already on something, it stacks anyway. So, if an enchanter casts Tash on a mob, then a bard procs Tash from a weapon, it stacks. If any other class tries to proc tash over an enchanter cast tash they get "Your spell did not take hold". I don't know why, but bard songs/spells always stack if they are cast last.
If the bard procs Tash before the enchanter casts Tash, then the enchanter one will overwrite it I'm pretty sure, and the bard will need to proc again to make them stack. This is the same idea with bard dmg shield stacking (which I hear is supposedly being fixed again next patch, let's see how badly this breaks bards). Bards can sing at least 2 dmg shield songs and also cast a dmg shield from a SS bracer, and then also cast a dmg shield from a summoned Lava Diamond (from magician VP staff off Druusshk). This is a big key in making monster 130+ pt dmg shields for MTing things like Avatar of War.
|
https://web.archive.org/web/20020514....topic&index=4
Quote:
Originally Posted by Safehouse Thread May 2002
A bard can stack a tash spell on top of an enchanter's tash spell, as long as they aren't the same exact identical version of tash (there are 4 versions). No other class is able to do this.
|
Quote:
Originally Posted by Safehouse Thread May 2002
I did indeed confirm for myself last night that bard item tash stacks after enchanter tash is applied, and does get overwritten if the bard does it first.
|
https://web.archive.org/web/20020601...ID=11187.topic
I don't think this was abused much during Velious. If it were public knowledge in 2001 then Sony would have likely fixed it sooner. Sony was much less tolerant of this kind of thing back then. It obviously makes slowing much easier and would allow ST warders to become slowable when they otherwise would not be, assuming they had ~155 MR.
This exploit seems to have been patched out on September 26 2002:
Quote:
Originally Posted by eqdiva.com front page by Espio
A major patch today for bards, if mostly a stealth major patch. Dont let the offical text below fool you - they made some serious changes to bard stacking without letting us know. It appears that most 'special' bard stacking (such as tash orb/stick, DS'es, and illusions) no longer function (anyone wanna buy my orb?...). In addition, McVaxius' Rousing Rondo no longer stacks with the epic or primal proc. Oh, and the procs from the Epic and Sirensong now interrupt your twisting.
|
My Guesses at Sony's September 4 2002 Individual NPC Resist Increases
Unchanged NPCs: - The standard resist was not changed. It was 25/35 before and after. (this is factual, not a guess)
- Any mobs under 100 resist likely didn't change. I have a hard time thinking they went through the trouble to add small amounts to more trivial mobs.
- I think older era (i.e. pre-Velious) mobs were less likely to have changed. (other than the 435 thing) Sony was probably content to allow them to resist less rather than spend the time to adjust them. Kunark era mobs also tended to be resisty anyway, so I doubt they minded if they became easier to hit.
- The common Fearplane mobs look as though they were not changed. Log data from 2001 shows Amygdalans having the same resists.
- West ToV dragons, Eashen and Ikatiar look unchanged for all resists.
- Tormax, Vindicator, Dain, Tunare, Yelinak and Velketor also look unchanged to me.
- Suits of sentient armor in WL have about 105 FR and CR on Live, which I think was unchanged. The tipa logs and old comments agree they were resistant.
- I parsed the resists of "a geonid" in WL which had 335 CR and ~115 FR. Old comments remark on their high resistance and suggest this was unchanged.
Luclin raid bosses seem to have not been changed, or if they changed some I can't tell. They all pretty much have low to moderate resists on Live servers and the few that are resisty were known to be resisty. (e.g. Aten) Aten would have still been easily slowable with the same MR. This is a little weird because many Velious NPCs seem to have gained resists but Luclin looks unmodified. Perhaps because they had so much HP and Sony was nerfing sustained CH rotations at the time that they just decided to let nukes hit easier.
I did find one exception (well, two) for Luclin however: after the September 4 patch, Seru and Emperor both became magic slowable, and Sony had to disable the spawns temporarily to prevent guilds from killing them easily. This bolsters the theory that they didn't adjust Luclin NPC resists. If they didn't bother to change those two mobs then it seems less likely they changed others.
https://dbsanfte.github.io/eq-archiv...msg-148342.txt
"old ahr used to have super resists and apparently at some point mob resists all got lowered by a *lot* in luclin!" (4/19/03)
https://web.archive.org/web/20030521...topic&index=68
Changed NPCs: - All NToV dragons (minus Kreizenn) look like they had their MR raised by 50.
- ToV trash mobs with 185 MR likely gained 50 MR. It looks as if every NPC in the zone that had over 100 MR gained 50 MR.
- Aaryonar, Feshlak, Mirenilla and Koi`Doken have high all resists so these mobs may have been given +50 to all but I'm far from certain.
- The back five dragons in NToV have lower resists so these may not have gained +50 to FR CR PR DR, but their MR looks elevated.
- Dozekar looks like his MR was raised but not the other resists.
- Three ST Warders (not Hraashna) seem to have gained 100 MR.
- Sont, Kland and Dagarn got 100 MR. Other resists may have been raised too but it's hard to say. (more likely than not)
- Statue and Idol gained 100 MR. Non-MR raised at least 50, maybe 100.
- Avatar of War gained MR, otherwise cripple wouldn't land. Probably 100 although 50 would allow cripple to land with Occlusion.
- Mobs with 435 may have had their resists increased from 335. Mobs with 335 probably were 335 before.
- Old comments under dracoliche say fire hit easily but the FR I parsed was 175. Sounds like FR got 100, maybe less. MR as well.
- Kyenka and Yetarr look like they may have had MR increases.
- Wuoshi had to of gained 100 MR to be slowable. CR and FR raised by 50-100.
- A 2001 comment under Tantor says he was slowed by a mage and enchanter. This probably means Tantor's resists were increased because he parsed at 185 MR.
- Many WW dragons, Mraaka and Tantor have 185 MR and they also have high secondary resists. It's very plausible to me that these had something like +50 applied to all resists. If Tantor was given resists then likely most or all of these did.
Post-Planes of Power Resist Modifications
I am also aware of Sony reducing resists on NPCs in 2006. Rashere (a Sony developer) at the time was planning on doing another resist system revamp, and part of this revamp included a reduction in resists on many NPCs to compensate for the algorithm changes. Rashere ended up not going through with the logic changes but apparently the resist reductions stayed in, unfortunately. I have only noticed the reductions on PoP era NPCs but this is only possible to measure on a tiny handful of NPCs from old logs due to needing hundreds of spell casts to notice small adjustments so it could have been applied to an unknown number of NPCs from any era. See this post for more details: https://www.eqemulator.org/forums/sh...6&postcount=19
I'll snip the most relevant portion of that post for convenience:
Quote:
Originally Posted by Torven
I also want to mention that I found considerable differences in the resist rates on Planes of Power NPCs on Live vs. what I see in AK logs. I can calculate a resist estimate using logs where AoE spells were employed (PBAoEs or wizards quadding) which provide a reasonable sample size. Mobs that have 50 resist on Live seemed to have about 65 resist on AK. I also found mobs that have 65 MR on Live having about 80-85 on AK. (e.g. PoFire flameheads) BoT minibosses (the level 65 ones) also resisted slow A LOT on AK, and their current 155 MR on Live is just too low to match AK logs. I had also done some crude MR tests on Diaku ogres on AK to find the best charm pet, and the results indicated higher MR on AK. (except for one which was oddly lower) Just about every mob I could check from AK logs showed higher resists on PoP NPCs, but I can only check a handful of NPCs because large samples are required.
|
I also found a few cases of Live server NPC resists being wildly different than what AK logs showed. On Live servers now, and was the case in 2014 when I parsed the NPC, Azaracks in Sky are highly resistant to fire but AK logs show fire hitting them well. Sony at some point after AK's time raised their fire resist by a significant amount. The same is the case for Rhag`Zhezum, Arch Lich Rhag`Zadune and Rhag`Mozdezh which are all fire immune on Live servers but AK logs showed fire landing easily. Emporer Ssraeshza is disease immune on Live but old websites all said he was disease slowable. There are likely more cases like these but they are hard to find.
My NPC statistics spreadsheet, which has my parsed Live NPC resist values and is the source for TAKP's data, can be found here: https://docs.google.com/spreadsheets...gid=1108344399
The Level Difference Modifier
An ex-Sony/Daybreak developer by the name of Prathun wrote a post in 2010 which outlined how the resist algorithm worked at the time. It was an extremely illuminating post and invaluable to the emulation community for obvious reasons.
The resist value of the target, while resisting a spell, is increased or decreased prior to the roll by an amount that increases exponentially as the level gap between the caster and the target increases. This is why higher level casters land spells easier on lower level targets and lower level casters have a harder time landing spells.
Prathun's 2010 resist pseudocode post mentions the level difference modifier being this:
Code:
Adjust resist chance for level difference between caster and target.
Set temp level difference to (target level - caster level).
If target is at least level 67 and target is an NPC, temp level difference is set to (66 - caster level) or 0, whichever is greater.
If target is a PC, and caster level is at least 21, and temp level difference is greater than 15, set temp level difference to 15.
If target is an NPC, and temp level difference is less than -9, set temp level difference to -9.
Set level modifier to (temp level difference * temp level difference / 2)
If temp level difference is negative, make level modifier negative.
If target is an NPC and caster is far below target's level, set level modifier to 1000.
That is very similar to the older logic. All of the older clients (including the February 1999 client) do this:
Code:
level_diff = defender_level - attacker_level;
if (!IsNPC()) {
if (level > 20 && level_diff > 10) { // green/low cons casting on clients; cap resist advantage player gets to 50
level_diff = 10;
}
}
else if (IsNPC() && level_diff < -9) { // caps the level advantage when casting on green/low NPCs to -40 resist; same as modern EQ
level_diff = -9;
}
level_mod = (level_diff * level_diff) / 2; // modern EQ still does it this way
if (level_diff < 0) {
level_mod = -level_mod;
}
So the two main differences between old EQ and modern EQ are that players get a lot more benefit from level difference when saving against green NPCs in modern EQ and the penalty from level difference when attacking very high level NPCs is capped at defender_level = 66. Also starting in Omens of War when players go over level 66, the level difference mod starts to vanish even from NPCs below the player's level.
The level 67+ thing would have made level 70 bosses resist less while the player cap was still 60. It's possible this was put in during Velious in the October 8 patch. It's also possible it was put in with the September 4 2002 patch. It's also possible it was put in for Omens of War or some other time. We can't know without some kind of Darkpaw leak. The need for such a thing increased with PoP where boss levels went from +10 to +15. (bosses could be level 80 in PoP although I suppose Sony didn't have to make them that high) I really doubt it went in during Velious because it would have made raid bosses much easier to land slows on and I've found nothing mentioning this. Furthermore Luclin bosses are all level 66. If I were forced to pick one, I'd say Sept 4 '02.
The old clients do not have that 1000 modifier in them. We could not find anything relating to the "six level limit" (I explain what that is later) in the old clients or any sort of resist wall when fighting against NPCs that are much higher level than the caster. It seems to have been outside the functions we have decompiled and likely in a function the clients didn't have. The diff*diff/2 modifier would have prevented spells from landing on very deep reds though, and anything 16 or more levels above the player would have resisted even lifetaps in classic unless it was magic debuffed if this six level limit didn't exist, as it's not capped in that direction on NPC targets.
Resist Roll Details
Prathun's 2010 resist pseudocode post describes modern EQ's resist roll this way:
Code:
Roll a random number between 0 and 200.
If the roll is greater than the resist chance, spell lands.
However by doing tests on Live servers in 2021 I have determined that the roll is actually 1-200 there. I don't think Prathun was very concerned about being precise and unambiguous here when he wrote that, but we can't be sure when the roll changed from 0 to 1. Sony's random number function, in the code, is called like this: RollNum(X, Y) and the possible outcomes end up being X to (Y-1). So when Prathun said "0 to 200" he may have meant 0-199 inclusive there. I speculated in another thread that Rashere (a Sony dev) may have done it in 2006 but I really can't know when it was, and it may just have been some time between 2010 and 2021 as Prathun's post suggests.
Classic eras (pre Sept 4 2002) for certain rolled 0-99 (inclusive) as the decompiles show. I'm somewhat sure (although I wouldn't say 100% sure) that after the September 4 2002 patch the roll still started at zero instead of one. It was either 0-199 or 0-200. I have no way to determine if the top roll is 199 or 200 without a running Sony PoP server to parse on, which don't exist anymore (RIP AK), but I was able to find out via old logs that the lowest roll is very likely 0 post Sept 4 revamp. See this post for details: https://www.eqemulator.org/forums/sh...6&postcount=19
In all eras that I'm aware of, when the roll is equal to the effective resist value then the spell will resist. This is the case in the decompiles, Live circa 2021, and Prathun's pseudocode also says > instead of >=. My AK log data cannot rule out >= as it's not enough to, but the data does suggest that it was also > in PoP.
There are two consequences to the roll starting at zero and checking it using a > instead of a >=.
The first is that the effective resist rate in classic (which does a 0-99 roll) ends up being resist value + 1. For example if the resist value is 5 then the possible roll outcomes which result in a resist are 0, 1, 2, 3, 4, 5. That is six values out of 100 possible rolls so a 6% resist rate. After Sept 4 the resist rate ends up being 3%. (or 2.985% if a 0-200 roll) If the roll starts at 1, then the resist rate becomes 2.5% using a 1-200 roll. This has large implications for tick saves.
The second is that it becomes possible to resist a spell even at zero resist value by rolling a zero if not accounted for. Classic eras had floors to prevent the resist value from being zero, but after Sept 4 2002 it became possible to guarantee that a spell will land for full on a target. (either by debuffs, level difference or spell resist modifier) The partial damage math breaks if the resist value is zero, so it seems that Sony had a "if resist == 0 then spell lands full" check in place before they switched to 1-200.
The classic clients also did this:
Code:
if (roll > 98 || roll > resist) {
// land spell
} else {
// resist spell
}
They explicitly allowed for a one percent chance of landing a spell regardless of the target's resist value. The roll is skipped if the 151 threshold is met earlier in the logic however, so basically if the target was lifetappable, then there was at least a 1% chance to land any (MR) spell on it. Theoretically this would have allowed slow spells to land on some otherwise unslowable targets, although the gap between 99 and 151 isn't very large and most unslowable mobs would have been above that 151 threshold. It also makes the difference between the 99 PC cap and 100 NPC cap mostly pointless because a 99 roll will simply land the spell although NPCs would partially resist a tiny bit more.
I did find log evidence of this. I have 2001-2002 logs of my warrior getting hit by spells very very infrequently when I know that my resist was > 100 at the time. (seen in screenshots and logged buffs) I was pulling mushrooms in Fungus Grove for AoE groups, which cast several hundred spells on me. The hit rate was about 1% as expected. I also had Thunder Blast land once in 61 casts in a log.
|
 |
|
 |
 |
|
 |

06-05-2025, 03:59 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Hardcoded Immunities and Spell Level Limits
Various spell immunities are in the decompiled resist function. This confirms a lot of what was common knowledge but it also provides hard evidence for less understood immunities, such as charm on merchants, so this was a nice thing to find.
Charm Limits
Charm level limits are in the clients. They were hardcoded back then. Not only that but Sony changed the limits on some of these spells over time.
The pre-launch February 1999 client did not have any immunities or limits in it. They were added later.
The next client we have and examined is the March 21 2000 client. (very late pre-K classic era) The limits are in this client, and they are the following:
Charm: 18 (-7)
Befriend Animal: 21 (-3)
Beguile Plants: 24 (-1)
Dominate Undead: 32
Charm Animals: 33
Tunare's Request: 35
Beguile: 37
Solon's Song of the Sirens: 37
Beguild Animals: 43
Cajoling Whispers: 46
Beguile Undead: 46
Allure: 51
Allure of the Wild: 51 (+2)
Cajole Undead: 51
Call of Karana: 51 (-2)
Thrall of Bones: 51 (-2)
Boltran's Agacerie: 51 (-2)
Enslave Death: 51 (-4)
Dictate: 51 (-7)
(The number in parentheses is the difference from Al'Kabor/TAKP's spell data)
This client calculates a level limit for lower level charms using a simple formula. So it appears as though Sony first tried to level limit charm using a formula but later decided to go with individual spell hardcodes for the higher level ones. Said formula is level_spell_was_obtained * 15 / 10, capped at 24. Enslave Death and Dictate are set to 52, then right after that there is a "if higher than 51 make equal to 51" check, which appears to be vestigial. It was gone in the next client we looked at.
Thott mentions that, for a period some time prior to March 2000, charm level caps were spell_level + 10 here: https://web.archive.org/web/20040301...bard_charm.php
It's noteworthy that Boltran's originally had a cap of level 51. Necro charms also had lower limits. Druid 44 charm had a higher limit and it was nerfed at some point after Kunark. Dictate too was much lower than it ended up being later. This client also included a "Your target is too high of a level for your charm spell" message. (patched in October 28 says notes) You might ask why Boltran's and Call of Karana were useful then because they had the same level limits as the Allures and Boltran's had a shorter duration. The answer is because the Kunark charms had -10 modifiers and shorter cast times.
The March 2000 client also had charm immunity for city guard NPCs. The spell logic checked for specific race IDs in a switch case. These IDs are: 44, 67, 71, 77, 78, 81, 88, 90, 92, 93, 94, 106, 112, 139. If charm was cast on any NPC of any of these races, the spell automatically resisted. To be clear: charm immunity was added to guards at some point during the classic era, and this March 2000 client is only one of two clients in that era that we were able to obtain and decompile, the other one being before the game even launched, so it was likely added much earlier than March 2000. (perhaps December 99 since a patch note mentions guards gaining 'stats')
The next oldest client I examined was from 4 days post Kunark launch. (April 28 2000) These clients are 5 weeks apart. The differences between these clients include: - Merchants were made immune to charm. Class was checked for this. (class 15 at the time was merchants)
- Body type 6 (emus call it type 'Extraplanar') was made immune to charm. Not many NPCs have this type. These NPCs include: Efreetis, Fearplane minis, PoSky bosses, planar projections, Yael and a few other things.
- A check was put in that prevents level 38+ bards from charming NPCs that are higher in level than the bard is.
- Enslave Death and Dictate were raised to 52 because the "if < 51, set equal to 51" check was removed.
The following spells were either added to the client or had their limits changed:
Charm: 25
Befriend Animal: 25 (+1)
Beguile Plants: 25
Solon's Bewitching Bravura: 51
Enslave Death: 52 (-3)
Dictate: 52 (-6)
Alluring Whispers: 49
Persuade: 35
The November 29 2000 client, which is the final client before Velious launch a week later, had the same limits as the client from 4 days after Kunark launch.
When did Boltran's limit increase from 51 to 53? A post in this January 30 2001 thread says it was 53: https://web.archive.org/web/20010212.../052705-4.html
The above charm level limits applied to Kunark and later era zones. In pre-K classic zones (Sky is not included in this), charm was limited to level 51 and under NPCs, meaning Dictate would fail on level 52 NPCs in these zones. Sony had stricter rules for these older zones. That may sound strange but a May 2002 patch note says this: "The Hole now uses Kunark-level Mez/Charm/Taunt rules" and this: "High level NPCs can now be taunted in Cazic Thule". It appears that at Velious launch they increased Boltran's to 53 and Dictate to 58 but limited all charms in classic zones to level 51, and Hole was limited like classic zones until May 2002. (incidentally the old taunt rules were probably that NPCs above level 50 were immune)
The decompiles don't show this pre-K charm limit. I found it in old threads. This Caster's Realm thread is quite explicit about it:
Quote:
Originally Posted by A Player on April 27 2001
OLD WORLD charms (ALL OLD WORLD CHARMS) cap at level 51. This includes the plane of hate, the plane of fear, the hole, etc. Also, all mesmerize spells cap somewhere around 51 - not sure if it's 51 exactly...Maybe more like 53.
New World charms have increased level caps - Boltran's caps at 53 in the new world, while Dictate caps at about 58. The new world includes all of velious and kunark as well as the plane of sky. Don't believe me? Try Dictating your level 55 friend in west commons, and then try it in dreadlands.
|
The thread can be found here: https://web.archive.org/web/20010430...c&f=9&t=004361
Fear Immunity
The March 2000 client has the fear immunity for level 53+ NPCs in it, so it existed in pre-K classic era.
I found what appear to be checks for the fear immunity discipline in the later clients, and also some other kind of immunity check that I could not decipher. It might be some kind of NPC immunity flag.
Mez Immunity
Giant and dragon mez immunity is found in the Kunark clients. It's missing from the March 21 2000 client (at least the red message for it) but is found in the April 28 client. I think prior to Kunark, the spells landed without giving a resist message but the spells just didn't function. Why I think that is because I found this comment:
Quote:
Originally Posted by Enchanter forum comment March 31 2000
Giants are completely immune to mez series and stuns.....
it will say "they are mezzed" but if you watch them, they are NOT.
|
https://dbsanfte.github.io/eq-archiv...html/2557.html
Specifically what blocks mez is: if the target is stun immune, or has a body type of 4 (giant) or 26 (dragon) or 29 (dragon 2).
The IsStunImmune() function found in the clients needs some explaining. This function is found in all of the clients except the February 1999 one. This function is very small and does the following: checks if the target is a NPC, and checks if the level is above level 52. If both are true then it returns true, else false. Now obviously mobs above level 52 could be stunned and mezed. Why I think the clients have this is because this function is using the pre-K classic era rules as I explained in the charm section above. In pre-K classic, nothing could be mezed or stunned above level 52. This would include the Hole until Luclin (May 2002) when they switched the zone's rules as the patch note stated.
"Enchanters are the only class that can mezz these things, so , thats what the spell is for. You dont have to use it in ANY old world zone ( including hate / fear ) because old world mez rules apply ( anything 53+ cant be mezzed or charmed at all, like in the hole )." (5/18/01)
https://web.archive.org/web/20020114...icID=325.topic
Bard Mez Secondary Resists
At some point during pre-K classic era (August 1999? I can't find a patch note) Sony nerfed bard mez because they considered it too powerful to perma-mez an NPC with a manaless ability. Abashi says this:
"There is no way that we can make a no-mana song as powerful as a mez as good as a spell that uses mana, which is the reason that it was changed to its current state last August or so."
"I think that I've effectively addressed these issues above, but I'll elaborate. The original implementation of the song made it much too powerful considering its lack of a mana control." -- Abashi (09-07-2000)
https://web.archive.org/web/20010303.../011801-2.html
The nerf consisted of a secondary resist roll that wasn't MR based. These secondary resist rolls were found in the clients.
This September 2000 post by a player describes how bard mez resists worked:
"Bard mez songs (Lullaby, Pixie Strike, Twilight) have two resists. The first is your ordinary resist that all songs/spells get. The second resist is something GZ threw in only for bard mez. When a bard tries to mez a mob, three things can happen:
1) The mob succeeds its first resist check and the song doesn't work. Message:
"Mob resisted your <song name>."
2) The mob fails its first resist check, but succeeds its second resist check and the song doesn't work. Message:
"Mob's head nods."
"Mob resists your attempt to mesmerize it."
3) The song is not resisted and works. Message:
"Mob's head nods."" (09-07-2000)
https://web.archive.org/web/20010303.../011801-2.html
The secondary resist check was in the HitBySpell() function in the clients but later moved into the resist check function. Sony moved it because the above post mentions the songs were resulting in a success message even when the secondary resist fails, and moving the check into the primary resist function makes the secondary resist look like a normal resist. Sony moved it not long after that post was written.
There are actually two different secondary resist checks: one for single target mezes and one for AoE mezes. The AoE songs had a harsher secondary resist check. Because Sony failed to mention this, players at the time were reporting that bard mez resists were higher than what Sony was telling them they should be, as players were testing using AoE songs which had a higher resist chance.
The secondary resist chances were the following for single target mez songs:
target level 1-24 = 5%
target level 25-29 = 25%
target level 30-49 = 34%
target level 40-44 = 50%
target level 45+ = 60%
The secondary resist chances were the following for AoE target mez songs:
target level 1-24 = 34%
target level 25-29 = 50%
target level 30-49 = 67%
target level 40-44 = 78%
target level 45+ = 82%
I found three posts from two different players who ran some meticulous resist tests during this era on bard mezes that are worth linking here. In these tests you can clearly see the green con MR floor of 3% and the resist rate on Kelin's cast on level 1 mobs being ~35%.
https://dbsanfte.github.io/eq-archiv...tml/14644.html
https://dbsanfte.github.io/eq-archiv...tml/20040.html
https://dbsanfte.github.io/eq-archiv...tml/13968.html
Those secondary resists were the case until November 15 2000. On that date the patch notes included the following:
Quote:
Originally Posted by Nov 15 2000 Patch Note
– Pixie Strike: Made it less resistible by reducing the secondary resist.
– Song of Twilight: Increased duration. Made less resistible by removing the secondary resist. Allowed it to be cast at any time of day.
|
After that patch the single target mez secondary resists changed to the following:
target level 1-24 = 5%
target level 25-29 = 10%
target level 30-39 = 15%
target level 40-44 = 25%
target level 45-49 = 35%
target level 50+ = 60%
Song of Twilight was hard coded to always win the secondary roll. The AoE mez secondary resists were unchanged.
Kelin's Lucid Lullaby was changed into a single target mez in the big September 4 2002 patch. Presumably the big resist revamp patch is also when they removed the secondary resists on bard mezes. I found comments as late as December 2001 complaining about resists, then after Sept 4 2002 people started mentioning that the resists were better. (Luclin era had less chatter on any given topic)
Also regarding Kelin's: I found two comments remarking about how overpowered this song was in beta, so I think it's possible that Sony gave AoE mez songs a secondary resist before single target mez songs did in what Absor says was August 1999. Neither were found in the February 1999 client however.
|
 |
|
 |
 |
|
 |

06-05-2025, 04:04 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Lulls
Lull spells before the September 4 2002 patch were almost useless. Similar to bard mez only worse, they were effective at earlier levels then the resist rate ramped up considerably to an unusable level, until NPCs became entirely immune at level 50. Lulls were so bad that players used the spells to pull because the aggro on resists was supposedly low.
"When you can reliably get lull to stick on 3 mobs in a 4 mob pack and pull the last one alone let me know."
"lull is the most useless spell ever. try casting it on any creature you want, doesn't matter if its green even, just wait for the resist message. we get upgrades in this line which you might think would work better, but they don't. we even get an area effect lull at lvl 51, that's the biggest joke of them all. they should just drop that spell and give us another useless spell in it's place like another in the whirl line."
"And when you let him know you had better take a screen shot and post it too because I'm gonna read it and call you a liar. Lull does nothing, I use it to pull in hate since it's agro isn't very high and it's easy to taunt off me."
"I read on a different board once that they should either fix the Lull line or change the first letter to P. /rofl"
"put this one way at the back of your spell book, and do the same with any upgrades should you happen to purchase them. There's a very good reason Lull is usually referred to as "Pull" and the level 51 AE version Wake of Tranquility is usually referred to as "Wake of Train-quility"."
(April 2001 thread on Caster's Realm)
https://web.archive.org/web/20010430...c&f=9&t=004347
Classic lulls had an MR floor that increased as the target's level increased. This variable floor is in the clients so we can know what they were. (it was not in the Feb 1999 client however) This floor was applied after the level difference modifier was applied, like the other floors.
The resist rates for lulls in classic were the following: (assuming the NPC did not have higher MR than these floors)
On targets level 1-14: 11% (10 MR floor)
On targets level 15-24: 21% (20 MR floor)
On targets level 25-34: 34% (33 MR floor)
On targets level 35-39: 43% (42 MR floor)
On targets level 40-49: 55% (54 MR floor)
On targets level 50+: 100% (100 MR floor)
To be clear: this is a tiered floor, not an override. If you cast Lull on a level 1 NPC as a level 1 player, the resist rate would be 26% because the standard MR at level 1 is 25. So even at lower levels the spells weren't that good.
Late into Luclin, lulls were turned into an override of 15 MR. (before level difference) Sony did not mention this massive improvement to the lull line in a patch note however, so the date is technically unknown however it likely was in the September 4 2002 patch. EQ's lead designer at the time, Rich Waters, said this about seven weeks after the September 4 2002 patch:
Quote:
Originally Posted by Rich Waters, October 2002
Traditionally, most lull spells haven't really worked very well. Lull had a high resist rate and wasn't very reliable. We've recently looked at lulls and improved the way they work to make them more useful to players. You should find that the lull line of spells works more often than it did, though it will still fail some of the time.
|
The song Kelin`s Lugubrious Lament was hardcoded to be special. What this song did was halve the target's effective resist value, and it had a floor of only 5 MR. This made it the best lull spell by far and the only one that could be used on level 50+ NPCs. It's only a few ticks and I suppose Sony wanted to give bards something unique so that's probably why they made this exception.
Druid Harmony (the level 5 targeted AoE) was supposedly unresistable but I found several comments by players saying that it could be resisted, however it would still never aggro on a resist. I presume that the players are referring to lull immune NPCs. This spell was heavily nerfed on PoP's launch day (October 21 2002), after which the spell was unusable on NPCs above level 40. Rich Waters says this at the time:
Quote:
Originally Posted by Rich Waters, October 2002
We've had the ability for awhile now to make it so certain monsters were totally immune to being lulled. This is especially common in higher level outdoor zones such as Cazic-Thule, since Harmony would take any challenge out of splitting groups of monsters. Having a such powerful game dynamic with no risk of failure made these encounters too easy, so we were forced to make many monsters totally lull immune to compensate. With this change to Harmony, we'll now be able to remove the lull immunity from many monsters, and turn this ability into a useful spell line that gives great benefits, but with some risk that your spell may be resisted.
|
Literally the next day after PoP's launch they put in the single target spell Harmony of Nature.
Lull Early Fade Chance
Lulls could fade early in a manner similar to charm and root. This lull break chance was found in all clients and still exists on Live. (it was not removed on Sept 4 '02) I ran tests on Live servers years ago to come up with data so I could mimic it on TAKP.
The clients have a routine in them that appears to be called on a debuff's tick, but only for a handful of SEs. (similar to EQEmu's Mob:  oBuffTic()) This function has the break early chance for lulls in it, but it does not have the break early rolls for charm, root, fear, blind. This function shows lulls having a preliminary roll just like root, fear and blind which is a 3 in 4 chance to do a resistance roll. It also has the +4 caster level in the resist function call, so this is a nice verification of that existing. This is the same as Live servers today. However the February 1999 client has this preliminary roll being 2 of 3 instead, so it appears at the end of beta at least lulls were a bit less likely to fade early.
The floors do apply in the resist checks done on the lull's tick saves, so even if lulls were to land they wouldn't have lasted very long. The spells may have a listed duration of multiple minutes but practically speaking they were extremely unlikely to last the full duration on anything but the lowest level NPCs.
Incidentally I did find a January 2000 post mentioning that critical fails occurred back then: https://dbsanfte.github.io/eq-archiv...tml/13483.html
The critical fail chance in PoP and Live was and is: 90 - CHA / 4. See my other thread for where I found this. I can't say if it was the same in classic but if I had to guess I'd say yes.
Spell Resist Modifiers
The resist function had the hardcodes for individual spell resist modifiers. These were all hardcoded prior to September 4 2002, and after that patch they finally put in a proper spell field for it. This is the 'lure' number everybody checks for in the spell data. Some of the spells prior to that patch weren't just a simple negative number however. Classic era lure spells I explained earlier were more complicated and so are a few other things in this section. I will also include some other non-lure related resist modifiers here.
Wizard DDs
Wizard DDs gained their micro-lure (-10) behavior on January 20th 2000. Sony simply subtracted 10 resist on wizard DD spells in the resist algorithm to do this.
The qualifications for spells to get this -10 are simply: the spell must be MR, FR, or CR; it must be level 35 or higher; and if it's a MR spell, the first spell effect must be 0 or 79.
The Charisma Resist Modifier
Some spells got a resist reduction to them if the caster had high charisma. I mentioned this in my Charm, Root and Lulls thread regarding charm. Most people have heard about this but I found some more information about it.
First of all, it applied to all classes, not just enchanters. NPC casters were excluded.
Secondly it applied to Charm, Mez, Mem Blur, and SE 34 which EQEmu labels as 'Confuse'. SE 34 seems to be an unused and abandoned SE.
Thirdly this did apply to charm tick saves in classic from what I can tell, but it of course did not take the resist below the floors. The floors made charm much worse back then, however this charisma modifier working on tick saves often removed the need to keep tash on the pet.
The formula is the same in all clients: reduce the resist by 1 point for every 8 charisma above 75. This reduction is capped however.
The reduction cap was not always -25. The cap was -10 until some time between September 2000 and November 2000. The September 19 client has a -10 cap and the November 15 client has a -25 cap. This means any charisma above 155 did nothing for resists until late Kunark.
Damage Spells Penalty on Yellows and Reds
There is a resist penalty when casting damage spells on level 17+ NPC targets above your level. This penalty is level_diff * 2 and can be debuffed away.
For example: a level 60 caster attacking a level 70 NPC will suffer a resist penalty of 20 added to the target's resist value.
This penalty is only applied if the first spell effect ID is 0 or 79. So it may affect spells with a stun component and such if damage is in the first slot.
This penalty was mentioned in the Prathun pseudocode post, so it seemingly never went away.
Other Spells With Micro-lures
Six spells had a -10 micro-lure applied to them like wizard DDs. In the case of charm these did apply to tick saves.
The spells are:
Enforced Reverence
Call of Karana
Thrall of Bones
Fascination
Glamour of Kintaz
Boltran's Agacerie
All six were in the March 2000 client.
Fear Spell Modifiers
For spells Invoke Fear, Panic the Dead, Terrorize Animal:
If the target was higher in level than the caster, the resist adjust was -5.
If the target was equal to or lower in level than the caster, the resist adjust was -(caster_level / 2).
For spells Trepidation, Repulse Animal, Dread of Night:
If the target was higher in level than the caster, the resist adjust was -15.
If the target was equal to or lower in level than the caster, the resist adjust was -5 - caster_level / 2.
All six of these spells had an MR floor of 5. These are all found in the March 2000 client.
These resist modifiers are why fear spells have the same or similar durations and cast times but cost more mana and are obtained at higher levels, although the resist reduction isn't really worth the increased mana costs, channeling benefits of lower level spells and reduced fizzles.
Harm Touch
Harm Touch was hardcoded to resist less but it wasn't a resist value adjustment. Instead it was a mitigation reduction done after the partial damage was calculated. This means that Harm Touch would still fully resist every time at 151 effective resist value but at 150 or less the damage would hit for full damage much more often and mitigate less.
In the February 1999 client this reduction was only 15%. In the March 2000 and later clients this reduction was 45%.
Misc Modifiers
Three specific tash spells (the Enchanter MR reduction line) are hard coded to reduce the target's MR to 2/3rds before landing. This appears to be vestigial because tash spells are unresistable. Sony oddly left this in for a long time. (it's in the November 29 2000 client)
Lycanthyropy spells (SE 44) get a +30 resist adjustment to them, making them easier to resist. Only one spell in TAKP's spell data uses this, which is the 'Lycanthropy' spell. It appears to be some kind of unfinished effect as the debuff seemingly does nothing in the TAKP client. Google tells me that later versions of EQ (after TAKP's time) seem to have made this functional.
Resists in PvP
PvP resists were the same as NPC resists for the most part, but there were some important distinctions.
PvP damage was reduced and this reduction was handled outside the resist function. There is a Test server patch note from August 26 1999 which mentions a 60% reduction for the teams server, and a September 13 1999 patch note says: "All damage spells cast in PVP combat will do less damage to the PC than the same
spell would do to an NPC" so that may have been when it went in. I don't know the specifics of when the mitigation went in or how it may have changed. Solar got the precise PvP mitigation percentages (it varied depending on level and class) from one of the clients and I think these numbers were in use for the vast majority of the older eras. I'll outline it here:
Hybrid (4 classes) spells were all 80% of normal.
Caster+priest spells obtained before level 14 were 88% of normal.
Caster+priest spells obtained in levels 14-23 were 78% of normal.
Caster+priest spells obtained in levels 24-38 were 68% of normal.
Caster+priest spells obtained after level 38 were 63% of normal.
Harm Touch was 68% of normal.
Harm Touch was changed to 68% in this patch:
Quote:
Originally Posted by February 21 2001 Patch Note
— Due to the recent improvements to “Harmtouch”, it is doing much
more damage than it would before and unbalances PvP. As such it will
now do less damage in PvP (68% of PvE, down from 80%).
|
Before October 8 2001, a major difference in PvP resists compared to PvE was that the NPC partial damage modifiers only applied to NPC targets, so higher level players didn't resist DD spells more and the lowest level players didn't resist less like NPCs did. See the partial damage section for details on these modifiers.
The first significant PvP resists patch that I'm aware of was May 31 2001. This patch made debuffs 50% stronger in PvP:
Quote:
Originally Posted by May 31 2001 Patch Note
- Resist-debuffs will do 1.5 times their normal value for PvP
encounters. In other words, if the spell did -60MR in PvP before, it
does -90MR in PvP now. PvE (combat versus NPCs) remains unaffected.
|
Late Luclin, the September 4 2002 patch radically changed the PvP game because resists essentially became half as powerful, and you needed twice as much resist value to resist at the same rate as you did prior to the patch. This angered a lot of PvP players. (I quoted some of these players in the first section of this document) Sony responded by changing the resist scale for PvP resists. This PvP scale is bow shaped instead if linear such that resists under 200 made spells resist more than they would in the linear PvE scale but the resist rates ended up the same near 200. This new scale was put in a few weeks later:
Quote:
Originally Posted by September 26 2002 Patch Note
- The recent resist changes have been adjusted for PvP. They will not
be exactly as they were prior to the resist change, but they should be
reasonable now. Please let us know if you feel they need further
adjustment.
|
For TAKP I mimicked this PvP scale by doing this:
Code:
// PvP
if (caster->IsClient() && target && target->IsClient() && !use_classic_resists) {
if (resist_chance > 1 && resist_chance < 200) {
resist_chance = resist_chance * 400 / (200 + resist_chance);
}
if (resist_chance > 196) {
resist_chance = 196; // minimum 2% chance for spells to land
}
}
With that PvP scale patch, Sony introduced a bug. If players had very high resists, north of 300, then the resist rate started to go down. The cause seems to be that Sony's PvP resist curve was parabolic and they failed to prevent higher resists from using this curve, which peaked around 275 then went down. The problem was so bad that at 500 resist value, the resist rate for all-or-nothing spells was about equal to that of somebody with about 50 resist. I parsed this on Al'Kabor and I also found some old comments about it. I don't know when they fixed it but it was seemingly after June 2003.
This Graffe's thread from June 20 2003 was started by a player who noticed spells landing frequently with very high resist values:
https://web.archive.org/web/20030829...ID=27020.topic
An April 2003 patch put a cap on DD spells in PvP:
Quote:
Originally Posted by April 8 2003 Patch Note
- Direct Damage spells in Player vs. Player combat can only do up to
75% damage of the target’s maximum hit points in a single hit. This
modification is done before resists are taken into account. For
example, if a player has 1000 hit points when fully healed, no single
direct damage spell will do more than 750 damage to him in a single
hit.
|
That patch was particularly important for Harm Touch because prior to that Harm Touch could do 4896 damage to players in PvP. (7200 * 0.68 )
In August 2004 Sony added more PvP related fields to spells to allow them to land on high resist payers.

|
 |
|
 |
 |
|
 |

06-05-2025, 04:09 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Bash Resists and Stun Immunity
Bash and kick stuns aren't spells but since we found the logic in the client and it's somewhat similar I'll go over it here.
The chance to 'resist' a bash stun is 55%, modified by the same level difference modifier as spells. I.e. level^2/2. It uses the same function.
The bash stun immunity check is the same function used by spells.
The check for level 55+ warriors using kick to stun and ogre race immunity is found in the decompiles.
Bashes that don't actually stun the target due to failing the roll will do a second roll to determine if the bash will interrupt a spell cast without stunning the target. These stunless interrupts still check for stun immunity however and will not happen on stun immune targets. This chance is 7 in 9, or about 78%.
Spells 859 to 1023 are excluded from this stunless interrupt chance.
There is a 1% minimum chance to full stun the target if it's not stun immune. (level difference would make deep reds immune otherwise)
The logic is this with some irrelevant things removed for clarity:
Code:
bash_resist_chance = GetBashResistChance(attacker_level);
roll = Rand0(0,100); // random number from 0 to 99 inclusive
if (roll < 99 && roll <= bash_resist_chance) {
roll = Rand0(0,9); // random number from 0 to 8 inclusive
if (roll > 1 && (spell_id < 859 || spell_id > 1023) && !this->IsStunImmune())
{
InterruptCast();
}
}
else {
StunMe(2000); // Stun for 2000 milliseconds. This function has a stun immunity check inside it as well
}
Pre-Velious if a target was stun immune then it was also always mez immune. Before Velious this immunity was solely hardcoded by race and level as far as I know. But in Velious they added a mez immune flag, so a target could be made mez immune but not stun immune. I started finding mobs in PoP that were stun immune and not mez immune but I can't say when exactly they added a stun immunity flag.
Interestingly the stun immunity check was added in the late pre-K classic period. My earliest log is from December 31 1999, and is of my wizard on a Vox raid. In this log Lady Vox is interrupted by bashes 19 times, and her melee damage is less frequent than expected, so the bashes may have actually been stunning. Even bashes from pets were interrupting Vox's casts. I have more Lady Vox logs from late pre-K classic era and there are no Vox interrupts in them, the next log being February 19 2000, so Sony added the stun immunity check to dragons between Dec 31 and Feb 19.
The February 1999 client handles bash a little differently. There is no stun immunity check and the chance to stun is reduced on targets above level 30 by an amount that increases the higher level the target is. The base stun chance is also 50 instead of 55.
Enchanter NPC Resist Override
Level 25+ Enchanter NPCs were hardcoded to be able to land spells on players no matter how much MR the players had. This was in all the decompiles except the February 1999 one.
The Enchanter NPC resist override reduced the effective resist to the target's level if the level was lower than the resist, then capped it at 40. So technically this would be a very low ceiling but since players are unlikely to face enchanter NPCs before they reach level 40 aside from evil eyes I just labeled it an override.
For the most part it caused green enchanter NPCs in Velious zones to grief players. I found several old threads to confirm this override:
"Um Sirens can charm through 200MR when they are green ... (trust me after that long soloing those things for the shawl) ...
Their charm is lured ... because this is the only spell that will land ... and being chanters all spells are MR based"
"I guess you could call it a lure. What's really wrong with sirens charm is the same thing as every other mobs charm. It's broken.
It doesn't matter what your mr is, because according to popular opinion on these boards, the NPC charm spell doesn't do a check on your MR, it just sticks.
Whats even worse than that is you can be charmed by a mob that already has a pet."
(Feb 2002)
https://web.archive.org/web/20020214...&f=19&t=001372
"By the way, if invis pops you could have 200 mr and those green freaking sirens can still charm a lvl 60. Talk about overpowered." (04-25-2001)
https://web.archive.org/web/20040928...p/t-10835.html
"They charm you right away as soon as they are within casting range. My Magic Resist with druid buff was at 170 and I was charmed just as often as my druid partner was with his MR at 60." (Jan 3rd 2002)
https://web.archive.org/web/20030130....shtml?id=6153
"As a level 60 monk...with 120-130 MR, green con sirens in CS have little trouble charming me... h8 those things!" (22 Apr 2002)
https://dbsanfte.github.io/eq-archiv...msg-108955.txt
This June 2002 post seems to indicate this override being removed mid-Luclin:
Quote:
Originally Posted by A Player on June 7 2002
"There used to be the same problem with Enchanter mobs,
they could Charm you with ease, even if they were *way*
green to you.
But that was mentioned as changed in one of the recent
flurry of patch messages -- now you get a normal
level/MR check against being Charmed.
And I can verify it. Last night I soloed the Sirens outside
of Sirens Grotto to clear them away before our raid
group came through, and although they tried to Charm
me a total of about 30 times, not one stuck (58 Paladin
with 91 MR). Hooray."
|
https://dbsanfte.github.io/eq-archiv...msg-121620.txt
Although I could not find a patch note mentioning this.
Al'Kabor logs do not show any enchanter NPC resist override. I do see some charms on players in AK logs but these are Dictate casts. (Silverwing, Lady Nevederia and level 60+ PoP NPCs cast Dictate)
Why did Sony add this? Because reaching 100 MR, which is very easy to hit, would make enchanter NPCs entirely unthreatening as all spells would be resisted without it. That is my guess anyway. (although enchanter NPCs do cast tash)
More Post September 4 2002 Issues
When Sony switched to the new resist system about six weeks before PoP launched, they failed to account for some things which resulted in peculiar behavior.
One of the most significant issues is that many of the CHA -4 lures were made much harder to resist, because before the patch all lures were all-or-nothing rolls and after the patch some lures became partial hittable. Not only that but the partial damage scale became 600, so these spells went from a resist cap of 75% with somewhere around 200 resist value, to resisting ~64% with 500 resist value. Luclin added bard songs which hit outside of the group so this wasn't quite as bad as it sounds, but resist rates still went way down for these spells after the revamp.
The CHA -4 issue was very preventable however because Sony added a no_partial_save field to spells. They just neglected to flip this on for CHA -4 spells for whatever reason. They did apply this flag to the CHA -6 dragon breaths. Notably Freezing Breath (Ventani's AoE) and Stream of Acid (Nexona's AoE) were flagged to not partial save. If this flag were not flipped on then these AoEs would have resisted at a rate of 15% if the player had 192 resist value at level 60 whereas the rate is 45% using classic rules.
CHA -4 AoEs were more prevalent than CHA -6 AoEs in NToV. This made NToV harder than before the patch although by then it was fairly easy content due to AAs, Luclin gear and spells, spoilers and players having experienced it before. Vyemm's AoE is a CHA -4 AoE however.
"Is a lot tougher and longer fight. With nearly 300 mr i only resisted about 3 of his 50+ aoes, the fight usually takes us a couple mins, today it took 30! But nonetheless it was pretty fun fighting for 30 mins against nev"
"This fight is utter BS now. Some of the people in our guild had 350+ MR and were resisting 1 out of 30"
"We did Vyemm and Lady N and with at least 180+ in MR at all times I never resisted the Gflux and I only once resisted the "spin the bottle thingy""
"Fight was three times as long as before for us tonight. With 177 MR after she tash'd me I resisted about 20 to 25% of her AOEs. Now she also seemed to be AOEing more as well. Lord Koi was also longer, but not nearly the same upgrade that Lady N got"
Thread title: Lady N with new resists
https://web.archive.org/web/20021120...cID=1237.topic
Stun Breath is Phara Dar's AoE and Zlandicar's proc spell. This spell became much easier to resist after the revamp because Sony either neglected to put a -150 resist adjust on it or decided not to. Unfortunately not doing that made those fights trivial and the fights would have still been much easier even if they had applied a -150 adjust on the spell because bards can raise MR by over 200 and the lure caps were removed.
Immolating Breath is Talendor's AoE and this had its no_partial_save field set true because it would have used the partial scale otherwise. This spell also unfortunately did not get a -150 adjust to it when it should have, so resisting it became very easy and it trivialized the encounter.
Ceticious Cloud is the weirdest spell of all of these. This is Severilous' and Wuoshi's AoE. The spell data for this spell had a direct damage component but the spell didn't actually do any damage in-game. It also had no recast timer/cooldown on it. Sev and Wuoshi in fact spammed this spell every 3-5 seconds and this was the case even on Al'Kabor. Sony seems to have "fixed" this spell's incorrect recast timer by making it not do damage. Why they did not just give it a recast time I don't know. Anyway this spell used to be a dragon breath lure before the revamp but all spells with damage in the first slot were made partialable and that includes this spell, so it needed the no-partial flag set to true which Sony did set, but they neglected to apply a -150 adjust on the spell. The result is that it became very easy to resist and these two dragons became loot pinatas.
The revamp removed the lure caps, and so players could be made immune from all the CHA -6 dragon breaths using bards. This made the encounters using these spells much easier if bards were available, as Sony had put a low cap (45% or not much higher) on these AoEs because the spells are what made the encounters difficult. It also made resists much less effective per-unit, so if your raid didn't have a bard then you had a harder time. The revamp made bards much more valuable for raids.
Memory Blur Resists
The resist function checks for SE 63 (mem blur) and applies the charisma modifier on these spells. This appears to be vestigial.
Blur effects are most likely handled outside of the resist function but I'll include what I know about them for completeness.
Developer posts and patch notes mention blurs:
May 18, 1999
"High level creatures get a better save vs. Memory Blur (Enchanter) and Atone (Cleric) spells"
https://web.archive.org/web/20001022...i/news.pl?id=7
May 21, 1999 GZ comments on Enchanter spell changes pt.1
"Only monsters with level 40 or higher gain resistance to Blur."
http://web.archive.org/web/200105010...ws/99-5-16.htm
May 24th, 1999
"The saving throw against Memory Blur and Atone was reduced (harder to save against) from the last patch."
https://groups.yahoo.com/api/v1/grou...t/messages/335
EverQuest Third Anniversary State of the Game part 2 (March 2002)
"Enchanters' Memory Blur line, and aspects of Rapture and Glamour of Kintaz have been improved."
http://boards.station.sony.com/ubb/e...ML/000521.html
March 19, 2002
"- Memory Blur, Mind Wipe, Blanket of Forgetfulness, Memory Flux, Glamour of Kintaz and Rapture have been given a greater chance of clearing the ‘hate list’"
A player I've corresponded with named Ravenwing who played on Al'Kabor and TAKP is extremely knowledgeable about the game and is somebody I and the community consider to be of high character. His input has been invaluable to my emu work and I consider any claims he makes to be highly credible. He posted this on the TAKP board:
Quote:
Originally Posted by Ravenwing
I personally confirmed that any memory blur - even a 1% blur, such as that on the level 4 mez - against a low-level NPC had a 100% success rate on Al'Kabor.
In 2010, long after the Al'Kabor era, a player named Crystilla (the webmaster at eqclerics.org, who seems to have been part of some kind of volunteer player committee with access to the developers) posted the following about Atone:
Quote:
Originally Posted by Crystilla
I talked to a Dev and it does. For those curious here's the formula and summary.
Description: Clears the affected NPC's entire hatelist.
Base effect 1 is a % bonus for the effect to occur.
The player is given a percentage bonus based on level, lower is better. < 17, bonus = 100, > 53 bonus = 25. Del's note: Referring to mob level with this part
Another bonus is applied for charisma. (Cha - 150) / 10 up to a maximum of 15.
|
Now, granted, this post is about eight years post Al'Kabor. However, I think the formula is accurate. First, because it matches my subjective impression of memory blur's success; second, because Pithy (one of Al'Kabor's smarter and more empirically-minded players) did a bit of testing of Blanket of Forgetfulness and Memory Flux on AK and found their success rates on high-level NPCs to be around 85% and 70% with ~300 cha, respectively; and third, because the mob levels specified in the formula suggest that it was written early on in the game's development.
|
Presumably that's how mem blur's chance to succeed worked from March 19 2002 onward.
I found an older comment regarding mem blur success rates from 2000:
Quote:
Originally Posted by Caster's Realm Poster, Sept 2000
I disagree that the entire memory blur line is useless at the higher levels, and find your figure of 12.5% success rate to be much, much lower than what I've experienced with the "real" blur spell, blanket of forgetfulness. Even against NPCs level 57 to 62, this spell is effective 30-50% of the time. That is extremely powerful, when you consider what the spell is really doing.
|
He claims that Blanket of Forgetfulness worked about half as often back then. The Luclin patch probably removed the level 40+ increased resistance. OG Sony liked to make things not work at higher levels, so them having made mem blur not work well at higher levels is unsurprising.
I also ran some mem blur tests on Live servers in 2021. The results did not quite match Crystilla's claim:
Memory Blur cast on a level 69 target by a level 65 enchanter with 277 charisma 401 times resulted in about 146 blurs, or 36%.
Memory Blur cast on a level 62 target by a level 65 enchanter with 277 charisma 478 times resulted in about 203 blurs, or 42.5%.
Expected chance would be 10%+25%+12% = 47%
Memory Flux cast on a level 62 target by a level 65 enchanter with 277 charisma 335 times resulted in about 199 blurs, or 59.5%.
Expected chance would be 30%+25%+12% = 67%
Blanket of Forgetfulness cast on a level 62 target by a level 65 enchanter with 277 charisma 354 times resulted in about 254 blurs, or 72%.
Expected chance would be 20%+25%+12%=57%+24.5% = 81.5%
So either Sony/Darkpaw nerfed mem blur somewhat after 2010, or Crystilla's claim is incorrect, or we're misinterpreting Crystilla's explanation or my tests were faulty. The tests were done by standing on top of a KOS NPC while an enchanter was blurring from outside of aggro range using an automated push button tool. NPCs will do their "I'm aggro" emote every 5.5 minutes and also when they aggro again after a blur, so I had to manually account for these extra emotes which is why I say "about" X blurs.
Miscellaneous
Spells That Didn't Roll
Several spell IDs were hardcoded to bypass the resist roll. These spells are: Dictate, Rapture, Enslave Death. Lifetaps (spell type 13) also bypassed the resist roll. All of these spells however could be blocked by the 151 limit, so all of these spells could still resist on mobs with high enough MR, and they all resisted exactly the same way. If the target had 150 or less effective resist then the spells landed, for full. (assuming they met level requirements)
CHA -5 spells (mostly wizard lures) and Poison Breath were also found in the "IsUnresistableSpell()" (I'm guessing at the name) function that Dictate, Rapture and Enslave Death were in. It appears as though Sony had early plans to make wizard lures (and Takanon's AoE) resist like lifetaps but instead decided to create the separate roll just for lures which I outlined earlier. This "IsUnresistableSpell()" function would never be called for lures because lures branch away from the main resist logic early in the function and do their own rolls.
In regards to lifetaps, apparently they were made resistable at some point in late 1999. I found old comments mentioning that time frame. The Feb 1999 client doesn't have an exception for lifetap spells in it but the March 2000 client does. Players mentioned something about Sony not liking newbie necros taking down Sand Giants. They also mention the "six level" rule applying.
I need to make this clear: lifetaps were not unresistable in 2000 and later. In fact they resisted on so many raid NPCs that necros made it a primary issue for their class. See this thread: https://web.archive.org/web/20010124.../045291-5.html
I did find an old comment which mentioned lifetaps not landing on Aaryonar until malo was applied, and that agrees with the logic and data I outlined.
Cazic Touch is also hardcoded right at the top of the resist algorithm to always land.
Mage Pet Level Advantage
At the top of the resist function there is a +5 to the caster level if something equals 27. We think that this may be a body type check but we're not entirely sure. If it is then this would give summoned mage pets a +5 to their caster level for their spells.
Resistant Discipline
The November 29 2000 client is the first client with the resist discipline in it. In that client the discipline always granted +3 to all resists, which is terrible. Sony buffed the discipline on December 6 2000. I checked the April 4 2001 client and can confirm that client scales from 3 to 10, with +4 starting at level 35.
|
 |
|
 |
 |
|
 |

06-05-2025, 04:16 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Noticeably Absent Things
The Six Level Limit (1.25x Limit)
The "six level limit" was a 1.25x level limit with a minimum of 5 hittable levels above the caster. At 6 or more levels above the caster the targets became immune.
Quote:
Originally Posted by September 4 2002 Patch Note
- The hard level limit involving players casting on NPCs has been
removed. This used to be referred to in EQ folklore as the “Six Level
Limit” (It was actually 1.25 times the caster’s level, but more people
likely thought about it the other way.) This means that in the vast
majority of cases, there is at least a small chance that a person will
be able to connect a spell with an NPC, even if they are out of that
NPC’s traditional level range.
|
The September 4 2002 patch partially removed this limit for players. This limit was not found in the resist function, so it had to be outside of it. The function it was in was probably not in the client although we could have easily missed it.
I can confirm that this limit had five hittable levels because I have a 2001 log with a level 14 player (Shortok) casting nukes on level 20 Oasis NPCs and they fully resisted. The chance of a full resist in those circumstances would be 4% if the limit didn't exist. The log had three full resists and the player was a cleric so these stood out a lot as he had virtually zero full resists on other NPCs.
This same cleric at level 26 got four of four full resists on a level 33 goblin NPC in Highkeep. 26 x 1.25 = 32.5, so this confirms that the multiplier is 1.25x and not 1.3x. I found an old Sony developer quote from early 2002 which stated 1.3x but he was incorrect.
I have logs of myself powerleveling characters on Al'Kabor, which is post-patch. I found that I was able to land spells beyond this limit however the resist rate went up dramatically. The patch didn't remove this limit but instead turned it into a soft resist barrier at lower levels. I had a level 19 cleric casting AoEs on a large Solusek's Eye goblin pull which had the king in it, which is a level 30 mob. At level 19 I cast ten spells on the king, and 3 landed for minimal damage. During this single pull my level went from 19 to 24. My resists on the king were significantly elevated until level 24 when they began hitting for full damage. I estimate that Sony may have been adding around 150 points to the king's effective resist value.
At higher levels this 1.25x hard limit remained in place. This is easily found in logs because this limit applied to pets. Beastlord pets cast a lot of spells, and I have logs of level 60 beastlords doing raid content. These pets are level 47. I found that the pets could not hit NPCs which were above level 58. In Kael for example the pets could hit Kallis Stormcaller easily but they could not hit the Statue of Rallos Zek (level 59) or the temple guardians (level 60) or anything else above 58.
The mage epic pet is level 49 unfocused. I found logs of an epic pet in Plane of Valor and the pet could hit the level 61 mobs but none of the level 62 golems. Since direct damage spells use the 600 scale after the patch, the fact that literally zero pet spells landed indicates that the 1.25x limit at upper levels is a hard limit.
In my 2014 resists thread, I mentioned hard level limits existing at the time on Live servers which prevented all spells from landing. These limits appear to have been the same since the September 4 2002 patch, which seems to have put in place additional limits beyond the 1.25x limit. (either then or PoP launch) Note that, as of now, these limits have been raised on Live servers as of 2025. On Live servers right now a level 50 may land full damage spells on a level 70. But these were the limits in 2014:
Level 50 may hit up to 62 (+12) (this prevents level 50s from hitting level 63 PoSky bosses btw)
Level 55 may hit up to 68 (+13)
Level 60 may hit up to 70 (+10)
Level 61-63 may hit up to 75 (+14 to +12)
Level 64 may hit up to 80 (+16)
Level 65 may hit up to 86 (+21)
So it appears that Sony limited 60 and under players to level 70 targets, and after that limited targets to level 75, 80 and 86. How do I know this was the case since late 2002? Because the pet summons for level 61-65 players could not land spells on mobs above level 70. I.e. your level 65 necro specter pet (a level 60 pet) which casts SpectreLifetap could never land this spell on a level 71, ever. I found zero cases of this in old logs, and this spell has a -200 adjust on it. (nor did I find a beastlord pet spell landing) In fact I find pet spells landing on the fake Vallon spawns (which are level 70) in PoTimeB but not the real one. It's very obvious.
To summarize: - The "six level limit" means only three levels of red are hittable until the caster reaches level 24 when the 1.25x limit allows four levels of red.
- The 1.25x limit still existed after Sept 4 2002, it just turned into a soft limit for lower level players only.
- There are other level limits in place restricting who may damage level 71+ mobs with spells. (the attacker must be level 61 or higher)
- Pets are subjected to these limits.
- These limits apply to lifetaps.
I did find an old comment that raises an issue however. This level 53 wizard claims to have landed a lure nuke on the Velious revamp Cazic Thule, which is a level 70 NPC:
Quote:
Originally Posted by Graffe Poster
Was just on a CT raid and the higher level wizards reported ice based droughts hitting. They stopped using lures completely. At level 53 nothing but my lure of frost would hit though. Oh well, good reason to lvl now. Dots were also landing for my necro friend at lvl 57.
|
https://web.archive.org/web/20011225...picID=45.topic
This suggests that lures were exempted from the level limits but that seems weird to me. Maybe they were. A single comment isn't much to go by.
Rain Spell Handicaps
Rain spells in classic had a 20% resist penalty on them when casted on NPCs. This resist penalty is not found in the resist function, oddly.
Quote:
Originally Posted by February 21 2001 Patch Note
— Rain spells no longer count pets against total number of entities they can damage (all rain spells)
|
Quote:
Originally Posted by August 26 2003 Patch Note
- AE Rain Spells were resisted 100% of the time when an NPC was above level 20 and had less than 10% of its hit points left. This will no longer happen.
|
Quote:
Originally Posted by April 12 2005 Patch Note
- NPC’s innate 20% resistance against rain spells has been removed.*
|
I attempted to find the earliest comments remarking on rain resists being unusually high. I found several threads from the late Kunark era mentioning this, with the earliest comment being August 2nd 2000. I can't say when the 20% rain resists went in exactly. There aren't a lot of comments from before that time.
Incidentally I found several comments about rains being low aggro, so it's possible Sony's rain beacon logic was intentionally or unintentionally resulting in near zero aggro. This does make some sense considering the elevated resist rate and resists otherwise doing full hate on typical spells. Some players at the time theorized that the under 10% hitpoints immunity thing was to prevent corpses from poofing, but I don't know why Sony added that.
Belly Casters
"Belly caster" NPCs are NPCs that require the player to be inside of melee range in order to land spells on them, otherwise the spells will always fully resist. The check for this flag was not found in the resist function.
Some people may ask why this exists at all. As somebody who was actively raiding in 1999, I can give the likely explanation. Sony NPCs could get stuck on the zone geometry, and large NPCs could get stuck in doorways. Nagafen and Vox in particular used to often get suck in the doors. While these NPCs could still summon, this allowed players to avoid much of the melee damage. The summon timer is 11 seconds for NPCs under level 66 and players' pets would distract the dragons as well, preventing summons. This seems the most likely reason.
I suspect that belly caster checks were done by race like stun immunity is.
Green NPC Caster Partial Resist Modifier
Prathun's pseudocode includes this:
Code:
If caster is an NPC...
If target is at least 20 levels higher than caster, add (level difference * 1.5) to partial resist modifier.
That was not found in the decompiles, but the neighboring modifiers were. Mid-Luclin seems the most likely date for this:
Quote:
Originally Posted by June 12 2002 Patch Note
- NPCs that are much lower level than their target have had their
potential casting damage reduced using a similar mechanic to the one
that player characters’ level differences use
|
Files, Links and Notes
I uploaded most of the examined decompiled functions in the form of text files to my Google Drive, which can be found here: https://drive.google.com/drive/folde...PV?usp=sharing
I have a large text file full of research notes (mostly old player comments) which is here: https://drive.google.com/file/d/19SQ...usp=drive_link
My large NPC statistics spreadsheet which includes parsed NPC resist values from Live servers from 2014 onward: https://docs.google.com/spreadsheets...usp=drive_link
My saved webpage archive which includes old Sony developer posts and informative player comments:
https://drive.google.com/drive/folde...usp=drive_link
Kicnlag found the tipa16384 archive on Github which was a very rare and valuable source of pre Sept 4 2002 logs:
https://github.com/tipa16384/harcour.../tree/main/DSR
An Internet Archive link which has the old client binaries we decompiled with Ghidra:
https://web.archive.org/web/*/everqu...wnload/patch/*
Internet Archive also has EverQuest ISOs which are easy to find.
My simulation scripts can be found here:
https://drive.google.com/drive/folde...usp=drive_link
Using Ghidra isn't difficult and I would encourage anybody with technical skill who is interested enough to download that and use it. Interpreting the output is the hard part. We certainly did not come close to finding everything interesting.
I also encourage people to download and store local copies of my work, because I know better than most that the Internet is not forever and links often break. Some Internet Archive pages have been lost already. Redundancy is the only way to preserve digital information.
|
 |
|
 |
 |
|
 |

06-05-2025, 04:20 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
I will paste my translations of several resist function decompiles here for convenience. These and more are found on my public Google Drive.
Code:
// CheckResistSpell() from February 16 1999 client. (original launch CD) 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
// param_1 = spell_data
// param_2 = caster level
// param_3 = no_resist_floor
// param_4 = caster_object
// param_5 = spell ID
// this + 0x38 = level
// uVar5 = roll
// uVar6 = final_resist
// sVar7 = level_diff
// 0x94 = IsNPC()
// iVar10 = start_resist
uint __thiscall
FUN_00416fd0(int this, int spell_data, byte caster_level, int no_resist_floor, undefined4 caster_object, int spell_id)
{
char cVar1;
byte level;
int iVar3;
int iVar4;
uint roll;
uint final_resist;
short level_diff;
int iVar8;
int iVar9;
int start_resist;
int local_4;
level = caster_level;
start_resist = 0;
level_diff = this->GetLevel() - caster_level;
temp_resist = 0;
local_4 = -1;
if (spell_data == nullptr) {
return 100;
}
iVar8 = -1;
if (*(int *)(this + 0xa0) == nullptr) {
return 100;
}
do {
if (((iVar8 != -1) ||
((iVar3 = FUN_00416f50(spell_data,start_resist), iVar9 = start_resist, iVar3 == 0 && (iVar9 = iVar8, false)
))) && (iVar4 = FUN_00416f50(spell_data,start_resist), iVar9 = iVar8, iVar3 = start_resist, iVar4 != 0))
break;
start_resist = start_resist + 1; // compiler optimizer reusing variables; this is basically 'i' for this loop
iVar8 = iVar9;
iVar3 = local_4; // -1
} while (start_resist < 4); // only 4 spell slots max in this era
local_4 = iVar3;
if (iVar9 == -1) {
return 100; // no valid spell slots
}
switch(*(undefined *)(spell_data + 0x1f9)) { // spell resist type
case SPELL_RESIST_TYPE_1_MAGIC:
start_resist = GetMR(level); // attacker's level; needed to modify resist by level difference
break;
case SPELL_RESIST_TYPE_2_FIRE:
start_resist = GetFR(level);
break;
case SPELL_RESIST_TYPE_3_COLD:
start_resist = GetCR(level);
break;
case SPELL_RESIST_TYPE_4_POISON:
start_resist = GetPR(level);
break;
case SPELL_RESIST_TYPE_5_DISEASE:
start_resist = GetDR(level);
break;
default:
goto LAB_LAND_SPELL;
}
iVar8 = *(int *)(this + 0xa0); // used in IsNPC() lines that I removed for readability
if (!IsNPC()) {
if (start_resist > 99) {
start_resist = 99; // players capped at 99
}
}
else if (start_resist > 150) {
return 100; // guaranteed full resist if resist > 150
}
temp_resist = temp_resist + start_resist;
if (true) {
switch(*(undefined *)(iVar9 + 0x1fa + spell_data)) { // first spell slot effect
case SE_0_CURRENTHP:
case SE_79_CURRENT_HP_ONCE:
if (IsNPC() && level_diff > 0 && this->GetLevel() > 16) {
temp_resist = temp_resist + level_diff * 2;
}
break;
case SE_22_CHARM:
ModResistCHA(&temp_resist,caster_object);
case SE_3_MOVEMENTSPEED:
case SE_20_BLIND:
case SE_23_FEAR:
if (no_resist_floor == 0 && temp_resist < 5) {
temp_resist = 5;
}
break;
case SE_31_MEZ:
case SE_34_CONFUSE:
case SE_63_BLUR:
ModResistCHA(&temp_resist,caster_object);
break;
case SE_44_LYCANTHROPY:
temp_resist = temp_resist + 30;
}
}
// NPC target resist floors
if (IsNPC()) {
if (level_diff > -11 && this->GetLevel() > 14 && temp_resist < 10) {
temp_resist = 10;
}
if ((level_diff < -20) || this->GetLevel() < 15)) {
if (temp_resist < 2) {
temp_resist = 2;
}
}
else if (temp_resist < 5) {
temp_resist = 5;
}
}
else if (temp_resist < 1) {
temp_resist = 1; // player targets floor at 1
}
if (temp_resist > 100) {
temp_resist = 100; // resist capped at 100 here for everything
}
final_resist = temp_resist;
roll = RandNum(0,100); // roll a number from 0 to 99 inclusive
if (roll < 99 && roll <= final_resist)
{
if (local_4 != -1 && // -1 means there is only one valid spell effect
(cVar1 = *(char *)(iVar9 + 0x1fa + spell_data), cVar1 == SE_0_CURRENTHP || cVar1 == SE_79_CURRENT_HP_ONCE))
{
return 100; // if there are multiple effects in the spell, and the first effect is damage, then full resist
}
cVar1 = *(char *)(iVar9 + 0x1fa + spell_data); // get first spell effect
if (cVar1 == SE_0_CURRENTHP || cVar1 == SE_79_CURRENT_HP_ONCE) // only pure DD spells may partial hit
{
// calculate possible partial damage; still may full resist. Only single effect spells with SE 0 or 79 end up here
final_resist = (final_resist * 100 + roll * -100) / final_resist;
if (IsNPC())
{
if (level_diff > 0 && this->GetLevel() > 16) {
final_resist = final_resist + 5; // yellows and reds resist more
}
level = this->GetLevel(); // target's level
if (level > 29) {
final_resist = (final_resist - 25) + level; // level 30+ mobs resist more; the higher the level the more it resists
}
if (level < 15) {
final_resist = final_resist - 5; // newbie mobs resist less
}
}
if (spell_id == SPELL_88_HARM_TOUCH) {
final_resist = final_resist - 15;
}
if (final_resist < 0) {
final_resist = 0;
}
if (final_resist < 101) {
return final_resist;
}
}
return 100; // 100 means zero damage/full resist
}
LAB_LAND_SPELL:
return 0; // 0 means full damage
}
|
 |
|
 |
 |
|
 |

06-05-2025, 04:23 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
Code:
// CheckResistSpell() from March 21 2000 client. (a month before Kunark launch) 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 __fastcall
FUN_0041a9a0(int param_1_00, undefined4 param_2_00, int spell_data, byte caster_level, int param_3, int caster, int spell_id)
{
char cVar1;
byte bVar2;
undefined4 in_EAX;
int iVar3;
int iVar4;
uint roll_result;
uint charm_level_limit;
byte bVarLevel;
int iVar8;
int iVar9;
int iVar10;
short sVar11;
uint temp_resist;
int local_10;
int local_c;
int level_diff;
int local_4;
level_diff = CONCAT22((short)((uint)in_EAX >> 0x10),GetLevel()) -
(CONCAT31((int3)((uint)param_2_00 >> 8),caster_level) & 0xffff00ff);
iVar10 = 0;
iVar8 = -1;
temp_resist = 0;
local_c = 0xffffffff;
local_4 = -1;
if (spell_id == SPELL_982_CAZIC_TOUCH) {
return 0;
}
if (spell_data == 0) {
return 100;
}
if (*(int *)(param_1_00 + 0xa0) == 0) {
return 100;
}
local_10 = param_1_00;
bVarLevel = caster_level;
if ((caster != 0) && (*(int *)(caster + 0xf0) == 27)) { // probably body type. 27 is elementals
caster_level = caster_level + 5;
bVarLevel = caster_level;
}
do {
if (((iVar8 != -1) ||
((iVar3 = FUN_0041a8d0(spell_data,iVar10), iVar9 = iVar10, iVar3 == 0 && (iVar9 = iVar8, false)
))) && (iVar4 = FUN_0041a8d0(spell_data,iVar10), iVar9 = iVar8, iVar3 = iVar10, iVar4 != 0))
break;
iVar10 = iVar10 + 1;
iVar8 = iVar9;
iVar3 = local_4;
} while (iVar10 < 4);
local_4 = iVar3;
if (iVar9 == -1) {
return 100;
}
local_c = iVar9;
switch(*(undefined *)(spell_data + 0x1f9)) {
case SPELL_RESIST_TYPE_1_MAGIC:
iVar8 = FUN_004155e0(caster_level);
if (((bVarLevel > 34) && (caster != 0)) && caster->GetClass() == WIZARD)) {
if (*(char *)(spell_data + 0x1fa) == SPELL_EFFECT_TYPE_0_HP) goto LAB_0041ab3e;
if (*(char *)(spell_data + 0x1fa) == SPELL_EFFECT_TYPE_79_INSTANTHP) {
iVar8 = iVar8 + -10;
}
}
break;
case SPELL_RESIST_TYPE_2_FIRE:
iVar8 = FUN_004156d0(caster_level);
if (((bVarLevel > 34) && (caster != 0)) && caster->GetClass() == WIZARD)) {
iVar8 = iVar8 + -10;
}
break;
case SPELL_RESIST_TYPE_3_COLD:
iVar8 = FUN_004157d0(caster_level);
if (((bVarLevel > 34) && (caster != 0)) && caster->GetClass() == WIZARD)) {
LAB_0041ab3e:
iVar8 = iVar8 + -10;
}
break;
case SPELL_RESIST_TYPE_4_POISON:
iVar8 = FUN_00415960(caster_level);
break;
case SPELL_RESIST_TYPE_5_DISEASE:
iVar8 = FUN_004158a0(caster_level);
break;
default:
goto LAB_LAND_SPELL;
}
cVar1 = *(char *)(iVar9 + 0x1fa + spell_data);
if (cVar1 == SE_23_FEAR && IsNPC() && GetLevel() > 52)
{
return 100;
}
if (cVar1 == SE_22_CHARM && caster != 0 && !caster->IsNPC())
{
bVar2 = *(byte *)(spell_data + 0x204 + (uint)*(byte *)(caster + 0x95)); // level the caster gets this charm spell
switch(GetRace()) {
case RACE_44_FREEPORT_GUARD:
case RACE_67_HIGHPASS_CITIZEN:
case RACE_71_QEYNOS_CITIZEN:
case RACE_77_NERIAK_CITIZEN:
case RACE_78_ERUDITE_CITIZEN:
case RACE_81_RIVERVALE_CITIZEN:
case RACE_88_CLOCKWORK_GNOME:
case RACE_90_HALAS_CITIIZEN:
case RACE_92_GROB_CITIZEN:
case RACE_93_OGGOK_CITIZEN:
case RACE_94_KALADIM_CITIZEN:
case RACE_106_FELGUARD:
case RACE_112_FAYGUARD:
case RACE_139_IKSAR_CITIZEN:
goto LAB_RESIST_SPELL; // guards are immune to charm casted by players
case 0x2d: // 45
case 0x2e:
case 0x2f:
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
case 0x40:
case 0x41:
case 0x42:
case 0x44:
case 0x45:
case 0x46:
case 0x48:
case 0x49:
case 0x4a:
case 0x4b:
case 0x4c:
case 0x4f:
case 0x50:
case 0x52:
case 0x53:
case 0x54:
case 0x55:
case 0x56:
case 0x57:
case 0x59:
case 0x5b:
case 0x5f:
case 0x60:
case 0x61:
case 0x62:
case 99:
case 100:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77:
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f:
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a: // race 138 Yeti; highest Kunark race is 167 though?
bVarLevel = caster_level; // this doesn't appear to do anything. maybe vestigial
}
if (bVar2 > 50) {
bVar2 = caster->GetLevel() / 2;
}
charm_level_limit = bVar2;
if (caster->GetClass() != ENCHANTER) {
bVar2 = *(byte *)(spell_data + 0x212); // level enchanter gets the spell
if (bVar2 != 0 && bVar2 < 51) {
charm_level_limit = bVar2;
}
}
charm_level_limit = (charm_level_limit * 15) / 10;
if (charm_level_limit < 24) {
charm_level_limit = 24;
}
if (spell_id < 183) {
if (spell_id == SPELL_182_BEGUILE) {
LAB_0041acde:
charm_level_limit = 37;
}
else if (spell_id == SPELL_141_BEGUILE_ANIMALS) {
charm_level_limit = 43;
}
else if (spell_id == SPELL_142_ALLURE_OF_THE_WILD) {
charm_level_limit = 51;
}
}
else if (spell_id < 197) {
if (spell_id == SPELL_196_DOMINATE_UNDEAD) {
charm_level_limit = 32;
}
else {
iVar10 = spell_id - 183;
LAB_0041acb4:
if (iVar10 == 0) { // Cajoling Whispers is spell ID 183; Beguile Undead is 197
charm_level_limit = 46;
}
else if (iVar10 == 1) { // Allure is spell ID 184; Cajole Undead is 198
charm_level_limit = 51;
}
}
}
else if (spell_id < 261) {
if (spell_id != SPELL_260_CHARM_ANIMALS) {
iVar10 = spell_id - 197;
goto LAB_0041acb4;
}
charm_level_limit = 33;
}
else if (spell_id < 1554) {
if (spell_id == SPELL_1553_CALL_OF_KARANA) {
switchD_0041acf9_caseD_658:
charm_level_limit = 51;
}
else if (spell_id == SPELL_725_SOLONS_SONG_OF_THE_SIRENS) goto LAB_0041acde;
}
else if (true) {
switch(spell_id) {
case SPELL_1556_TUNARES_REQUEST:
charm_level_limit = 35;
break;
case SPELL_1624_THRALL_OF_BONES:
case SPELL_1705_BOLTRANS_AGACERIE:
goto switchD_0041acf9_caseD_658;
case SPELL_1629_ENSLAVE_DEATH:
case SPELL_1707_DICTATE:
charm_level_limit = 52;
}
}
if (charm_level_limit > 51) {
charm_level_limit = 51;
}
iVar9 = local_c;
if (charm_level_limit < GetLevel()) {
if (*(int *)(caster + 0x74) != 0) { // presumably some kind of IsClient() subclass check
FUN_004ca2a0(*(int *)(caster + 0x74),s_Target_Too_High_level_for_your_c_005a8b14,0xd);
return 100;
}
return 100;
}
}
if (!IsNPC()) {
if (iVar8 > 99) {
iVar8 = 99;
}
}
else if (iVar8 > 150) {
return 100;
}
if (spell_id == SPELL_127_INVOKE_FEAR || spell_id == SPELL_59_PANIC_THE_DEAD || spell_id == SPELL_514_TERRORIZE_ANIMAL)
{
if (bVarLevel < GetLevel()) {
iVar10 = -5;
}
else {
iVar10 = -(uint)(caster_level >> 1);
}
iVar8 = iVar8 + iVar10;
if (iVar8 < 5) {
iVar8 = 5;
}
}
if (spell_id == SPELL_1527_TREPIDATION || spell_id == SPELL_1550_REPULSE_ANIMAL || spell_id == SPELL_1532_DREAD_OF_NIGHT)
{
if (bVarLevel < GetLevel()) {
iVar10 = -15;
}
else {
iVar10 = -5 - caster_level / 2;
}
iVar8 = iVar8 + iVar10;
if (iVar8 < 5) {
iVar8 = 5;
}
}
sVar11 = (short)iVar8;
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)
{
sVar11 = sVar11 - 10;
}
iVar8 = IsUnresistableSpell(spell_data,spell_id);
if (iVar8 == 0 && *(char *)(spell_data + 0x1fe) != 13) { // spell type 13 (lifetap) will be false here and land
temp_resist = temp_resist + (int)sVar11;
if (true) {
switch(*(undefined *)(iVar9 + 0x1fa + spell_data)) {
case SE_0_CURRENTHP:
case SE_79_CURRENT_HP_ONCE:
if (IsNPC() && level_diff > 0 && GetLevel() > 16) {
temp_resist = temp_resist + level_diff * 2;
}
break;
case SE_22_CHARM:
CharismaMod(&temp_resist,caster);
case SE_3_MOVEMENTSPEED:
case SE_20_BLIND:
case SE_23_FEAR:
if (param_3 == 0 && temp_resist < 5) {
temp_resist = 5;
}
break;
case SE_31_MEZ:
case SE_34_CONFUSE:
case SE_63_BLUR:
CharismaMod(&temp_resist,caster);
break;
case SE_44_LYCANTHROPY:
temp_resist = temp_resist + 0x1e;
}
}
iVar8 = local_10;
if (spell_id == SPELL_676_TASHAN || spell_id == SPELL_677_TASHANI || spell_id == SPELL_678_TASHANIA) {
temp_resist = temp_resist * 2 / 3;
}
// resist floors
if (IsNPC()) {
if (level_diff > -11 && GetLevel() > 14 && (temp_resist < 10)) {
temp_resist = 10;
}
if (level_diff < -20 || GetLevel() < 15) {
if (temp_resist < 2) {
temp_resist = 2;
}
}
else if (temp_resist < 5) {
temp_resist = 5;
}
}
else if (temp_resist < 1) {
temp_resist = 1;
}
if (temp_resist > 100) {
temp_resist = 100;
}
iVar10 = IsLullSpell(spell_id);
if (iVar10 != 0) {
if (spell_id == SPELL_728_KELINS_LUGUBRIOUS_LAMENT) {
temp_resist = temp_resist / 2;
if (temp_resist < 5) {
temp_resist = 5;
}
effective_resist = 5; // note: the decompile code reuses the charm_level_limit variable for this
} // but I renamed it starting from here for readability
else {
bVarLevel = GetLevel();
if (bVarLevel < 15) {
effective_resist = 10;
}
else if (bVarLevel < 25) {
effective_resist = 20;
}
else if (bVarLevel < 35) {
effective_resist = 33;
}
else if (bVarLevel < 40) {
effective_resist = 42;
}
else {
effective_resist = (-(uint)(bVarLevel < 50) & 0xffffffd2) + 100;
}
}
if (temp_resist < effective_resist) {
temp_resist = effective_resist;
}
}
if (caster != nullptr && caster->IsNPC()) &&
(caster->GetClass() == ENCHANTER || caster->GetClass() == CLASS_30_ENCHANTER_GM) &&
caster->GetLevel() > 24)
{
effective_resist = GetLevel();
if (effective_resist < temp_resist) {
temp_resist = effective_resist;
}
if (temp_resist > 40) {
temp_resist = 40;
}
}
effective_resist = temp_resist;
roll_result = Roll0(0,100);
if (roll_result < 99 && roll_result <= effective_resist)
{
if ((local_4 == -1 || // -1 means there is only one valid spell effect
(((cVar1 = *(char *)(iVar9 + 0x1fa + spell_data), cVar1 != SE_0_CURRENTHP && (cVar1 != SE_79_CURRENT_HP_ONCE)) ||
(*(byte *)(spell_data + 0x212) < 13)))) || (*(char *)(local_4 + 0x1fa + spell_data) == SE_27_CANCELMAGIC)
)
{
cVar1 = *(char *)(iVar9 + 0x1fa + spell_data); // first spell effect
if (cVar1 != SE_0_CURRENTHP && cVar1 != SE_79_CURRENT_HP_ONCE) {
return 100;
}
effective_resist = (effective_resist * 100 + roll_result * -100) / effective_resist;
if (IsNPC()) {
if (level_diff > 0 && GetLevel() > 16) {
effective_resist = effective_resist + 5;
}
bVarLevel = GetLevel();
if (bVarLevel > 29) {
effective_resist = (effective_resist - 25) + bVarLevel;
}
if (bVarLevel < 15) {
effective_resist = effective_resist - 5;
}
}
if (spell_id == SPELL_88_HARM_TOUCH) {
effective_resist = effective_resist - 45;
}
if (effective_resist < 0) {
effective_resist = 0;
}
if (effective_resist > 100) {
return 100;
}
return effective_resist;
}
LAB_RESIST_SPELL:
return 100;
}
}
LAB_LAND_SPELL:
return 0;
}
|
 |
|
 |
 |
|
 |

06-05-2025, 04:25 AM
|
Sarnak
|
|
Join Date: Aug 2014
Posts: 76
|
|
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;
}
|
 |
|
 |
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -4. The time now is 05:19 AM.
|
|
 |
|
 |
|
|
|
 |
|
 |
|
 |