// Halo PC (2003) Autosplitter
// Supports Custom Edition with base campaign maps
// Game must be updated to 1.0.10

// FEATUES
/* 
    - Basic start, split and reset for fullgame and individual level speedruns
    - BSP (loading... done) splitting
    - Custom IL splits (WIP)
    - Start timer on any level mode (useful for Hunter% type meme runs)
    - Death counter
 */

state ("halo") {}
state ("haloce") {}

init {
    vars.watchers_h1 = new MemoryWatcherList();
    vars.watchers_h1xy = new MemoryWatcherList();
    vars.watchers_h1fade = new MemoryWatcherList();
    vars.watchers_a50 = new MemoryWatcherList();
    vars.watchers_b30 = new MemoryWatcherList();
    vars.watchers_c40 = new MemoryWatcherList();

    if (modules.First().ToString() == "halo.exe") {
        version = "Retail";

        vars.H1_map_globals = 0x319738;
        vars.H1_map = 0x2A8154;
        vars.H1_cinflags = 0x3FFFD678;
        vars.H1_coords = 0x2AC5BC;
        vars.H1_fade = 0x3FF15814;
        vars.H1_hsthread = 0x47A470;

        vars.watchers_h1.Add(new MemoryWatcher<uint>(new DeepPointer(0x2F1D8C)) { Name = "tickcounter" });
        vars.watchers_h1.Add(new MemoryWatcher<byte>(new DeepPointer(0x29E8D8)) { Name = "bspstate" });
        vars.watchers_h1.Add(new MemoryWatcher<byte>(new DeepPointer(0x3FC00126)) { Name = "difficulty" });

        vars.watchers_h1.Add(new StringWatcher(new DeepPointer(vars.H1_map + 0x20), 32) { Name = "levelname" });
        vars.watchers_h1.Add(new StringWatcher(new DeepPointer(vars.H1_map + 0x40), 32) { Name = "buildversion" });

        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_map_globals)) { Name = "mapreset" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_map_globals + 0x1)) { Name = "gamewon" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_cinflags + 0x1)) { Name = "cinematic" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_cinflags + 0x2)) { Name = "cutsceneskip" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_map_globals + 0x17)) { Name = "deathflag" });

        vars.watchers_h1xy.Add(new MemoryWatcher<float>(new DeepPointer(vars.H1_coords)) { Name = "xpos" });
        vars.watchers_h1xy.Add(new MemoryWatcher<float>(new DeepPointer(vars.H1_coords + 0x4)) { Name = "ypos" });
        vars.watchers_h1xy.Add(new MemoryWatcher<float>(new DeepPointer(vars.H1_coords + 0x8)) { Name = "zpos" });
        vars.watchers_h1xy.Add(new MemoryWatcher<byte>(new DeepPointer(vars.H1_coords - 0x8)) { Name = "chiefstate" });

        vars.watchers_h1fade.Add(new MemoryWatcher<uint>(new DeepPointer(vars.H1_fade)) { Name = "fadetick" });
        vars.watchers_h1fade.Add(new MemoryWatcher<ushort>(new DeepPointer(vars.H1_fade + 0x4)) { Name = "fadelength" });
        vars.watchers_h1fade.Add(new MemoryWatcher<byte>(new DeepPointer(vars.H1_fade + 0x6)) { Name = "fadebyte" });

        vars.watchers_c40.Add(new MemoryWatcher<byte>(new DeepPointer(0x4603B0, 0x868, 0x1F4)) { Name = "door3state" });
        vars.watchers_c40.Add(new MemoryWatcher<float>(new DeepPointer(0x4603B0, 0x730, 0x124)) { Name = "liftstate" });
        vars.watchers_c40.Add(new MemoryWatcher<float>(new DeepPointer(0x47ABF0, 0x94)) { Name = "brokendoorstate" });
        vars.watchers_c40.Add(new MemoryWatcher<float>(new DeepPointer(0x47ABF0, 0xA4)) { Name = "lastdoorstate" });

    }

    else if (modules.First().ToString() == "haloce.exe") {
        version = "Custom Edition";

        vars.H1_map_globals = 0x2B47C8;
        vars.H1_map = 0x243044;
        vars.H1_cinflags = 0x3FFFD678;
        vars.H1_coords = 0x2474EC;
        vars.H1_fade = 0x3FF15814;
        vars.H1_hsthread = 0x415910;

        vars.watchers_h1.Add(new MemoryWatcher<uint>(new DeepPointer(0x292E9C)) { Name = "tickcounter" });
        vars.watchers_h1.Add(new MemoryWatcher<byte>(new DeepPointer(0x2397D0)) { Name = "bspstate" });
        vars.watchers_h1.Add(new MemoryWatcher<byte>(new DeepPointer(0x3FC00126)) { Name = "difficulty" });

        vars.watchers_h1.Add(new StringWatcher(new DeepPointer(vars.H1_map + 0x20), 32) { Name = "levelname" });
        vars.watchers_h1.Add(new StringWatcher(new DeepPointer(vars.H1_map + 0x40), 32) { Name = "buildversion" });

        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_map_globals)) { Name = "mapreset" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_map_globals + 0x1)) { Name = "gamewon" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_cinflags + 0x1)) { Name = "cinematic" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_cinflags + 0x2)) { Name = "cutsceneskip" });
        vars.watchers_h1.Add(new MemoryWatcher<bool>(new DeepPointer(vars.H1_map_globals + 0x17)) { Name = "deathflag" });

        vars.watchers_h1xy.Add(new MemoryWatcher<float>(new DeepPointer(vars.H1_coords)) { Name = "xpos" });
        vars.watchers_h1xy.Add(new MemoryWatcher<float>(new DeepPointer(vars.H1_coords + 0x4)) { Name = "ypos" });
        vars.watchers_h1xy.Add(new MemoryWatcher<float>(new DeepPointer(vars.H1_coords + 0x8)) { Name = "zpos" });
        vars.watchers_h1xy.Add(new MemoryWatcher<byte>(new DeepPointer(vars.H1_coords - 0x8)) { Name = "chiefstate" });

        vars.watchers_h1fade.Add(new MemoryWatcher<uint>(new DeepPointer(vars.H1_fade)) { Name = "fadetick" });
        vars.watchers_h1fade.Add(new MemoryWatcher<ushort>(new DeepPointer(vars.H1_fade + 0x4)) { Name = "fadelength" });
        vars.watchers_h1fade.Add(new MemoryWatcher<byte>(new DeepPointer(vars.H1_fade + 0x6)) { Name = "fadebyte" });

        vars.watchers_c40.Add(new MemoryWatcher<byte>(new DeepPointer(0x3FB710, 0x868, 0x1F4)) { Name = "door3state" });
        vars.watchers_c40.Add(new MemoryWatcher<float>(new DeepPointer(0x3FB710, 0x730, 0x124)) { Name = "liftstate" });
        vars.watchers_c40.Add(new MemoryWatcher<float>(new DeepPointer(0x416110, 0x94)) { Name = "brokendoorstate" });
        vars.watchers_c40.Add(new MemoryWatcher<float>(new DeepPointer(0x416110, 0xA4)) { Name = "lastdoorstate" });

    }

    vars.watchers_a50.Add(new MemoryWatcher<uint>(new DeepPointer(vars.H1_hsthread, 0x9734)) { Name = "dropship" });

    vars.watchers_b30.Add(new MemoryWatcher<uint>(new DeepPointer(vars.H1_hsthread, 0xAEC)) { Name = "pelican" });

    vars.watchers_c40.Add(new MemoryWatcher<byte>(new DeepPointer(vars.H1_hsthread, 0x8CDA)) { Name = "gen1state" });
}

startup {
    // START AND END CONDITIONS
    vars.H1_ILstart = new Dictionary<string, Func<bool>> {
        {"a10", () => vars.watchers_h1["bspstate"].Current == 0 && vars.watchers_h1xy["xpos"].Current < -55 && vars.watchers_h1["tickcounter"].Current > 280 && !vars.watchers_h1["cinematic"].Current && vars.watchers_h1["cinematic"].Old }, //poa
        {"a30", () => ((vars.watchers_h1["tickcounter"].Current >= 182 && vars.watchers_h1["tickcounter"].Current < 190) || (!vars.watchers_h1["cinematic"].Current && vars.watchers_h1["cinematic"].Old && vars.watchers_h1["tickcounter"].Current > 500 && vars.watchers_h1["tickcounter"].Current < 900)) && !vars.watchers_h1["cutsceneskip"].Current }, //halo
        {"a50", () => vars.watchers_h1["tickcounter"].Current > 30 && vars.watchers_h1["tickcounter"].Current < 900 && !vars.watchers_h1["cinematic"].Current && vars.watchers_h1["cinematic"].Old }, //tnr
        {"b30", () => vars.watchers_h1["tickcounter"].Current > 30 && vars.watchers_h1["tickcounter"].Current < 1060 && !vars.watchers_h1["cinematic"].Current && vars.watchers_h1["cinematic"].Old }, //sc
        {"b40", () => vars.watchers_h1["tickcounter"].Current > 30 && vars.watchers_h1["tickcounter"].Current < 950 && !vars.watchers_h1["cinematic"].Current && vars.watchers_h1["cinematic"].Old }, //aotcr
        {"c10", () => vars.watchers_h1["tickcounter"].Current > 30 && vars.watchers_h1["tickcounter"].Current < 700 && !vars.watchers_h1["cinematic"].Current && vars.watchers_h1["cinematic"].Old }, //343
        {"c20", () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old }, //library
        {"c40", () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old }, //tb
        {"d20", () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old }, //keyes
        {"d40", () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old }, //maw
    };

    vars.H1_customstart = new Dictionary<string, Func<bool>> {
        {"lumoria_a", () => (vars.watchers_h1["tickcounter"].Current >= 686 && vars.watchers_h1["tickcounter"].Current < 690 && !vars.watchers_h1["cutsceneskip"].Current)},
    };

    vars.H1_ILendsplits = new Dictionary<string, Func<bool>> {
        {"a10", () => vars.watchers_h1["bspstate"].Current == 6 && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current }, //poa
        {"a30", () => vars.watchers_h1["bspstate"].Current == 1 && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current }, //halo
        {"a50", () => (vars.watchers_h1["bspstate"].Current == 3 || vars.watchers_h1["bspstate"].Current == 2) && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1fade["fadelength"].Current == 15 }, //tnr
        {"b30", () => vars.watchers_h1["bspstate"].Current == 0 && !vars.watchers_h1["cinematic"].Current && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current }, //sc
        {"b40", () => vars.watchers_h1["bspstate"].Current == 2 && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current }, //aotcr
        {"c10", () => vars.watchers_h1["bspstate"].Current != 2 && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current }, //343
        {"c20", () => vars.watchers_h1["cinematic"].Current && !vars.watchers_h1["cinematic"].Old && vars.watchers_h1["tickcounter"].Current > 30 }, //library
        {"c40", () => vars.watchers_h1["tickcounter"].Current > 30 && !vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1fade["fadebyte"].Current != 1 }, //tb
        {"d20", () => vars.watchers_h1fade["fadelength"].Current == 30 && !vars.watchers_h1["cinematic"].Old && vars.watchers_h1["cinematic"].Current}, //keyes
        {"d40", () => !vars.watchers_h1["cinematic"].Old && vars.watchers_h1["cinematic"].Current && !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1xy["xpos"].Current > 1000 && !vars.watchers_h1["deathflag"].Current}, //maw
    };


    // IL SPLITS
    vars.H1_a10splits = new Dictionary<byte, Func<bool>> {
        {0, () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["bspstate"].Current == 1}, //Split on bridge cs
        {1, () => vars.watchers_h1["bspstate"].Current == 2 && vars.watchers_h1["bspstate"].Old != 2}, //Split on bsp switch after cafe
        {2, () => vars.watchers_h1["bspstate"].Current == 2 && vars.watchers_h1xy["xpos"].Current < -44 && vars.watchers_h1xy["ypos"].Current > 20}, //Split on approaching flank encounter
        {3, () => vars.watchers_h1["bspstate"].Current == 3 && vars.watchers_h1["bspstate"].Old != 3}, //Split on load before loop encounter
        {4, () => vars.watchers_h1["bspstate"].Current == 4 && vars.watchers_h1["bspstate"].Old != 4}, //Split on load before stair encounter
        {5, () => vars.watchers_h1["bspstate"].Current == 4 && vars.watchers_h1xy["xpos"].Current < -60 && !(vars.watchers_h1xy["xpos"].Old < -60) && vars.watchers_h1xy["ypos"].Current < 34}, //Split on entering maintenance tunnel
        {6, () => vars.watchers_h1["bspstate"].Current == 6 && vars.watchers_h1["bspstate"].Old != 6}, //Split on last load
    };

    vars.H1_a50splits = new Dictionary<byte, Func<bool>> {
        {0, () => vars.watchers_a50["dropship"].Changed && vars.watchers_a50["dropship"].Current != 0 && vars.watchers_a50["dropship"].Old != 0}, //Split on c ship spawn
        {1, () => vars.watchers_h1["bspstate"].Current == 1 && vars.watchers_h1["bspstate"].Old != 1}, //Split on belly
        {2, () => vars.watchers_h1xy["ypos"].Current < -24 && vars.watchers_h1["bspstate"].Current == 2}, //Split on entering hangar
        {3, () => vars.watchers_h1["bspstate"].Current == 3 && vars.watchers_h1["bspstate"].Old != 3}, //Split on bridge bsp
        {4, () => vars.watchers_h1xy["xpos"].Current < -6.1 && vars.watchers_h1["bspstate"].Current == 3}, //Split on bridge exit
        {5, () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["bspstate"].Current == 3}, //Split on prison cs
    };

    vars.H1_b30splits = new Dictionary<byte, Func<bool>> {
        {0, () => vars.watchers_h1xy["chiefstate"].Current == 2 && vars.watchers_h1["tickcounter"].Current > 1100}, //Split on entering hog
        {1, () => (vars.watchers_h1xy["ypos"].Current < -23.3 && vars.watchers_h1xy["xpos"].Current < 3.7) || (vars.watchers_h1["bspstate"].Current == 1 && vars.watchers_h1xy["zpos"].Current < 2.0 )}, //Split on fling
        {2, () => !vars.watchers_h1["cutsceneskip"].Current && vars.watchers_h1["cutsceneskip"].Old && vars.watchers_h1["bspstate"].Current == 1}, //Split on button
        {3, () => vars.watchers_b30["pelican"].Changed && vars.watchers_b30["pelican"].Current != 0 && vars.watchers_b30["pelican"].Old != 0}, //Split on pelican spawn
    };

    vars.H1_c40splits = new Dictionary<byte, Func<bool>> {
        {0, () => vars.watchers_c40["door3state"].Current == 4 && vars.watchers_c40["door3state"].Old == 0}, //Split on ext door button
        {1, () => vars.watchers_h1xy["chiefstate"].Current == 2 && vars.watchers_h1xy["chiefstate"].Old != 2 && vars.watchers_h1["bspstate"].Current == 2}, //Split on banshee
        {2, () => vars.watchers_c40["gen1state"].Current == 1 && vars.watchers_c40["gen1state"].Old != 1}, //Split on gen1
        {3, () => vars.watchers_h1["bspstate"].Current == 10 && vars.watchers_h1["bspstate"].Old != 10}, //Split on ice bridge bsp
        {4, () => vars.watchers_h1["bspstate"].Current == 1 && vars.watchers_h1["bspstate"].Old != 1 && vars.watchers_h1xy["xpos"].Current > 275}, //Split on b1 start
        {5, () => vars.watchers_h1["bspstate"].Current == 9 && vars.watchers_h1["bspstate"].Old != 9 && vars.watchers_h1xy["xpos"].Current > 275 && vars.watchers_h1xy["ypos"].Current < -495}, //Split on b1 end
        {6, () => vars.watchers_h1["bspstate"].Current == 1 && vars.watchers_h1["bspstate"].Old != 1 && vars.watchers_h1xy["xpos"].Current < 275 && vars.watchers_h1xy["ypos"].Current < -495}, //Split on b2 start
        {7, () => vars.watchers_h1["bspstate"].Current == 8 && vars.watchers_h1["bspstate"].Old != 8 && vars.watchers_h1xy["xpos"].Current < 275}, //Split on b2 end
        {8, () => vars.watchers_c40["liftstate"].Current == 1 && vars.watchers_c40["liftstate"].Old == 0 && vars.watchers_h1["bspstate"].Current == 8}, //Split on lift
        {9, () => vars.watchers_h1xy["chiefstate"].Current == 2 && vars.watchers_h1xy["chiefstate"].Old != 2 && vars.watchers_h1["bspstate"].Current == 1}, //Split on ghost
        {10, () => vars.watchers_h1["bspstate"].Current == 6 && vars.watchers_h1["bspstate"].Old != 6}, //Split on gen2 entry
        {11, () => vars.watchers_h1xy["chiefstate"].Current == 2 && vars.watchers_h1xy["chiefstate"].Old != 2 && vars.watchers_h1["bspstate"].Current == 1}, //Split on gen2 banshee
        {12, () => vars.watchers_c40["brokendoorstate"].Current == 1 && vars.watchers_c40["brokendoorstate"].Old == 0}, //Split on broken door button
        {13, () => vars.watchers_c40["lastdoorstate"].Current == 1 && vars.watchers_c40["lastdoorstate"].Old == 0}, //Split on final door button
    };


    vars.H1_levellist = new Dictionary<string, byte[]> {
        {"a10", new byte[] { 1, 2, 3, 4, 5, 6 }}, //poa
        {"a30", new byte[] { 1 }}, //halo
        {"a50", new byte[] { 1, 2, 3 }}, //tnr
        {"b30", new byte[] { 1 }}, //sc
        {"b40", new byte[] { 0, 1, 2, 4, 8, 9, 10, 11 }}, //aotcr - put the others in for fullpath andys
        {"c10", new byte[] { 1, 3, 4, 5 }}, //343
        {"c20", new byte[] { 1, 2, 3 }}, //library
        {"c40", new byte[] { 12, 10, 1, 9, 8, 6, 0, 5 }}, //tb
        {"d20", new byte[] { 4, 3, 2 }}, //keyes
        {"d40", new byte[] { 1, 2, 3, 4, 5, 6, 7 }}, //maw
    };


    //GENERAL VARS INIT - most of these need to be reinit on timer reset
    vars.startedlevel = "000";
    vars.varsreset = false;
    vars.index = 0;
    vars.dirtybsps_byte = new List<byte>();


    //SETTINGS
    settings.Add("LoadSplit", true, "Spit on loading screen");
    settings.SetToolTip("LoadSplit", "Spit on loading screen between levels (does nothing if in IL mode)");

    settings.Add("MenuSplit", false, "Spit on loading level from main menu");
    settings.SetToolTip("MenuSplit", "Spit on loading a level from the main menu. Useful for Hunter%");

    settings.Add("anylevel", false, "Start full-game runs on any level (READ THE TOOLTIP)");
    settings.SetToolTip("anylevel", "You probably don't need to use this. This option starts the timer on any level instead of just the first level for full-game runs.");

    settings.Add("CustomMap", false, "Start full-game run for a custom campaign");
    settings.SetToolTip("CustomMap", "Starts the timer for a supported custom campaign (ie Lumoria)");

    settings.Add("ILmode", false, "Individual Level mode");
    settings.SetToolTip("ILmode", "Makes the timer start, reset and ending split at the correct IL time for each level.");

    settings.Add("ILsplits", false, "Individual Level splits", "ILmode");
    settings.SetToolTip("ILsplits", "Cambid's special sauce IL splits. You will need: \n" +
        "PoA: 8 Splits \n" +
        "T&R: 7 Splits \n" +
        "SC: 5 Splits \n" +
        "TB: 15 Splits \n" +
        "Yet to add other levels");

    settings.Add("bspmode", false, "Split on unique \"Loading... Done\"'s ");
    settings.SetToolTip("bspmode", "Split on unique bsp loads (\"Loading... Done\") within levels. \n" +
        "You'll need to add a lot of extra splits for this option"
    );
    settings.Add("deathcounter", false, "Enable Death Counter");
    settings.SetToolTip("deathcounter", "Will automatically create a layout component for you. Feel free \n" +
        "to move it around, but you won't be able to rename it"
    );
    settings.Add("loadremoval", false, "Remove Loads (DO NOT ENABLE)");
    settings.SetToolTip("loadremoval", "For testing only. On current HR rules, loads are still timed on Halo PC");

    //DEATH COUNTERS AND FUN
    //DEATHS
    vars.TextDeathCounter     = null;
    vars.DeathCounter         = 0;
    vars.UpdateDeathCounter = (Action)(() => {
        if(vars.TextDeathCounter == null) {
            foreach (dynamic component in timer.Layout.Components) {
                if (component.GetType().Name != "TextComponent") continue;
                
                if (component.Settings.Text1 == "Deaths:") {
                    vars.TextDeathCounter = component.Settings;
                    break;
                }
            }
            if(vars.TextDeathCounter == null) {
                vars.TextDeathCounter = vars.CreateTextComponent("Deaths:");
            }
        }
        vars.TextDeathCounter.Text2 = vars.DeathCounter.ToString();
    });

    vars.CreateTextComponent = (Func<string, dynamic>)((name) => {
        var textComponentAssembly = Assembly.LoadFrom("Components\\LiveSplit.Text.dll");
        dynamic textComponent = Activator.CreateInstance(textComponentAssembly.GetType("LiveSplit.UI.Components.TextComponent"), timer);
        timer.Layout.LayoutComponents.Add(new LiveSplit.UI.Components.LayoutComponent("LiveSplit.Text.dll", textComponent as LiveSplit.UI.Components.IComponent));
        textComponent.Settings.Text1 = name;
        return textComponent.Settings;
    }); 
}

update {
    vars.watchers_h1.UpdateAll(game);
    if (timer.CurrentPhase == TimerPhase.NotRunning) {
        vars.watchers_h1xy.UpdateAll(game);
    }
    else {
        if (settings["ILmode"]) {
            string checklevel = vars.watchers_h1["levelname"].Current;
            switch (checklevel) {
                case "a10":
                    if (settings["ILsplits"]) {
                        vars.watchers_h1xy.UpdateAll(game);
                    }
                break;

                case "a50":
                    vars.watchers_h1fade.UpdateAll(game);
                    if (settings["ILsplits"]) {
                        vars.watchers_a50.UpdateAll(game);
                        vars.watchers_h1xy.UpdateAll(game);
                    }
                break;

                case "b30":
                    if (settings["ILsplits"]) {
                        vars.watchers_b30.UpdateAll(game);
                        vars.watchers_h1xy.UpdateAll(game);
                    }
                break;

                case "c40":
                    vars.watchers_h1fade.UpdateAll(game);
                    vars.watchers_c40.UpdateAll(game);
                    vars.watchers_h1xy.UpdateAll(game);
                break;

                case "d20":
                    vars.watchers_h1fade.UpdateAll(game);
                break;

                case "d40":
                    vars.watchers_h1xy.UpdateAll(game);
                break;
            }
        }

        if (settings["bspmode"]) {
            if (vars.watchers_h1["levelname"].Current == "b40" || vars.watchers_h1["levelname"].Current == "c40") {
                vars.watchers_h1xy.UpdateAll(game);
            }
        }
    }

    if (timer.CurrentPhase == TimerPhase.Running && !vars.varsreset) {
        vars.varsreset = true;
    }
    else if (timer.CurrentPhase == TimerPhase.NotRunning && vars.varsreset) {
        vars.varsreset = false;
        vars.dirtybsps_byte.Clear();
        vars.startedlevel = "000";
        vars.index = 0;

        vars.DeathCounter = 0;
        if (settings["deathcounter"]) {
            vars.UpdateDeathCounter();
        }
        print ("Autosplitter vars reinitalized!");
    }
}

start {
    if (vars.watchers_h1["levelname"].Current != "" || vars.watchers_h1["levelname"].Current != "ui") {
        if(!settings["CustomMap"]) {
            // Check that maps are build with tool, not invader-build or something.
            if((version == "Retail" && vars.watchers_h1["buildversion"].Current == "01.00.00.0564") || (version == "Custom Edition" && vars.watchers_h1["buildversion"].Current == "01.00.00.0609")) {
                foreach (var entry in vars.H1_ILstart) {
                    if (entry.Key == vars.watchers_h1["levelname"].Current && (entry.Key == "a10" || (settings["ILmode"] || settings["anylevel"]))) {
                        if (entry.Value()) {
                            vars.startedlevel = entry.Key;
                            return true;
                        }
                    }
                }
            }
        }
        else {
            foreach (var entry in vars.H1_customstart) {
                if (entry.Key == vars.watchers_h1["levelname"].Current && entry.Value()) {
                    vars.startedlevel = entry.Key;
                    return true;
                }
            }
        }
    }
}

split {
    string checklevel = vars.watchers_h1["levelname"].Current;

    //Death counter check
    if (settings["deathcounter"]) {
        if (vars.watchers_h1["deathflag"].Current && !vars.watchers_h1["deathflag"].Old) {
            print ("adding death");
            vars.DeathCounter += 1;
            vars.UpdateDeathCounter();
        }
    }


    if (settings["bspmode"] && !settings["ILsplits"]) {
        if (checklevel == "b40") {
            if (vars.watchers_h1["bspstate"].Current != vars.watchers_h1["bspstate"].Old && Array.Exists((byte[]) vars.H1_levellist[checklevel], x => x == vars.watchers_h1["bspstate"].Current) && !(vars.dirtybsps_byte.Contains(vars.watchers_h1["bspstate"].Current))) {
                if (vars.watchers_h1["bspstate"].Current == 0) {
                    if (vars.watchers_h1xy["ypos"].Current > (-19.344 - 0.2) && vars.watchers_h1xy["ypos"].Current < (-19.344 + 0.2)) {
                        vars.dirtybsps_byte.Add(vars.watchers_h1["bspstate"].Current);
                        return true;
                    }
                    else {
                        return false;
                    }
                } 
                else {
                    vars.dirtybsps_byte.Add(vars.watchers_h1["bspstate"].Current);
                    return true;
                }
            }
        }
        else if (checklevel == "c40") {
            if (vars.watchers_h1["bspstate"].Current != vars.watchers_h1["bspstate"].Old && Array.Exists((byte[]) vars.H1_levellist[checklevel], x => x == vars.watchers_h1["bspstate"].Current) && !(vars.dirtybsps_byte.Contains(vars.watchers_h1["bspstate"].Current)) && vars.watchers_h1["tickcounter"].Current > 30) {
                if (vars.watchers_h1["bspstate"].Current == 0) {
                    if (vars.watchers_h1xy["xpos"].Current > 171.87326 && vars.watchers_h1xy["xpos"].Current < 185.818526 && vars.watchers_h1xy["ypos"].Current > -295.3629 && vars.watchers_h1xy["ypos"].Current < -284.356986) {
                        vars.dirtybsps_byte.Add(vars.watchers_h1["bspstate"].Current);
                        return true;
                    }
                    else {
                        return false;
                    }
                } 
                else {
                    vars.dirtybsps_byte.Add(vars.watchers_h1["bspstate"].Current);
                    return true;
                }
            }
        }
        else {
            if (vars.watchers_h1["bspstate"].Current != vars.watchers_h1["bspstate"].Old && Array.Exists((byte[]) vars.H1_levellist[checklevel], x => x == vars.watchers_h1["bspstate"].Current) && !(vars.dirtybsps_byte.Contains(vars.watchers_h1["bspstate"].Current))) {
                vars.dirtybsps_byte.Add(vars.watchers_h1["bspstate"].Current);
                return true;
            }
        }
    }

    if (settings["ILmode"]) {
        if (settings["ILsplits"]) {
            switch (checklevel) {
                case "a10":
                    foreach (var entry in vars.H1_a10splits) {
                        if (entry.Key == vars.index) {
                            if (entry.Value()) {
                                vars.index++;
                                return true;
                            }
                        }
                    }
                break;

                case "a50":
                    foreach (var entry in vars.H1_a50splits) {
                        if (entry.Key == vars.index) {
                            if (entry.Value()) {
                                vars.index++;
                                return true;
                            }
                        }
                    }
                break;

                case "b30":
                    foreach (var entry in vars.H1_b30splits) {
                        if (entry.Key == vars.index) {
                            if (entry.Value()) {
                                vars.index++;
                                return true;
                            }
                        }
                    }
                break;

                case "c40":
                    foreach (var entry in vars.H1_c40splits) {
                        if (entry.Key == vars.index) {
                            if (entry.Value()) {
                                vars.index++;
                                return true;
                            }
                        }
                    }
                break;
            }
        }

        foreach (var entry in vars.H1_ILendsplits) {
            if (entry.Key == vars.watchers_h1["levelname"].Current) {
                if (entry.Value()) {
		            vars.dirtybsps_byte.Clear();
                    return true;
                }
            }
        }
    }
    else {
        if ((settings["LoadSplit"] && vars.watchers_h1["levelname"].Current != "ui" && vars.watchers_h1["gamewon"].Current && !vars.watchers_h1["gamewon"].Old) 
        || (settings["MenuSplit"] && vars.watchers_h1["levelname"].Current == "ui" && vars.watchers_h1["gamewon"].Current && !vars.watchers_h1["gamewon"].Old) 
        || (vars.watchers_h1["levelname"].Current == "d40" && !vars.watchers_h1["cinematic"].Old && vars.watchers_h1["cinematic"].Current && !vars.watchers_h1["cutsceneskip"].Current)) {
		    vars.dirtybsps_byte.Clear();
            return true;
        }
    }
}

reset {
    if (settings["ILmode"]) {
        return ((vars.watchers_h1["mapreset"].Current && !vars.watchers_h1["mapreset"].Old) || (vars.watchers_h1["levelname"].Current == "ui"));
    }
    else if ((settings["anylevel"] || settings["CustomMap"] || vars.watchers_h1["levelname"].Current == "a10") && vars.watchers_h1["levelname"].Current == vars.startedlevel && timer.CurrentPhase != TimerPhase.Ended) {
        return ((vars.watchers_h1["mapreset"].Current && !vars.watchers_h1["mapreset"].Old) || (vars.watchers_h1["levelname"].Current != "ui" && vars.watchers_h1["levelname"].Old == "ui"));
    }
}

isLoading {
    if (settings["loadremoval"]) {
        return (vars.watchers_h1["levelname"].Current != "ui" && vars.watchers_h1["gamewon"].Current);
    }
    else {
        return false;
    }
}

gameTime {
}