View Single Post
  #2  
Old 08-04-2021, 02:45 AM
Torven
Sarnak
 
Join Date: Aug 2014
Posts: 52
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