Former RS researcher. Some of this stuff may be obsolete.
Research Findings[edit | edit source]
- Attack and Defence only affect the probability of missing
- Strength only affects maximum damage.
- In normal conditions (
Current Opponent HP>=
Max Hit) the probability of each positive damage amount is equal. That means if a monster has 30 HP left and the player's max hit is 18, the probability of hitting an 18 is equal to the probability of hitting a 1.
- There exist "hit zeros," where the player hits his opponent but does zero damage, and "miss zeros" where he fails to hit his opponent entirely. In magic combat a miss zero is represented with a splash. The probability of doing hit zeros is equal to the normal probability of each positive damage amount. So even with a 100% chance to hit, the player can still do zero damage.
Current Opponent HP<
Max Hit, the probability of hitting a damage amount equal to that HP level is higher than normal. For example, if the player's max hit is 15 and his opponent has 2 HP left, the chances of hitting a 2 is 14 times higher than the chances of hitting a 1. This effect causes the actual probability for each positive damage amount in a given combat session to be a linearly decreasing function of damage:
probability = chancetohit*(maxhit+1+hp-2*damage)/hp/(maxhit+1)
chancetohit= Player's probability of hitting his opponent, or chance to hit
maxhit= Player's maximum damage amount
hp= Opponent's average maximum HP
- Chance to Hit can be accurately computed after recording each damage amount of a very large number of hit attempts against a single type of opponent:
chancetohit = (1-relfreqzero)/(1-1/(maxhit+1))
relfreqzero= relative frequency of doing zero damage
So if a player with a max hit of 20 had a 25% chance of doing zero damage, his chance to hit was actually 78.75%, not 75%.
- Each monster has its own average HP regeneration rate. That means a monster that regenerates an average of 1 hitpoint every 30 seconds of being damaged may regenerate 1 hitpoint after 5 seconds of being damaged, but this is much rarer than doing so after 31 seconds. The average total amount of HP a monster regenerates is a linear function of the amount of time it is damaged.
- The delay in seconds between each of a weapon's attacks is equal to 6-0.6*Speed where “Speed” is the number of blue squares in that weapon's [http://www.runescape.com/kbase/viewarticle.ws?article_id=2010 speed bar]. If the attack mode is set to 'Rapid', the speed value increases by 1. For example, a rune crossbow set to 'Rapid' mode will fire every 6-0.6*(4+1) = 3.0 seconds. Published. The speed value for all elemental autocast spells is 5.
- Against demons, Silverlight increases each damage amount dealt by 60%, rounded down. Published.
- See Minimap#Scale and Transportation#On foot.
- See Attack range
- Each server cycle still appears to be 0.6 second as specified by the RSC-era "Runescape script command summary" found here.
- Running to a target square an odd number of squares away (path-wise) from the player's present square will cause the player to walk the last square. If the target square is an even number of squares away, the player will run the entire way. This is because a running player travels at a constant speed but can only travel across 2 squares in 1 server cycle and not 1 square in 0.5 server cycle.
- If a server cannot execute the pathfinding algorithm for an already-running player within one server cycle, the player will "backtrack" or "sidetrack" at least one square when the server completes its task in order for the player to strictly adhere to the new path.
- Party Pete counts down to an imminent party in server cycles and not in seconds.
Guidelines for Combat Data Gathering[edit | edit source]
- Enter the damage amount for each and every hit attempt in a text file like so:
9 8 0 1 0 2 1 99 16 0 3 2 99The "99" above is the "killmarker." A killmarker is any number greater than your max hit, like 99 or 999. It must be entered into the file after each and every kill.
- Do not use poison, offensive potions, special attacks, barrows set effects, bolt enchantments or anything that might lead to inconsistent data. Prayer, salve, etc. is fine as long as it's used for the entire duration of the data gathering session.
- If you start fighting an opponent that already had some HP taken off, stop recording immediately and erase all data entered for that particular opponent
- Erase any and all other data that might be bad
- Include the following details separately from your text file: opponent, base attack skill, weapon and other equipment used, attack mode used and prayer used.
MATLAB/Octave Analysis Code[edit | edit source]
This function, damage.m, is to be used with the above guidelines.
Caveats[edit | edit source]
- It is assumed you can easily kill your opponent(s) before they can regenerate any HP. You may use Monster Examine to help determine whether you can.
- It is assumed you can easily kill your opponent(s) before you need to eat or drink.
function damage(filename,speed) % filename = Name of data file, typed as quoted string % speed = Number of blue squares in weapon's speed bar (http://www.runescape.com/kbase/viewarticle.ws?article_id=2010) Add +1 for Rapid Mode % % Example: damage('data.txt',5) clc % Preliminary Analysis delay=6-0.6*speed; rawdata=load(filename); killmarker=max(rawdata); j=1; k=1; for i=1:length(rawdata) n=rawdata(i); if n==killmarker killlocation(j)=i; j=j+1; elseif n<killmarker data(k)=n; k=k+1; end end kills=length(killlocation); totaldam=sum(data); hitattempts=length(data); maxhit=max(data); killingdam=rawdata(killlocation-1); hpmat(:,5)=killlocation'; % HP Analysis disp('Columns: Damage Done per Opponent, Frequency, Relative Frequency (%), Min Combat Time (s), Mean Combat Time (s), Max Combat Time (s), Relative Frequency of 1-Damage Killing Hits (%)') start=1; stop=2; i=1; while start<length(rawdata) comtime=delay; while stop<killlocation(i) hp=sum(rawdata(start:stop)); stop=stop+1; comtime=comtime+delay; end start=stop+1; stop=stop+2; hpmat(i,1)=hp; hpmat(i,2)=comtime; i=i+1; end start=1; k=1; while start<length(rawdata) dam=rawdata(start); if dam==0 start=start+1; elseif dam>0 firstposhitloc(k)=start; start=killlocation(k)+1; k=k+1; end end hpmat(:,3)=delay*(killlocation'-firstposhitloc'-1); basehp=min(hpmat(:,1)); hpmat(:,4)=killingdam; hpanalysis=zeros(max(hpmat(:,1))-basehp+1,7); hpanalysis(:,1)=[basehp:max(hpmat(:,1))]'; i=1; for j=1:length(hpmat(:,1)) hp=hpmat(j,1); hpanalysis(hp-basehp+1,2)=hpanalysis(hp-basehp+1,2)+1; comtime=hpmat(j,2); commat(hp-basehp+1,i)=comtime; killingdam=hpmat(j,4); killmat(hp-basehp+1,i)=killingdam; i=i+1; end hpanalysis(:,3)=100*hpanalysis(:,2)/sum(hpanalysis(:,2)); for j=1:length(commat(:,1)) comrow=commat(j,:); killrow=killmat(j,:); i=1; ii=1; poskillrow=0; for k=1:length(comrow) n1=comrow(k); if n1>0 poscomrow(i)=n1; i=i+1; end n2=killrow(k); if n2>0 poskillrow(ii)=n2; ii=ii+1; end end hpanalysis(j,4)=min(poscomrow); hpanalysis(j,5)=mean(poscomrow); hpanalysis(j,6)=max(poscomrow); onesfreq=0; for iii=1:length(poskillrow) n3=poskillrow(iii); if n3==1 onesfreq=onesfreq+1; end end relonesfreq=100*onesfreq/length(poskillrow); hpanalysis(j,7)=relonesfreq; end hpanalysis hp=totaldam/kills; % Damage Analysis disp('Columns: Damage, Frequency, Relative Frequency (%), Predicted Relative Frequency (%), Absolute Difference (%)') damanalysis=zeros(maxhit+1,5); damanalysis(2:maxhit+1,1)=[1:maxhit]'; for k=1:hitattempts n=data(k); damanalysis(n+1,2)=damanalysis(n+1,2)+1; end damanalysis(:,3)=100*damanalysis(:,2)/hitattempts; chancehit=100*(1-damanalysis(1,3)/100)/(1-1/(maxhit+1)); chancemiss=100-chancehit; predfreq=chancehit*(maxhit+1+hp-2*[1:maxhit])/hp/(maxhit+1); normfreq=chancehit/(maxhit+1); idealfreq=100/(maxhit+1); damanalysis(1,4)=damanalysis(1,3); damanalysis(2:maxhit+1,4)=predfreq'; damanalysis(2:maxhit+1,5)=damanalysis(2:maxhit+1,3)-damanalysis(2:maxhit+1,4); damanalysis % Other Analysis and Plots avgcomtime=mean(hpmat(:,2)); combattime=sum(hpmat(:,2))/3600; avgdamtime=mean(hpmat(:,3)); regenrate=60*(hp-basehp)/(avgdamtime-delay/2); avgdam=mean(data); predavgdam=dot(predfreq,[1:maxhit])/100; idealavgdam=maxhit/2; normavgdam=chancehit*idealavgdam/100; avgdps=avgdam/delay; predavgdps=predavgdam/delay; idealavgdps=idealavgdam/delay; normavgdps=normavgdam/delay; dameff=100*avgdam/idealavgdam; preddameff=dot(predfreq,[1:maxhit])/idealavgdam; accuracy=100-100*abs(avgdam-predavgdam)/avgdam; fprintf('Hit Attempts = %.0f\n', hitattempts) fprintf('Opponents Killed = %.0f\n', kills) fprintf('Average Damage Done per Opponent = %f\n', hp) fprintf('Average Opponent HP Regeneration Rate = %f HP/Minute\n', regenrate) fprintf('Average Combat Time = %f Seconds\n', avgcomtime) fprintf('Total Combat Time = %f Hours\n\n', combattime) fprintf('Average DPS = %f\n', avgdps) fprintf('Predicted Average DPS = %f\n', predavgdps) fprintf('Semi-Ideal Average DPS (Infinite HP) = %f\n', normavgdps) fprintf('Ideal Average DPS (Infinite HP & Zero Defence) = %f\n\n', idealavgdps) fprintf('Damage Efficiency = %f\n', dameff) fprintf('Predicted Damage Efficiency = %f\n\n', preddameff) fprintf('Model Accuracy = %f\n\n', accuracy) fprintf('Chance to Miss = %f\n', chancemiss) fprintf('Chance to Hit = %f\n', chancehit) fprintf('Chance to Hit and Do Zero Damage = %f\n', normfreq) fprintf('Chance to Hit and Do Positive Damage = %f\n\n', 100-damanalysis(1,3)) % Uncomment line below to write HP data to file. Columns: HP, Combat Time, Damaged Time, Damage Amount of Last Hit, Kill Line # in Raw Data File %dlmwrite('hpdata.csv',hpmat) figure(1) x=0:maxhit; plot(x,damanalysis(:,3)','-ob') hold on plot(1:maxhit,polyval(polyfit(1:maxhit,damanalysis(2:(maxhit+1),3)',1),1:maxhit),'-b') plot(x,[damanalysis(1,3) predfreq],'-r') plot(x,[damanalysis(1,3) normfreq*ones(1,maxhit)],'-g') plot(x,idealfreq*ones(1,maxhit+1),'-c') title('Relative Frequency vs. Damage') xlabel('Damage') ylabel('Frequency (%)') grid on legend('Real','Real Linear Fit','Predicted','Semi-Ideal','Ideal') hold off figure(2) x=1:length(data); plot(x,data,'-b') hold on plot(x,avgdam*ones(1,length(data)),'-r') plot(x,predavgdam*ones(1,length(data)),'-m') plot(x,normavgdam*ones(1,length(data)),'-g') plot(x,idealavgdam*ones(1,length(data)),'-c') title('Damage vs. Hit Attempt') xlabel('Hit Attempt') ylabel('Damage') grid on legend('Raw','Average','Predicted Average','Semi-Ideal Average','Ideal Average') hold off figure(3) x1=linspace(min(hpmat(:,3))-delay/2,max(hpmat(:,3))); x2=linspace(min(hpmat(:,3)),max(hpmat(:,3))); y1=polyval(polyfit(hpmat(:,3)-delay/2,hpmat(:,1),3),x1)-basehp; y2=polyval(polyfit(hpmat(:,3),hpmat(:,1),3),x2)-basehp; plot(x2,y2,'-b') hold on plot(x1,y1,'-c') plot(x1,regenrate*x1/60,'-r') plot(x1,(hp-basehp)*ones(1,length(x1)),'-g') title('Average HP Regenerated vs. Damaged Time') xlabel('Damaged Time (seconds)') ylabel('Average HP Regenerated') grid on legend('Real Cubic Fit','Adjusted Cubic Fit','Predicted','Session Average',4) hold off figure(4) x=1:length(hpmat(:,1)); plot(x,hpmat(:,1),'-b') hold on plot(x,hp*ones(1,length(hpmat(:,1))),'-r') title('Opponent HP vs. Kill') xlabel('Kill') ylabel('HP') grid on legend('Raw','Average') hold off