state("ChromaGun2-Win64-Shipping") {}

startup {

    vars.chambers = new List<string>() {
        "Chamber_0-1_Demo",
        "Chamber_0-2_Demo",
        "Chamber_0-3_Demo",
        "Chamber_0-3_Demo_Chickenverse",
        "Chamber_0-4_Demo"
    };

    Assembly.Load(File.ReadAllBytes("Components/asl-help")).CreateInstance("Basic");
    vars.Helper.AlertGameTime();

    settings.Add("start", true, "Start after skipping intro text");
    settings.Add("demo_any%", true, "Split on loading screens between chambers: ");
    settings.Add("demo_end", true, "Split on entering the portal in the final chamber");
    settings.Add("split_golden", false, "Split on collecting a Golden ChromaGun");
    settings.Add("reset", true, "Reset on returning to Main Menu");

    for (int i = 1; i < vars.chambers.Count; i++) {
        var str = vars.chambers[i - 1]  + " -> " + vars.chambers[i];
        settings.Add(str, true, str, "demo_any%");
    }

}

init {
    var gEngine = vars.Helper.ScanRel(3, "48 8B 0D ???????? 66 0F 5A C9 E8");
    var gWorld = vars.Helper.ScanRel(3, "48 8B 1D ???????? 48 85 DB 74 ?? 41 B0 01");
    var fNames = vars.Helper.ScanRel(7, "8B D9 74 ?? 48 8D 15 ???????? EB");
    var gSyncLoad = vars.Helper.ScanRel(5, "89 43 60 8B 05");

    if (gEngine == IntPtr.Zero || gWorld == IntPtr.Zero)
         throw new Exception();

    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;
    });

    // GWorld name
    vars.Helper["GWorldName"] = vars.Helper.Make<ulong>(gWorld, 0x18);
    // GWorld.OwningGameInstance.LocalPlayers[0].PlayerController.FName
    vars.Helper["ULocalPlayer"] = vars.Helper.Make<ulong>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x18);
    // GWorld.OwningGameInstance.LocalPlayers[0].PlayerController.MyHUD.CurrentHUDConfig
    //vars.Helper["EHUDConfig"] = vars.Helper.Make<byte>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x340, 0x3B8);
    // GWorld.OwningGameInstance.LocalPlayers[0].PlayerController.MyHUD.HUDLayoutInstance
    //vars.Helper["test"] = vars.Helper.Make<byte>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x340, 0x3B0, 0x4B0, 0x02F8, 0x00DC);
    // GWorld.AuthorityGamemode.HUDClass.HUDLayoutInstance.Widgets
    //int widgets = vars.Helper.Read<int>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x340, 0x3B0, 0x460 + 0x8);
    /*
    for (int i = 0; i < widgets; i++) {
        ulong fName = vars.Helper.Read<ulong>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x340, 0x3B0, 0x460 + i * 0x18 + 0x8, 0x18);
        print(vars.FNameToString(fName));
    }*/

    //GWorld.OwningGameInstance.LocalPlayers[0].PlayerController.PlayerCameraManager.TransformComponent.RelativeLocation
    vars.Helper["x"] = vars.Helper.Make<double>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x0348, 0x298, 0x128);
    vars.Helper["z"] = vars.Helper.Make<double>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x0348, 0x298, 0x130);
    vars.Helper["y"] = vars.Helper.Make<double>(gWorld, 0x1D8, 0x38, 0x0, 0x30, 0x0348, 0x298, 0x138);

    // GEngine.TransitionType
    //vars.Helper["TransitionType"] = vars.Helper.Make<uint>(gEngine, 0xBBB);

    // GEngine.GameViewport.SpeedRunWidget.TimeText...
    vars.Helper["SpeedrunTimer"] = vars.Helper.MakeString(128, ReadStringType.UTF16, gEngine, 0xA80, 0x3F0, 0x2E8, 0x1A0, 0x28, 0x0);

    // GEngine.GameViewport.SpeedrunWidget.TrinketText
    vars.Helper["TrinketCount"] = vars.Helper.MakeString(128, ReadStringType.UTF16, gEngine, 0xA80, 0x3F0, 0x2F0, 0x1A0, 0x28, 0x0);

    // GEngine.TransitionDescription
    //vars.Helper["TransitionDescription"] = vars.Helper.MakeString(gEngine, 0xBC0, 0x0);
    // GWorld.AuthorityGamemode.FName
    //vars.Helper["AuthorityGamemode"] = vars.Helper.Make<uint>(gEngine, 0x158, 0x2F0, 0x18);

    //vars.Helper["loading"] = vars.Helper.Make<bool>(gSyncLoad);

    current.positionTotal = 0;
    current.chamber = "Chamber_0-1_Demo";
}

update {
    vars.Helper.Update();
    vars.Helper.MapPointers();
    current.time = TimeSpan.Parse("00:" + current.SpeedrunTimer);
    current.trinkets = Int32.Parse(current.TrinketCount.Substring(current.TrinketCount.Length - 2));
    current.world = vars.FNameToString(current.GWorldName);
    if (vars.chambers.Contains(current.world)) current.chamber = current.world;

    if (current.chamber == "Chamber_0-4_Demo") current.positionTotal = Math.Floor(current.x + current.y + current.z);
    current.positionTotal = Math.Floor(current.x + current.y + current.z);

    //print(current.TrinketCount[current.TrinketCount.Length - 1] + "");
    //print(current.trinkets + "");

    //if (old.positionTotal != current.positionTotal) print(old.positionTotal + " -> " + current.positionTotal);
    //if (old.x != current.x || old.y != current.y || old.z != current.z) print("X: " + current.x + ",Y: " + current.y + ",Z: " + current.z);
    if (old.world != current.world) print("world: " + old.world + " -> " + current.world);
    if (old.chamber != current.chamber) print("chamber: " + old.chamber + " -> " + current.chamber);
}

gameTime {
    return current.time;
}

isLoading {
    return true;
}

start {
    return settings["start"] && current.chamber == "Chamber_0-1_Demo" && old.time.Milliseconds <= 50 && current.time.Milliseconds > 50;
}

split {
    return (old.GWorldName != current.GWorldName && settings[old.chamber + " -> " + current.chamber]) ||
            (settings["split_golden"] && current.trinkets - old.trinkets == 1) ||
            (settings["demo_end"] && current.chamber == "Chamber_0-4_Demo" && current.positionTotal == 20657 && old.positionTotal != current.positionTotal);
}

reset {
    return settings["reset"] && old.world != "MainMenu" && current.world == "MainMenu";
}