// Poppy Playtime Ch4 Autosplitter and Load Remover
// Supports Load Remover IGT (Soon.TM)
// Splits for campaigns can be obtained from:
// Pointers, Item/Checkpoint Autosplits & Load Removal by TheDementedSalad
// Pause/Inventory Removal by Lox

state("ch4_pro-Win64-Shipping"){}
state("ch4_pro-WinGDK-Shipping"){}

startup
{
	Assembly.Load(File.ReadAllBytes("Components/asl-help")).CreateInstance("Basic");
	vars.Helper.Settings.CreateFromXml("Components/PPCH4.Settings.xml");
	vars.Helper.AlertLoadless();
	
	vars.completedSplits = new HashSet<string>();
	vars.Inventory = new Dictionary<ulong, int>();
	vars.splitstoComplete = new HashSet<string>();
}

init
{
	switch (modules.First().ModuleMemorySize)
	{
		case (139485184):
			version = "SteamRelease";
			break;
	}

	IntPtr gWorld = vars.Helper.ScanRel(3, "48 8B 05 ???????? 48 3B C? 48 0F 44 C? 48 89 05 ???????? E8");
	IntPtr gEngine = vars.Helper.ScanRel(3, "48 89 05 ???????? 48 85 c9 74 ?? e8 ???????? 48 8d 4d");
	IntPtr fNames = vars.Helper.ScanRel(3, "48 8d 0d ???????? e8 ???????? c6 05 ?????????? 0f 10 07");
	IntPtr gSyncLoad = vars.Helper.ScanRel(21, "33 C0 0F 57 C0 F2 0F 11 05");
	
	if (gWorld == IntPtr.Zero || gEngine == IntPtr.Zero)
	{
		const string Msg = "Not all required addresses could be found by scanning.";
		throw new Exception(Msg);
	}
	
	vars.Helper["isLoading"] = vars.Helper.Make<bool>(gSyncLoad);

	vars.Helper["TransitionType"] = vars.Helper.Make<byte>(gEngine, 0xB93);
	
	vars.Helper["GameLoading"] = vars.Helper.Make<byte>(gEngine, 0x1080, 0x108, 0x38, 0x50, 0x298, 0x2C8, 0x80);
	vars.Helper["GameLoading"].FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull;
		
	vars.Helper["Level"] = vars.Helper.MakeString(gEngine, 0xB98, 0x14);

	vars.Helper["CheckpointID"] = vars.Helper.Make<ulong>(gEngine, 0xA58, 0x78, 0x830, 0x158, 0x34);
	
	vars.Helper["localPlayer"] = vars.Helper.Make<ulong>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x18);
	vars.Helper["localPlayer"].FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull;
	
	vars.Helper["AcknowledgedPawn"] = vars.Helper.Make<ulong>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0x18);
	vars.Helper["AcknowledgedPawn"].FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull;
	
	//gEngine.GameInstance.LocalPlayers[0].PlayerController.Character.CapsuleMovement.RelativeLocationYXZ
	vars.Helper["X"] = vars.Helper.Make<double>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0x330, 0x128);
	vars.Helper["Y"] = vars.Helper.Make<double>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0x330, 0x138);
	vars.Helper["Z"] = vars.Helper.Make<double>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0x330, 0x130);
	
	vars.Helper["Inventory"] = vars.Helper.Make<IntPtr>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0xAF8, 0x1B0 + 0x0);
	
	vars.Helper["ItemCount"] = vars.Helper.Make<uint>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0xAF8, 0x1B8);
	
	vars.Helper["FinalItem"] = vars.Helper.Make<ulong>(gEngine, 0x1080, 0x38, 0x0, 0x30, 0x340, 0xAF8, 0x1B0, 0x3E8, 0x78);

	
	vars.FNameToString = (Func<ulong, string>)(fName =>
	{
		var nameIdx  = (fName & 0x000000000000FFFF) >> 0x00;
		var chunkIdx = (fName & 0x00000000FFFF0000) >> 0x10;
		var number   = (fName & 0xFFFFFFFF00000000) >> 0x20;

		IntPtr chunk = vars.Helper.Read<IntPtr>(fNames + 0x10 + (int)chunkIdx * 0x8);
		IntPtr entry = chunk + (int)nameIdx * sizeof(short);

		int length = vars.Helper.Read<short>(entry) >> 6;
		string name = vars.Helper.ReadString(length, ReadStringType.UTF8, entry + sizeof(short));

		return number == 0 ? name : name + "_" + number;
	});
	
	vars.FNameToShortString = (Func<ulong, string>)(fName =>
	{
		string name = vars.FNameToString(fName);

		int dot = name.LastIndexOf('.');
		int slash = name.LastIndexOf('/');

		return name.Substring(Math.Max(dot, slash) + 1);
	});
	
	vars.FNameToShortString2 = (Func<ulong, string>)(fName =>
	{
		string name = vars.FNameToString(fName);

		int under = name.LastIndexOf('_');

		return name.Substring(0, under + 1);
	});

	vars.FNameToShortString3 = (Func<ulong, string>)(fName =>
	{
		string name = vars.FNameToString(fName);

		int check = name.IndexOf('.');

		return name.Substring(check + 1);
	});
}

update
{
	//Uncomment debug information in the event of an update.
	//print(modules.First().ModuleMemorySize.ToString());
	
	vars.Helper.Update();
	vars.Helper.MapPointers();
	
	if(timer.CurrentPhase == TimerPhase.NotRunning)
	{
		vars.completedSplits.Clear();
	}

	//print(current.ItemCount.ToString());
	
	//print(vars.FNameToShortString3(current.FinalItem));
}

onStart
{
	vars.Inventory.Clear();
}

start
{
	return current.Y < 8544f && current.Y != old.Y && current.Level == "pro/Maps/00_Persistent/00_Persistent";
}

split
{
	const string ItemFormat = "[{0}] {1} ({2})";
	string setting = "";
	
	if(vars.FNameToShortString2(current.AcknowledgedPawn) == "BP_PPPlayerCharacter_C_"){ 
		for (int i = 0; i < current.ItemCount; i++)
		{

			ulong item = vars.Helper.Read<ulong>(current.Inventory + 0x10 + (i * 0x18), 0x78);
			byte used = vars.Helper.Read<byte>(current.Inventory + 0x10 + (i * 0x18), 0x68);

			int oldUsed;
			if (vars.Inventory.TryGetValue(item, out oldUsed))
			{
				if (oldUsed < used){
					setting = string.Format(ItemFormat, '+', vars.FNameToShortString(item), used);
					vars.splitstoComplete.Add(setting);
				}
			}
			else
			{
				setting = string.Format(ItemFormat, '+', vars.FNameToShortString(item), '!');
				vars.splitstoComplete.Add(setting);
			}
			
			vars.Inventory[item] = used;
			
			// Debug. Comment out before release.
			//if (!string.IsNullOrEmpty(setting))
			//vars.Log(setting);
		
			if (settings.ContainsKey(setting) && settings[setting] && vars.completedSplits.Add(setting) && vars.splitstoComplete.Contains(setting)){
				return true;
				vars.splitstoComplete.Clear();
			}
		}
	}
	
	if(vars.FNameToShortString3(current.CheckpointID) != vars.FNameToShortString3(old.CheckpointID)){
		setting = vars.FNameToShortString3(current.CheckpointID);
	}
	
	if(vars.FNameToShortString3(current.CheckpointID) == "Checkpoint.TheLabsEntrance.2" && current.ItemCount == old.ItemCount - 1){
		return true;
	}
	
	// Debug. Comment out before release.
	if (!string.IsNullOrEmpty(setting))
	vars.Log(setting);

	if (settings.ContainsKey(setting) && settings[setting] && vars.completedSplits.Add(setting)){
		return true;
	}
}

isLoading
{
	return current.isLoading || current.TransitionType == 1 || vars.FNameToShortString2(current.localPlayer) != "BP_PPPlayerController_C_" || current.GameLoading == 1;
}

reset
{}