state("iji") {}

startup
{
	// Optional splits on bosses
	// Krotera and Asha are instakilled in a normal run and Iosa 2 is unintersting, short and RNG based.
	settings.Add("Krotera", false);
	settings.Add("Asha", false);
	settings.Add("Proxima", true);
	settings.Add("Iosa 1", true);
	settings.Add("Iosa 2", false);
	settings.SetToolTip("Iosa 2", "After destroying Iosa's annihilator form");
	settings.Add("Tor", true);
}

init
{
	vars.globalsPtr = new MemoryWatcher<int>(new DeepPointer("iji.exe", 0x189720, 4));
	vars.torPtr = new MemoryWatcher<int>(new DeepPointer("iji.exe", 0x1AF2F4, 0x80, 0x390, 0, 0x10C, 4));
	vars.watchers = new MemoryWatcherList();
	vars.fightingTor = false;
	vars.initialized = false;

	vars.error = (Action<string>)((string info) => {
		MessageBox.Show(timer.Form,
			"Couldn't find " + info +
			"\nTry restarting the game.",
			"Iji Autosplitter script",
			MessageBoxButtons.OK,
			MessageBoxIcon.Error);
		vars.broken = true;
	});
	vars.broken = false;
}

update
{
	if (vars.broken) return false;

	bool globalsUpdated = vars.globalsPtr.Update(game);
	if (globalsUpdated || !vars.initialized) {
		if (vars.globalsPtr.Current == 0) { return false; } // Game loading
		bool found = false;
		IntPtr ptr = new IntPtr(vars.globalsPtr.Current);
		while (true) {
			int key = memory.ReadValue<int>(ptr);
			if (key < 100000 || key > 105000) break;
			if (key == 100058) {
				vars.inGame = new MemoryWatcher<double>(ptr + 0x10);
				found = true;
				break;
			}
			ptr = ptr + 0x28;
		}
		if (!found) { return false; } // Still loading
		vars.initialized = true;
	}

	if (!vars.initialized) return false;

	vars.inGame.Update(game);

	if (vars.inGame.Current == 1) {
		if (globalsUpdated || vars.watchers.Count == 0) {
			int remaining = 4;
			IntPtr sector = IntPtr.Zero,
				levelTime = IntPtr.Zero,
				totalTime = IntPtr.Zero,
				iosa2 = IntPtr.Zero;
			IntPtr ptr = new IntPtr(vars.globalsPtr.Current);
			while (true) {
				int key = memory.ReadValue<int>(ptr);
				if (key < 100000 || key > 105000) break;
				switch (key) {
					case 100159: sector =    ptr + 0x10; --remaining; break;
					case 100172: levelTime = ptr + 0x10; --remaining; break;
					case 100616: totalTime = ptr + 0x10; --remaining; break;
					case 100612: iosa2 =     ptr + 0x10; --remaining; break;
					default: break;
				}
				if (remaining == 0) break;
				ptr = ptr + 0x28;
			}
			if (remaining > 0) { vars.error("Sector Info memory locations."); return false; }

			MemoryWatcher<double> sectorWatcher    = new MemoryWatcher<double>(sector)    { Name = "Sector"     };
			MemoryWatcher<double> levelTimeWatcher = new MemoryWatcher<double>(levelTime) { Name = "Level Time" };
			MemoryWatcher<double> totalTimeWatcher = new MemoryWatcher<double>(totalTime) { Name = "Total Time" };
			MemoryWatcher<double> iosa2Watcher     = new MemoryWatcher<double>(iosa2)     { Name = "Iosa 2"     };

			if (vars.watchers.Count > 0) {
				sectorWatcher.Old = vars.watchers["Sector"].Old;
				sectorWatcher.Current = vars.watchers["Sector"].Current;
				levelTimeWatcher.Old = vars.watchers["Level Time"].Old;
				levelTimeWatcher.Current = vars.watchers["Level Time"].Current;
				totalTimeWatcher.Old = vars.watchers["Total Time"].Old;
				totalTimeWatcher.Current = vars.watchers["Total Time"].Current;
				iosa2Watcher.Old = vars.watchers["Iosa 2"].Old;
				iosa2Watcher.Current = vars.watchers["Iosa 2"].Current;
			}

			vars.watchers.Clear();
			vars.watchers.Add(sectorWatcher);
			vars.watchers.Add(levelTimeWatcher);
			vars.watchers.Add(totalTimeWatcher);
			vars.watchers.Add(iosa2Watcher);
		}
		vars.watchers.UpdateAll(game);

		if (vars.watchers["Sector"].Current == 15) {
			//print("Checking tor");
			if (vars.torPtr.Update(game)) {
				int remaining = 2;
				bool tor = false;
				IntPtr currentHp = IntPtr.Zero, ptr = new IntPtr(vars.torPtr.Current);
				while (true) {
					int key = memory.ReadValue<int>(ptr);
					if (key < 100000 || key > 105000) break;
					switch (key) {
						case 100005:
							currentHp = ptr + 0x10; --remaining; break;
						case 100316:
							// identify tor with security stat
							tor = memory.ReadValue<double>(ptr + 0x10) == 250;
							--remaining; break;
						default: break;
					}
					if (remaining == 0) break;
					ptr = ptr + 0x28;
				}
				if (tor) {
					if (remaining > 0) { vars.error("Tor Hp memory location."); return false; }
					vars.fightingTor = true;
					vars.torHp = new MemoryWatcher<double>(currentHp);
				}
			}
			if (vars.fightingTor)
				vars.torHp.Update(game);
		}
	}
}

start
{
	return vars.inGame.Old == 0 && vars.inGame.Current == 1;
}

reset
{
	return vars.inGame.Old == 1 && vars.inGame.Current == 0;
}

split
{
	if (vars.inGame.Current == 1) {
		double oldSector = vars.watchers["Sector"].Old, currentSector = vars.watchers["Sector"].Current;
		if (oldSector != currentSector) return (
			// standard progression
			(oldSector + 1 == currentSector && currentSector != 1) ||
			// sector 9 to sector X
			(oldSector == 9 && currentSector == 0) ||

			// there's a delay between boss room -> sector -> next sector
			// these are some (probably obsolete) fail-safes
			(oldSector == 11 && currentSector == 4) ||
			(oldSector == 12 && currentSector == 6) ||
			(oldSector == 13 && currentSector == 8) ||
			(oldSector == 14 && currentSector == 0) ||

			// entering boss rooms
			(settings["Krotera"] && currentSector == 11) ||
			(settings["Asha"]    && currentSector == 12) ||
			(settings["Proxima"] && currentSector == 13) ||
			(settings["Iosa 1"]  && currentSector == 14) ||
			(settings["Tor"]     && currentSector == 15));
		else if
			// destroying Iosa's annihilator form
			(settings["Iosa 2"] && currentSector == 14 &&
			 vars.watchers["Iosa 2"].Old == 0 && vars.watchers["Iosa 2"].current == 1) return true;
		else if
			// killing Tor - final split
			(vars.fightingTor && vars.torHp.Current <= 0) { vars.fightingTor = false; return true; }
	}
}

isLoading
{
	return true;
}

gameTime
{
	if (vars.inGame.Current == 1)
		return TimeSpan.FromSeconds(vars.watchers["Level Time"].Current + vars.watchers["Total Time"].Current);
	else return TimeSpan.FromSeconds(0);
}