Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Development

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

 
 
Thread Tools Display Modes
Prev Previous Post   Next Post Next
  #2  
Old 08-04-2021, 02:45 AM
Torven
Sarnak
 
Join Date: Aug 2014
Posts: 76
Default

I use lua scripts to parse logs or run simulations. I will paste them here.

This is my charm simulator:
Code:
local RESIST_VALUE = 50;
local CASTS = 150000;
local CASTER_LEVEL = 65;
local TARGET_LEVEL = 62;
local CHARM_DURATION_TICKS = 76;
--local CHARM_DURATION_TICKS = 180;
local CHARISMA = 280;
local USE_BOW_CURVE = false;	-- PvP resist curve is bow shaped
local USE_FLOOR = true;
local FLOOR_VALUE = 5;
local MIN_ROLL = 1;				-- use 1 for Live, 0 for AK
local MAX_ROLL = 200;
local CharmBreakCheckChance = 50;
local CharismaEffectiveness = 8;
local TDBonusPct = 0;
--local TDBonusPct = 35;						-- TD = 15%, 25%, 35% says raidloot.com

math.randomseed(os.time());

local tickResistChecks, tickResistFails = 0, 0;

function charmResist(charmTick, resistValue, targetLevel, casterLevel, charisma)

	local hitStatus = false;
	if ( charmTick ) then
		casterLevel = casterLevel + 4;
	end
	local levelDiff = targetLevel - casterLevel;
	local tempLevelDiff = levelDiff;
	
	if ( targetLevel >= 67 ) then
		tempLevelDiff = 66 - casterLevel;
		if ( tempLevelDiff < 0 ) then
			tempLevelDiff = 0;
		end
	end
	
	if ( tempLevelDiff < -9 ) then
		tempLevelDiff = -9;
	end
	
	local levelMod = math.floor(tempLevelDiff * tempLevelDiff / 2);
	
	if ( tempLevelDiff < 0 ) then
		levelMod = -levelMod;
	end
	
	local effectiveResistValue = resistValue + levelMod;
	local bonus = 0;

	if ( not charmTick ) then
	
		if ( CHARISMA >= 75 ) then

			bonus = math.floor((CHARISMA - 75) / CharismaEffectiveness);
			if ( bonus > 25 ) then
				bonus = 25;
			end
			effectiveResistValue = effectiveResistValue - bonus;
		end
		
	else
		tickResistChecks = tickResistChecks + 1;
	end


	if ( not USE_FLOOR and charmTick and math.random(20) == 1 ) then -- 5% chance to fail
		hitStatus = false;
	else
		local erv = effectiveResistValue;
		if ( USE_BOW_CURVE and effectiveResistValue < 200 ) then
			erv = -0.07868992 + 1.53452*effectiveResistValue - 0.002708188*(effectiveResistValue*effectiveResistValue);
		end
		
		if ( charmTick and USE_FLOOR) then
			
			if ( erv < FLOOR_VALUE ) then
				erv = FLOOR_VALUE;
			end			
		end
		
		if ( math.random(MIN_ROLL, MAX_ROLL) > erv ) then
			hitStatus = true;
			
		else
			hitStatus = false;
		end
	end
	if ( charmTick and not hitStatus ) then
		tickResistFails = tickResistFails + 1;
	end
	return hitStatus, effectiveResistValue, levelMod;
end

local hitStatus, effectiveResistValue, levelMod, tickHitStatus, tickEffectiveResistValue = 0, 0, 0, 0, 0;
local fullResists, avgDuration, duration, lands, longestCharm, maxCharms = 0, 0, 0, 0, 0, 0;
local durationsStr = "";
local ticks, tickFails1, tdChecks, tdSuccesses = 0, 0, 0, 0;
local tickTable = { [0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, };


fullResists, lands, avgDuration = 0, 0, 0;

for i = 1, CASTS do

	hitStatus, effectiveResistValue, levelMod = charmResist(false, RESIST_VALUE, TARGET_LEVEL, CASTER_LEVEL, CHARISMA);
	
	if ( hitStatus ) then
	
		duration = 0;
	
		for j = 1, CHARM_DURATION_TICKS do
		
			ticks = ticks + 1;
			
			-- Mob::PassCharismaCheck() called by spell_effects.cpp does this before calling ResistSpell()
			if ( math.random(1, 100) > CharmBreakCheckChance ) then
				tickHitStatus = true; -- charm holds
			else
				tickHitStatus = false;
				tickFails1 = tickFails1 + 1;
			end

			if ( not tickHitStatus ) then
				tickHitStatus, tickEffectiveResistValue = charmResist(true, RESIST_VALUE, TARGET_LEVEL, CASTER_LEVEL, CHARISMA);
			end
			
			if ( not tickHitStatus and TDBonusPct > 0 ) then
				tdChecks = tdChecks + 1;
				if ( math.random(1, 100) <= TDBonusPct ) then
					tickHitStatus = true;
					tdSuccesses = tdSuccesses + 1;
				end
			end

			if ( tickHitStatus ) then
				duration = duration + 1;
			else
				if ( duration < 6 ) then
					tickTable[duration] = tickTable[duration] + 1;
				end
				break;
			end
			
			if ( j == CHARM_DURATION_TICKS ) then
				maxCharms = maxCharms + 1;
			end
		end
		
		if ( duration > longestCharm ) then
			longestCharm = duration;
		end
		avgDuration = avgDuration + duration;
		lands = lands + 1;
		
		--print(duration);
		--durationsStr = durationsStr..duration..", "
	else
		fullResists = fullResists + 1;
	end
end

avgDuration = avgDuration / lands;

print("Simulating "..CASTS.." casts at "..RESIST_VALUE.." resist value; caster level "..CASTER_LEVEL.."; CHA: "..CHARISMA..
";  target level "..TARGET_LEVEL..";  Roll: "..MIN_ROLL.."-"..MAX_ROLL..";  CharmBreakCheckChance = "..CharmBreakCheckChance..
";  Max Charm Duration: "..CHARM_DURATION_TICKS..";  TD%: "..TDBonusPct);
print("Initial Effective resist value: "..effectiveResistValue..";  tick effective value: "..tickEffectiveResistValue..
";  Floor: "..FLOOR_VALUE.."  UseFloor?  Bow curve?:", USE_FLOOR, USE_BOW_CURVE);
print(string.format("full resists: %i (%0.2f%%); lands: %i (%0.2f%%); avg duration ticks: %0.1f (%0.2f%%); max duration charms: %i (%0.2f%%)",
fullResists, (fullResists/CASTS*100), lands, (lands/CASTS*100), avgDuration, avgDuration/CHARM_DURATION_TICKS*100, maxCharms, maxCharms/CASTS*100));
--print(durationsStr);
print("longest charm: "..longestCharm.." ticks");
print(string.format("base tick fail%%: %0.3f%%   TD success%%: %0.3f%%  tick resist fail%%: %0.3f%%", 
(tickFails1/ticks*100), (tdSuccesses/tdChecks*100), (tickResistFails/tickResistChecks*100)));
for i = 0, 5 do print(string.format("%i tick charms: %i (%0.2f%%)", i, tickTable[i], tickTable[i]/lands*100)); end

This is my charm log parser:

Code:
local INPUT_DIR = "I:\\Google Drive\\Classic EverQuest Preservation\\EQLive 2014 Sourced Data\\Logs\\Resist Mechanics Logs\\Charms\\";
--local INPUT_DIR = "I:\\Parse\\";

local INPUT_FILENAME = "eqlog_Torria_test - lvl65 ench CHA15 TD0 vs crystalline golem CoD.txt";
local AK_LOG = false;
local SPELL_NAME = "Command of Druzzil";
local IS_DRUID_SPELL = false;
local SPELL_DURATION = 75; -- duration in ticks  (PoP charms = 75 at 65)
--local TASH_SPELL = "Tashina"; -- nil this if not using a tash spell


function parseTime(line)
	local offset = line:find("%[") - 1;
	return tonumber(line:sub(offset + 10, offset + 11)) * 86400 + tonumber(line:sub(offset + 13, offset + 14)) * 3600 +
tonumber(line:sub(offset + 16, offset + 17)) * 60 + tonumber(line:sub(offset + 19, offset + 20));
end

local charmLandTime, charmEndTime, charmDuration, charmMin, charmSec, charmTicks, castTime, prevLandTime, prevEndTime, lastEndTime = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
local charms, avgTicks, maxDuration, minDuration, maxCharms, resists, lands, breaks, ignored, interrupts, invisBreaks = 0, 0, 0, 9999, 0, 0, 0, 0, 0, 0, 0;
local charmActive, tashActive = false, false;
local castingSpell;
local charmList = {};
local s, ts;

local landText = " has been charmed";
local breakText = "Your "..SPELL_NAME.." spell has worn off of ";
local resistText = " resisted your "..SPELL_NAME.."!";
local tashOffText = "Your "..(TASH_SPELL or "nil").." spell has worn off of ";
if ( AK_LOG ) then
	if ( IS_DRUID_SPELL ) then
		landText = " blinks.";
	else
		landText = "You begin casting "..SPELL_NAME;
	end
	breakText = "Your charm spell has worn off";
	resistText = "Your target resisted the "..SPELL_NAME.." spell.";
end

for line in io.lines(INPUT_DIR..INPUT_FILENAME) do

	if ( AK_LOG and line:find("You begin casting ", 28, true) ) then
	
		_, _, castingSpell = line:find("^You begin casting (.+)%.", 28);
		castTime = parseTime(line);
		
		if ( not IS_DRUID_SPELL and castingSpell == SPELL_NAME ) then
			prevLandTime = charmLandTime;
			prevEndTime = charmEndTime;
			
			charmLandTime = castTime;
			charmEndTime = 0;
			lands = lands + 1;
			if ( AK_LOG and charmActive ) then
				lands = lands - 1;
			end
			charmActive = true;
		end
	
	elseif ( (not AK_LOG or IS_DRUID_SPELL) and line:find(landText, 28, true) and (not TASH_SPELL or tashActive) ) then
		charmLandTime = parseTime(line);
		charmEndTime = 0;
		lands = lands + 1;
		charmActive = true;
	
	elseif ( charmActive and line:find(breakText, 28, true) ) then
		charmEndTime = parseTime(line);
		lastEndTime = charmEndTime;
		breaks = breaks + 1;
		charmActive = false;
		
		if ( charmLandTime > 0 and charmEndTime > 0 ) then
			
			charmDuration = charmEndTime - charmLandTime;
			
			if ( charmDuration > ((SPELL_DURATION+3) * 6) or charmDuration < 0 ) then
				ignored = ignored + 1;
			elseif ( TASH_SPELL and not tashActive ) then
				ignored = ignored + 1;			
			else
				charms = charms + 1;
				avgTicks = avgTicks + charmDuration;
				charmLandTime = 0;
				charmEndTime = 0;
				charmMin = math.floor(charmDuration / 60);
				charmSec = charmDuration % 60;
				if ( charmSec < 10 ) then
					charmSec = "0"..charmSec;
				end
				charmTicks = math.floor(charmDuration / 6);
				if ( charmTicks > maxDuration ) then
					maxDuration = charmTicks;
				end
				if ( minDuration > charmTicks ) then
					minDuration = charmTicks;
				end
				table.insert(charmList, charmTicks);
			end		
		end	
		
		if ( charmDuration > 0 ) then
			--print(line.."\t"..charmMin..":"..charmSec.."\t"..charmTicks.." ticks\n");
			charmDuration = 0;
		end
		
	elseif ( AK_LOG and not IS_DRUID_SPELL and castingSpell == SPELL_NAME and line:find("Your spell is interrupted", 28, true) ) then
		if ( parseTime(line) - castTime < 5 ) then
		
			charmLandTime = prevLandTime;
			charmEndTime = charmEndTime;
			interrupts = interrupts + 1;

			--print("Possible charm cast interrupt:");
			--print(line);
		end
		
	elseif ( line:find(resistText, 28, true) ) then
		resists = resists + 1;
		
	elseif ( TASH_SPELL and line:find(" glances nervously about.", 28, true) ) then
		tashActive = true;
		
	elseif ( TASH_SPELL and line:find(tashOffText, 28, true) ) then
		tashActive = false;
		
	elseif ( (line:find("You vanish.", 28, true) or line:find("Your body fades away.", 28, true) or line:find("You are no longer hidden.", 28, true))
		and lastEndTime == parseTime(line)
	) then
		invisBreaks = invisBreaks + 1;
		charmList[#charmList] = -charmList[#charmList];
	end

end
	
if ( charms > 0 ) then
	avgTicks = avgTicks / charms / 6;
	
	s = "durations: ";
	
	for i, tick in ipairs(charmList) do
		if ( tick < 0 ) then
			ts = "("..tostring(-tick)..")";
		else
			ts = tostring(tick)
		end
		s = s..ts..", ";
		if ( tick == maxDuration or tick == (maxDuration - 1) ) then
			maxCharms = maxCharms + 1;
		end
	end
	print(INPUT_FILENAME);
	print(s);
	
	print(string.format(SPELL_NAME.." casts: %i;  Lands: %i (%0.2f%%);  Resists: %i (%0.2f%%);  Breaks: %i", 
	resists+lands, lands, lands/(lands+resists)*100, resists, resists/(lands+resists)*100, breaks));
	
	if ( ignored > 0 or interrupts > 0 or invisBreaks > 0 ) then
		print(string.format("Ignored: %i;  Interrupts: %i;  Invis Breaks: %i", ignored, interrupts, invisBreaks));
	end
	
	print(string.format("min duration: %i ticks;  max duration: %i ticks;  avg duration: %0.1f ticks;  est max charms: %i (%0.2f%%)", 
	minDuration, maxDuration, avgTicks, maxCharms, maxCharms/(breaks-ignored)*100));
	
	local x;
	for j = 0, 5 do
		x = 0;
		for i, tick in ipairs(charmList) do
			if ( tick == j ) then
				x = x + 1;
			end
		end
		print(string.format("%i tick charms: %i (%0.2f%%)", j, x, x/lands*100));
	end
else
	print("no charms found");
end

This is my root simulator:

Code:
local RESIST_VALUE = 50;
local CASTS = 100000;
local CASTER_LEVEL = 65;
local TARGET_LEVEL = 65;
local ROOT_DURATION_TICKS = 30;
local USE_FLOOR = true;

local RootBreakCheckChance = 75;
local RootMinResist = 5;

math.randomseed(os.time());

function RootResist(rootTick, resistValue, targetLevel, casterLevel)

	local hitStatus = false;
	
	if ( rootTick ) then
		casterLevel = casterLevel + 4;
	end
	
	local levelDiff = targetLevel - casterLevel;
	local tempLevelDiff = levelDiff;
	
	if ( targetLevel >= 67 ) then
		tempLevelDiff = 66 - casterLevel;
		if ( tempLevelDiff < 0 ) then
			tempLevelDiff = 0;
		end
	end
	
	if ( tempLevelDiff < -9 ) then
		tempLevelDiff = -9;
	end
	
	local levelMod = math.floor(tempLevelDiff * tempLevelDiff / 2);
	
	if ( tempLevelDiff < 0 ) then
		levelMod = -levelMod;
	end
	
	local effectiveResistValue = resistValue + levelMod;

	if ( rootTick ) then
		
		if ( USE_FLOOR ) then
			if ( effectiveResistValue < RootMinResist ) then
				effectiveResistValue = RootMinResist;
			end
		else
			if ( math.random(20) == 1 ) then
				return false, effectiveResistValue, levelMod;
			end
		end
	end

	if ( math.random(1, 200) > effectiveResistValue ) then
		hitStatus = true;
	else
		hitStatus = false;
	end
	return hitStatus, effectiveResistValue, levelMod;
end

local hitStatus, effectiveResistValue, levelMod, tickHitStatus, tickEffectiveResistValue = 0, 0, 0, 0, 0;
local fullResists, avgDuration, duration, lands, longestRoot, maxRoots = 0, 0, 0, 0, 0, 0;
local durationsStr = "";


fullResists, lands, avgDuration = 0, 0, 0;

for i = 1, CASTS do

	hitStatus, effectiveResistValue, levelMod = RootResist(false, RESIST_VALUE, TARGET_LEVEL, CASTER_LEVEL);
	
	if ( hitStatus ) then

		if ( math.random(100) > 50 ) then
			duration = 1;
		else
			duration = 0;
		end
	
		for j = 1, ROOT_DURATION_TICKS do
		
			tickHitStatus, tickEffectiveResistValue = RootResist(true, RESIST_VALUE, TARGET_LEVEL, CASTER_LEVEL);
			
			if ( math.random(0, 99) > RootBreakCheckChance ) then
				tickHitStatus = true;
			end
			
			if ( tickHitStatus ) then
				duration = duration + 1;
			else
				break;
			end
			
			if ( j == ROOT_DURATION_TICKS ) then
				maxRoots = maxRoots + 1;
			end
		end
		
		if ( duration > longestRoot ) then
			longestRoot = duration;
		end
		avgDuration = avgDuration + duration;
		lands = lands + 1;
		
		--print(duration);
		--durationsStr = durationsStr..duration..", "
	else
		fullResists = fullResists + 1;
	end
end

avgDuration = string.format("%.2f", avgDuration / lands);

print("Simulating "..CASTS.." casts at "..RESIST_VALUE.." resist value; caster level "..CASTER_LEVEL.." target level "..TARGET_LEVEL..
"; MinResist == "..RootMinResist..";  Use floor:", USE_FLOOR);
print("Initial Effective resist value: "..effectiveResistValue..";  tick effective value: "..tickEffectiveResistValue..
";  Break Check Chance: "..RootBreakCheckChance);
print("full resists: "..fullResists.." ("..(fullResists/CASTS*100).."%); lands: "..lands.." ("..(lands/CASTS*100)..
"%); avg duration ticks: "..avgDuration.." ("..math.floor(avgDuration/(ROOT_DURATION_TICKS+1)*100)..
"%); max duration roots: "..maxRoots.." ("..math.floor(maxRoots/lands*100).."%)");
--	print(durationsStr);
print("longest root: "..longestRoot.." ticks");

This is my root log parser:

Code:
local INPUT_DIR = "I:\\Google Drive\\Classic EverQuest Preservation\\EQLive 2014 Sourced Data\\Logs\\Resist Mechanics Logs\\Roots\\";

local INPUT_FILENAME = "eqlog_Torria_test - lvl65 ench Paralyzing Earth vs a unicorn.txt";

function parseTime(line)
	local offset = line:find("%[") - 1;
	return tonumber(line:sub(offset + 10, offset + 11)) * 86400 + tonumber(line:sub(offset + 13, offset + 14)) * 3600 + 
tonumber(line:sub(offset + 16, offset + 17)) * 60 + tonumber(line:sub(offset + 19, offset + 20));
end


local rootLandTime, rootEndTime, rootDuration, rootMin, rootsec, rootTicks, maxRoots, resists, clips = 0, 0, 0, 0, 0, 0, 0, 0, 0;
local roots, avgTime, avgTicks, maxDurationTicks, maxDurationSecs, minDurationTicks, minDurationSecs = 0, 0, 0, 0, 0, 0, 9999;
local rootTicksTbl = {};
local rootDurations = {};
local s, now;

for line in io.lines(INPUT_DIR..INPUT_FILENAME) do

	if ( line:find(" adheres to the ground.", 28, true) ) then
		now = parseTime(line);
		if ( rootLandTime > 0 and (now - rootLandTime) < 200 ) then
			clips = clips + 1;
		end
		rootLandTime = now;
		rootEndTime = 0;
		
	elseif ( line:find("spell has worn off of ", 28, true) ) then
		rootEndTime = parseTime(line);
		
	elseif ( line:find("Your target resisted the ", 28, true) or line:find(" resisted your ", 28, true) ) then
		resists = resists + 1;
	end
	
	if ( rootLandTime > 0 and rootEndTime > 0 ) then
		rootDuration = rootEndTime - rootLandTime;
		roots = roots + 1;
		avgTime = avgTime + rootDuration;
		rootLandTime = 0;
		rootEndTime = 0;
		rootMin = math.floor(rootDuration / 60);
		rootsec = rootDuration % 60;
		if ( rootsec < 10 ) then
			rootsec = "0"..rootsec;
		end
		rootTicks = math.floor(rootDuration / 6);
		if ( rootTicks > maxDurationTicks ) then
			maxDurationTicks = rootTicks;
		end
		if ( minDurationTicks > rootTicks ) then
			minDurationTicks = rootTicks;
		end
		if ( rootDuration > maxDurationSecs ) then
			maxDurationSecs = rootDuration;
		end
		if ( minDurationSecs > rootDuration ) then
			minDurationSecs = rootDuration;
		end
		table.insert(rootTicksTbl, rootTicks);
		table.insert(rootDurations, rootDuration);
	end
end
	
if ( roots > 0 ) then
	print(INPUT_FILENAME);
	avgTicks = avgTime / roots / 6;
	avgTicks = math.floor(avgTicks * 10) / 10;
	s = "Tick durations: ";
	for i, tick in ipairs(rootTicksTbl) do
		s = s..tick..", ";
		
		if ( tick == maxDurationTicks or tick == (maxDurationTicks - 1) ) then
			maxRoots = maxRoots + 1;
		end
	end
	--print(s);
	print(string.format("Landed roots: %i;  max duration: %i ticks;  avg duration: %0.1f ticks (%0.2f%%);  max roots: %i (%0.2f%%)", 
	roots, maxDurationTicks, avgTicks, avgTicks/maxDurationTicks*100, maxRoots, maxRoots/roots*100));
	if ( resists > 0 ) then
		print(string.format("Resists: %i (%0.2f%%)", resists, resists/(resists+roots)*100));
	end
	if ( clips > 0 ) then
		print("Clips: "..clips);
	end
	
	--[[
	avgTime = avgTime / roots;
	avgTime = math.floor(avgTime * 10) / 10;
	s = "Second durations: ";
	for i, tick in ipairs(rootDurations) do
		s = s..tick..", ";
	end
	print(s);
	print(string.format("min duration: %is;  max duration: %i s;  avg duration: %i s (%0.2f%%)", 
		minDurationSecs, maxDurationSecs, avgTime, avgTime/maxDurationSecs*100));
	]]
end
Reply With Quote
 


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 07:53 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3