public void RandomizeTrees(Random random, Permutation permutation, SkillSplitter.Assignment split)
        {
            // >= 700: prosthetics
            // < 400: skills before mushin
            GameEditor editor = game.Editor;
            PARAM      param  = game.Params["SkillParam"];

            // Orderings for skills which completely supersede each other. (For prosthetics, just use their natural id ordering)
            Dictionary <int, int> skillOrderings = new Dictionary <int, int>
            {
                [110] = 111,  // Nightjar slash
                [210] = 211,  // Ichimonji
                [310] = 311,  // Praying Strikes
            };
            Dictionary <int, ItemKey> texts = new Dictionary <int, ItemKey>
            {
                [0] = game.ItemForName("Shinobi Esoteric Text"),
                [1] = game.ItemForName("Prosthetic Esoteric Text"),
                [2] = game.ItemForName("Ashina Esoteric Text"),
                [3] = game.ItemForName("Senpou Esoteric Text"),
                // [4] = game.ItemForName("Mushin Esoteric Text"),
            };
            SortedDictionary <ItemKey, string> names = game.Names();

            string descName(int desc)
            {
                return(names[new ItemKey(ItemType.WEAPON, desc)]);
            }

            Dictionary <int, SkillData>     allData    = new Dictionary <int, SkillData>();
            Dictionary <int, SkillSlot>     allSlots   = new Dictionary <int, SkillSlot>();
            Dictionary <ItemKey, SkillData> skillItems = new Dictionary <ItemKey, SkillData>();
            List <SkillData> skills          = new List <SkillData>();
            List <SkillSlot> skillSlots      = new List <SkillSlot>();
            List <SkillData> prosthetics     = new List <SkillData>();
            List <SkillSlot> prostheticSlots = new List <SkillSlot>();

            bool explain = false;

            foreach (PARAM.Row r in param.Rows)
            {
                SkillData data = new SkillData
                {
                    ID           = (int)r.ID,
                    Item         = (int)r["SkilLDescriptionId"].Value,
                    Equip        = (int)r["Unk1"].Value,
                    Flag         = (int)r["EventFlagId"].Value,
                    Placeholder  = (int)r["Unk5"].Value,
                    SpEffects    = new[] { (int)r["Unk2"].Value, (int)r["Unk3"].Value },
                    EmblemChange = (byte)r["Unk10"].Value != 0,
                };
                data.Key             = new ItemKey(ItemType.WEAPON, data.Item);
                skillItems[data.Key] = data;
                SkillSlot slot = new SkillSlot
                {
                    ID   = (int)r.ID,
                    Col  = (short)r["MenuDisplayPositionIndexXZ"].Value,
                    Row  = (short)r["MenuDisplayPositionIndexY"].Value,
                    Text = data.ID < 400 && texts.TryGetValue((byte)r["Unk7"].Value, out ItemKey text) ? text : null,
                };
                if (explain)
                {
                    Console.WriteLine($"{r.ID}: {data.Item}, {data.Equip}, {descName(data.Item)}");
                }
                if (data.ID < 400)
                {
                    skills.Add(data);
                    skillSlots.Add(slot);
                }
                else if (data.ID >= 700)
                {
                    prosthetics.Add(data);
                    prostheticSlots.Add(slot);
                }
                allData[data.ID]  = data;
                allSlots[slot.ID] = slot;
            }
            void applyData(PARAM.Row r, SkillData data)
            {
                r["SkilLDescriptionId"].Value = data.Item;
                r["EventFlagId"].Value        = data.Flag;
                r["Unk1"].Value  = data.Equip;
                r["Unk2"].Value  = data.SpEffects[0];
                r["Unk3"].Value  = data.SpEffects[0];
                r["Unk5"].Value  = data.Placeholder;
                r["Unk10"].Value = (byte)(data.EmblemChange ? 1 : 0);
            }

            Shuffle(random, skills);
            Shuffle(random, skillSlots);
            Shuffle(random, prosthetics);
            Shuffle(random, prostheticSlots);

            // Skills rando
            if (split == null)
            {
                Dictionary <ItemKey, string> textWeight    = new Dictionary <ItemKey, string>();
                Dictionary <ItemKey, string> textLocations = texts.Values.ToDictionary(t => t, t => {
                    SlotKey target    = permutation.GetFiniteTargetKey(t);
                    textWeight[t]     = permutation.GetLogOrder(target);
                    SlotAnnotation sn = ann.Slot(data.Location(target).LocScope);
                    if (explain)
                    {
                        Console.WriteLine($"{game.Name(t)} in {sn.Area} - {sn.Text}. Lateness {(permutation.ItemLateness.TryGetValue(t, out double val) ? val : -1)}");
                    }
                    return(sn.Area);
                });